mirror of
git://sourceware.org/git/lvm2.git
synced 2025-10-08 19:33:19 +03:00
labels: add async label scan
This commit is contained in:
16
configure.in
16
configure.in
@@ -39,6 +39,7 @@ case "$host_os" in
|
||||
LDDEPS="$LDDEPS .export.sym"
|
||||
LIB_SUFFIX=so
|
||||
DEVMAPPER=yes
|
||||
AIO=yes
|
||||
BUILD_LVMETAD=no
|
||||
BUILD_LVMPOLLD=no
|
||||
LOCKDSANLOCK=no
|
||||
@@ -58,6 +59,7 @@ case "$host_os" in
|
||||
CLDNOWHOLEARCHIVE=
|
||||
LIB_SUFFIX=dylib
|
||||
DEVMAPPER=yes
|
||||
AIO=no
|
||||
ODIRECT=no
|
||||
DM_IOCTLS=no
|
||||
SELINUX=no
|
||||
@@ -1131,6 +1133,19 @@ if test "$DEVMAPPER" = yes; then
|
||||
AC_DEFINE([DEVMAPPER_SUPPORT], 1, [Define to 1 to enable LVM2 device-mapper interaction.])
|
||||
fi
|
||||
|
||||
################################################################################
|
||||
dnl -- Disable aio
|
||||
AC_MSG_CHECKING(whether to use aio)
|
||||
AC_ARG_ENABLE(aio,
|
||||
AC_HELP_STRING([--disable-aio],
|
||||
[disable async i/o]),
|
||||
AIO=$enableval)
|
||||
AC_MSG_RESULT($AIO)
|
||||
|
||||
if test "$AIO" = yes; then
|
||||
AC_DEFINE([AIO_SUPPORT], 1, [Define to 1 to enable async i/o.])
|
||||
fi
|
||||
|
||||
################################################################################
|
||||
dnl -- Build lvmetad
|
||||
AC_MSG_CHECKING(whether to build LVMetaD)
|
||||
@@ -2072,6 +2087,7 @@ AC_SUBST(DEFAULT_USE_LVMETAD)
|
||||
AC_SUBST(DEFAULT_USE_LVMPOLLD)
|
||||
AC_SUBST(DEFAULT_USE_LVMLOCKD)
|
||||
AC_SUBST(DEVMAPPER)
|
||||
AC_SUBST(AIO)
|
||||
AC_SUBST(DLM_CFLAGS)
|
||||
AC_SUBST(DLM_LIBS)
|
||||
AC_SUBST(DL_LIBS)
|
||||
|
@@ -77,6 +77,10 @@ include $(top_builddir)/make.tmpl
|
||||
LIBS += $(LVMINTERNAL_LIBS) -ldevmapper $(PTHREAD_LIBS)
|
||||
CFLAGS += -fno-strict-aliasing $(EXTRA_EXEC_CFLAGS)
|
||||
|
||||
ifeq ("@AIO@", "yes")
|
||||
LIBS += -laio
|
||||
endif
|
||||
|
||||
INSTALL_TARGETS = \
|
||||
install_clvmd
|
||||
|
||||
|
57
lib/cache/lvmcache.c
vendored
57
lib/cache/lvmcache.c
vendored
@@ -592,7 +592,6 @@ const struct format_type *lvmcache_fmt_from_vgname(struct cmd_context *cmd,
|
||||
{
|
||||
struct lvmcache_vginfo *vginfo;
|
||||
struct lvmcache_info *info;
|
||||
struct label *label;
|
||||
struct dm_list *devh, *tmp;
|
||||
struct dm_list devs;
|
||||
struct device_list *devl;
|
||||
@@ -637,7 +636,7 @@ const struct format_type *lvmcache_fmt_from_vgname(struct cmd_context *cmd,
|
||||
|
||||
dm_list_iterate_safe(devh, tmp, &devs) {
|
||||
devl = dm_list_item(devh, struct device_list);
|
||||
(void) label_read(devl->dev, &label, UINT64_C(0));
|
||||
label_read(devl->dev, NULL, UINT64_C(0));
|
||||
dm_list_del(&devl->list);
|
||||
dm_free(devl);
|
||||
}
|
||||
@@ -806,16 +805,20 @@ char *lvmcache_vgname_from_pvid(struct cmd_context *cmd, const char *pvid)
|
||||
return vgname;
|
||||
}
|
||||
|
||||
static int _scan_invalid_dev_count;
|
||||
|
||||
static void _rescan_entry(struct lvmcache_info *info)
|
||||
{
|
||||
struct label *label;
|
||||
|
||||
if (info->status & CACHE_INVALID)
|
||||
(void) label_read(info->dev, &label, UINT64_C(0));
|
||||
if (info->status & CACHE_INVALID) {
|
||||
label_read(info->dev, NULL, UINT64_C(0));
|
||||
_scan_invalid_dev_count++;
|
||||
}
|
||||
}
|
||||
|
||||
static int _scan_invalid(void)
|
||||
static int _label_scan_invalid(void)
|
||||
{
|
||||
_scan_invalid_dev_count = 0;
|
||||
|
||||
dm_hash_iter(_pvid_hash, (dm_hash_iterate_fn) _rescan_entry);
|
||||
|
||||
return 1;
|
||||
@@ -1139,11 +1142,7 @@ int lvmcache_label_scan(struct cmd_context *cmd)
|
||||
struct dm_list add_cache_devs;
|
||||
struct lvmcache_info *info;
|
||||
struct device_list *devl;
|
||||
struct label *label;
|
||||
struct dev_iter *iter;
|
||||
struct device *dev;
|
||||
struct format_type *fmt;
|
||||
int dev_count = 0;
|
||||
|
||||
int r = 0;
|
||||
|
||||
@@ -1168,33 +1167,38 @@ int lvmcache_label_scan(struct cmd_context *cmd)
|
||||
* lvmcache_make_valid).
|
||||
*/
|
||||
if (_has_scanned && !_force_label_scan) {
|
||||
r = _scan_invalid();
|
||||
log_debug_devs("Scanning labels of invalid infos");
|
||||
r = _label_scan_invalid();
|
||||
log_debug_devs("Scanned %d labels of invalid infos", _scan_invalid_dev_count);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (_force_label_scan && (cmd->full_filter && !cmd->full_filter->use_count) && !refresh_filters(cmd))
|
||||
goto_out;
|
||||
|
||||
if (!cmd->full_filter || !(iter = dev_iter_create(cmd->full_filter, _force_label_scan))) {
|
||||
log_error("dev_iter creation failed");
|
||||
if (!cmd->full_filter) {
|
||||
log_error("label scan is missing full filter");
|
||||
goto out;
|
||||
}
|
||||
|
||||
log_very_verbose("Scanning device labels");
|
||||
|
||||
/*
|
||||
* Duplicates found during this label scan are added to _found_duplicate_devs().
|
||||
*/
|
||||
_destroy_duplicate_device_list(&_found_duplicate_devs);
|
||||
|
||||
while ((dev = dev_iter_get(iter))) {
|
||||
(void) label_read(dev, &label, UINT64_C(0));
|
||||
dev_count++;
|
||||
}
|
||||
|
||||
dev_iter_destroy(iter);
|
||||
|
||||
log_very_verbose("Scanned %d device labels", dev_count);
|
||||
/*
|
||||
* Do the actual scanning. This populates lvmcache
|
||||
* with infos/vginfos based on reading headers from
|
||||
* each device, and a vg summary from each mda.
|
||||
*
|
||||
* Note that these will *skip* scanning a device if
|
||||
* an info struct already exists in lvmcache for
|
||||
* the device. To really scan every device here,
|
||||
* you need to destroy lvmcache first.
|
||||
* (even "force" does not force this to scan devices.)
|
||||
*/
|
||||
if (!cmd->use_aio || !label_scan_async(cmd))
|
||||
label_scan_sync(cmd);
|
||||
|
||||
/*
|
||||
* _choose_preferred_devs() returns:
|
||||
@@ -1228,7 +1232,7 @@ int lvmcache_label_scan(struct cmd_context *cmd)
|
||||
|
||||
dm_list_iterate_items(devl, &add_cache_devs) {
|
||||
log_debug_cache("Rescan preferred device %s for lvmcache", dev_name(devl->dev));
|
||||
(void) label_read(devl->dev, &label, UINT64_C(0));
|
||||
label_read(devl->dev, NULL, UINT64_C(0));
|
||||
}
|
||||
|
||||
dm_list_splice(&_unused_duplicate_devs, &del_cache_devs);
|
||||
@@ -1434,7 +1438,6 @@ const char *lvmcache_pvid_from_devname(struct cmd_context *cmd,
|
||||
const char *devname)
|
||||
{
|
||||
struct device *dev;
|
||||
struct label *label;
|
||||
|
||||
if (!(dev = dev_cache_get(devname, cmd->filter))) {
|
||||
log_error("%s: Couldn't find device. Check your filters?",
|
||||
@@ -1442,7 +1445,7 @@ const char *lvmcache_pvid_from_devname(struct cmd_context *cmd,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!(label_read(dev, &label, UINT64_C(0))))
|
||||
if (!label_read(dev, NULL, UINT64_C(0)))
|
||||
return NULL;
|
||||
|
||||
return dev->pvid;
|
||||
|
@@ -163,6 +163,7 @@ struct cmd_context {
|
||||
unsigned vg_notify:1;
|
||||
unsigned lv_notify:1;
|
||||
unsigned pv_notify:1;
|
||||
unsigned use_aio:1;
|
||||
|
||||
/*
|
||||
* Filtering.
|
||||
|
@@ -1553,6 +1553,26 @@ cfg(metadata_pvmetadataignore_CFG, "pvmetadataignore", metadata_CFG_SECTION, CFG
|
||||
"If metadata areas on a PV are ignored, LVM will not store metadata\n"
|
||||
"in them.\n")
|
||||
|
||||
cfg(metadata_scan_async_CFG, "scan_async", metadata_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_BOOL, DEFAULT_SCAN_ASYNC, vsn(2, 2, 173), NULL, 0, NULL,
|
||||
"Use async I/O to read headers and metadata from disks in parallel.\n")
|
||||
|
||||
cfg(metadata_scan_size_CFG, "scan_size", metadata_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_INT, DEFAULT_SCAN_SIZE_KB, vsn(2, 2, 173), NULL, 0, NULL,
|
||||
"Number of KiB to read from each disk when scanning disks.\n"
|
||||
"The initial scan size is intended to cover all the headers\n"
|
||||
"and metadata that LVM places at the start of each disk so\n"
|
||||
"that a single read operation can retrieve them all.\n"
|
||||
"Any headers or metadata that lie beyond this size require\n"
|
||||
"an additional disk read.\n")
|
||||
|
||||
cfg(metadata_async_events_CFG, "async_events", metadata_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_INT, DEFAULT_ASYNC_EVENTS, vsn(2, 2, 173), NULL, 0, NULL,
|
||||
"Max number of concurrent async reads when scanning disks.\n"
|
||||
"Up to this many disks can be read concurrently when scanning\n"
|
||||
"disks with async I/O. If there are more disks than this,\n"
|
||||
"they will be scanned serially with synchronous reads.\n"
|
||||
"Increasing this number to match a larger number of disks may\n"
|
||||
"improve performance, but will increase memory requirements.\n"
|
||||
"This setting is limitted by the system aio configuration.\n")
|
||||
|
||||
cfg(metadata_stripesize_CFG, "stripesize", metadata_CFG_SECTION, CFG_ADVANCED | CFG_DEFAULT_COMMENTED, CFG_TYPE_INT, DEFAULT_STRIPESIZE, vsn(1, 0, 0), NULL, 0, NULL, NULL)
|
||||
|
||||
cfg_array(metadata_dirs_CFG, "dirs", metadata_CFG_SECTION, CFG_ADVANCED | CFG_DEFAULT_UNDEFINED, CFG_TYPE_STRING, NULL, vsn(1, 0, 0), NULL, 0, NULL,
|
||||
|
@@ -60,6 +60,10 @@
|
||||
#define DEFAULT_LVDISPLAY_SHOWS_FULL_DEVICE_PATH 0
|
||||
#define DEFAULT_UNKNOWN_DEVICE_NAME "[unknown]"
|
||||
|
||||
#define DEFAULT_SCAN_ASYNC 1
|
||||
#define DEFAULT_SCAN_SIZE_KB 128
|
||||
#define DEFAULT_ASYNC_EVENTS 100
|
||||
|
||||
#define DEFAULT_SANLOCK_LV_EXTEND_MB 256
|
||||
|
||||
#define DEFAULT_MIRRORLOG MIRROR_LOG_DISK
|
||||
|
@@ -1081,6 +1081,8 @@ static void _full_scan(int dev_scan)
|
||||
if (_cache.has_scanned && !dev_scan)
|
||||
return;
|
||||
|
||||
log_debug_devs("Adding device paths to dev cache");
|
||||
|
||||
_insert_dirs(&_cache.dirs);
|
||||
|
||||
(void) dev_cache_index_devs();
|
||||
@@ -1090,6 +1092,8 @@ static void _full_scan(int dev_scan)
|
||||
|
||||
_cache.has_scanned = 1;
|
||||
init_full_scan_done(1);
|
||||
|
||||
log_debug_devs("Added %d device paths to dev cache", dm_hash_get_num_entries(_cache.names));
|
||||
}
|
||||
|
||||
int dev_cache_has_scanned(void)
|
||||
|
@@ -321,6 +321,9 @@ static int _raw_read_mda_header(struct mda_header *mdah, struct device_area *dev
|
||||
if (!dev_open_readonly(dev_area->dev))
|
||||
return_0;
|
||||
|
||||
log_debug_metadata("Reading mda header sector from %s at %llu",
|
||||
dev_name(dev_area->dev), (unsigned long long)dev_area->start);
|
||||
|
||||
if (!dev_read(dev_area->dev, dev_area->start, MDA_HEADER_SIZE, mdah)) {
|
||||
if (!dev_close(dev_area->dev))
|
||||
stack;
|
||||
|
@@ -52,13 +52,23 @@ int text_vgsummary_import(const struct format_type *fmt,
|
||||
if (!(cft = config_open(CONFIG_FILE_SPECIAL, NULL, 0)))
|
||||
return_0;
|
||||
|
||||
if ((!dev && !config_file_read(cft)) ||
|
||||
(dev && !config_file_read_fd(cft, dev, offset, size,
|
||||
if (dev) {
|
||||
log_debug_metadata("Reading metadata from %s at %llu size %d (+%d)",
|
||||
dev_name(dev), (unsigned long long)offset,
|
||||
size, size2);
|
||||
|
||||
if (!config_file_read_fd(cft, dev, offset, size,
|
||||
offset2, size2, checksum_fn,
|
||||
vgsummary->mda_checksum,
|
||||
checksum_only, 1))) {
|
||||
log_error("Couldn't read volume group metadata.");
|
||||
goto out;
|
||||
checksum_only, 1)) {
|
||||
log_error("Couldn't read volume group metadata from %s.", dev_name(dev));
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
if (!config_file_read(cft)) {
|
||||
log_error("Couldn't read volume group metadata from file.");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (checksum_only) {
|
||||
|
@@ -14,6 +14,7 @@
|
||||
*/
|
||||
|
||||
#include "lib.h"
|
||||
#include "toolcontext.h"
|
||||
#include "label.h"
|
||||
#include "crc.h"
|
||||
#include "xlate.h"
|
||||
@@ -23,6 +24,7 @@
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <libaio.h>
|
||||
|
||||
/* FIXME Allow for larger labels? Restricted to single sector currently */
|
||||
|
||||
@@ -97,21 +99,17 @@ struct labeller *label_get_handler(const char *name)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void _update_lvmcache_orphan(struct lvmcache_info *info)
|
||||
{
|
||||
struct lvmcache_vgsummary vgsummary_orphan = {
|
||||
.vgname = lvmcache_fmt(info)->orphan_vg_name,
|
||||
};
|
||||
/*
|
||||
* FIXME: handle errors, if there is lvm data on the device but
|
||||
* it's bad, then we need to move this device into a special
|
||||
* set of defective devices that can be reported or repaired.
|
||||
*/
|
||||
|
||||
memcpy(&vgsummary_orphan.vgid, lvmcache_fmt(info)->orphan_vg_name, strlen(lvmcache_fmt(info)->orphan_vg_name));
|
||||
|
||||
if (!lvmcache_update_vgname_and_id(info, &vgsummary_orphan))
|
||||
stack;
|
||||
}
|
||||
|
||||
static struct labeller *_find_labeller(struct device *dev, char *buf,
|
||||
uint64_t *label_sector,
|
||||
uint64_t scan_sector)
|
||||
static struct labeller *_find_label_header(struct device *dev,
|
||||
char *scan_buf,
|
||||
char *label_buf,
|
||||
uint64_t *label_sector,
|
||||
uint64_t scan_sector)
|
||||
{
|
||||
struct labeller_i *li;
|
||||
struct labeller *r = NULL;
|
||||
@@ -119,19 +117,15 @@ static struct labeller *_find_labeller(struct device *dev, char *buf,
|
||||
struct lvmcache_info *info;
|
||||
uint64_t sector;
|
||||
int found = 0;
|
||||
char readbuf[LABEL_SCAN_SIZE] __attribute__((aligned(8)));
|
||||
|
||||
if (!dev_read(dev, scan_sector << SECTOR_SHIFT,
|
||||
LABEL_SCAN_SIZE, readbuf)) {
|
||||
log_debug_devs("%s: Failed to read label area", dev_name(dev));
|
||||
goto out;
|
||||
}
|
||||
/*
|
||||
* Find which sector in scan_buf starts with a valid label,
|
||||
* and copy it into label_buf.
|
||||
*/
|
||||
|
||||
/* Scan a few sectors for a valid label */
|
||||
for (sector = 0; sector < LABEL_SCAN_SECTORS;
|
||||
sector += LABEL_SIZE >> SECTOR_SHIFT) {
|
||||
lh = (struct label_header *) (readbuf +
|
||||
(sector << SECTOR_SHIFT));
|
||||
lh = (struct label_header *) (scan_buf + (sector << SECTOR_SHIFT));
|
||||
|
||||
if (!strncmp((char *)lh->id, LABEL_ID, sizeof(lh->id))) {
|
||||
if (found) {
|
||||
@@ -173,7 +167,7 @@ static struct labeller *_find_labeller(struct device *dev, char *buf,
|
||||
continue;
|
||||
}
|
||||
r = li->l;
|
||||
memcpy(buf, lh, LABEL_SIZE);
|
||||
memcpy(label_buf, lh, LABEL_SIZE);
|
||||
if (label_sector)
|
||||
*label_sector = sector + scan_sector;
|
||||
found = 1;
|
||||
@@ -182,11 +176,16 @@ static struct labeller *_find_labeller(struct device *dev, char *buf,
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
if (!found) {
|
||||
if ((info = lvmcache_info_from_pvid(dev->pvid, dev, 0)))
|
||||
_update_lvmcache_orphan(info);
|
||||
log_very_verbose("%s: No label detected", dev_name(dev));
|
||||
|
||||
if ((info = lvmcache_info_from_pvid(dev->pvid, dev, 0))) {
|
||||
/* Lacking a label does *not* make the device an orphan! */
|
||||
/* _update_lvmcache_orphan(info); */
|
||||
|
||||
log_warn("Device %s has no label, removing PV info from lvmcache.", dev_name(dev));
|
||||
lvmcache_del(info);
|
||||
}
|
||||
}
|
||||
|
||||
return r;
|
||||
@@ -206,10 +205,10 @@ int label_remove(struct device *dev)
|
||||
|
||||
memset(buf, 0, LABEL_SIZE);
|
||||
|
||||
log_very_verbose("Scanning for labels to wipe from %s", dev_name(dev));
|
||||
|
||||
if (!dev_open(dev))
|
||||
return_0;
|
||||
if (!dev_open(dev)) {
|
||||
log_debug_devs("Removing label skipped can't open %s", dev_name(dev));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* We flush the device just in case someone is stupid
|
||||
@@ -217,6 +216,8 @@ int label_remove(struct device *dev)
|
||||
*/
|
||||
dev_flush(dev);
|
||||
|
||||
log_debug_devs("Reading label sectors to remove from device %s", dev_name(dev));
|
||||
|
||||
if (!dev_read(dev, UINT64_C(0), LABEL_SCAN_SIZE, readbuf)) {
|
||||
log_debug_devs("%s: Failed to read label area", dev_name(dev));
|
||||
goto out;
|
||||
@@ -225,8 +226,7 @@ int label_remove(struct device *dev)
|
||||
/* Scan first few sectors for anything looking like a label */
|
||||
for (sector = 0; sector < LABEL_SCAN_SECTORS;
|
||||
sector += LABEL_SIZE >> SECTOR_SHIFT) {
|
||||
lh = (struct label_header *) (readbuf +
|
||||
(sector << SECTOR_SHIFT));
|
||||
lh = (struct label_header *) (readbuf + (sector << SECTOR_SHIFT));
|
||||
|
||||
wipe = 0;
|
||||
|
||||
@@ -235,8 +235,7 @@ int label_remove(struct device *dev)
|
||||
wipe = 1;
|
||||
} else {
|
||||
dm_list_iterate_items(li, &_labellers) {
|
||||
if (li->l->ops->can_handle(li->l, (char *) lh,
|
||||
sector)) {
|
||||
if (li->l->ops->can_handle(li->l, (char *) lh, sector)) {
|
||||
wipe = 1;
|
||||
break;
|
||||
}
|
||||
@@ -268,38 +267,81 @@ int label_remove(struct device *dev)
|
||||
return r;
|
||||
}
|
||||
|
||||
int label_read(struct device *dev, struct label **result,
|
||||
uint64_t scan_sector)
|
||||
/*
|
||||
* synchronously read label from one device (not for label_scan)
|
||||
*
|
||||
* Note that this will *not* reread the label from disk if an
|
||||
* info struct for this dev exists in lvmcache.
|
||||
*/
|
||||
|
||||
int label_read(struct device *dev, struct label **labelp, uint64_t scan_sector)
|
||||
{
|
||||
char buf[LABEL_SIZE] __attribute__((aligned(8)));
|
||||
char scanbuf[LABEL_SCAN_SIZE] __attribute__((aligned(8)));
|
||||
char label_buf[LABEL_SIZE] __attribute__((aligned(8)));
|
||||
struct label *label;
|
||||
struct labeller *l;
|
||||
uint64_t sector;
|
||||
struct lvmcache_info *info;
|
||||
int r = 0;
|
||||
|
||||
if (!labelp)
|
||||
labelp = &label;
|
||||
|
||||
if ((info = lvmcache_info_from_pvid(dev->pvid, dev, 1))) {
|
||||
log_debug_devs("Reading label from lvmcache for %s", dev_name(dev));
|
||||
*result = lvmcache_get_label(info);
|
||||
log_debug_devs("Reading label skipped in cache %s", dev_name(dev));
|
||||
if (labelp)
|
||||
*labelp = lvmcache_get_label(info);
|
||||
return 1;
|
||||
}
|
||||
|
||||
log_debug_devs("Reading label from device %s", dev_name(dev));
|
||||
|
||||
if (!dev_open_readonly(dev)) {
|
||||
stack;
|
||||
log_debug_devs("Reading label skipped can't open %s", dev_name(dev));
|
||||
|
||||
/* Can't open the device, does *not* mean it's an orphan! */
|
||||
/*
|
||||
if ((info = lvmcache_info_from_pvid(dev->pvid, dev, 0)))
|
||||
_update_lvmcache_orphan(info);
|
||||
*/
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
if ((l = _find_labeller(dev, buf, §or, scan_sector)))
|
||||
if ((r = (l->ops->read)(l, dev, buf, result)) && result && *result) {
|
||||
(*result)->dev = dev;
|
||||
(*result)->sector = sector;
|
||||
}
|
||||
memset(scanbuf, 0, sizeof(scanbuf));
|
||||
|
||||
log_debug_devs("Reading label sectors from device %s", dev_name(dev));
|
||||
|
||||
/*
|
||||
* Read first four sectors into scanbuf.
|
||||
*/
|
||||
if (!dev_read(dev, scan_sector << SECTOR_SHIFT, LABEL_SCAN_SIZE, scanbuf)) {
|
||||
log_debug_devs("%s: Failed to read label area", dev_name(dev));
|
||||
goto out;
|
||||
}
|
||||
|
||||
log_debug_devs("Parsing label and data from device %s", dev_name(dev));
|
||||
|
||||
/*
|
||||
* Finds the sector from scanbuf containing the label and copies into label_buf.
|
||||
* label_buf: struct label_header + struct pv_header + struct pv_header_extension
|
||||
*/
|
||||
if (!(l = _find_label_header(dev, scanbuf, label_buf, §or, scan_sector))) {
|
||||
/* FIXME: handle bad label */
|
||||
goto_out;
|
||||
}
|
||||
|
||||
/*
|
||||
* ops->read() is usually _text_read() which reads
|
||||
* the pv_header, mda locations, mda contents.
|
||||
* It saves the info it finds into lvmcache info/vginfo structs.
|
||||
*/
|
||||
if ((r = (l->ops->read)(l, dev, label_buf, labelp)) && *labelp) {
|
||||
(*labelp)->dev = dev;
|
||||
(*labelp)->sector = sector;
|
||||
} else {
|
||||
/* FIXME: handle errors */
|
||||
}
|
||||
out:
|
||||
if (!dev_close(dev))
|
||||
stack;
|
||||
|
||||
@@ -356,22 +398,26 @@ int label_write(struct device *dev, struct label *label)
|
||||
/* Unused */
|
||||
int label_verify(struct device *dev)
|
||||
{
|
||||
char scanbuf[LABEL_SCAN_SIZE] __attribute__((aligned(8)));
|
||||
char label_buf[LABEL_SIZE] __attribute__((aligned(8)));
|
||||
struct labeller *l;
|
||||
char buf[LABEL_SIZE] __attribute__((aligned(8)));
|
||||
uint64_t sector;
|
||||
struct lvmcache_info *info;
|
||||
int r = 0;
|
||||
|
||||
if (!dev_open_readonly(dev)) {
|
||||
if ((info = lvmcache_info_from_pvid(dev->pvid, dev, 0)))
|
||||
_update_lvmcache_orphan(info);
|
||||
if (!dev_open_readonly(dev))
|
||||
return_0;
|
||||
|
||||
if (!dev_read(dev, 0, LABEL_SCAN_SIZE, scanbuf)) {
|
||||
log_debug_devs("%s: Failed to read label area", dev_name(dev));
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!(l = _find_labeller(dev, buf, §or, UINT64_C(0))))
|
||||
if (!(l = _find_label_header(dev, scanbuf, label_buf, §or, UINT64_C(0)))) {
|
||||
/* FIXME: handle bad label */
|
||||
goto out;
|
||||
}
|
||||
|
||||
r = l->ops->verify ? l->ops->verify(l, buf, sector) : 1;
|
||||
r = l->ops->verify ? l->ops->verify(l, label_buf, sector) : 1;
|
||||
|
||||
out:
|
||||
if (!dev_close(dev))
|
||||
@@ -401,3 +447,426 @@ struct label *label_create(struct labeller *labeller)
|
||||
|
||||
return label;
|
||||
}
|
||||
|
||||
struct label_read_data {
|
||||
char *buf; /* 2K aligned memory buffer */
|
||||
struct iocb iocb;
|
||||
struct device *dev;
|
||||
struct dm_list list;
|
||||
int buf_len; /* LABEL_SCAN_SIZE */
|
||||
int try_sync;
|
||||
int read_done;
|
||||
int read_result;
|
||||
int process_done;
|
||||
};
|
||||
|
||||
/*
|
||||
* Start label aio read on a device.
|
||||
*/
|
||||
static int _label_read_async_start(struct cmd_context *cmd, io_context_t aio_ctx, struct label_read_data *ld)
|
||||
{
|
||||
struct iocb *iocb = &ld->iocb;
|
||||
int ret;
|
||||
|
||||
iocb->data = ld;
|
||||
iocb->aio_fildes = dev_fd(ld->dev);
|
||||
iocb->aio_lio_opcode = IO_CMD_PREAD;
|
||||
iocb->u.c.buf = ld->buf;
|
||||
iocb->u.c.nbytes = ld->buf_len;
|
||||
iocb->u.c.offset = 0;
|
||||
|
||||
ret = io_submit(aio_ctx, 1, &iocb);
|
||||
if (ret < 0)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#define MAX_GET_EVENTS 8
|
||||
|
||||
/*
|
||||
* Reap aio reads from devices.
|
||||
*/
|
||||
static int _label_read_async_wait(struct cmd_context *cmd, io_context_t aio_ctx, int wait_count)
|
||||
{
|
||||
struct io_event events[MAX_GET_EVENTS];
|
||||
int wait_nr;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
retry:
|
||||
memset(&events, 0, sizeof(events));
|
||||
|
||||
if (wait_count >= MAX_GET_EVENTS)
|
||||
wait_nr = MAX_GET_EVENTS;
|
||||
else
|
||||
wait_nr = wait_count;
|
||||
|
||||
ret = io_getevents(aio_ctx, 1, wait_nr, (struct io_event *)&events, NULL);
|
||||
if (ret == -EINTR)
|
||||
goto retry;
|
||||
if (ret < 0)
|
||||
return 0;
|
||||
if (!ret)
|
||||
return 1;
|
||||
|
||||
for (i = 0; i < ret; i++) {
|
||||
struct iocb *iocb = events[i].obj;
|
||||
struct label_read_data *ld = iocb->data;
|
||||
ld->read_result = events[i].res;
|
||||
ld->read_done = 1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Process / parse headers from buffer holding label header.
|
||||
* Populates lvmcache with device / mda locations / vgname
|
||||
* so that vg_read(vgname) will know which devices/locations
|
||||
* to read metadata from.
|
||||
*/
|
||||
static int _label_read_async_process(struct cmd_context *cmd, struct label_read_data *ld)
|
||||
{
|
||||
char label_buf[LABEL_SIZE] __attribute__((aligned(8)));
|
||||
struct label *label = NULL;
|
||||
struct labeller *l;
|
||||
uint64_t sector;
|
||||
int r = 0;
|
||||
|
||||
if ((ld->read_result < 0) || (ld->read_result != ld->buf_len)) {
|
||||
/* FIXME: handle errors */
|
||||
log_error("Reading label sectors aio error %d from %s", ld->read_result, dev_name(ld->dev));
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Finds the sector from scanbuf containing the label and copies into label_buf.
|
||||
* label_buf: struct label_header + struct pv_header + struct pv_header_extension
|
||||
*/
|
||||
if (!(l = _find_label_header(ld->dev, ld->buf, label_buf, §or, 0))) {
|
||||
/* Non-PVs exit here */
|
||||
/* FIXME: check for PVs with errors that also exit here. */
|
||||
goto_out;
|
||||
}
|
||||
|
||||
/*
|
||||
* ops->read() is usually _text_read() which reads
|
||||
* the pv_header, mda locations, mda contents.
|
||||
* It saves the info it finds into lvmcache info/vginfo structs.
|
||||
*/
|
||||
if ((r = (l->ops->read)(l, ld->dev, label_buf, &label)) && label) {
|
||||
label->dev = ld->dev;
|
||||
label->sector = sector;
|
||||
} else {
|
||||
/* FIXME: handle errors */
|
||||
}
|
||||
out:
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* label_scan iterates over all visible devices, looking
|
||||
* for any that belong to lvm, and fills lvmcache with
|
||||
* basic info about them. It's main job is to prepare
|
||||
* for subsequent vg_reads. vg_read(vgname) needs to
|
||||
* know which devices/locations to read metadata from
|
||||
* for the given vg name. The label_scan has scanned
|
||||
* all devices and saved info in lvmcache about:
|
||||
* the device, the mda locations on that device,
|
||||
* and the vgname referenced in those mdas. So,
|
||||
* vg_read(vgname) can map the vgname to a set of
|
||||
* mda locations it needs to read to get the metadata.
|
||||
*/
|
||||
|
||||
int label_scan_async(struct cmd_context *cmd)
|
||||
{
|
||||
struct dm_list label_read_list;
|
||||
struct label_read_data *ld, *ld2;
|
||||
struct dev_iter *iter;
|
||||
struct device *dev;
|
||||
io_context_t aio_ctx;
|
||||
struct lvmcache_info *info;
|
||||
char *buf;
|
||||
char **p_buf;
|
||||
int buf_len;
|
||||
int need_wait_count;
|
||||
int need_process_count;
|
||||
int dev_count = 0;
|
||||
int error;
|
||||
|
||||
dm_list_init(&label_read_list);
|
||||
|
||||
/*
|
||||
* "buf" is the buffer into which the first four sectors
|
||||
* of each device is read.
|
||||
* (LABEL_SCAN_SIZE is four 512-byte sectors, i.e. 2K).
|
||||
*
|
||||
* The label is expected to be one of the first four sectors.
|
||||
* buf needs to be aligned.
|
||||
*/
|
||||
buf_len = LABEL_SCAN_SIZE;
|
||||
|
||||
/*
|
||||
* if aio setup fails, caller will revert to sync scan
|
||||
*/
|
||||
memset(&aio_ctx, 0, sizeof(io_context_t));
|
||||
|
||||
error = io_setup(128, &aio_ctx);
|
||||
if (error < 0) {
|
||||
log_debug_devs("async io setup error %d, reverting to sync io.", error);
|
||||
return_0;
|
||||
}
|
||||
|
||||
log_debug_devs("Finding devices to scan");
|
||||
|
||||
dev_cache_full_scan(cmd->full_filter);
|
||||
|
||||
log_debug_devs("Scanning labels async");
|
||||
|
||||
if (!(iter = dev_iter_create(cmd->full_filter, 0))) {
|
||||
log_error("Scanning labels failed to get devices.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* allocate all the structs/mem for the aio
|
||||
*
|
||||
* FIXME: dev_iter_get opens/closes each dev once or twice when applying filters,
|
||||
* once to check the size, and a second time (in some cases) to check the block size.
|
||||
*/
|
||||
while ((dev = dev_iter_get(iter))) {
|
||||
/*
|
||||
* FIXME: fix code so it's not scanning labels when it's not needed,
|
||||
* then stuff like this can be removed.
|
||||
*/
|
||||
if ((info = lvmcache_info_from_pvid(dev->pvid, dev, 1))) {
|
||||
log_debug_devs("Reading label skipped in cache %s", dev_name(dev));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!dev_open_readonly(dev)) {
|
||||
log_debug_devs("Reading label skipped can't open %s", dev_name(dev));
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* FIXME: mem pool code doesn't work for this, probably because
|
||||
* of the posix_memalign below. Try using mem pool to allocate
|
||||
* all the ld structs first, then allocate all the aligned aio
|
||||
* buffers.
|
||||
*/
|
||||
if (!(ld = malloc(sizeof(*ld))))
|
||||
goto_bad;
|
||||
|
||||
memset(ld, 0, sizeof(*ld));
|
||||
|
||||
buf = NULL;
|
||||
p_buf = &buf;
|
||||
|
||||
if (posix_memalign((void *)p_buf, getpagesize(), buf_len))
|
||||
goto_bad;
|
||||
|
||||
memset(buf, 0, buf_len);
|
||||
|
||||
ld->dev = dev;
|
||||
ld->buf = buf;
|
||||
ld->buf_len = buf_len;
|
||||
|
||||
dm_list_add(&label_read_list, &ld->list);
|
||||
dev_count++;
|
||||
};
|
||||
dev_iter_destroy(iter);
|
||||
|
||||
/*
|
||||
* Start the aio reads on each dev. Flag any that
|
||||
* fail and the next loop will try a sync read for it.
|
||||
*/
|
||||
dm_list_iterate_items(ld, &label_read_list) {
|
||||
if (!_label_read_async_start(cmd, aio_ctx, ld))
|
||||
ld->try_sync = 1;
|
||||
else
|
||||
log_debug_devs("Reading label sectors from device %s async", dev_name(ld->dev));
|
||||
}
|
||||
|
||||
/*
|
||||
* Try a synchronous read for any dev where aio couldn't be submitted.
|
||||
*/
|
||||
dm_list_iterate_items(ld, &label_read_list) {
|
||||
if (ld->try_sync) {
|
||||
log_debug_devs("Reading label sectors from device %s async", dev_name(ld->dev));
|
||||
|
||||
if (!dev_read(ld->dev, 0, ld->buf_len, ld->buf)) {
|
||||
log_debug_devs("%s: Failed to read label area", dev_name(ld->dev));
|
||||
ld->read_result = -1;
|
||||
} else {
|
||||
ld->read_result = ld->buf_len;
|
||||
}
|
||||
ld->read_done = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Reap the aio and process the results.
|
||||
*/
|
||||
|
||||
check_aio:
|
||||
need_wait_count = 0;
|
||||
need_process_count = 0;
|
||||
|
||||
dm_list_iterate_items(ld, &label_read_list) {
|
||||
if (!ld->read_done)
|
||||
need_wait_count++;
|
||||
else if (!ld->process_done)
|
||||
need_process_count++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Process devices that have finished reading label sectors.
|
||||
* Processing includes sync i/o to read mda locations and vg metadata.
|
||||
*/
|
||||
if (need_process_count) {
|
||||
dm_list_iterate_items(ld, &label_read_list) {
|
||||
if (!ld->read_done || ld->process_done)
|
||||
continue;
|
||||
log_debug_devs("Parsing label and data from device %s", dev_name(ld->dev));
|
||||
_label_read_async_process(cmd, ld);
|
||||
ld->process_done = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait for more devices to finish reading label sectors.
|
||||
*/
|
||||
if (need_wait_count) {
|
||||
if (_label_read_async_wait(cmd, aio_ctx, need_wait_count))
|
||||
goto check_aio;
|
||||
|
||||
/* TODO: handle this error */
|
||||
/* an error getting aio events, should we fall back
|
||||
to doing sync dev_read() on any that aren't done? */
|
||||
log_error(INTERNAL_ERROR "aio getevents error");
|
||||
}
|
||||
|
||||
io_destroy(aio_ctx);
|
||||
|
||||
dm_list_iterate_items_safe(ld, ld2, &label_read_list) {
|
||||
dm_list_del(&ld->list);
|
||||
dev_close(ld->dev);
|
||||
if (ld->buf)
|
||||
free(ld->buf);
|
||||
free(ld);
|
||||
}
|
||||
|
||||
log_debug_devs("Scanned %d labels async", dev_count);
|
||||
return 1;
|
||||
|
||||
bad:
|
||||
/* caller will try sync scan */
|
||||
log_error("async label scan failed, reverting to sync scan.");
|
||||
|
||||
dev_iter_destroy(iter);
|
||||
io_destroy(aio_ctx);
|
||||
|
||||
dm_list_iterate_items_safe(ld, ld2, &label_read_list) {
|
||||
dm_list_del(&ld->list);
|
||||
dev_close(ld->dev);
|
||||
if (ld->buf)
|
||||
free(ld->buf);
|
||||
free(ld);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _label_read_sync(struct cmd_context *cmd, struct device *dev)
|
||||
{
|
||||
char scanbuf[LABEL_SCAN_SIZE] __attribute__((aligned(8)));
|
||||
char label_buf[LABEL_SIZE] __attribute__((aligned(8)));
|
||||
struct label *label = NULL;
|
||||
struct labeller *l;
|
||||
uint64_t sector;
|
||||
int r = 0;
|
||||
|
||||
memset(scanbuf, 0, sizeof(scanbuf));
|
||||
|
||||
log_debug_devs("Reading label sectors from device %s", dev_name(dev));
|
||||
|
||||
/*
|
||||
* Read first four sectors into scanbuf.
|
||||
*/
|
||||
if (!dev_read(dev, 0, LABEL_SCAN_SIZE, scanbuf)) {
|
||||
log_debug_devs("%s: Failed to read label area", dev_name(dev));
|
||||
goto out;
|
||||
}
|
||||
|
||||
log_debug_devs("Parsing label and data from device %s", dev_name(dev));
|
||||
|
||||
/*
|
||||
* Finds the sector from scanbuf containing the label and copies into label_buf.
|
||||
* label_buf: struct label_header + struct pv_header + struct pv_header_extension
|
||||
*/
|
||||
if (!(l = _find_label_header(dev, scanbuf, label_buf, §or, 0))) {
|
||||
/* FIXME: handle bad label */
|
||||
goto_out;
|
||||
}
|
||||
|
||||
/*
|
||||
* ops->read() is usually _text_read() which reads
|
||||
* the pv_header, mda locations, mda contents.
|
||||
* It saves the info it finds into lvmcache info/vginfo structs.
|
||||
*/
|
||||
if ((r = (l->ops->read)(l, dev, label_buf, &label)) && label) {
|
||||
label->dev = dev;
|
||||
label->sector = sector;
|
||||
} else {
|
||||
/* FIXME: handle errors */
|
||||
}
|
||||
out:
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read and process device labels/data without aio.
|
||||
*/
|
||||
int label_scan_sync(struct cmd_context *cmd)
|
||||
{
|
||||
struct dev_iter *iter;
|
||||
struct device *dev;
|
||||
int dev_count = 0;
|
||||
struct lvmcache_info *info;
|
||||
|
||||
log_debug_devs("Finding devices to scan");
|
||||
|
||||
dev_cache_full_scan(cmd->full_filter);
|
||||
|
||||
log_very_verbose("Scanning labels sync");
|
||||
|
||||
if (!(iter = dev_iter_create(cmd->full_filter, 0))) {
|
||||
log_error("Scanning labels failed to get devices.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
while ((dev = dev_iter_get(iter))) {
|
||||
if ((info = lvmcache_info_from_pvid(dev->pvid, dev, 1))) {
|
||||
log_debug_devs("Reading label skipped in cache %s", dev_name(dev));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!dev_open_readonly(dev)) {
|
||||
log_debug_devs("Reading label skipped can't open %s", dev_name(dev));
|
||||
continue;
|
||||
}
|
||||
|
||||
_label_read_sync(cmd, dev);
|
||||
|
||||
if (!dev_close(dev))
|
||||
stack;
|
||||
|
||||
dev_count++;
|
||||
}
|
||||
dev_iter_destroy(iter);
|
||||
|
||||
log_very_verbose("Scanned %d labels sync", dev_count);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@@ -18,6 +18,7 @@
|
||||
|
||||
#include "uuid.h"
|
||||
#include "device.h"
|
||||
#include "toolcontext.h"
|
||||
|
||||
#define LABEL_ID "LABELONE"
|
||||
#define LABEL_SIZE SECTOR_SIZE /* Think very carefully before changing this */
|
||||
@@ -99,11 +100,13 @@ int label_register_handler(struct labeller *handler);
|
||||
struct labeller *label_get_handler(const char *name);
|
||||
|
||||
int label_remove(struct device *dev);
|
||||
int label_read(struct device *dev, struct label **result,
|
||||
uint64_t scan_sector);
|
||||
int label_read(struct device *dev, struct label **label, uint64_t scan_sector);
|
||||
int label_write(struct device *dev, struct label *label);
|
||||
int label_verify(struct device *dev);
|
||||
struct label *label_create(struct labeller *labeller);
|
||||
void label_destroy(struct label *label);
|
||||
|
||||
int label_scan_async(struct cmd_context *cmd);
|
||||
int label_scan_sync(struct cmd_context *cmd);
|
||||
|
||||
#endif
|
||||
|
@@ -45,6 +45,10 @@ include $(top_builddir)/make.tmpl
|
||||
LDFLAGS += -L$(top_builddir)/lib -L$(top_builddir)/daemons/dmeventd
|
||||
LIBS += $(LVMINTERNAL_LIBS) -ldevmapper
|
||||
|
||||
ifeq ("@AIO@", "yes")
|
||||
LIBS += -laio
|
||||
endif
|
||||
|
||||
.PHONY: install_dynamic install_static install_include install_pkgconfig
|
||||
|
||||
INSTALL_TYPE = install_dynamic
|
||||
|
@@ -31,6 +31,10 @@ endif
|
||||
LVMLIBS = @LVM2APP_LIB@ -ldevmapper
|
||||
endif
|
||||
|
||||
ifeq ("@AIO@", "yes")
|
||||
LVMLIBS += -laio
|
||||
endif
|
||||
|
||||
LVM_SCRIPTS = lvmdump.sh lvmconf.sh
|
||||
DM_SCRIPTS =
|
||||
|
||||
|
@@ -109,6 +109,10 @@ ifeq ("@CMDLIB@", "yes")
|
||||
INSTALL_LVM_TARGETS += $(INSTALL_CMDLIB_TARGETS)
|
||||
endif
|
||||
|
||||
ifeq ("@AIO@", "yes")
|
||||
LVMLIBS += -laio
|
||||
endif
|
||||
|
||||
EXPORTED_HEADER = $(srcdir)/lvm2cmd.h
|
||||
EXPORTED_FN_PREFIX = lvm2
|
||||
|
||||
|
@@ -2386,6 +2386,8 @@ static int _get_current_settings(struct cmd_context *cmd)
|
||||
!_merge_synonym(cmd, metadatacopies_ARG, vgmetadatacopies_ARG)))
|
||||
return EINVALID_CMD_LINE;
|
||||
|
||||
cmd->use_aio = find_config_tree_bool(cmd, metadata_scan_async_CFG, NULL);
|
||||
|
||||
/* Zero indicates success */
|
||||
return 0;
|
||||
}
|
||||
|
Reference in New Issue
Block a user