From 04d20a662109de6727232eb1213627877bb9662f Mon Sep 17 00:00:00 2001 From: David Allan Date: Tue, 19 May 2009 16:35:15 -0400 Subject: [PATCH] Step 7 of 8 Implement the driver methods --- src/Makefile.am | 4 +- src/node_device.c | 430 +++++++++++++++++++++++++++++++++++++++++++ src/node_device.h | 13 ++ src/node_device_conf.c | 136 ++++++++++++-- src/node_device_conf.h | 22 ++- src/node_device_hal.c | 5 + src/node_device_hal.h | 40 ++++ src/node_device_hal_linux.c | 170 +++++++++++++++++ src/qemu_driver.c | 2 +- src/storage_backend.c | 24 +-- src/xen_unified.c | 2 +- tests/nodedevxml2xmltest.c | 2 +- 12 files changed, 810 insertions(+), 40 deletions(-) create mode 100644 src/node_device_hal.h create mode 100644 src/node_device_hal_linux.c diff --git a/src/Makefile.am b/src/Makefile.am index fd692b4..39fabce 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -188,7 +188,9 @@ NODE_DEVICE_DRIVER_SOURCES = \ node_device.c node_device.h NODE_DEVICE_DRIVER_HAL_SOURCES = \ - node_device_hal.c + node_device_hal.c \ + node_device_hal_linux.c + NODE_DEVICE_DRIVER_DEVKIT_SOURCES = \ node_device_devkit.c diff --git a/src/node_device.c b/src/node_device.c index b84729f..4f73baf 100644 --- a/src/node_device.c +++ b/src/node_device.c @@ -25,6 +25,8 @@ #include #include +#include +#include #include "virterror_internal.h" #include "datatypes.h" @@ -133,6 +135,53 @@ cleanup: return ret; } + +static virNodeDevicePtr +nodeDeviceLookupByWWN(virConnectPtr conn, + const char *wwnn, + const char *wwpn) +{ + unsigned int i; + virDeviceMonitorStatePtr driver = conn->devMonPrivateData; + virNodeDeviceObjListPtr devs = &driver->devs; + virNodeDevCapsDefPtr cap = NULL; + virNodeDeviceObjPtr obj = NULL; + virNodeDevicePtr dev = NULL; + + nodeDeviceLock(driver); + + for (i = 0; i < devs->count; i++) { + + obj = devs->objs[i]; + virNodeDeviceObjLock(obj); + cap = obj->def->caps; + + while (cap) { + + if (cap->type == VIR_NODE_DEV_CAP_SCSI_HOST) { + if (cap->data.scsi_host.flags & + VIR_NODE_DEV_CAP_FLAG_HBA_FC_HOST) { + + if (STREQ(cap->data.scsi_host.wwnn, wwnn) && + STREQ(cap->data.scsi_host.wwpn, wwpn)) { + dev = virGetNodeDevice(conn, obj->def->name); + virNodeDeviceObjUnlock(obj); + goto out; + } + } + } + cap = cap->next; + } + + virNodeDeviceObjUnlock(obj); + } + +out: + nodeDeviceUnlock(driver); + return dev; +} + + static char *nodeDeviceDumpXML(virNodeDevicePtr dev, unsigned int flags ATTRIBUTE_UNUSED) { @@ -258,6 +307,385 @@ cleanup: } +static int +nodeDeviceVportCreateDelete(virConnectPtr conn, + const int parent_host, + const char *wwpn, + const char *wwnn, + int operation) +{ + int fd = -1; + int retval = 0; + char *operation_path = NULL, *vport_name = NULL; + const char *operation_file = NULL; + size_t towrite = 0; + unsigned int written = 0; + + switch (operation) { + case VPORT_CREATE: + operation_file = LINUX_SYSFS_VPORT_CREATE_POSTFIX; + break; + case VPORT_DELETE: + operation_file = LINUX_SYSFS_VPORT_DELETE_POSTFIX; + break; + default: + virNodeDeviceReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("Invalid vport operation (%d)"), operation); + retval = -1; + goto cleanup; + break; + } + + if (virAsprintf(&operation_path, + "%shost%d%s", + LINUX_SYSFS_FC_HOST_PREFIX, + parent_host, + operation_file) < 0) { + + virReportOOMError(); + retval = -1; + goto cleanup; + } + + VIR_DEBUG("Vport operation path is '%s'", operation_path); + + fd = open(operation_path, O_WRONLY); + + if (fd < 0) { + virReportSystemError(errno, + _("Could not open '%s' for vport operation"), + operation_path); + retval = -1; + goto cleanup; + } + + if (virAsprintf(&vport_name, + "%s:%s", + wwpn, + wwnn) < 0) { + + virReportOOMError(); + retval = -1; + goto cleanup; + } + + towrite = strlen(vport_name); + written = safewrite(fd, vport_name, towrite); + if (written != towrite) { + virReportSystemError(errno, + _("Write of '%s' to '%s' during " + "vport create/delete failed " + "(towrite: %lu written: %d)"), + vport_name, operation_path, + towrite, written); + retval = -1; + } + +cleanup: + if (fd != -1) { + close(fd); + } + VIR_FREE(vport_name); + VIR_FREE(operation_path); + VIR_DEBUG("%s", _("Vport operation complete")); + return retval; +} + + +static int +get_wwns(virConnectPtr conn, + virNodeDeviceDefPtr def, + char **wwnn, + char **wwpn) +{ + virNodeDevCapsDefPtr cap = NULL; + int ret = 0; + + cap = def->caps; + while (cap != NULL) { + if (cap->type == VIR_NODE_DEV_CAP_SCSI_HOST && + cap->data.scsi_host.flags & VIR_NODE_DEV_CAP_FLAG_HBA_FC_HOST) { + *wwnn = strdup(cap->data.scsi_host.wwnn); + *wwpn = strdup(cap->data.scsi_host.wwpn); + break; + } + + cap = cap->next; + } + + if (cap == NULL) { + virNodeDeviceReportError(conn, VIR_ERR_NO_SUPPORT, + "%s", _("Device is not a fibre channel HBA")); + ret = -1; + } + + if (*wwnn == NULL || *wwpn == NULL) { + /* Free the other one, if allocated... */ + VIR_FREE(wwnn); + VIR_FREE(wwpn); + ret = -1; + virReportOOMError(); + } + + return ret; +} + + +static int +get_parent_host(virConnectPtr conn, + virDeviceMonitorStatePtr driver, + const char *dev_name, + const char *parent_name, + int *parent_host) +{ + virNodeDeviceObjPtr parent = NULL; + virNodeDevCapsDefPtr cap = NULL; + int ret = 0; + + parent = virNodeDeviceFindByName(&driver->devs, parent_name); + if (parent == NULL) { + virNodeDeviceReportError(conn, VIR_ERR_INVALID_NODE_DEVICE, + _("Could not find parent device for '%s'"), + dev_name); + ret = -1; + goto out; + } + + cap = parent->def->caps; + while (cap != NULL) { + if (cap->type == VIR_NODE_DEV_CAP_SCSI_HOST && + (cap->data.scsi_host.flags & + VIR_NODE_DEV_CAP_FLAG_HBA_VPORT_OPS)) { + *parent_host = cap->data.scsi_host.host; + break; + } + + cap = cap->next; + } + + if (cap == NULL) { + virNodeDeviceReportError(conn, VIR_ERR_INVALID_NODE_DEVICE, + _("Device %s is not capable of vport operations"), + parent->def->name); + ret = -1; + } + + virNodeDeviceObjUnlock(parent); + +out: + return ret; +} + + +static int +get_time(virConnectPtr conn, time_t *t) +{ + int ret = 0; + + *t = time(NULL); + if (*t == (time_t)-1) { + virNodeDeviceReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("Could not get current time")); + + *t = 0; + ret = -1; + } + + return ret; +} + + +/* When large numbers of devices are present on the host, it's + * possible for udev not to realize that it has work to do before we + * get here. We thus keep trying to find the new device we just + * created for up to LINUX_NEW_DEVICE_WAIT_TIME. Note that udev's + * default settle time is 180 seconds, so once udev realizes that it + * has work to do, it might take that long for the udev wait to + * return. Thus the total maximum time for this function to return is + * the udev settle time plus LINUX_NEW_DEVICE_WAIT_TIME. + * + * This whole area is a race, but if we retry the udev wait for + * LINUX_NEW_DEVICE_WAIT_TIME seconds and there's still no device, + * it's probably safe to assume it's not going to appear. + */ +static virNodeDevicePtr +find_new_device(virConnectPtr conn, const char *wwnn, const char *wwpn) +{ + virDeviceMonitorStatePtr driver = conn->devMonPrivateData; + virNodeDevicePtr dev = NULL; + time_t start = 0, now = 0; + + /* The thread that creates the device takes the driver lock, so we + * must release it in order to allow the device to be created. + * We're not doing anything with the driver pointer at this point, + * so it's safe to release it, assuming that the pointer itself + * doesn't become invalid. */ + nodeDeviceUnlock(driver); + + get_time(conn, &start); + + while ((now - start) < LINUX_NEW_DEVICE_WAIT_TIME) { + + virNodeDeviceWaitForDevices(conn); + + dev = nodeDeviceLookupByWWN(conn, wwnn, wwpn); + + if (dev != NULL) { + break; + } + + sleep(5); + if (get_time(conn, &now) == -1) { + break; + } + } + + nodeDeviceLock(driver); + + return dev; +} + +static virNodeDevicePtr +nodeDeviceCreateXML(virConnectPtr conn, + const char *xmlDesc, + unsigned int flags ATTRIBUTE_UNUSED) +{ + virDeviceMonitorStatePtr driver = conn->devMonPrivateData; + virNodeDeviceDefPtr def = NULL; + char *wwnn = NULL, *wwpn = NULL; + int parent_host = -1; + virNodeDevicePtr dev = NULL; + + nodeDeviceLock(driver); + + def = virNodeDeviceDefParseString(conn, xmlDesc, CREATE_DEVICE); + if (def == NULL) { + goto cleanup; + } + + if (get_wwns(conn, def, &wwnn, &wwpn) == -1) { + goto cleanup; + } + + if (get_parent_host(conn, + driver, + def->name, + def->parent, + &parent_host) == -1) { + goto cleanup; + } + + if (nodeDeviceVportCreateDelete(conn, + parent_host, + wwpn, + wwnn, + VPORT_CREATE) == -1) { + goto cleanup; + } + + dev = find_new_device(conn, wwnn, wwpn); + /* We don't check the return value, because one way or another, + * we're returning what we get... */ + + if (dev == NULL) { + virNodeDeviceReportError(conn, VIR_ERR_NO_NODE_DEVICE, NULL); + } + +cleanup: + nodeDeviceUnlock(driver); + virNodeDeviceDefFree(def); + VIR_FREE(wwnn); + VIR_FREE(wwpn); + return dev; +} + + +static int +nodeDeviceDestroy(virNodeDevicePtr dev) +{ + int ret = 0; + virDeviceMonitorStatePtr driver = dev->conn->devMonPrivateData; + virNodeDeviceObjPtr obj = NULL; + char *parent_name = NULL, *wwnn = NULL, *wwpn = NULL; + int parent_host = -1; + + nodeDeviceLock(driver); + obj = virNodeDeviceFindByName(&driver->devs, dev->name); + nodeDeviceUnlock(driver); + + if (!obj) { + virNodeDeviceReportError(dev->conn, VIR_ERR_NO_NODE_DEVICE, NULL); + goto out; + } + + if (get_wwns(dev->conn, obj->def, &wwnn, &wwpn) == -1) { + goto out; + } + + parent_name = strdup(obj->def->parent); + + /* get_parent_host will cause the device object's lock to be + * taken, so we have to dup the parent's name and drop the lock + * before calling it. We don't need the reference to the object + * any more once we have the parent's name. */ + virNodeDeviceObjUnlock(obj); + obj = NULL; + + if (parent_name == NULL) { + virReportOOMError(); + goto out; + } + + if (get_parent_host(dev->conn, + driver, + dev->name, + parent_name, + &parent_host) == -1) { + goto out; + } + + if (nodeDeviceVportCreateDelete(dev->conn, + parent_host, + wwpn, + wwnn, + VPORT_DELETE) == -1) { + goto out; + } + +out: + VIR_FREE(parent_name); + VIR_FREE(wwnn); + VIR_FREE(wwpn); + return ret; +} + + +#if defined(UDEVADM) || defined(UDEVSETTLE) +void virNodeDeviceWaitForDevices(virConnectPtr conn) +{ +#ifdef UDEVADM + const char *const settleprog[] = { UDEVADM, "settle", NULL }; +#else + const char *const settleprog[] = { UDEVSETTLE, NULL }; +#endif + int exitstatus; + + if (access(settleprog[0], X_OK) != 0) + return; + + /* + * NOTE: we ignore errors here; this is just to make sure that any device + * nodes that are being created finish before we try to scan them. + * If this fails for any reason, we still have the backup of polling for + * 5 seconds for device nodes. + */ + virRun(settleprog, &exitstatus); +} +#else +void virNodeDeviceWaitForDevices(virConnectPtr conn ATTRIBUTE_UNUSED) {} +#endif + + void registerCommonNodeFuncs(virDeviceMonitorPtr driver) { driver->numOfDevices = nodeNumOfDevices; @@ -267,6 +695,8 @@ void registerCommonNodeFuncs(virDeviceMonitorPtr driver) driver->deviceGetParent = nodeDeviceGetParent; driver->deviceNumOfCaps = nodeDeviceNumOfCaps; driver->deviceListCaps = nodeDeviceListCaps; + driver->deviceCreateXML = nodeDeviceCreateXML; + driver->deviceDestroy = nodeDeviceDestroy; } diff --git a/src/node_device.h b/src/node_device.h index 9496120..882ba0f 100644 --- a/src/node_device.h +++ b/src/node_device.h @@ -28,6 +28,17 @@ #include "driver.h" #include "node_device_conf.h" +#define LINUX_SYSFS_SCSI_HOST_PREFIX "/sys/class/scsi_host" +#define LINUX_SYSFS_SCSI_HOST_POSTFIX "device" +#define LINUX_SYSFS_FC_HOST_PREFIX "/sys/class/fc_host/" + +#define VPORT_CREATE 0 +#define VPORT_DELETE 1 +#define LINUX_SYSFS_VPORT_CREATE_POSTFIX "/vport_create" +#define LINUX_SYSFS_VPORT_DELETE_POSTFIX "/vport_delete" + +#define LINUX_NEW_DEVICE_WAIT_TIME 60 + #ifdef HAVE_HAL int halNodeRegister(void); #endif @@ -42,4 +53,6 @@ void registerCommonNodeFuncs(virDeviceMonitorPtr mon); int nodedevRegister(void); +void virNodeDeviceWaitForDevices(virConnectPtr conn); + #endif /* __VIR_NODE_DEVICE_H__ */ diff --git a/src/node_device_conf.c b/src/node_device_conf.c index 6e04112..5b35b60 100644 --- a/src/node_device_conf.c +++ b/src/node_device_conf.c @@ -53,9 +53,34 @@ VIR_ENUM_IMPL(virNodeDevNetCap, VIR_NODE_DEV_CAP_NET_LAST, "80203", "80211") +VIR_ENUM_IMPL(virNodeDevHBACap, VIR_NODE_DEV_CAP_HBA_LAST, + "fc_host", + "vport_ops") #define virNodeDeviceLog(msg...) fprintf(stderr, msg) +static int +virNodeDevCapsDefParseString(virConnectPtr conn, + const char *xpath, + xmlXPathContextPtr ctxt, + char **string, + virNodeDeviceDefPtr def, + const char *missing_error_fmt) +{ + char *s; + + s = virXPathString(xpath, ctxt); + if (s == NULL) { + virNodeDeviceReportError(conn, VIR_ERR_INTERNAL_ERROR, + missing_error_fmt, + def->name); + return -1; + } + + *string = s; + return 0; +} + virNodeDeviceObjPtr virNodeDeviceFindByName(const virNodeDeviceObjListPtr devs, const char *name) { @@ -302,6 +327,18 @@ char *virNodeDeviceDefFormat(virConnectPtr conn, case VIR_NODE_DEV_CAP_SCSI_HOST: virBufferVSprintf(&buf, " %d\n", data->scsi_host.host); + if (data->scsi_host.flags & VIR_NODE_DEV_CAP_FLAG_HBA_FC_HOST) { + virBufferAddLit(&buf, " \n"); + virBufferVSprintf(&buf, + " %s\n", data->scsi_host.wwnn); + virBufferVSprintf(&buf, + " %s\n", data->scsi_host.wwpn); + virBufferAddLit(&buf, " \n"); + } + if (data->scsi_host.flags & VIR_NODE_DEV_CAP_FLAG_HBA_VPORT_OPS) { + virBufferAddLit(&buf, " \n"); + } + break; case VIR_NODE_DEV_CAP_SCSI: virBufferVSprintf(&buf, " %d\n", data->scsi.host); @@ -561,26 +598,91 @@ virNodeDevCapScsiHostParseXML(virConnectPtr conn, xmlXPathContextPtr ctxt, virNodeDeviceDefPtr def, xmlNodePtr node, - union _virNodeDevCapData *data) + union _virNodeDevCapData *data, + int create) { - xmlNodePtr orignode; - int ret = -1; + xmlNodePtr orignode, *nodes = NULL; + int ret = -1, n = 0, i; + char *type = NULL; orignode = ctxt->node; ctxt->node = node; - if (virNodeDevCapsDefParseULong(conn, "number(./host[1])", ctxt, + if (create == EXISTING_DEVICE && + virNodeDevCapsDefParseULong(conn, "number(./host[1])", ctxt, &data->scsi_host.host, def, _("no SCSI host ID supplied for '%s'"), - _("invalid SCSI host ID supplied for '%s'")) < 0) + _("invalid SCSI host ID supplied for '%s'")) < 0) { goto out; + } + + if ((n = virXPathNodeSet("./capability", ctxt, &nodes)) < 0) { + virNodeDeviceReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("error parsing SCSI host capabilities for '%s'"), + def->name); + goto out; + } + + for (i = 0 ; i < n ; i++) { + type = virXMLPropString(nodes[i], "type"); + + if (!type) { + virNodeDeviceReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("missing SCSI host capability type for '%s'"), + def->name); + goto out; + } + + if (STREQ(type, "vport_ops")) { + + data->scsi_host.flags |= VIR_NODE_DEV_CAP_FLAG_HBA_VPORT_OPS; + + } else if (STREQ(type, "fc_host")) { + + xmlNodePtr orignode2; + + data->scsi_host.flags |= VIR_NODE_DEV_CAP_FLAG_HBA_FC_HOST; + + orignode2 = ctxt->node; + ctxt->node = nodes[i]; + + if (virNodeDevCapsDefParseString(conn, "string(./wwnn[1])", + ctxt, + &data->scsi_host.wwnn, + def, + _("no WWNN supplied for '%s'")) < 0) { + goto out; + } + + if (virNodeDevCapsDefParseString(conn, "string(./wwpn[1])", + ctxt, + &data->scsi_host.wwpn, + def, + _("no WWPN supplied for '%s'")) < 0) { + goto out; + } + + ctxt->node = orignode2; + + } else { + virNodeDeviceReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unknown SCSI host capability type '%s' for '%s'"), + type, def->name); + goto out; + } + + VIR_FREE(type); + } ret = 0; + out: + VIR_FREE(type); ctxt->node = orignode; return ret; } + static int virNodeDevCapNetParseXML(virConnectPtr conn, xmlXPathContextPtr ctxt, @@ -848,7 +950,8 @@ static virNodeDevCapsDefPtr virNodeDevCapsDefParseXML(virConnectPtr conn, xmlXPathContextPtr ctxt, virNodeDeviceDefPtr def, - xmlNodePtr node) + xmlNodePtr node, + int create) { virNodeDevCapsDefPtr caps; char *tmp; @@ -892,7 +995,7 @@ virNodeDevCapsDefParseXML(virConnectPtr conn, ret = virNodeDevCapNetParseXML(conn, ctxt, def, node, &caps->data); break; case VIR_NODE_DEV_CAP_SCSI_HOST: - ret = virNodeDevCapScsiHostParseXML(conn, ctxt, def, node, &caps->data); + ret = virNodeDevCapScsiHostParseXML(conn, ctxt, def, node, &caps->data, create); break; case VIR_NODE_DEV_CAP_SCSI: ret = virNodeDevCapScsiParseXML(conn, ctxt, def, node, &caps->data); @@ -918,7 +1021,7 @@ error: } static virNodeDeviceDefPtr -virNodeDeviceDefParseXML(virConnectPtr conn, xmlXPathContextPtr ctxt) +virNodeDeviceDefParseXML(virConnectPtr conn, xmlXPathContextPtr ctxt, int create) { virNodeDeviceDefPtr def; virNodeDevCapsDefPtr *next_cap; @@ -931,7 +1034,12 @@ virNodeDeviceDefParseXML(virConnectPtr conn, xmlXPathContextPtr ctxt) } /* Extract device name */ - def->name = virXPathString("string(./name[1])", ctxt); + if (create == EXISTING_DEVICE) { + def->name = virXPathString("string(./name[1])", ctxt); + } else { + def->name = strdup("new device"); + } + if (!def->name) { virNodeDeviceReportError(conn, VIR_ERR_NO_NAME, NULL); goto error; @@ -951,7 +1059,7 @@ virNodeDeviceDefParseXML(virConnectPtr conn, xmlXPathContextPtr ctxt) next_cap = &def->caps; for (i = 0 ; i < n ; i++) { - *next_cap = virNodeDevCapsDefParseXML(conn, ctxt, def, nodes[i]); + *next_cap = virNodeDevCapsDefParseXML(conn, ctxt, def, nodes[i], create); if (!*next_cap) { VIR_FREE(nodes); goto error; @@ -969,7 +1077,7 @@ virNodeDeviceDefParseXML(virConnectPtr conn, xmlXPathContextPtr ctxt) } static virNodeDeviceDefPtr -virNodeDeviceDefParseNode(virConnectPtr conn, xmlDocPtr xml, xmlNodePtr root) +virNodeDeviceDefParseNode(virConnectPtr conn, xmlDocPtr xml, xmlNodePtr root, int create) { xmlXPathContextPtr ctxt = NULL; virNodeDeviceDefPtr def = NULL; @@ -987,7 +1095,7 @@ virNodeDeviceDefParseNode(virConnectPtr conn, xmlDocPtr xml, xmlNodePtr root) } ctxt->node = root; - def = virNodeDeviceDefParseXML(conn, ctxt); + def = virNodeDeviceDefParseXML(conn, ctxt, create); cleanup: xmlXPathFreeContext(ctxt); @@ -1015,7 +1123,7 @@ catchXMLError(void *ctx, const char *msg ATTRIBUTE_UNUSED, ...) } virNodeDeviceDefPtr -virNodeDeviceDefParseString(virConnectPtr conn, const char *str) +virNodeDeviceDefParseString(virConnectPtr conn, const char *str, int create) { xmlParserCtxtPtr pctxt; xmlDocPtr xml = NULL; @@ -1046,7 +1154,7 @@ virNodeDeviceDefParseString(virConnectPtr conn, const char *str) goto cleanup; } - def = virNodeDeviceDefParseNode(conn, xml, root); + def = virNodeDeviceDefParseNode(conn, xml, root, create); cleanup: xmlFreeParserCtxt(pctxt); diff --git a/src/node_device_conf.h b/src/node_device_conf.h index 26e5558..62b4e71 100644 --- a/src/node_device_conf.h +++ b/src/node_device_conf.h @@ -28,6 +28,9 @@ #include "util.h" #include "threads.h" +#define CREATE_DEVICE 1 +#define EXISTING_DEVICE 0 + enum virNodeDevCapType { /* Keep in sync with VIR_ENUM_IMPL in node_device_conf.c */ VIR_NODE_DEV_CAP_SYSTEM, /* System capability */ @@ -48,8 +51,16 @@ enum virNodeDevNetCapType { VIR_NODE_DEV_CAP_NET_LAST }; +enum virNodeDevHBACapType { + /* Keep in sync with VIR_ENUM_IMPL in node_device_conf.c */ + VIR_NODE_DEV_CAP_HBA_FC_HOST, /* fibre channel HBA */ + VIR_NODE_DEV_CAP_HBA_VPORT_OPS, /* capable of vport operations */ + VIR_NODE_DEV_CAP_HBA_LAST +}; + VIR_ENUM_DECL(virNodeDevCap) VIR_ENUM_DECL(virNodeDevNetCap) +VIR_ENUM_DECL(virNodeDevHBACap) enum virNodeDevStorageCapFlags { VIR_NODE_DEV_CAP_STORAGE_REMOVABLE = (1 << 0), @@ -57,6 +68,11 @@ enum virNodeDevStorageCapFlags { VIR_NODE_DEV_CAP_STORAGE_HOTPLUGGABLE = (1 << 2), }; +enum virNodeDevScsiHostCapFlags { + VIR_NODE_DEV_CAP_FLAG_HBA_FC_HOST = (1 << 0), + VIR_NODE_DEV_CAP_FLAG_HBA_VPORT_OPS = (1 << 1), +}; + typedef struct _virNodeDevCapsDef virNodeDevCapsDef; typedef virNodeDevCapsDef *virNodeDevCapsDefPtr; struct _virNodeDevCapsDef { @@ -108,6 +124,9 @@ struct _virNodeDevCapsDef { } net; struct { unsigned host; + char *wwnn; + char *wwpn; + unsigned flags; } scsi_host; struct { unsigned host; @@ -185,7 +204,8 @@ char *virNodeDeviceDefFormat(virConnectPtr conn, const virNodeDeviceDefPtr def); virNodeDeviceDefPtr virNodeDeviceDefParseString(virConnectPtr conn, - const char *str); + const char *str, + int create); void virNodeDeviceDefFree(virNodeDeviceDefPtr def); diff --git a/src/node_device_hal.c b/src/node_device_hal.c index b214f60..5927ba1 100644 --- a/src/node_device_hal.c +++ b/src/node_device_hal.c @@ -28,6 +28,7 @@ #include #include "node_device_conf.h" +#include "node_device_hal.h" #include "virterror_internal.h" #include "driver.h" #include "datatypes.h" @@ -37,6 +38,8 @@ #include "logging.h" #include "node_device.h" +#define VIR_FROM_THIS VIR_FROM_NODEDEV + /* * Host device enumeration (HAL implementation) */ @@ -215,6 +218,8 @@ static int gather_scsi_host_cap(LibHalContext *ctx, const char *udi, union _virNodeDevCapData *d) { (void)get_int_prop(ctx, udi, "scsi_host.host", (int *)&d->scsi_host.host); + (void)check_fc_host(d); + (void)check_vport_capable(d); return 0; } diff --git a/src/node_device_hal.h b/src/node_device_hal.h new file mode 100644 index 0000000..0b4a2ef --- /dev/null +++ b/src/node_device_hal.h @@ -0,0 +1,40 @@ +/* + * node_device_hal.h: node device enumeration - HAL-based implementation + * + * Copyright (C) 2009 Red Hat, Inc. + * + * This library 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; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __VIR_NODE_DEVICE_HAL_H__ +#define __VIR_NODE_DEVICE_HAL_H__ + +#ifdef __linux__ + +#define check_fc_host(d) check_fc_host_linux(d) +int check_fc_host_linux(union _virNodeDevCapData *d); + +#define check_vport_capable(d) check_vport_capable_linux(d) +int check_vport_capable_linux(union _virNodeDevCapData *d); + +#else /* __linux__ */ + +#define check_fc_host(d) +#define check_vport_capable(d) + +#endif /* __linux__ */ + +#endif /* __VIR_NODE_DEVICE_HAL_H__ */ diff --git a/src/node_device_hal_linux.c b/src/node_device_hal_linux.c new file mode 100644 index 0000000..1deb6d2 --- /dev/null +++ b/src/node_device_hal_linux.c @@ -0,0 +1,170 @@ +/* + * node_device_hal_linuc.c: Linux specific code to gather device data + * not available through HAL. + * + * Copyright (C) 2009 Red Hat, Inc. + * + * This library 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; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include + +#include + +#include "node_device.h" +#include "node_device_hal.h" +#include "virterror_internal.h" +#include "memory.h" +#include "logging.h" + +#define VIR_FROM_THIS VIR_FROM_NODEDEV + +#ifdef __linux__ + +int check_fc_host_linux(union _virNodeDevCapData *d) +{ + char *sysfs_path = NULL; + char *wwnn_path = NULL; + char *wwpn_path = NULL; + char *p = NULL; + int fd = -1; + char buf[64]; + struct stat st; + + VIR_DEBUG("Checking if host%d is an FC HBA", d->scsi_host.host); + + if (virAsprintf(&sysfs_path, "%s/host%d", + LINUX_SYSFS_FC_HOST_PREFIX, + d->scsi_host.host) < 0) { + virReportOOMError(); + goto out; + } + + if (stat(sysfs_path, &st) != 0) { + /* Not an FC HBA */ + goto out; + } + + d->scsi_host.flags |= VIR_NODE_DEV_CAP_FLAG_HBA_FC_HOST; + + if (virAsprintf(&wwnn_path, "%s/node_name", + sysfs_path) < 0) { + virReportOOMError(); + goto out; + } + + if ((fd = open(wwnn_path, O_RDONLY)) < 0) { + goto out; + } + + memset(buf, 0, sizeof(buf)); + if (saferead(fd, buf, sizeof(buf)) < 0) { + goto out; + } + + close(fd); + fd = -1; + + p = strstr(buf, "0x"); + if (p != NULL) { + p += strlen("0x"); + } else { + p = buf; + } + + d->scsi_host.wwnn = strndup(p, sizeof(buf)); + if (d->scsi_host.wwnn == NULL) { + virReportOOMError(); + goto out; + } + + p = strchr(d->scsi_host.wwnn, '\n'); + if (p != NULL) { + *p = '\0'; + } + + if (virAsprintf(&wwpn_path, "%s/port_name", + sysfs_path) < 0) { + virReportOOMError(); + goto out; + } + + if ((fd = open(wwpn_path, O_RDONLY)) < 0) { + goto out; + } + + memset(buf, 0, sizeof(buf)); + if (saferead(fd, buf, sizeof(buf)) < 0) { + goto out; + } + + close(fd); + fd = -1; + + p = strstr(buf, "0x"); + if (p != NULL) { + p += strlen("0x"); + } else { + p = buf; + } + + d->scsi_host.wwpn = strndup(p, sizeof(buf)); + if (d->scsi_host.wwpn == NULL) { + virReportOOMError(); + goto out; + } + + p = strchr(d->scsi_host.wwpn, '\n'); + if (p != NULL) { + *p = '\0'; + } + +out: + if (fd != -1) { + close(fd); + } + VIR_FREE(sysfs_path); + VIR_FREE(wwnn_path); + VIR_FREE(wwpn_path); + return 0; +} + + +int check_vport_capable_linux(union _virNodeDevCapData *d) +{ + char *sysfs_path = NULL; + struct stat st; + + if (virAsprintf(&sysfs_path, "%s/host%d/vport_create", + LINUX_SYSFS_FC_HOST_PREFIX, + d->scsi_host.host) < 0) { + virReportOOMError(); + goto out; + } + + if (stat(sysfs_path, &st) != 0) { + /* Not a vport capable HBA */ + goto out; + } + + d->scsi_host.flags |= VIR_NODE_DEV_CAP_FLAG_HBA_VPORT_OPS; + +out: + VIR_FREE(sysfs_path); + return 0; +} + +#endif /* __linux__ */ diff --git a/src/qemu_driver.c b/src/qemu_driver.c index bd60b29..057e97b 100644 --- a/src/qemu_driver.c +++ b/src/qemu_driver.c @@ -5089,7 +5089,7 @@ qemudNodeDeviceGetPciInfo (virNodeDevicePtr dev, if (!xml) goto out; - def = virNodeDeviceDefParseString(dev->conn, xml); + def = virNodeDeviceDefParseString(dev->conn, xml, EXISTING_DEVICE); if (!def) goto out; diff --git a/src/storage_backend.c b/src/storage_backend.c index b154140..74759cf 100644 --- a/src/storage_backend.c +++ b/src/storage_backend.c @@ -46,6 +46,7 @@ #include "virterror_internal.h" #include "util.h" #include "memory.h" +#include "node_device.h" #include "storage_backend.h" @@ -245,30 +246,11 @@ virStorageBackendUpdateVolTargetInfoFD(virConnectPtr conn, return 0; } -#if defined(UDEVADM) || defined(UDEVSETTLE) void virStorageBackendWaitForDevices(virConnectPtr conn) { -#ifdef UDEVADM - const char *const settleprog[] = { UDEVADM, "settle", NULL }; -#else - const char *const settleprog[] = { UDEVSETTLE, NULL }; -#endif - int exitstatus; - - if (access(settleprog[0], X_OK) != 0) - return; - - /* - * NOTE: we ignore errors here; this is just to make sure that any device - * nodes that are being created finish before we try to scan them. - * If this fails for any reason, we still have the backup of polling for - * 5 seconds for device nodes. - */ - virRun(settleprog, &exitstatus); + virNodeDeviceWaitForDevices(conn); + return; } -#else -void virStorageBackendWaitForDevices(virConnectPtr conn ATTRIBUTE_UNUSED) {} -#endif /* * Given a volume path directly in /dev/XXX, iterate over the diff --git a/src/xen_unified.c b/src/xen_unified.c index e708980..8da4e23 100644 --- a/src/xen_unified.c +++ b/src/xen_unified.c @@ -1439,7 +1439,7 @@ xenUnifiedNodeDeviceGetPciInfo (virNodeDevicePtr dev, if (!xml) goto out; - def = virNodeDeviceDefParseString(dev->conn, xml); + def = virNodeDeviceDefParseString(dev->conn, xml, EXISTING_DEVICE); if (!def) goto out; diff --git a/tests/nodedevxml2xmltest.c b/tests/nodedevxml2xmltest.c index 29cdb9e..7621212 100644 --- a/tests/nodedevxml2xmltest.c +++ b/tests/nodedevxml2xmltest.c @@ -29,7 +29,7 @@ static int testCompareXMLToXMLFiles(const char *xml) { if (virtTestLoadFile(xml, &xmlPtr, MAX_FILE) < 0) goto fail; - if (!(dev = virNodeDeviceDefParseString(NULL, xmlData))) + if (!(dev = virNodeDeviceDefParseString(NULL, xmlData, EXISTING_DEVICE))) goto fail; if (!(actual = virNodeDeviceDefFormat(NULL, dev))) -- 1.6.0.6