[PATCH 4/4] kexec/arch/i386: Add get_memory_ranges_xen() function

From: Daniel Kiper
Date: Sun Aug 21 2011 - 10:04:34 EST


get_memory_ranges_sysfs() and get_memory_ranges_proc_iomem()
are unreliable under Xen. Proper machine memory map could be
obtained under Xen by calling __HYPERVISOR_memory_op hypercall
with XENMEM_machine_memory_map argument. get_memory_ranges_xen()
does that. It is implemented using ioctl() or libxenctrl interface.
This solution is compatible with 3.x and 4.x Xen versions.

Signed-off-by: Daniel Kiper <dkiper@xxxxxxxxxxxx>
---
configure.ac | 3 +
kexec/arch/i386/kexec-x86-common.c | 203 ++++++++++++++++++++++++++++++++++--
2 files changed, 197 insertions(+), 9 deletions(-)

diff --git a/configure.ac b/configure.ac
index 172a52a..0d09bba 100644
--- a/configure.ac
+++ b/configure.ac
@@ -157,6 +157,9 @@ if test "$with_xen" = yes ; then
AC_CHECK_HEADER(xenctrl.h,
AC_CHECK_LIB(xenctrl, xc_version, ,
AC_MSG_NOTICE([Xen support disabled])))
+ if test "$ac_cv_lib_xenctrl_xc_version" = yes ; then
+ AC_CHECK_FUNCS(xc_get_machine_memory_map)
+ fi
fi

dnl ---Sanity checks
diff --git a/kexec/arch/i386/kexec-x86-common.c b/kexec/arch/i386/kexec-x86-common.c
index 8f59c6c..609d35d 100644
--- a/kexec/arch/i386/kexec-x86-common.c
+++ b/kexec/arch/i386/kexec-x86-common.c
@@ -17,6 +17,10 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

+#define _XOPEN_SOURCE 600
+#define _BSD_SOURCE
+
+#include <fcntl.h>
#include <stddef.h>
#include <stdio.h>
#include <errno.h>
@@ -24,12 +28,29 @@
#include <string.h>
#include <limits.h>
#include <stdlib.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <unistd.h>
#include "../../kexec.h"
#include "../../kexec-syscall.h"
#include "../../firmware_memmap.h"
#include "../../crashdump.h"
#include "kexec-x86.h"

+#ifdef HAVE_LIBXENCTRL
+#ifdef HAVE_XC_GET_MACHINE_MEMORY_MAP
+#include <xenctrl.h>
+#else
+#define __XEN_TOOLS__ 1
+#include <x86/x86-linux.h>
+#include <xen/xen.h>
+#include <xen/memory.h>
+#include <xen/sys/privcmd.h>
+#endif /* HAVE_XC_GET_MACHINE_MEMORY_MAP */
+#endif /* HAVE_LIBXENCTRL */
+
static struct memory_range memory_range[MAX_MEMORY_RANGES];

/**
@@ -129,6 +150,172 @@ static int get_memory_ranges_sysfs(struct memory_range **range, int *ranges)
return 0;
}

+#ifdef HAVE_LIBXENCTRL
+static unsigned e820_to_kexec_type(uint32_t type)
+{
+ switch (type) {
+ case E820_RAM:
+ return RANGE_RAM;
+ case E820_ACPI:
+ return RANGE_ACPI;
+ case E820_NVS:
+ return RANGE_ACPI_NVS;
+ case E820_RESERVED:
+ default:
+ return RANGE_RESERVED;
+ }
+}
+
+/**
+ * Memory map detection for Xen.
+ *
+ * @param[out] range pointer that will be set to an array that holds the
+ * memory ranges
+ * @param[out] ranges number of ranges valid in @p range
+ *
+ * @return 0 on success, any other value on failure.
+ */
+#ifdef HAVE_XC_GET_MACHINE_MEMORY_MAP
+static int get_memory_ranges_xen(struct memory_range **range, int *ranges)
+{
+ int rc, ret = -1;
+ struct e820entry e820entries[MAX_MEMORY_RANGES];
+ unsigned int i;
+#ifdef XENCTRL_HAS_XC_INTERFACE
+ xc_interface *xc;
+#else
+ int xc;
+#endif
+
+#ifdef XENCTRL_HAS_XC_INTERFACE
+ xc = xc_interface_open(NULL, NULL, 0);
+
+ if (!xc) {
+ fprintf(stderr, "%s: Failed to open Xen control interface\n", __func__);
+ goto err;
+ }
+#else
+ xc = xc_interface_open();
+
+ if (xc == -1) {
+ fprintf(stderr, "%s: Failed to open Xen control interface\n", __func__);
+ goto err;
+ }
+#endif
+
+ rc = xc_get_machine_memory_map(xc, e820entries, MAX_MEMORY_RANGES);
+
+ if (rc < 0) {
+ fprintf(stderr, "%s: xc_get_machine_memory_map: %s\n", __func__, strerror(rc));
+ goto err;
+ }
+
+ for (i = 0; i < rc; ++i) {
+ memory_range[i].start = e820entries[i].addr;
+ memory_range[i].end = e820entries[i].addr + e820entries[i].size;
+ memory_range[i].type = e820_to_kexec_type(e820entries[i].type);
+ }
+
+ qsort(memory_range, rc, sizeof(struct memory_range), compare_ranges);
+
+ *range = memory_range;
+ *ranges = rc;
+
+ ret = 0;
+
+err:
+ xc_interface_close(xc);
+
+ return ret;
+}
+#else
+static int get_memory_ranges_xen(struct memory_range **range, int *ranges)
+{
+ int fd, rc, ret = -1;
+ privcmd_hypercall_t hypercall;
+ struct e820entry *e820entries = NULL;
+ struct xen_memory_map *xen_memory_map = NULL;
+ unsigned int i;
+
+ fd = open("/proc/xen/privcmd", O_RDWR);
+
+ if (fd == -1) {
+ fprintf(stderr, "%s: open(/proc/xen/privcmd): %m\n", __func__);
+ goto err;
+ }
+
+ rc = posix_memalign((void **)&e820entries, sysconf(_SC_PAGESIZE),
+ sizeof(struct e820entry) * MAX_MEMORY_RANGES);
+
+ if (rc) {
+ fprintf(stderr, "%s: posix_memalign(e820entries): %s\n", __func__, strerror(rc));
+ e820entries = NULL;
+ goto err;
+ }
+
+ rc = posix_memalign((void **)&xen_memory_map, sysconf(_SC_PAGESIZE),
+ sizeof(struct xen_memory_map));
+
+ if (rc) {
+ fprintf(stderr, "%s: posix_memalign(xen_memory_map): %s\n", __func__, strerror(rc));
+ xen_memory_map = NULL;
+ goto err;
+ }
+
+ if (mlock(e820entries, sizeof(struct e820entry) * MAX_MEMORY_RANGES) == -1) {
+ fprintf(stderr, "%s: mlock(e820entries): %m\n", __func__);
+ goto err;
+ }
+
+ if (mlock(xen_memory_map, sizeof(struct xen_memory_map)) == -1) {
+ fprintf(stderr, "%s: mlock(xen_memory_map): %m\n", __func__);
+ goto err;
+ }
+
+ xen_memory_map->nr_entries = MAX_MEMORY_RANGES;
+ set_xen_guest_handle(xen_memory_map->buffer, e820entries);
+
+ hypercall.op = __HYPERVISOR_memory_op;
+ hypercall.arg[0] = XENMEM_machine_memory_map;
+ hypercall.arg[1] = (__u64)xen_memory_map;
+
+ rc = ioctl(fd, IOCTL_PRIVCMD_HYPERCALL, &hypercall);
+
+ if (rc == -1) {
+ fprintf(stderr, "%s: ioctl(IOCTL_PRIVCMD_HYPERCALL): %m\n", __func__);
+ goto err;
+ }
+
+ for (i = 0; i < xen_memory_map->nr_entries; ++i) {
+ memory_range[i].start = e820entries[i].addr;
+ memory_range[i].end = e820entries[i].addr + e820entries[i].size;
+ memory_range[i].type = e820_to_kexec_type(e820entries[i].type);
+ }
+
+ qsort(memory_range, xen_memory_map->nr_entries, sizeof(struct memory_range), compare_ranges);
+
+ *range = memory_range;
+ *ranges = xen_memory_map->nr_entries;
+
+ ret = 0;
+
+err:
+ munlock(xen_memory_map, sizeof(struct xen_memory_map));
+ munlock(e820entries, sizeof(struct e820entry) * MAX_MEMORY_RANGES);
+ free(xen_memory_map);
+ free(e820entries);
+ close(fd);
+
+ return ret;
+}
+#endif /* HAVE_XC_GET_MACHINE_MEMORY_MAP */
+#else
+static int get_memory_ranges_xen(struct memory_range **range, int *ranges)
+{
+ return 0;
+}
+#endif /* HAVE_LIBXENCTRL */
+
static void remove_range(struct memory_range *range, int nr_ranges, int index)
{
int i, j;
@@ -242,23 +429,21 @@ int get_memory_ranges(struct memory_range **range, int *ranges,
{
int ret, i;

- /*
- * When using Xen, /sys/firmware/memmap (i.e., the E820 map) is
- * wrong, it just provides one large memory are and that cannot
- * be used for Kdump. Use always the /proc/iomem interface there
- * even if we have /sys/firmware/memmap. Without that, /proc/vmcore
- * is empty in the kdump kernel.
- */
if (!efi_map_added() && !xen_present() && have_sys_firmware_memmap()) {
ret = get_memory_ranges_sysfs(range, ranges);
if (!ret)
ret = fixup_memory_ranges(range, ranges);
+ } else if (xen_present()) {
+ ret = get_memory_ranges_xen(range, ranges);
+ if (!ret)
+ ret = fixup_memory_ranges(range, ranges);
} else
ret = get_memory_ranges_proc_iomem(range, ranges);

/*
- * both get_memory_ranges_sysfs() and get_memory_ranges_proc_iomem()
- * have already printed an error message, so fail silently here
+ * get_memory_ranges_sysfs(), get_memory_ranges_proc_iomem() and
+ * get_memory_ranges_xen() have already printed an error message,
+ * so fail silently here.
*/
if (ret != 0)
return ret;
--
1.5.6.5
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/