From patchwork Fri Feb 5 10:12:53 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Olaf Hering X-Patchwork-Id: 8233261 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 2EB179F37A for ; Fri, 5 Feb 2016 10:17:13 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 8B18B20397 for ; Fri, 5 Feb 2016 10:17:09 +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 6F24D2026D for ; Fri, 5 Feb 2016 10:17:05 +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 1aRdNz-0004b7-WD; Fri, 05 Feb 2016 10:13:08 +0000 Received: from mail6.bemta3.messagelabs.com ([195.245.230.39]) by lists.xen.org with esmtp (Exim 4.72) (envelope-from ) id 1aRdNx-0004aQ-Jn for xen-devel@lists.xen.org; Fri, 05 Feb 2016 10:13:06 +0000 Received: from [85.158.137.68] by server-9.bemta-3.messagelabs.com id 83/FD-03066-0B574B65; Fri, 05 Feb 2016 10:13:04 +0000 X-Env-Sender: olaf@aepfle.de X-Msg-Ref: server-13.tower-31.messagelabs.com!1454667183!19998853!1 X-Originating-IP: [81.169.146.221] X-SpamReason: No, hits=0.5 required=7.0 tests=BODY_RANDOM_LONG X-StarScan-Received: X-StarScan-Version: 7.35.1; banners=-,-,- X-VirusChecked: Checked Received: (qmail 60078 invoked from network); 5 Feb 2016 10:13:03 -0000 Received: from mo4-p00-ob.smtp.rzone.de (HELO mo4-p00-ob.smtp.rzone.de) (81.169.146.221) by server-13.tower-31.messagelabs.com with DHE-RSA-AES256-GCM-SHA384 encrypted SMTP; 5 Feb 2016 10:13:03 -0000 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; t=1454667183; l=66062; s=domk; d=aepfle.de; h=Content-Transfer-Encoding:Content-Type:MIME-Version:References: In-Reply-To:Date:Subject:Cc:To:From; bh=1cYC/b639ZGRGEB8FkW+7FJY+91SQW5IMbZAbPhphr4=; b=Fgtbh9PgsYPjKstEPF91VDtvnaGdb/F0D307PkU/RNhN7UFArrpGHcQLxhyVHmCJTSb qvzK13u1CGb+jUnIME5TbuioyYLrm1ezU9B5Ag46ZiqEfPbOWG2B7XEywH2z7d6xh+8De BgkRQfW7+SbcZrajMZA9NqOOUyu5yGg3pYM= X-RZG-AUTH: :P2EQZWCpfu+qG7CngxMFH1J+yackYocTD1iAi8x+OWi/zfN1cLnDYUgqRlyBxIG94Z+l4sPG5TVHaI4yEcpOb/EH5xRkdg== X-RZG-CLASS-ID: mo00 Received: from probook ([2001:a62:1198:c1ff:b962:113d:f241:eaf9]) by smtp.strato.de (RZmta 37.17 AUTH) with ESMTPSA id j0033es15AD2VGj (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (curve secp521r1 with 521 ECDH bits, eq. 15360 bits RSA)) (Client did not present a certificate); Fri, 5 Feb 2016 11:13:02 +0100 (CET) Received: by probook (Postfix, from userid 1000) id 7EF92508D6; Fri, 5 Feb 2016 11:13:02 +0100 (CET) From: Olaf Hering To: xen-devel@lists.xen.org Date: Fri, 5 Feb 2016 10:12:53 +0000 Message-Id: <1454667175-24282-4-git-send-email-olaf@aepfle.de> X-Mailer: git-send-email 2.7.0 In-Reply-To: <1454667175-24282-1-git-send-email-olaf@aepfle.de> References: <1454667175-24282-1-git-send-email-olaf@aepfle.de> MIME-Version: 1.0 Cc: Wei Liu , Olaf Hering , Ian Jackson , Ian Campbell , Stefano Stabellini Subject: [Xen-devel] [PATCH v7 3/5] libxl: add support for vscsi 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: , Sender: xen-devel-bounces@lists.xen.org Errors-To: xen-devel-bounces@lists.xen.org X-Spam-Status: No, score=-4.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_MED, T_DKIM_INVALID, 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 Port pvscsi support from xend to libxl: vscsi=['pdev,vdev{,options}'] xl scsi-attach xl scsi-detach xl scsi-list Signed-off-by: Olaf Hering Cc: Ian Jackson Cc: Stefano Stabellini Cc: Ian Campbell Cc: Wei Liu --- docs/man/xl.cfg.pod.5 | 55 +++ docs/man/xl.pod.1 | 18 + tools/libxl/Makefile | 2 + tools/libxl/libxl.c | 578 ++++++++++++++++++++++++++++ tools/libxl/libxl.h | 35 ++ tools/libxl/libxl_create.c | 1 + tools/libxl/libxl_device.c | 2 + tools/libxl/libxl_internal.h | 14 + tools/libxl/libxl_types.idl | 53 +++ tools/libxl/libxl_types_internal.idl | 1 + tools/libxl/libxl_vscsi.c | 344 +++++++++++++++++ tools/libxl/libxlu_vscsi.c | 718 +++++++++++++++++++++++++++++++++++ tools/libxl/libxlutil.h | 18 + tools/libxl/xl.h | 3 + tools/libxl/xl_cmdimpl.c | 208 +++++++++- tools/libxl/xl_cmdtable.c | 15 + 16 files changed, 2064 insertions(+), 1 deletion(-) diff --git a/docs/man/xl.cfg.pod.5 b/docs/man/xl.cfg.pod.5 index 8899f75..3b5b1c5 100644 --- a/docs/man/xl.cfg.pod.5 +++ b/docs/man/xl.cfg.pod.5 @@ -517,6 +517,61 @@ value is optional if this is a guest domain. =back +=item B + +Specifies the PVSCSI devices to be provided to the guest. PVSCSI passes +SCSI devices from the backend domain to the guest. + +Each VSCSI_SPEC_STRING consists of "pdev,vdev[,options]". +'pdev' describes the physical device, preferable in a persistent format such as /dev/disk/by-*/*. +'vdev' is the domU device in vHOST:CHANNEL:TARGET:LUN notation, all integers. +'options' lists additional flags which a backend may recognize. + +The supported values for "pdev" and "options" depends on the backend driver used: + +=over 4 + +=item B + +=over 4 + +=item C + +The backend driver in the pvops kernel is part of the Linux-IO Target framework +(LIO). As such the SCSI devices have to be configured first with the tools +provided by this framework, such as a xen-scsiback aware targetcli. The "pdev" +in domU.cfg has to refer to a config item in that framework instead of the raw +device. Usually this is a WWN in the form of "na.WWN:LUN". + +=item C + +No options recognized. + +=back + +=item B + +=over 4 + +=item C + +The dom0 device in either /dev/scsidev or pHOST:CHANNEL:TARGET:LUN notation. + +It's recommended to use persistent names "/dev/disk/by-*/*" to refer to a "pdev". +The toolstack will translate this internally to "h:c:t:l" notation, which is how +the backend driver will access the device. Using the "h:c:t:l" notation for +"pdev" in domU.cfg is discouraged because this value will change across reboots, +depending on the detection order in the OS. + +=item C + +Currently only the option value "feature-host" is recognized. SCSI command +emulation in backend driver is bypassed when "feature-host" is specified. + +=back + +=back + =item B Specifies the paravirtual framebuffer devices which should be supplied diff --git a/docs/man/xl.pod.1 b/docs/man/xl.pod.1 index 4279c7c..0674149 100644 --- a/docs/man/xl.pod.1 +++ b/docs/man/xl.pod.1 @@ -1293,6 +1293,24 @@ List virtual trusted platform modules for a domain. =back +=head2 PVSCSI DEVICES + +=over 4 + +=item B I I I,I<[feature-host]> + +Creates a new vscsi device in the domain specified by I. + +=item B I I + +Removes the vscsi device from domain specified by I. + +=item B I I<[domain-id] ...> + +List vscsi devices for the domain specified by I. + +=back + =head1 PCI PASS-THROUGH =over 4 diff --git a/tools/libxl/Makefile b/tools/libxl/Makefile index 620720e..a71abf2 100644 --- a/tools/libxl/Makefile +++ b/tools/libxl/Makefile @@ -99,6 +99,7 @@ endif LIBXL_LIBS += -lyajl LIBXL_OBJS = flexarray.o libxl.o libxl_create.o libxl_dm.o libxl_pci.o \ + libxl_vscsi.o \ libxl_dom.o libxl_exec.o libxl_xshelp.o libxl_device.o \ libxl_internal.o libxl_utils.o libxl_uuid.o \ libxl_json.o libxl_aoutils.o libxl_numa.o libxl_vnuma.o \ @@ -141,6 +142,7 @@ AUTOINCS= libxlu_cfg_y.h libxlu_cfg_l.h _libxl_list.h _paths.h \ AUTOSRCS= libxlu_cfg_y.c libxlu_cfg_l.c AUTOSRCS += _libxl_save_msgs_callout.c _libxl_save_msgs_helper.c LIBXLU_OBJS = libxlu_cfg_y.o libxlu_cfg_l.o libxlu_cfg.o \ + libxlu_vscsi.o \ libxlu_disk_l.o libxlu_disk.o libxlu_vif.o libxlu_pci.o $(LIBXLU_OBJS): CFLAGS += $(CFLAGS_libxenctrl) # For xentoollog.h diff --git a/tools/libxl/libxl.c b/tools/libxl/libxl.c index 2bde0f5..ece7301 100644 --- a/tools/libxl/libxl.c +++ b/tools/libxl/libxl.c @@ -2059,6 +2059,572 @@ static int libxl__resolve_domid(libxl__gc *gc, const char *name, } /******************************************************************************/ + +static void libxl__device_vscsidev_backend_rm(libxl__gc *gc, + libxl_device_vscsidev *v, + xs_transaction_t t, + const char *be_path, + int dev_wait) +{ + char *dir, *path, *val; + + dir = GCSPRINTF("%s/vscsi-devs/dev-%u", be_path, v->vscsidev_id); + path = GCSPRINTF("%s/state", dir); + val = libxl__xs_read(gc, t, path); + LOG(DEBUG, "%s is %s", path, val); + if (val && strcmp(val, GCSPRINTF("%d", dev_wait)) == 0) { + xs_rm(CTX->xsh, t, GCSPRINTF("%s/state", dir)); + xs_rm(CTX->xsh, t, GCSPRINTF("%s/p-devname", dir)); + xs_rm(CTX->xsh, t, GCSPRINTF("%s/p-dev", dir)); + xs_rm(CTX->xsh, t, GCSPRINTF("%s/v-dev", dir)); + xs_rm(CTX->xsh, t, dir); + } else { + LOG(ERROR, "%s has %s, expected %d", path, val, dev_wait); + } +} + +static int libxl__device_vscsidev_backend_set_add(libxl__gc *gc, + libxl_device_vscsidev *v, + flexarray_t *back) +{ + int rc; + char *dir; + unsigned int hst, chn, tgt; + unsigned long long lun; + + + dir = GCSPRINTF("vscsi-devs/dev-%u", v->vscsidev_id); + switch (v->pdev.type) { + case LIBXL_VSCSI_PDEV_TYPE_WWN: + flexarray_append_pair(back, + GCSPRINTF("%s/p-dev", dir), + v->pdev.u.wwn.m); + break; + case LIBXL_VSCSI_PDEV_TYPE_HCTL: + hst = v->pdev.u.hctl.m.hst; + chn = v->pdev.u.hctl.m.chn; + tgt = v->pdev.u.hctl.m.tgt; + lun = v->pdev.u.hctl.m.lun; + flexarray_append_pair(back, + GCSPRINTF("%s/p-dev", dir), + GCSPRINTF("%u:%u:%u:%llu", hst, chn, tgt, lun)); + break; + case LIBXL_VSCSI_PDEV_TYPE_INVALID: + default: + rc = ERROR_FAIL; + goto out; + } + flexarray_append_pair(back, + GCSPRINTF("%s/p-devname", dir), + v->pdev.p_devname); + hst = v->vdev.hst; + chn = v->vdev.chn; + tgt = v->vdev.tgt; + lun = v->vdev.lun; + flexarray_append_pair(back, + GCSPRINTF("%s/v-dev", dir), + GCSPRINTF("%u:%u:%u:%llu", hst, chn, tgt, lun)); + flexarray_append_pair(back, + GCSPRINTF("%s/state", dir), + GCSPRINTF("%d", XenbusStateInitialising)); + rc = 0; +out: + return rc; +} + +static int libxl__device_vscsidev_backend_set_rm(libxl__gc *gc, + libxl_device_vscsidev *v, + flexarray_t *back) +{ + int rc; + char *dir; + + dir = GCSPRINTF("vscsi-devs/dev-%u", v->vscsidev_id); + rc = flexarray_append_pair(back, + GCSPRINTF("%s/state", dir), + GCSPRINTF("%d", XenbusStateClosing)); + return rc; +} + +static int libxl__device_vscsi_new_backend(libxl__egc *egc, + libxl__ao_device *aodev, + libxl_device_vscsictrl *vscsi, + libxl_domain_config *d_config) +{ + STATE_AO_GC(aodev->ao); + int rc, i; + flexarray_t *back; + flexarray_t *front; + libxl_device_vscsidev *v; + xs_transaction_t t = XBT_NULL; + + /* Prealloc key+value: 4 toplevel + 4 per device */ + i = 2 * (4 + (4 * vscsi->num_vscsidevs)); + back = flexarray_make(gc, i, 1); + front = flexarray_make(gc, 2 * 2, 1); + + flexarray_append_pair(back, "frontend-id", GCSPRINTF("%d", aodev->dev->domid)); + flexarray_append_pair(back, "online", "1"); + flexarray_append_pair(back, "state", GCSPRINTF("%d", XenbusStateInitialising)); + flexarray_append_pair(back, "feature-host", + libxl_defbool_val(vscsi->scsi_raw_cmds) ? + "1" : "0"); + + flexarray_append_pair(front, "backend-id", GCSPRINTF("%d", vscsi->backend_domid)); + flexarray_append_pair(front, "state", GCSPRINTF("%d", XenbusStateInitialising)); + + for (i = 0; i < vscsi->num_vscsidevs; i++) { + v = vscsi->vscsidevs + i; + rc = libxl__device_vscsidev_backend_set_add(gc, v, back); + if (rc) return rc; + } + + for (;;) { + rc = libxl__xs_transaction_start(gc, &t); + if (rc) goto out; + + rc = libxl__device_exists(gc, t, aodev->dev); + if (rc < 0) goto out; + if (rc == 1) { /* already exists in xenstore */ + LOG(ERROR, "device already exists in xenstore"); + rc = ERROR_DEVICE_EXISTS; + goto out; + } + + if (aodev->update_json) { + rc = libxl__set_domain_configuration(gc, aodev->dev->domid, d_config); + if (rc) goto out; + } + + libxl__device_generic_add(gc, t, aodev->dev, + libxl__xs_kvs_of_flexarray(gc, back, + back->count), + libxl__xs_kvs_of_flexarray(gc, front, + front->count), + NULL); + + rc = libxl__xs_transaction_commit(gc, &t); + if (!rc) break; + if (rc < 0) goto out; + } + + libxl__wait_device_connection(egc, aodev); + rc = 0; + +out: + libxl__xs_transaction_abort(gc, &t); + return rc; +} + +static int libxl__device_vscsi_reconfigure_add(libxl__egc *egc, + libxl__ao_device *aodev, + libxl_device_vscsictrl *vscsi, + libxl_domain_config *d_config, + const char *be_path) +{ + STATE_AO_GC(aodev->ao); + int rc, i, be_state, be_wait; + char *dev_path, *state_path, *state_val; + flexarray_t *back; + libxl_device_vscsidev *v; + xs_transaction_t t = XBT_NULL; + bool do_reconfigure = false; + + /* Prealloc key+value: 1 toplevel + 4 per device */ + i = 2 * (1 + (4 * vscsi->num_vscsidevs)); + back = flexarray_make(gc, i, 1); + + state_path = GCSPRINTF("%s/state", be_path); + + for (;;) { + rc = libxl__xs_transaction_start(gc, &t); + if (rc) goto out; + + state_val = libxl__xs_read(gc, t, state_path); + LOG(DEBUG, "%s is %s", state_path, state_val); + if (!state_val) { + rc = ERROR_FAIL; + goto out; + } + + be_state = atoi(state_val); + switch (be_state) { + case XenbusStateUnknown: + case XenbusStateInitialising: + case XenbusStateClosing: + case XenbusStateClosed: + default: + /* The backend is in a bad state */ + rc = ERROR_FAIL; + goto out; + case XenbusStateInitialised: + case XenbusStateReconfiguring: + case XenbusStateReconfigured: + /* Backend is still busy, caller has to retry */ + rc = ERROR_NOT_READY; + goto out; + case XenbusStateInitWait: + /* The frontend did not connect yet */ + be_wait = XenbusStateInitWait; + do_reconfigure = false; + break; + case XenbusStateConnected: + /* The backend can handle reconfigure */ + be_wait = XenbusStateConnected; + flexarray_append_pair(back, "state", GCSPRINTF("%d", XenbusStateReconfiguring)); + do_reconfigure = true; + break; + } + + /* Append new vscsidev or skip existing */ + for (i = 0; i < vscsi->num_vscsidevs; i++) { + unsigned int nb = 0; + v = vscsi->vscsidevs + i; + dev_path = GCSPRINTF("%s/vscsi-devs/dev-%u", be_path, v->vscsidev_id); + if (libxl__xs_directory(gc, XBT_NULL, dev_path, &nb)) { + /* FIXME Sanity check */ + LOG(DEBUG, "%s exists already with %u entries", dev_path, nb); + continue; + } + rc = libxl__device_vscsidev_backend_set_add(gc, v, back); + if (rc) goto out; + } + + if (aodev->update_json) { + rc = libxl__set_domain_configuration(gc, aodev->dev->domid, d_config); + if (rc) goto out; + } + + libxl__xs_writev(gc, t, be_path, + libxl__xs_kvs_of_flexarray(gc, back, back->count)); + + rc = libxl__xs_transaction_commit(gc, &t); + if (!rc) break; + if (rc < 0) goto out; + } + + if (do_reconfigure) { + /* Poll for backend change */ + rc = libxl__wait_for_backend(gc, be_path, GCSPRINTF("%d", be_wait)); + if (rc) goto out; + } + rc = 0; + +out: + libxl__xs_transaction_abort(gc, &t); + return rc; +} + +static int libxl__device_vscsi_reconfigure_rm(libxl__egc *egc, + libxl__ao_device *aodev, + libxl_device_vscsictrl *vscsi, + const char *be_path, + int *dev_wait) +{ + STATE_AO_GC(aodev->ao); + int rc, i, be_state, be_wait; + char *dev_path, *state_path, *state_val; + flexarray_t *back; + libxl_device_vscsidev *v; + xs_transaction_t t = XBT_NULL; + bool do_reconfigure = false; + + /* Prealloc key+value: 1 toplevel + 4 per device */ + i = 2 * (1 + (4 * vscsi->num_vscsidevs)); + back = flexarray_make(gc, i, 1); + + state_path = GCSPRINTF("%s/state", be_path); + + for (;;) { + rc = libxl__xs_transaction_start(gc, &t); + if (rc) goto out; + + state_val = libxl__xs_read(gc, t, state_path); + LOG(DEBUG, "%s is %s", state_path, state_val); + if (!state_val) { + rc = ERROR_FAIL; + goto out; + } + + be_state = atoi(state_val); + switch (be_state) { + case XenbusStateUnknown: + case XenbusStateInitialising: + case XenbusStateClosing: + case XenbusStateClosed: + default: + /* The backend is in a bad state */ + rc = ERROR_FAIL; + goto out; + case XenbusStateInitialised: + case XenbusStateReconfiguring: + case XenbusStateReconfigured: + /* Backend is still busy, caller has to retry */ + rc = ERROR_NOT_READY; + goto out; + case XenbusStateInitWait: + /* The frontend did not connect yet */ + be_wait = XenbusStateInitWait; + *dev_wait = XenbusStateInitialising; + do_reconfigure = false; + break; + case XenbusStateConnected: + /* The backend can handle reconfigure */ + be_wait = XenbusStateConnected; + *dev_wait = XenbusStateClosed; + flexarray_append_pair(back, "state", GCSPRINTF("%d", XenbusStateReconfiguring)); + do_reconfigure = true; + break; + } + + /* Append new vscsidev or skip existing */ + for (i = 0; i < vscsi->num_vscsidevs; i++) { + unsigned int nb = 0; + v = vscsi->vscsidevs + i; + dev_path = GCSPRINTF("%s/vscsi-devs/dev-%u", be_path, v->vscsidev_id); + if (!libxl__xs_directory(gc, XBT_NULL, dev_path, &nb)) { + /* FIXME Sanity check */ + LOG(DEBUG, "%s does not exist anymore", dev_path); + continue; + } + rc = libxl__device_vscsidev_backend_set_rm(gc, v, back); + if (rc) goto out; + } + + libxl__xs_writev(gc, t, be_path, + libxl__xs_kvs_of_flexarray(gc, back, back->count)); + + rc = libxl__xs_transaction_commit(gc, &t); + if (!rc) break; + if (rc < 0) goto out; + } + + if (do_reconfigure) { + /* Poll for backend change */ + rc = libxl__wait_for_backend(gc, be_path, GCSPRINTF("%d", be_wait)); + if (rc) goto out; + } + rc = 0; + +out: + libxl__xs_transaction_abort(gc, &t); + return rc; +} + +static int libxl__vscsictrl_next_vscsidev_id(libxl__gc *gc, uint32_t domid, + libxl_device_vscsictrl *vscsi, + libxl_devid *vscsidev_id) +{ + char *path, *val; + xs_transaction_t t = XBT_NULL; + unsigned int id; + int rc; + + path = GCSPRINTF("%s/vscsi/%u/next_vscsidev_id", + libxl__xs_libxl_path(gc, domid), + vscsi->devid); + + for (;;) { + rc = libxl__xs_transaction_start(gc, &t); + if (rc) goto out; + + val = libxl__xs_read(gc, t, path); + id = val ? strtoul(val, NULL, 10) : 0; + + LOG(DEBUG, "%s = %s vscsidev_id %u", path, val, id); + + val = GCSPRINTF("%u", id + 1); + rc = libxl__xs_write_checked(gc, t, path, val); + if (rc) goto out; + + rc = libxl__xs_transaction_commit(gc, &t); + if (!rc) break; + if (rc < 0) goto out; + } + + *vscsidev_id = id; + rc = 0; + +out: + libxl__xs_transaction_abort(gc, &t); + return rc; +} + +static int libxl__vscsi_assign_vscsidev_ids(libxl__gc *gc, uint32_t domid, + libxl_device_vscsictrl *vscsi) +{ + libxl_device_vscsidev *dev; + libxl_devid vscsidev_id; + int rc, i; + + for (i = 0; i < vscsi->num_vscsidevs; i++) { + dev = &vscsi->vscsidevs[i]; + if (dev->vscsidev_id >= 0) + continue; + rc = libxl__vscsictrl_next_vscsidev_id(gc, domid, vscsi, &vscsidev_id); + if (rc) { + LOG(ERROR, "domid %u, failed to assign vscsidev_id to %s", + domid, dev->pdev.p_devname); + goto out; + } + dev->vscsidev_id = vscsidev_id; + } + + rc = 0; +out: + return rc; +} + +static int libxl__device_from_vscsictrl(libxl__gc *gc, uint32_t domid, + libxl_device_vscsictrl *vscsi, + libxl__device *device) +{ + device->backend_domid = vscsi->backend_domid; + device->devid = vscsi->devid; + device->domid = domid; + device->backend_kind = LIBXL__DEVICE_KIND_VSCSI; + device->kind = LIBXL__DEVICE_KIND_VSCSI; + + return 0; +} + +void libxl__device_vscsictrl_add(libxl__egc *egc, uint32_t domid, + libxl_device_vscsictrl *vscsi, + libxl__ao_device *aodev) +{ + STATE_AO_GC(aodev->ao); + libxl__device *device; + char *be_path; + unsigned int be_dirs = 0; + int rc; + libxl_domain_config d_config; + libxl_device_vscsictrl vscsi_saved; + libxl__domain_userdata_lock *lock = NULL; + + libxl_domain_config_init(&d_config); + + libxl_device_vscsictrl_init(&vscsi_saved); + libxl_device_vscsictrl_copy(CTX, &vscsi_saved, vscsi); + + if (vscsi->devid == -1) { + LOGE(ERROR, "new vscsictrl for domid %u has uninitialized devid", domid); + rc = ERROR_FAIL; + goto out; + } + + rc = libxl__vscsi_assign_vscsidev_ids(gc, domid, &vscsi_saved); + if (rc) goto out; + + GCNEW(device); + rc = libxl__device_from_vscsictrl(gc, domid, vscsi, device); + if (rc) goto out; + + if (aodev->update_json) { + lock = libxl__lock_domain_userdata(gc, domid); + if (!lock) { + rc = ERROR_LOCK_FAIL; + goto out; + } + + rc = libxl__get_domain_configuration(gc, domid, &d_config); + if (rc) goto out; + + /* Replace or append the copy to the domain config */ + DEVICE_ADD(vscsictrl, vscsictrls, domid, &vscsi_saved, COMPARE_VSCSI, &d_config); + } + + aodev->dev = device; + + be_path = libxl__device_backend_path(gc, aodev->dev); + if (libxl__xs_directory(gc, XBT_NULL, be_path, &be_dirs)) { + rc = libxl__device_vscsi_reconfigure_add(egc, aodev, &vscsi_saved, &d_config, be_path); + if (rc) + goto out; + /* Notify that this is done */ + aodev->callback(egc, aodev); + } else { + rc = libxl__device_vscsi_new_backend(egc, aodev, &vscsi_saved, &d_config); + if (rc) + goto out; + } + + rc = 0; +out: + if (lock) libxl__unlock_domain_userdata(lock); + libxl_device_vscsictrl_dispose(&vscsi_saved); + libxl_domain_config_dispose(&d_config); + aodev->rc = rc; + if (rc) aodev->callback(egc, aodev); + return; +} + +static void libxl__device_vscsidev_rm(libxl__egc *egc, + libxl_device_vscsictrl *vscsi, + libxl__ao_device *aodev) +{ + STATE_AO_GC(aodev->ao); + char *be_path; + int rc, i, dev_wait; + libxl_device_vscsidev *v; + xs_transaction_t t = XBT_NULL; + + be_path = libxl__device_backend_path(gc, aodev->dev); + rc = libxl__device_vscsi_reconfigure_rm(egc, aodev, vscsi, be_path, &dev_wait); + if (rc) goto out; + + for (;;) { + rc = libxl__xs_transaction_start(gc, &t); + if (rc) goto out; + + for (i = 0; i < vscsi->num_vscsidevs; i++) { + v = vscsi->vscsidevs + i; + libxl__device_vscsidev_backend_rm(gc, v, t, be_path, dev_wait); + } + + rc = libxl__xs_transaction_commit(gc, &t); + if (!rc) break; + if (rc < 0) goto out; + } + + rc = 0; + +out: + aodev->rc = rc; + /* Notify that this is done */ + aodev->callback(egc, aodev); +} + +/* FIXME */ +/* Extended variant of DEFINE_DEVICE_REMOVE to handle reconfigure */ +int libxl_device_vscsidev_remove(libxl_ctx *ctx, uint32_t domid, + libxl_device_vscsictrl *vscsi, + const libxl_asyncop_how *ao_how) +{ + AO_CREATE(ctx, domid, ao_how); + libxl__device *device; + libxl__ao_device *aodev; + int rc; + + GCNEW(device); + rc = libxl__device_from_vscsictrl(gc, domid, vscsi, device); + if (rc != 0) goto out; + + GCNEW(aodev); + libxl__prepare_ao_device(ao, aodev); + aodev->action = LIBXL__DEVICE_ACTION_REMOVE; + aodev->dev = device; + aodev->callback = device_addrm_aocomplete; + aodev->force = 0; + aodev->update_json = true; + + + libxl__device_vscsidev_rm(egc, vscsi, aodev); + +out: + if (rc) return AO_CREATE_FAIL(rc); + return AO_INPROGRESS; +} + int libxl__device_vtpm_setdefault(libxl__gc *gc, libxl_device_vtpm *vtpm) { int rc; @@ -4162,6 +4728,8 @@ out: * libxl_device_disk_destroy * libxl_device_nic_remove * libxl_device_nic_destroy + * libxl_device_vscsictrl_remove + * libxl_device_vscsictrl_destroy * libxl_device_vtpm_remove * libxl_device_vtpm_destroy * libxl_device_vkb_remove @@ -4206,6 +4774,10 @@ DEFINE_DEVICE_REMOVE(disk, destroy, 1) DEFINE_DEVICE_REMOVE(nic, remove, 0) DEFINE_DEVICE_REMOVE(nic, destroy, 1) +/* vscsi */ +DEFINE_DEVICE_REMOVE(vscsictrl, remove, 0) +DEFINE_DEVICE_REMOVE(vscsictrl, destroy, 1) + /* vkb */ DEFINE_DEVICE_REMOVE(vkb, remove, 0) DEFINE_DEVICE_REMOVE(vkb, destroy, 1) @@ -4231,6 +4803,7 @@ DEFINE_DEVICE_REMOVE(vtpm, destroy, 1) /* The following functions are defined: * libxl_device_disk_add * libxl_device_nic_add + * libxl_device_vscsictrl_add * libxl_device_vtpm_add */ @@ -4260,6 +4833,9 @@ DEFINE_DEVICE_ADD(disk) /* nic */ DEFINE_DEVICE_ADD(nic) +/* vscsi */ +DEFINE_DEVICE_ADD(vscsictrl) + /* vtpm */ DEFINE_DEVICE_ADD(vtpm) @@ -6799,6 +7375,8 @@ int libxl_retrieve_domain_configuration(libxl_ctx *ctx, uint32_t domid, MERGE(nic, nics, COMPARE_DEVID, {}); + MERGE(vscsictrl, vscsictrls, COMPARE_VSCSI, {}); + MERGE(vtpm, vtpms, COMPARE_DEVID, {}); MERGE(pci, pcidevs, COMPARE_PCI, {}); diff --git a/tools/libxl/libxl.h b/tools/libxl/libxl.h index 156c0d5..68d84b4 100644 --- a/tools/libxl/libxl.h +++ b/tools/libxl/libxl.h @@ -836,6 +836,13 @@ void libxl_mac_copy(libxl_ctx *ctx, libxl_mac *dst, libxl_mac *src); #define LIBXL_HAVE_PCITOPOLOGY 1 /* + * LIBXL_HAVE_VSCSI + * + * If this is defined, the PV SCSI feature is supported. + */ +#define LIBXL_HAVE_VSCSI 1 + +/* * LIBXL_HAVE_SOCKET_BITMAP * * If this is defined, then libxl_socket_bitmap_alloc and @@ -1539,6 +1546,34 @@ int libxl_device_channel_getinfo(libxl_ctx *ctx, uint32_t domid, libxl_device_channel *channel, libxl_channelinfo *channelinfo); +/* Virtual SCSI */ +int libxl_device_vscsictrl_add(libxl_ctx *ctx, uint32_t domid, + libxl_device_vscsictrl *vscsi, + const libxl_asyncop_how *ao_how) + LIBXL_EXTERNAL_CALLERS_ONLY; +int libxl_device_vscsictrl_remove(libxl_ctx *ctx, uint32_t domid, + libxl_device_vscsictrl *vscsi, + const libxl_asyncop_how *ao_how) + LIBXL_EXTERNAL_CALLERS_ONLY; +int libxl_device_vscsictrl_destroy(libxl_ctx *ctx, uint32_t domid, + libxl_device_vscsictrl *vscsi, + const libxl_asyncop_how *ao_how) + LIBXL_EXTERNAL_CALLERS_ONLY; + +libxl_device_vscsictrl *libxl_device_vscsictrl_list(libxl_ctx *ctx, uint32_t domid, int *num); +int libxl_device_vscsictrl_getinfo(libxl_ctx *ctx, uint32_t domid, + libxl_device_vscsictrl *vscsictrl, + libxl_device_vscsidev *vscsidev, + libxl_vscsiinfo *vscsiinfo); +/* Remove vscsidev connected to vscsictrl */ +int libxl_device_vscsidev_remove(libxl_ctx *ctx, uint32_t domid, + libxl_device_vscsictrl *vscsi, + const libxl_asyncop_how *ao_how) + LIBXL_EXTERNAL_CALLERS_ONLY; +void libxl_device_vscsictrl_append_vscsidev(libxl_ctx *ctx, + libxl_device_vscsictrl *ctrl, + libxl_device_vscsidev *dev); + /* Virtual TPMs */ int libxl_device_vtpm_add(libxl_ctx *ctx, uint32_t domid, libxl_device_vtpm *vtpm, const libxl_asyncop_how *ao_how) diff --git a/tools/libxl/libxl_create.c b/tools/libxl/libxl_create.c index e491d83..64f3c70 100644 --- a/tools/libxl/libxl_create.c +++ b/tools/libxl/libxl_create.c @@ -1166,6 +1166,7 @@ static void domcreate_rebuild_done(libxl__egc *egc, libxl__multidev_begin(ao, &dcs->multidev); dcs->multidev.callback = domcreate_launch_dm; libxl__add_disks(egc, ao, domid, d_config, &dcs->multidev); + libxl__add_vscsictrls(egc, ao, domid, d_config, &dcs->multidev); libxl__multidev_prepared(egc, &dcs->multidev, 0); return; diff --git a/tools/libxl/libxl_device.c b/tools/libxl/libxl_device.c index 8bb5e93..0bf882c 100644 --- a/tools/libxl/libxl_device.c +++ b/tools/libxl/libxl_device.c @@ -543,6 +543,7 @@ void libxl__multidev_prepared(libxl__egc *egc, * The following functions are defined: * libxl__add_disks * libxl__add_nics + * libxl__add_vscsictrls * libxl__add_vtpms */ @@ -562,6 +563,7 @@ void libxl__multidev_prepared(libxl__egc *egc, DEFINE_DEVICES_ADD(disk) DEFINE_DEVICES_ADD(nic) +DEFINE_DEVICES_ADD(vscsictrl) DEFINE_DEVICES_ADD(vtpm) #undef DEFINE_DEVICES_ADD diff --git a/tools/libxl/libxl_internal.h b/tools/libxl/libxl_internal.h index b194e65..7eb44fb 100644 --- a/tools/libxl/libxl_internal.h +++ b/tools/libxl/libxl_internal.h @@ -2576,6 +2576,10 @@ _hidden void libxl__device_nic_add(libxl__egc *egc, uint32_t domid, libxl_device_nic *nic, libxl__ao_device *aodev); +_hidden void libxl__device_vscsictrl_add(libxl__egc *egc, uint32_t domid, + libxl_device_vscsictrl *vscsi, + libxl__ao_device *aodev); + _hidden void libxl__device_vtpm_add(libxl__egc *egc, uint32_t domid, libxl_device_vtpm *vtpm, libxl__ao_device *aodev); @@ -3294,6 +3298,10 @@ _hidden void libxl__add_nics(libxl__egc *egc, libxl__ao *ao, uint32_t domid, libxl_domain_config *d_config, libxl__multidev *multidev); +_hidden void libxl__add_vscsictrls(libxl__egc *egc, libxl__ao *ao, uint32_t domid, + libxl_domain_config *d_config, + libxl__multidev *multidev); + _hidden void libxl__add_vtpms(libxl__egc *egc, libxl__ao *ao, uint32_t domid, libxl_domain_config *d_config, libxl__multidev *multidev); @@ -3506,6 +3514,11 @@ _hidden void libxl__domain_suspend(libxl__egc *egc, /* used by libxc to suspend the guest during migration */ _hidden void libxl__domain_suspend_callback(void *data); +/* return an array of vscsictrls with num elements */ +_hidden int libxl__vscsi_collect_ctrls(libxl__gc *gc, + uint32_t domid, + libxl_device_vscsictrl **ctrls, + int *num); /* * Convenience macros. @@ -3963,6 +3976,7 @@ static inline void libxl__update_config_vtpm(libxl__gc *gc, * devices have same identifier. */ #define COMPARE_DEVID(a, b) ((a)->devid == (b)->devid) #define COMPARE_DISK(a, b) (!strcmp((a)->vdev, (b)->vdev)) +#define COMPARE_VSCSI(a, b) ((a)->devid == (b)->devid) #define COMPARE_PCI(a, b) ((a)->func == (b)->func && \ (a)->bus == (b)->bus && \ (a)->dev == (b)->dev) diff --git a/tools/libxl/libxl_types.idl b/tools/libxl/libxl_types.idl index 9ad7eba..8c8f049 100644 --- a/tools/libxl/libxl_types.idl +++ b/tools/libxl/libxl_types.idl @@ -630,6 +630,42 @@ libxl_device_channel = Struct("device_channel", [ ])), ]) +libxl_vscsi_pdev_type = Enumeration("vscsi_pdev_type", [ + (0, "INVALID"), + (1, "HCTL"), + (2, "WWN"), + ]) + +libxl_vscsi_hctl = Struct("vscsi_hctl", [ + ("hst", uint32), + ("chn", uint32), + ("tgt", uint32), + ("lun", uint64), + ]) + +libxl_vscsi_pdev = Struct("vscsi_pdev", [ + ("p_devname", string), + ("u", KeyedUnion(None, libxl_vscsi_pdev_type, "type", + [ + ("invalid", None), + ("hctl", Struct(None, [("m", libxl_vscsi_hctl)])), + ("wwn", Struct(None, [("m", string)])), + ])), + ]) + +libxl_device_vscsidev = Struct("device_vscsidev", [ + ("vscsidev_id", libxl_devid), + ("pdev", libxl_vscsi_pdev), + ("vdev", libxl_vscsi_hctl), + ]) + +libxl_device_vscsictrl = Struct("device_vscsictrl", [ + ("backend_domid", libxl_domid), + ("devid", libxl_devid), + ("vscsidevs", Array(libxl_device_vscsidev, "num_vscsidevs")), + ("scsi_raw_cmds", libxl_defbool), + ]) + libxl_domain_config = Struct("domain_config", [ ("c_info", libxl_domain_create_info), ("b_info", libxl_domain_build_info), @@ -641,6 +677,7 @@ libxl_domain_config = Struct("domain_config", [ ("dtdevs", Array(libxl_device_dtdev, "num_dtdevs")), ("vfbs", Array(libxl_device_vfb, "num_vfbs")), ("vkbs", Array(libxl_device_vkb, "num_vkbs")), + ("vscsictrls", Array(libxl_device_vscsictrl, "num_vscsictrls")), ("vtpms", Array(libxl_device_vtpm, "num_vtpms")), # a channel manifests as a console with a name, # see docs/misc/channels.txt @@ -676,6 +713,22 @@ libxl_nicinfo = Struct("nicinfo", [ ("rref_rx", integer), ], dir=DIR_OUT) +libxl_vscsiinfo = Struct("vscsiinfo", [ + ("backend", string), + ("backend_id", uint32), + ("frontend", string), + ("frontend_id", uint32), + ("devid", libxl_devid), + ("pdev", libxl_vscsi_pdev), + ("vdev", libxl_vscsi_hctl), + ("vscsidev_id", libxl_devid), + ("scsi_raw_cmds", bool), + ("vscsictrl_state", integer), + ("vscsidev_state", integer), + ("evtch", integer), + ("rref", integer), + ], dir=DIR_OUT) + libxl_vtpminfo = Struct("vtpminfo", [ ("backend", string), ("backend_id", uint32), diff --git a/tools/libxl/libxl_types_internal.idl b/tools/libxl/libxl_types_internal.idl index 5e55685..84c44f5 100644 --- a/tools/libxl/libxl_types_internal.idl +++ b/tools/libxl/libxl_types_internal.idl @@ -22,6 +22,7 @@ libxl__device_kind = Enumeration("device_kind", [ (6, "VKBD"), (7, "CONSOLE"), (8, "VTPM"), + (9, "VSCSI"), ]) libxl__console_backend = Enumeration("console_backend", [ diff --git a/tools/libxl/libxl_vscsi.c b/tools/libxl/libxl_vscsi.c new file mode 100644 index 0000000..9b3e7aa --- /dev/null +++ b/tools/libxl/libxl_vscsi.c @@ -0,0 +1,344 @@ +/* + * Copyright (C) 2015 SUSE Linux GmbH + * Author Olaf Hering + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; version 2.1 only. with the special + * exception on linking described in file LICENSE. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + */ +#include "libxl_osdeps.h" /* must come before any other headers */ +#include "libxl_internal.h" + +#define XLU_WWN_LEN 16 + +static int vscsi_parse_hctl(char *str, libxl_vscsi_hctl *hctl) +{ + unsigned int hst, chn, tgt; + unsigned long long lun; + + if (sscanf(str, "%u:%u:%u:%llu", &hst, &chn, &tgt, &lun) != 4) + return ERROR_INVAL; + + hctl->hst = hst; + hctl->chn = chn; + hctl->tgt = tgt; + hctl->lun = lun; + return 0; +} + +static bool vscsi_wwn_valid(const char *p) +{ + bool ret = true; + int i = 0; + + for (i = 0; i < XLU_WWN_LEN; i++, p++) { + if (*p >= '0' && *p <= '9') + continue; + if (*p >= 'a' && *p <= 'f') + continue; + if (*p >= 'A' && *p <= 'F') + continue; + ret = false; + break; + } + return ret; +} + +/* Translate p-dev back into pdev.type */ +static bool vscsi_parse_pdev(libxl__gc *gc, libxl_device_vscsidev *dev, + char *c, char *p, char *v) +{ + libxl_vscsi_hctl hctl; + unsigned long long lun; + char wwn[XLU_WWN_LEN + 1]; + bool parsed_ok = false; + + libxl_vscsi_hctl_init(&hctl); + + dev->pdev.p_devname = libxl__strdup(NOGC, c); + + if (strncmp(p, "naa.", 4) == 0) { + /* WWN as understood by pvops */ + memset(wwn, 0, sizeof(wwn)); + if (sscanf(p, "naa.%16c:%llu", wwn, &lun) == 2 && vscsi_wwn_valid(wwn)) { + libxl_vscsi_pdev_init_type(&dev->pdev, LIBXL_VSCSI_PDEV_TYPE_WWN); + dev->pdev.u.wwn.m = libxl__strdup(NOGC, p); + parsed_ok = true; + } + } else if (vscsi_parse_hctl(p, &hctl) == 0) { + /* Either xenlinux, or pvops with properly configured alias in sysfs */ + libxl_vscsi_pdev_init_type(&dev->pdev, LIBXL_VSCSI_PDEV_TYPE_HCTL); + libxl_vscsi_hctl_copy(CTX, &dev->pdev.u.hctl.m, &hctl); + parsed_ok = true; + } + + if (parsed_ok && vscsi_parse_hctl(v, &dev->vdev) != 0) + parsed_ok = false; + + libxl_vscsi_hctl_dispose(&hctl); + + return parsed_ok; +} + +void libxl_device_vscsictrl_append_vscsidev(libxl_ctx *ctx, + libxl_device_vscsictrl *ctrl, + libxl_device_vscsidev *dev) +{ + GC_INIT(ctx); + ctrl->vscsidevs = libxl__realloc(NOGC, ctrl->vscsidevs, sizeof(*dev) * (ctrl->num_vscsidevs + 1)); + libxl_device_vscsidev_init(ctrl->vscsidevs + ctrl->num_vscsidevs); + libxl_device_vscsidev_copy(CTX, ctrl->vscsidevs + ctrl->num_vscsidevs, dev); + ctrl->num_vscsidevs++; + GC_FREE; +} + +static bool libxl__vscsi_fill_dev(libxl__gc *gc, + xs_transaction_t t, + const char *devs_path, + const char *dev_dir, + libxl_device_vscsidev *dev) +{ + char *path, *c, *p, *v, *s; + unsigned int devid; + int r; + bool parsed_ok; + + r = sscanf(dev_dir, "dev-%u", &devid); + if (r != 1) { + LOG(ERROR, "expected dev-N, got '%s'", dev_dir); + return false; + } + dev->vscsidev_id = devid; + + path = GCSPRINTF("%s/%s", devs_path, dev_dir); + c = libxl__xs_read(gc, t, GCSPRINTF("%s/p-devname", path)); + p = libxl__xs_read(gc, t, GCSPRINTF("%s/p-dev", path)); + v = libxl__xs_read(gc, t, GCSPRINTF("%s/v-dev", path)); + s = libxl__xs_read(gc, t, GCSPRINTF("%s/state", path)); + LOG(DEBUG, "%s/state is %s", path, s); + if (!(c && p && v && s)) { + LOG(ERROR, "p-devname '%s' p-dev '%s' v-dev '%s'", c, p, v); + return false; + } + + parsed_ok = vscsi_parse_pdev(gc, dev, c, p, v); + if (!parsed_ok) { + LOG(ERROR, "failed to parse %s: %s %s %s %s", path, c, p, v, s); + return false; + } + + switch (atoi(s)) { + case XenbusStateUnknown: + case XenbusStateInitialising: + case XenbusStateInitWait: + case XenbusStateInitialised: + case XenbusStateConnected: + case XenbusStateReconfiguring: + case XenbusStateReconfigured: + parsed_ok = true; + break; + case XenbusStateClosing: + case XenbusStateClosed: + parsed_ok = false; + break; + } + + if (!parsed_ok) + LOG(ERROR, "unexpected state in %s: %s", path, s); + + return parsed_ok; +} + +static bool libxl__vscsi_fill_ctrl(libxl__gc *gc, + xs_transaction_t t, + const char *fe_path, + const char *dir, + libxl_device_vscsictrl *ctrl) +{ + libxl_device_vscsidev dev; + char *tmp, *be_path, *devs_path; + char **dev_dirs; + unsigned int ndev_dirs, dev_dir; + bool parsed_ok; + + ctrl->devid = atoi(dir); + + be_path = libxl__xs_read(gc, t, GCSPRINTF("%s/%s/backend", fe_path, dir)); + /* FIXME what if xenstore is broken? */ + if (!be_path) { + libxl_defbool_set(&ctrl->scsi_raw_cmds, false); + return false; + } + + tmp = libxl__xs_read(gc, t, GCSPRINTF("%s/%s/backend-id", fe_path, dir)); + /* FIXME what if xenstore is broken? */ + if (tmp) + ctrl->backend_domid = atoi(tmp); + + parsed_ok = false; + tmp = libxl__xs_read(gc, t, GCSPRINTF("%s/feature-host", be_path)); + if (tmp) + parsed_ok = atoi(tmp) != 0; + libxl_defbool_set(&ctrl->scsi_raw_cmds, parsed_ok); + + parsed_ok = true; + devs_path = GCSPRINTF("%s/vscsi-devs", be_path); + dev_dirs = libxl__xs_directory(gc, t, devs_path, &ndev_dirs); + for (dev_dir = 0; dev_dirs && dev_dir < ndev_dirs; dev_dir++) { + libxl_device_vscsidev_init(&dev); + parsed_ok = libxl__vscsi_fill_dev(gc, t, devs_path, dev_dirs[dev_dir], &dev); + if (parsed_ok == true) + libxl_device_vscsictrl_append_vscsidev(CTX, ctrl, &dev); + libxl_device_vscsidev_dispose(&dev); + if (parsed_ok == false) + break; + } + + return parsed_ok; +} + +int libxl__vscsi_collect_ctrls(libxl__gc *gc, + uint32_t domid, + libxl_device_vscsictrl **ctrls, + int *num) +{ + xs_transaction_t t = XBT_NULL; + libxl_device_vscsictrl ctrl; + char *fe_path; + char **dirs; + unsigned int ndirs = 0, dir; + int rc; + + fe_path = GCSPRINTF("%s/device/vscsi", libxl__xs_get_dompath(gc, domid)); + + for (;;) { + *num = 0; + + rc = libxl__xs_transaction_start(gc, &t); + if (rc) goto out; + + dirs = libxl__xs_directory(gc, t, fe_path, &ndirs); + /* Nothing to do */ + if (!(dirs && ndirs)) + break; + + /* List of ctrls to be returned to the caller */ + *ctrls = libxl__malloc(NOGC, ndirs * sizeof(**ctrls)); + + for (dir = 0; dir < ndirs; dir++) { + libxl_device_vscsictrl_init(*ctrls + dir); + + libxl_device_vscsictrl_init(&ctrl); + if (libxl__vscsi_fill_ctrl(gc, t, fe_path, dirs[dir], &ctrl)) { + libxl_device_vscsictrl_copy(CTX, *ctrls + *num, &ctrl); + (*num)++; + } + libxl_device_vscsictrl_dispose(&ctrl); + } + + rc = libxl__xs_transaction_commit(gc, &t); + if (!rc) break; + + if (rc < 0) { + for (dir = 0; dir < ndirs; dir++) + libxl_device_vscsictrl_dispose(*ctrls + dir); + free(*ctrls); + *ctrls = NULL; + *num = 0; + goto out; + } + } + +out: + libxl__xs_transaction_abort(gc, &t); + return rc; +} + +libxl_device_vscsictrl *libxl_device_vscsictrl_list(libxl_ctx *ctx, + uint32_t domid, + int *num) +{ + GC_INIT(ctx); + libxl_device_vscsictrl *ctrls = NULL; + int rc, num_ctrls = 0; + + *num = 0; + + rc = libxl__vscsi_collect_ctrls(gc, domid, &ctrls, &num_ctrls); + if (rc == 0) + *num = num_ctrls; + + GC_FREE; + return ctrls; +} + +int libxl_device_vscsictrl_getinfo(libxl_ctx *ctx, uint32_t domid, + libxl_device_vscsictrl *vscsictrl, + libxl_device_vscsidev *vscsidev, + libxl_vscsiinfo *vscsiinfo) +{ + GC_INIT(ctx); + char *dompath, *vscsipath; + char *val; + int rc = ERROR_FAIL; + + libxl_vscsiinfo_init(vscsiinfo); + dompath = libxl__xs_get_dompath(gc, domid); + vscsiinfo->devid = vscsictrl->devid; + vscsiinfo->vscsidev_id = vscsidev->vscsidev_id; + libxl_vscsi_pdev_copy(ctx, &vscsiinfo->pdev, &vscsidev->pdev); + libxl_vscsi_hctl_copy(ctx, &vscsiinfo->vdev, &vscsidev->vdev); + + vscsipath = GCSPRINTF("%s/device/vscsi/%d", dompath, vscsiinfo->devid); + vscsiinfo->backend = xs_read(ctx->xsh, XBT_NULL, + GCSPRINTF("%s/backend", vscsipath), NULL); + if (!vscsiinfo->backend) + goto out; + if(!libxl__xs_read(gc, XBT_NULL, vscsiinfo->backend)) + goto out; + + val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/backend-id", vscsipath)); + vscsiinfo->backend_id = val ? strtoul(val, NULL, 10) : -1; + + val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/state", vscsipath)); + vscsiinfo->vscsictrl_state = val ? strtoul(val, NULL, 10) : -1; + + val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/event-channel", vscsipath)); + vscsiinfo->evtch = val ? strtoul(val, NULL, 10) : -1; + + val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/ring-ref", vscsipath)); + vscsiinfo->rref = val ? strtoul(val, NULL, 10) : -1; + + vscsiinfo->frontend = xs_read(ctx->xsh, XBT_NULL, + GCSPRINTF("%s/frontend", vscsiinfo->backend), NULL); + + val = libxl__xs_read(gc, XBT_NULL, + GCSPRINTF("%s/frontend-id", vscsiinfo->backend)); + vscsiinfo->frontend_id = val ? strtoul(val, NULL, 10) : -1; + + val = libxl__xs_read(gc, XBT_NULL, + GCSPRINTF("%s/vscsi-devs/dev-%u/state", + vscsiinfo->backend, vscsidev->vscsidev_id)); + vscsiinfo->vscsidev_state = val ? strtoul(val, NULL, 10) : -1; + + rc = 0; +out: + GC_FREE; + return rc; +} + + + +/* + * Local variables: + * mode: C + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/tools/libxl/libxlu_vscsi.c b/tools/libxl/libxlu_vscsi.c new file mode 100644 index 0000000..0e81b5f --- /dev/null +++ b/tools/libxl/libxlu_vscsi.c @@ -0,0 +1,718 @@ +/* + * libxlu_vscsi.c - xl configuration file parsing: setup and helper functions + * + * Copyright (C) 2015 SUSE Linux GmbH + * Author Olaf Hering + * Author Ond?ej Hole?ek + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; version 2.1 only. with the special + * exception on linking described in file LICENSE. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + */ +#include "libxl_osdeps.h" /* must come before any other headers */ +#include +#include +#include +#include +#include +#include "libxlu_internal.h" + +#ifdef __linux__ +#define LOG(_c, _x, _a...) \ + if((_c) && (_c)->report) fprintf((_c)->report, "%s(%u): " _x "\n", __func__, __LINE__, ##_a) + +#define XLU_SYSFS_TARGET_PVSCSI "/sys/kernel/config/target/xen-pvscsi" +#define XLU_WWN_LEN 16 +struct xlu__vscsi_target { + XLU_Config *cfg; + libxl_vscsi_hctl *pdev_hctl; + libxl_vscsi_pdev *pdev; + char path[PATH_MAX]; + char udev_path[PATH_MAX]; + char wwn[XLU_WWN_LEN + 1]; + unsigned long long lun; +}; + +static int xlu__vscsi_parse_hctl(char *str, libxl_vscsi_hctl *hctl) +{ + unsigned int hst, chn, tgt; + unsigned long long lun; + + if (sscanf(str, "%u:%u:%u:%llu", &hst, &chn, &tgt, &lun) != 4) + return ERROR_INVAL; + + hctl->hst = hst; + hctl->chn = chn; + hctl->tgt = tgt; + hctl->lun = lun; + return 0; +} + +static char *xlu__vscsi_trim_string(char *s) +{ + size_t len; + + while (isspace(*s)) + s++; + len = strlen(s); + while (len-- > 1 && isspace(s[len])) + s[len] = '\0'; + return s; +} + + +static int xlu__vscsi_parse_dev(XLU_Config *cfg, char *pdev, libxl_vscsi_hctl *hctl) +{ + struct stat dentry; + char *sysfs = NULL; + const char *type; + int rc, found = 0; + DIR *dirp; + struct dirent *de; + + /* stat pdev to get device's sysfs entry */ + if (stat (pdev, &dentry) < 0) { + LOG(cfg, "%s, device node not found", pdev); + rc = ERROR_INVAL; + goto out; + } + + if (S_ISBLK (dentry.st_mode)) { + type = "block"; + } else if (S_ISCHR (dentry.st_mode)) { + type = "char"; + } else { + LOG(cfg, "%s, device node not a block or char device", pdev); + rc = ERROR_INVAL; + goto out; + } + + /* /sys/dev/type/major:minor symlink added in 2.6.27 */ + if (asprintf(&sysfs, "/sys/dev/%s/%u:%u/device/scsi_device", type, + major(dentry.st_rdev), minor(dentry.st_rdev)) < 0) { + sysfs = NULL; + rc = ERROR_NOMEM; + goto out; + } + + dirp = opendir(sysfs); + if (!dirp) { + LOG(cfg, "%s, no major:minor link in sysfs", pdev); + rc = ERROR_INVAL; + goto out; + } + + while ((de = readdir(dirp))) { + if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) + continue; + + if (xlu__vscsi_parse_hctl(de->d_name, hctl)) + continue; + + found = 1; + break; + } + closedir(dirp); + + if (!found) { + LOG(cfg, "%s, no h:c:t:l link in sysfs", pdev); + rc = ERROR_INVAL; + goto out; + } + + rc = 0; +out: + free(sysfs); + return rc; +} + +static bool xlu__vscsi_wwn_valid(const char *p) +{ + bool ret = true; + int i = 0; + + for (i = 0; i < XLU_WWN_LEN; i++, p++) { + if (*p >= '0' && *p <= '9') + continue; + if (*p >= 'a' && *p <= 'f') + continue; + if (*p >= 'A' && *p <= 'F') + continue; + ret = false; + break; + } + return ret; +} + +static bool xlu__vscsi_compare_hctl(libxl_vscsi_hctl *a, libxl_vscsi_hctl *b) +{ + if (a->hst == b->hst && + a->chn == b->chn && + a->tgt == b->tgt && + a->lun == b->lun) + return true; + return false; +} + +/* Finally at + * /sys/kernel/config/target/xen-pvscsi/naa./tpgt_1/lun/lun_0//udev_path + */ +static bool xlu__vscsi_compare_udev(struct xlu__vscsi_target *tgt) +{ + bool ret; + int fd; + ssize_t read_sz; + libxl_vscsi_hctl udev_hctl; + + libxl_vscsi_hctl_init(&udev_hctl); + + fd = open(tgt->path, O_RDONLY); + if (fd < 0){ + ret = false; + goto out; + } + read_sz = read(fd, &tgt->udev_path, sizeof(tgt->udev_path) - 1); + close(fd); + + if (read_sz <= 0 || read_sz > sizeof(tgt->udev_path) - 1) { + ret = false; + goto out; + } + tgt->udev_path[read_sz] = '\0'; + read_sz--; + if (tgt->udev_path[read_sz] == '\n') + tgt->udev_path[read_sz] = '\0'; + + if (xlu__vscsi_parse_dev(tgt->cfg, tgt->udev_path, &udev_hctl)) { + ret = false; + goto out; + } + ret = xlu__vscsi_compare_hctl(tgt->pdev_hctl, &udev_hctl); + +out: + libxl_vscsi_hctl_dispose(&udev_hctl); + return ret; +} + +/* /sys/kernel/config/target/xen-pvscsi/naa./tpgt_1/lun/lun_0//udev_path */ +static bool xlu__vscsi_walk_dir_lun(struct xlu__vscsi_target *tgt) +{ + bool found; + DIR *dirp; + struct dirent *de; + size_t path_len = strlen(tgt->path); + char *subdir = &tgt->path[path_len]; + + dirp = opendir(tgt->path); + if (!dirp) + return false; + + found = false; + while ((de = readdir(dirp))) { + if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) + continue; + + snprintf(subdir, sizeof(tgt->path) - path_len, "/%s/udev_path", de->d_name); + + found = xlu__vscsi_compare_udev(tgt); + if (found) + break; + + *subdir = '\0'; + } + closedir(dirp); + return found; +} + +/* /sys/kernel/config/target/xen-pvscsi/naa./tpgt_1/lun/lun_0 */ +static bool xlu__vscsi_walk_dir_luns(struct xlu__vscsi_target *tgt) +{ + bool found; + DIR *dirp; + struct dirent *de; + size_t path_len = strlen(tgt->path); + char *subdir = &tgt->path[path_len]; + + dirp = opendir(tgt->path); + if (!dirp) + return false; + + found = false; + while ((de = readdir(dirp))) { + if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) + continue; + + if (sscanf(de->d_name, "lun_%llu", &tgt->lun) != 1) + continue; + + + snprintf(subdir, sizeof(tgt->path) - path_len, "/%s", de->d_name); + + found = xlu__vscsi_walk_dir_lun(tgt); + if (found) + break; + + *subdir = '\0'; + } + closedir(dirp); + return found; +} + +/* /sys/kernel/config/target/xen-pvscsi/naa./tpgt_1 */ +static bool xlu__vscsi_walk_dir_naa(struct xlu__vscsi_target *tgt) +{ + bool found; + DIR *dirp; + struct dirent *de; + size_t path_len = strlen(tgt->path); + char *subdir = &tgt->path[path_len]; + unsigned int tpgt; + + dirp = opendir(tgt->path); + if (!dirp) + return false; + + found = false; + while ((de = readdir(dirp))) { + if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) + continue; + + if (sscanf(de->d_name, "tpgt_%u", &tpgt) != 1) + continue; + + snprintf(subdir, sizeof(tgt->path) - path_len, "/%s/lun", de->d_name); + + found = xlu__vscsi_walk_dir_luns(tgt); + if (found) + break; + + *subdir = '\0'; + } + closedir(dirp); + return found; +} + +/* /sys/kernel/config/target/xen-pvscsi/naa. */ +static bool xlu__vscsi_find_target_wwn(struct xlu__vscsi_target *tgt) +{ + bool found; + DIR *dirp; + struct dirent *de; + size_t path_len = strlen(tgt->path); + char *subdir = &tgt->path[path_len]; + + dirp = opendir(tgt->path); + if (!dirp) + return false; + + found = false; + while ((de = readdir(dirp))) { + if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) + continue; + + if (sscanf(de->d_name, "naa.%16c", tgt->wwn) != 1) + continue; + if (!xlu__vscsi_wwn_valid(tgt->wwn)) + continue; + + snprintf(subdir, sizeof(tgt->path) - path_len, "/%s", de->d_name); + + found = xlu__vscsi_walk_dir_naa(tgt); + if (found) + break; + + *subdir = '\0'; + } + closedir(dirp); + return found; +} + +/* + * Convert pdev from config string into pdev property for backend, + * which is either h:c:t:l for xenlinux or naa.wwn:lun for pvops + */ +static int xlu__vscsi_dev_to_pdev(XLU_Config *cfg, libxl_ctx *ctx, char *str, + libxl_vscsi_hctl *pdev_hctl, + libxl_vscsi_pdev *pdev) +{ + int rc = ERROR_INVAL; + struct xlu__vscsi_target *tgt; + static const char xen_pvscsi[] = XLU_SYSFS_TARGET_PVSCSI; + + /* First get hctl representation of config item */ + if (xlu__vscsi_parse_dev(cfg, str, pdev_hctl)) + goto out; + + /* Check if a SCSI target item exists for the config item */ + if (access(xen_pvscsi, F_OK) == 0) { + tgt = calloc(1, sizeof(*tgt)); + if (!tgt) { + rc = ERROR_NOMEM; + goto out; + } + tgt->cfg = cfg; + tgt->pdev_hctl = pdev_hctl; + tgt->pdev = pdev; + snprintf(tgt->path, sizeof(tgt->path), "%s", xen_pvscsi); + if (xlu__vscsi_find_target_wwn(tgt) == true) { + LOG(cfg, "'%s' maps to '%s(%s)'", str, tgt->path, tgt->udev_path); + libxl_vscsi_pdev_init_type(pdev, LIBXL_VSCSI_PDEV_TYPE_WWN); + if (asprintf(&pdev->u.wwn.m, "naa.%s:%llu", tgt->wwn, tgt->lun) < 0) { + rc = ERROR_NOMEM; + goto out; + } + } + free(tgt); + } else { + /* Assume xenlinux backend */ + libxl_vscsi_pdev_init_type(pdev, LIBXL_VSCSI_PDEV_TYPE_HCTL); + libxl_vscsi_hctl_copy(ctx, &pdev->u.hctl.m, pdev_hctl); + } + rc = 0; + +out: + return rc; +} + +/* WWN as understood by pvops */ +static int xlu__vscsi_wwn_to_pdev(XLU_Config *cfg, char *str, libxl_vscsi_pdev *pdev) +{ + int rc = ERROR_INVAL; + unsigned long long lun; + char wwn[XLU_WWN_LEN + 1]; + + memset(wwn, 0, sizeof(wwn)); + if (sscanf(str, "naa.%16c:%llu", wwn, &lun) == 2 && xlu__vscsi_wwn_valid(wwn)) { + libxl_vscsi_pdev_init_type(pdev, LIBXL_VSCSI_PDEV_TYPE_WWN); + pdev->u.wwn.m = strdup(str); + rc = pdev->u.wwn.m ? 0 : ERROR_NOMEM; + } + return rc; +} + +static int xlu__vscsi_parse_pdev(XLU_Config *cfg, libxl_ctx *ctx, char *str, + libxl_vscsi_pdev *pdev) +{ + int rc = ERROR_INVAL; + libxl_vscsi_hctl pdev_hctl; + + libxl_vscsi_hctl_init(&pdev_hctl); + if (strncmp(str, "/dev/", 5) == 0) { + rc = xlu__vscsi_dev_to_pdev(cfg, ctx, str, &pdev_hctl, pdev); + } else if (strncmp(str, "naa.", 4) == 0) { + rc = xlu__vscsi_wwn_to_pdev(cfg, str, pdev); + } else if (xlu__vscsi_parse_hctl(str, &pdev_hctl) == 0) { + /* Either xenlinux, or pvops with properly configured alias in sysfs */ + libxl_vscsi_pdev_init_type(pdev, LIBXL_VSCSI_PDEV_TYPE_HCTL); + libxl_vscsi_hctl_copy(ctx, &pdev->u.hctl.m, &pdev_hctl); + rc = 0; + } + + if (rc == 0) { + pdev->p_devname = strdup(str); + if (!pdev->p_devname) + rc = ERROR_NOMEM; + } + + libxl_vscsi_hctl_dispose(&pdev_hctl); + return rc; +} + +int xlu_vscsi_parse(XLU_Config *cfg, libxl_ctx *ctx, const char *str, + libxl_device_vscsictrl *new_ctrl, + libxl_device_vscsidev *new_dev) +{ + int rc; + char *tmp, *pdev, *vdev, *fhost; + + tmp = strdup(str); + if (!tmp) { + rc = ERROR_NOMEM; + goto out; + } + + pdev = strtok(tmp, ","); + vdev = strtok(NULL, ","); + fhost = strtok(NULL, ","); + if (!(pdev && vdev)) { + LOG(cfg, "invalid devspec: '%s'\n", str); + rc = ERROR_INVAL; + goto out; + } + + pdev = xlu__vscsi_trim_string(pdev); + vdev = xlu__vscsi_trim_string(vdev); + + rc = xlu__vscsi_parse_pdev(cfg, ctx, pdev, &new_dev->pdev); + if (rc) { + LOG(cfg, "failed to parse %s, rc == %d", pdev, rc); + goto out; + } + + if (xlu__vscsi_parse_hctl(vdev, &new_dev->vdev)) { + LOG(cfg, "invalid '%s', expecting hst:chn:tgt:lun", vdev); + rc = ERROR_INVAL; + goto out; + } + + /* Record group index */ + new_ctrl->devid = new_dev->vdev.hst; + + if (fhost) { + fhost = xlu__vscsi_trim_string(fhost); + if (strcmp(fhost, "feature-host") == 0) { + libxl_defbool_set(&new_ctrl->scsi_raw_cmds, true); + } else { + LOG(cfg, "invalid option '%s', expecting %s", fhost, "feature-host"); + rc = ERROR_INVAL; + goto out; + } + } else + libxl_defbool_set(&new_ctrl->scsi_raw_cmds, false); + rc = 0; + +out: + free(tmp); + return rc; +} + +int xlu_vscsi_get_ctrl(XLU_Config *cfg, libxl_ctx *ctx, uint32_t domid, + const char *str, libxl_device_vscsictrl *vscsictrl) +{ + libxl_device_vscsidev *new_dev = NULL; + libxl_device_vscsictrl *new_ctrl, *vscsictrls = NULL, *tmp; + int rc, found_ctrl = -1, i; + int num_ctrls; + + new_ctrl = malloc(sizeof(*new_ctrl)); + new_dev = malloc(sizeof(*new_dev)); + if (!(new_ctrl && new_dev)) { + rc = ERROR_NOMEM; + goto out; + } + libxl_device_vscsictrl_init(new_ctrl); + libxl_device_vscsidev_init(new_dev); + + rc = xlu_vscsi_parse(cfg, ctx, str, new_ctrl, new_dev); + if (rc) + goto out; + + /* Look for existing vscsictrl for given domain */ + vscsictrls = libxl_device_vscsictrl_list(ctx, domid, &num_ctrls); + if (vscsictrls) { + for (i = 0; i < num_ctrls; ++i) { + if (vscsictrls[i].devid == new_ctrl->devid) { + found_ctrl = i; + break; + } + } + } + + if (found_ctrl == -1) { + /* Not found, create new ctrl */ + tmp = new_ctrl; + } else { + tmp = vscsictrls + found_ctrl; + + /* Check if the vdev address is already taken */ + for (i = 0; i < tmp->num_vscsidevs; ++i) { + if (tmp->vscsidevs[i].vdev.chn == new_dev->vdev.chn && + tmp->vscsidevs[i].vdev.tgt == new_dev->vdev.tgt && + tmp->vscsidevs[i].vdev.lun == new_dev->vdev.lun) { + unsigned long long lun = new_dev->vdev.lun; + LOG(cfg, "vdev '%u:%u:%u:%llu' is already used.\n", + new_dev->vdev.hst, new_dev->vdev.chn, new_dev->vdev.tgt, lun); + rc = ERROR_INVAL; + goto out; + } + } + + if (libxl_defbool_val(new_ctrl->scsi_raw_cmds) != + libxl_defbool_val(tmp->scsi_raw_cmds)) { + LOG(cfg, "different feature-host setting: " + "existing ctrl has it %s, new ctrl has it %s\n", + libxl_defbool_val(new_ctrl->scsi_raw_cmds) ? "set" : "unset", + libxl_defbool_val(tmp->scsi_raw_cmds) ? "set" : "unset"); + rc = ERROR_INVAL; + goto out; + } + } + + libxl_device_vscsictrl_copy(ctx, vscsictrl, tmp); + libxl_device_vscsictrl_append_vscsidev(ctx, vscsictrl, new_dev); + + rc = 0; + +out: + if (vscsictrls) { + for (i = 0; i < num_ctrls; ++i) + libxl_device_vscsictrl_dispose(&vscsictrls[i]); + free(vscsictrls); + } + libxl_device_vscsidev_dispose(new_dev); + libxl_device_vscsictrl_dispose(new_ctrl); + free(new_dev); + free(new_ctrl); + return rc; +} + +int xlu_vscsi_detach(XLU_Config *cfg, libxl_ctx *ctx, uint32_t domid, char *str) +{ + libxl_device_vscsidev dev = { }, *vd; + libxl_device_vscsictrl ctrl = { }, *vc, *vscsictrls; + int num_ctrls, c, d, found = 0; + char *tmp = NULL; + + libxl_device_vscsictrl_init(&ctrl); + libxl_device_vscsidev_init(&dev); + + /* Create a dummy cfg */ + if (asprintf(&tmp, "0:0:0:0,%s", str) < 0) { + LOG(cfg, "asprintf failed while removing %s from domid %u", str, domid); + goto out; + } + + if (xlu_vscsi_parse(cfg, ctx, tmp, &ctrl, &dev)) + goto out; + + libxl_device_vscsictrl_append_vscsidev(ctx, &ctrl, &dev); + + vscsictrls = libxl_device_vscsictrl_list(ctx, domid, &num_ctrls); + if (!vscsictrls) + goto out; + + for (c = 0; c < num_ctrls; ++c) { + vc = vscsictrls + c; + for (d = 0; d < vc->num_vscsidevs; d++) { + vd = vc->vscsidevs + d; +#define CMP(member) (vd->vdev.member == dev.vdev.member) + if (vc->devid == ctrl.devid && + CMP(hst) && CMP(chn) && CMP(tgt) && CMP(lun)) { + if (vc->num_vscsidevs > 1) { + /* Remove single vscsidev connected to this vscsictrl */; + ctrl.devid = vc->devid; + ctrl.vscsidevs[0].vscsidev_id = vd->vscsidev_id; + libxl_device_vscsidev_remove(ctx, domid, &ctrl, NULL); + } else { + /* Wipe entire vscsictrl */; + libxl_device_vscsictrl_remove(ctx, domid, vc, NULL); + found = 1; + } + } +#undef CMP + libxl_device_vscsidev_dispose(vd); + } + libxl_device_vscsictrl_dispose(vc); + } + free(vscsictrls); + +out: + free(tmp); + libxl_device_vscsidev_dispose(&dev); + libxl_device_vscsictrl_dispose(&ctrl); + return found; +} + +int xlu_vscsi_config_add(XLU_Config *cfg, + libxl_ctx *ctx, + const char *str, + int *num_vscsis, + libxl_device_vscsictrl **vscsis) +{ + int rc, i; + libxl_device_vscsidev dev = { }; + libxl_device_vscsictrl *tmp_ctrl, ctrl = { }; + bool ctrl_found = false; + + /* + * #1: parse the devspec and place it in temporary ctrl+dev part + * #2: find existing vscsictrl with number vdev.hst + * if found, append the vscsidev to this vscsictrl + * #3: otherwise, create new vscsictrl and append vscsidev + * Note: vdev.hst does not represent the index named "num_vscsis", + * it is a private index used just in the config file + */ + libxl_device_vscsictrl_init(&ctrl); + libxl_device_vscsidev_init(&dev); + + rc = xlu_vscsi_parse(cfg, ctx, str, &ctrl, &dev); + if (rc) + goto out; + + if (*num_vscsis) { + for (i = 0; i < *num_vscsis; i++) { + tmp_ctrl = *vscsis + i; + if (tmp_ctrl->devid == ctrl.devid) { + libxl_device_vscsictrl_append_vscsidev(ctx, tmp_ctrl, &dev); + ctrl_found = true; + break; + } + } + } + + if (!ctrl_found || !*num_vscsis) { + tmp_ctrl = realloc(*vscsis, sizeof(ctrl) * (*num_vscsis + 1)); + if (!tmp_ctrl) { + LOG(cfg, "realloc #%d failed", *num_vscsis + 1); + rc = ERROR_NOMEM; + goto out; + } + *vscsis = tmp_ctrl; + tmp_ctrl = *vscsis + *num_vscsis; + libxl_device_vscsictrl_init(tmp_ctrl); + + libxl_device_vscsictrl_copy(ctx, tmp_ctrl, &ctrl); + + libxl_device_vscsictrl_append_vscsidev(ctx, tmp_ctrl, &dev); + + (*num_vscsis)++; + } + + rc = 0; +out: + libxl_device_vscsidev_dispose(&dev); + libxl_device_vscsictrl_dispose(&ctrl); + return rc; +} +#else /* ! __linux__ */ +int xlu_vscsi_get_ctrl(XLU_Config *config, + libxl_ctx *ctx, + uint32_t domid, + const char *str, + libxl_device_vscsictrl *vscsictrl) +{ + return ERROR_INVAL; +} + +int xlu_vscsi_parse(XLU_Config *cfg, + libxl_ctx *ctx, + const char *str, + libxl_device_vscsictrl *new_ctrl, + libxl_device_vscsidev *new_dev) +{ + return ERROR_INVAL; +} + +int xlu_vscsi_detach(XLU_Config *cfg, + libxl_ctx *ctx, + uint32_t domid, + char *str) +{ + return ERROR_INVAL; +} + +int xlu_vscsi_config_add(XLU_Config *cfg, + libxl_ctx *ctx, + const char *str, + int *num_vscsis, + libxl_device_vscsictrl **vscsis) +{ + return ERROR_INVAL; +} +#endif diff --git a/tools/libxl/libxlutil.h b/tools/libxl/libxlutil.h index e81b644..886c625 100644 --- a/tools/libxl/libxlutil.h +++ b/tools/libxl/libxlutil.h @@ -118,6 +118,24 @@ int xlu_rdm_parse(XLU_Config *cfg, libxl_rdm_reserve *rdm, const char *str); int xlu_vif_parse_rate(XLU_Config *cfg, const char *rate, libxl_device_nic *nic); +/* Fill vscsictrl with device described in str (pdev,vdev[,options]) */ +int xlu_vscsi_get_ctrl(XLU_Config *config, + libxl_ctx *ctx, + uint32_t domid, + const char *str, + libxl_device_vscsictrl *vscsictrl); +/* Parse config string and fill provided vscsi ctrl and vscsi device */ +int xlu_vscsi_parse(XLU_Config *cfg, libxl_ctx *ctx, const char *str, + libxl_device_vscsictrl *new_ctrl, + libxl_device_vscsidev *new_dev); +/* Detach vscsi device described in config string (pdev,vdev[,options]) */ +int xlu_vscsi_detach(XLU_Config *cfg, libxl_ctx *ctx, uint32_t domid, char *str); +/* Add vscsi device described in config string (pdev,vdev[,options]) to d_config */ +int xlu_vscsi_config_add(XLU_Config *cfg, + libxl_ctx *ctx, + const char *str, + int *num_vscsis, + libxl_device_vscsictrl **vscsis); #endif /* LIBXLUTIL_H */ /* diff --git a/tools/libxl/xl.h b/tools/libxl/xl.h index bdab125..877c695 100644 --- a/tools/libxl/xl.h +++ b/tools/libxl/xl.h @@ -89,6 +89,9 @@ int main_channellist(int argc, char **argv); int main_blockattach(int argc, char **argv); int main_blocklist(int argc, char **argv); int main_blockdetach(int argc, char **argv); +int main_vscsiattach(int argc, char **argv); +int main_vscsilist(int argc, char **argv); +int main_vscsidetach(int argc, char **argv); int main_vtpmattach(int argc, char **argv); int main_vtpmlist(int argc, char **argv); int main_vtpmdetach(int argc, char **argv); diff --git a/tools/libxl/xl_cmdimpl.c b/tools/libxl/xl_cmdimpl.c index 25507c7..e311538 100644 --- a/tools/libxl/xl_cmdimpl.c +++ b/tools/libxl/xl_cmdimpl.c @@ -1263,7 +1263,7 @@ static void parse_config_data(const char *config_source, const char *buf; long l, vcpus = 0; XLU_Config *config; - XLU_ConfigList *cpus, *vbds, *nics, *pcis, *cvfbs, *cpuids, *vtpms; + XLU_ConfigList *cpus, *vbds, *nics, *pcis, *cvfbs, *cpuids, *vtpms, *vscsictrls; XLU_ConfigList *channels, *ioports, *irqs, *iomem, *viridian, *dtdevs; int num_ioports, num_irqs, num_iomem, num_cpus, num_viridian; int pci_power_mgmt = 0; @@ -1793,6 +1793,17 @@ static void parse_config_data(const char *config_source, } } + if (!xlu_cfg_get_list(config, "vscsi", &vscsictrls, 0, 0)) { + int num_vscsi_items = 0; + d_config->num_vscsictrls = 0; + d_config->vscsictrls = NULL; + while ((buf = xlu_cfg_get_listitem (vscsictrls, num_vscsi_items)) != NULL) { + if (xlu_vscsi_config_add(config, ctx, buf, &d_config->num_vscsictrls, &d_config->vscsictrls)) + exit(1); + num_vscsi_items++; + } + } + if (!xlu_cfg_get_list(config, "vtpm", &vtpms, 0, 0)) { d_config->num_vtpms = 0; d_config->vtpms = NULL; @@ -6744,6 +6755,201 @@ int main_blockdetach(int argc, char **argv) return rc; } +int main_vscsiattach(int argc, char **argv) +{ + uint32_t domid; + int opt, rc; + XLU_Config *config = NULL; + libxl_device_vscsictrl *vscsictrl = NULL; + char *str = NULL, *feat_buf = NULL; + + SWITCH_FOREACH_OPT(opt, "", NULL, "scsi-attach", 1) { + /* No options */ + } + + if (argc < 4 || argc > 5) { + help("scsi-attach"); + return 1; + } + + if (libxl_domain_qualifier_to_domid(ctx, argv[optind], &domid) < 0) { + fprintf(stderr, "%s is an invalid domain identifier\n", argv[optind]); + return 1; + } + + optind++; + + if (argc == 5) { + if (asprintf(&feat_buf, ",%s", argv[4]) < 0) { + perror("asprintf"); + return 1; + } + } + + if (asprintf(&str, "%s,%s%s", argv[2], argv[3], feat_buf ?: "") < 0) { + perror("asprintf"); + rc = 1; + goto out;; + } + + vscsictrl = xmalloc(sizeof(*vscsictrl)); + libxl_device_vscsictrl_init(vscsictrl); + + config = xlu_cfg_init(stderr, "command line"); + if (!config) { + fprintf(stderr, "Failed to allocate for configuration\n"); + rc = 1; + goto out; + } + + /* Parse config string and store result */ + rc = xlu_vscsi_get_ctrl(config, ctx, domid, str, vscsictrl); + if (rc < 0) + goto out; + + if (dryrun_only) { + char *json = libxl_device_vscsictrl_to_json(ctx, vscsictrl); + printf("vscsi: %s\n", json); + free(json); + if (ferror(stdout) || fflush(stdout)) { perror("stdout"); exit(-1); } + rc = 0; + goto out; + } + + /* Finally add the device */ + if (libxl_device_vscsictrl_add(ctx, domid, vscsictrl, NULL)) { + fprintf(stderr, "libxl_device_vscsictrl_add failed.\n"); + rc = 1; + goto out; + } + + rc = 0; +out: + if (config) + xlu_cfg_destroy(config); + libxl_device_vscsictrl_dispose(vscsictrl); + free(vscsictrl); + free(str); + free(feat_buf); + return rc; +} + +int main_vscsilist(int argc, char **argv) +{ + int opt; + uint32_t domid; + libxl_device_vscsictrl *vscsictrls; + libxl_vscsiinfo vscsiinfo; + int num_ctrls, h, d; + + SWITCH_FOREACH_OPT(opt, "", NULL, "scsi-list", 1) { + /* No options */ + } + if (argc < 2) { + help("scsi-list"); + return 1; + } + + /* Idx BE state ctrl p_hst v_hst state */ + printf("%-3s %-3s %-5s %-5s %-10s %-10s %-5s\n", + "Idx", "BE", "state", "ctrl", "phy-hctl", "vir-hctl", "devstate"); + for (argv += optind, argc -= optind; argc > 0; --argc, ++argv) { + if (libxl_domain_qualifier_to_domid(ctx, *argv, &domid) < 0) { + fprintf(stderr, "%s is an invalid domain identifier\n", *argv); + continue; + } + if (!(vscsictrls = libxl_device_vscsictrl_list(ctx, domid, &num_ctrls))) { + continue; + } + for (h = 0; h < num_ctrls; ++h) { + for (d = 0; d < vscsictrls[h].num_vscsidevs; d++) { + if (!libxl_device_vscsictrl_getinfo(ctx, domid, &vscsictrls[h], + &vscsictrls[h].vscsidevs[d], + &vscsiinfo)) { + char pdev[64], vdev[64]; + unsigned long long lun; + switch (vscsiinfo.pdev.type) { + case LIBXL_VSCSI_PDEV_TYPE_HCTL: + lun = vscsiinfo.pdev.u.hctl.m.lun; + snprintf(pdev, sizeof(pdev), "%u:%u:%u:%llu", + vscsiinfo.pdev.u.hctl.m.hst, + vscsiinfo.pdev.u.hctl.m.chn, + vscsiinfo.pdev.u.hctl.m.tgt, + lun); + break; + case LIBXL_VSCSI_PDEV_TYPE_WWN: + snprintf(pdev, sizeof(pdev), "%s", + vscsiinfo.pdev.u.wwn.m); + break; + default: + pdev[0] = '\0'; + break; + } + lun = vscsiinfo.vdev.lun; + snprintf(vdev, sizeof(vdev), "%u:%u:%u:%llu", + vscsiinfo.vdev.hst, + vscsiinfo.vdev.chn, + vscsiinfo.vdev.tgt, + lun); + /* Idx BE state Sta */ + printf("%-3d %-3d %-5d %-5d %-10s %-10s %d\n", + vscsiinfo.devid, + vscsiinfo.backend_id, + vscsiinfo.vscsictrl_state, + vscsiinfo.backend_id, + pdev, vdev, + vscsiinfo.vscsidev_state); + + } + libxl_vscsiinfo_dispose(&vscsiinfo); + } + libxl_device_vscsictrl_dispose(&vscsictrls[h]); + } + free(vscsictrls); + + } + + return 0; +} + +int main_vscsidetach(int argc, char **argv) +{ + int opt; + char *dom = argv[1], *str = argv[2]; + uint32_t domid; + XLU_Config *config = NULL; + int found = 0; + + SWITCH_FOREACH_OPT(opt, "", NULL, "scsi-detach", 1) { + /* No options */ + } + + if (argc < 3) { + help("scsi-detach"); + return 1; + } + + if (libxl_domain_qualifier_to_domid(ctx, dom, &domid) < 0) { + fprintf(stderr, "%s is an invalid domain identifier\n", dom); + return 1; + } + + config = xlu_cfg_init(stderr, "command line"); + if (!config) { + fprintf(stderr, "Failed to allocate for configuration\n"); + goto out; + } + + found = xlu_vscsi_detach(config, ctx, domid, str); + if (!found) + fprintf(stderr, "%s(%u) vdev %s does not exist in domain %s\n", __func__, __LINE__, str, dom); + +out: + if (config) + xlu_cfg_destroy(config); + return !found; +} + int main_vtpmattach(int argc, char **argv) { int opt; diff --git a/tools/libxl/xl_cmdtable.c b/tools/libxl/xl_cmdtable.c index fdc1ac6..11e1c26 100644 --- a/tools/libxl/xl_cmdtable.c +++ b/tools/libxl/xl_cmdtable.c @@ -351,6 +351,21 @@ struct cmd_spec cmd_table[] = { "Destroy a domain's virtual block device", " ", }, + { "scsi-attach", + &main_vscsiattach, 1, 1, + "Attach a dom0 SCSI device to a domain.", + " ", + }, + { "scsi-list", + &main_vscsilist, 0, 0, + "List all dom0 SCSI devices currently attached to a domain.", + "", + }, + { "scsi-detach", + &main_vscsidetach, 0, 1, + "Detach a specified SCSI device from a domain.", + " ", + }, { "vtpm-attach", &main_vtpmattach, 1, 1, "Create a new virtual TPM device",