1
0
mirror of git://sourceware.org/git/lvm2.git synced 2024-12-21 13:34:40 +03:00
lvm2/lib/filters/filter-persistent.c
Zdenek Kabelac 2d64ffaee5 hash: use individual hint sizes
Use different 'hint' size for dm_hash_create() call - so
when debug info about hash is printed we can recognize which
hash was in use.

This patch doesn't change actual used size since that is always
rounded to be power of 2 and >=16 - so as such is only a
help to developer.

We could eventually use 'name' arg, but since this would have changed
API and this patchset will be routed to libdm & stable - we will
just use this small trick.
2021-03-08 15:33:15 +01:00

207 lines
5.6 KiB
C

/*
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
* Copyright (C) 2004-2007 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 "base/memory/zalloc.h"
#include "lib/misc/lib.h"
#include "lib/filters/filter.h"
#include "lib/config/config.h"
struct pfilter {
struct dm_hash_table *devices;
struct dev_filter *real;
struct dev_types *dt;
};
/*
* The persistent filter is filter layer that sits above the other filters and
* caches the final result of those other filters. When a device is first
* checked against filters, it will not be in this cache, so this filter will
* pass the device down to the other filters to check it. The other filters
* will run and either include the device (good/pass) or exclude the device
* (bad/fail). That good or bad result propagates up through this filter which
* saves the result. The next time some code checks the filters against the
* device, this persistent/cache filter is checked first. This filter finds
* the previous result in its cache and returns it without reevaluating the
* other real filters.
*
* FIXME: a cache like this should not be needed. The fact it's needed is a
* symptom of code that should be fixed to not reevaluate filters multiple
* times. A device should be checked against the filter once, and then not
* need to be checked again. With scanning now controlled, we could probably
* do this.
*/
static int _good_device;
static int _bad_device;
/*
* The hash table holds one of these two states
* against each entry.
*/
#define PF_BAD_DEVICE ((void *) &_good_device)
#define PF_GOOD_DEVICE ((void *) &_bad_device)
static int _init_hash(struct pfilter *pf)
{
if (pf->devices)
dm_hash_destroy(pf->devices);
if (!(pf->devices = dm_hash_create(111)))
return_0;
return 1;
}
static void _persistent_filter_wipe(struct cmd_context *cmd, struct dev_filter *f, struct device *dev, const char *use_filter_name)
{
struct pfilter *pf = (struct pfilter *) f->private;
struct dm_str_list *sl;
if (!dev) {
dm_hash_wipe(pf->devices);
} else {
dm_list_iterate_items(sl, &dev->aliases)
dm_hash_remove(pf->devices, sl->str);
}
}
static int _lookup_p(struct cmd_context *cmd, struct dev_filter *f, struct device *dev, const char *use_filter_name)
{
struct pfilter *pf = (struct pfilter *) f->private;
void *l;
struct dm_str_list *sl;
int pass = 1;
if (use_filter_name && strcmp(f->name, use_filter_name))
return pf->real->passes_filter(cmd, pf->real, dev, use_filter_name);
if (dm_list_empty(&dev->aliases)) {
log_debug_devs("%d:%d: filter cache skipping (no name)",
(int)MAJOR(dev->dev), (int)MINOR(dev->dev));
return 0;
}
l = dm_hash_lookup(pf->devices, dev_name(dev));
/* Cached bad, skip dev */
if (l == PF_BAD_DEVICE) {
log_debug_devs("%s: filter cache skipping (cached bad)", dev_name(dev));
return 0;
}
/* Cached good, use dev */
if (l == PF_GOOD_DEVICE) {
log_debug_devs("%s: filter cache using (cached good)", dev_name(dev));
return 1;
}
/* Uncached, check filters and cache the result */
if (!l) {
dev->flags &= ~DEV_FILTER_AFTER_SCAN;
pass = pf->real->passes_filter(cmd, pf->real, dev, use_filter_name);
if (!pass) {
/*
* A device that does not pass one filter is excluded
* even if the result of another filter is deferred,
* because the deferred result won't change the exclude.
*/
l = PF_BAD_DEVICE;
} else if ((pass == -EAGAIN) || (dev->flags & DEV_FILTER_AFTER_SCAN)) {
/*
* When the filter result is deferred, we let the device
* pass for now, but do not cache the result. We need to
* rerun the filters later. At that point the final result
* will be cached.
*/
log_debug_devs("filter cache deferred %s", dev_name(dev));
dev->flags |= DEV_FILTER_AFTER_SCAN;
pass = 1;
goto out;
} else if (pass) {
l = PF_GOOD_DEVICE;
}
log_debug_devs("filter caching %s %s", pass ? "good" : "bad", dev_name(dev));
dm_list_iterate_items(sl, &dev->aliases)
if (!dm_hash_insert(pf->devices, sl->str, l)) {
log_error("Failed to hash alias to filter.");
return 0;
}
}
out:
return pass;
}
static void _persistent_destroy(struct dev_filter *f)
{
struct pfilter *pf = (struct pfilter *) f->private;
if (f->use_count)
log_error(INTERNAL_ERROR "Destroying persistent filter while in use %u times.", f->use_count);
dm_hash_destroy(pf->devices);
pf->real->destroy(pf->real);
free(pf);
free(f);
}
struct dev_filter *persistent_filter_create(struct dev_types *dt, struct dev_filter *real)
{
struct pfilter *pf;
struct dev_filter *f = NULL;
if (!(pf = zalloc(sizeof(*pf)))) {
log_error("Allocation of persistent filter failed.");
return NULL;
}
pf->dt = dt;
pf->real = real;
if (!(_init_hash(pf))) {
log_error("Couldn't create hash table for persistent filter.");
goto bad;
}
if (!(f = zalloc(sizeof(*f)))) {
log_error("Allocation of device filter for persistent filter failed.");
goto bad;
}
f->passes_filter = _lookup_p;
f->destroy = _persistent_destroy;
f->use_count = 0;
f->private = pf;
f->wipe = _persistent_filter_wipe;
f->name = "persistent";
log_debug_devs("Persistent filter initialised.");
return f;
bad:
if (pf->devices)
dm_hash_destroy(pf->devices);
free(pf);
free(f);
return NULL;
}