diff mbox

[v2,05/13] xen-xsplice: Tool to manipulate xsplice payloads (v3)

Message ID 1452808031-706-6-git-send-email-konrad.wilk@oracle.com (mailing list archive)
State New, archived
Headers show

Commit Message

Konrad Rzeszutek Wilk Jan. 14, 2016, 9:47 p.m. UTC
A simple tool that allows an system admin to perform
basic xsplice operations:

 - Upload a xsplice file (with an unique id)
 - List all the xsplice payloads loaded.
 - Apply, revert, replace, unload, or check the payload using the
   unique id.
 - Do all three - upload, check, and apply the
   payload in one go (load). Also will use the name of the
   file as the <id>

Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Signed-off-by: Ross Lagerwall <ross.lagerwall@citrix.com>
---
v2:
 - Removed REVERTED state.
 - Fixed bugs handling XSPLICE_STATUS_PROGRESS.
 - Split status into state and error.
   Add REPLACE action.
v3:
 - Utilize the timeout and use the default one (let the hypervisor
   pick it).
 - Change the s/all/load and infer the <id> from name of file.
---
 .gitignore               |   1 +
 tools/misc/Makefile      |   4 +
 tools/misc/xen-xsplice.c | 475 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 480 insertions(+)
 create mode 100644 tools/misc/xen-xsplice.c

Comments

Wei Liu Jan. 19, 2016, 11:14 a.m. UTC | #1
On Thu, Jan 14, 2016 at 04:47:03PM -0500, Konrad Rzeszutek Wilk wrote:
[...]
> +/* This value was choosen adhoc. It could be 42 too. */
> +#define MAX_LEN 11
> +static int list_func(int argc, char *argv[])
> +{
> +    unsigned int idx, done, left, i;
> +    xen_xsplice_status_t *info = NULL;
> +    char *id = NULL;
> +    uint32_t *len = NULL;
> +    int rc = ENOMEM;
> +
> +    if ( argc )
> +    {
> +        show_help();
> +        return -1;
> +    }
> +    idx = left = 0;
> +    info = malloc(sizeof(*info) * MAX_LEN);
> +    if ( !info )
> +        goto out;
> +    id = malloc(sizeof(*id) * XEN_XSPLICE_NAME_SIZE * MAX_LEN);
> +    if ( !id )
> +        goto out;
> +    len = malloc(sizeof(*len) * MAX_LEN);
> +    if ( !len )
> +        goto out;
> +
> +    fprintf(stdout," ID                                     | status\n"
> +                   "----------------------------------------+------------\n");
> +    do {
> +        done = 0;
> +        memset(info, 'A', sizeof(*info) * MAX_LEN); /* Optional. */
> +        memset(id, 'i', sizeof(*id) * MAX_LEN * XEN_XSPLICE_NAME_SIZE); /* Optional. */

Line too long.

[...]
> +static int upload_func(int argc, char *argv[])
> +{
> +    char *filename;
> +    char id[XEN_XSPLICE_NAME_SIZE];
> +    int fd = 0, rc;
> +    struct stat buf;
> +    unsigned char *fbuf;
> +    ssize_t len;
> +    DECLARE_HYPERCALL_BUFFER(char, payload);
> +

I don't think you need to declare hypercall buffer here in the utility.
It should be libxc's responsibility to bounce the buffer accordingly.


Wei.
Ross Lagerwall Jan. 19, 2016, 2:30 p.m. UTC | #2
On 01/14/2016 09:47 PM, Konrad Rzeszutek Wilk wrote:
> A simple tool that allows an system admin to perform
> basic xsplice operations:
>
>   - Upload a xsplice file (with an unique id)

s/id/name throughout the rest of this patch
diff mbox

Patch

diff --git a/.gitignore b/.gitignore
index e0df903..2528d8f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -169,6 +169,7 @@  tools/misc/xc_shadow
 tools/misc/xen_cpuperf
 tools/misc/xen-detect
 tools/misc/xen-tmem-list-parse
+tools/misc/xen-xsplice
 tools/misc/xenperf
 tools/misc/xenpm
 tools/misc/xen-hvmctx
diff --git a/tools/misc/Makefile b/tools/misc/Makefile
index c4490f3..c46873e 100644
--- a/tools/misc/Makefile
+++ b/tools/misc/Makefile
@@ -30,6 +30,7 @@  INSTALL_SBIN                   += xenlockprof
 INSTALL_SBIN                   += xenperf
 INSTALL_SBIN                   += xenpm
 INSTALL_SBIN                   += xenwatchdogd
+INSTALL_SBIN                   += xen-xsplice
 INSTALL_SBIN += $(INSTALL_SBIN-y)
 
 # Everything to be installed in a private bin/
@@ -98,6 +99,9 @@  xen-mfndump: xen-mfndump.o
 xenwatchdogd: xenwatchdogd.o
 	$(CC) $(LDFLAGS) -o $@ $< $(LDLIBS_libxenctrl) $(APPEND_LDFLAGS)
 
+xen-xsplice: xen-xsplice.o
+	$(CC) $(LDFLAGS) -o $@ $< $(LDLIBS_libxenctrl) $(APPEND_LDFLAGS)
+
 xen-lowmemd: xen-lowmemd.o
 	$(CC) $(LDFLAGS) -o $@ $< $(LDLIBS_libxenctrl) $(LDLIBS_libxenstore) $(APPEND_LDFLAGS)
 
diff --git a/tools/misc/xen-xsplice.c b/tools/misc/xen-xsplice.c
new file mode 100644
index 0000000..0c7f4da
--- /dev/null
+++ b/tools/misc/xen-xsplice.c
@@ -0,0 +1,475 @@ 
+/*
+ * Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved.
+ */
+
+#include <fcntl.h>
+#include <libgen.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <xenctrl.h>
+#include <xenstore.h>
+
+static xc_interface *xch;
+
+void show_help(void)
+{
+    fprintf(stderr,
+            "xen-xsplice: Xsplice test tool\n"
+            "Usage: xen-xsplice <command> [args]\n"
+            " <id> An unique name of payload. Up to %d characters.\n"
+            "Commands:\n"
+            "  help                 display this help\n"
+            "  upload <id> <file>   upload file <file> with <id> name\n"
+            "  list                 list payloads uploaded.\n"
+            "  apply <id>           apply <id> patch.\n"
+            "  revert <id>          revert id <id> patch.\n"
+            "  replace <id>         apply <id> patch and revert all others.\n"
+            "  unload <id>          unload id <id> patch.\n"
+            "  check <id>           check id <id> patch.\n"
+            "  load  <file>         upload, check and apply <file>.\n"
+            "                       id is the <file> name\n",
+            XEN_XSPLICE_NAME_SIZE);
+}
+
+/* wrapper function */
+static int help_func(int argc, char *argv[])
+{
+    show_help();
+    return 0;
+}
+
+#define ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0]))
+
+static const char *state2str(long state)
+{
+#define STATE(x) [XSPLICE_STATE_##x] = #x
+    static const char *const names[] = {
+            STATE(LOADED),
+            STATE(CHECKED),
+            STATE(APPLIED),
+    };
+#undef STATE
+    if (state >= ARRAY_SIZE(names))
+        return "unknown";
+
+    if (state < 0)
+        return "-EXX";
+
+    if (!names[state])
+        return "unknown";
+
+    return names[state];
+}
+
+/* This value was choosen adhoc. It could be 42 too. */
+#define MAX_LEN 11
+static int list_func(int argc, char *argv[])
+{
+    unsigned int idx, done, left, i;
+    xen_xsplice_status_t *info = NULL;
+    char *id = NULL;
+    uint32_t *len = NULL;
+    int rc = ENOMEM;
+
+    if ( argc )
+    {
+        show_help();
+        return -1;
+    }
+    idx = left = 0;
+    info = malloc(sizeof(*info) * MAX_LEN);
+    if ( !info )
+        goto out;
+    id = malloc(sizeof(*id) * XEN_XSPLICE_NAME_SIZE * MAX_LEN);
+    if ( !id )
+        goto out;
+    len = malloc(sizeof(*len) * MAX_LEN);
+    if ( !len )
+        goto out;
+
+    fprintf(stdout," ID                                     | status\n"
+                   "----------------------------------------+------------\n");
+    do {
+        done = 0;
+        memset(info, 'A', sizeof(*info) * MAX_LEN); /* Optional. */
+        memset(id, 'i', sizeof(*id) * MAX_LEN * XEN_XSPLICE_NAME_SIZE); /* Optional. */
+        memset(len, 'l', sizeof(*len) * MAX_LEN); /* Optional. */
+        rc = xc_xsplice_list(xch, MAX_LEN, idx, info, id, len, &done, &left);
+        if ( rc )
+        {
+            fprintf(stderr, "Failed to list %d/%d: %d(%s)!\n", idx, left, errno, strerror(errno));
+            break;
+        }
+        for ( i = 0; i < done; i++ )
+        {
+            unsigned int j;
+            uint32_t sz;
+            char *str;
+
+            sz = len[i];
+            str = id + (i * XEN_XSPLICE_NAME_SIZE);
+            for ( j = sz; j < XEN_XSPLICE_NAME_SIZE; j++ )
+                str[j] = '\0';
+
+            printf("%-40s| %s", str, state2str(info[i].state));
+            if ( info[i].rc )
+                printf(" (%d, %s)\n", -info[i].rc, strerror(-info[i].rc));
+            else
+                puts("");
+        }
+        idx += done;
+    } while ( left );
+
+out:
+    free(id);
+    free(info);
+    free(len);
+    return rc;
+}
+#undef MAX_LEN
+
+static int get_id(int argc, char *argv[], char *id)
+{
+    ssize_t len = strlen(argv[0]);
+    if ( len > XEN_XSPLICE_NAME_SIZE )
+    {
+        fprintf(stderr, "ID MUST be %d characters!\n", XEN_XSPLICE_NAME_SIZE);
+        errno = EINVAL;
+        return errno;
+    }
+    /* Don't want any funny strings from the stack. */
+    memset(id, 0, XEN_XSPLICE_NAME_SIZE);
+    strncpy(id, argv[0], len);
+    return 0;
+}
+
+static int upload_func(int argc, char *argv[])
+{
+    char *filename;
+    char id[XEN_XSPLICE_NAME_SIZE];
+    int fd = 0, rc;
+    struct stat buf;
+    unsigned char *fbuf;
+    ssize_t len;
+    DECLARE_HYPERCALL_BUFFER(char, payload);
+
+    if ( argc != 2 )
+    {
+        show_help();
+        return -1;
+    }
+
+    if ( get_id(argc, argv, id) )
+        return EINVAL;
+
+    filename = argv[1];
+    fd = open(filename, O_RDONLY);
+    if ( fd < 0 )
+    {
+        fprintf(stderr, "Could not open %s, error: %d(%s)\n",
+                filename, errno, strerror(errno));
+        return errno;
+    }
+    if ( stat(filename, &buf) != 0 )
+    {
+        fprintf(stderr, "Could not get right size %s, error: %d(%s)\n",
+                filename, errno, strerror(errno));
+        close(fd);
+        return errno;
+    }
+
+    len = buf.st_size;
+    fbuf = mmap(0, len, PROT_READ, MAP_PRIVATE, fd, 0);
+    if ( fbuf == MAP_FAILED )
+    {
+        fprintf(stderr,"Could not map: %s, error: %d(%s)\n",
+                filename, errno, strerror(errno));
+        close (fd);
+        return errno;
+    }
+    printf("Uploading %s (%zu bytes)\n", filename, len);
+    payload = xc_hypercall_buffer_alloc(xch, payload, len);
+    memcpy(payload, fbuf, len);
+
+    rc = xc_xsplice_upload(xch, id, payload, len);
+    if ( rc )
+    {
+        fprintf(stderr, "Upload failed: %s, error: %d(%s)!\n",
+                filename, errno, strerror(errno));
+        goto out;
+    }
+    xc_hypercall_buffer_free(xch, payload);
+
+out:
+    if ( munmap( fbuf, len) )
+    {
+        fprintf(stderr, "Could not unmap!? error: %d(%s)!\n",
+                errno, strerror(errno));
+        rc = errno;
+    }
+    close(fd);
+
+    return rc;
+}
+
+/* These MUST match to the 'action_options[]' array slots. */
+enum {
+    ACTION_APPLY = 0,
+    ACTION_REVERT = 1,
+    ACTION_UNLOAD = 2,
+    ACTION_CHECK = 3,
+    ACTION_REPLACE = 4,
+};
+
+struct {
+    int allow; /* State it must be in to call function. */
+    int expected; /* The state to be in after the function. */
+    const char *name;
+    int (*function)(xc_interface *xch, char *id, uint32_t timeout);
+    unsigned int executed; /* Has the function been called?. */
+} action_options[] = {
+    {   .allow = XSPLICE_STATE_CHECKED,
+        .expected = XSPLICE_STATE_APPLIED,
+        .name = "apply",
+        .function = xc_xsplice_apply,
+    },
+    {   .allow = XSPLICE_STATE_APPLIED,
+        .expected = XSPLICE_STATE_CHECKED,
+        .name = "revert",
+        .function = xc_xsplice_revert,
+    },
+    {   .allow = XSPLICE_STATE_CHECKED | XSPLICE_STATE_LOADED,
+        .expected = -ENOENT,
+        .name = "unload",
+        .function = xc_xsplice_unload,
+    },
+    {   .allow = XSPLICE_STATE_CHECKED | XSPLICE_STATE_LOADED,
+        .expected = XSPLICE_STATE_CHECKED,
+        .name = "check",
+        .function = xc_xsplice_check
+    },
+    {   .allow = XSPLICE_STATE_CHECKED,
+        .expected = XSPLICE_STATE_APPLIED,
+        .name = "replace",
+        .function = xc_xsplice_replace,
+    },
+};
+
+/* Go around 300 * 0.1 seconds = 30 seconds. */
+#define RETRIES 300
+/* aka 0.1 second */
+#define DELAY 100000
+
+int action_func(int argc, char *argv[], unsigned int idx)
+{
+    char id[XEN_XSPLICE_NAME_SIZE];
+    int rc, original_state;
+    xen_xsplice_status_t status;
+    unsigned int retry = 0;
+
+    if ( argc != 1 )
+    {
+        show_help();
+        return -1;
+    }
+
+    if ( idx >= ARRAY_SIZE(action_options) )
+        return -1;
+
+    if ( get_id(argc, argv, id) )
+        return EINVAL;
+
+    /* Check initial status. */
+    rc = xc_xsplice_get(xch, id, &status);
+    if ( rc )
+        goto err;
+
+    if ( status.rc == -EAGAIN )
+    {
+        printf("%s failed. Operation already in progress\n", id);
+        return -1;
+    }
+
+    if ( status.state == action_options[idx].expected )
+    {
+        printf("No action needed\n");
+        return 0;
+    }
+
+    /* Perform action. */
+    if ( action_options[idx].allow & status.state )
+    {
+        printf("Performing %s:", action_options[idx].name);
+        rc = action_options[idx].function(xch, id, 0);
+        if ( rc )
+            goto err;
+    }
+    else
+    {
+        printf("%s: in wrong state (%s), expected (%s)\n",
+               id, state2str(status.state),
+               state2str(action_options[idx].expected));
+        return -1;
+    }
+
+    original_state = status.state;
+    do {
+        rc = xc_xsplice_get(xch, id, &status);
+        if ( rc )
+        {
+            rc = -errno;
+            break;
+        }
+
+        if ( status.state != original_state )
+            break;
+        if ( status.rc && status.rc != -EAGAIN )
+        {
+            rc = status.rc;
+            break;
+        }
+
+        printf(".");
+        fflush(stdout);
+        usleep(DELAY);
+    } while ( ++retry < RETRIES );
+
+    if ( retry >= RETRIES )
+    {
+        printf("%s: Operation didn't complete after 30 seconds.\n", id);
+        return -1;
+    }
+    else
+    {
+        if ( rc == 0 )
+            rc = status.state;
+
+        if ( action_options[idx].expected == rc )
+            printf(" completed\n");
+        else if ( rc < 0 )
+        {
+            printf("%s failed with %d(%s)\n", id, -rc, strerror(-rc));
+            return -1;
+        }
+        else
+        {
+            printf("%s: in wrong state (%s), expected (%s)\n",
+               id, state2str(rc),
+               state2str(action_options[idx].expected));
+            return -1;
+        }
+    }
+
+    return 0;
+
+ err:
+    printf("%s failed with %d(%s)\n", id, -rc, strerror(-rc));
+    return rc;
+}
+
+static int load_func(int argc, char *argv[])
+{
+    int rc;
+    char *new_argv[2];
+    char *id, *name, *lastdot;
+
+    if ( argc != 1 )
+    {
+        show_help();
+        return -1;
+    }
+    /* <file> */
+    new_argv[1] = argv[0];
+
+    /* Synthesize the <id> */
+    name = strdup(argv[0]);
+
+    id = basename(name);
+    lastdot = strrchr(id, '.');
+    if (lastdot != NULL)
+        *lastdot = '\0';
+    new_argv[0] = id;
+    printf("%s %s %s\n", argv[0], new_argv[0], new_argv[1]);
+
+    rc = upload_func(2 /* <id> <file> */, new_argv);
+    if ( rc )
+        return rc;
+
+    rc = action_func(1 /* only <id> */, new_argv, ACTION_CHECK);
+    if ( rc )
+        goto unload;
+
+    rc = action_func(1 /* only <id> */, new_argv, ACTION_APPLY);
+    if ( rc )
+        goto unload;
+
+    free(name);
+    return 0;
+unload:
+    action_func(1, new_argv, ACTION_UNLOAD);
+    free(name);
+    return rc;
+}
+
+/*
+ * These are also functions in action_options that are called in case
+ * none of these match.
+ */
+struct {
+    const char *name;
+    int (*function)(int argc, char *argv[]);
+} main_options[] = {
+    { "help", help_func },
+    { "list", list_func },
+    { "upload", upload_func },
+    { "load", load_func },
+};
+
+int main(int argc, char *argv[])
+{
+    int i, j, ret;
+
+    if ( argc  <= 1 )
+    {
+        show_help();
+        return 0;
+    }
+    for ( i = 0; i < ARRAY_SIZE(main_options); i++ )
+        if (!strncmp(main_options[i].name, argv[1], strlen(argv[1])))
+            break;
+
+    if ( i == ARRAY_SIZE(main_options) )
+    {
+        for ( j = 0; j < ARRAY_SIZE(action_options); j++ )
+            if (!strncmp(action_options[j].name, argv[1], strlen(argv[1])))
+                break;
+
+        if ( j == ARRAY_SIZE(action_options) )
+        {
+            fprintf(stderr, "Unrecognised command '%s' -- try "
+                   "'xen-xsplice help'\n", argv[1]);
+            return 1;
+        }
+    } else
+        j = ARRAY_SIZE(action_options);
+
+    xch = xc_interface_open(0,0,0);
+    if ( !xch )
+    {
+        fprintf(stderr, "failed to get the handler\n");
+        return 0;
+    }
+
+    if ( i == ARRAY_SIZE(main_options) )
+        ret = action_func(argc -2, argv + 2, j);
+    else
+        ret = main_options[i].function(argc -2, argv + 2);
+
+    xc_interface_close(xch);
+
+    return !!ret;
+}