From patchwork Thu Jan 14 21:47:03 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Konrad Rzeszutek Wilk X-Patchwork-Id: 8035771 Return-Path: X-Original-To: patchwork-xen-devel@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id EFDAB9F1C0 for ; Thu, 14 Jan 2016 21:50:27 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id BA7A620251 for ; Thu, 14 Jan 2016 21:50:25 +0000 (UTC) Received: from lists.xen.org (lists.xenproject.org [50.57.142.19]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 49AEC202EB for ; Thu, 14 Jan 2016 21:50:24 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=lists.xen.org) by lists.xen.org with esmtp (Exim 4.72) (envelope-from ) id 1aJpjw-0006WC-O1; Thu, 14 Jan 2016 21:47:32 +0000 Received: from mail6.bemta14.messagelabs.com ([193.109.254.103]) by lists.xen.org with esmtp (Exim 4.72) (envelope-from ) id 1aJpjt-0006Ri-Qr for xen-devel@lists.xenproject.org; Thu, 14 Jan 2016 21:47:30 +0000 Received: from [193.109.254.147] by server-8.bemta-14.messagelabs.com id B1/05-24450-17718965; Thu, 14 Jan 2016 21:47:29 +0000 X-Env-Sender: konrad@char.us.oracle.com X-Msg-Ref: server-5.tower-27.messagelabs.com!1452808047!16976811!1 X-Originating-IP: [141.146.126.69] X-SpamReason: No, hits=0.0 required=7.0 tests=sa_preprocessor: VHJ1c3RlZCBJUDogMTQxLjE0Ni4xMjYuNjkgPT4gMjc3MjE4\n X-StarScan-Received: X-StarScan-Version: 7.35.1; banners=-,-,- X-VirusChecked: Checked Received: (qmail 6134 invoked from network); 14 Jan 2016 21:47:28 -0000 Received: from aserp1040.oracle.com (HELO aserp1040.oracle.com) (141.146.126.69) by server-5.tower-27.messagelabs.com with DHE-RSA-AES256-GCM-SHA384 encrypted SMTP; 14 Jan 2016 21:47:28 -0000 Received: from aserv0021.oracle.com (aserv0021.oracle.com [141.146.126.233]) by aserp1040.oracle.com (Sentrion-MTA-4.3.2/Sentrion-MTA-4.3.2) with ESMTP id u0ELlJo9014841 (version=TLSv1 cipher=DHE-RSA-AES256-SHA bits=256 verify=FAIL); Thu, 14 Jan 2016 21:47:20 GMT Received: from aserv0122.oracle.com (aserv0122.oracle.com [141.146.126.236]) by aserv0021.oracle.com (8.13.8/8.13.8) with ESMTP id u0ELlJKA016021 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=FAIL); Thu, 14 Jan 2016 21:47:19 GMT Received: from abhmp0014.oracle.com (abhmp0014.oracle.com [141.146.116.20]) by aserv0122.oracle.com (8.13.8/8.13.8) with ESMTP id u0ELlJLg004949; Thu, 14 Jan 2016 21:47:19 GMT Received: from char.us.oracle.com (/10.137.176.158) by default (Oracle Beehive Gateway v4.0) with ESMTP ; Thu, 14 Jan 2016 13:47:19 -0800 Received: by char.us.oracle.com (Postfix, from userid 1000) id 3C86B6A4DAC; Thu, 14 Jan 2016 16:47:17 -0500 (EST) From: Konrad Rzeszutek Wilk To: xen-devel@lists.xenproject.org, ross.lagerwall@citrix.com, mpohlack@amazon.com, andrew.cooper3@citrix.com, stefano.stabellini@citrix.com, jbeulich@suse.com, ian.jackson@eu.citrix.com, ian.campbell@citrix.com, wei.liu2@citrix.com, sasha.levin@oracle.com Date: Thu, 14 Jan 2016 16:47:03 -0500 Message-Id: <1452808031-706-6-git-send-email-konrad.wilk@oracle.com> X-Mailer: git-send-email 2.1.0 In-Reply-To: <1452808031-706-1-git-send-email-konrad.wilk@oracle.com> References: <1452808031-706-1-git-send-email-konrad.wilk@oracle.com> X-Source-IP: aserv0021.oracle.com [141.146.126.233] Cc: Konrad Rzeszutek Wilk Subject: [Xen-devel] [PATCH v2 05/13] xen-xsplice: Tool to manipulate xsplice payloads (v3) X-BeenThere: xen-devel@lists.xen.org X-Mailman-Version: 2.1.13 Precedence: list List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: xen-devel-bounces@lists.xen.org Errors-To: xen-devel-bounces@lists.xen.org X-Spam-Status: No, score=-4.2 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP 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 Signed-off-by: Konrad Rzeszutek Wilk Signed-off-by: Ross Lagerwall --- 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 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 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static xc_interface *xch; + +void show_help(void) +{ + fprintf(stderr, + "xen-xsplice: Xsplice test tool\n" + "Usage: xen-xsplice [args]\n" + " An unique name of payload. Up to %d characters.\n" + "Commands:\n" + " help display this help\n" + " upload upload file with name\n" + " list list payloads uploaded.\n" + " apply apply patch.\n" + " revert revert id patch.\n" + " replace apply patch and revert all others.\n" + " unload unload id patch.\n" + " check check id patch.\n" + " load upload, check and apply .\n" + " id is the 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; + } + /* */ + new_argv[1] = argv[0]; + + /* Synthesize the */ + 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 /* */, new_argv); + if ( rc ) + return rc; + + rc = action_func(1 /* only */, new_argv, ACTION_CHECK); + if ( rc ) + goto unload; + + rc = action_func(1 /* only */, 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; +}