return NULL;
}
+int lvmcache_pvsummary_count(const char *vgname)
+{
+ struct lvmcache_vginfo *vginfo;
+
+ if (!(vginfo = lvmcache_vginfo_from_vgname(vgname, NULL)))
+ return_0;
+
+ return dm_list_size(&vginfo->pvsummaries);
+}
+
/*
* Check if any PVs in vg->pvs have the same PVID as any
* entries in _unused_duplicates.
unsigned int lvmcache_vg_info_count(void);
+int lvmcache_pvsummary_count(const char *vgname);
+
#endif
unsigned enable_hints:1; /* hints are enabled for cmds in general */
unsigned use_hints:1; /* if hints are enabled this cmd can use them */
unsigned pvscan_recreate_hints:1; /* enable special case hint handling for pvscan --cache */
- unsigned hints_pvs_online:1; /* hints="pvs_online" */
unsigned scan_lvs:1;
unsigned wipe_outdated_pvs:1;
unsigned enable_devices_list:1; /* command is using --devices option */
}
/*
- * pvscan --cache is specialized/optimized to look only at command args,
+ * autoactivation is specialized/optimized to look only at command args,
* so this just sets up the devices file, then individual devices are
- * added to dev-cache and matched with device_ids later in pvscan.
+ * added to dev-cache and matched with device_ids.
*/
-int setup_devices_for_pvscan_cache(struct cmd_context *cmd)
+int setup_devices_for_online_autoactivation(struct cmd_context *cmd)
{
if (cmd->enable_devices_list) {
if (!_setup_devices_list(cmd))
int setup_devices(struct cmd_context *cmd);
int setup_device(struct cmd_context *cmd, const char *devname);
-/* Normal device setup functions are split up for pvscan optimization. */
-int setup_devices_for_pvscan_cache(struct cmd_context *cmd);
+int setup_devices_for_online_autoactivation(struct cmd_context *cmd);
int setup_devname_in_dev_cache(struct cmd_context *cmd, const char *devname);
int setup_devno_in_dev_cache(struct cmd_context *cmd, dev_t devno);
return 1;
}
+void free_po_list(struct dm_list *list)
+{
+ struct pv_online *po, *po2;
+
+ dm_list_iterate_items_safe(po, po2, list) {
+ dm_list_del(&po->list);
+ free(po);
+ }
+}
+
+int get_pvs_online(struct dm_list *pvs_online, const char *vgname)
+{
+ char path[PATH_MAX];
+ char file_vgname[NAME_LEN];
+ DIR *dir;
+ struct dirent *de;
+ struct pv_online *po;
+ int file_major = 0, file_minor = 0;
+
+ if (!(dir = opendir(PVS_ONLINE_DIR)))
+ return 0;
+
+ while ((de = readdir(dir))) {
+ if (de->d_name[0] == '.')
+ continue;
+
+ if (strlen(de->d_name) != ID_LEN)
+ continue;
+
+ memset(path, 0, sizeof(path));
+ snprintf(path, sizeof(path), "%s/%s", PVS_ONLINE_DIR, de->d_name);
+
+ file_major = 0;
+ file_minor = 0;
+ memset(file_vgname, 0, sizeof(file_vgname));
+
+ if (!online_pvid_file_read(path, &file_major, &file_minor, file_vgname))
+ continue;
+
+ if (vgname && strcmp(file_vgname, vgname))
+ continue;
+
+ if (!(po = zalloc(sizeof(*po))))
+ continue;
+
+ memcpy(po->pvid, de->d_name, ID_LEN);
+ if (file_major || file_minor)
+ po->devno = MKDEV(file_major, file_minor);
+ if (file_vgname[0])
+ strncpy(po->vgname, file_vgname, NAME_LEN-1);
+
+ dm_list_add(pvs_online, &po->list);
+ }
+ if (closedir(dir))
+ log_sys_debug("closedir", PVS_ONLINE_DIR);
+ return 1;
+}
+
/*
* When a PV goes offline, remove the vg online file for that VG
* (even if other PVs for the VG are still online). This means
return 0;
}
+int get_pvs_lookup(struct dm_list *pvs_online, const char *vgname)
+{
+ char lookup_path[PATH_MAX] = { 0 };
+ char path[PATH_MAX] = { 0 };
+ char line[64];
+ char pvid[ID_LEN + 1] __attribute__((aligned(8))) = { 0 };
+ char file_vgname[NAME_LEN];
+ struct pv_online *po;
+ int file_major = 0, file_minor = 0;
+ FILE *fp;
+
+ if (dm_snprintf(lookup_path, sizeof(path), "%s/%s", PVS_LOOKUP_DIR, vgname) < 0)
+ return_0;
+
+ if (!(fp = fopen(lookup_path, "r")))
+ return_0;
+
+ while (fgets(line, sizeof(line), fp)) {
+ memcpy(pvid, line, ID_LEN);
+ if (strlen(pvid) != ID_LEN)
+ goto_bad;
+
+ memset(path, 0, sizeof(path));
+ snprintf(path, sizeof(path), "%s/%s", PVS_ONLINE_DIR, pvid);
+
+ file_major = 0;
+ file_minor = 0;
+ memset(file_vgname, 0, sizeof(file_vgname));
+
+ if (!online_pvid_file_read(path, &file_major, &file_minor, file_vgname))
+ goto_bad;
+
+ if (vgname && strcmp(file_vgname, vgname))
+ goto_bad;
+
+ if (!(po = zalloc(sizeof(*po))))
+ goto_bad;
+
+ memcpy(po->pvid, pvid, ID_LEN);
+ if (file_major || file_minor)
+ po->devno = MKDEV(file_major, file_minor);
+ if (file_vgname[0])
+ strncpy(po->vgname, file_vgname, NAME_LEN-1);
+
+ dm_list_add(pvs_online, &po->list);
+ }
+
+ fclose(fp);
+ return 1;
+
+bad:
+ free_po_list(pvs_online);
+ fclose(fp);
+ return 0;
+}
+
void online_dir_setup(struct cmd_context *cmd)
{
struct stat st;
if ((rv < 0) && stat(PVS_LOOKUP_DIR, &st))
log_error_pvscan(cmd, "Failed to create %s %d", PVS_LOOKUP_DIR, errno);
-
-
}
#ifndef _ONLINE_H
#define _ONLINE_H
+struct pv_online {
+ struct dm_list list;
+ struct device *dev;
+ dev_t devno;
+ char pvid[ID_LEN + 1];
+ char vgname[NAME_LEN];
+};
+
/*
* Avoid a duplicate pvscan[%d] prefix when logging to the journal.
* FIXME: this should probably replace if (udevoutput) with
int online_pvid_file_create(struct cmd_context *cmd, struct device *dev, const char *vgname);
int online_pvid_file_exists(const char *pvid);
void online_dir_setup(struct cmd_context *cmd);
+int get_pvs_online(struct dm_list *pvs_online, const char *vgname);
+int get_pvs_lookup(struct dm_list *pvs_online, const char *vgname);
+void free_po_list(struct dm_list *list);
#endif
* probably want to exclude that command from attempting this optimization,
* because it would be difficult to know what VG that command wanted to use.
*/
-static void _get_single_vgname_cmd_arg(struct cmd_context *cmd,
- struct dm_list *hints, char **vgname)
+void get_single_vgname_cmd_arg(struct cmd_context *cmd,
+ struct dm_list *hints, char **vgname)
{
struct hint *hint;
char namebuf[NAME_LEN];
return;
check:
+ if (!hints) {
+ *vgname = name;
+ return;
+ }
+
/*
* Only use this vgname hint if there are hints that contain this
* vgname. This might happen if we aren't able to properly extract the
free(name);
}
-static int _get_hints_from_pvs_online(struct cmd_context *cmd, struct dm_list *hints_out,
- struct dm_list *devs_in, struct dm_list *devs_out)
-{
- char path[PATH_MAX];
- char file_vgname[NAME_LEN];
- struct dm_list hints_list;
- struct hint file_hint;
- struct hint *alloc_hint;
- struct hint *hint, *hint2;
- struct device_list *devl, *devl2;
- int file_major, file_minor;
- int found = 0;
- DIR *dir;
- struct dirent *de;
- char *vgname = NULL;
- char *pvid;
-
- dm_list_init(&hints_list);
-
- if (!(dir = opendir(PVS_ONLINE_DIR)))
- return 0;
-
- while ((de = readdir(dir))) {
- if (de->d_name[0] == '.')
- continue;
-
- pvid = de->d_name;
-
- if (strlen(pvid) != ID_LEN) /* 32 */
- continue;
-
- memset(path, 0, sizeof(path));
- snprintf(path, sizeof(path), "%s/%s", PVS_ONLINE_DIR, pvid);
-
- memset(&file_hint, 0, sizeof(file_hint));
- memset(file_vgname, 0, sizeof(file_vgname));
- file_major = 0;
- file_minor = 0;
-
- if (!online_pvid_file_read(path, &file_major, &file_minor, file_vgname))
- continue;
-
- if (!dm_strncpy(file_hint.pvid, pvid, sizeof(file_hint.pvid)))
- continue;
-
- file_hint.devt = makedev(file_major, file_minor);
-
- if (file_vgname[0] && validate_name(file_vgname)) {
- if (!dm_strncpy(file_hint.vgname, file_vgname, sizeof(file_hint.vgname)))
- continue;
- }
-
- if (!(alloc_hint = malloc(sizeof(struct hint))))
- continue;
-
- memcpy(alloc_hint, &file_hint, sizeof(struct hint));
-
- log_debug("add hint %s %d:%d %s from pvs_online", file_hint.pvid, file_major, file_minor, file_vgname);
- dm_list_add(&hints_list, &alloc_hint->list);
- found++;
- }
-
- if (closedir(dir))
- stack;
-
- log_debug("accept hints found %d from pvs_online", found);
-
- _get_single_vgname_cmd_arg(cmd, &hints_list, &vgname);
-
- /*
- * apply_hints equivalent, move devs from devs_in to devs_out if
- * their devno matches the devno of a hint (and if the hint matches
- * the vgname when a vgname is present.)
- */
- dm_list_iterate_items_safe(devl, devl2, devs_in) {
- dm_list_iterate_items_safe(hint, hint2, &hints_list) {
- if ((MAJOR(devl->dev->dev) == MAJOR(hint->devt)) &&
- (MINOR(devl->dev->dev) == MINOR(hint->devt))) {
-
- if (vgname && hint->vgname[0] && strcmp(vgname, hint->vgname))
- goto next_dev;
-
- snprintf(hint->name, sizeof(hint->name), "%s", dev_name(devl->dev));
- hint->chosen = 1;
-
- dm_list_del(&devl->list);
- dm_list_add(devs_out, &devl->list);
- }
- }
- next_dev:
- ;
- }
-
- log_debug("applied hints using %d other %d vgname %s from pvs_online",
- dm_list_size(devs_out), dm_list_size(devs_in), vgname ?: "");
-
- dm_list_splice(hints_out, &hints_list);
-
- free(vgname);
-
- return 1;
-}
-
/*
* Returns 0: no hints are used.
* . newhints is set if this command should create new hints after scan
*newhints = NEWHINTS_NONE;
/* No commands are using hints. */
- if (!cmd->enable_hints && !cmd->hints_pvs_online)
+ if (!cmd->enable_hints)
return 0;
/*
if (!cmd->use_hints)
return 0;
- /*
- * enable_hints is 0 for the special hints=pvs_online
- * and by lvm.conf hints="none" does not disable hints=pvs_online.
- * hints=pvs_online can be disabled with --nohints.
- */
- if (cmd->hints_pvs_online) {
- if (!_get_hints_from_pvs_online(cmd, &hints_list, devs_in, devs_out)) {
- log_debug("get_hints: pvs_online failed");
- return 0;
- }
- return 1;
- }
-
/*
* Check if another command created the nohints file to prevent us from
* using hints.
* us which devs are PVs. We might want to enable this optimization
* separately.)
*/
- _get_single_vgname_cmd_arg(cmd, &hints_list, &vgname);
+ get_single_vgname_cmd_arg(cmd, &hints_list, &vgname);
_apply_hints(cmd, &hints_list, vgname, devs_in, devs_out);
void pvscan_recreate_hints_begin(struct cmd_context *cmd);
+void get_single_vgname_cmd_arg(struct cmd_context *cmd,
+ struct dm_list *hints, char **vgname);
+
#endif
#include "lib/metadata/metadata.h"
#include "lib/format_text/layout.h"
#include "lib/device/device_id.h"
+#include "lib/device/online.h"
#include <sys/stat.h>
#include <fcntl.h>
return ret;
}
+/*
+ * Use files under /run/lvm/, created by pvscan --cache autoactivation,
+ * to optimize device setup/scanning for a command that is run for a
+ * specific vg name. autoactivation happens during system startup
+ * when the hints file is not useful, so this uses the online files as
+ * an alternative.
+ */
+
+int label_scan_vg_online(struct cmd_context *cmd, const char *vgname)
+{
+ struct dm_list pvs_online;
+ struct dm_list devs;
+ struct pv_online *po;
+ struct device_list *devl, *devl2;
+ int relax_deviceid_filter = 0;
+ int metadata_pv_count;
+
+ dm_list_init(&pvs_online);
+ dm_list_init(&devs);
+
+ /* reads devices file, does not populate dev-cache */
+ if (!setup_devices_for_online_autoactivation(cmd))
+ return 0;
+
+ /*
+ * First attempt to use /run/lvm/pvs_lookup/vgname which should be
+ * used in cases where all PVs in a VG do not contain metadata.
+ * When the pvs_lookup file does not exist, then simply use all
+ * /run/lvm/pvs_online/pvid files that contain a matching vgname.
+ * The list of po structs represents the PVs in the VG, and the
+ * info from the online files tell us which devices those PVs are
+ * located on.
+ */
+ if (!get_pvs_lookup(&pvs_online, vgname)) {
+ if (!get_pvs_online(&pvs_online, vgname))
+ goto bad;
+ }
+
+ /* for each po devno add a struct dev to dev-cache */
+
+ dm_list_iterate_items(po, &pvs_online) {
+ if (!setup_devno_in_dev_cache(cmd, po->devno)) {
+ log_error("No device set up for %d:%d PVID %s",
+ (int)MAJOR(po->devno), (int)MINOR(po->devno), po->pvid);
+ goto bad;
+ }
+
+ if (!(po->dev = dev_cache_get_by_devt(cmd, po->devno, NULL, NULL))) {
+ log_error("No device found for %d:%d PVID %s",
+ (int)MAJOR(po->devno), (int)MINOR(po->devno), po->pvid);
+ goto bad;
+ }
+
+ if (!(devl = dm_pool_zalloc(cmd->mem, sizeof(*devl))))
+ goto_bad;
+
+ devl->dev = po->dev;
+ dm_list_add(&devs, &devl->list);
+ }
+
+ /*
+ * factor code common to pvscan_cache_args
+ */
+
+ if (cmd->enable_devices_file) {
+ dm_list_iterate_items(devl, &devs)
+ device_ids_match_dev(cmd, devl->dev);
+ }
+
+ if (cmd->enable_devices_list)
+ device_ids_match_device_list(cmd);
+
+ if (cmd->enable_devices_file && device_ids_use_devname(cmd)) {
+ relax_deviceid_filter = 1;
+ cmd->filter_deviceid_skip = 1;
+ }
+
+ cmd->filter_nodata_only = 1;
+
+ dm_list_iterate_items_safe(devl, devl2, &devs) {
+ if (!cmd->filter->passes_filter(cmd, cmd->filter, devl->dev, NULL)) {
+ log_print("%s excluded by filters: %s.",
+ dev_name(devl->dev), dev_filtered_reason(devl->dev));
+ dm_list_del(&devl->list);
+ }
+ }
+
+ cmd->filter_nodata_only = 0;
+
+ /*
+ * Clear the results of nodata filters that were saved by the
+ * persistent filter so that the complete set of filters will
+ * be checked by passes_filter below.
+ */
+ dm_list_iterate_items(devl, &devs)
+ cmd->filter->wipe(cmd, cmd->filter, devl->dev, NULL);
+
+ /*
+ * Read header from each dev.
+ * Eliminate non-lvm devs.
+ * Apply all filters.
+ */
+
+ log_debug("label_scan_vg_online: read and filter devs");
+
+ label_scan_setup_bcache();
+
+ dm_list_iterate_items_safe(devl, devl2, &devs) {
+ int has_pvid;
+
+ if (!label_read_pvid(devl->dev, &has_pvid)) {
+ log_print("%s cannot read label.", dev_name(devl->dev));
+ dm_list_del(&devl->list);
+ continue;
+ }
+
+ if (!has_pvid) {
+ /* Not an lvm device */
+ log_print("%s not an lvm device.", dev_name(devl->dev));
+ dm_list_del(&devl->list);
+ continue;
+ }
+
+ /*
+ * filter-deviceid is not being used because of unstable devnames,
+ * so in place of that check if the pvid is in the devices file.
+ */
+ if (relax_deviceid_filter) {
+ if (!get_du_for_pvid(cmd, devl->dev->pvid)) {
+ log_print("%s excluded by devices file (checking PVID).",
+ dev_name(devl->dev));
+ dm_list_del(&devl->list);
+ continue;
+ }
+ }
+
+ /* Applies all filters, including those that need data from dev. */
+ if (!cmd->filter->passes_filter(cmd, cmd->filter, devl->dev, NULL)) {
+ log_print("%s excluded by filters: %s.",
+ dev_name(devl->dev), dev_filtered_reason(devl->dev));
+ dm_list_del(&devl->list);
+ }
+ }
+
+ if (relax_deviceid_filter)
+ cmd->filter_deviceid_skip = 0;
+
+ free_po_list(&pvs_online);
+
+ if (dm_list_empty(&devs))
+ return 1;
+
+ /*
+ * Scan devs to populate lvmcache info, which includes the mda info that's
+ * needed to read vg metadata.
+ * bcache data from label_read_pvid above is not invalidated so it can
+ * be reused (more data may need to be read depending on how much of the
+ * metadata was covered when reading the pvid.)
+ */
+ _scan_list(cmd, NULL, &devs, 0, NULL);
+
+ /*
+ * Check if all PVs from the VG were found after scanning the devs
+ * produced from the online files. The online files are effectively
+ * hints that usually work, but are not definitive, so we need to
+ * be able to fall back to a standard label scan if the online hints
+ * gave fewer PVs than listed in VG metadata.
+ */
+ metadata_pv_count = lvmcache_pvsummary_count(vgname);
+ if (metadata_pv_count != dm_list_size(&devs)) {
+ log_debug("Incorrect PV list from online files %d metadata %d.",
+ dm_list_size(&devs), metadata_pv_count);
+ return 0;
+ }
+
+ return 1;
+bad:
+ free_po_list(&pvs_online);
+ return 0;
+}
+
/*
* Scan devices on the system to discover which are LVM devices.
* Info about the LVM devices (PVs) is saved in lvmcache in a
int label_scan_open_rw(struct device *dev);
int label_scan_reopen_rw(struct device *dev);
int label_read_pvid(struct device *dev, int *has_pvid);
+int label_scan_vg_online(struct cmd_context *cmd, const char *vgname);
int label_scan_for_pvid(struct cmd_context *cmd, char *pvid, struct device **dev_out);
aux udev_wait
wait_lvm_activate $vg4
+ls "$RUNDIR/lvm/pvs_lookup/"
+cat "$RUNDIR/lvm/pvs_lookup/$vg4" || true
ls "$RUNDIR/lvm/pvs_online/$PVID1"
ls "$RUNDIR/lvm/pvs_online/$PVID2"
ls "$RUNDIR/lvm/pvs_online/$PVID3"
mdadm --create --metadata=1.0 "$mddev" --level 1 --chunk=64 --raid-devices=2 "$dev1" "$dev2"
wait_md_create "$mddev"
vgcreate $vg9 "$mddev"
+lvmdevices --adddev "$mddev" || true
PVIDMD=`pvs $mddev --noheading -o uuid | tr -d - | awk '{print $1}'`
BDEVMD=$(basename "$mddev")
if (!strcmp(hint_mode, "none")) {
cmd->enable_hints = 0;
cmd->use_hints = 0;
- } else if (!strcmp(hint_mode, "pvs_online")) {
- cmd->hints_pvs_online = 1;
}
}
* Does not do dev_cache_scan (adds nothing to dev-cache), and
* does not do any device id matching.
*/
- if (!setup_devices_for_pvscan_cache(cmd)) {
+ if (!setup_devices_for_online_autoactivation(cmd)) {
log_error_pvscan(cmd, "Failed to set up devices.");
return 0;
}
#include "tools.h"
#include "lib/device/device_id.h"
+#include "lib/label/hints.h"
struct vgchange_params {
int lock_start_count;
vp->vg_complete_to_activate = 1;
- if (!arg_is_set(cmd, nohints_ARG))
- cmd->hints_pvs_online = 1;
- else
- cmd->use_hints = 0;
+ cmd->use_hints = 0;
return 1;
}
{
struct vgchange_params vp = { 0 };
struct processing_handle *handle;
+ char *vgname = NULL;
uint32_t flags = 0;
int ret;
return ECMD_FAILED;
if (skip_command)
return ECMD_PROCESSED;
+
+ /*
+ * Special label scan optimized for autoactivation
+ * that is based on info read from /run/lvm/ files
+ * created by pvscan --cache during autoactivation.
+ * (Add an option to disable this optimization?)
+ */
+ get_single_vgname_cmd_arg(cmd, NULL, &vgname);
+ if (vgname) {
+ if (!label_scan_vg_online(cmd, vgname))
+ log_debug("Standard label_scan required in place of online scan.");
+ else
+ flags |= PROCESS_SKIP_SCAN;
+ }
}
if (update)