mirror of
git://sourceware.org/git/lvm2.git
synced 2024-12-21 13:34:40 +03:00
Reread global state the lvmetad copy is stale
This is the client side handling of the global_invalid state added to lvmetad in commit c595b50cec8a6b95c6ac4988912d1412f3cc0237. The function added here: . checks if the global state in lvmetad is invalid . if so, scans disks to update the state in lvmetad . clears the global_invalid flag in lvmetad . updates the local udev db to reflect any changes
This commit is contained in:
parent
679b6b5b29
commit
c23e7ff2a0
296
lib/cache/lvmetad.c
vendored
296
lib/cache/lvmetad.c
vendored
@ -1283,3 +1283,299 @@ int lvmetad_vg_clear_outdated_pvs(struct volume_group *vg)
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Records the state of cached PVs in lvmetad so we can look for changes
|
||||||
|
* after rescanning.
|
||||||
|
*/
|
||||||
|
struct pv_cache_list {
|
||||||
|
struct dm_list list;
|
||||||
|
dev_t devt;
|
||||||
|
struct id pvid;
|
||||||
|
const char *vgid;
|
||||||
|
unsigned found : 1;
|
||||||
|
unsigned update_udev : 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the list of PVs known to lvmetad.
|
||||||
|
*/
|
||||||
|
static int _lvmetad_get_pv_cache_list(struct cmd_context *cmd, struct dm_list *pvc_list)
|
||||||
|
{
|
||||||
|
daemon_reply reply;
|
||||||
|
struct dm_config_node *cn;
|
||||||
|
struct pv_cache_list *pvcl;
|
||||||
|
const char *pvid_txt;
|
||||||
|
const char *vgid;
|
||||||
|
|
||||||
|
if (!lvmetad_active())
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
log_debug_lvmetad("Asking lvmetad for complete list of known PVs");
|
||||||
|
reply = _lvmetad_send("pv_list", NULL);
|
||||||
|
if (!_lvmetad_handle_reply(reply, "list PVs", "", NULL)) {
|
||||||
|
log_error("lvmetad message failed.");
|
||||||
|
daemon_reply_destroy(reply);
|
||||||
|
return_0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((cn = dm_config_find_node(reply.cft->root, "physical_volumes"))) {
|
||||||
|
for (cn = cn->child; cn; cn = cn->sib) {
|
||||||
|
if (!(pvcl = dm_pool_zalloc(cmd->mem, sizeof(*pvcl)))) {
|
||||||
|
log_error("pv_cache_list allocation failed.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pvid_txt = cn->key;
|
||||||
|
if (!id_read_format(&pvcl->pvid, pvid_txt)) {
|
||||||
|
stack;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
pvcl->devt = dm_config_find_int(cn->child, "device", 0);
|
||||||
|
|
||||||
|
if ((vgid = dm_config_find_str(cn->child, "vgid", NULL)))
|
||||||
|
pvcl->vgid = dm_pool_strdup(cmd->mem, vgid);
|
||||||
|
|
||||||
|
dm_list_add(pvc_list, &pvcl->list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
daemon_reply_destroy(reply);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Opening the device RDWR should trigger a udev db update.
|
||||||
|
* FIXME: is there a better way to update the udev db than
|
||||||
|
* doing an open/close of the device?
|
||||||
|
*/
|
||||||
|
static void _update_pv_in_udev(struct cmd_context *cmd, dev_t devt)
|
||||||
|
{
|
||||||
|
struct device *dev;
|
||||||
|
|
||||||
|
log_debug_devs("device %d:%d open to update udev",
|
||||||
|
(int)MAJOR(devt), (int)MINOR(devt));
|
||||||
|
|
||||||
|
if (!(dev = dev_cache_get_by_devt(devt, cmd->lvmetad_filter))) {
|
||||||
|
log_error("_update_pv_in_udev no dev found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dev_open(dev))
|
||||||
|
return;
|
||||||
|
dev_close(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Compare before and after PV lists from before/after rescanning,
|
||||||
|
* and update udev db for changes.
|
||||||
|
*
|
||||||
|
* For PVs that have changed pvid or vgid in lvmetad from rescanning,
|
||||||
|
* there may be information in the udev database to update, so open
|
||||||
|
* these devices to trigger a udev update.
|
||||||
|
*
|
||||||
|
* "before" refers to the list of pvs from lvmetad before rescanning
|
||||||
|
* "after" refers to the list of pvs from lvmetad after rescanning
|
||||||
|
*
|
||||||
|
* Comparing both lists, we can see which PVs changed (pvid or vgid),
|
||||||
|
* and trigger a udev db update for those.
|
||||||
|
*/
|
||||||
|
static void _update_changed_pvs_in_udev(struct cmd_context *cmd,
|
||||||
|
struct dm_list *pvc_before,
|
||||||
|
struct dm_list *pvc_after)
|
||||||
|
{
|
||||||
|
struct pv_cache_list *before;
|
||||||
|
struct pv_cache_list *after;
|
||||||
|
char id_before[ID_LEN + 1] __attribute__((aligned(8)));
|
||||||
|
char id_after[ID_LEN + 1] __attribute__((aligned(8)));
|
||||||
|
int found;
|
||||||
|
|
||||||
|
dm_list_iterate_items(before, pvc_before) {
|
||||||
|
found = 0;
|
||||||
|
|
||||||
|
dm_list_iterate_items(after, pvc_after) {
|
||||||
|
if (after->found)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (before->devt != after->devt)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!id_equal(&before->pvid, &after->pvid)) {
|
||||||
|
memset(id_before, 0, sizeof(id_before));
|
||||||
|
memset(id_after, 0, sizeof(id_after));
|
||||||
|
strncpy(&id_before[0], (char *) &before->pvid, sizeof(id_before) - 1);
|
||||||
|
strncpy(&id_after[0], (char *) &after->pvid, sizeof(id_after) - 1);
|
||||||
|
|
||||||
|
log_debug_devs("device %d:%d changed pvid from %s to %s",
|
||||||
|
(int)MAJOR(before->devt), (int)MINOR(before->devt),
|
||||||
|
id_before, id_after);
|
||||||
|
|
||||||
|
before->update_udev = 1;
|
||||||
|
|
||||||
|
} else if ((before->vgid && !after->vgid) ||
|
||||||
|
(after->vgid && !before->vgid) ||
|
||||||
|
(before->vgid && after->vgid && strcmp(before->vgid, after->vgid))) {
|
||||||
|
|
||||||
|
log_debug_devs("device %d:%d changed vg from %s to %s",
|
||||||
|
(int)MAJOR(before->devt), (int)MINOR(before->devt),
|
||||||
|
before->vgid ?: "none", after->vgid ?: "none");
|
||||||
|
|
||||||
|
before->update_udev = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
after->found = 1;
|
||||||
|
before->found = 1;
|
||||||
|
found = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
memset(id_before, 0, sizeof(id_before));
|
||||||
|
strncpy(&id_before[0], (char *) &before->pvid, sizeof(id_before) - 1);
|
||||||
|
|
||||||
|
log_debug_devs("device %d:%d pvid %s vg %s is gone",
|
||||||
|
(int)MAJOR(before->devt), (int)MINOR(before->devt),
|
||||||
|
id_before, before->vgid ? before->vgid : "none");
|
||||||
|
|
||||||
|
before->update_udev = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dm_list_iterate_items(before, pvc_before) {
|
||||||
|
if (before->update_udev)
|
||||||
|
_update_pv_in_udev(cmd, before->devt);
|
||||||
|
}
|
||||||
|
|
||||||
|
dm_list_iterate_items(after, pvc_after) {
|
||||||
|
if (after->update_udev)
|
||||||
|
_update_pv_in_udev(cmd, after->devt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Before this command was run, some external entity may have
|
||||||
|
* invalidated lvmetad's cache of global information, e.g. lvmlockd.
|
||||||
|
*
|
||||||
|
* The global information includes things like a new VG, a
|
||||||
|
* VG that was removed, the assignment of a PV to a VG;
|
||||||
|
* any change that is not isolated within a single VG.
|
||||||
|
*
|
||||||
|
* The external entity, like a lock manager, would invalidate
|
||||||
|
* the lvmetad global cache if it detected that the global
|
||||||
|
* information had been changed on disk by something other
|
||||||
|
* than a local lvm command, e.g. an lvm command on another
|
||||||
|
* host with access to the same devices. (How it detects
|
||||||
|
* the change is specific to lock manager or other entity.)
|
||||||
|
*
|
||||||
|
* The effect is that metadata on disk is newer than the metadata
|
||||||
|
* in the local lvmetad daemon, and the local lvmetad's cache
|
||||||
|
* should be updated from disk before this command uses it.
|
||||||
|
*
|
||||||
|
* So, using this function, a command checks if lvmetad's global
|
||||||
|
* cache is valid. If so, it does nothing. If not, it rescans
|
||||||
|
* devices to update the lvmetad cache, then it notifies lvmetad
|
||||||
|
* that it's cache is valid again (consistent with what's on disk.)
|
||||||
|
* This command can then go ahead and use the newly refreshed metadata.
|
||||||
|
*
|
||||||
|
* 1. Check if the lvmetad global cache is invalid.
|
||||||
|
* 2. If so, reread metadata from all devices and update the lvmetad cache.
|
||||||
|
* 3. Tell lvmetad that the global cache is now valid.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void lvmetad_validate_global_cache(struct cmd_context *cmd, int force)
|
||||||
|
{
|
||||||
|
struct dm_list pvc_before; /* pv_cache_list */
|
||||||
|
struct dm_list pvc_after; /* pv_cache_list */
|
||||||
|
daemon_reply reply;
|
||||||
|
int global_invalid;
|
||||||
|
|
||||||
|
dm_list_init(&pvc_before);
|
||||||
|
dm_list_init(&pvc_after);
|
||||||
|
|
||||||
|
if (!lvmetad_used())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (force)
|
||||||
|
goto do_scan;
|
||||||
|
|
||||||
|
reply = daemon_send_simple(_lvmetad, "get_global_info",
|
||||||
|
"token = %s", "skip",
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
if (reply.error) {
|
||||||
|
log_error("lvmetad_validate_global_cache get_global_info error %d", reply.error);
|
||||||
|
goto do_scan;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(daemon_reply_str(reply, "response", ""), "OK")) {
|
||||||
|
log_error("lvmetad_validate_global_cache get_global_info not ok");
|
||||||
|
goto do_scan;
|
||||||
|
}
|
||||||
|
|
||||||
|
global_invalid = daemon_reply_int(reply, "global_invalid", -1);
|
||||||
|
|
||||||
|
daemon_reply_destroy(reply);
|
||||||
|
|
||||||
|
if (!global_invalid) {
|
||||||
|
/* cache is valid */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
do_scan:
|
||||||
|
/*
|
||||||
|
* Save the current state of pvs from lvmetad so after devices are
|
||||||
|
* scanned, we can compare to the new state to see if pvs changed.
|
||||||
|
*/
|
||||||
|
_lvmetad_get_pv_cache_list(cmd, &pvc_before);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Update the local lvmetad cache so it correctly reflects any
|
||||||
|
* changes made on remote hosts.
|
||||||
|
*/
|
||||||
|
lvmetad_pvscan_all_devs(cmd, NULL);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Clear the global_invalid flag in lvmetad.
|
||||||
|
* Subsequent local commands that read global state
|
||||||
|
* from lvmetad will not see global_invalid until
|
||||||
|
* another host makes another global change.
|
||||||
|
*/
|
||||||
|
reply = daemon_send_simple(_lvmetad, "set_global_info",
|
||||||
|
"token = %s", "skip",
|
||||||
|
"global_invalid = %d", 0,
|
||||||
|
NULL);
|
||||||
|
if (reply.error)
|
||||||
|
log_error("lvmetad_validate_global_cache set_global_info error %d", reply.error);
|
||||||
|
|
||||||
|
if (strcmp(daemon_reply_str(reply, "response", ""), "OK"))
|
||||||
|
log_error("lvmetad_validate_global_cache set_global_info not ok");
|
||||||
|
|
||||||
|
daemon_reply_destroy(reply);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Populate this command's lvmcache structures from lvmetad.
|
||||||
|
*/
|
||||||
|
lvmcache_seed_infos_from_lvmetad(cmd);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Update the local udev database to reflect PV changes from
|
||||||
|
* other hosts.
|
||||||
|
*
|
||||||
|
* Compare the before and after PV lists, and if a PV's
|
||||||
|
* pvid or vgid has changed, then open that device to trigger
|
||||||
|
* a uevent to update the udev db.
|
||||||
|
*
|
||||||
|
* This has no direct benefit to lvm, but is just a best effort
|
||||||
|
* attempt to keep the udev db updated and reflecting current
|
||||||
|
* lvm information.
|
||||||
|
*
|
||||||
|
* FIXME: lvmcache_seed_infos_from_lvmetad() and _lvmetad_get_pv_cache_list()
|
||||||
|
* each get pv_list from lvmetad, and they could share a single pv_list reply.
|
||||||
|
*/
|
||||||
|
if (!dm_list_empty(&pvc_before)) {
|
||||||
|
_lvmetad_get_pv_cache_list(cmd, &pvc_after);
|
||||||
|
_update_changed_pvs_in_udev(cmd, &pvc_before, &pvc_after);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
2
lib/cache/lvmetad.h
vendored
2
lib/cache/lvmetad.h
vendored
@ -167,6 +167,7 @@ int lvmetad_pvscan_all_devs(struct cmd_context *cmd, activation_handler handler)
|
|||||||
int lvmetad_pvscan_foreign_vgs(struct cmd_context *cmd, activation_handler handler);
|
int lvmetad_pvscan_foreign_vgs(struct cmd_context *cmd, activation_handler handler);
|
||||||
|
|
||||||
int lvmetad_vg_clear_outdated_pvs(struct volume_group *vg);
|
int lvmetad_vg_clear_outdated_pvs(struct volume_group *vg);
|
||||||
|
void lvmetad_validate_global_cache(struct cmd_context *cmd, int force);
|
||||||
|
|
||||||
# else /* LVMETAD_SUPPORT */
|
# else /* LVMETAD_SUPPORT */
|
||||||
|
|
||||||
@ -195,6 +196,7 @@ int lvmetad_vg_clear_outdated_pvs(struct volume_group *vg);
|
|||||||
# define lvmetad_pvscan_all_devs(cmd, handler) (0)
|
# define lvmetad_pvscan_all_devs(cmd, handler) (0)
|
||||||
# define lvmetad_pvscan_foreign_vgs(cmd, handler) (0)
|
# define lvmetad_pvscan_foreign_vgs(cmd, handler) (0)
|
||||||
# define lvmetad_vg_clear_outdated_pvs(vg) (1)
|
# define lvmetad_vg_clear_outdated_pvs(vg) (1)
|
||||||
|
# define lvmetad_validate_global_cache(cmd, force) do { } while (0)
|
||||||
|
|
||||||
# endif /* LVMETAD_SUPPORT */
|
# endif /* LVMETAD_SUPPORT */
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user