From patchwork Wed Apr 27 19:27:01 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: 8962391 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 8A9679F1C1 for ; Wed, 27 Apr 2016 19:35:04 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 2A97D201EC for ; Wed, 27 Apr 2016 19:35:03 +0000 (UTC) Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) (using TLSv1.2 with cipher AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 5D7E520268 for ; Wed, 27 Apr 2016 19:35:01 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.84_2) (envelope-from ) id 1avVD1-0007ed-GE; Wed, 27 Apr 2016 19:33:15 +0000 Received: from mail6.bemta5.messagelabs.com ([195.245.231.135]) by lists.xenproject.org with esmtp (Exim 4.84_2) (envelope-from ) id 1avVD0-0007dw-JY for xen-devel@lists.xenproject.org; Wed, 27 Apr 2016 19:33:14 +0000 Received: from [85.158.139.211] by server-6.bemta-5.messagelabs.com id 20/1F-01795-9F311275; Wed, 27 Apr 2016 19:33:13 +0000 X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFmpnkeJIrShJLcpLzFFi42KZM10+UPensGK 4wfbzbBbft0xmcmD0OPzhCksAYxRrZl5SfkUCa0bDvWvsBX2lFYd+7WNqYJwa1MXIxSEk0MEk cWr5F1YI5wujxNVJU1kgnA2MErv/dUJluhkl3u/czN7FyAnkFEl8eHaLsYuRg4NNwETizSpHk LCIwHJGiRdn8kDqmQX2M0rcevqDFSQhLOAt0XbrAhuIzSKgKtH/8RcLiM0r4CpxdesBFpA5Eg JyEgsupIOYnAJuEm/WRUNscpV4eOU7WLWEgLFE+9uLbBMY+RcwMqxi1ChOLSpLLdI1NNBLKsp MzyjJTczMAfJM9XJTi4sT01NzEpOK9ZLzczcxAgOIAQh2MK6Z6nyIUZKDSUmUd8MfhXAhvqT8 lMqMxOKM+KLSnNTiQ4wyHBxKErzPhBTDhQSLUtNTK9Iyc4ChDJOW4OBREuFNA0nzFhck5hZnp kOkTjEqSonzBoIkBEASGaV5cG2w+LnEKCslzMsIdIgQT0FqUW5mCar8K0ZxDkYlYd6jIFN4Mv NK4Ka/AlrMBLT48iFZkMUliQgpqQZG8feKfOnP9j+ep35wvU+iZd9T2b7pJ19qn+iL4Kh405S w+kD7aSP/x0+3plvcXRIrsDD+/S2Faab/b2zyPRPNt7P/aZ5FU/hDtiiVkjCTOftPzpZNsL35 wNxjXeYD3yUd6X8vnP6je3JDsuIJx3CrwonOq69OqWVI3GeXfZZt7VnNJImg3WteKLEUZyQaa jEXFScCAGNtnJ+aAgAA X-Env-Sender: konrad@char.us.oracle.com X-Msg-Ref: server-11.tower-206.messagelabs.com!1461785591!24560582!1 X-Originating-IP: [156.151.31.81] X-SpamReason: No, hits=0.0 required=7.0 tests=sa_preprocessor: VHJ1c3RlZCBJUDogMTU2LjE1MS4zMS44MSA9PiAyODgzMzk=\n X-StarScan-Received: X-StarScan-Version: 8.34; banners=-,-,- X-VirusChecked: Checked Received: (qmail 35194 invoked from network); 27 Apr 2016 19:33:12 -0000 Received: from userp1040.oracle.com (HELO userp1040.oracle.com) (156.151.31.81) by server-11.tower-206.messagelabs.com with DHE-RSA-AES256-GCM-SHA384 encrypted SMTP; 27 Apr 2016 19:33:12 -0000 Received: from aserv0022.oracle.com (aserv0022.oracle.com [141.146.126.234]) by userp1040.oracle.com (Sentrion-MTA-4.3.2/Sentrion-MTA-4.3.2) with ESMTP id u3RJS29Z004794 (version=TLSv1 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Wed, 27 Apr 2016 19:28:02 GMT Received: from userv0122.oracle.com (userv0122.oracle.com [156.151.31.75]) by aserv0022.oracle.com (8.13.8/8.13.8) with ESMTP id u3RJS1na019118 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Wed, 27 Apr 2016 19:28:02 GMT Received: from abhmp0002.oracle.com (abhmp0002.oracle.com [141.146.116.8]) by userv0122.oracle.com (8.14.4/8.14.4) with ESMTP id u3RJS1X1005251; Wed, 27 Apr 2016 19:28:01 GMT Received: from char.us.oracle.com (/10.137.176.158) by default (Oracle Beehive Gateway v4.0) with ESMTP ; Wed, 27 Apr 2016 12:28:00 -0700 Received: by char.us.oracle.com (Postfix, from userid 1000) id C2FA26A0124; Wed, 27 Apr 2016 15:27:57 -0400 (EDT) From: Konrad Rzeszutek Wilk To: konrad@kernel.org, xen-devel@lists.xenproject.org, sasha.levin@oracle.com, andrew.cooper3@citrix.com, ross.lagerwall@citrix.com, mpohlack@amazon.de Date: Wed, 27 Apr 2016 15:27:01 -0400 Message-Id: <1461785241-4481-5-git-send-email-konrad.wilk@oracle.com> X-Mailer: git-send-email 2.5.0 In-Reply-To: <1461785241-4481-1-git-send-email-konrad.wilk@oracle.com> References: <1461785241-4481-1-git-send-email-konrad.wilk@oracle.com> X-Source-IP: aserv0022.oracle.com [141.146.126.234] Cc: Wei Liu , Stefano Stabellini , Ian Jackson , Konrad Rzeszutek Wilk Subject: [Xen-devel] [PATCH v10 04/24] xen-xsplice: Tool to manipulate xsplice payloads X-BeenThere: xen-devel@lists.xen.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: xen-devel-bounces@lists.xen.org Sender: "Xen-devel" 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, 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 Signed-off-by: Konrad Rzeszutek Wilk Signed-off-by: Ross Lagerwall Acked-by: Wei Liu --- Cc: Ian Jackson Cc: Stefano Stabellini Cc: Wei Liu 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. - 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. v10: - Drop the 'check' in the help message. --- --- .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 20ffa2d..39eb779 100644 --- a/.gitignore +++ b/.gitignore @@ -182,6 +182,7 @@ tools/misc/xen_cpuperf tools/misc/xen-cpuid 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 a94dad9..3a5f842 100644 --- a/tools/misc/Makefile +++ b/tools/misc/Makefile @@ -32,6 +32,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/ @@ -103,6 +104,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..0f1ab5a --- /dev/null +++ b/tools/misc/xen-xsplice.c @@ -0,0 +1,463 @@ +/* + * 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" + " load upload 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(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; + } + /* */ + 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_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: + */