1
0
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:
David Teigland
2017-07-25 15:42:36 -05:00
parent 6015e7f48d
commit b968e33760
15 changed files with 639 additions and 88 deletions

View File

@@ -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)

View File

@@ -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
View File

@@ -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;

View File

@@ -163,6 +163,7 @@ struct cmd_context {
unsigned vg_notify:1;
unsigned lv_notify:1;
unsigned pv_notify:1;
unsigned use_aio:1;
/*
* Filtering.

View File

@@ -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,

View File

@@ -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

View File

@@ -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)

View File

@@ -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;

View File

@@ -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) {

View File

@@ -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, &sector, 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, &sector, 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, &sector, UINT64_C(0))))
if (!(l = _find_label_header(dev, scanbuf, label_buf, &sector, 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, &sector, 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, &sector, 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;
}

View File

@@ -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

View File

@@ -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

View File

@@ -31,6 +31,10 @@ endif
LVMLIBS = @LVM2APP_LIB@ -ldevmapper
endif
ifeq ("@AIO@", "yes")
LVMLIBS += -laio
endif
LVM_SCRIPTS = lvmdump.sh lvmconf.sh
DM_SCRIPTS =

View File

@@ -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

View File

@@ -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;
}