--- src/storage_backend_iscsi.c | 327 +--------------------- src/storage_backend_scsi.c | 649 ++++++++++++++++++++----------------------- src/storage_backend_scsi.h | 22 +- 3 files changed, 319 insertions(+), 679 deletions(-) diff --git a/src/storage_backend_iscsi.c b/src/storage_backend_iscsi.c index d5b10e5..1b2dfd0 100644 --- a/src/storage_backend_iscsi.c +++ b/src/storage_backend_iscsi.c @@ -35,6 +35,7 @@ #include <dirent.h> #include "virterror_internal.h" +#include "storage_backend_scsi.h" #include "storage_backend_iscsi.h" #include "util.h" #include "memory.h" @@ -169,145 +170,6 @@ virStorageBackendISCSIConnection(virConnectPtr conn, return 0; } -static int -virStorageBackendISCSINewLun(virConnectPtr conn, virStoragePoolObjPtr pool, - unsigned int lun, const char *dev) -{ - virStorageVolDefPtr vol; - int fd = -1; - char *devpath = NULL; - int opentries = 0; - - if (VIR_ALLOC(vol) < 0) { - virReportOOMError(conn); - goto cleanup; - } - - vol->type = VIR_STORAGE_VOL_BLOCK; - - if (virAsprintf(&(vol->name), "lun-%d", lun) < 0) { - virReportOOMError(conn); - goto cleanup; - } - - if (virAsprintf(&devpath, "/dev/%s", dev) < 0) { - virReportOOMError(conn); - goto cleanup; - } - - /* It can take a little while between logging into the ISCSI - * server and udev creating the /dev nodes, so if we get ENOENT - * we must retry a few times - they should eventually appear. - * We currently wait for upto 5 seconds. Is this good enough ? - * Perhaps not on a very heavily loaded system Any other - * options... ? - */ - reopen: - if ((fd = open(devpath, O_RDONLY)) < 0) { - opentries++; - if (errno == ENOENT && opentries < 50) { - usleep(100 * 1000); - goto reopen; - } - virReportSystemError(conn, errno, - _("cannot open '%s'"), - devpath); - goto cleanup; - } - - /* Now figure out the stable path - * - * XXX this method is O(N) because it scans the pool target - * dir every time its run. Should figure out a more efficient - * way of doing this... - */ - if ((vol->target.path = virStorageBackendStablePath(conn, - pool, - devpath)) == NULL) - goto cleanup; - - VIR_FREE(devpath); - - if (virStorageBackendUpdateVolTargetInfoFD(conn, - &vol->target, - fd, - &vol->allocation, - &vol->capacity) < 0) - goto cleanup; - - /* XXX use unique iSCSI id instead */ - vol->key = strdup(vol->target.path); - if (vol->key == NULL) { - virReportOOMError(conn); - goto cleanup; - } - - - pool->def->capacity += vol->capacity; - pool->def->allocation += vol->allocation; - - if (VIR_REALLOC_N(pool->volumes.objs, - pool->volumes.count+1) < 0) { - virReportOOMError(conn); - goto cleanup; - } - pool->volumes.objs[pool->volumes.count++] = vol; - - close(fd); - - return 0; - - cleanup: - if (fd != -1) close(fd); - VIR_FREE(devpath); - virStorageVolDefFree(vol); - return -1; -} - -static int notdotdir(const struct dirent *dir) -{ - return !(STREQLEN(dir->d_name, ".", 1) || STREQLEN(dir->d_name, "..", 2)); -} - -/* Function to check if the type file in the given sysfs_path is a - * Direct-Access device (i.e. type 0). Return -1 on failure, 0 if not - * a Direct-Access device, and 1 if a Direct-Access device - */ -static int directAccessDevice(const char *sysfs_path) -{ - char typestr[3]; - char *gottype, *p; - FILE *typefile; - int type; - - typefile = fopen(sysfs_path, "r"); - if (typefile == NULL) { - /* there was no type file; that doesn't seem right */ - return -1; - } - gottype = fgets(typestr, 3, typefile); - fclose(typefile); - - if (gottype == NULL) { - /* we couldn't read the type file; have to give up */ - return -1; - } - - /* we don't actually care about p, but if you pass NULL and the last - * character is not \0, virStrToLong_i complains - */ - if (virStrToLong_i(typestr, &p, 10, &type) < 0) { - /* Hm, type wasn't an integer; seems strange */ - return -1; - } - - if (type != 0) { - /* saw a device other than Direct-Access */ - return 0; - } - - return 1; -} static int virStorageBackendISCSIFindLUNs(virConnectPtr conn, @@ -315,196 +177,17 @@ virStorageBackendISCSIFindLUNs(virConnectPtr conn, const char *session) { char sysfs_path[PATH_MAX]; - uint32_t host, bus, target, lun; - DIR *sysdir; - struct dirent *sys_dirent; - struct dirent **namelist; - int i, n, tries, retval, directaccess; - char *block, *scsidev, *block2; - - retval = 0; - block = NULL; - scsidev = NULL; + int retval = 0; snprintf(sysfs_path, PATH_MAX, "/sys/class/iscsi_session/session%s/device", session); - sysdir = opendir(sysfs_path); - if (sysdir == NULL) { + if (virStorageBackendSCSIFindTargets(conn, pool, sysfs_path) < 0) { virReportSystemError(conn, errno, - _("Failed to opendir sysfs path '%s'"), + _("Failed to get target list for path '%s'"), sysfs_path); - return -1; + retval = -1; } - while ((sys_dirent = readdir(sysdir))) { - /* double-negative, so we can use the same function for scandir below */ - if (!notdotdir(sys_dirent)) - continue; - - if (STREQLEN(sys_dirent->d_name, "target", 6)) { - if (sscanf(sys_dirent->d_name, "target%u:%u:%u", - &host, &bus, &target) != 3) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("Failed to parse target from sysfs path %s/%s"), - sysfs_path, sys_dirent->d_name); - closedir(sysdir); - return -1; - } - break; - } - } - closedir(sysdir); - - /* we now have the host, bus, and target; let's scan for LUNs */ - snprintf(sysfs_path, PATH_MAX, - "/sys/class/iscsi_session/session%s/device/target%u:%u:%u", - session, host, bus, target); - - n = scandir(sysfs_path, &namelist, notdotdir, versionsort); - if (n <= 0) { - /* we didn't find any reasonable entries; return failure */ - virReportSystemError(conn, errno, - _("Failed to find any LUNs for session '%s'"), - session); - return -1; - } - - for (i=0; i<n; i++) { - block = NULL; - scsidev = NULL; - - if (sscanf(namelist[i]->d_name, "%u:%u:%u:%u\n", - &host, &bus, &target, &lun) != 4) - continue; - - /* we found a LUN */ - /* Note, however, that just finding a LUN doesn't mean it is - * actually useful to us. There are a few different types of - * LUNs, enumerated in the linux kernel in - * drivers/scsi/scsi.c:scsi_device_types[]. Luckily, these device - * types form part of the ABI between the kernel and userland, so - * are unlikely to change. For now, we ignore everything that isn't - * type 0; that is, a Direct-Access device - */ - snprintf(sysfs_path, PATH_MAX, - "/sys/bus/scsi/devices/%u:%u:%u:%u/type", - host, bus, target, lun); - - directaccess = directAccessDevice(sysfs_path); - if (directaccess < 0) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("Failed to determine if %u:%u:%u:%u is a Direct-Access LUN"), - host, bus, target, lun); - retval = -1; - goto namelist_cleanup; - } - else if (directaccess == 0) { - /* not a direct-access device; skip */ - continue; - } - /* implicit else if (access == 1); Direct-Access device */ - - /* It might take some time for the - * /sys/bus/scsi/devices/host:bus:target:lun/block{:sda,/sda} - * link to show up; wait up to 5 seconds for it, then give up - */ - tries = 0; - while (block == NULL && tries < 50) { - snprintf(sysfs_path, PATH_MAX, "/sys/bus/scsi/devices/%u:%u:%u:%u", - host, bus, target, lun); - - sysdir = opendir(sysfs_path); - if (sysdir == NULL) { - virReportSystemError(conn, errno, - _("Failed to opendir sysfs path '%s'"), - sysfs_path); - retval = -1; - goto namelist_cleanup; - } - while ((sys_dirent = readdir(sysdir))) { - if (!notdotdir(sys_dirent)) - continue; - if (STREQLEN(sys_dirent->d_name, "block", 5)) { - block = strdup(sys_dirent->d_name); - break; - } - } - closedir(sysdir); - tries++; - if (block == NULL) - usleep(100 * 1000); - } - - if (block == NULL) { - /* we couldn't find the device link for this device; fail */ - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("Failed to find device link for lun %d"), - lun); - retval = -1; - goto namelist_cleanup; - } - - if (strlen(block) == 5) { - /* OK, this is exactly "block"; must be new-style */ - snprintf(sysfs_path, PATH_MAX, - "/sys/bus/scsi/devices/%u:%u:%u:%u/block", - host, bus, target, lun); - sysdir = opendir(sysfs_path); - if (sysdir == NULL) { - virReportSystemError(conn, errno, - _("Failed to opendir sysfs path '%s'"), - sysfs_path); - retval = -1; - goto namelist_cleanup; - } - while ((sys_dirent = readdir(sysdir))) { - if (!notdotdir(sys_dirent)) - continue; - - scsidev = strdup(sys_dirent->d_name); - break; - } - closedir(sysdir); - } - else { - /* old-style; just parse out the sd */ - block2 = strrchr(block, ':'); - if (block2 == NULL) { - /* Hm, wasn't what we were expecting; have to give up */ - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("Failed to parse block path %s"), - block); - retval = -1; - goto namelist_cleanup; - } - block2++; - scsidev = strdup(block2); - } - if (scsidev == NULL) { - virReportOOMError(conn); - retval = -1; - goto namelist_cleanup; - } - - retval = virStorageBackendISCSINewLun(conn, pool, lun, scsidev); - if (retval < 0) - break; - VIR_FREE(scsidev); - VIR_FREE(block); - } - -namelist_cleanup: - /* we call these VIR_FREE here to make sure we don't leak memory on - * error cases; in the success case, these are already freed but NULL, - * which should be fine - */ - VIR_FREE(scsidev); - VIR_FREE(block); - - for (i=0; i<n; i++) - VIR_FREE(namelist[i]); - - VIR_FREE(namelist); return retval; } diff --git a/src/storage_backend_scsi.c b/src/storage_backend_scsi.c index 236d894..3752bf2 100644 --- a/src/storage_backend_scsi.c +++ b/src/storage_backend_scsi.c @@ -23,10 +23,10 @@ #include <config.h> -#include <c-ctype.h> #include <unistd.h> #include <stdio.h> -#include <hal/libhal.h> +#include <dirent.h> +#include <fcntl.h> #include "virterror_internal.h" #include "storage_backend_scsi.h" @@ -34,152 +34,91 @@ #define VIR_FROM_THIS VIR_FROM_STORAGE -#define LINUX_SYSFS_SCSI_HOST_PREFIX "/sys/class/scsi_host/" -#define LINUX_SYSFS_SCSI_HOST_POSTFIX "/device" - -/** - * virStorageBackendSCSIMakeHostDevice: - * @conn: report errors agains - * @pool: pool to resolve - * @buf: pre-allocated buffer to fill with path name - * @buflen: size of buffer - * - * Resolve a HBA name to HBA device path - * - * Given a pool object configured for a SCSI HBA, resolve the HBA name - * into the canonical sysfs path for the HBA device. This can then be - * used to lookup the device in HAL. - * - * Returns 0 on success, -1 on failure - */ -static int virStorageBackendSCSIMakeHostDevice(virConnectPtr conn, - virStoragePoolObjPtr pool, - char *buf, - size_t buflen) +static int +virStorageBackendSCSIRefreshPool(virConnectPtr conn, + virStoragePoolObjPtr pool) { - char *dev = NULL; - char *tmp = pool->def->source.adapter; - char relLink[PATH_MAX], absLink[PATH_MAX]; - ssize_t len; - - if (!tmp) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("Missing adapter name for pool")); - return -1; - } + char sysfs_path[PATH_MAX]; + int ret = 0; - /* Sanity check host adapter name - should only be 0-9, a-z. - * Anything else is bogus & so we reject it, to prevent them - * making us read arbitrary paths on host - */ - while (*tmp) { - if (!c_isalnum(*tmp)) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("Invalid character in pool adapter name '%s'"), - pool->def->source.adapter); - return -1; - } - tmp++; - } + pool->def->allocation = pool->def->capacity = pool->def->available = 0; - /* - * First get the class based path eg - * - * /sys/class/scsi_host/host1/device - */ - if ((dev = malloc(sizeof(LINUX_SYSFS_SCSI_HOST_PREFIX) + - strlen(pool->def->source.adapter) + - sizeof(LINUX_SYSFS_SCSI_HOST_POSTFIX) + - 1)) == NULL) { - virReportOOMError(conn); - return -1; - } + virStorageBackendWaitForDevices(conn); - strcpy(dev, LINUX_SYSFS_SCSI_HOST_PREFIX); - strcat(dev, pool->def->source.adapter); - strcat(dev, LINUX_SYSFS_SCSI_HOST_POSTFIX); + snprintf(sysfs_path, PATH_MAX, "%s/%s/%s", + LINUX_SYSFS_SCSI_HOST_PREFIX, + pool->def->source.adapter, + LINUX_SYSFS_SCSI_HOST_POSTFIX); - /* - * Now resolve the class based path symlink to the real - * device path, which is likely under PCI bus hierarchy - * and is the path tracked by HAL - */ - /* Readlink does not null terminate, so we reserve one byte */ - if ((len = readlink(dev, relLink, sizeof(relLink)-1)) < 0) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot find SCSI host adapter '%s' at '%s'"), - pool->def->source.adapter, dev); - free(dev); - return -1; + if (virStorageBackendSCSIFindTargets(conn, pool, sysfs_path) < 0) { + virReportSystemError(conn, errno, + _("Failed to get target list for path '%s'"), + sysfs_path); + ret = -1; } - relLink[len] = '\0'; + + return ret; +} - /* - * The symlink is relative, so now we have to turn it - * into a absolute path - */ - if ((tmp = realloc(dev, - sizeof(LINUX_SYSFS_SCSI_HOST_PREFIX) + - strlen(pool->def->source.adapter) + - 1 + - strlen(relLink) + - 1)) == NULL) { - virReportOOMError(conn); - free(dev); +static int +notdotdir(const struct dirent *dir) +{ + return !(STREQLEN(dir->d_name, ".", 1) || STREQLEN(dir->d_name, "..", 2)); +} + + +/* Function to check if the type file in the given sysfs_path is a + * Direct-Access device (i.e. type 0). Return -1 on failure, 0 if not + * a Direct-Access device, and 1 if a Direct-Access device + */ +static int +directAccessDevice(const char *sysfs_path) +{ + char typestr[3]; + char *gottype, *p; + FILE *typefile; + int type; + + typefile = fopen(sysfs_path, "r"); + if (typefile == NULL) { + /* there was no type file; that doesn't seem right */ return -1; } - dev = tmp; + gottype = fgets(typestr, 3, typefile); + fclose(typefile); - strcpy(dev, LINUX_SYSFS_SCSI_HOST_PREFIX); - strcat(dev, pool->def->source.adapter); - strcat(dev, "/"); - strcat(dev, relLink); + if (gottype == NULL) { + /* we couldn't read the type file; have to give up */ + return -1; + } - /* - * And finally canonicalize the absolute path to remove the - * copious .. components + /* we don't actually care about p, but if you pass NULL and the last + * character is not \0, virStrToLong_i complains */ - if (!realpath(dev, absLink)) { - char ebuf[1024]; - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("cannot canonicalize link: %s"), - virStrerror(errno, ebuf, sizeof ebuf)); - free(dev); + if (virStrToLong_i(typestr, &p, 10, &type) < 0) { + /* Hm, type wasn't an integer; seems strange */ return -1; } - free(dev); - if (strlen(absLink) >= buflen) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("link too long for buffer")); - return -1; + if (type != 0) { + /* saw a device other than Direct-Access */ + return 0; } - strcpy(buf, absLink); - return 0; + return 1; } -/** - * virStorageBackendSCSICreateVol - * - * Allocate a virStorageVolDef object for the specified - * metadata and hook it into the pool - * - * Returns 0 on success, -1 on failure - */ static int -virStorageBackendSCSICreateVol(virConnectPtr conn, - virStoragePoolObjPtr pool, - const char *name, - const char *path, - const char *key, - unsigned long long size) +virStorageBackendSCSINewLun(virConnectPtr conn, virStoragePoolObjPtr pool, + unsigned int lun, const char *dev) { virStorageVolDefPtr vol; - char *tmppath; + int fd = -1; + char *devpath = NULL; + int opentries = 0; if (VIR_ALLOC(vol) < 0) { virReportOOMError(conn); @@ -188,18 +127,36 @@ virStorageBackendSCSICreateVol(virConnectPtr conn, vol->type = VIR_STORAGE_VOL_BLOCK; - tmppath = strdup(path); - vol->name = strdup(name); - vol->key = strdup(key); - vol->allocation = vol->capacity = size; + if (virAsprintf(&(vol->name), "lun-%d", lun) < 0) { + virReportOOMError(conn); + goto cleanup; + } - if ((vol->name == NULL) || - (vol->key == NULL) || - (tmppath == NULL)) { + if (virAsprintf(&devpath, "/dev/%s", dev) < 0) { virReportOOMError(conn); goto cleanup; } + /* For SAN storage it can take a little while between getting + * access to the array and udev creating the /dev nodes, so if we + * get ENOENT we must retry a few times - they should eventually + * appear. We currently wait for upto 5 seconds. Is this good + * enough ? Perhaps not on a very heavily loaded system Any other + * options... ? + */ + reopen: + if ((fd = open(devpath, O_RDONLY)) < 0) { + opentries++; + if (errno == ENOENT && opentries < 50) { + usleep(100 * 1000); + goto reopen; + } + virReportSystemError(conn, errno, + _("cannot open '%s'"), + devpath); + goto cleanup; + } + /* Now figure out the stable path * * XXX this method is O(N) because it scans the pool target @@ -208,15 +165,25 @@ virStorageBackendSCSICreateVol(virConnectPtr conn, */ if ((vol->target.path = virStorageBackendStablePath(conn, pool, - tmppath)) == NULL) + devpath)) == NULL) goto cleanup; - if (tmppath != vol->target.path) - free(tmppath); - tmppath = NULL; + VIR_FREE(devpath); + + if (virStorageBackendUpdateVolTargetInfoFD(conn, + &vol->target, + fd, + &vol->allocation, + &vol->capacity) < 0) + goto cleanup; - if (virStorageBackendUpdateVolInfo(conn, vol, 1) < 0) + /* XXX use unique iSCSI id instead */ + vol->key = strdup(vol->target.path); + if (vol->key == NULL) { + virReportOOMError(conn); goto cleanup; + } + pool->def->capacity += vol->capacity; pool->def->allocation += vol->allocation; @@ -224,248 +191,244 @@ virStorageBackendSCSICreateVol(virConnectPtr conn, if (VIR_REALLOC_N(pool->volumes.objs, pool->volumes.count+1) < 0) { virReportOOMError(conn); - virStorageVolDefFree(vol); goto cleanup; } pool->volumes.objs[pool->volumes.count++] = vol; + close(fd); + return 0; -cleanup: - free(tmppath); + cleanup: + if (fd != -1) close(fd); + VIR_FREE(devpath); virStorageVolDefFree(vol); return -1; } -/** - * virStorageBackendSCSIAddLUN: - * - * @conn: connection to report errors against - * @pool: pool to register volume with - * @ctx: HAL context - * @devname: HAL device name for LUN - * - * Given a HAL device supported 'block' and 'storage' capabilities - * register it as a volume in the pool - * - * Returns 0 on success, -1 on error - */ static int -virStorageBackendSCSIAddLUN(virConnectPtr conn, - virStoragePoolObjPtr pool, - LibHalContext *ctx, - const char *devname) +virStorageBackendSCSIFindLUNs(virConnectPtr conn, + virStoragePoolObjPtr pool, + uint32_t host, + uint32_t bus, + uint32_t target) { - char **strdevs = NULL; - int numstrdevs = 0; - char *dev = NULL, *key = NULL; - unsigned long long size; - int host, bus, target, lun; - int n = -1, i; - int ret = -1; - char name[100]; - DBusError derr = DBUS_ERROR_INIT; - - if ((strdevs = libhal_manager_find_device_string_match(ctx, - "info.parent", - devname, - &numstrdevs, - &derr)) == NULL) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("HAL failed to lookup LUNs for '%s': %s"), - devname, derr.message); - goto cleanup; - } - for (i = 0 ; i < numstrdevs && n == -1 ; i++) { - char *cat = libhal_device_get_property_string(ctx, - strdevs[0], - "info.category", - NULL); - /* XXX derr */ - if (cat != NULL) { - if (STREQ(cat, "storage")) - n = i; - free(cat); - } - } - /* No storage vol for device - probably a removable vol so ignore */ - if (n == -1) { - ret = 0; - goto cleanup; - } - -#define HAL_GET_PROPERTY(path, name, err, type, value) \ - (value) = libhal_device_get_property_ ## type(ctx, (path), (name), (err)); \ - if (dbus_error_is_set((err))) { \ - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, \ - "unable to lookup '%s' property on %s: %s", \ - (name), (path), derr.message); \ - goto cleanup; \ + char sysfs_path[PATH_MAX]; + int i, n, tries, retval, directaccess; + unsigned int tmphost, tmpbus, tmptarget, tmplun; + DIR *sysdir; + char *block, *block2; + struct dirent *sys_dirent; + struct dirent **namelist; + char *scsidev; + + retval = 0; + + /* we now have the host, bus, and target; let's scan for LUNs */ + snprintf(sysfs_path, PATH_MAX, + "/sys/bus/scsi/devices/target%u:%u:%u", + host, bus, target); + + n = scandir(sysfs_path, &namelist, notdotdir, versionsort); + if (n <= 0) { + /* we didn't find any reasonable entries; return failure */ + virReportSystemError(conn, errno, + _("Failed to find any LUNs for host %d bus %d target %d"), + host, bus, target); + return -1; } - /* These are props of the physical device */ - HAL_GET_PROPERTY(devname, "scsi.host", &derr, int, host); - HAL_GET_PROPERTY(devname, "scsi.bus", &derr, int, bus); - HAL_GET_PROPERTY(devname, "scsi.target", &derr, int, target); - HAL_GET_PROPERTY(devname, "scsi.lun", &derr, int, lun); - - /* These are props of the logic device */ - HAL_GET_PROPERTY(strdevs[0], "block.device", &derr, string, dev); - /* - * XXX storage.serial is not actually unique if they have - * multipath on the fibre channel adapter - */ - HAL_GET_PROPERTY(strdevs[0], "storage.serial", &derr, string, key); - HAL_GET_PROPERTY(strdevs[0], "storage.size", &derr, uint64, size); - -#undef HAL_GET_PROPERTY - - snprintf(name, sizeof(name), "%d:%d:%d:%d", host, bus, target, lun); - name[sizeof(name)-1] = '\0'; - - if (virStorageBackendSCSICreateVol(conn, pool, name, dev, key, size) < 0) - goto cleanup; - ret = 0; - -cleanup: - for (i = 0 ; strdevs && i < numstrdevs ; i++) - free(strdevs[i]); - free(strdevs); - free(dev); - free(key); - if (dbus_error_is_set(&derr)) - dbus_error_free(&derr); - return ret; -} + for (i = 0 ; i < n ; i++) { + block = NULL; + scsidev = NULL; + + if (sscanf(namelist[i]->d_name, "%u:%u:%u:%u\n", + &tmphost, &tmpbus, &tmptarget, &tmplun) != 4) + continue; + + /* we found a LUN */ + /* Note, however, that just finding a LUN doesn't mean it is + * actually useful to us. There are a few different types of + * LUNs, enumerated in the linux kernel in + * drivers/scsi/scsi.c:scsi_device_types[]. Luckily, these device + * types form part of the ABI between the kernel and userland, so + * are unlikely to change. For now, we ignore everything that isn't + * type 0; that is, a Direct-Access device + */ + snprintf(sysfs_path, PATH_MAX, + "/sys/bus/scsi/devices/%u:%u:%u:%u/type", + tmphost, tmpbus, tmptarget, tmplun); + + directaccess = directAccessDevice(sysfs_path); + if (directaccess < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("Failed to determine if %u:%u:%u:%u is a Direct-Access LUN"), + tmphost, tmpbus, tmptarget, tmplun); + retval = -1; + goto namelist_cleanup; + } + else if (directaccess == 0) { + /* not a direct-access device; skip */ + continue; + } + /* implicit else if (access == 1); Direct-Access device */ + + /* It might take some time for the + * /sys/bus/scsi/devices/host:bus:target:lun/block{:sda,/sda} + * link to show up; wait up to 5 seconds for it, then give up + */ + tries = 0; + while (block == NULL && tries < 50) { + snprintf(sysfs_path, PATH_MAX, "/sys/bus/scsi/devices/%u:%u:%u:%u", + tmphost, tmpbus, tmptarget, tmplun); + + sysdir = opendir(sysfs_path); + if (sysdir == NULL) { + virReportSystemError(conn, errno, + _("Failed to opendir sysfs path '%s'"), + sysfs_path); + retval = -1; + goto namelist_cleanup; + } + while ((sys_dirent = readdir(sysdir))) { + if (!notdotdir(sys_dirent)) + continue; + if (STREQLEN(sys_dirent->d_name, "block", 5)) { + block = strdup(sys_dirent->d_name); + break; + } + } + closedir(sysdir); + tries++; + if (block == NULL) + usleep(100 * 1000); + } + if (block == NULL) { + /* we couldn't find the device link for this device; fail */ + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("Failed to find device link for lun %d"), + tmplun); + retval = -1; + goto namelist_cleanup; + } -/** - * virStorageBackendSCSIRefreshPool: - * - * Query HAL for all storage devices associated with a specific - * SCSI HBA. - * - * XXX, currently we only support regular devices in /dev/, - * or /dev/disk/by-XXX. In future we also need to be able to - * map to multipath devices setup under /dev/mpath/XXXX. - */ -static int -virStorageBackendSCSIRefreshPool(virConnectPtr conn, - virStoragePoolObjPtr pool) -{ - char hostdevice[PATH_MAX]; - LibHalContext *ctx = NULL; - DBusConnection *sysbus = NULL; - DBusError derr = DBUS_ERROR_INIT; - char **hbadevs = NULL, **blkdevs = NULL; - int numhbadevs = 0, numblkdevs = 0; - int i, ret = -1; - - /* Get the canonical sysfs path for the HBA device */ - if (virStorageBackendSCSIMakeHostDevice(conn, pool, - hostdevice, sizeof(hostdevice)) < 0) - goto cleanup; + if (strlen(block) == 5) { + /* OK, this is exactly "block"; must be new-style */ + snprintf(sysfs_path, PATH_MAX, + "/sys/bus/scsi/devices/%u:%u:%u:%u/block", + tmphost, tmpbus, tmptarget, tmplun); + sysdir = opendir(sysfs_path); + if (sysdir == NULL) { + virReportSystemError(conn, errno, + _("Failed to opendir sysfs path '%s'"), + sysfs_path); + retval = -1; + goto namelist_cleanup; + } + while ((sys_dirent = readdir(sysdir))) { + if (!notdotdir(sys_dirent)) + continue; + + scsidev = strdup(sys_dirent->d_name); + break; + } + closedir(sysdir); + } + else { + /* old-style; just parse out the sd */ + block2 = strrchr(block, ':'); + if (block2 == NULL) { + /* Hm, wasn't what we were expecting; have to give up */ + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("Failed to parse block path %s"), + block); + retval = -1; + goto namelist_cleanup; + } + block2++; + scsidev = strdup(block2); + } + if (scsidev == NULL) { + virReportOOMError(conn); + retval = -1; + goto namelist_cleanup; + } - if ((ctx = libhal_ctx_new()) == NULL) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("failed to allocate HAL context")); - goto cleanup; + retval = virStorageBackendSCSINewLun(conn, pool, tmplun, scsidev); + if (retval < 0) + break; + VIR_FREE(scsidev); + VIR_FREE(block); } - sysbus = dbus_bus_get(DBUS_BUS_SYSTEM, &derr); - if (sysbus == NULL) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("failed to get dbus system bus: '%s'"), - derr.message); - goto cleanup; - } +namelist_cleanup: + /* we call these VIR_FREE here to make sure we don't leak memory on + * error cases; in the success case, these are already freed but NULL, + * which should be fine + */ + VIR_FREE(scsidev); + VIR_FREE(block); - if (!libhal_ctx_set_dbus_connection(ctx, sysbus)) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("failed to set HAL dbus connection")); - goto cleanup; + for (i = 0 ; i < n ; i++) { + VIR_FREE(namelist[i]); } + VIR_FREE(namelist); - if (!libhal_ctx_init(ctx, &derr)) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("failed to initialize HAL context: '%s'"), - derr.message); - goto cleanup; - } + return retval; +} - if ((hbadevs = libhal_manager_find_device_string_match(ctx, - "linux.sysfs_path", - hostdevice, - &numhbadevs, - &derr)) == NULL) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("HAL failed to lookup device '%s': %s"), - hostdevice, derr.message); - goto cleanup; - } - if (numhbadevs != 1) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("unexpected number of HBA devices from HAL " - "(expected 1, got %d) when looking up device " - "'%s'"), numhbadevs, hostdevice); - goto cleanup; +int +virStorageBackendSCSIFindTargets(virConnectPtr conn, + virStoragePoolObjPtr pool, + const char *sysfs_path) +{ + uint32_t host, bus, target; + DIR *sysdir; + struct dirent *sys_dirent; + int retval = 0; + + sysdir = opendir(sysfs_path); + if (sysdir == NULL) { + virReportSystemError(conn, errno, + _("Failed to opendir sysfs path '%s'"), + sysfs_path); + retval = -1; + goto out; } - if ((blkdevs = libhal_manager_find_device_string_match(ctx, - "info.parent", - hbadevs[0], - &numblkdevs, - &derr)) == NULL) { - virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, - _("failed to lookup LUNs for %s with HAL: %s"), - hbadevs[0], derr.message); - goto cleanup; + while ((sys_dirent = readdir(sysdir))) { + /* double-negative, so we can use the same function for scandir below */ + if (!notdotdir(sys_dirent)) + continue; + + if (STREQLEN(sys_dirent->d_name, "target", 6)) { + if (sscanf(sys_dirent->d_name, "target%u:%u:%u", + &host, &bus, &target) != 3) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("Failed to parse target from sysfs path %s/%s"), + sysfs_path, sys_dirent->d_name); + closedir(sysdir); + retval = -1; + goto out; + } + /* There may be more than one target on a host, so we + * continue to check after finding one. */ + virStorageBackendSCSIFindLUNs(conn, pool, host, bus, target); + } } + closedir(sysdir); - for (i = 0 ; i < numblkdevs ; i++) - virStorageBackendSCSIAddLUN(conn, - pool, - ctx, - blkdevs[i]); - - ret = 0; - -cleanup: - for (i = 0 ; hbadevs && i < numhbadevs ; i++) - free(hbadevs[i]); - free(hbadevs); - for (i = 0 ; blkdevs && i < numblkdevs ; i++) - free(blkdevs[i]); - free(blkdevs); - libhal_ctx_shutdown(ctx, NULL); - libhal_ctx_free(ctx); - if (sysbus) - dbus_connection_unref(sysbus); - if (dbus_error_is_set(&derr)) - dbus_error_free(&derr); - return ret; +out: + return retval; } - virStorageBackend virStorageBackendSCSI = { .type = VIR_STORAGE_POOL_SCSI, + .refreshPool = virStorageBackendSCSIRefreshPool, }; - -/* - * vim: set tabstop=4: - * vim: set shiftwidth=4: - * vim: set expandtab: - */ -/* - * Local variables: - * indent-tabs-mode: nil - * c-indent-level: 4 - * c-basic-offset: 4 - * tab-width: 4 - * End: - */ diff --git a/src/storage_backend_scsi.h b/src/storage_backend_scsi.h index c912269..022efef 100644 --- a/src/storage_backend_scsi.h +++ b/src/storage_backend_scsi.h @@ -26,20 +26,14 @@ #include "storage_backend.h" +#define LINUX_SYSFS_SCSI_HOST_PREFIX "/sys/class/scsi_host" +#define LINUX_SYSFS_SCSI_HOST_POSTFIX "device" + extern virStorageBackend virStorageBackendSCSI; -#endif /* __VIR_STORAGE_BACKEND_SCSI_H__ */ +int +virStorageBackendSCSIFindTargets(virConnectPtr conn, + virStoragePoolObjPtr pool, + const char *sysfs_path); -/* - * vim: set tabstop=4: - * vim: set shiftwidth=4: - * vim: set expandtab: - */ -/* - * Local variables: - * indent-tabs-mode: nil - * c-indent-level: 4 - * c-basic-offset: 4 - * tab-width: 4 - * End: - */ +#endif /* __VIR_STORAGE_BACKEND_SCSI_H__ */ -- 1.6.0.6 -- Libvir-list mailing list Libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list