s390utils/0051-lsmem-chmem-Tools-to-manage-memory-hotplug.patch
2011-03-25 14:59:32 +01:00

730 lines
19 KiB
Diff

From 411a47d37b69a0763d1d7b1e3e132cfab67815cd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
Date: Fri, 28 Jan 2011 14:15:39 +0100
Subject: [PATCH 51/61] lsmem/chmem: Tools to manage memory hotplug
Summary: lsmem/chmem: Tools to manage memory hotplug.
Description: With lsmem, you can display the online status of all available
memory. With chmem, you can set memory online or offline.
---
README | 2 +
zconf/Makefile | 17 +++-
zconf/chmem | 325 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
zconf/chmem.8 | 71 ++++++++++++
zconf/lsmem | 158 +++++++++++++++++++++++++++
zconf/lsmem.8 | 69 ++++++++++++
6 files changed, 639 insertions(+), 3 deletions(-)
create mode 100644 zconf/chmem
create mode 100644 zconf/chmem.8
create mode 100644 zconf/lsmem
create mode 100644 zconf/lsmem.8
diff --git a/README b/README
index 4335b43..dbb1475 100644
--- a/README
+++ b/README
@@ -112,6 +112,8 @@ s390-tools (1.8.2)
adapters.
- cio_ignore: Query and modify the contents of the CIO device driver
blacklist.
+ - lsmem: Display the online status of the available memory.
+ - chmem: Set hotplug memory online or offline.
* dumpconf:
Allows to configure the dump device used for system dump in case a kernel
diff --git a/zconf/Makefile b/zconf/Makefile
index 9fe8b42..10f2b87 100644
--- a/zconf/Makefile
+++ b/zconf/Makefile
@@ -5,14 +5,16 @@ include ../common.mak
SCRIPTS = lsdasd lstape lscss chccwdev lsqeth lszfcp lschp chchp lszcrypt \
chzcrypt lsluns cio_ignore znetconf
+USRSBIN_SCRIPTS = lsmem chmem
MANPAGES= lsdasd.8 lstape.8 lscss.8 chccwdev.8 lsqeth.8 lszfcp.8 lschp.8 \
- chchp.8 lszcrypt.8 chzcrypt.8 lsluns.8 cio_ignore.8 znetconf.8
+ chchp.8 lszcrypt.8 chzcrypt.8 lsluns.8 cio_ignore.8 znetconf.8 \
+ chmem.8 lsmem.8
all:
clean:
-install: install-scripts install-manpages
+install: install-scripts install-manpages install-usrsbin-scripts
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 lsznet.raw $(TOOLS_LIBDIR)
$(INSTALL) -g $(GROUP) -o $(OWNER) -m 755 znetcontrolunits \
$(TOOLS_LIBDIR)
@@ -26,6 +28,15 @@ install-scripts: $(SCRIPTS)
chmod 755 $(BINDIR)/$$i; \
done
+install-usrsbin-scripts: $(USRSBIN_SCRIPTS)
+ @for i in $^; do \
+ cat $$i | \
+ sed -e 's+%S390_TOOLS_VERSION%+$(S390_TOOLS_RELEASE)+' \
+ >$(USRSBINDIR)/$$i; \
+ chown $(OWNER).$(GROUP) $(USRSBINDIR)/$$i; \
+ chmod 755 $(USRSBINDIR)/$$i; \
+ done
+
install-manpages: $(MANPAGES)
@if [ ! -d $(MANDIR) ]; then \
mkdir -p $(MANDIR)/man8; \
@@ -38,4 +49,4 @@ install-manpages: $(MANPAGES)
install -o $(OWNER) -g $(GROUP) -m 644 $$i $(MANDIR)/man8; \
done
-.PHONY: all install clean install-scripts install-manpages
+.PHONY: all install clean install-scripts install-manpages install-usrsbin-scripts
diff --git a/zconf/chmem b/zconf/chmem
new file mode 100644
index 0000000..bdc25a4
--- /dev/null
+++ b/zconf/chmem
@@ -0,0 +1,325 @@
+#!/usr/bin/perl
+###############################################################################
+# chmem - script to show memory hotplug status.
+#
+# Copyright IBM Corp. 2010
+# Author(s): Gerald Schaefer <gerald.schaefer@de.ibm.com>
+###############################################################################
+
+use strict;
+use warnings;
+use Getopt::Long qw(:config no_ignore_case no_auto_abbrev);
+use File::Basename;
+
+my $script_name = fileparse($0);
+my $online = 0;
+my $offline = 0;
+my $memdir = "/sys/devices/system/memory";
+my $block_size = 0;
+my $total_blocks = 0;
+my $devices = {};
+my $dev_size;
+my $blocks_per_dev = 0;
+my $devs_per_block = 0;
+my $ret = 0;
+
+sub chmem_usage()
+{
+ print <<HERE;
+Usage: $script_name [OPTIONS] SIZE|RANGE
+
+The $script_name command sets a particular size or range of memory online
+or offline.
+
+Specify SIZE as <size>[m|M|g|G]. With m or M, <size> specifies the memory
+size in MB (1024 x 1024 bytes). With g or G, <size> specifies the memory size
+in GB (1024 x 1024 x 1024 bytes). The default unit is MB.
+
+Specify RANGE in the form 0x<start>-0x<end> as shown in the output of the
+lsmem command. <start> is the hexadecimal address of the first byte and <end>
+is the hexadecimal address of the last byte in the memory range.
+
+SIZE and RANGE must be aligned to the Linux memory block size, as shown in
+the output of the lsmem command.
+
+OPTIONS
+ -e, --enable
+ Set the given RANGE or SIZE of memory online.
+
+ -d, --disable
+ Set the given RANGE or SIZE of memory offline.
+
+ -h, --help
+ Print a short help text, then exit.
+
+ -v, --version
+ Print the version number, then exit.
+HERE
+}
+
+sub chmem_version()
+{
+ print "$script_name: version %S390_TOOLS_VERSION%\n";
+ print "Copyright IBM Corp. 2010\n";
+}
+
+sub chmem_get_dev_size()
+{
+ my $i = 0;
+ my $device = 0;
+ my $old_device = 0;
+
+ while (-d "$memdir/memory$i") {
+ $device = `cat $memdir/memory$i/phys_device`;
+ chomp($device);
+ if ($device > $old_device) {
+ $dev_size = int($dev_size / ($device - $old_device));
+ last;
+ }
+ $dev_size += $block_size;
+ $i++;
+ }
+}
+
+sub chmem_online($)
+{
+ my $block = shift;
+
+ qx(echo online > $memdir/memory$block/state 2>/dev/null);
+ return $? >> 8;
+}
+
+sub chmem_offline($)
+{
+ my $block = shift;
+
+ qx(echo offline > $memdir/memory$block/state 2>/dev/null);
+ return $? >> 8;;
+}
+
+sub chmem_read_attr($$$)
+# parameters: state, device, block
+{
+ my @attributes = qw(state phys_device);
+ foreach (0..1) {
+ $_[$_] = `cat $memdir/memory$_[2]/$attributes[$_]`;
+ chomp($_[$_]);
+ }
+}
+
+sub chmem_read_devices()
+{
+ my $i = 0;
+ my $device = 0;
+ my $old_device = 0;
+ my $blocks = 0;
+ my $state;
+
+ while (-d "$memdir/memory$i") {
+ chmem_read_attr($state, $device, $i);
+ if ($device != $old_device) {
+ $devices->{$old_device}->{'id'} = $old_device;
+ $devices->{$old_device}->{'blocks'} = $blocks;
+ $old_device = $device;
+ $blocks = 0;
+ }
+ if ($state eq "online") {
+ $blocks++;
+ }
+ $i++;
+ }
+ $devices->{$old_device}->{'blocks'} = $blocks;
+ $devices->{$old_device}->{'id'} = $old_device;
+}
+
+sub chmem_dev_action($$)
+{
+ my ($dev_id, $blocks) = @_;
+ my ($start_block, $end_block, $tmp_block, $max_blocks);
+ my $state;
+ my $i = 0;
+ my $count = 0;
+
+ if ($blocks_per_dev > 0) {
+ $start_block = $dev_id * $blocks_per_dev;
+ $end_block = $start_block + $blocks_per_dev - 1;
+ $max_blocks = $blocks_per_dev;
+ } else {
+ $start_block = int($dev_id / $devs_per_block);
+ $end_block = $start_block;
+ $max_blocks = 1;
+ }
+ if ($blocks > $max_blocks) {
+ $blocks = $max_blocks;
+ }
+ while ($count < $blocks && $i < $max_blocks) {
+ $tmp_block = $online ? $start_block + $i : $end_block - $i;
+ $state = `cat $memdir/memory$tmp_block/state`;
+ chomp($state);
+ if ($offline && $state eq "online") {
+ $count++ unless chmem_offline($tmp_block);
+ }
+ if ($online && $state eq "offline") {
+ $count++ unless chmem_online($tmp_block);
+ }
+ $i++;
+ }
+ return $count;
+}
+
+sub chmem_size($)
+{
+ my $size = shift;
+ my ($blocks, $dev_blocks, $dev_id);
+
+ $blocks = int($size / $block_size);
+ if ($online) {
+ foreach my $device (sort {$b->{'blocks'} <=> $a->{'blocks'} ||
+ $a->{'id'} <=> $b->{'id'}}
+ values %{$devices}) {
+ $dev_blocks = $device->{'blocks'};
+ $dev_id = $device->{'id'};
+ if ($dev_blocks < $blocks_per_dev || $dev_blocks == 0) {
+ $blocks -= chmem_dev_action($dev_id, $blocks);
+ if ($blocks == 0) {
+ last;
+ }
+ }
+ }
+ if ($blocks > 0) {
+ printf(STDERR "chmem: Could only set %lu MB of memory ".
+ "online.\n", $size - $blocks * $block_size);
+ $ret = 1;
+ }
+ } else {
+ foreach my $device (sort {$a->{'blocks'} <=> $b->{'blocks'} ||
+ $b->{'id'} <=> $a->{'id'}}
+ values %{$devices}) {
+ $dev_blocks = $device->{'blocks'};
+ $dev_id = $device->{'id'};
+ if ($dev_blocks > 0) {
+ $blocks -= chmem_dev_action($dev_id, $blocks);
+ if ($blocks == 0) {
+ last;
+ }
+ }
+ }
+ if ($blocks > 0) {
+ printf(STDERR "chmem: Could only set %lu MB of memory ".
+ "offline.\n", $size - $blocks * $block_size);
+ $ret = 1;
+ }
+ }
+}
+
+sub chmem_range($$)
+{
+ my ($start, $end) = @_;
+ my $block = 0;
+ my $state;
+
+ while ($start < $end && $block < $total_blocks - 1) {
+ $block = int($start / ($block_size << 20));
+ $state = `cat $memdir/memory$block/state`;
+ chomp($state);
+ if ($online && $state eq "offline") {
+ if (chmem_online($block)) {
+ printf(STDERR "chmem: Could not set ".
+ "0x%016x-0x%016x online\n", $start,
+ $start + ($block_size << 20) - 1);
+ $ret = 1;
+ }
+ }
+ if ($offline && $state eq "online") {
+ if (chmem_offline($block)) {
+ printf(STDERR "chmem: Could not set ".
+ "0x%016x-0x%016x offline\n", $start,
+ $start + ($block_size << 20) - 1);
+ $ret = 1;
+ }
+ }
+ $start += $block_size << 20;
+ }
+}
+
+sub chmem_check()
+{
+ unless (-d $memdir) {
+ die "chmem: No memory hotplug interface in sysfs ($memdir).\n";
+ }
+ $block_size = `cat $memdir/block_size_bytes`;
+ chomp($block_size);
+ if ($block_size =~ /(?:0x)?([[:xdigit:]]+)/) {
+ $block_size = unpack("Q", pack("H16",
+ substr("0" x 16 . $1, -16)));
+ $block_size = $block_size >> 20;
+ } else {
+ die "chmem: Unknown block size format in sysfs.\n";
+ }
+ if ($online == 0 && $offline == 0) {
+ die "chmem: Please specify one of the options -e or -d.\n";
+ }
+ if ($online == 1 && $offline == 1) {
+ die "chmem: You cannot specify both options -e and -d.\n";
+ }
+
+ while (-d "$memdir/memory$total_blocks") {
+ $total_blocks++;
+ }
+ chmem_get_dev_size();
+ if ($dev_size >= $block_size) {
+ $blocks_per_dev = int($dev_size / $block_size);
+ } else {
+ $devs_per_block = int($block_size / $dev_size);
+ }
+}
+
+sub chmem_action()
+{
+ my ($start, $end, $size, $unit);
+
+ if (!defined($ARGV[0])) {
+ die "chmem: Missing size or range.\n";
+ }
+ if ($ARGV[0] =~ /^0x([[:xdigit:]]+)-0x([[:xdigit:]]+)$/) {
+ $start = unpack("Q", pack("H16", substr("0" x 16 . $1, -16)));
+ $end = unpack("Q", pack("H16", substr("0" x 16 . $2, -16)));
+ if ($start % ($block_size << 20) ||
+ ($end + 1) % ($block_size << 20)) {
+ die "chmem: Start address and (end address + 1) must ".
+ "be aligned to memory block size ($block_size MB).\n";
+ }
+ chmem_range($start, $end);
+ } else {
+ if ($ARGV[0] =~ m/^(\d+)([mg]?)$/i) {
+ $size = $1;
+ $unit = $2 || "";
+ if ($unit =~ /g/i) {
+ $size = $size << 10;
+ }
+ if ($size % $block_size) {
+ die "chmem: Size must be aligned to memory ".
+ "block size ($block_size MB).\n";
+ }
+ chmem_size($size);
+ } else {
+ printf(STDERR "chmem: Invalid size or range: %s\n",
+ $ARGV[0]);
+ exit 1;
+ }
+ }
+}
+
+
+# Main
+unless (GetOptions('v|version' => sub {chmem_version(); exit 0;},
+ 'h|help' => sub {chmem_usage(); exit 0;},
+ 'e|enable' => \$online,
+ 'd|disable' => \$offline)) {
+ die "Try '$script_name --help' for more information.\n";
+};
+
+chmem_read_devices();
+chmem_check();
+chmem_action();
+exit $ret;
diff --git a/zconf/chmem.8 b/zconf/chmem.8
new file mode 100644
index 0000000..34bea3c
--- /dev/null
+++ b/zconf/chmem.8
@@ -0,0 +1,71 @@
+.TH CHMEM 8 "Apr 2010" "s390-tools"
+.
+.
+.SH NAME
+chmem \- set memory online or offline.
+.
+.SH SYNOPSIS
+.B chmem
+.RB OPTIONS
+.RB [SIZE|RANGE]
+.
+.
+.SH DESCRIPTION
+The chmem command sets a particular size or range of memory online or offline.
+.
+.IP "\(hy" 2
+Specify SIZE as <size>[m|M|g|G]. With m or M, <size> specifies the memory
+size in MB (1024 x 1024 bytes). With g or G, <size> specifies the memory size
+in GB (1024 x 1024 x 1024 bytes). The default unit is MB.
+.
+.IP "\(hy" 2
+Specify RANGE in the form 0x<start>-0x<end> as shown in the output of the
+lsmem command. <start> is the hexadecimal address of the first byte and <end>
+is the hexadecimal address of the last byte in the memory range.
+.
+.PP
+SIZE and RANGE must be aligned to the Linux memory block size, as shown in
+the output of the lsmem command.
+
+Setting memory online can fail if the hypervisor does not have enough memory
+left, for example because memory was overcommitted. Setting memory offline can
+fail if Linux cannot free the memory. If only part of the requested memory can
+be set online or offline, a message tells you how much memory was set online
+or offline instead of the requested amount.
+.
+.
+.SH OPTIONS
+.TP
+.BR \-h ", " \-\-help
+Print a short help text, then exit.
+.
+.TP
+.BR \-v ", " \-\-version
+Print the version number, then exit.
+.
+.TP
+.BR \-e ", " \-\-enable
+Set the given RANGE or SIZE of memory online.
+.
+.TP
+.BR \-d ", " \-\-disable
+Set the given RANGE or SIZE of memory offline.
+.
+.
+.SH EXAMPLES
+.TP
+.B chmem --enable 1024
+This command requests 1024 MB of memory to be set online.
+.
+.TP
+.B chmem -e 2g
+This command requests 2 GB of memory to be set online.
+.
+.TP
+.B chmem --disable 0x00000000e4000000-0x00000000f3ffffff
+This command requests the memory range starting with 0x00000000e4000000
+and ending with 0x00000000f3ffffff to be set offline.
+.
+.
+.SH SEE ALSO
+.BR lsmem (8)
diff --git a/zconf/lsmem b/zconf/lsmem
new file mode 100644
index 0000000..e6ed1fa
--- /dev/null
+++ b/zconf/lsmem
@@ -0,0 +1,158 @@
+#!/usr/bin/perl
+###############################################################################
+# lsmem - script to show memory hotplug status.
+#
+# Copyright IBM Corp. 2010
+# Author(s): Gerald Schaefer <gerald.schaefer@de.ibm.com>
+###############################################################################
+
+use strict;
+use warnings;
+use Getopt::Long qw(:config no_ignore_case no_auto_abbrev);
+use File::Basename;
+
+my $script_name = fileparse($0);
+my $memdir = "/sys/devices/system/memory";
+my $block_size = 0;
+my $list_all = 0;
+my $dev_size = 0;
+
+
+sub lsmem_read_attr($$$$)
+# parameters: state, rem, device, block_nr
+{
+ my @attributes = qw(state removable phys_device);
+ foreach (0..2) {
+ $_[$_] = `cat $memdir/memory$_[3]/$attributes[$_]`;
+ chomp($_[$_]);
+ }
+}
+
+sub lsmem_get_dev_size()
+{
+ my $i = 0;
+ my ($device, $old_device) = (0, 0);
+
+ while (-d "$memdir/memory$i") {
+ $device = `cat $memdir/memory$i/phys_device`;
+ chomp($device);
+ if ($device > $old_device) {
+ $dev_size = int($dev_size / ($device - $old_device));
+ last;
+ }
+ $dev_size += $block_size;
+ $i++;
+ }
+}
+
+sub lsmem_list()
+{
+ my $i = 0;
+ my ($start, $end, $size) = (0, 0, 0);
+ my ($state, $old_state) = (0, 0);
+ my ($rem, $old_rem) = (0, 0);
+ my ($device, $old_device) = (0, 0);
+ my ($mem_online, $mem_offline) = (0, 0);
+ my ($last_block, $end_dev) = (0, 0);
+
+ if (-d "$memdir/memory0") {
+ lsmem_read_attr($old_state, $old_rem, $old_device, 0);
+ } else {
+ die "lsmem: No memory hotplug interface in sysfs ($memdir).\n";
+ }
+
+ $block_size = `cat $memdir/block_size_bytes`;
+ chomp($block_size);
+ if ($block_size =~ /(?:0x)?([[:xdigit:]]+)/) {
+ $block_size = unpack("Q", pack("H16",
+ substr("0" x 16 . $1, -16)));
+ $block_size = $block_size >> 20;
+ } else {
+ die "lsmem: Unknown block size format in sysfs.\n";
+ }
+ lsmem_get_dev_size();
+
+ print <<HERE;
+Address Range Size (MB) State Removable Device
+===============================================================================
+HERE
+ while (-d "$memdir/memory$i") {
+ $i++;
+ if (-d "$memdir/memory$i") {
+ lsmem_read_attr($state, $rem, $device, $i);
+ } else {
+ $last_block = 1;
+ }
+ if ($state ne $old_state || $rem != $old_rem || $list_all ||
+ $last_block) {
+ $end = $i * ($block_size << 20) - 1;
+ $size = ($end - $start + 1) >> 20;
+ if ($old_state eq "going-offline") {
+ $old_state = "on->off";
+ }
+ printf("0x%016x-0x%016x %10lu %-7s ", $start, $end,
+ $size, $old_state);
+ if ($old_state eq "online") {
+ printf(" %-9s ", $old_rem ? "yes" : "no");
+ $mem_online += $size;
+ } else {
+ printf(" %-9s ", "-");
+ $mem_offline += $size;
+ }
+ $end_dev = ($end / $dev_size) >> 20;
+ if ($old_device == $end_dev) {
+ printf("%d\n", $old_device);
+ } else {
+ printf("%d-%d\n", $old_device, $end_dev);
+ }
+ $old_state = $state;
+ $old_rem = $rem;
+ $old_device = $device;
+ $start = $end + 1;
+ }
+ }
+ printf("\n");
+ printf("Memory device size : %lu MB\n", $dev_size);
+ printf("Memory block size : %lu MB\n", $block_size);
+ printf("Total online memory : %lu MB\n", $mem_online);
+ printf("Total offline memory: %lu MB\n", $mem_offline);
+}
+
+sub lsmem_usage()
+{
+ print <<HERE;
+Usage: $script_name [OPTIONS]
+
+The $script_name command lists the ranges of available memory with their online
+status. The listed memory blocks correspond to the memory block representation
+in sysfs. The command also shows the memory block size, the device size, and
+the amount of memory in online and offline state.
+
+OPTIONS
+ -a, --all
+ List each individual memory block, instead of combining memory blocks
+ with similar attributes.
+
+ -h, --help
+ Print a short help text, then exit.
+
+ -v, --version
+ Print the version number, then exit.
+HERE
+}
+
+sub lsmem_version()
+{
+ print "$script_name: version %S390_TOOLS_VERSION%\n";
+ print "Copyright IBM Corp. 2010\n";
+}
+
+
+# Main
+unless (GetOptions('v|version' => sub {lsmem_version(); exit 0;},
+ 'h|help' => sub {lsmem_usage(); exit 0;},
+ 'a|all' => \$list_all)) {
+ die "Try '$script_name --help' for more information.\n";
+};
+
+lsmem_list();
diff --git a/zconf/lsmem.8 b/zconf/lsmem.8
new file mode 100644
index 0000000..ed052ea
--- /dev/null
+++ b/zconf/lsmem.8
@@ -0,0 +1,69 @@
+.TH LSMEM 8 "Apr 2010" s390\-tools
+.
+.
+.SH NAME
+lsmem \- list the ranges of available memory with their online status.
+.
+.
+.SH SYNOPSIS
+.B lsmem
+.RB [OPTIONS]
+.
+.
+.SH DESCRIPTION
+The lsmem command lists the ranges of available memory with their online
+status. The listed memory blocks correspond to the memory block representation
+in sysfs. The command also shows the memory block size, the device size, and
+the amount of memory in online and offline state.
+.
+.SS "Column description"
+.
+.TP 4
+Address Range
+Start and end address of the memory range.
+.
+.TP 4
+Size
+Size of the memory range in MB (1024 x 1024 bytes).
+.
+.TP 4
+State
+Indication of the online status of the memory range. State on->off means
+that the address range is in transition from online to offline.
+.
+.TP 4
+Removable
+"yes" if the memory range can be set offline, "no" if it cannot be set offline.
+A dash ("\-") means that the range is already offline.
+.
+.TP 4
+Device
+Device number or numbers that correspond to the memory range.
+
+Each device represents a memory unit for the hypervisor in control of the
+memory. The hypervisor cannot reuse a memory unit unless the corresponding
+memory range is completely offline. For best memory utilization, each device
+should either be completely online or completely offline.
+
+The chmem command with the size parameter automatically chooses the best suited
+device or devices when setting memory online or offline. The device size depends
+on the hypervisor and on the amount of total online and offline memory.
+.
+.
+.SH OPTIONS
+.TP
+.BR \-a ", " \-\-all
+List each individual memory block, instead of combining memory blocks with
+similar attributes.
+.
+.TP
+.BR \-h ", " \-\-help
+Print a short help text, then exit.
+.
+.TP
+.BR \-v ", " \-\-version
+Print the version number, then exit.
+.
+.
+.SH SEE ALSO
+.BR chmem (8)
--
1.7.3.5