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 <o...@aepfle.de> Cc: Ian Jackson <ian.jack...@eu.citrix.com> Cc: Stefano Stabellini <stefano.stabell...@eu.citrix.com> Cc: Ian Campbell <ian.campb...@citrix.com> Cc: Wei Liu <wei.l...@citrix.com> --- docs/man/xl.cfg.pod.5 | 56 ++ docs/man/xl.pod.1 | 18 + tools/libxl/Makefile | 2 + tools/libxl/libxl.c | 9 + tools/libxl/libxl.h | 42 ++ tools/libxl/libxl_create.c | 41 +- tools/libxl/libxl_device.c | 2 + tools/libxl/libxl_internal.h | 8 + tools/libxl/libxl_types.idl | 53 ++ tools/libxl/libxl_types_internal.idl | 1 + tools/libxl/libxl_vscsi.c | 1160 ++++++++++++++++++++++++++++++++++ tools/libxl/libxlu_vscsi.c | 664 +++++++++++++++++++ tools/libxl/libxlutil.h | 18 + tools/libxl/xl.h | 3 + tools/libxl/xl_cmdimpl.c | 225 ++++++- tools/libxl/xl_cmdtable.c | 15 + 16 files changed, 2313 insertions(+), 4 deletions(-) diff --git a/docs/man/xl.cfg.pod.5 b/docs/man/xl.cfg.pod.5 index a4cc1b3..be4546b 100644 --- a/docs/man/xl.cfg.pod.5 +++ b/docs/man/xl.cfg.pod.5 @@ -517,6 +517,62 @@ value is optional if this is a guest domain. =back +=item B<vscsi=[ "VSCSI_SPEC_STRING", "VSCSI_SPEC_STRING", ...]> + +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<Linux> + +=over 4 + +=item C<pdev> + +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 "naa.WWN:LUN". + +=item C<options> + +No options recognized. + +=back + +=item B<Linux based on classic Xen kernel> + +=over 4 + +=item C<pdev> + +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<options> + +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<vfb=[ "VFB_SPEC_STRING", "VFB_SPEC_STRING", ...]> Specifies the paravirtual framebuffer devices which should be supplied diff --git a/docs/man/xl.pod.1 b/docs/man/xl.pod.1 index e2bd32d..d3a4a5f 100644 --- a/docs/man/xl.pod.1 +++ b/docs/man/xl.pod.1 @@ -1416,6 +1416,24 @@ List virtual trusted platform modules for a domain. =back +=head2 PVSCSI DEVICES + +=over 4 + +=item B<scsi-attach> I<domain-id> I<pdev> I<vdev>,I<[feature-host]> + +Creates a new vscsi device in the domain specified by I<domain-id>. + +=item B<scsi-detach> I<domain-id> I<vdev> + +Removes the vscsi device from domain specified by I<domain-id>. + +=item B<scsi-list> I<domain-id> I<[domain-id] ...> + +List vscsi devices for the domain specified by I<domain-id>. + +=back + =head1 PCI PASS-THROUGH =over 4 diff --git a/tools/libxl/Makefile b/tools/libxl/Makefile index 4fc264d..660e6f5 100644 --- a/tools/libxl/Makefile +++ b/tools/libxl/Makefile @@ -108,6 +108,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 \ @@ -151,6 +152,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 8719f54..60e3803 100644 --- a/tools/libxl/libxl.c +++ b/tools/libxl/libxl.c @@ -4315,6 +4315,7 @@ DEFINE_DEVICE_REMOVE_CUSTOM(usbctrl, destroy, 1) /* The following functions are defined: * libxl_device_disk_add * libxl_device_nic_add + * libxl_device_vscsictrl_add * libxl_device_vtpm_add * libxl_device_usbctrl_add * libxl_device_usbdev_add @@ -4346,6 +4347,9 @@ DEFINE_DEVICE_ADD(disk) /* nic */ DEFINE_DEVICE_ADD(nic) +/* vscsi */ +DEFINE_DEVICE_ADD(vscsictrl) + /* vtpm */ DEFINE_DEVICE_ADD(vtpm) @@ -7275,6 +7279,11 @@ int libxl_retrieve_domain_configuration(libxl_ctx *ctx, uint32_t domid, MERGE(nic, nics, COMPARE_DEVID, {}); + MERGE(vscsictrl, vscsictrls, COMPARE_DEVID, { + libxl_device_vscsictrl_dispose(dst); + libxl_device_vscsictrl_copy(CTX, dst, src); + }); + MERGE(vtpm, vtpms, COMPARE_DEVID, {}); MERGE(pci, pcidevs, COMPARE_PCI, {}); diff --git a/tools/libxl/libxl.h b/tools/libxl/libxl.h index fb98b8e..6421208 100644 --- a/tools/libxl/libxl.h +++ b/tools/libxl/libxl.h @@ -874,6 +874,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 @@ -1695,6 +1702,41 @@ 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); +int libxl_device_vscsidev_add(libxl_ctx *ctx, uint32_t domid, + libxl_device_vscsidev *dev, + const libxl_asyncop_how *ao_how) + LIBXL_EXTERNAL_CALLERS_ONLY; +/* Remove vscsidev connected to vscsictrl */ +int libxl_device_vscsidev_remove(libxl_ctx *ctx, uint32_t domid, + libxl_device_vscsidev *dev, + 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); +void libxl_device_vscsictrl_remove_vscsidev(libxl_ctx *ctx, + libxl_device_vscsictrl *ctrl, + unsigned int idx); + /* 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 cae7ab0..63729d8 100644 --- a/tools/libxl/libxl_create.c +++ b/tools/libxl/libxl_create.c @@ -740,6 +740,8 @@ static void domcreate_bootloader_done(libxl__egc *egc, static void domcreate_launch_dm(libxl__egc *egc, libxl__multidev *aodevs, int ret); +static void domcreate_attach_vscsictrls(libxl__egc *egc, libxl__multidev *multidev, + int ret); static void domcreate_attach_vtpms(libxl__egc *egc, libxl__multidev *multidev, int ret); static void domcreate_attach_usbctrls(libxl__egc *egc, @@ -1426,13 +1428,13 @@ static void domcreate_devmodel_started(libxl__egc *egc, if (d_config->num_nics > 0) { /* Attach nics */ libxl__multidev_begin(ao, &dcs->multidev); - dcs->multidev.callback = domcreate_attach_vtpms; + dcs->multidev.callback = domcreate_attach_vscsictrls; libxl__add_nics(egc, ao, domid, d_config, &dcs->multidev); libxl__multidev_prepared(egc, &dcs->multidev, 0); return; } - domcreate_attach_vtpms(egc, &dcs->multidev, 0); + domcreate_attach_vscsictrls(egc, &dcs->multidev, 0); return; error_out: @@ -1440,7 +1442,7 @@ error_out: domcreate_complete(egc, dcs, ret); } -static void domcreate_attach_vtpms(libxl__egc *egc, +static void domcreate_attach_vscsictrls(libxl__egc *egc, libxl__multidev *multidev, int ret) { @@ -1455,6 +1457,39 @@ static void domcreate_attach_vtpms(libxl__egc *egc, goto error_out; } + /* Plug vscsi devices */ + if (d_config->num_vscsictrls > 0) { + /* Attach vscsictrls */ + libxl__multidev_begin(ao, &dcs->multidev); + dcs->multidev.callback = domcreate_attach_vtpms; + libxl__add_vscsictrls(egc, ao, domid, d_config, &dcs->multidev); + libxl__multidev_prepared(egc, &dcs->multidev, 0); + return; + } + + domcreate_attach_vtpms(egc, multidev, 0); + return; + +error_out: + assert(ret); + domcreate_complete(egc, dcs, ret); +} + +static void domcreate_attach_vtpms(libxl__egc *egc, + libxl__multidev *multidev, + int ret) +{ + libxl__domain_create_state *dcs = CONTAINER_OF(multidev, *dcs, multidev); + STATE_AO_GC(dcs->ao); + int domid = dcs->guest_domid; + + libxl_domain_config* const d_config = dcs->guest_config; + + if(ret) { + LOG(ERROR, "unable to add vscsi devices"); + goto error_out; + } + /* Plug vtpm devices */ if (d_config->num_vtpms > 0) { /* Attach vtpms */ diff --git a/tools/libxl/libxl_device.c b/tools/libxl/libxl_device.c index dfdf860..24d81c5 100644 --- a/tools/libxl/libxl_device.c +++ b/tools/libxl/libxl_device.c @@ -623,6 +623,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 * libxl__add_usbctrls * libxl__add_usbs @@ -644,6 +645,7 @@ void libxl__multidev_prepared(libxl__egc *egc, DEFINE_DEVICES_ADD(disk) DEFINE_DEVICES_ADD(nic) +DEFINE_DEVICES_ADD(vscsictrl) DEFINE_DEVICES_ADD(vtpm) DEFINE_DEVICES_ADD(usbctrl) DEFINE_DEVICES_ADD(usbdev) diff --git a/tools/libxl/libxl_internal.h b/tools/libxl/libxl_internal.h index 6dce93f..e5701b8 100644 --- a/tools/libxl/libxl_internal.h +++ b/tools/libxl/libxl_internal.h @@ -2626,6 +2626,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 *vscsictrl, + libxl__ao_device *aodev); + _hidden void libxl__device_vtpm_add(libxl__egc *egc, uint32_t domid, libxl_device_vtpm *vtpm, libxl__ao_device *aodev); @@ -3484,6 +3488,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); diff --git a/tools/libxl/libxl_types.idl b/tools/libxl/libxl_types.idl index c3161f3..226c59c 100644 --- a/tools/libxl/libxl_types.idl +++ b/tools/libxl/libxl_types.idl @@ -696,6 +696,43 @@ 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), + ("idx", 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), @@ -707,6 +744,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 @@ -744,6 +782,21 @@ 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), + ("idx", libxl_devid), + ("vscsidev_id", libxl_devid), + ("scsi_raw_cmds", bool), + ("vscsictrl_state", integer), + ("vscsidev_state", 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 177f9b7..51076e6 100644 --- a/tools/libxl/libxl_types_internal.idl +++ b/tools/libxl/libxl_types_internal.idl @@ -24,6 +24,7 @@ libxl__device_kind = Enumeration("device_kind", [ (8, "VTPM"), (9, "VUSB"), (10, "QUSB"), + (11, "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..cbaba82 --- /dev/null +++ b/tools/libxl/libxl_vscsi.c @@ -0,0 +1,1160 @@ +/* + * Copyright (C) 2016 SUSE Linux GmbH + * Author Olaf Hering <o...@aepfle.de> + * + * 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" + +typedef struct vscsidev_rm { + libxl_device_vscsictrl *ctrl; + char *be_path; + int dev_wait; + libxl__device dev; +} vscsidev_rm_t; + +typedef void (*vscsictrl_add)(libxl__egc *egc, + libxl__ao_device *aodev, + libxl_device_vscsictrl *vscsictrl, + libxl_domain_config *d_config); + +#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; +} + +/* 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.%16[0-9a-fA-F]:%llu", wwn, &lun) == 2) { + 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; +} + +static bool 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; + + 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; + } + + if (!vscsi_parse_pdev(gc, dev, c, p, v)) { + LOG(ERROR, "failed to parse %s: %s %s %s %s", path, c, p, v, s); + return false; + } + + return true; +} + +static bool 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 ok; + + ctrl->devid = atoi(dir); + + be_path = libxl__xs_read(gc, t, GCSPRINTF("%s/%s/backend", fe_path, dir)); + if (!be_path) + goto out; + + tmp = libxl__xs_read(gc, t, GCSPRINTF("%s/%s/backend-id", fe_path, dir)); + if (!tmp) + goto out; + ctrl->backend_domid = atoi(tmp); + + tmp = libxl__xs_read(gc, t, GCSPRINTF("%s/idx", be_path)); + if (!tmp) + goto out; + ctrl->idx = atoi(tmp); + + tmp = libxl__xs_read(gc, t, GCSPRINTF("%s/feature-host", be_path)); + if (!tmp) + goto out; + ok = atoi(tmp) != 0; + libxl_defbool_set(&ctrl->scsi_raw_cmds, ok); + + 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); + ok = vscsi_fill_dev(gc, t, devs_path, dev_dirs[dev_dir], &dev); + if (ok == true) + ok = ctrl->idx == dev.vdev.hst; + if (ok == true) + libxl_device_vscsictrl_append_vscsidev(CTX, ctrl, &dev); + libxl_device_vscsidev_dispose(&dev); + if (ok == false) + break; + } + + return ok; + +out: + libxl_defbool_set(&ctrl->scsi_raw_cmds, false); + return false; +} + +/* return an array of vscsictrls with num elements */ +static int 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 (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; +} + +/* Simplified variant of device_addrm_aocomplete */ +static void vscsi_aodev_complete(libxl__egc *egc, libxl__ao_device *aodev) +{ + STATE_AO_GC(aodev->ao); + libxl__ao_complete(egc, ao, aodev->rc); +} + +static int libxl__device_from_vscsictrl(libxl__gc *gc, uint32_t domid, + libxl_device_vscsictrl *vscsictrl, + libxl__device *device) +{ + device->backend_devid = vscsictrl->devid; + device->backend_domid = vscsictrl->backend_domid; + device->devid = vscsictrl->devid; + device->domid = domid; + device->backend_kind = LIBXL__DEVICE_KIND_VSCSI; + device->kind = LIBXL__DEVICE_KIND_VSCSI; + + return 0; +} + +static int vscsictrl_remove(libxl_ctx *ctx, + uint32_t domid, + libxl_device_vscsictrl *vscsictrl, + const libxl_asyncop_how *ao_how, + int force) +{ + AO_CREATE(ctx, domid, ao_how); + libxl__device *device; + libxl__ao_device *aodev; + int rc; + + GCNEW(device); + rc = libxl__device_from_vscsictrl(gc, domid, vscsictrl, 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 = vscsi_aodev_complete; + aodev->force = force; + libxl__initiate_device_generic_remove(egc, aodev); + +out: + if (rc) return AO_CREATE_FAIL(rc); + return AO_INPROGRESS; +} + +static int vscsidev_be_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 vscsictrl_reconfigure_rm(libxl__ao_device *aodev, + const char *state_path, + int *be_wait) + +{ + STATE_AO_GC(aodev->ao); + vscsidev_rm_t *vscsidev_rm = CONTAINER_OF(aodev->dev, *vscsidev_rm, dev); + libxl_device_vscsictrl *ctrl = vscsidev_rm->ctrl; + const char *be_path = vscsidev_rm->be_path; + int rc, i, be_state; + char *dev_path, *state_val; + flexarray_t *back; + libxl_device_vscsidev *v; + xs_transaction_t t = XBT_NULL; + + /* Prealloc key+value: 1 toplevel + 1 per device */ + i = 2 * (1 + 1); + back = flexarray_make(gc, i, 1); + + 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_NOTFOUND; + 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; + vscsidev_rm->dev_wait = XenbusStateClosing; + break; + case XenbusStateConnected: + /* The backend can handle reconfigure */ + *be_wait = XenbusStateConnected; + vscsidev_rm->dev_wait = XenbusStateClosed; + flexarray_append_pair(back, "state", + GCSPRINTF("%d", XenbusStateReconfiguring)); + break; + } + + /* Append new vscsidev or skip existing */ + for (i = 0; i < ctrl->num_vscsidevs; i++) { + unsigned int nb = 0; + v = ctrl->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 = vscsidev_be_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; + } + + rc = 0; + +out: + libxl__xs_transaction_abort(gc, &t); + return rc; +} + +static void vscsictrl_remove_be_dev(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 void vscsictrl_remove_be_cb(libxl__egc *egc, + libxl__ev_devstate *ds, + int rc) +{ + libxl__ao_device *aodev = CONTAINER_OF(ds, *aodev, backend_ds); + STATE_AO_GC(aodev->ao); + vscsidev_rm_t *vscsidev_rm = CONTAINER_OF(aodev->dev, *vscsidev_rm, dev); + libxl_device_vscsictrl *ctrl = vscsidev_rm->ctrl; + xs_transaction_t t = XBT_NULL; + int i; + + for (;;) { + rc = libxl__xs_transaction_start(gc, &t); + if (rc) goto out; + + for (i = 0; i < ctrl->num_vscsidevs; i++) + vscsictrl_remove_be_dev(gc, ctrl->vscsidevs + i, t, + vscsidev_rm->be_path, + vscsidev_rm->dev_wait); + + rc = libxl__xs_transaction_commit(gc, &t); + if (!rc) break; + if (rc < 0) goto out; + } + +out: + aodev->rc = rc; + aodev->callback(egc, aodev); +} + +static void vscsidev__remove(libxl__egc *egc, libxl__ao_device *aodev) +{ + STATE_AO_GC(aodev->ao); + vscsidev_rm_t *vscsidev_rm = CONTAINER_OF(aodev->dev, *vscsidev_rm, dev); + char *state_path; + int rc, be_wait; + + vscsidev_rm->be_path = libxl__device_backend_path(gc, aodev->dev); + state_path = GCSPRINTF("%s/state", vscsidev_rm->be_path); + + rc = vscsictrl_reconfigure_rm(aodev, state_path, &be_wait); + if (rc) goto out; + + rc = libxl__ev_devstate_wait(ao, &aodev->backend_ds, + vscsictrl_remove_be_cb, + state_path, be_wait, + LIBXL_DESTROY_TIMEOUT * 1000); + if (rc) { + LOG(ERROR, "unable to wait for %s", state_path); + goto out; + } + + return; + +out: + aodev->rc = rc; + /* Notify that this is done */ + aodev->callback(egc, aodev); +} + +static int vscsidev_remove(libxl_ctx *ctx, + uint32_t domid, + libxl_device_vscsictrl *vscsictrl, + const libxl_asyncop_how *ao_how) +{ + AO_CREATE(ctx, domid, ao_how); + libxl__ao_device *aodev; + vscsidev_rm_t *vscsidev_rm; + libxl__device *device; + int rc; + + GCNEW(aodev); + + GCNEW(vscsidev_rm); + vscsidev_rm->ctrl = vscsictrl; + device = &vscsidev_rm->dev; + + rc = libxl__device_from_vscsictrl(gc, domid, vscsictrl, device); + if (rc) goto out; + + libxl__prepare_ao_device(ao, aodev); + aodev->dev = device; + aodev->action = LIBXL__DEVICE_ACTION_REMOVE; + aodev->callback = vscsi_aodev_complete; + + vscsidev__remove(egc, aodev); + +out: + if (rc) AO_CREATE_FAIL(rc); + return AO_INPROGRESS; +} + +static int vscsidev_backend_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: + /* fallthrough */ + 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 void vscsictrl_new_backend(libxl__egc *egc, + libxl__ao_device *aodev, + libxl_device_vscsictrl *vscsictrl, + 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 * vscsictrl->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, + "libxl_ctrl_index", + GCSPRINTF("%d", vscsictrl->idx)); + flexarray_append_pair(back, "feature-host", + libxl_defbool_val(vscsictrl->scsi_raw_cmds) ? + "1" : "0"); + + flexarray_append_pair(front, + "backend-id", + GCSPRINTF("%d", vscsictrl->backend_domid)); + flexarray_append_pair(front, + "state", + GCSPRINTF("%d", XenbusStateInitialising)); + + for (i = 0; i < vscsictrl->num_vscsidevs; i++) { + v = vscsictrl->vscsidevs + i; + rc = vscsidev_backend_add(gc, v, back); + if (rc) goto out; + } + + 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); + return; + +out: + libxl__xs_transaction_abort(gc, &t); + aodev->rc = rc; + aodev->callback(egc, aodev); +} + +static void vscsictrl_do_reconfigure_add_cb(libxl__egc *egc, + libxl__ev_devstate *ds, + int rc) +{ + libxl__ao_device *aodev = CONTAINER_OF(ds, *aodev, backend_ds); + STATE_AO_GC(aodev->ao); + aodev->rc = rc; + aodev->callback(egc, aodev); +} + +static void vscsictrl_do_reconfigure_add(libxl__egc *egc, + libxl__ao_device *aodev, + libxl_device_vscsictrl *vscsictrl, + libxl_domain_config *d_config) +{ + STATE_AO_GC(aodev->ao); + int rc, i, be_state, be_wait; + const char *be_path; + 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 * vscsictrl->num_vscsidevs)); + back = flexarray_make(gc, i, 1); + + be_path = libxl__device_backend_path(gc, aodev->dev); + 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 < vscsictrl->num_vscsidevs; i++) { + unsigned int nb = 0; + v = vscsictrl->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 = vscsidev_backend_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) { + rc = libxl__ev_devstate_wait(ao, &aodev->backend_ds, + vscsictrl_do_reconfigure_add_cb, + state_path, be_wait, + LIBXL_INIT_TIMEOUT * 1000); + if (rc) goto out; + } + return; + +out: + libxl__xs_transaction_abort(gc, &t); + aodev->rc = rc; + aodev->callback(egc, aodev); +} + +static int vscsictrl_next_vscsidev_id(libxl__gc *gc, + const char *libxl_path, + libxl_devid *vscsidev_id) +{ + const char *val; + xs_transaction_t t = XBT_NULL; + unsigned int id; + int rc; + + for (;;) { + rc = libxl__xs_transaction_start(gc, &t); + if (rc) goto out; + + val = libxl__xs_read(gc, t, libxl_path); + id = val ? strtoul(val, NULL, 10) : 0; + + LOG(DEBUG, "%s = %s vscsidev_id %u", libxl_path, val, id); + + val = GCSPRINTF("%u", id + 1); + rc = libxl__xs_write_checked(gc, t, libxl_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 vscsictrl_assign_vscsidev_ids(libxl__gc *gc, + uint32_t domid, + libxl_device_vscsictrl *vscsictrl) +{ + libxl_device_vscsidev *dev; + libxl_devid vscsidev_id; + const char *libxl_path; + int rc, i; + + libxl_path = GCSPRINTF("%s/vscsi/%u/next_vscsidev_id", + libxl__xs_libxl_path(gc, domid), + vscsictrl->devid); + for (i = 0; i < vscsictrl->num_vscsidevs; i++) { + dev = &vscsictrl->vscsidevs[i]; + if (dev->vscsidev_id >= 0) + continue; + rc = vscsictrl_next_vscsidev_id(gc, libxl_path, &vscsidev_id); + if (rc) { + LOG(ERROR, "failed to assign vscsidev_id to %s for %s", + libxl_path, dev->pdev.p_devname); + goto out; + } + dev->vscsidev_id = vscsidev_id; + } + + rc = 0; +out: + return rc; +} + +static void vscsictrl_update_json(libxl__egc *egc, + libxl__ao_device *aodev, + libxl_device_vscsictrl *vscsictrl, + vscsictrl_add fn) +{ + STATE_AO_GC(aodev->ao); + int rc; + uint32_t domid = aodev->dev->domid; + libxl_device_vscsictrl vscsictrl_saved; + libxl_domain_config d_config; + libxl__domain_userdata_lock *lock = NULL; + + libxl_domain_config_init(&d_config); + libxl_device_vscsictrl_init(&vscsictrl_saved); + + libxl_device_vscsictrl_copy(CTX, &vscsictrl_saved, vscsictrl); + + rc = vscsictrl_assign_vscsidev_ids(gc, domid, &vscsictrl_saved); + 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, &vscsictrl_saved, COMPARE_DEVID, &d_config); + } + + fn(egc, aodev, &vscsictrl_saved, &d_config); + +out: + if (lock) libxl__unlock_domain_userdata(lock); + libxl_device_vscsictrl_dispose(&vscsictrl_saved); + libxl_domain_config_dispose(&d_config); + if (rc) { + aodev->rc = rc; + aodev->callback(egc, aodev); + } +} + +static void vscsictrl__reconfigure_add(libxl__egc *egc, + uint32_t domid, + libxl_device_vscsictrl *vscsictrl, + libxl__ao_device *aodev) +{ + STATE_AO_GC(aodev->ao); + libxl__device *device; + vscsictrl_add fn; + int rc; + + GCNEW(device); + rc = libxl__device_from_vscsictrl(gc, domid, vscsictrl, device); + if (rc) goto out; + aodev->dev = device; + + fn = vscsictrl_do_reconfigure_add; + vscsictrl_update_json(egc, aodev, vscsictrl, fn); + return; + +out: + aodev->rc = rc; + aodev->callback(egc, aodev); +} + +static int vscsictrl_reconfigure_add(libxl_ctx *ctx, + uint32_t domid, + libxl_device_vscsictrl *vscsictrl, + const libxl_asyncop_how *ao_how) +{ + AO_CREATE(ctx, domid, ao_how); + libxl__ao_device *aodev; + + GCNEW(aodev); + libxl__prepare_ao_device(ao, aodev); + aodev->action = LIBXL__DEVICE_ACTION_ADD; + aodev->callback = vscsi_aodev_complete; + aodev->update_json = true; + vscsictrl__reconfigure_add(egc, domid, vscsictrl, aodev); + + return AO_INPROGRESS; +} + +void libxl__device_vscsictrl_add(libxl__egc *egc, uint32_t domid, + libxl_device_vscsictrl *vscsictrl, + libxl__ao_device *aodev) +{ + STATE_AO_GC(aodev->ao); + libxl__device *device; + vscsictrl_add fn; + int rc; + + if (vscsictrl->devid == -1) { + if ((vscsictrl->devid = libxl__device_nextid(gc, domid, "vscsi")) < 0) { + rc = ERROR_FAIL; + goto out; + } + } + + GCNEW(device); + rc = libxl__device_from_vscsictrl(gc, domid, vscsictrl, device); + if (rc) goto out; + aodev->dev = device; + + fn = vscsictrl_new_backend; + vscsictrl_update_json(egc, aodev, vscsictrl, fn); + return; + +out: + aodev->rc = rc; + aodev->callback(egc, aodev); +} + +int libxl_device_vscsictrl_remove(libxl_ctx *ctx, uint32_t domid, + libxl_device_vscsictrl *vscsictrl, + const libxl_asyncop_how *ao_how) +{ + return vscsictrl_remove(ctx, domid, vscsictrl, ao_how, 0); +} + +int libxl_device_vscsictrl_destroy(libxl_ctx *ctx, uint32_t domid, + libxl_device_vscsictrl *vscsictrl, + const libxl_asyncop_how *ao_how) +{ + return vscsictrl_remove(ctx, domid, vscsictrl, ao_how, 1); +} + +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 = 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/idx", vscsipath)); + vscsiinfo->idx = 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; +} + +int libxl_device_vscsidev_add(libxl_ctx *ctx, uint32_t domid, + libxl_device_vscsidev *vscsidev, + const libxl_asyncop_how *ao_how) +{ + GC_INIT(ctx); + libxl_device_vscsictrl *vc, *ctrls = NULL; + libxl_device_vscsidev *vd; + int c, d, rc, num_ctrls = 0; + int duplicate = 0; + + rc = vscsi_collect_ctrls(gc, domid, &ctrls, &num_ctrls); + if (rc != 0) goto out; + + + for (c = 0; c < num_ctrls; ++c) { + vc = ctrls + c; + if (vc->idx != vscsidev->vdev.hst) + continue; + + for (d = 0; d < vc->num_vscsidevs; d++) { + vd = vc->vscsidevs + d; + if (vd->vdev.hst == vscsidev->vdev.hst && + vd->vdev.chn == vscsidev->vdev.chn && + vd->vdev.tgt == vscsidev->vdev.tgt && + vd->vdev.lun == vscsidev->vdev.lun) { + unsigned long long lun = vd->vdev.lun; + LOG(ERROR, "vdev '%u:%u:%u:%llu' is already used.\n", + vd->vdev.hst, vd->vdev.chn, vd->vdev.tgt, lun); + rc = ERROR_DEVICE_EXISTS; + duplicate = 1; + break; + } + } + + if (!duplicate) { + /* Append vscsidev to this vscsictrl, trigger reconfigure */ + libxl_device_vscsictrl_append_vscsidev(ctx, vc, vscsidev); + rc = vscsictrl_reconfigure_add(ctx, domid, vc, ao_how); + } + break; + } + + for (c = 0; c < num_ctrls; ++c) + libxl_device_vscsictrl_dispose(ctrls + c); + free(ctrls); + +out: + GC_FREE; + return rc; +} + +int libxl_device_vscsidev_remove(libxl_ctx *ctx, uint32_t domid, + libxl_device_vscsidev *vscsidev, + const libxl_asyncop_how *ao_how) +{ + GC_INIT(ctx); + libxl_device_vscsictrl *vc, *ctrls = NULL; + libxl_device_vscsidev *vd; + int c, d, rc, num_ctrls = 0; + int found = 0, idx; + int head, tail, i; + + rc = vscsi_collect_ctrls(gc, domid, &ctrls, &num_ctrls); + if (rc != 0) goto out; + + + for (c = 0; c < num_ctrls; ++c) { + vc = ctrls + c; + + for (d = 0; d < vc->num_vscsidevs; d++) { + vd = vc->vscsidevs + d; + if (vd->vdev.hst == vscsidev->vdev.hst && + vd->vdev.chn == vscsidev->vdev.chn && + vd->vdev.tgt == vscsidev->vdev.tgt && + vd->vdev.lun == vscsidev->vdev.lun) { + found = 1; + idx = d; + break; + } + } + + if (found) { + if (vc->num_vscsidevs > 1) { + /* Prepare vscsictrl, leave only desired vscsidev */ + head = idx; + tail = vc->num_vscsidevs - idx - 1; + for (i = 0; i < head; i++) + libxl_device_vscsictrl_remove_vscsidev(ctx, vc, 0); + for (i = 0; i < tail; i++) + libxl_device_vscsictrl_remove_vscsidev(ctx, vc, 1); + + /* Remove single vscsidev connected to this vscsictrl */ + rc = vscsidev_remove(ctx, domid, vc, ao_how); + } else { + /* Wipe entire vscsictrl */; + rc = vscsictrl_remove(ctx, domid, vc, ao_how, 0); + } + break; + } + } + + for (c = 0; c < num_ctrls; ++c) + libxl_device_vscsictrl_dispose(ctrls + c); + free(ctrls); + + if (!found) + rc = ERROR_NOTFOUND; + +out: + GC_FREE; + return rc; +} + +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; +} + +void libxl_device_vscsictrl_remove_vscsidev(libxl_ctx *ctx, + libxl_device_vscsictrl *ctrl, + unsigned int idx) +{ + GC_INIT(ctx); + if (idx >= ctrl->num_vscsidevs) + return; + libxl_device_vscsidev_dispose(&ctrl->vscsidevs[idx]); + if (ctrl->num_vscsidevs > idx + 1) + memmove(&ctrl->vscsidevs[idx], + &ctrl->vscsidevs[idx + 1], + (ctrl->num_vscsidevs - idx - 1) * sizeof(*ctrl->vscsidevs)); + ctrl->vscsidevs = libxl__realloc(NOGC, ctrl->vscsidevs, sizeof(*ctrl->vscsidevs) * (ctrl->num_vscsidevs - 1)); + ctrl->num_vscsidevs--; + GC_FREE; +} + +/* + * 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..31d917b --- /dev/null +++ b/tools/libxl/libxlu_vscsi.c @@ -0,0 +1,664 @@ +/* + * libxlu_vscsi.c - xl configuration file parsing: setup and helper functions + * + * Copyright (C) 2016 SUSE Linux GmbH + * Author Olaf Hering <o...@aepfle.de> + * Author Ondřej Holeček <aaa...@gmail.com> + * + * 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 <unistd.h> +#include <ctype.h> +#include <dirent.h> +#include <sys/stat.h> +#include <fcntl.h> +#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_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.<wwn>/tpgt_1/lun/lun_0/<X>/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.<wwn>/tpgt_1/lun/lun_0/<X>/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.<wwn>/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.<wwn>/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.<wwn> */ +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.%16[0-9a-fA-F]", tgt->wwn) != 1) + 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.%16[0-9a-fA-F]:%llu", wwn, &lun) == 2) { + 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; + } + + new_ctrl->idx = 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 *ctrl, + libxl_device_vscsidev *dev, + libxl_device_vscsictrl **existing) +{ + libxl_device_vscsictrl *vscsictrls = NULL, *tmp; + int rc, found_ctrl = -1, i; + int num_ctrls; + + + rc = xlu_vscsi_parse(cfg, ctx, str, ctrl, 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].idx == dev->vdev.hst) { + found_ctrl = i; + break; + } + } + } + + if (found_ctrl == -1) { + *existing = NULL; + } 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 == dev->vdev.chn && + tmp->vscsidevs[i].vdev.tgt == dev->vdev.tgt && + tmp->vscsidevs[i].vdev.lun == dev->vdev.lun) { + unsigned long long lun = dev->vdev.lun; + LOG(cfg, "vdev '%u:%u:%u:%llu' is already used.\n", + dev->vdev.hst, dev->vdev.chn, dev->vdev.tgt, lun); + rc = ERROR_INVAL; + goto out; + } + } + + if (libxl_defbool_val(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(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, *existing, tmp); + } + + rc = 0; + +out: + if (vscsictrls) { + for (i = 0; i < num_ctrls; ++i) + libxl_device_vscsictrl_dispose(vscsictrls + i); + free(vscsictrls); + } + return rc; +} + +int xlu_vscsi_detach(XLU_Config *cfg, libxl_ctx *ctx, uint32_t domid, char *str) +{ + libxl_device_vscsidev dev = { }; + libxl_device_vscsictrl ctrl = { }; + int rc; + 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); + rc = ERROR_FAIL; + goto out; + } + + rc = xlu_vscsi_parse(cfg, ctx, tmp, &ctrl, &dev); + if (rc) goto out; + + rc = libxl_device_vscsidev_remove(ctx, domid, &dev, NULL); + switch (rc) { + case ERROR_NOTFOUND: + LOG(cfg, "detach failed: %s does not exist in domid %u", str, domid); + break; + default: + break; + } + +out: + free(tmp); + libxl_device_vscsidev_dispose(&dev); + libxl_device_vscsictrl_dispose(&ctrl); + return rc; +} + +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->idx == dev.vdev.hst) { + 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 *cfg, libxl_ctx *ctx, uint32_t domid, + const char *str, + libxl_device_vscsictrl *ctrl, + libxl_device_vscsidev *dev, + libxl_device_vscsictrl **existing) +{ + 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..8f8e431 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 ctrl/dev with device described in str (pdev,vdev[,options]) */ +int xlu_vscsi_get_ctrl(XLU_Config *cfg, libxl_ctx *ctx, uint32_t domid, + const char *str, + libxl_device_vscsictrl *ctrl, + libxl_device_vscsidev *dev, + libxl_device_vscsictrl **existing); +/* 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 e601ca1..060a6c4 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 2ee6c74..163fac5 100644 --- a/tools/libxl/xl_cmdimpl.c +++ b/tools/libxl/xl_cmdimpl.c @@ -1325,7 +1325,7 @@ static void parse_config_data(const char *config_source, long l, vcpus = 0; XLU_Config *config; XLU_ConfigList *cpus, *vbds, *nics, *pcis, *cvfbs, *cpuids, *vtpms, - *usbctrls, *usbdevs; + *usbctrls, *usbdevs, *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; @@ -1855,6 +1855,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; @@ -7407,6 +7418,218 @@ 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 ctrl, *existing = NULL; + libxl_device_vscsidev dev; + char *str = NULL, *feat_buf = NULL; + char *json; + + 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;; + } + + existing = xmalloc(sizeof(*existing)); + libxl_device_vscsictrl_init(existing); + libxl_device_vscsictrl_init(&ctrl); + libxl_device_vscsidev_init(&dev); + + 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, &ctrl, &dev, &existing); + if (rc < 0) + goto out; + + if (dryrun_only) { + libxl_device_vscsictrl_append_vscsidev(ctx, existing ? : &ctrl, &dev); + json = libxl_device_vscsictrl_to_json(ctx, existing ? : &ctrl); + 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 (existing) { + if (libxl_device_vscsidev_add(ctx, domid, &dev, NULL)) { + fprintf(stderr, "libxl_device_vscsidev_add failed\n"); + rc = 1; + goto out; + } + } else { + libxl_device_vscsictrl_append_vscsidev(ctx, &ctrl, &dev); + if (libxl_device_vscsictrl_add(ctx, domid, &ctrl, 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(existing); + libxl_device_vscsictrl_dispose(&ctrl); + libxl_device_vscsidev_dispose(&dev); + free(existing); + 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; + } + vscsictrls = libxl_device_vscsictrl_list(ctx, domid, &num_ctrls); + if (!vscsictrls) + 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 rc = 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; + } + + rc = xlu_vscsi_detach(config, ctx, domid, str); + if (rc) + fprintf(stderr, "scsi-detach %s %s failed: %d\n", dom, str, rc); + +out: + if (config) + xlu_cfg_destroy(config); + return !!rc; +} + int main_vtpmattach(int argc, char **argv) { int opt; diff --git a/tools/libxl/xl_cmdtable.c b/tools/libxl/xl_cmdtable.c index bf69ffb..c431e34 100644 --- a/tools/libxl/xl_cmdtable.c +++ b/tools/libxl/xl_cmdtable.c @@ -354,6 +354,21 @@ struct cmd_spec cmd_table[] = { "Destroy a domain's virtual block device", "<Domain> <DevId>", }, + { "scsi-attach", + &main_vscsiattach, 1, 1, + "Attach a dom0 SCSI device to a domain.", + "<Domain> <PhysDevice> <VirtDevice>", + }, + { "scsi-list", + &main_vscsilist, 0, 0, + "List all dom0 SCSI devices currently attached to a domain.", + "<Domain(s)>", + }, + { "scsi-detach", + &main_vscsidetach, 0, 1, + "Detach a specified SCSI device from a domain.", + "<Domain> <VirtDevice>", + }, { "vtpm-attach", &main_vtpmattach, 1, 1, "Create a new virtual TPM device", _______________________________________________ Xen-devel mailing list Xen-devel@lists.xen.org http://lists.xen.org/xen-devel