2009-09-08 13:47:45 +00:00
|
|
|
/*
|
|
|
|
* storage_backend_mpath.c: storage backend for multipath handling
|
|
|
|
*
|
2011-11-07 17:47:31 +00:00
|
|
|
* Copyright (C) 2009-2011 Red Hat, Inc.
|
2009-09-08 13:47:45 +00:00
|
|
|
* Copyright (C) 2009-2008 Dave Allan
|
|
|
|
*
|
|
|
|
* 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
|
2012-09-20 22:30:55 +00:00
|
|
|
* License along with this library. If not, see
|
2012-07-21 10:06:23 +00:00
|
|
|
* <http://www.gnu.org/licenses/>.
|
2009-09-08 13:47:45 +00:00
|
|
|
*
|
|
|
|
* Author: Dave Allan <dallan@redhat.com>
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
|
|
|
|
#include <libdevmapper.h>
|
|
|
|
|
|
|
|
#include "virterror_internal.h"
|
|
|
|
#include "storage_conf.h"
|
|
|
|
#include "storage_backend.h"
|
|
|
|
#include "memory.h"
|
|
|
|
#include "logging.h"
|
2011-07-19 18:32:58 +00:00
|
|
|
#include "virfile.h"
|
2009-09-08 13:47:45 +00:00
|
|
|
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_STORAGE
|
|
|
|
|
|
|
|
static int
|
2010-02-10 11:42:56 +00:00
|
|
|
virStorageBackendMpathUpdateVolTargetInfo(virStorageVolTargetPtr target,
|
2009-09-08 13:47:45 +00:00
|
|
|
unsigned long long *allocation,
|
|
|
|
unsigned long long *capacity)
|
|
|
|
{
|
2010-05-20 18:16:54 +00:00
|
|
|
int ret = -1;
|
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;
|
2009-09-08 13:47:45 +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)
|
2009-09-08 13:47:45 +00:00
|
|
|
goto out;
|
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
|
|
|
fd = fdret;
|
2009-09-08 13:47:45 +00:00
|
|
|
|
2010-02-04 20:02:58 +00:00
|
|
|
if (virStorageBackendUpdateVolTargetInfoFD(target,
|
2009-09-08 13:47:45 +00:00
|
|
|
fd,
|
|
|
|
allocation,
|
2010-05-20 18:16:54 +00:00
|
|
|
capacity) < 0)
|
2009-09-08 13:47:45 +00:00
|
|
|
goto out;
|
|
|
|
|
2010-05-20 17:29:24 +00:00
|
|
|
if (virStorageBackendDetectBlockVolFormatFD(target, fd) < 0)
|
2009-09-08 13:47:45 +00:00
|
|
|
goto out;
|
|
|
|
|
2010-05-20 18:16:54 +00:00
|
|
|
ret = 0;
|
2009-09-08 13:47:45 +00:00
|
|
|
out:
|
2010-11-09 20:48:48 +00:00
|
|
|
VIR_FORCE_CLOSE(fd);
|
2009-09-08 13:47:45 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
2010-02-10 11:42:56 +00:00
|
|
|
virStorageBackendMpathNewVol(virStoragePoolObjPtr pool,
|
2009-09-08 13:47:45 +00:00
|
|
|
const int devnum,
|
|
|
|
const char *dev)
|
|
|
|
{
|
|
|
|
virStorageVolDefPtr vol;
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
if (VIR_ALLOC(vol) < 0) {
|
2010-02-04 18:19:08 +00:00
|
|
|
virReportOOMError();
|
2009-09-08 13:47:45 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
vol->type = VIR_STORAGE_VOL_BLOCK;
|
|
|
|
|
|
|
|
if (virAsprintf(&(vol->name), "dm-%u", devnum) < 0) {
|
2010-02-04 18:19:08 +00:00
|
|
|
virReportOOMError();
|
2009-09-08 13:47:45 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virAsprintf(&vol->target.path, "/dev/%s", dev) < 0) {
|
2010-02-04 18:19:08 +00:00
|
|
|
virReportOOMError();
|
2009-09-08 13:47:45 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2010-02-10 11:42:56 +00:00
|
|
|
if (virStorageBackendMpathUpdateVolTargetInfo(&vol->target,
|
2009-09-08 13:47:45 +00:00
|
|
|
&vol->allocation,
|
|
|
|
&vol->capacity) < 0) {
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* XXX should use logical unit's UUID instead */
|
|
|
|
vol->key = strdup(vol->target.path);
|
|
|
|
if (vol->key == NULL) {
|
2010-02-04 18:19:08 +00:00
|
|
|
virReportOOMError();
|
2009-09-08 13:47:45 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (VIR_REALLOC_N(pool->volumes.objs,
|
|
|
|
pool->volumes.count + 1) < 0) {
|
2010-02-04 18:19:08 +00:00
|
|
|
virReportOOMError();
|
2009-09-08 13:47:45 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
pool->volumes.objs[pool->volumes.count++] = vol;
|
|
|
|
pool->def->capacity += vol->capacity;
|
|
|
|
pool->def->allocation += vol->allocation;
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
|
|
|
|
if (ret != 0)
|
|
|
|
virStorageVolDefFree(vol);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
2011-09-16 12:05:58 +00:00
|
|
|
virStorageBackendIsMultipath(const char *dev_name)
|
2009-09-08 13:47:45 +00:00
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
struct dm_task *dmt = NULL;
|
|
|
|
void *next = NULL;
|
|
|
|
uint64_t start, length;
|
|
|
|
char *target_type = NULL;
|
|
|
|
char *params = NULL;
|
|
|
|
|
|
|
|
dmt = dm_task_create(DM_DEVICE_TABLE);
|
|
|
|
if (dmt == NULL) {
|
|
|
|
ret = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2011-09-16 12:05:58 +00:00
|
|
|
if (dm_task_set_name(dmt, dev_name) == 0) {
|
2009-09-08 13:47:45 +00:00
|
|
|
ret = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
dm_task_no_open_count(dmt);
|
|
|
|
|
|
|
|
if (!dm_task_run(dmt)) {
|
|
|
|
ret = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2010-02-11 14:26:37 +00:00
|
|
|
dm_get_next_target(dmt, next, &start, &length, &target_type, ¶ms);
|
2009-09-08 13:47:45 +00:00
|
|
|
|
|
|
|
if (target_type == NULL) {
|
|
|
|
ret = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (STREQ(target_type, "multipath")) {
|
|
|
|
ret = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
if (dmt != NULL) {
|
|
|
|
dm_task_destroy(dmt);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
2011-09-16 12:05:58 +00:00
|
|
|
virStorageBackendGetMinorNumber(const char *dev_name, uint32_t *minor)
|
2009-09-08 13:47:45 +00:00
|
|
|
{
|
2009-09-08 15:32:57 +00:00
|
|
|
int ret = -1;
|
|
|
|
struct dm_task *dmt;
|
|
|
|
struct dm_info info;
|
2009-09-08 13:47:45 +00:00
|
|
|
|
2009-09-08 15:32:57 +00:00
|
|
|
if (!(dmt = dm_task_create(DM_DEVICE_INFO))) {
|
|
|
|
goto out;
|
|
|
|
}
|
2009-09-08 13:47:45 +00:00
|
|
|
|
2011-09-16 12:05:58 +00:00
|
|
|
if (!dm_task_set_name(dmt, dev_name)) {
|
2009-09-08 15:32:57 +00:00
|
|
|
goto out;
|
|
|
|
}
|
2009-09-08 13:47:45 +00:00
|
|
|
|
2009-09-08 15:32:57 +00:00
|
|
|
if (!dm_task_run(dmt)) {
|
|
|
|
goto out;
|
|
|
|
}
|
2009-09-08 13:47:45 +00:00
|
|
|
|
2009-09-08 15:32:57 +00:00
|
|
|
if (!dm_task_get_info(dmt, &info)) {
|
|
|
|
goto out;
|
|
|
|
}
|
2009-09-08 13:47:45 +00:00
|
|
|
|
2009-09-08 15:32:57 +00:00
|
|
|
*minor = info.minor;
|
|
|
|
ret = 0;
|
2009-09-08 13:47:45 +00:00
|
|
|
|
|
|
|
out:
|
2009-09-08 15:32:57 +00:00
|
|
|
if (dmt != NULL)
|
|
|
|
dm_task_destroy(dmt);
|
2009-09-08 13:47:45 +00:00
|
|
|
|
2009-09-08 15:32:57 +00:00
|
|
|
return ret;
|
2009-09-08 13:47:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
2010-02-10 11:42:56 +00:00
|
|
|
virStorageBackendCreateVols(virStoragePoolObjPtr pool,
|
2009-09-08 13:47:45 +00:00
|
|
|
struct dm_names *names)
|
|
|
|
{
|
2010-05-20 18:16:54 +00:00
|
|
|
int retval = -1, is_mpath = 0;
|
2009-09-08 13:47:45 +00:00
|
|
|
char *map_device = NULL;
|
|
|
|
uint32_t minor = -1;
|
2011-02-15 02:12:24 +00:00
|
|
|
uint32_t next;
|
2009-09-08 13:47:45 +00:00
|
|
|
|
|
|
|
do {
|
|
|
|
is_mpath = virStorageBackendIsMultipath(names->name);
|
|
|
|
|
|
|
|
if (is_mpath < 0) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is_mpath == 1) {
|
|
|
|
|
|
|
|
if (virAsprintf(&map_device, "mapper/%s", names->name) < 0) {
|
2010-02-04 18:19:08 +00:00
|
|
|
virReportOOMError();
|
2009-09-08 13:47:45 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virStorageBackendGetMinorNumber(names->name, &minor) < 0) {
|
2012-07-18 11:38:29 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Failed to get %s minor number"),
|
|
|
|
names->name);
|
2009-09-08 13:47:45 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2010-05-20 18:16:54 +00:00
|
|
|
if (virStorageBackendMpathNewVol(pool, minor, map_device) < 0) {
|
|
|
|
goto out;
|
|
|
|
}
|
2009-09-08 13:47:45 +00:00
|
|
|
|
|
|
|
VIR_FREE(map_device);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Given the way libdevmapper returns its data, I don't see
|
|
|
|
* any way to avoid this series of casts. */
|
2011-02-15 02:12:24 +00:00
|
|
|
next = names->next;
|
|
|
|
names = (struct dm_names *)(((char *)names) + next);
|
2009-09-08 13:47:45 +00:00
|
|
|
|
2011-02-15 02:12:24 +00:00
|
|
|
} while (next);
|
2009-09-08 13:47:45 +00:00
|
|
|
|
2010-05-20 18:16:54 +00:00
|
|
|
retval = 0;
|
2009-09-08 13:47:45 +00:00
|
|
|
out:
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
2010-02-10 11:42:56 +00:00
|
|
|
virStorageBackendGetMaps(virStoragePoolObjPtr pool)
|
2009-09-08 13:47:45 +00:00
|
|
|
{
|
|
|
|
int retval = 0;
|
|
|
|
struct dm_task *dmt = NULL;
|
|
|
|
struct dm_names *names = NULL;
|
|
|
|
|
|
|
|
if (!(dmt = dm_task_create(DM_DEVICE_LIST))) {
|
|
|
|
retval = 1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
dm_task_no_open_count(dmt);
|
|
|
|
|
|
|
|
if (!dm_task_run(dmt)) {
|
|
|
|
retval = 1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(names = dm_task_get_names(dmt))) {
|
|
|
|
retval = 1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!names->dev) {
|
|
|
|
/* No devices found */
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2010-02-10 11:42:56 +00:00
|
|
|
virStorageBackendCreateVols(pool, names);
|
2009-09-08 13:47:45 +00:00
|
|
|
|
|
|
|
out:
|
|
|
|
if (dmt != NULL) {
|
2012-10-17 09:23:12 +00:00
|
|
|
dm_task_destroy(dmt);
|
2009-09-08 13:47:45 +00:00
|
|
|
}
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2010-11-11 20:09:20 +00:00
|
|
|
static int
|
|
|
|
virStorageBackendMpathCheckPool(virConnectPtr conn ATTRIBUTE_UNUSED,
|
|
|
|
virStoragePoolObjPtr pool ATTRIBUTE_UNUSED,
|
|
|
|
bool *isActive)
|
|
|
|
{
|
|
|
|
const char *path = "/dev/mpath";
|
|
|
|
|
|
|
|
*isActive = false;
|
|
|
|
|
|
|
|
if (access(path, F_OK) == 0)
|
|
|
|
*isActive = true;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-09-08 13:47:45 +00:00
|
|
|
|
|
|
|
static int
|
2010-02-10 11:42:56 +00:00
|
|
|
virStorageBackendMpathRefreshPool(virConnectPtr conn ATTRIBUTE_UNUSED,
|
2009-09-08 13:47:45 +00:00
|
|
|
virStoragePoolObjPtr pool)
|
|
|
|
{
|
|
|
|
int retval = 0;
|
|
|
|
|
2011-02-22 02:10:31 +00:00
|
|
|
VIR_DEBUG("conn=%p, pool=%p", conn, pool);
|
2009-09-08 13:47:45 +00:00
|
|
|
|
|
|
|
pool->def->allocation = pool->def->capacity = pool->def->available = 0;
|
|
|
|
|
2010-02-04 22:41:52 +00:00
|
|
|
virFileWaitForDevices();
|
2009-09-08 13:47:45 +00:00
|
|
|
|
2010-02-10 11:42:56 +00:00
|
|
|
virStorageBackendGetMaps(pool);
|
2009-09-08 13:47:45 +00:00
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
virStorageBackend virStorageBackendMpath = {
|
|
|
|
.type = VIR_STORAGE_POOL_MPATH,
|
|
|
|
|
2010-11-11 20:09:20 +00:00
|
|
|
.checkPool = virStorageBackendMpathCheckPool,
|
2009-09-08 13:47:45 +00:00
|
|
|
.refreshPool = virStorageBackendMpathRefreshPool,
|
|
|
|
};
|