[PATCH] kexec-tools: s390: Fix memory detection for memory hotplug
Michael Holzheu
holzheu at linux.vnet.ibm.com
Fri Oct 28 09:35:35 EDT 2011
Hello Simon,
Here comes the patch...
On Thu, 2011-10-27 at 07:31 +0900, Simon Horman wrote:
> On Tue, Oct 25, 2011 at 07:17:17PM +0200, Michael Holzheu wrote:
> > To fix this I could parse /sys/devices/system/memory and exclude each
> > memory chunk that in not online from the /proc/iomem info. Do you think
> > that this approach is fine or is there a better solution?
>
> Hi Michael,
>
> that sounds like a reasonable approach to me.
> IIRC, kexec xen on ia64 makes use of an alternate iomem file,
> and this seems to be another example of /proc/iomem not being
> the right source of information.
From: Michael Holzheu <holzheu at linux.vnet.ibm.com>
Currently on s390 for memory detection only the "/proc/iomem" file is used.
This file does not include information on offlined memory chunks. With this
patch the memory hotplug information is read from "/sys/devices/system/memory"
and is added to the "/proc/iomem" info.
Also the MAX_MEMORY_RANGES count is increased to 1024 in order to support
systems with many memory holes.
Signed-off-by: Michael Holzheu <holzheu at linux.vnet.ibm.com>
---
kexec/arch/s390/kexec-s390.c | 122 ++++++++++++++++++++++++++++++++++++++++++-
kexec/arch/s390/kexec-s390.h | 3 -
2 files changed, 123 insertions(+), 2 deletions(-)
--- a/kexec/arch/s390/kexec-s390.c
+++ b/kexec/arch/s390/kexec-s390.c
@@ -11,10 +11,13 @@
#define _GNU_SOURCE
#include <stddef.h>
#include <stdio.h>
+#include <stdlib.h>
#include <errno.h>
#include <stdint.h>
#include <string.h>
#include <getopt.h>
+#include <sys/types.h>
+#include <dirent.h>
#include "../../kexec.h"
#include "../../kexec-syscall.h"
#include "kexec-s390.h"
@@ -23,6 +26,122 @@
static struct memory_range memory_range[MAX_MEMORY_RANGES];
/*
+ * Read string from file
+ */
+static void read_str(char *string, const char *path, size_t len)
+{
+ size_t rc;
+ FILE *fh;
+
+ fh = fopen(path, "rb");
+ if (fh == NULL)
+ die("Could not open \"%s\"", path);
+ rc = fread(string, 1, len - 1, fh);
+ if (rc == 0 && ferror(fh))
+ die("Could not read \"%s\"", path);
+ fclose(fh);
+ string[rc] = 0;
+ if (string[strlen(string) - 1] == '\n')
+ string[strlen(string) - 1] = 0;
+}
+
+/*
+ * Return number of memory chunks
+ */
+static int memory_range_cnt(struct memory_range chunks[])
+{
+ int i;
+
+ for (i = 0; i < MAX_MEMORY_RANGES; i++) {
+ if (chunks[i].end == 0)
+ break;
+ }
+ return i;
+}
+
+/*
+ * Create memory hole with given address and size
+ *
+ * lh = local hole
+ */
+static void add_mem_hole(struct memory_range chunks[], unsigned long addr,
+ unsigned long size)
+{
+ unsigned long lh_start, lh_end, lh_size, chunk_cnt;
+ int i;
+
+ chunk_cnt = memory_range_cnt(chunks);
+
+ for (i = 0; i < chunk_cnt; i++) {
+ if (addr + size <= chunks[i].start)
+ break;
+ if (addr > chunks[i].end)
+ continue;
+ lh_start = MAX(addr, chunks[i].start);
+ lh_end = MIN(addr + size - 1, chunks[i].end);
+ lh_size = lh_end - lh_start + 1;
+ if (lh_start == chunks[i].start && lh_end == chunks[i].end) {
+ /* Remove chunk */
+ memmove(&chunks[i], &chunks[i + 1],
+ sizeof(struct memory_range) *
+ (MAX_MEMORY_RANGES - (i + 1)));
+ memset(&chunks[MAX_MEMORY_RANGES - 1], 0,
+ sizeof(struct memory_range));
+ chunk_cnt--;
+ i--;
+ } else if (lh_start == chunks[i].start) {
+ /* Make chunk smaller at start */
+ chunks[i].start = chunks[i].start + lh_size;
+ break;
+ } else if (lh_end == chunks[i].end) {
+ /* Make chunk smaller at end */
+ chunks[i].end = lh_start - 1;
+ } else {
+ /* Split chunk into two */
+ if (chunk_cnt >= MAX_MEMORY_RANGES)
+ die("Unable to create memory hole: %i", i);
+ memmove(&chunks[i + 1], &chunks[i],
+ sizeof(struct memory_range) *
+ (MAX_MEMORY_RANGES - (i + 1)));
+ chunks[i + 1].start = lh_start + lh_size;
+ chunks[i].end = lh_start - 1;
+ break;
+ }
+ }
+}
+
+/*
+ * Remove offline memory from memory chunks
+ */
+static void remove_offline_memory(struct memory_range memory_range[])
+{
+ unsigned long block_size, chunk_nr;
+ struct dirent *dirent;
+ char path[PATH_MAX];
+ char str[64];
+ DIR *dir;
+
+ read_str(str, "/sys/devices/system/memory/block_size_bytes",
+ sizeof(str));
+ sscanf(str, "%lx", &block_size);
+
+ dir = opendir("/sys/devices/system/memory");
+ if (!dir)
+ die("Could not read \"/sys/devices/system/memory\"");
+ while ((dirent = readdir(dir))) {
+ if (sscanf(dirent->d_name, "memory%ld\n", &chunk_nr) != 1)
+ continue;
+ sprintf(path, "/sys/devices/system/memory/%s/state",
+ dirent->d_name);
+ read_str(str, path, sizeof(str));
+ if (strncmp(str, "offline", 6) != 0)
+ continue;
+ add_mem_hole(memory_range, chunk_nr * block_size, block_size);
+ }
+ closedir(dir);
+}
+
+/*
* Get memory ranges of type "System RAM" from /proc/iomem. If with_crashk=1
* then also type "Crash kernel" is added.
*/
@@ -66,7 +185,8 @@ int get_memory_ranges_s390(struct memory
}
}
fclose(fp);
- *ranges = current_range;
+ remove_offline_memory(memory_range);
+ *ranges = memory_range_cnt(memory_range);
return 0;
}
--- a/kexec/arch/s390/kexec-s390.h
+++ b/kexec/arch/s390/kexec-s390.h
@@ -19,10 +19,11 @@
#define OLDMEM_SIZE_OFFS 0x420
#define COMMAND_LINE_OFFS 0x480
#define COMMAND_LINESIZE 896
-#define MAX_MEMORY_RANGES 64
+#define MAX_MEMORY_RANGES 1024
#define ALIGN_UP(addr, size) (((addr) + ((size)-1)) & (~((size)-1)))
#define MAX(x, y) ((x) > (y) ? (x) : (y))
+#define MIN(x, y) ((x) < (y) ? (x) : (y))
extern int image_s390_load(int, char **, const char *, off_t, struct kexec_info *);
extern int image_s390_probe(const char *, off_t);
More information about the kexec
mailing list