[Date Prev][Date Next] [Thread Prev][Thread Next]
[Thread Index]
[Date Index]
[Author Index]
[libvirt] [RFC 5/5]: Rewrite findLuns function
- From: Chris Lalancette <clalance redhat com>
- To: libvir-list redhat com
- Subject: [libvirt] [RFC 5/5]: Rewrite findLuns function
- Date: Thu, 12 Jun 2008 16:09:39 +0200
This rather large patch rewrites the virStorageBackendISCSIFindLUNs() function
to only rely on sysfs for finding LUNs, given a session number. Along the way,
it also fixes the bug where we wouldn't find LUNs for older kernels (with the
block:sda format), and also (possibly) fixes a race condition where we could try
to find the LUN before udev has finished connecting it. I say it "possibly"
fixes it because I haven't been able to hit it so far, but I definitely need
more testing to try and confirm.
Signed-off-by: Chris Lalancette <clalance redhat com>
--- libvirt.sendtarget/src/storage_backend_iscsi.c 2008-06-12 15:31:00.000000000 +0200
+++ libvirt/src/storage_backend_iscsi.c 2008-06-12 15:43:39.000000000 +0200
@@ -164,110 +164,30 @@ virStorageBackendISCSIConnection(virConn
return 0;
}
-
static int
-virStorageBackendISCSIMakeLUN(virConnectPtr conn,
- virStoragePoolObjPtr pool,
- char **const groups,
- void *data)
+virStorageBackendISCSINewLun(virConnectPtr conn, virStoragePoolObjPtr pool,
+ unsigned int lun, char *dev)
{
virStorageVolDefPtr vol;
int fd = -1;
- unsigned int target, channel, id, lun;
- char lunid[100];
- int opentries = 0;
char *devpath = NULL;
- char *session = data;
- char sysfs_path[PATH_MAX];
- char *dev = NULL;
- DIR *sysdir;
- struct dirent *block_dirent;
- struct stat sbuf;
- int len;
-
- if ((virStrToLong_ui(groups[0], NULL, 10, &target) < 0) ||
- (virStrToLong_ui(groups[1], NULL, 10, &channel) < 0) ||
- (virStrToLong_ui(groups[2], NULL, 10, &id) < 0) ||
- (virStrToLong_ui(groups[3], NULL, 10, &lun) < 0)) {
- virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "%s",
- _("Failed parsing iscsiadm commands"));
- return -1;
- }
-
- if (lun == 0) {
- /* the 0'th LUN isn't a real LUN, it's just a control LUN; skip it */
- return 0;
- }
-
- snprintf(sysfs_path, PATH_MAX,
- "/sys/class/iscsi_session/session%s/device/"
- "target%d:%d:%d/%d:%d:%d:%d/block",
- session, target, channel, id, target, channel, id, lun);
-
- if (stat(sysfs_path, &sbuf) < 0) {
- /* block path in subdir didn't exist; this is unexpected, so fail */
- virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
- _("Failed to find the sysfs path for %d:%d:%d:%d: %s"),
- target, channel, id, lun, strerror(errno));
- return -1;
- }
-
- sysdir = opendir(sysfs_path);
- if (sysdir == NULL) {
- /* we failed for some reason; return an error */
- virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
- _("Failed to opendir sysfs path %s: %s"),
- sysfs_path, strerror(errno));
- return -1;
- }
-
- while ((block_dirent = readdir(sysdir)) != NULL) {
- len = strlen(block_dirent->d_name);
- if ((len == 1 && block_dirent->d_name[0] == '.') ||
- (len == 2 && block_dirent->d_name[0] == '.' && block_dirent->d_name[1] == '.')) {
- /* the . and .. directories; just skip them */
- continue;
- }
-
- /* OK, not . or ..; let's see if it is a SCSI device */
- if (len > 2 &&
- block_dirent->d_name[0] == 's' &&
- block_dirent->d_name[1] == 'd') {
- /* looks like a scsi device, smells like scsi device; it must be
- a scsi device */
- dev = strdup(block_dirent->d_name);
- break;
- }
- }
- closedir(sysdir);
-
- if (dev == NULL) {
- /* we didn't find the sd? device we were looking for; fail */
- virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
- _("Failed to find SCSI device for %d:%d:%d:%d: %s"),
- target, channel, id, lun, strerror(errno));
- return -1;
- }
-
- snprintf(lunid, sizeof(lunid)-1, "lun-%s", groups[3]);
+ int opentries = 0;
if (VIR_ALLOC(vol) < 0) {
virStorageReportError(conn, VIR_ERR_NO_MEMORY, "%s", _("volume"));
goto cleanup;
}
- if ((vol->name = strdup(lunid)) == NULL) {
+ if (asprintf(&(vol->name), "lun-%d", lun) < 0) {
virStorageReportError(conn, VIR_ERR_NO_MEMORY, "%s", _("name"));
goto cleanup;
}
- if (VIR_ALLOC_N(devpath, 5 + strlen(dev) + 1) < 0) {
+ if (asprintf(&devpath, "/dev/%s", dev) < 0) {
virStorageReportError(conn, VIR_ERR_NO_MEMORY, "%s", _("devpath"));
goto cleanup;
- }
- strcpy(devpath, "/dev/");
- strcat(devpath, dev);
- VIR_FREE(dev);
+ }
+
/* 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.
@@ -312,7 +232,6 @@ virStorageBackendISCSIMakeLUN(virConnect
goto cleanup;
}
-
pool->def->capacity += vol->capacity;
pool->def->allocation += vol->allocation;
@@ -328,51 +247,183 @@ virStorageBackendISCSIMakeLUN(virConnect
if (fd != -1) close(fd);
VIR_FREE(devpath);
virStorageVolDefFree(vol);
- VIR_FREE(dev);
return -1;
}
+static int notdotdir(const struct dirent *dir)
+{
+ return !(STREQLEN(dir->d_name, ".", 1) || STREQLEN(dir->d_name, "..", 2));
+}
+
static int
virStorageBackendISCSIFindLUNs(virConnectPtr conn,
virStoragePoolObjPtr pool,
const char *session)
{
- /*
- * # iscsiadm --mode session -r $session -P 3
- *
- * scsi1 Channel 00 Id 0 Lun: 0
- * scsi1 Channel 00 Id 0 Lun: 1
- * Attached scsi disk sdc State: running
- * scsi1 Channel 00 Id 0 Lun: 2
- * Attached scsi disk sdd State: running
- * scsi1 Channel 00 Id 0 Lun: 3
- * Attached scsi disk sde State: running
- * scsi1 Channel 00 Id 0 Lun: 4
- * Attached scsi disk sdf State: running
- * scsi1 Channel 00 Id 0 Lun: 5
- * Attached scsi disk sdg State: running
- *
- * Need a regex to match the Channel:Id:Lun lines
- */
- const char *regexes[] = {
- "^\\s*scsi(\\S+)\\s+Channel\\s+(\\S+)\\s+Id\\s+(\\S+)\\s+Lun:\\s+(\\S+)\\s*$"
- };
- int vars[] = {
- 4
- };
- const char *prog[] = {
- ISCSIADM, "--mode", "session", "-r", session, "-P", "3", NULL,
- };
+ 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;
+ char *block, *scsidev, *block2;
+
+ retval = 0;
+ block = NULL;
+ scsidev = NULL;
- return virStorageBackendRunProgRegex(conn, pool,
- prog,
- 1,
- regexes,
- vars,
- virStorageBackendISCSIMakeLUN,
- (void *)session, NULL);
-}
+ snprintf(sysfs_path, PATH_MAX,
+ "/sys/class/iscsi_session/session%s/device", session);
+ sysdir = opendir(sysfs_path);
+ if (sysdir == NULL) {
+ virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
+ _("Failed to opendir sysfs path %s: %s"),
+ sysfs_path, strerror(errno));
+ return -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 in sysfs path %s: %s"),
+ sysfs_path, strerror(errno));
+ 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 */
+ virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
+ _("Failed to find any LUNs for session %s: %s"),
+ session, strerror(errno));
+
+ return -1;
+ }
+
+ for (i=0; i<n; i++) {
+ if (sscanf(namelist[i]->d_name, "%u:%u:%u:%u\n",
+ &host, &bus, &target, &lun) != 4)
+ continue;
+
+ if (lun == 0)
+ /* the 0'th LUN isn't a real LUN, it's just a control LUN; skip */
+ continue;
+
+ /* we found a LUN; let's find the device */
+ block = NULL;
+ scsidev = NULL;
+ tries = 0;
+
+ while (block == NULL && tries < 5) {
+ snprintf(sysfs_path, PATH_MAX, "/sys/bus/scsi/devices/%u:%u:%u:%u",
+ host, bus, target, lun);
+
+ sysdir = opendir(sysfs_path);
+ if (sysdir == NULL) {
+ virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
+ _("Failed to opendir sysfs path %s: %s"),
+ sysfs_path, strerror(errno));
+ 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) {
+ virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR,
+ _("Failed to opendir sysfs path %s: %s"),
+ sysfs_path, strerror(errno));
+ 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);
+ }
+
+ 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;
+}
static int
virStorageBackendISCSIRescanLUNs(virConnectPtr conn,
[Date Prev][Date Next] [Thread Prev][Thread Next]
[Thread Index]
[Date Index]
[Author Index]