[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH v5 08/28] xen-xsplice: Tool to manipulate xsplice payloads
A simple tool that allows an system admin to perform basic xsplice operations: - Upload a xsplice file (with an unique name) - List all the xsplice payloads loaded. - Apply, revert, replace, or unload the payload using the unique name. - Do all two - upload, and apply the payload in one go (load). Also will use the name of the file as the <name> Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@xxxxxxxxxx> Signed-off-by: Ross Lagerwall <ross.lagerwall@xxxxxxxxxx> Acked-by: Wei Liu <wei.liu2@xxxxxxxxxx> --- Cc: Ian Jackson <ian.jackson@xxxxxxxxxxxxx> Cc: Stefano Stabellini <stefano.stabellini@xxxxxxxxxxxxx> Cc: Wei Liu <wei.liu2@xxxxxxxxxx> 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. - s/id/name/ - Don't use hypercall buffer in upload_func, instead do it in libxc - Remove the debug printk. - Remove goto's (per Wei's review) - Use fprintf(stderr in error paths. - Add local variable block. - Syntax, expand comment, and don't overwrite rc if xc_xsplice_upload failed. v4: - Remove LOADED state. Only have CHECKED state. --- .gitignore | 1 + tools/misc/Makefile | 4 + tools/misc/xen-xsplice.c | 463 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 468 insertions(+) create mode 100644 tools/misc/xen-xsplice.c diff --git a/.gitignore b/.gitignore index b40453e..b9c9550 100644 --- a/.gitignore +++ b/.gitignore @@ -181,6 +181,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 a2ef0ec..e1956f6 100644 --- a/tools/misc/Makefile +++ b/tools/misc/Makefile @@ -31,6 +31,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/ @@ -99,6 +100,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_libxenevtchn) $(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..fb9228e --- /dev/null +++ b/tools/misc/xen-xsplice.c @@ -0,0 +1,463 @@ +/* + * 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" + " <name> An unique name of payload. Up to %d characters.\n" + "Commands:\n" + " help display this help\n" + " upload <name> <file> upload file <file> with <name> name\n" + " list list payloads uploaded.\n" + " apply <name> apply <name> patch.\n" + " revert <name> revert name <name> patch.\n" + " replace <name> apply <name> patch and revert all others.\n" + " unload <name> unload name <name> patch.\n" + " load <file> upload, check and apply <file>.\n" + " name 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(unsigned int state) +{ +#define STATE(x) [XSPLICE_STATE_##x] = #x + static const char *const names[] = { + STATE(CHECKED), + STATE(APPLIED), + }; +#undef STATE + if (state >= ARRAY_SIZE(names) || !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 *name = 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 ) + return rc; + name = malloc(sizeof(*name) * XEN_XSPLICE_NAME_SIZE * MAX_LEN); + if ( !name ) + { + free(info); + return rc; + } + len = malloc(sizeof(*len) * MAX_LEN); + if ( !len ) { + free(name); + free(info); + return rc; + } + + fprintf(stdout," ID | status\n" + "----------------------------------------+------------\n"); + do { + done = 0; + /* The memset is done to catch errors. */ + memset(info, 'A', sizeof(*info) * MAX_LEN); + memset(name, 'B', sizeof(*name * MAX_LEN * XEN_XSPLICE_NAME_SIZE)); + memset(len, 'C', sizeof(*len) * MAX_LEN); + rc = xc_xsplice_list(xch, MAX_LEN, idx, info, name, 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 = name + (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 ); + + free(name); + free(info); + free(len); + return rc; +} +#undef MAX_LEN + +static int get_name(int argc, char *argv[], char *name) +{ + 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(name, 0, XEN_XSPLICE_NAME_SIZE); + strncpy(name, argv[0], len); + return 0; +} + +static int upload_func(int argc, char *argv[]) +{ + char *filename; + char name[XEN_XSPLICE_NAME_SIZE]; + int fd = 0, rc; + struct stat buf; + unsigned char *fbuf; + ssize_t len; + + if ( argc != 2 ) + { + show_help(); + return -1; + } + + if ( get_name(argc, argv, name) ) + 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); + rc = xc_xsplice_upload(xch, name, fbuf, len); + if ( rc ) + fprintf(stderr, "Upload failed: %s, error: %d(%s)!\n", + filename, errno, strerror(errno)); + + if ( munmap( fbuf, len) ) + { + fprintf(stderr, "Could not unmap!? error: %d(%s)!\n", + errno, strerror(errno)); + if ( !rc ) + 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_REPLACE = 3, +}; + +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 *name, 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, + .expected = -ENOENT, + .name = "unload", + .function = xc_xsplice_unload, + }, + { .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 name[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_name(argc, argv, name) ) + return EINVAL; + + /* Check initial status. */ + rc = xc_xsplice_get(xch, name, &status); + if ( rc ) + { + fprintf(stderr, "%s failed to get status (rc=%d, %s)!\n", + name, -rc, strerror(-rc)); + return -1; + } + if ( status.rc == -EAGAIN ) + { + fprintf(stderr, "%s failed. Operation already in progress\n", name); + 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, name, 0); + if ( rc ) + { + fprintf(stderr, "%s failed with %d(%s)\n", name, -rc, strerror(-rc)); + return -1; + } + } + else + { + printf("%s: in wrong state (%s), expected (%s)\n", + name, state2str(status.state), + state2str(action_options[idx].expected)); + return -1; + } + + original_state = status.state; + do { + rc = xc_xsplice_get(xch, name, &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 ) + { + fprintf(stderr, "%s: Operation didn't complete after 30 seconds.\n", name); + return -1; + } + else + { + if ( rc == 0 ) + rc = status.state; + + if ( action_options[idx].expected == rc ) + printf(" completed\n"); + else if ( rc < 0 ) + { + fprintf(stderr, "%s failed with %d(%s)\n", name, -rc, strerror(-rc)); + return -1; + } + else + { + fprintf(stderr, "%s: in wrong state (%s), expected (%s)\n", + name, state2str(rc), + state2str(action_options[idx].expected)); + return -1; + } + } + + return 0; +} + +static int load_func(int argc, char *argv[]) +{ + int rc; + char *new_argv[2]; + char *path, *name, *lastdot; + + if ( argc != 1 ) + { + show_help(); + return -1; + } + /* <file> */ + new_argv[1] = argv[0]; + + /* Synthesize the <id> */ + path = strdup(argv[0]); + + name = basename(path); + lastdot = strrchr(name, '.'); + if ( lastdot != NULL ) + *lastdot = '\0'; + new_argv[0] = name; + + rc = upload_func(2 /* <id> <file> */, new_argv); + if ( rc ) + return rc; + + rc = action_func(1 /* only <id> */, new_argv, ACTION_APPLY); + if ( rc ) + action_func(1, new_argv, ACTION_UNLOAD); + + free(path); + return rc; +} + +/* + * These are also functions in action_options that are called in case + * none of the ones in main_options 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; +} + +/* + * Local variables: + * mode: C + * c-file-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ -- 2.5.0 _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxx http://lists.xen.org/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |