1
0
mirror of git://sourceware.org/git/lvm2.git synced 2025-03-10 16:58:47 +03:00

dev-cache: switch use of btree to radix_tree

Use radix_tree instead of not so well efficient btree code.

This change allows to use more efficient  'radix_tree_iterate()',
so just easily visit every stored value within the tree.

To allow simple transtion, still provide  'dev_iter()' functionality
so not every code needs immediate rewrite and can use the old iteration
(with extra allocated memory for every tree value)
This commit is contained in:
Zdenek Kabelac 2024-06-01 00:42:33 +02:00
parent 6e6d4c62b3
commit b5245d3bcc
5 changed files with 530 additions and 366 deletions

View File

@ -14,10 +14,10 @@
*/
#include "base/memory/zalloc.h"
#include "base/data-struct/radix-tree.h"
#include "lib/misc/lib.h"
#include "lib/device/dev-type.h"
#include "lib/device/device_id.h"
#include "lib/datastruct/btree.h"
#include "lib/config/config.h"
#include "lib/commands/toolcontext.h"
#include "device_mapper/misc/dm-ioctl.h"
@ -35,8 +35,10 @@
#include <sys/file.h>
struct dev_iter {
struct btree_iter *current;
union radix_value *values;
struct dev_filter *filter;
unsigned nr_values;
unsigned pos;
};
struct dir_list {
@ -49,8 +51,8 @@ static struct {
struct dm_hash_table *names;
struct dm_hash_table *vgid_index;
struct dm_hash_table *lvid_index;
struct btree *sysfs_only_devices; /* see comments in _get_device_for_sysfs_dev_name_using_devno */
struct btree *devices;
struct radix_tree *sysfs_only_devices; /* see comments in _get_device_for_sysfs_dev_name_using_devno */
struct radix_tree *devices;
struct dm_regex *preferred_names_matcher;
const char *dev_dir;
@ -476,6 +478,31 @@ static struct dm_list *_get_or_add_list_by_index_key(struct dm_hash_table *idx,
return list;
}
void dev_cache_iterate(struct radix_tree_iterator *it)
{
radix_tree_iterate(_cache.devices, NULL, 0, it);
}
static uint32_t _shuffle_devno(dev_t d)
{
return (d & 0xff000000) >> 24 | (d & 0xffff00) | ((d & 0xff) << 24);
}
static bool _dev_cache_insert_devno(struct radix_tree *rt, dev_t devno, void *dev)
{
union radix_value v = { .ptr = dev };
uint32_t key = _shuffle_devno(devno);
return radix_tree_insert(rt, &key, sizeof(key), v);
}
static struct device *_dev_cache_lookup_devno(struct radix_tree *rt, dev_t devno)
{
uint32_t key = _shuffle_devno(devno);
return radix_tree_lookup_ptr(rt, &key, sizeof(key));
}
static struct device *_insert_sysfs_dev(dev_t devno, const char *devname)
{
static struct device _fake_dev = { .flags = DEV_USED_FOR_LV };
@ -503,7 +530,7 @@ static struct device *_insert_sysfs_dev(dev_t devno, const char *devname)
return_NULL;
}
if (!btree_insert(_cache.sysfs_only_devices, (uint32_t) devno, dev)) {
if (!_dev_cache_insert_devno(_cache.sysfs_only_devices, devno, dev)) {
log_error("Couldn't add device to binary tree of sysfs-only devices in dev cache.");
_free(dev);
return NULL;
@ -534,7 +561,7 @@ static struct device *_get_device_for_sysfs_dev_name_using_devno(const char *dev
}
devno = MKDEV(major, minor);
if (!(dev = (struct device *) btree_lookup(_cache.devices, (uint32_t) devno))) {
if (!(dev = _dev_cache_lookup_devno(_cache.devices, devno))) {
/*
* If we get here, it means the device is referenced in sysfs, but it's not yet in /dev.
* This may happen in some rare cases right after LVs get created - we sync with udev
@ -546,7 +573,7 @@ static struct device *_get_device_for_sysfs_dev_name_using_devno(const char *dev
* where different directory for dev nodes is used (e.g. our test suite). So track
* such devices in _cache.sysfs_only_devices hash for the vgid/lvid check to work still.
*/
if (!(dev = (struct device *) btree_lookup(_cache.sysfs_only_devices, (uint32_t) devno)) &&
if (!(dev = _dev_cache_lookup_devno(_cache.sysfs_only_devices, devno)) &&
!(dev = _insert_sysfs_dev(devno, devname)))
return_NULL;
}
@ -744,7 +771,7 @@ static int _insert_dev(const char *path, dev_t d)
struct device *dev_by_devt;
struct device *dev_by_path;
dev_by_devt = (struct device *) btree_lookup(_cache.devices, (uint32_t) d);
dev_by_devt = _dev_cache_lookup_devno(_cache.devices, d);
dev_by_path = (struct device *) dm_hash_lookup(_cache.names, path);
dev = dev_by_devt;
@ -762,14 +789,12 @@ static int _insert_dev(const char *path, dev_t d)
*/
if (!dev_by_devt && !dev_by_path) {
log_debug_devs("Found dev %u:%u %s - new.", MAJOR(d), MINOR(d), path);
if (!(dev = (struct device *) btree_lookup(_cache.sysfs_only_devices, (uint32_t) d))) {
if (!(dev = _dev_cache_lookup_devno(_cache.sysfs_only_devices, d)))
/* create new device */
if (!(dev = _dev_create(d)))
return_0;
}
if (!(btree_insert(_cache.devices, (uint32_t) d, dev))) {
if (!(_dev_cache_insert_devno(_cache.devices, d, dev))) {
log_error("Couldn't insert device into binary tree.");
_free(dev);
return 0;
@ -802,13 +827,13 @@ static int _insert_dev(const char *path, dev_t d)
MAJOR(d), MINOR(d), path,
MAJOR(dev_by_path->dev), MINOR(dev_by_path->dev));
if (!(dev = (struct device *) btree_lookup(_cache.sysfs_only_devices, (uint32_t) d))) {
if (!(dev = _dev_cache_lookup_devno(_cache.sysfs_only_devices, d))) {
/* create new device */
if (!(dev = _dev_create(d)))
return_0;
}
if (!(btree_insert(_cache.devices, (uint32_t) d, dev))) {
if (!(_dev_cache_insert_devno(_cache.devices, d, dev))) {
log_error("Couldn't insert device into binary tree.");
_free(dev);
return 0;
@ -927,22 +952,40 @@ static int _insert_dir(const char *dir)
return r;
}
/* Sharing 'struct visitor' for several radix tree iterators
* and just adding/using members as needed. */
struct visitor {
struct radix_tree_iterator it;
struct cmd_context *cmd;
struct dev_types *dt;
struct device *dev;
const char *pvid;
unsigned found;
};
static bool _visit_dev_cache_iterate_devs_for_index(struct radix_tree_iterator *it,
const void *key, size_t keylen,
union radix_value v)
{
struct visitor *vt = container_of(it, struct visitor, it);
struct device *dev = v.ptr;
if (!_index_dev_by_vgid_and_lvid(vt->cmd, dev))
vt->found = 1;
return true;
}
static int _dev_cache_iterate_devs_for_index(struct cmd_context *cmd)
{
struct btree_iter *iter = btree_first(_cache.devices);
struct device *dev;
int r = 1;
struct visitor vt = {
.it.visit = _visit_dev_cache_iterate_devs_for_index,
.cmd = cmd,
};
while (iter) {
dev = btree_get_data(iter);
dev_cache_iterate(&vt.it);
if (!_index_dev_by_vgid_and_lvid(cmd, dev))
r = 0;
iter = btree_next(iter);
}
return r;
return !vt.found;
}
static int _dev_cache_iterate_sysfs_for_index(struct cmd_context *cmd, const char *path)
@ -974,8 +1017,8 @@ static int _dev_cache_iterate_sysfs_for_index(struct cmd_context *cmd, const cha
}
devno = MKDEV(major, minor);
if (!(dev = (struct device *) btree_lookup(_cache.devices, (uint32_t) devno)) &&
!(dev = (struct device *) btree_lookup(_cache.sysfs_only_devices, (uint32_t) devno))) {
if (!(dev = _dev_cache_lookup_devno(_cache.devices, devno)) &&
!(dev = _dev_cache_lookup_devno(_cache.sysfs_only_devices, devno))) {
if (!dm_device_get_name(major, minor, 1, devname, sizeof(devname)) ||
!(dev = _insert_sysfs_dev(devno, devname))) {
partial_failure = 1;
@ -1318,12 +1361,12 @@ int dev_cache_init(struct cmd_context *cmd)
return_0;
}
if (!(_cache.devices = btree_create(_cache.mem))) {
if (!(_cache.devices = radix_tree_create(NULL, NULL))) {
log_error("Couldn't create binary tree for dev-cache.");
goto bad;
}
if (!(_cache.sysfs_only_devices = btree_create(_cache.mem))) {
if (!(_cache.sysfs_only_devices = radix_tree_create(NULL, NULL))) {
log_error("Couldn't create binary tree for sysfs-only devices in dev cache.");
goto bad;
}
@ -1406,6 +1449,12 @@ int dev_cache_exit(void)
if (_cache.lvid_index)
dm_hash_destroy(_cache.lvid_index);
if (_cache.devices)
radix_tree_destroy(_cache.devices);
if (_cache.sysfs_only_devices)
radix_tree_destroy(_cache.sysfs_only_devices);
memset(&_cache, 0, sizeof(_cache));
return (!num_open);
@ -1537,7 +1586,7 @@ static struct device *_dev_cache_get(struct cmd_context *cmd, const char *name,
* Remove incorrect info and then add new dev-cache entry.
*/
if (dev && (st.st_rdev != dev->dev)) {
struct device *dev_by_devt = (struct device *) btree_lookup(_cache.devices, (uint32_t) st.st_rdev);
struct device *dev_by_devt = _dev_cache_lookup_devno(_cache.devices, st.st_rdev);
/*
* lvm commands create this condition when they
@ -1623,7 +1672,8 @@ static struct device *_dev_cache_get(struct cmd_context *cmd, const char *name,
* Without dropping the aliases, it's plausible that lvm commands
* could end up using the wrong dm device.
*/
struct device *dev_by_devt = (struct device *) btree_lookup(_cache.devices, (uint32_t) st.st_rdev);
struct device *dev_by_devt = _dev_cache_lookup_devno(_cache.devices, st.st_rdev);
if (dev_by_devt) {
log_debug("Dropping aliases for %u:%u before adding new path %s.",
MAJOR(st.st_rdev), MINOR(st.st_rdev), name);
@ -1683,7 +1733,7 @@ struct device *dev_cache_get(struct cmd_context *cmd, const char *name, struct d
struct device *dev_cache_get_by_devt(struct cmd_context *cmd, dev_t devt)
{
struct device *dev = (struct device *) btree_lookup(_cache.devices, (uint32_t) devt);
struct device *dev = _dev_cache_lookup_devno(_cache.devices, devt);
if (dev)
return dev;
@ -1691,21 +1741,32 @@ struct device *dev_cache_get_by_devt(struct cmd_context *cmd, dev_t devt)
return NULL;
}
static bool _visit_dev_cache_get_by_devt(struct radix_tree_iterator *it,
const void *key, size_t keylen,
union radix_value v)
{
struct visitor *vt = container_of(it, struct visitor, it);
struct device *dev = v.ptr;
if (memcmp(dev->pvid, vt->pvid, ID_LEN))
return true;
vt->dev = dev;
return false; /* found -> stop iterator */
}
struct device *dev_cache_get_by_pvid(struct cmd_context *cmd, const char *pvid)
{
struct btree_iter *iter = btree_first(_cache.devices);
struct device *dev;
struct visitor vt = {
.it.visit = _visit_dev_cache_get_by_devt,
.cmd = cmd,
.pvid = pvid,
};
while (iter) {
dev = btree_get_data(iter);
dev_cache_iterate(&vt.it);
if (!memcmp(dev->pvid, pvid, ID_LEN))
return dev;
iter = btree_next(iter);
}
return NULL;
return vt.dev;
}
struct dev_iter *dev_iter_create(struct dev_filter *f, int unused)
@ -1713,11 +1774,18 @@ struct dev_iter *dev_iter_create(struct dev_filter *f, int unused)
struct dev_iter *di = malloc(sizeof(*di));
if (!di) {
log_error("dev_iter allocation failed");
log_error("dev_iter allocation failed.");
return NULL;
}
di->current = btree_first(_cache.devices);
if (!radix_tree_values(_cache.devices, NULL, 0,
&di->values, &di->nr_values)) {
log_error("dev_iter values allocation failed.");
free(di);
return NULL;
}
di->pos = 0;
di->filter = f;
if (di->filter)
di->filter->use_count++;
@ -1729,23 +1797,18 @@ void dev_iter_destroy(struct dev_iter *iter)
{
if (iter->filter)
iter->filter->use_count--;
free(iter->values);
free(iter);
}
static struct device *_iter_next(struct dev_iter *iter)
{
struct device *d = btree_get_data(iter->current);
iter->current = btree_next(iter->current);
return d;
}
struct device *dev_iter_get(struct cmd_context *cmd, struct dev_iter *iter)
{
struct dev_filter *f;
struct device *d;
int ret;
while (iter->current) {
struct device *d = _iter_next(iter);
while (iter->pos < iter->nr_values) {
d = iter->values[iter->pos++].ptr;
ret = 1;
f = iter->filter;
@ -1773,21 +1836,31 @@ const char *dev_name(const struct device *dev)
return unknown_device_name();
}
bool dev_cache_has_md_with_end_superblock(struct dev_types *dt)
static bool _dev_cache_has_md_with_end_superblock_it(struct radix_tree_iterator *it,
const void *key, size_t keylen,
union radix_value v)
{
struct btree_iter *iter = btree_first(_cache.devices);
struct device *dev;
struct visitor *vt = container_of(it, struct visitor, it);
struct device *dev = v.ptr;
while (iter) {
dev = btree_get_data(iter);
if (dev_is_md_with_end_superblock(dt, dev))
return true;
iter = btree_next(iter);
if (dev_is_md_with_end_superblock(vt->dt, dev)) {
vt->found = 1;
return false; /* stop further search */
}
return false;
return true;
}
bool dev_cache_has_md_with_end_superblock(struct dev_types *dt)
{
struct visitor vt = {
.it.visit = _dev_cache_has_md_with_end_superblock_it,
.dt = dt,
};
dev_cache_iterate(&vt.it);
return vt.found;
}
static int _setup_devices_list(struct cmd_context *cmd)

View File

@ -69,6 +69,8 @@ struct dev_iter;
struct dev_iter *dev_iter_create(struct dev_filter *f, int unused);
void dev_iter_destroy(struct dev_iter *iter);
struct device *dev_iter_get(struct cmd_context *cmd, struct dev_iter *iter);
struct radix_tree_iterator;
void dev_cache_iterate(struct radix_tree_iterator *it);
void dev_cache_failed_path(struct device *dev, const char *path);

View File

@ -13,6 +13,7 @@
*/
#include "base/memory/zalloc.h"
#include "base/data-struct/radix-tree.h"
#include "lib/misc/lib.h"
#include "lib/commands/toolcontext.h"
#include "lib/device/device.h"
@ -2663,12 +2664,41 @@ void device_ids_match_device_list(struct cmd_context *cmd)
}
}
struct visitor {
struct radix_tree_iterator it;
struct cmd_context *cmd;
struct dev_use *du;
struct dm_list *serial_str_list;
struct dm_list *devs;
unsigned count;
uint32_t hash;
};
static bool _visit_device_ids_match(struct radix_tree_iterator *it,
const void *key, size_t keylen,
union radix_value v)
{
struct visitor *vt = container_of(it, struct visitor, it);
struct device *dev = v.ptr;
/* skip a dev that's already matched to another entry */
if (dev->flags & DEV_MATCHED_USE_ID ||
!_match_du_to_dev(vt->cmd, vt->du, dev))
return true; /* search next */
vt->count = 1;
return false; /* stop search */
}
void device_ids_match(struct cmd_context *cmd)
{
struct dev_iter *iter;
struct visitor vt = {
.it.visit = _visit_device_ids_match,
.cmd = cmd,
};
struct dev_use *du;
struct device *dev;
int found;
if (cmd->enable_devices_list) {
device_ids_match_device_list(cmd);
@ -2736,26 +2766,16 @@ void device_ids_match(struct cmd_context *cmd)
* NULL filter is used because we are just setting up the
* the du/dev pairs in preparation for using the filters.
*/
found = 0;
vt.du = du;
vt.count = 0;
dev_cache_iterate(&vt.it);
if (!(iter = dev_iter_create(NULL, 0)))
continue;
while ((dev = dev_iter_get(cmd, iter))) {
/* skip a dev that's already matched to another entry */
if (dev->flags & DEV_MATCHED_USE_ID)
continue;
if (_match_du_to_dev(cmd, du, dev)) {
log_debug("Match %s %s PVID %s: done %s",
idtype_to_str(du->idtype), du->idname ?: ".", du->pvid ?: ".",
dev_name(du->dev));
found = 1;
break;
}
}
dev_iter_destroy(iter);
if (!found)
log_debug("Match %s %s PVID %s: no device matches",
if (vt.count)
log_debug("Match %s %s PVID %s: done %s.",
idtype_to_str(du->idtype), du->idname ?: ".", du->pvid ?: ".",
dev_name(du->dev));
else
log_debug("Match %s %s PVID %s: no device matches.",
idtype_to_str(du->idtype), du->idname ?: ".", du->pvid ?: ".");
}
@ -2847,61 +2867,56 @@ void device_ids_match(struct cmd_context *cmd)
}
}
static void _get_devs_with_serial_numbers(struct cmd_context *cmd, struct dm_list *serial_str_list, struct dm_list *devs)
static bool _visit_get_devs_with_serial_numbers(struct radix_tree_iterator *it,
const void *key, size_t keylen,
union radix_value v)
{
struct dev_iter *iter;
struct device *dev;
struct visitor *vt = container_of(it, struct visitor, it);
struct cmd_context *cmd = vt->cmd;
struct device *dev = v.ptr;
struct device_list *devl;
struct dev_id *id;
const char *idname;
struct dev_filter *f = cmd->filter;
if (!(iter = dev_iter_create(NULL, 0)))
return;
while ((dev = dev_iter_get(cmd, iter))) {
/* if serial has already been read for this dev then use it */
dm_list_iterate_items(id, &dev->ids) {
if (id->idtype == DEV_ID_TYPE_SYS_SERIAL && id->idname) {
if (str_list_match_item(serial_str_list, id->idname)) {
if (!(devl = dm_pool_zalloc(cmd->mem, sizeof(*devl))))
goto next_continue;
devl->dev = dev;
dm_list_add(devs, &devl->list);
}
goto next_continue;
}
}
/* just copying the no-data filters in similar device_ids_search */
if (!cmd->filter->passes_filter(cmd, cmd->filter, dev, "sysfs"))
continue;
if (!cmd->filter->passes_filter(cmd, cmd->filter, dev, "type"))
continue;
if (!cmd->filter->passes_filter(cmd, cmd->filter, dev, "usable"))
continue;
if (!cmd->filter->passes_filter(cmd, cmd->filter, dev, "mpath"))
continue;
if ((idname = device_id_system_read(cmd, dev, DEV_ID_TYPE_SYS_SERIAL))) {
if (str_list_match_item(serial_str_list, idname)) {
/* if serial has already been read for this dev then use it */
dm_list_iterate_items(id, &dev->ids)
if (id->idtype == DEV_ID_TYPE_SYS_SERIAL && id->idname) {
if (str_list_match_item(vt->serial_str_list, id->idname)) {
if (!(devl = dm_pool_zalloc(cmd->mem, sizeof(*devl))))
goto next_free;
if (!(id = zalloc(sizeof(struct dev_id))))
goto next_free;
id->idtype = DEV_ID_TYPE_SYS_SERIAL;
id->idname = (char *)idname;
dm_list_add(&dev->ids, &id->list);
return true;
devl->dev = dev;
dm_list_add(devs, &devl->list);
idname = NULL;
dm_list_add(vt->devs, &devl->list);
}
return true;
}
next_free:
if (idname)
free((char *)idname);
next_continue:
continue;
/* just copying the no-data filters in similar device_ids_search */
if (!f->passes_filter(cmd, f, dev, "sysfs") ||
!f->passes_filter(cmd, f, dev, "type") ||
!f->passes_filter(cmd, f, dev, "usable") ||
!f->passes_filter(cmd, f, dev, "mpath"))
return true;
if ((idname = device_id_system_read(cmd, dev, DEV_ID_TYPE_SYS_SERIAL)) &&
str_list_match_item(vt->serial_str_list, idname)) {
if (!(devl = dm_pool_zalloc(cmd->mem, sizeof(*devl))))
goto next_free;
if (!(id = zalloc(sizeof(struct dev_id))))
goto next_free;
id->idtype = DEV_ID_TYPE_SYS_SERIAL;
id->idname = (char *)idname;
dm_list_add(&dev->ids, &id->list);
devl->dev = dev;
dm_list_add(vt->devs, &devl->list);
idname = NULL;
}
dev_iter_destroy(iter);
next_free:
if (idname)
free((char *)idname);
return true;
}
/*
@ -3412,6 +3427,12 @@ void device_ids_check_serial(struct cmd_context *cmd, struct dm_list *scan_devs,
int found;
int count;
int err;
struct visitor vt = {
.it.visit = _visit_get_devs_with_serial_numbers,
.cmd = cmd,
.serial_str_list = &cmd->device_ids_check_serial,
.devs = &devs_check,
};
dm_list_init(&dus_check);
dm_list_init(&devs_check);
@ -3440,7 +3461,7 @@ void device_ids_check_serial(struct cmd_context *cmd, struct dm_list *scan_devs,
* devs that match the suspect serial numbers.
*/
log_debug("Finding all devs with suspect serial numbers.");
_get_devs_with_serial_numbers(cmd, &cmd->device_ids_check_serial, &devs_check);
dev_cache_iterate(&vt.it);
/*
* Read the PVID from any devs_check entries that have not been scanned
@ -3656,6 +3677,36 @@ void device_ids_check_serial(struct cmd_context *cmd, struct dm_list *scan_devs,
_device_ids_update_try(cmd);
}
static bool _visit_device_ids_search(struct radix_tree_iterator *it,
const void *key, size_t keylen,
union radix_value v)
{
struct visitor *vt = container_of(it, struct visitor, it);
struct cmd_context *cmd = vt->cmd;
struct device *dev = v.ptr;
struct dev_filter *f = cmd->filter;
struct device_list *devl; /* holds struct device */
if (dev->flags & DEV_MATCHED_USE_ID)
return true;
if (!f->passes_filter(cmd, f, dev, "sysfs") ||
!f->passes_filter(cmd, f, dev, "type") ||
!f->passes_filter(cmd, f, dev, "usable") ||
!f->passes_filter(cmd, f, dev, "mpath"))
return true;
if (!(devl = dm_pool_zalloc(cmd->mem, sizeof(*devl))))
return true;
devl->dev = dev;
dm_list_add(vt->devs, &devl->list);
vt->count++;
vt->hash = calc_crc(vt->hash, (const uint8_t *)&dev->dev, sizeof(dev_t));
return true;
}
/*
* Devices with IDNAME=devname that are mistakenly included by filter-deviceid
* due to a devname change are fully scanned and added to lvmcache.
@ -3683,7 +3734,6 @@ void device_ids_search(struct cmd_context *cmd, struct dm_list *new_devs,
struct device *dev;
struct dev_use *du;
struct dev_id *id;
struct dev_iter *iter;
struct device_list *devl; /* holds struct device */
struct device_id_list *dil, *dil2; /* holds struct device + pvid */
struct dm_list search_pvids; /* list of device_id_list */
@ -3698,7 +3748,14 @@ void device_ids_search(struct cmd_context *cmd, struct dm_list *new_devs,
int search_pvids_count = 0;
int search_devs_count = 0;
uint32_t search_pvids_hash = INITIAL_CRC;
uint32_t search_devs_hash = INITIAL_CRC;
uint32_t search_devs_hash;
struct visitor vt = {
.it.visit = _visit_device_ids_search,
.cmd = cmd,
.serial_str_list = &cmd->device_ids_check_serial,
.devs = &search_devs,
.hash = INITIAL_CRC,
};
dm_list_init(&search_pvids);
dm_list_init(&search_devs);
@ -3794,27 +3851,9 @@ void device_ids_search(struct cmd_context *cmd, struct dm_list *new_devs,
* filter), in the process of doing this search outside the deviceid
* filter.
*/
if (!(iter = dev_iter_create(NULL, 0)))
return;
while ((dev = dev_iter_get(cmd, iter))) {
if (dev->flags & DEV_MATCHED_USE_ID)
continue;
if (!cmd->filter->passes_filter(cmd, cmd->filter, dev, "sysfs"))
continue;
if (!cmd->filter->passes_filter(cmd, cmd->filter, dev, "type"))
continue;
if (!cmd->filter->passes_filter(cmd, cmd->filter, dev, "usable"))
continue;
if (!cmd->filter->passes_filter(cmd, cmd->filter, dev, "mpath"))
continue;
if (!(devl = dm_pool_zalloc(cmd->mem, sizeof(*devl))))
continue;
devl->dev = dev;
dm_list_add(&search_devs, &devl->list);
search_devs_count++;
search_devs_hash = calc_crc(search_devs_hash, (const uint8_t *)&devl->dev->dev, sizeof(dev_t));
}
dev_iter_destroy(iter);
dev_cache_iterate(&vt.it);
search_devs_count = vt.count;
search_devs_hash = vt.hash;
/*
* A previous command searched for devnames and found nothing, so it

View File

@ -137,6 +137,7 @@
#include "lib/misc/lib.h"
#include "base/memory/zalloc.h"
#include "base/data-struct/radix-tree.h"
#include "lib/label/label.h"
#include "lib/misc/crc.h"
#include "lib/cache/lvmcache.h"
@ -454,6 +455,58 @@ static int _dev_in_hint_hash(struct cmd_context *cmd, struct device *dev)
return 1;
}
struct visitor {
struct radix_tree_iterator it;
FILE *fp;
struct cmd_context *cmd;
struct dm_list *hints;
uint32_t hash;
uint32_t count;
int ret;
};
static bool _visit_validate_hints(struct radix_tree_iterator *it,
const void *key, size_t keylen,
union radix_value v)
{
struct visitor *vt = container_of(it, struct visitor, it);
struct device *dev = v.ptr;
struct hint *hint;
if (dm_list_empty(&dev->aliases))
return true;
if (!(hint = _find_hint_name(vt->hints, dev_name(dev))))
return true;
/* The cmd hasn't needed this hint's dev so it's not been scanned. */
if (!hint->chosen)
return true;
/*
* label_scan was unable to read the dev so we don't know its pvid.
* Since we are unable to verify the hint is correct, it's possible
* that the PVID is actually found on a different device, so don't
* depend on hints. (This would also fail the following pvid check.)
*/
if (dev->flags & DEV_SCAN_NOT_READ) {
log_debug("Uncertain hint for unread device %d:%d %s",
major(hint->devt), minor(hint->devt), dev_name(dev));
vt->ret = 0;
return true;
}
if (strcmp(dev->pvid, hint->pvid)) {
log_debug("Invalid hint device %d:%d %s pvid %s had hint pvid %s",
major(hint->devt), minor(hint->devt), dev_name(dev),
dev->pvid, hint->pvid);
vt->ret = 0;
}
vt->count++;
return true;
}
/*
* Hints were used to reduce devs that were scanned. After the reduced
* scanning is done, this is called to check if the hints may have been
@ -465,9 +518,12 @@ static int _dev_in_hint_hash(struct cmd_context *cmd, struct device *dev)
*/
int validate_hints(struct cmd_context *cmd, struct dm_list *hints)
{
struct visitor vt = {
.it.visit = _visit_validate_hints,
.cmd = cmd,
.hints = hints,
};
struct hint *hint;
struct dev_iter *iter;
struct device *dev;
int valid_hints = 0;
int ret = 1;
@ -499,41 +555,10 @@ int validate_hints(struct cmd_context *cmd, struct dm_list *hints)
* became stale somehow (e.g. manually copying devices with dd) and
* need to be refreshed.
*/
if (!(iter = dev_iter_create(NULL, 0)))
return 0;
while ((dev = dev_iter_get(cmd, iter))) {
if (dm_list_empty(&dev->aliases))
continue;
if (!(hint = _find_hint_name(hints, dev_name(dev))))
continue;
/* The cmd hasn't needed this hint's dev so it's not been scanned. */
if (!hint->chosen)
continue;
/*
* label_scan was unable to read the dev so we don't know its pvid.
* Since we are unable to verify the hint is correct, it's possible
* that the PVID is actually found on a different device, so don't
* depend on hints. (This would also fail the following pvid check.)
*/
if (dev->flags & DEV_SCAN_NOT_READ) {
log_debug("Uncertain hint for unread device %d:%d %s",
major(hint->devt), minor(hint->devt), dev_name(dev));
ret = 0;
continue;
}
if (strcmp(dev->pvid, hint->pvid)) {
log_debug("Invalid hint device %d:%d %s pvid %s had hint pvid %s",
major(hint->devt), minor(hint->devt), dev_name(dev),
dev->pvid, hint->pvid);
ret = 0;
}
valid_hints++;
}
dev_iter_destroy(iter);
vt.ret = ret;
dev_cache_iterate(&vt.it);
ret = vt.ret;
valid_hints = vt.count;
/*
* Check in lvmcache to see if the scan noticed any missing PVs
@ -682,6 +707,33 @@ static void _filter_to_str(struct cmd_context *cmd, int filter_cfg, char **strp)
*strp = str;
}
static bool _visit_read_hint_file(struct radix_tree_iterator *it,
const void *key, size_t keylen,
union radix_value v)
{
struct visitor *vt = container_of(it, struct visitor, it);
struct device *dev = v.ptr;
const char *devpath;
/*
* This loop does two different things (for clarity this should be
* two separate dev_iter loops, but one is used for efficiency).
* 1. compute the hint hash from all relevant devs
* 2. add PVs to the hint file
*/
if (vt->cmd->enable_devices_file && !get_du_for_dev(vt->cmd, dev))
return true;
if (!_dev_in_hint_hash(vt->cmd, dev))
return true;
devpath = dev_name(dev);
vt->hash = calc_crc(vt->hash, (const uint8_t *)devpath, strlen(devpath));
vt->count++;
return true;
}
/*
* Return 1 and needs_refresh 0: the hints can be used
* Return 1 and needs_refresh 1: the hints can't be used and should be updated
@ -691,19 +743,19 @@ static void _filter_to_str(struct cmd_context *cmd, int filter_cfg, char **strp)
*/
static int _read_hint_file(struct cmd_context *cmd, struct dm_list *hints, int *needs_refresh)
{
char devpath[PATH_MAX];
struct visitor vt = {
.it.visit = _visit_read_hint_file,
.cmd = cmd,
.hash = INITIAL_CRC,
};
FILE *fp;
struct dev_iter *iter;
struct dev_use *du;
struct hint hint;
struct hint *alloc_hint, *hp;
struct device *dev;
char *split[HINT_LINE_WORDS];
char *name, *pvid, *devn, *vgname, *p, *filter_str = NULL;
uint32_t read_hash = 0;
uint32_t calc_hash = INITIAL_CRC;
uint32_t read_count = 0;
uint32_t calc_count = 0;
int found = 0;
int keylen;
int hv_major, hv_minor;
@ -877,25 +929,12 @@ static int _read_hint_file(struct cmd_context *cmd, struct dm_list *hints, int *
/*
* Calculate and compare hash of devices that may be scanned.
*/
if (!(iter = dev_iter_create(NULL, 0)))
return 0;
while ((dev = dev_iter_get(cmd, iter))) {
if (cmd->enable_devices_file && !get_du_for_dev(cmd, dev))
continue;
dev_cache_iterate(&vt.it);
if (!_dev_in_hint_hash(cmd, dev))
continue;
dm_strncpy(devpath, dev_name(dev), sizeof(devpath));
calc_hash = calc_crc(calc_hash, (const uint8_t *)devpath, strlen(devpath));
calc_count++;
}
dev_iter_destroy(iter);
if (read_hash && (read_hash != calc_hash)) {
if (read_hash && (read_hash != vt.hash)) {
/* The count is just informational. */
log_debug("ignore hints with read_hash %u count %u calc_hash %u count %u",
read_hash, read_count, calc_hash, calc_count);
read_hash, read_count, vt.hash, vt.count);
*needs_refresh = 1;
return 1;
}
@ -930,6 +969,74 @@ static int _read_hint_file(struct cmd_context *cmd, struct dm_list *hints, int *
return 1;
}
static bool _visit_write_hint_file(struct radix_tree_iterator *it,
const void *key, size_t keylen,
union radix_value v)
{
struct visitor *vt = container_of(it, struct visitor, it);
struct device *dev = v.ptr;
const char *devpath;
const char *vgname;
struct lvmcache_info *info;
/*
* This loop does two different things (for clarity this should be
* two separate dev_iter loops, but one is used for efficiency).
* 1. compute the hint hash from all relevant devs
* 2. add PVs to the hint file
*/
if (vt->cmd->enable_devices_file && !get_du_for_dev(vt->cmd, dev))
return true;
if (!_dev_in_hint_hash(vt->cmd, dev)) {
if (dev->flags & DEV_SCAN_FOUND_LABEL) {
/* should never happen */
log_error("skip hint hash but found label %s.", dev_name(dev));
}
return true;
}
/*
* Create a hash of all device names on the system so we can
* detect when the devices on the system change, which
* invalidates the existing hints.
*/
devpath = dev_name(dev);
vt->hash = calc_crc(vt->hash, (const uint8_t *)devpath, strlen(devpath));
vt->count++;
if (!(dev->flags & DEV_SCAN_FOUND_LABEL))
return true;
if (dev->flags & DEV_IS_MD_COMPONENT) {
log_debug_devs("exclude md component from hints %s.", dev_name(dev));
return true;
}
/*
* No vgname will be found here for a PV with no mdas,
* in which case the vgname hint will be incomplete.
* (The label scan cannot associate nomda-pvs with the
* correct vg in lvmcache; that is only done by vg_read.)
* When using vgname hint we would always want to also
* scan any PVs missing a vgname hint in case they are
* part of the vg we are looking for.
*/
if ((info = lvmcache_info_from_pvid(dev->pvid, dev, 0)))
vgname = lvmcache_vgname_from_info(info);
else
vgname = NULL;
if (vgname && is_orphan_vg(vgname))
vgname = NULL;
fprintf(vt->fp, "scan:%s pvid:%s devn:%u:%u vg:%s\n", dev_name(dev),
dev->pvid, MAJOR(dev->dev), MINOR(dev->dev), vgname ?: "-");
return true;
}
/*
* Include any device in the hints that label_scan saw which had an lvm label
* header. label_scan set DEV_SCAN_FOUND_LABEL on the dev if it saw an lvm
@ -963,16 +1070,14 @@ static int _read_hint_file(struct cmd_context *cmd, struct dm_list *hints, int *
int write_hint_file(struct cmd_context *cmd, int newhints)
{
char devpath[PATH_MAX];
struct visitor vt = {
.it.visit = _visit_write_hint_file,
.cmd = cmd,
.hash = INITIAL_CRC,
};
FILE *fp;
struct lvmcache_info *info;
struct dev_iter *iter;
struct device *dev;
const char *vgname;
char *filter_str = NULL;
const char *config_devices_file = NULL;
uint32_t hash = INITIAL_CRC;
uint32_t count = 0;
time_t t;
int ret = 1;
@ -1048,79 +1153,16 @@ int write_hint_file(struct cmd_context *cmd, int newhints)
* iterate through all devs and write a line for each
* dev flagged DEV_SCAN_FOUND_LABEL
*/
vt.fp = fp;
dev_cache_iterate(&vt.it);
if (!(iter = dev_iter_create(NULL, 0))) {
ret = 0;
goto out_close;
}
/*
* This loop does two different things (for clarity this should be
* two separate dev_iter loops, but one is used for efficiency).
* 1. compute the hint hash from all relevant devs
* 2. add PVs to the hint file
*/
while ((dev = dev_iter_get(cmd, iter))) {
if (cmd->enable_devices_file && !get_du_for_dev(cmd, dev))
continue;
if (!_dev_in_hint_hash(cmd, dev)) {
if (dev->flags & DEV_SCAN_FOUND_LABEL) {
/* should never happen */
log_error("skip hint hash but found label %s", dev_name(dev));
}
continue;
}
/*
* Create a hash of all device names on the system so we can
* detect when the devices on the system change, which
* invalidates the existing hints.
*/
dm_strncpy(devpath, dev_name(dev), sizeof(devpath));
hash = calc_crc(hash, (const uint8_t *)devpath, strlen(devpath));
count++;
if (!(dev->flags & DEV_SCAN_FOUND_LABEL))
continue;
if (dev->flags & DEV_IS_MD_COMPONENT) {
log_debug("exclude md component from hints %s", dev_name(dev));
continue;
}
/*
* No vgname will be found here for a PV with no mdas,
* in which case the vgname hint will be incomplete.
* (The label scan cannot associate nomda-pvs with the
* correct vg in lvmcache; that is only done by vg_read.)
* When using vgname hint we would always want to also
* scan any PVs missing a vgname hint in case they are
* part of the vg we are looking for.
*/
if ((info = lvmcache_info_from_pvid(dev->pvid, dev, 0)))
vgname = lvmcache_vgname_from_info(info);
else
vgname = NULL;
if (vgname && is_orphan_vg(vgname))
vgname = NULL;
fprintf(fp, "scan:%s pvid:%s devn:%d:%d vg:%s\n",
dev_name(dev),
dev->pvid,
major(dev->dev), minor(dev->dev),
vgname ?: "-");
}
fprintf(fp, "devs_hash: %u %u\n", hash, count);
dev_iter_destroy(iter);
fprintf(fp, "devs_hash: %u %u\n", vt.hash, vt.count);
out_flush:
if (fflush(fp))
stack;
log_debug("Wrote hint file with devs_hash %u count %u", hash, count);
log_debug("Wrote hint file with devs_hash %u count %u", vt.hash, vt.count);
/*
* We are writing refreshed hints because another command told us to by
@ -1129,7 +1171,6 @@ int write_hint_file(struct cmd_context *cmd, int newhints)
if (newhints == NEWHINTS_FILE)
_unlink_newhints();
out_close:
if (fclose(fp))
log_debug("write_hint_file close errno %d", errno);

View File

@ -15,6 +15,7 @@
#include "lib/misc/lib.h"
#include "base/memory/zalloc.h"
#include "base/data-struct/radix-tree.h"
#include "lib/label/label.h"
#include "lib/misc/crc.h"
#include "lib/mm/xlate.h"
@ -864,6 +865,49 @@ void prepare_open_file_limit(struct cmd_context *cmd, unsigned int num_devs)
#endif
}
struct visitor {
struct radix_tree_iterator it;
struct cmd_context *cmd;
struct device *dev;
const char *pvid;
struct dm_list *all_devs;
};
static bool _visit_label_scan_for_pvid(struct radix_tree_iterator *it,
const void *key, size_t keylen,
union radix_value v)
{
struct visitor *vt = container_of(it, struct visitor, it);
char buf[LABEL_SIZE] __attribute__((aligned(8)));
const struct pv_header *pvh = (const struct pv_header *)(buf + 32);
struct device *dev = v.ptr;
struct dev_filter *f = vt->cmd->filter;
if (f) {
if (!(dev->flags & DEV_REGULAR) &&
!f->passes_filter(vt->cmd, f, dev, NULL))
return true; /* next */
} else if (!(dev->flags & DEV_REGULAR))
return true; /* next */
if (!label_scan_open(dev))
return true; /* next */
if (!dev_read_bytes(dev, 512, LABEL_SIZE, buf)) {
_scan_dev_close(dev);
return false;
}
_scan_dev_close(dev);
if (memcmp(pvh->pv_uuid, vt->pvid, ID_LEN))
return true; /* next */
vt->dev = dev;
return false; /* stop search */
}
/*
* Currently the only caller is pvck which probably doesn't need
* deferred filters checked after the read... it wants to know if
@ -872,15 +916,11 @@ void prepare_open_file_limit(struct cmd_context *cmd, unsigned int num_devs)
int label_scan_for_pvid(struct cmd_context *cmd, char *pvid, struct device **dev_out)
{
char buf[LABEL_SIZE] __attribute__((aligned(8)));
struct dm_list devs;
struct dev_iter *iter;
struct device_list *devl, *devl2;
struct device *dev;
struct pv_header *pvh;
int ret = 0;
dm_list_init(&devs);
struct visitor vt = {
.it.visit = _visit_label_scan_for_pvid,
.cmd = cmd,
.pvid = pvid,
};
/*
* Creates a list of available devices, does not open or read any,
@ -891,62 +931,21 @@ int label_scan_for_pvid(struct cmd_context *cmd, char *pvid, struct device **dev
return 0;
}
if (!label_scan_setup_bcache())
return_0;
log_debug_devs("Filtering devices to scan.");
/*
* Iterating over all available devices with cmd->filter filters
* devices; those returned from dev_iter_get are the devs that
* pass filters, and are those we can use.
*/
cmd->filter->use_count++;
dev_cache_iterate(&vt.it);
cmd->filter->use_count--;
if (!(iter = dev_iter_create(cmd->filter, 0))) {
log_error("Scanning failed to get devices.");
return 0;
}
log_debug_devs("Filtering devices to scan");
while ((dev = dev_iter_get(cmd, iter))) {
if (!(devl = zalloc(sizeof(*devl))))
continue;
devl->dev = dev;
dm_list_add(&devs, &devl->list);
};
dev_iter_destroy(iter);
if (!label_scan_setup_bcache())
goto_out;
log_debug_devs("Reading labels for pvid");
dm_list_iterate_items(devl, &devs) {
dev = devl->dev;
memset(buf, 0, sizeof(buf));
if (!label_scan_open(dev))
continue;
if (!dev_read_bytes(dev, 512, LABEL_SIZE, buf)) {
_scan_dev_close(dev);
goto out;
}
pvh = (struct pv_header *)(buf + 32);
if (!memcmp(pvh->pv_uuid, pvid, ID_LEN)) {
*dev_out = devl->dev;
_scan_dev_close(dev);
break;
}
_scan_dev_close(dev);
}
ret = 1;
out:
dm_list_iterate_items_safe(devl, devl2, &devs) {
dm_list_del(&devl->list);
free(devl);
}
return ret;
return ((*dev_out = vt.dev)) ? 1 : 0;
}
/*
@ -1239,6 +1238,28 @@ bad:
return 0;
}
static bool _visit_label_scan(struct radix_tree_iterator *it,
const void *key, size_t keylen,
union radix_value v)
{
struct visitor *vt = container_of(it, struct visitor, it);
struct device *dev = v.ptr;
struct device_list *devl;
if (!(devl = zalloc(sizeof(*devl))))
return true;
devl->dev = dev;
dm_list_add(vt->all_devs, &devl->list);
/*
* label_scan should not generally be called a second time,
* so this will usually do nothing.
*/
label_scan_invalidate(dev);
return true;
}
/*
* Scan devices on the system to discover which are LVM devices.
* Info about the LVM devices (PVs) is saved in lvmcache in a
@ -1253,13 +1274,17 @@ int label_scan(struct cmd_context *cmd)
struct dm_list filtered_devs;
struct dm_list scan_devs;
struct dm_list hints_list;
struct dev_iter *iter;
struct device_list *devl, *devl2;
struct device *dev;
uint64_t max_metadata_size_bytes;
int using_hints;
int create_hints = 0; /* NEWHINTS_NONE */
unsigned devs_features = 0;
struct visitor vt = {
.it.visit = _visit_label_scan,
.cmd = cmd,
.all_devs = &all_devs,
};
log_debug_devs("Finding devices to scan");
@ -1313,23 +1338,7 @@ int label_scan(struct cmd_context *cmd)
* Invalidate bcache data for all devs (there will usually be no bcache
* data to invalidate.)
*/
if (!(iter = dev_iter_create(NULL, 0))) {
log_error("Failed to get device list.");
return 0;
}
while ((dev = dev_iter_get(cmd, iter))) {
if (!(devl = zalloc(sizeof(*devl))))
continue;
devl->dev = dev;
dm_list_add(&all_devs, &devl->list);
/*
* label_scan should not generally be called a second time,
* so this will usually do nothing.
*/
label_scan_invalidate(dev);
}
dev_iter_destroy(iter);
dev_cache_iterate(&vt.it);
/*
* Exclude devices that fail nodata filters. (Those filters that can be