mirror of
git://sourceware.org/git/lvm2.git
synced 2024-12-21 13:34:40 +03:00
c0e0f5a923
We already do check for suspended devs within udev rules where the pvscan is to update lvmetad. So the check for suspended devs in "pre-lvmetad" chain is not useful here - remove it - it may be a source of hardly to detect races anyway (if udev rule detects the device is not suspended and then the pvscan instance sees the dev as suspended, we may end up not reacting to the event properly).
196 lines
4.7 KiB
C
196 lines
4.7 KiB
C
/*
|
|
* Copyright (C) 2014 Red Hat, Inc. All rights reserved.
|
|
*
|
|
* This file is part of LVM2.
|
|
*
|
|
* This copyrighted material is made available to anyone wishing to use,
|
|
* modify, copy, or redistribute it subject to the terms and conditions
|
|
* of the GNU Lesser General Public License v.2.1.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include "lib.h"
|
|
#include "filter.h"
|
|
#include "activate.h" /* device_is_usable */
|
|
#ifdef UDEV_SYNC_SUPPORT
|
|
#include <libudev.h>
|
|
#include "dev-ext-udev-constants.h"
|
|
#endif
|
|
|
|
static const char *_too_small_to_hold_pv_msg = "Too small to hold a PV";
|
|
|
|
static int _native_check_pv_min_size(struct device *dev)
|
|
{
|
|
uint64_t size;
|
|
int ret = 0;
|
|
|
|
/* Check it's accessible */
|
|
if (!dev_open_readonly_quiet(dev)) {
|
|
log_debug_devs("%s: Skipping: open failed", dev_name(dev));
|
|
return 0;
|
|
}
|
|
|
|
/* Check it's not too small */
|
|
if (!dev_get_size(dev, &size)) {
|
|
log_debug_devs("%s: Skipping: dev_get_size failed", dev_name(dev));
|
|
goto out;
|
|
}
|
|
|
|
if (size < pv_min_size()) {
|
|
log_debug_devs("%s: Skipping: %s", dev_name(dev),
|
|
_too_small_to_hold_pv_msg);
|
|
goto out;
|
|
}
|
|
|
|
ret = 1;
|
|
out:
|
|
if (!dev_close(dev))
|
|
stack;
|
|
|
|
return ret;
|
|
}
|
|
|
|
#ifdef UDEV_SYNC_SUPPORT
|
|
static int _udev_check_pv_min_size(struct device *dev)
|
|
{
|
|
struct dev_ext *ext;
|
|
const char *size_str;
|
|
char *endp;
|
|
uint64_t size;
|
|
|
|
if (!(ext = dev_ext_get(dev)))
|
|
return_0;
|
|
|
|
if (!(size_str = udev_device_get_sysattr_value((struct udev_device *)ext->handle, DEV_EXT_UDEV_SYSFS_ATTR_SIZE))) {
|
|
log_debug_devs("%s: Skipping: failed to get size from sysfs [%s:%p]",
|
|
dev_name(dev), dev_ext_name(dev), dev->ext.handle);
|
|
return 0;
|
|
}
|
|
|
|
errno = 0;
|
|
size = strtoull(size_str, &endp, 10);
|
|
if (errno || !endp || *endp) {
|
|
log_debug_devs("%s: Skipping: failed to parse size from sysfs [%s:%p]",
|
|
dev_name(dev), dev_ext_name(dev), dev->ext.handle);
|
|
return 0;
|
|
}
|
|
|
|
if (size < pv_min_size()) {
|
|
log_debug_devs("%s: Skipping: %s [%s:%p]", dev_name(dev),
|
|
_too_small_to_hold_pv_msg,
|
|
dev_ext_name(dev), dev->ext.handle);
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
#else
|
|
static int _udev_check_pv_min_size(struct device *dev)
|
|
{
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
static int _check_pv_min_size(struct device *dev)
|
|
{
|
|
if (dev->ext.src == DEV_EXT_NONE)
|
|
return _native_check_pv_min_size(dev);
|
|
|
|
if (dev->ext.src == DEV_EXT_UDEV)
|
|
return _udev_check_pv_min_size(dev);
|
|
|
|
log_error(INTERNAL_ERROR "Missing hook for PV min size check "
|
|
"using external device info source %s", dev_ext_name(dev));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int _passes_usable_filter(struct dev_filter *f, struct device *dev)
|
|
{
|
|
filter_mode_t mode = *((filter_mode_t *) f->private);
|
|
struct dev_usable_check_params ucp = {0};
|
|
int r = 1;
|
|
|
|
/* further checks are done on dm devices only */
|
|
if (dm_is_dm_major(MAJOR(dev->dev))) {
|
|
switch (mode) {
|
|
case FILTER_MODE_NO_LVMETAD:
|
|
ucp.check_empty = 1;
|
|
ucp.check_blocked = 1;
|
|
ucp.check_suspended = ignore_suspended_devices();
|
|
ucp.check_error_target = 1;
|
|
ucp.check_reserved = 1;
|
|
break;
|
|
case FILTER_MODE_PRE_LVMETAD:
|
|
ucp.check_empty = 1;
|
|
ucp.check_blocked = 1;
|
|
ucp.check_suspended = 0;
|
|
ucp.check_error_target = 1;
|
|
ucp.check_reserved = 1;
|
|
break;
|
|
case FILTER_MODE_POST_LVMETAD:
|
|
ucp.check_empty = 0;
|
|
ucp.check_blocked = 1;
|
|
ucp.check_suspended = ignore_suspended_devices();
|
|
ucp.check_error_target = 0;
|
|
ucp.check_reserved = 0;
|
|
break;
|
|
}
|
|
|
|
if (!(r = device_is_usable(dev, ucp)))
|
|
log_debug_devs("%s: Skipping unusable device.", dev_name(dev));
|
|
}
|
|
|
|
if (r) {
|
|
/* check if the device is not too small to hold a PV */
|
|
switch (mode) {
|
|
case FILTER_MODE_NO_LVMETAD:
|
|
/* fall through */
|
|
case FILTER_MODE_PRE_LVMETAD:
|
|
r = _check_pv_min_size(dev);
|
|
break;
|
|
case FILTER_MODE_POST_LVMETAD:
|
|
/* nothing to do here */
|
|
break;
|
|
}
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
static void _usable_filter_destroy(struct dev_filter *f)
|
|
{
|
|
if (f->use_count)
|
|
log_error(INTERNAL_ERROR "Destroying usable device filter while in use %u times.", f->use_count);
|
|
|
|
dm_free(f->private);
|
|
dm_free(f);
|
|
}
|
|
|
|
struct dev_filter *usable_filter_create(struct dev_types *dt __attribute__((unused)), filter_mode_t mode)
|
|
{
|
|
struct dev_filter *f;
|
|
|
|
if (!(f = dm_zalloc(sizeof(struct dev_filter)))) {
|
|
log_error("Usable device filter allocation failed");
|
|
return NULL;
|
|
}
|
|
|
|
f->passes_filter = _passes_usable_filter;
|
|
f->destroy = _usable_filter_destroy;
|
|
f->use_count = 0;
|
|
if (!(f->private = dm_zalloc(sizeof(filter_mode_t)))) {
|
|
log_error("Usable device filter mode allocation failed");
|
|
dm_free(f);
|
|
return NULL;
|
|
}
|
|
*((filter_mode_t *) f->private) = mode;
|
|
|
|
log_debug_devs("Usable device filter initialised.");
|
|
|
|
return f;
|
|
}
|