From patchwork Fri Feb 12 18:05:41 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: 8294871 Return-Path: X-Original-To: patchwork-xen-devel@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id BD3CBC02B9 for ; Fri, 12 Feb 2016 18:09:08 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 539DE2041D for ; Fri, 12 Feb 2016 18:09:07 +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 A67EE20429 for ; Fri, 12 Feb 2016 18:09:01 +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 1aUI7K-00064X-Fq; Fri, 12 Feb 2016 18:06:54 +0000 Received: from mail6.bemta5.messagelabs.com ([195.245.231.135]) by lists.xen.org with esmtp (Exim 4.72) (envelope-from ) id 1aUI7I-00062u-FP for xen-devel@lists.xen.org; Fri, 12 Feb 2016 18:06:52 +0000 Received: from [85.158.139.211] by server-3.bemta-5.messagelabs.com id EF/35-13487-B3F1EB65; Fri, 12 Feb 2016 18:06:51 +0000 X-Env-Sender: konrad@char.us.oracle.com X-Msg-Ref: server-2.tower-206.messagelabs.com!1455300409!22061419!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 21055 invoked from network); 12 Feb 2016 18:06:50 -0000 Received: from aserp1040.oracle.com (HELO aserp1040.oracle.com) (141.146.126.69) by server-2.tower-206.messagelabs.com with DHE-RSA-AES256-GCM-SHA384 encrypted SMTP; 12 Feb 2016 18:06:50 -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 u1CI6gVR025038 (version=TLSv1 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Fri, 12 Feb 2016 18:06:43 GMT Received: from userv0122.oracle.com (userv0122.oracle.com [156.151.31.75]) by aserv0021.oracle.com (8.13.8/8.13.8) with ESMTP id u1CI6gVT028595 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=FAIL); Fri, 12 Feb 2016 18:06:42 GMT Received: from abhmp0010.oracle.com (abhmp0010.oracle.com [141.146.116.16]) by userv0122.oracle.com (8.14.4/8.14.4) with ESMTP id u1CI6fC3017120; Fri, 12 Feb 2016 18:06:41 GMT Received: from char.us.oracle.com (/10.137.176.158) by default (Oracle Beehive Gateway v4.0) with ESMTP ; Fri, 12 Feb 2016 10:06:40 -0800 Received: by char.us.oracle.com (Postfix, from userid 1000) id 56BEE6A4BF3; Fri, 12 Feb 2016 13:06:39 -0500 (EST) From: Konrad Rzeszutek Wilk To: xen-devel@lists.xenproject.org, andrew.cooper3@citrix.com, konrad@kernel.org, mpohlack@amazon.de, ross.lagerwall@citrix.com, sasha.levin@citrix.com, jinsong.liu@alibaba-inc.com, Ian Jackson , Stefano Stabellini , Ian Campbell , Wei Liu , xen-devel@lists.xen.org Date: Fri, 12 Feb 2016 13:05:41 -0500 Message-Id: <1455300361-13092-4-git-send-email-konrad.wilk@oracle.com> X-Mailer: git-send-email 2.1.0 In-Reply-To: <1455300361-13092-1-git-send-email-konrad.wilk@oracle.com> References: <1455300361-13092-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 v3 03/23] xen-xsplice: Tool to manipulate xsplice payloads (v4) 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 name) - List all the xsplice payloads loaded. - Apply, revert, replace, unload, or check the payload using the unique name. - 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. v4: - s/id/name/ - Don't use hypercall buffer in upload_func, instead do it in libxc - Remove the debug printk. --- .gitignore | 1 + tools/misc/Makefile | 4 + tools/misc/xen-xsplice.c | 470 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 475 insertions(+) create mode 100644 tools/misc/xen-xsplice.c diff --git a/.gitignore b/.gitignore index 91f690c..5cae935 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..13f762f --- /dev/null +++ b/tools/misc/xen-xsplice.c @@ -0,0 +1,470 @@ +/* + * 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 name patch.\n" + " replace apply patch and revert all others.\n" + " unload unload name patch.\n" + " check check name patch.\n" + " load upload, check and apply .\n" + " name 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 *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 ) + goto out; + name = malloc(sizeof(*name) * XEN_XSPLICE_NAME_SIZE * MAX_LEN); + if ( !name ) + goto out; + len = malloc(sizeof(*len) * MAX_LEN); + if ( !len ) + goto out; + + 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 ); + +out: + 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)); + goto out; + } +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 *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 | 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 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 ) + goto err; + + if ( status.rc == -EAGAIN ) + { + printf("%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 ) + goto err; + } + 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 ) + { + printf("%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 ) + { + printf("%s failed with %d(%s)\n", name, -rc, strerror(-rc)); + return -1; + } + else + { + printf("%s: in wrong state (%s), expected (%s)\n", + name, state2str(rc), + state2str(action_options[idx].expected)); + return -1; + } + } + + return 0; + + err: + printf("%s failed with %d(%s)\n", name, -rc, strerror(-rc)); + return rc; +} + +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; + } + /* */ + new_argv[1] = argv[0]; + + /* Synthesize the */ + path = strdup(argv[0]); + + name = basename(path); + lastdot = strrchr(name, '.'); + if (lastdot != NULL) + *lastdot = '\0'; + new_argv[0] = name; + + 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(path); + return 0; +unload: + 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 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; +}