On Thu, Mar 26, 2009 at 05:55:48PM -0400, David Allan wrote:
This version of the code scans a particular host for targets and LUs
and adds any LUs it finds as storage volumes.
-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;
- }
The new generic 'NewLUN' method has lost this bit of retry / sleep logic.
This unfortauntely causes us to randomly loose LUNs due to fact that
udev may not yet have created the /dev/ node or the stable path.
In this example, I have a iSCSI pool with a single LUN that is active.
On the iSCSI target I then export a second LUNs, and refresh the pool
on the libvirt client. The first time I refresh, the OS sees the new
LUN (as per lsscsi), but libvirt misses it. I have to refresh libvirt
again to see it
# virsh vol-list lettuceiscsi
Name Path
-----------------------------------------
5.0.0.4 /dev/disk/by-path/ip-10.33.15.20:3260-iscsi-demo-lun-4
[root@dhcp-1-98 ~]#
[root@dhcp-1-98 ~]# lsscsi
[0:0:0:0] disk ATA HDT722516DLA380 V43O /dev/sda
[1:0:0:0] cd/dvd PLEXTOR DVDR PX-755A 1.04 /dev/sr0
[5:0:0:0] storage IET Controller 0001 -
[5:0:0:4] disk IET VIRTUAL-DISK 0001 /dev/sdb
[root@dhcp-1-98 ~]# virsh pool-refresh lettuceiscsi
Pool lettuceiscsi refreshed
[root@dhcp-1-98 ~]# lsscsi
[0:0:0:0] disk ATA HDT722516DLA380 V43O /dev/sda
[1:0:0:0] cd/dvd PLEXTOR DVDR PX-755A 1.04 /dev/sr0
[5:0:0:0] storage IET Controller 0001 -
[5:0:0:2] disk IET VIRTUAL-DISK 0001 /dev/sdc
[5:0:0:4] disk IET VIRTUAL-DISK 0001 /dev/sdb
[root@dhcp-1-98 ~]#
[root@dhcp-1-98 ~]# virsh vol-list lettuceiscsi
Name Path
-----------------------------------------
5.0.0.4 /dev/disk/by-path/ip-10.33.15.20:3260-iscsi-demo-lun-4
[root@dhcp-1-98 ~]# virsh pool-refresh lettuceiscsi
Pool lettuceiscsi refreshed
[root@dhcp-1-98 ~]# virsh vol-list lettuceiscsi
Name Path
-----------------------------------------
5.0.0.2 /dev/disk/by-path/ip-10.33.15.20:3260-iscsi-demo-lun-2
5.0.0.4 /dev/disk/by-path/ip-10.33.15.20:3260-iscsi-demo-lun-4
-
- /* 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, "target")
< 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
new file mode 100644
index 0000000..62c05ae
--- /dev/null
+++ b/src/storage_backend_scsi.c
@@ -0,0 +1,508 @@
+/*
+ * storage_backend_scsi.c: storage backend for SCSI handling
+ *
+ * Copyright (C) 2007-2008 Red Hat, Inc.
+ * Copyright (C) 2007-2008 Daniel P. Berrange
+ *
+ * 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
+ *
+ * Author: Daniel P. Berrange <berrange redhat com>
+ */
+
+#include <config.h>
+
+#include <unistd.h>
+#include <stdio.h>
+#include <dirent.h>
+#include <fcntl.h>
+
+#include "virterror_internal.h"
+#include "storage_backend_scsi.h"
+#include "memory.h"
+#include "logging.h"
+
+#define VIR_FROM_THIS VIR_FROM_STORAGE
+
+/* 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, type of
+ * the device otherwise.
+ */
+static int
+getDeviceType(virConnectPtr conn,
+ uint32_t host,
+ uint32_t bus,
+ uint32_t target,
+ uint32_t lun,
+ int *type)
+{
+ char *type_path = NULL;
+ char typestr[3];
+ char *gottype, *p;
+ FILE *typefile;
+ int retval = 0;
+
+ if (virAsprintf(&type_path, "/sys/bus/scsi/devices/%u:%u:%u:%u/type",
+ host, bus, target, lun) < 0) {
+ virReportOOMError(conn);
+ goto out;
+ }
+
+ typefile = fopen(type_path, "r");
+ if (typefile == NULL) {
+ virReportSystemError(conn, errno,
+ _("Could not find typefile '%s'"),
+ type_path);
+ /* there was no type file; that doesn't seem right */
+ retval = -1;
+ goto out;
+ }
+
+ gottype = fgets(typestr, 3, typefile);
+ fclose(typefile);
+
+ if (gottype == NULL) {
+ virReportSystemError(conn, errno,
+ _("Could not read typefile '%s'"),
+ type_path);
+ /* we couldn't read the type file; have to give up */
+ retval = -1;
+ goto out;
+ }
+
+ /* 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) {
+ virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
+ _("Device type '%s' is not an
integer"),
+ typestr);
+ /* Hm, type wasn't an integer; seems strange */
+ retval = -1;
+ goto out;
+ }
+
+ VIR_DEBUG(_("Device type is %d"), *type);
+
+out:
+ VIR_FREE(type_path);
+ return retval;
+}
+
+
+static int
+virStorageBackendSCSINewLun(virConnectPtr conn,
+ virStoragePoolObjPtr pool,
+ uint32_t host,
+ uint32_t bus,
+ uint32_t target,
+ uint32_t lun,
+ const char *dev)
+{
+ virStorageVolDefPtr vol;
+ char *devpath = NULL;
+ int retval = 0;
+
+ if (VIR_ALLOC(vol) < 0) {
+ virReportOOMError(conn);
+ retval = -1;
+ goto out;
+ }
+
+ vol->type = VIR_STORAGE_VOL_BLOCK;
+
+ if (virAsprintf(&(vol->name), "%u.%u.%u.%u", host, bus, target,
lun) < 0) {
+ virReportOOMError(conn);
+ retval = -1;
+ goto free_vol;
+ }
+
+ if (virAsprintf(&devpath, "/dev/%s", dev) < 0) {
+ virReportOOMError(conn);
+ retval = -1;
+ goto free_vol;
+ }
+
+ VIR_DEBUG(_("Trying to create volume for '%s'"), devpath);
+
This is where I think we need to reintroduce the open/try loop to wait
for the nodes to appear in /dev
+ /* 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) {
+ retval = -1;
+ goto free_vol;
+ }
+
+ if (STREQLEN(devpath, vol->target.path, PATH_MAX)) {
+ VIR_DEBUG(_("No stable path found for '%s' in '%s'"),
+ devpath, pool->def->target.path);
+ retval = -1;
+ goto free_vol;
+ }
Also this bit needs to be removed, because we need to fallback to the
generic /dev path if no stable link is present, or if the user has
explicitly configured /dev/ as the pool target
+
+ if (virStorageBackendUpdateVolTargetInfo(conn,
+ &vol->target,
+ &vol->allocation,
+ &vol->capacity) < 0) {
+
+ virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
+ _("Failed to update volume for '%s'"),
+ devpath);
+ retval = -1;
+ goto free_vol;
+ }
+
+ /* XXX should use logical unit's UUID instead */
+ vol->key = strdup(vol->target.path);
+ if (vol->key == NULL) {
+ virReportOOMError(conn);
+ retval = -1;
+ goto free_vol;
+ }
+
+ pool->def->capacity += vol->capacity;
+ pool->def->allocation += vol->allocation;
+
+ if (VIR_REALLOC_N(pool->volumes.objs,
+ pool->volumes.count + 1) < 0) {
+ virReportOOMError(conn);
+ retval = -1;
+ goto free_vol;
+ }
+ pool->volumes.objs[pool->volumes.count++] = vol;
+
+ goto out;
+
+free_vol:
+ virStorageVolDefFree(vol);
+out:
+ VIR_FREE(devpath);
+ return retval;
+}
+
+
diff --git a/src/storage_backend_scsi.h b/src/storage_backend_scsi.h
new file mode 100644
index 0000000..678ccd6
--- /dev/null
+++ b/src/storage_backend_scsi.h
@@ -0,0 +1,54 @@
+
+#ifndef __VIR_STORAGE_BACKEND_SCSI_H__
+#define __VIR_STORAGE_BACKEND_SCSI_H__
+
+#include "storage_backend.h"
+#include "hash.h"
+
+#define LINUX_SYSFS_SCSI_HOST_PREFIX "/sys/class/scsi_host"
+#define LINUX_SYSFS_SCSI_HOST_POSTFIX "device"
+
+struct _virDirectoryWalkData {
+ virConnectPtr conn;
+ virStoragePoolObjPtr pool;
+ const char *dir_path;
+ const char *pattern_to_match;
+ size_t expected_matches;
+ virHashTablePtr matches; // will be created by walk function
+ virHashIterator iterator;
+ virHashDeallocator deallocator;
+};
+typedef struct _virDirectoryWalkData virDirectoryWalkData;
+typedef virDirectoryWalkData *virDirectoryWalkDataPtr;
This struct/typedef doesn't appear to be used anywhere in the code. Guessing
this is just a left-over from earlier version.
FYI, my testing was on 2.6.18 RHEL-5 kernel, and 2.6.27.9 F10/9 kernels
Regards,
Daniel
--
|: Red Hat, Engineering, London -o-
http://people.redhat.com/berrange/ :|
|:
http://libvirt.org -o-
http://virt-manager.org -o-
http://ovirt.org :|
|:
http://autobuild.org -o-
http://search.cpan.org/~danberr/ :|
|: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|