2009-04-01 16:03:22 +00:00
|
|
|
/*
|
|
|
|
* 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"
|
2010-11-09 20:48:48 +00:00
|
|
|
#include "files.h"
|
2010-11-12 15:49:40 +00:00
|
|
|
#include "command.h"
|
2009-04-01 16:03:22 +00:00
|
|
|
|
|
|
|
#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
|
2010-02-10 11:42:56 +00:00
|
|
|
getDeviceType(uint32_t host,
|
2009-04-01 16:03:22 +00:00
|
|
|
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) {
|
2010-02-04 18:19:08 +00:00
|
|
|
virReportOOMError();
|
2009-04-01 16:03:22 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
typefile = fopen(type_path, "r");
|
|
|
|
if (typefile == NULL) {
|
2010-02-04 20:02:58 +00:00
|
|
|
virReportSystemError(errno,
|
2009-04-01 16:03:22 +00:00
|
|
|
_("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);
|
2010-11-17 02:13:29 +00:00
|
|
|
VIR_FORCE_FCLOSE(typefile);
|
2009-04-01 16:03:22 +00:00
|
|
|
|
|
|
|
if (gottype == NULL) {
|
2010-02-04 20:02:58 +00:00
|
|
|
virReportSystemError(errno,
|
2009-04-01 16:03:22 +00:00
|
|
|
_("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) {
|
2010-02-10 11:42:56 +00:00
|
|
|
virStorageReportError(VIR_ERR_INTERNAL_ERROR,
|
2009-04-01 16:03:22 +00:00
|
|
|
_("Device type '%s' is not an integer"),
|
|
|
|
typestr);
|
|
|
|
/* Hm, type wasn't an integer; seems strange */
|
|
|
|
retval = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2010-05-20 06:57:06 +00:00
|
|
|
VIR_DEBUG("Device type is %d", *type);
|
2009-04-01 16:03:22 +00:00
|
|
|
|
|
|
|
out:
|
|
|
|
VIR_FREE(type_path);
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2009-04-03 14:17:57 +00:00
|
|
|
struct diskType {
|
|
|
|
int part_table_type;
|
|
|
|
unsigned short offset;
|
|
|
|
unsigned short length;
|
|
|
|
unsigned long long magic;
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct diskType const disk_types[] = {
|
|
|
|
{ VIR_STORAGE_POOL_DISK_LVM2, 0x218, 8, 0x31303020324D564CULL },
|
|
|
|
{ VIR_STORAGE_POOL_DISK_GPT, 0x200, 8, 0x5452415020494645ULL },
|
|
|
|
{ VIR_STORAGE_POOL_DISK_DVH, 0x0, 4, 0x41A9E50BULL },
|
|
|
|
{ VIR_STORAGE_POOL_DISK_MAC, 0x0, 2, 0x5245ULL },
|
|
|
|
{ VIR_STORAGE_POOL_DISK_BSD, 0x40, 4, 0x82564557ULL },
|
|
|
|
{ VIR_STORAGE_POOL_DISK_SUN, 0x1fc, 2, 0xBEDAULL },
|
|
|
|
/*
|
|
|
|
* NOTE: pc98 is funky; the actual signature is 0x55AA (just like dos), so
|
|
|
|
* we can't use that. At the moment I'm relying on the "dummy" IPL
|
|
|
|
* bootloader data that comes from parted. Luckily, the chances of running
|
|
|
|
* into a pc98 machine running libvirt are approximately nil.
|
|
|
|
*/
|
|
|
|
/*{ 0x1fe, 2, 0xAA55UL },*/
|
|
|
|
{ VIR_STORAGE_POOL_DISK_PC98, 0x0, 8, 0x314C5049000000CBULL },
|
|
|
|
/*
|
|
|
|
* NOTE: the order is important here; some other disk types (like GPT and
|
|
|
|
* and PC98) also have 0x55AA at this offset. For that reason, the DOS
|
|
|
|
* one must be the last one.
|
|
|
|
*/
|
|
|
|
{ VIR_STORAGE_POOL_DISK_DOS, 0x1fe, 2, 0xAA55ULL },
|
|
|
|
{ -1, 0x0, 0, 0x0ULL },
|
|
|
|
};
|
|
|
|
|
|
|
|
static int
|
2010-02-04 20:02:58 +00:00
|
|
|
virStorageBackendSCSIUpdateVolTargetInfo(virStorageVolTargetPtr target,
|
2009-04-03 14:17:57 +00:00
|
|
|
unsigned long long *allocation,
|
|
|
|
unsigned long long *capacity)
|
|
|
|
{
|
storage: Check for invalid storage mode before opening
If a directory pool contains pipes or sockets, a pool start can fail or hang:
https://bugzilla.redhat.com/show_bug.cgi?id=589577
We already try to avoid these special files, but only attempt after
opening the path, which is where the problems lie. Unify volume opening
into helper functions, which use the proper open() flags to avoid error,
followed by fstat to validate storage mode.
Previously, virStorageBackendUpdateVolTargetInfoFD attempted to enforce the
storage mode check, but allowed callers to detect this case and silently
continue. In practice, only the FS backend was using this feature, the rest
were treating unknown mode as an error condition. Unfortunately the InfoFD
function wasn't raising an error message here, so error reporting was
busted.
This patch adds 2 functions: virStorageBackendVolOpen, and
virStorageBackendVolOpenModeSkip. The latter retains the original opt out
semantics, the former now throws an explicit error.
This patch maintains the previous volume mode checks: allowing specific
modes for specific pool types requires a bit of surgery, since VolOpen
is called through several different helper functions.
v2: Use ATTRIBUTE_NONNULL. Drop stat check, just open with
O_NONBLOCK|O_NOCTTY.
v3: Move mode check logic back to VolOpen. Use 2 VolOpen functions with
different error semantics.
v4: Make second VolOpen function more extensible. Didn't opt to change
FS backend defaults, this can just be to fix the original bug.
v5: Prefix default flags with VIR_, use ATTRIBUTE_RETURN_CHECK
2010-05-20 18:25:01 +00:00
|
|
|
int fdret, fd = -1;
|
|
|
|
int ret = -1;
|
2009-04-03 14:17:57 +00:00
|
|
|
|
storage: Check for invalid storage mode before opening
If a directory pool contains pipes or sockets, a pool start can fail or hang:
https://bugzilla.redhat.com/show_bug.cgi?id=589577
We already try to avoid these special files, but only attempt after
opening the path, which is where the problems lie. Unify volume opening
into helper functions, which use the proper open() flags to avoid error,
followed by fstat to validate storage mode.
Previously, virStorageBackendUpdateVolTargetInfoFD attempted to enforce the
storage mode check, but allowed callers to detect this case and silently
continue. In practice, only the FS backend was using this feature, the rest
were treating unknown mode as an error condition. Unfortunately the InfoFD
function wasn't raising an error message here, so error reporting was
busted.
This patch adds 2 functions: virStorageBackendVolOpen, and
virStorageBackendVolOpenModeSkip. The latter retains the original opt out
semantics, the former now throws an explicit error.
This patch maintains the previous volume mode checks: allowing specific
modes for specific pool types requires a bit of surgery, since VolOpen
is called through several different helper functions.
v2: Use ATTRIBUTE_NONNULL. Drop stat check, just open with
O_NONBLOCK|O_NOCTTY.
v3: Move mode check logic back to VolOpen. Use 2 VolOpen functions with
different error semantics.
v4: Make second VolOpen function more extensible. Didn't opt to change
FS backend defaults, this can just be to fix the original bug.
v5: Prefix default flags with VIR_, use ATTRIBUTE_RETURN_CHECK
2010-05-20 18:25:01 +00:00
|
|
|
if ((fdret = virStorageBackendVolOpen(target->path)) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
fd = fdret;
|
2009-04-03 14:17:57 +00:00
|
|
|
|
2010-02-04 20:02:58 +00:00
|
|
|
if (virStorageBackendUpdateVolTargetInfoFD(target,
|
2009-04-03 14:17:57 +00:00
|
|
|
fd,
|
|
|
|
allocation,
|
|
|
|
capacity) < 0)
|
2009-09-07 14:47:13 +00:00
|
|
|
goto cleanup;
|
2009-04-03 14:17:57 +00:00
|
|
|
|
2010-05-20 17:29:24 +00:00
|
|
|
if (virStorageBackendDetectBlockVolFormatFD(target, fd) < 0)
|
2009-09-07 14:47:13 +00:00
|
|
|
goto cleanup;
|
2009-04-03 14:17:57 +00:00
|
|
|
|
2009-09-07 14:47:13 +00:00
|
|
|
ret = 0;
|
|
|
|
|
storage: Check for invalid storage mode before opening
If a directory pool contains pipes or sockets, a pool start can fail or hang:
https://bugzilla.redhat.com/show_bug.cgi?id=589577
We already try to avoid these special files, but only attempt after
opening the path, which is where the problems lie. Unify volume opening
into helper functions, which use the proper open() flags to avoid error,
followed by fstat to validate storage mode.
Previously, virStorageBackendUpdateVolTargetInfoFD attempted to enforce the
storage mode check, but allowed callers to detect this case and silently
continue. In practice, only the FS backend was using this feature, the rest
were treating unknown mode as an error condition. Unfortunately the InfoFD
function wasn't raising an error message here, so error reporting was
busted.
This patch adds 2 functions: virStorageBackendVolOpen, and
virStorageBackendVolOpenModeSkip. The latter retains the original opt out
semantics, the former now throws an explicit error.
This patch maintains the previous volume mode checks: allowing specific
modes for specific pool types requires a bit of surgery, since VolOpen
is called through several different helper functions.
v2: Use ATTRIBUTE_NONNULL. Drop stat check, just open with
O_NONBLOCK|O_NOCTTY.
v3: Move mode check logic back to VolOpen. Use 2 VolOpen functions with
different error semantics.
v4: Make second VolOpen function more extensible. Didn't opt to change
FS backend defaults, this can just be to fix the original bug.
v5: Prefix default flags with VIR_, use ATTRIBUTE_RETURN_CHECK
2010-05-20 18:25:01 +00:00
|
|
|
cleanup:
|
2010-11-09 20:48:48 +00:00
|
|
|
VIR_FORCE_CLOSE(fd);
|
2009-09-07 14:47:13 +00:00
|
|
|
|
|
|
|
return ret;
|
2009-04-03 14:17:57 +00:00
|
|
|
}
|
2009-04-01 16:03:22 +00:00
|
|
|
|
2010-11-12 15:49:40 +00:00
|
|
|
|
|
|
|
static char *
|
|
|
|
virStorageBackendSCSISerial(const char *dev)
|
|
|
|
{
|
|
|
|
char *serial = NULL;
|
|
|
|
#ifdef HAVE_UDEV
|
|
|
|
virCommandPtr cmd = virCommandNewArgList(
|
|
|
|
"/lib/udev/scsi_id",
|
|
|
|
"--replace-whitespace",
|
|
|
|
"--whitelisted",
|
|
|
|
"--device", dev,
|
|
|
|
NULL
|
|
|
|
);
|
|
|
|
|
|
|
|
/* Run the program and capture its output */
|
|
|
|
virCommandSetOutputBuffer(cmd, &serial);
|
|
|
|
if (virCommandRun(cmd, NULL) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (serial && STRNEQ(serial, "")) {
|
|
|
|
char *nl = strchr(serial, '\n');
|
|
|
|
if (nl)
|
|
|
|
*nl = '\0';
|
|
|
|
} else {
|
|
|
|
VIR_FREE(serial);
|
|
|
|
if (!(serial = strdup(dev)))
|
|
|
|
virReportOOMError();
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef HAVE_UDEV
|
|
|
|
cleanup:
|
|
|
|
virCommandFree(cmd);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return serial;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-04-01 16:03:22 +00:00
|
|
|
static int
|
2010-02-10 11:42:56 +00:00
|
|
|
virStorageBackendSCSINewLun(virStoragePoolObjPtr pool,
|
2010-11-12 15:49:40 +00:00
|
|
|
uint32_t host ATTRIBUTE_UNUSED,
|
2009-04-01 16:03:22 +00:00
|
|
|
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) {
|
2010-02-04 18:19:08 +00:00
|
|
|
virReportOOMError();
|
2009-04-01 16:03:22 +00:00
|
|
|
retval = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
vol->type = VIR_STORAGE_VOL_BLOCK;
|
|
|
|
|
2010-11-12 15:49:40 +00:00
|
|
|
/* 'host' is dynamically allocated by the kernel, first come,
|
|
|
|
* first served, per HBA. As such it isn't suitable for use
|
|
|
|
* in the volume name. We only need uniqueness per-pool, so
|
|
|
|
* just leave 'host' out
|
|
|
|
*/
|
|
|
|
if (virAsprintf(&(vol->name), "unit:%u:%u:%u", bus, target, lun) < 0) {
|
2010-02-04 18:19:08 +00:00
|
|
|
virReportOOMError();
|
2009-04-01 16:03:22 +00:00
|
|
|
retval = -1;
|
|
|
|
goto free_vol;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virAsprintf(&devpath, "/dev/%s", dev) < 0) {
|
2010-02-04 18:19:08 +00:00
|
|
|
virReportOOMError();
|
2009-04-01 16:03:22 +00:00
|
|
|
retval = -1;
|
|
|
|
goto free_vol;
|
|
|
|
}
|
|
|
|
|
2010-05-20 06:57:06 +00:00
|
|
|
VIR_DEBUG("Trying to create volume for '%s'", devpath);
|
2009-04-01 16:03:22 +00:00
|
|
|
|
|
|
|
/* 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...
|
|
|
|
*/
|
2010-02-04 20:02:58 +00:00
|
|
|
if ((vol->target.path = virStorageBackendStablePath(pool,
|
2009-04-01 16:03:22 +00:00
|
|
|
devpath)) == NULL) {
|
|
|
|
retval = -1;
|
|
|
|
goto free_vol;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (STREQLEN(devpath, vol->target.path, PATH_MAX) &&
|
|
|
|
!(STREQ(pool->def->target.path, "/dev") ||
|
|
|
|
STREQ(pool->def->target.path, "/dev/"))) {
|
|
|
|
|
2010-05-20 06:57:06 +00:00
|
|
|
VIR_DEBUG("No stable path found for '%s' in '%s'",
|
2009-04-01 16:03:22 +00:00
|
|
|
devpath, pool->def->target.path);
|
|
|
|
|
|
|
|
retval = -1;
|
|
|
|
goto free_vol;
|
|
|
|
}
|
|
|
|
|
2010-02-04 20:02:58 +00:00
|
|
|
if (virStorageBackendSCSIUpdateVolTargetInfo(&vol->target,
|
2009-04-03 14:17:57 +00:00
|
|
|
&vol->allocation,
|
|
|
|
&vol->capacity) < 0) {
|
2009-04-01 16:03:22 +00:00
|
|
|
|
2010-02-10 11:42:56 +00:00
|
|
|
virStorageReportError(VIR_ERR_INTERNAL_ERROR,
|
2009-04-01 16:03:22 +00:00
|
|
|
_("Failed to update volume for '%s'"),
|
|
|
|
devpath);
|
|
|
|
retval = -1;
|
|
|
|
goto free_vol;
|
|
|
|
}
|
|
|
|
|
2010-11-12 15:49:40 +00:00
|
|
|
if (!(vol->key = virStorageBackendSCSISerial(vol->target.path))) {
|
2009-04-01 16:03:22 +00:00
|
|
|
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) {
|
2010-02-04 18:19:08 +00:00
|
|
|
virReportOOMError();
|
2009-04-01 16:03:22 +00:00
|
|
|
retval = -1;
|
|
|
|
goto free_vol;
|
|
|
|
}
|
|
|
|
pool->volumes.objs[pool->volumes.count++] = vol;
|
|
|
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
free_vol:
|
|
|
|
virStorageVolDefFree(vol);
|
|
|
|
out:
|
|
|
|
VIR_FREE(devpath);
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
2010-02-10 11:42:56 +00:00
|
|
|
getNewStyleBlockDevice(const char *lun_path,
|
2009-04-01 16:03:22 +00:00
|
|
|
const char *block_name ATTRIBUTE_UNUSED,
|
|
|
|
char **block_device)
|
|
|
|
{
|
|
|
|
char *block_path = NULL;
|
|
|
|
DIR *block_dir = NULL;
|
|
|
|
struct dirent *block_dirent = NULL;
|
|
|
|
int retval = 0;
|
|
|
|
|
|
|
|
if (virAsprintf(&block_path, "%s/block", lun_path) < 0) {
|
2010-02-04 18:19:08 +00:00
|
|
|
virReportOOMError();
|
2009-04-01 16:03:22 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2010-05-20 06:57:06 +00:00
|
|
|
VIR_DEBUG("Looking for block device in '%s'", block_path);
|
2009-04-01 16:03:22 +00:00
|
|
|
|
|
|
|
block_dir = opendir(block_path);
|
|
|
|
if (block_dir == NULL) {
|
2010-02-04 20:02:58 +00:00
|
|
|
virReportSystemError(errno,
|
2009-04-01 16:03:22 +00:00
|
|
|
_("Failed to opendir sysfs path '%s'"),
|
|
|
|
block_path);
|
|
|
|
retval = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
while ((block_dirent = readdir(block_dir))) {
|
|
|
|
|
|
|
|
if (STREQLEN(block_dirent->d_name, ".", 1)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
*block_device = strdup(block_dirent->d_name);
|
2009-11-08 21:08:54 +00:00
|
|
|
|
|
|
|
if (*block_device == NULL) {
|
2010-02-04 18:19:08 +00:00
|
|
|
virReportOOMError();
|
2009-11-08 21:08:54 +00:00
|
|
|
closedir(block_dir);
|
|
|
|
retval = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2010-05-20 06:57:06 +00:00
|
|
|
VIR_DEBUG("Block device is '%s'", *block_device);
|
2009-04-01 16:03:22 +00:00
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
closedir(block_dir);
|
|
|
|
|
|
|
|
out:
|
|
|
|
VIR_FREE(block_path);
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
2010-02-10 11:42:56 +00:00
|
|
|
getOldStyleBlockDevice(const char *lun_path ATTRIBUTE_UNUSED,
|
2009-04-01 16:03:22 +00:00
|
|
|
const char *block_name,
|
|
|
|
char **block_device)
|
|
|
|
{
|
|
|
|
char *blockp = NULL;
|
|
|
|
int retval = 0;
|
|
|
|
|
|
|
|
/* old-style; just parse out the sd */
|
|
|
|
blockp = strrchr(block_name, ':');
|
|
|
|
if (blockp == NULL) {
|
|
|
|
/* Hm, wasn't what we were expecting; have to give up */
|
2010-02-10 11:42:56 +00:00
|
|
|
virStorageReportError(VIR_ERR_INTERNAL_ERROR,
|
2009-04-01 16:03:22 +00:00
|
|
|
_("Failed to parse block name %s"),
|
|
|
|
block_name);
|
|
|
|
retval = -1;
|
|
|
|
} else {
|
|
|
|
blockp++;
|
|
|
|
*block_device = strdup(blockp);
|
|
|
|
|
2009-11-08 21:08:54 +00:00
|
|
|
if (*block_device == NULL) {
|
2010-02-04 18:19:08 +00:00
|
|
|
virReportOOMError();
|
2009-11-08 21:08:54 +00:00
|
|
|
retval = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2010-05-20 06:57:06 +00:00
|
|
|
VIR_DEBUG("Block device is '%s'", *block_device);
|
2009-04-01 16:03:22 +00:00
|
|
|
}
|
|
|
|
|
2009-11-08 21:08:54 +00:00
|
|
|
out:
|
2009-04-01 16:03:22 +00:00
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
2010-02-10 11:42:56 +00:00
|
|
|
getBlockDevice(uint32_t host,
|
2009-04-01 16:03:22 +00:00
|
|
|
uint32_t bus,
|
|
|
|
uint32_t target,
|
|
|
|
uint32_t lun,
|
|
|
|
char **block_device)
|
|
|
|
{
|
|
|
|
char *lun_path = NULL;
|
|
|
|
DIR *lun_dir = NULL;
|
|
|
|
struct dirent *lun_dirent = NULL;
|
|
|
|
int retval = 0;
|
|
|
|
|
|
|
|
if (virAsprintf(&lun_path, "/sys/bus/scsi/devices/%u:%u:%u:%u",
|
|
|
|
host, bus, target, lun) < 0) {
|
2010-02-04 18:19:08 +00:00
|
|
|
virReportOOMError();
|
2009-04-01 16:03:22 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
lun_dir = opendir(lun_path);
|
|
|
|
if (lun_dir == NULL) {
|
2010-02-04 20:02:58 +00:00
|
|
|
virReportSystemError(errno,
|
2009-04-01 16:03:22 +00:00
|
|
|
_("Failed to opendir sysfs path '%s'"),
|
|
|
|
lun_path);
|
|
|
|
retval = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
while ((lun_dirent = readdir(lun_dir))) {
|
|
|
|
if (STREQLEN(lun_dirent->d_name, "block", 5)) {
|
|
|
|
if (strlen(lun_dirent->d_name) == 5) {
|
2010-02-10 11:42:56 +00:00
|
|
|
retval = getNewStyleBlockDevice(lun_path,
|
2009-04-01 16:03:22 +00:00
|
|
|
lun_dirent->d_name,
|
|
|
|
block_device);
|
|
|
|
} else {
|
2010-02-10 11:42:56 +00:00
|
|
|
retval = getOldStyleBlockDevice(lun_path,
|
2009-04-01 16:03:22 +00:00
|
|
|
lun_dirent->d_name,
|
|
|
|
block_device);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
closedir(lun_dir);
|
|
|
|
|
|
|
|
out:
|
|
|
|
VIR_FREE(lun_path);
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
2010-02-10 11:42:56 +00:00
|
|
|
processLU(virStoragePoolObjPtr pool,
|
2009-04-01 16:03:22 +00:00
|
|
|
uint32_t host,
|
|
|
|
uint32_t bus,
|
|
|
|
uint32_t target,
|
|
|
|
uint32_t lun)
|
|
|
|
{
|
|
|
|
char *type_path = NULL;
|
|
|
|
int retval = 0;
|
|
|
|
int device_type;
|
|
|
|
char *block_device = NULL;
|
|
|
|
|
2010-05-20 06:57:06 +00:00
|
|
|
VIR_DEBUG("Processing LU %u:%u:%u:%u",
|
2009-04-01 16:03:22 +00:00
|
|
|
host, bus, target, lun);
|
|
|
|
|
2010-02-10 11:42:56 +00:00
|
|
|
if (getDeviceType(host, bus, target, lun, &device_type) < 0) {
|
|
|
|
virStorageReportError(VIR_ERR_INTERNAL_ERROR,
|
2009-04-01 16:03:22 +00:00
|
|
|
_("Failed to determine if %u:%u:%u:%u is a Direct-Access LUN"),
|
|
|
|
host, bus, target, lun);
|
|
|
|
retval = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We don't create volumes for devices other than disk and cdrom
|
|
|
|
* devices, but finding a device that isn't one of those types
|
|
|
|
* isn't an error, either. */
|
|
|
|
if (!(device_type == VIR_STORAGE_DEVICE_TYPE_DISK ||
|
|
|
|
device_type == VIR_STORAGE_DEVICE_TYPE_ROM))
|
|
|
|
{
|
|
|
|
retval = 0;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2010-05-20 06:57:06 +00:00
|
|
|
VIR_DEBUG("%u:%u:%u:%u is a Direct-Access LUN",
|
2009-04-01 16:03:22 +00:00
|
|
|
host, bus, target, lun);
|
|
|
|
|
2010-02-10 11:42:56 +00:00
|
|
|
if (getBlockDevice(host, bus, target, lun, &block_device) < 0) {
|
2009-04-01 16:03:22 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2010-02-10 11:42:56 +00:00
|
|
|
if (virStorageBackendSCSINewLun(pool,
|
2009-04-01 16:03:22 +00:00
|
|
|
host, bus, target, lun,
|
|
|
|
block_device) < 0) {
|
2010-05-20 06:57:06 +00:00
|
|
|
VIR_DEBUG("Failed to create new storage volume for %u:%u:%u:%u",
|
2009-04-01 16:03:22 +00:00
|
|
|
host, bus, target, lun);
|
|
|
|
retval = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2010-05-20 06:57:06 +00:00
|
|
|
VIR_DEBUG("Created new storage volume for %u:%u:%u:%u successfully",
|
2009-04-01 16:03:22 +00:00
|
|
|
host, bus, target, lun);
|
|
|
|
|
|
|
|
VIR_FREE(type_path);
|
|
|
|
|
|
|
|
out:
|
2010-12-10 12:26:41 +00:00
|
|
|
VIR_FREE(block_device);
|
2009-04-01 16:03:22 +00:00
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
2010-02-10 11:42:56 +00:00
|
|
|
virStorageBackendSCSIFindLUs(virStoragePoolObjPtr pool,
|
2009-04-01 16:03:22 +00:00
|
|
|
uint32_t scanhost)
|
|
|
|
{
|
|
|
|
int retval = 0;
|
|
|
|
uint32_t bus, target, lun;
|
|
|
|
char *device_path = NULL;
|
|
|
|
DIR *devicedir = NULL;
|
|
|
|
struct dirent *lun_dirent = NULL;
|
|
|
|
char devicepattern[64];
|
|
|
|
|
2010-05-20 06:57:06 +00:00
|
|
|
VIR_DEBUG("Discovering LUs on host %u", scanhost);
|
2009-04-01 16:03:22 +00:00
|
|
|
|
2010-02-04 22:41:52 +00:00
|
|
|
virFileWaitForDevices();
|
2009-04-01 16:03:22 +00:00
|
|
|
|
|
|
|
if (virAsprintf(&device_path, "/sys/bus/scsi/devices") < 0) {
|
2010-02-04 18:19:08 +00:00
|
|
|
virReportOOMError();
|
2009-04-01 16:03:22 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
devicedir = opendir(device_path);
|
|
|
|
|
|
|
|
if (devicedir == NULL) {
|
2010-02-04 20:02:58 +00:00
|
|
|
virReportSystemError(errno,
|
2009-04-01 16:03:22 +00:00
|
|
|
_("Failed to opendir path '%s'"), device_path);
|
|
|
|
retval = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
snprintf(devicepattern, sizeof(devicepattern), "%u:%%u:%%u:%%u\n", scanhost);
|
|
|
|
|
|
|
|
while ((lun_dirent = readdir(devicedir))) {
|
|
|
|
if (sscanf(lun_dirent->d_name, devicepattern,
|
|
|
|
&bus, &target, &lun) != 3) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2010-05-20 06:57:06 +00:00
|
|
|
VIR_DEBUG("Found LU '%s'", lun_dirent->d_name);
|
2009-04-01 16:03:22 +00:00
|
|
|
|
2010-02-10 11:42:56 +00:00
|
|
|
processLU(pool, scanhost, bus, target, lun);
|
2009-04-01 16:03:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
closedir(devicedir);
|
|
|
|
|
|
|
|
out:
|
|
|
|
VIR_FREE(device_path);
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
2010-02-04 22:41:52 +00:00
|
|
|
virStorageBackendSCSIGetHostNumber(const char *sysfs_path,
|
2009-04-01 16:03:22 +00:00
|
|
|
uint32_t *host)
|
|
|
|
{
|
|
|
|
int retval = 0;
|
|
|
|
DIR *sysdir = NULL;
|
|
|
|
struct dirent *dirent = NULL;
|
|
|
|
|
2010-05-20 06:57:06 +00:00
|
|
|
VIR_DEBUG("Finding host number from '%s'", sysfs_path);
|
2009-04-01 16:03:22 +00:00
|
|
|
|
2010-02-04 22:41:52 +00:00
|
|
|
virFileWaitForDevices();
|
2009-04-01 16:03:22 +00:00
|
|
|
|
|
|
|
sysdir = opendir(sysfs_path);
|
|
|
|
|
|
|
|
if (sysdir == NULL) {
|
2010-02-04 20:02:58 +00:00
|
|
|
virReportSystemError(errno,
|
2009-04-01 16:03:22 +00:00
|
|
|
_("Failed to opendir path '%s'"), sysfs_path);
|
|
|
|
retval = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
while ((dirent = readdir(sysdir))) {
|
|
|
|
if (STREQLEN(dirent->d_name, "target", strlen("target"))) {
|
|
|
|
if (sscanf(dirent->d_name,
|
|
|
|
"target%u:", host) != 1) {
|
2010-05-20 06:57:06 +00:00
|
|
|
VIR_DEBUG("Failed to parse target '%s'", dirent->d_name);
|
2009-04-01 16:03:22 +00:00
|
|
|
retval = -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
closedir(sysdir);
|
|
|
|
out:
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-04-07 12:50:17 +00:00
|
|
|
static int
|
2010-02-04 20:02:58 +00:00
|
|
|
virStorageBackendSCSITriggerRescan(uint32_t host)
|
2009-04-07 12:50:17 +00:00
|
|
|
{
|
|
|
|
int fd = -1;
|
|
|
|
int retval = 0;
|
|
|
|
char *path;
|
|
|
|
|
2010-05-20 06:57:06 +00:00
|
|
|
VIR_DEBUG("Triggering rescan of host %d", host);
|
2009-04-07 12:50:17 +00:00
|
|
|
|
|
|
|
if (virAsprintf(&path, "/sys/class/scsi_host/host%u/scan", host) < 0) {
|
2010-02-04 18:19:08 +00:00
|
|
|
virReportOOMError();
|
2009-04-07 12:50:17 +00:00
|
|
|
retval = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2010-05-20 06:57:06 +00:00
|
|
|
VIR_DEBUG("Scan trigger path is '%s'", path);
|
2009-04-07 12:50:17 +00:00
|
|
|
|
|
|
|
fd = open(path, O_WRONLY);
|
|
|
|
|
|
|
|
if (fd < 0) {
|
2010-02-04 20:02:58 +00:00
|
|
|
virReportSystemError(errno,
|
2009-04-07 12:50:17 +00:00
|
|
|
_("Could not open '%s' to trigger host scan"),
|
|
|
|
path);
|
|
|
|
retval = -1;
|
|
|
|
goto free_path;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (safewrite(fd,
|
|
|
|
LINUX_SYSFS_SCSI_HOST_SCAN_STRING,
|
|
|
|
sizeof(LINUX_SYSFS_SCSI_HOST_SCAN_STRING)) < 0) {
|
2010-11-09 20:48:48 +00:00
|
|
|
VIR_FORCE_CLOSE(fd);
|
2010-02-04 20:02:58 +00:00
|
|
|
virReportSystemError(errno,
|
2009-04-07 12:50:17 +00:00
|
|
|
_("Write to '%s' to trigger host scan failed"),
|
|
|
|
path);
|
|
|
|
retval = -1;
|
|
|
|
}
|
|
|
|
|
2010-11-09 20:48:48 +00:00
|
|
|
VIR_FORCE_CLOSE(fd);
|
2009-04-07 12:50:17 +00:00
|
|
|
free_path:
|
|
|
|
VIR_FREE(path);
|
|
|
|
out:
|
2010-05-20 06:57:06 +00:00
|
|
|
VIR_DEBUG("Rescan of host %d complete", host);
|
2009-04-07 12:50:17 +00:00
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2010-11-11 20:09:20 +00:00
|
|
|
static int
|
|
|
|
virStorageBackendSCSICheckPool(virConnectPtr conn ATTRIBUTE_UNUSED,
|
|
|
|
virStoragePoolObjPtr pool,
|
|
|
|
bool *isActive)
|
|
|
|
{
|
|
|
|
char *path;
|
|
|
|
|
|
|
|
*isActive = false;
|
|
|
|
if (virAsprintf(&path, "/sys/class/scsi_host/%s", pool->def->source.adapter) < 0) {
|
|
|
|
virReportOOMError();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (access(path, F_OK) == 0)
|
|
|
|
*isActive = true;
|
|
|
|
|
|
|
|
VIR_FREE(path);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2009-04-07 12:50:17 +00:00
|
|
|
|
2009-04-01 16:03:22 +00:00
|
|
|
static int
|
2010-02-10 11:42:56 +00:00
|
|
|
virStorageBackendSCSIRefreshPool(virConnectPtr conn ATTRIBUTE_UNUSED,
|
2009-04-01 16:03:22 +00:00
|
|
|
virStoragePoolObjPtr pool)
|
|
|
|
{
|
|
|
|
int retval = 0;
|
|
|
|
uint32_t host;
|
|
|
|
|
|
|
|
pool->def->allocation = pool->def->capacity = pool->def->available = 0;
|
|
|
|
|
|
|
|
if (sscanf(pool->def->source.adapter, "host%u", &host) != 1) {
|
2010-05-20 06:57:06 +00:00
|
|
|
VIR_DEBUG("Failed to get host number from '%s'",
|
2009-04-07 12:50:17 +00:00
|
|
|
pool->def->source.adapter);
|
2009-04-01 16:03:22 +00:00
|
|
|
retval = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2010-05-20 06:57:06 +00:00
|
|
|
VIR_DEBUG("Scanning host%u", host);
|
2009-04-01 16:03:22 +00:00
|
|
|
|
2010-02-04 20:02:58 +00:00
|
|
|
if (virStorageBackendSCSITriggerRescan(host) < 0) {
|
2009-04-07 12:50:17 +00:00
|
|
|
retval = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2010-02-10 11:42:56 +00:00
|
|
|
virStorageBackendSCSIFindLUs(pool, host);
|
2009-04-01 16:03:22 +00:00
|
|
|
|
|
|
|
out:
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
virStorageBackend virStorageBackendSCSI = {
|
|
|
|
.type = VIR_STORAGE_POOL_SCSI,
|
|
|
|
|
2010-11-11 20:09:20 +00:00
|
|
|
.checkPool = virStorageBackendSCSICheckPool,
|
2009-04-01 16:03:22 +00:00
|
|
|
.refreshPool = virStorageBackendSCSIRefreshPool,
|
|
|
|
};
|