[lvm-devel] [RFC 6/6] lvconvert: implement the wait policy

Lidong Zhong lzhong at suse.com
Mon Jun 8 07:48:32 UTC 2015


Based on udev, we wait for the missing device in a fixed time, otherwise
it will work according to the original policy set.
---
 tools/lvconvert.c | 282 +++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 271 insertions(+), 11 deletions(-)

diff --git a/tools/lvconvert.c b/tools/lvconvert.c
index 8fcbefd..47564de 100644
--- a/tools/lvconvert.c
+++ b/tools/lvconvert.c
@@ -16,6 +16,9 @@
 #include "polldaemon.h"
 #include "lv_alloc.h"
 #include "lvconvert_poll.h"
+#include "lvm-wrappers.h"
+#include <blkid/blkid.h>
+#include <libudev.h>
 
 struct lvconvert_params {
 	int cache;
@@ -925,6 +928,13 @@ static struct logical_volume *_original_lv(struct logical_volume *lv)
 	return next_lv;
 }
 
+/*
+ * replace_log/replace_mirrors value
+ * 0: remove
+ * 1: replace
+ * 2: wait+remove
+ * 3: wait+replace
+ */
 static void _lvconvert_mirrors_repair_ask(struct cmd_context *cmd,
 					  int failed_log, int failed_mirrors,
 					  int *replace_log, int *replace_mirrors)
@@ -932,12 +942,20 @@ static void _lvconvert_mirrors_repair_ask(struct cmd_context *cmd,
 	const char *leg_policy, *log_policy;
 	int force = arg_count(cmd, force_ARG);
 	int yes = arg_count(cmd, yes_ARG);
+	int keep_log = 0;
 
 	if (arg_count(cmd, use_policies_ARG)) {
 		leg_policy = find_config_tree_str(cmd, activation_mirror_image_fault_policy_CFG, NULL);
 		log_policy = find_config_tree_str(cmd, activation_mirror_log_fault_policy_CFG, NULL);
 		*replace_mirrors = strcmp(leg_policy, "remove");
 		*replace_log = strcmp(log_policy, "remove");
+		keep_log = find_config_tree_int(cmd, activation_mirror_keep_log_CFG, NULL);
+		*replace_mirrors = !!strcmp(leg_policy, "remove");
+		*replace_log = !!strcmp(log_policy, "remove");
+		if (keep_log) {
+			*replace_mirrors += 2;
+			*replace_log += 2;
+		}
 		return;
 	}
 
@@ -1004,6 +1022,193 @@ static int remove_device_from_waiting_dir(struct physical_volume *pv)
 	return r;
 }
 
+static int get_a_missing_device(struct logical_volume *lv, struct physical_volume **pv)
+{
+	struct lv_segment *lvseg;
+	unsigned s;
+	int r = 0;
+
+
+	dm_list_iterate_items(lvseg, &lv->segments) {
+		for (s = 0; s < lvseg->area_count; s++) {
+			if (seg_type(lvseg, s) == AREA_LV) {
+				if (is_temporary_mirror_layer(seg_lv(lvseg, s)))
+					return get_a_missing_device(seg_lv(lvseg, s), pv);
+				else if (seg_lv(lvseg, s)->status & PARTIAL_LV)
+					return get_a_missing_device(seg_lv(lvseg, s), pv);
+			}
+			else if (seg_type(lvseg, s) == AREA_PV &&
+				 is_missing_pv(seg_pv(lvseg, s))) {
+				if (already_being_waited(seg_pv(lvseg, s)))
+						continue;
+				*pv = seg_pv(lvseg, s);
+				r = 1;
+				break;
+			}
+		}
+		if (r == 1)
+			break;
+	}
+	return r;
+}
+
+static int missing_device_return(struct logical_volume *lv, struct physical_volume **pv,
+	            	dev_t *old, dev_t *new, int wait_time)
+{
+	struct udev_monitor *mon;
+	struct udev_device *device;
+	const char *new_uuid, *dev_nod;
+	struct udev *udev;
+	char uuid[64] __attribute__((aligned(8)));
+	blkid_probe pr;
+	int fd;
+	fd_set fds;
+	struct timeval tv;
+	int r = 0;
+
+	r = get_a_missing_device(lv, pv);
+	if (!r) {
+		log_error("Failed to get the missing pv");
+		goto bad;
+	}
+	/*
+	 * How to get the missing device dev_t?
+	 */
+	*old = (*pv)->old_dev;
+	if (!*old) {
+		log_error("Failed to get the dev_t of missing pv");
+		goto bad;
+	}
+
+	if (!id_write_format((&(*pv)->id), uuid, sizeof(uuid))) {
+		stack;
+		r = 0;
+		goto bad;
+	}
+
+	if (!(udev = udev_new())) {
+		r = 0;
+		goto bad;
+	}
+
+	mon = udev_monitor_new_from_netlink(udev, "udev");
+	udev_monitor_filter_add_match_subsystem_devtype(mon, "block", NULL);
+	udev_monitor_enable_receiving(mon);
+	fd = udev_monitor_get_fd(mon);
+
+	tv.tv_sec = wait_time;
+	tv.tv_usec = 0;
+	FD_ZERO(&fds);
+	FD_SET(fd, &fds);
+	while(1) {
+		r = select(fd+1, &fds, NULL, NULL, &tv);
+		if (r > 0 && FD_ISSET(fd, &fds)) {
+			/*
+			 * check if it's the missing device
+			 */
+			device = udev_monitor_receive_device(mon);
+			if (device) {
+				dev_nod = udev_device_get_devnode(device);
+				if (NULL == dev_nod) {
+					continue;
+				}
+				pr = blkid_new_probe_from_filename(dev_nod);
+				if (!pr) {
+					log_error("Failed to open %s",dev_nod);
+					goto out;
+				}
+				blkid_do_probe(pr);
+				blkid_probe_lookup_value(pr, "UUID", &new_uuid, NULL);
+
+				if (!strncmp(new_uuid, uuid, sizeof(uuid))) {
+					*new = udev_device_get_devnum(device);
+					r = 1;
+					break;
+				}
+				blkid_free_probe(pr);
+				udev_device_unref(device);
+			} else {
+				log_error("No device from udev_monitor_receive_device.");
+				goto out;
+			}
+		}
+		/*
+		 * timeout while waiting the missing device
+		 */
+		else if (r == 0) {
+			break;
+		}
+	}
+
+out:
+	udev_unref(udev);
+bad:
+	if(*pv)
+		remove_device_from_waiting_dir(*pv);
+	return r;
+}
+
+static int start_sync_lv(struct logical_volume *lv)
+{
+	char target[70];
+	int r = 1;
+
+	sprintf(target,"%s-%s", lv->vg->name, lv->name);
+
+	if (!dm_lv_simple_suspend(target) ||
+			!dm_lv_simple_resume(target)) {
+		r = 0;
+		log_error("Failed to suspend/resume %s", lv->name);
+	}
+
+	return r;
+}
+
+static int lv_replace_table(struct cmd_context *cmd, struct logical_volume *lv,
+		         struct physical_volume *pv, dev_t old, dev_t new)
+{
+	struct logical_volume *sub_lv = NULL;
+	struct lv_segment *lvseg;
+	char target[70];
+	unsigned s;
+	int r = 0;
+
+	/*check which lv is on the missing pv*/
+	dm_list_iterate_items(lvseg, &lv->segments) {
+		for (s = 0; s < lvseg->area_count; s++) {
+			if ((seg_type(lvseg, s) == AREA_LV) &&
+			     (seg_lv(lvseg, s)->status & PARTIAL_LV)) {
+				sub_lv = seg_lv(lvseg, s);
+				if (lv_is_on_pv(sub_lv, pv)) {
+					r = 1;
+					break;
+				}
+			}
+		}
+		if (1 == r) {
+			r = 0;
+			break;
+		}
+	}
+
+	if (!sub_lv)
+		goto fail;
+
+	sprintf(target,"%s-%s", sub_lv->vg->name, sub_lv->name);
+	if (!dm_lv_simple_suspend(target))
+		goto fail;
+
+	if (!dm_replace_mirror_table(target, old, new))
+		goto fail;
+
+	if (!dm_lv_simple_resume(target))
+		goto fail;
+	r = 1;
+fail:
+	return r;
+}
+
+
 /*
  * _get_log_count
  * @lv: the mirror LV
@@ -1510,6 +1715,10 @@ static int _lvconvert_mirrors_repair(struct cmd_context *cmd,
 	int replace_logs = 0;
 	int replace_mimages = 0;
 	uint32_t log_count;
+	int wait_time = 0;
+	int daemon_mode = 0;
+	dev_t old, new;
+	struct physical_volume *pv = NULL;
 
 	uint32_t original_mimages = lv_mirror_count(lv);
 	uint32_t original_logs = _get_log_count(lv);
@@ -1525,28 +1734,79 @@ static int _lvconvert_mirrors_repair(struct cmd_context *cmd,
 		return 1;
 	}
 
+	/*
+	 * Count the failed log devices
+	 */
 	failed_mimages = _failed_mirrors_count(lv);
 	failed_logs = _failed_logs_count(lv);
 
-	if (!mirror_remove_missing(cmd, lv, 0))
-		return_0;
+	/*
+	 * Find out our policies
+	 */
+	_lvconvert_mirrors_repair_ask(cmd, failed_logs, failed_mimages,
+					  &replace_logs, &replace_mimages);
+
 
 	if (failed_mimages)
 		log_print_unless_silent("Mirror status: %d of %d images failed.",
 					failed_mimages, original_mimages);
 
-	/*
-	 * Count the failed log devices
-	 */
-	if (failed_logs)
+	if (failed_logs) {
 		log_print_unless_silent("Mirror log status: %d of %d images failed.",
 					failed_logs, original_logs);
+		if (replace_logs >= 2) {
+			log_error("The log device failed, there is probably no meaning to keep the bitmap."
+					"We make keep_log doesn't work on log devices");
+			replace_logs -= 2;
+		}
+	}
 
-	/*
-	 * Find out our policies
-	 */
-	_lvconvert_mirrors_repair_ask(cmd, failed_logs, failed_mimages,
-				      &replace_logs, &replace_mimages);
+	if (replace_mimages >= 2) {
+		if (!udev_is_running()) {
+			log_error("Udev is not running!This feature need support by udev");
+			goto feature_fail;
+		}
+		/*
+		 *  Here we make it a daemon to wait
+		 */
+		daemon_mode = become_daemon(cmd, 0);
+		if (daemon_mode == 0)
+			return ECMD_PROCESSED; /*CHECK: safe for parent to return directly*/
+		else if (daemon_mode != 1) {
+			log_error("Failed to become daemon and disable this feature");
+			goto feature_fail;
+		}
+		wait_time = find_config_tree_int(cmd, activation_mirror_keep_log_timeout_CFG, NULL);
+		log_print_unless_silent("This mirror LV will wait %d secs before"
+				"adopting new policy", wait_time);
+		if (missing_device_return(lv, &pv, &old, &new, wait_time)) {
+			/* Remove the MISSING flag*/
+			pv->status &= ~MISSING_PV;
+	
+			if (!pv) {
+				log_error("Failed to get the missing device");
+				goto feature_fail;
+			}
+			if ((MAJOR(old) == MAJOR(new)) && (MINOR(old) == MINOR(new)))
+				goto skip_replace;
+			if (!lv_replace_table(cmd, lv, pv, old, new)) {
+				log_error("Failed to replace table since major/minor changed");
+				goto feature_fail;
+			}
+skip_replace:
+			/*Do sync now*/
+			if (!start_sync_lv(lv)) {
+				log_error("Failed to start do the inremental sync");
+				goto feature_fail;
+			}
+			return 1;
+		}
+feature_fail:
+		replace_mimages -= 2;
+	}
+
+	if (!mirror_remove_missing(cmd, lv, 0))
+		return_0;
 
 	/*
 	 * Second phase - replace faulty devices
-- 
1.8.1.4




More information about the lvm-devel mailing list