Blame SOURCES/0102-RHBZ-631009-deferred-remove.patch

1eb31d
---
ecd2a9
 libmultipath/Makefile      |    6 +
ecd2a9
 libmultipath/config.c      |    3 
ecd2a9
 libmultipath/config.h      |    3 
1eb31d
 libmultipath/configure.c   |    1 
1eb31d
 libmultipath/defaults.h    |    1 
ecd2a9
 libmultipath/devmapper.c   |  142 ++++++++++++++++++++++++++++++++++++++++-----
ecd2a9
 libmultipath/devmapper.h   |   11 ++-
ecd2a9
 libmultipath/dict.c        |  116 ++++++++++++++++++++++++++++++++++++
ecd2a9
 libmultipath/propsel.c     |   28 ++++++++
1eb31d
 libmultipath/propsel.h     |    1 
1eb31d
 libmultipath/structs.h     |    8 ++
ecd2a9
 libmultipath/structs_vec.c |    3 
1eb31d
 multipath/multipath.conf.5 |   14 ++++
1eb31d
 multipathd/main.c          |   23 +++++--
ecd2a9
 14 files changed, 333 insertions(+), 27 deletions(-)
1eb31d
1eb31d
Index: multipath-tools-130222/libmultipath/config.c
1eb31d
===================================================================
1eb31d
--- multipath-tools-130222.orig/libmultipath/config.c
1eb31d
+++ multipath-tools-130222/libmultipath/config.c
1eb31d
@@ -337,6 +337,7 @@ merge_hwe (struct hwentry * dst, struct
1eb31d
 	merge_num(user_friendly_names);
1eb31d
 	merge_num(retain_hwhandler);
1eb31d
 	merge_num(detect_prio);
1eb31d
+	merge_num(deferred_remove);
1eb31d
 
1eb31d
 	/*
1eb31d
 	 * Make sure features is consistent with
1eb31d
@@ -394,6 +395,7 @@ overwrite_hwe (struct hwentry * dst, str
1eb31d
 	overwrite_num(user_friendly_names);
1eb31d
 	overwrite_num(retain_hwhandler);
1eb31d
 	overwrite_num(detect_prio);
1eb31d
+	overwrite_num(deferred_remove);
1eb31d
 
1eb31d
 	/*
1eb31d
 	 * Make sure features is consistent with
1eb31d
@@ -617,6 +619,7 @@ load_config (char * file, struct udev *u
1eb31d
 	conf->fast_io_fail = DEFAULT_FAST_IO_FAIL;
1eb31d
 	conf->retain_hwhandler = DEFAULT_RETAIN_HWHANDLER;
1eb31d
 	conf->detect_prio = DEFAULT_DETECT_PRIO;
1eb31d
+	conf->deferred_remove = DEFAULT_DEFERRED_REMOVE;
1eb31d
 	conf->hw_strmatch = 0;
1eb31d
 	conf->force_sync = 0;
1eb31d
 
1eb31d
Index: multipath-tools-130222/libmultipath/config.h
1eb31d
===================================================================
1eb31d
--- multipath-tools-130222.orig/libmultipath/config.h
1eb31d
+++ multipath-tools-130222/libmultipath/config.h
1eb31d
@@ -61,6 +61,7 @@ struct hwentry {
1eb31d
 	int user_friendly_names;
1eb31d
 	int retain_hwhandler;
1eb31d
 	int detect_prio;
1eb31d
+	int deferred_remove;
1eb31d
 	char * bl_product;
1eb31d
 };
1eb31d
 
1eb31d
@@ -84,6 +85,7 @@ struct mpentry {
1eb31d
 	int flush_on_last_del;
1eb31d
 	int attribute_flags;
1eb31d
 	int user_friendly_names;
1eb31d
+	int deferred_remove;
1eb31d
 	uid_t uid;
1eb31d
 	gid_t gid;
1eb31d
 	mode_t mode;
1eb31d
@@ -128,6 +130,7 @@ struct config {
1eb31d
 	int retain_hwhandler;
1eb31d
 	int detect_prio;
1eb31d
 	int force_sync;
1eb31d
+	int deferred_remove;
1eb31d
 	unsigned int version[3];
1eb31d
 
1eb31d
 	char * dev;
1eb31d
Index: multipath-tools-130222/libmultipath/configure.c
1eb31d
===================================================================
1eb31d
--- multipath-tools-130222.orig/libmultipath/configure.c
1eb31d
+++ multipath-tools-130222/libmultipath/configure.c
1eb31d
@@ -290,6 +290,7 @@ setup_map (struct multipath * mpp, char
1eb31d
 	select_dev_loss(mpp);
1eb31d
 	select_reservation_key(mpp);
1eb31d
 	select_retain_hwhandler(mpp);
1eb31d
+	select_deferred_remove(mpp);
1eb31d
 
1eb31d
 	sysfs_set_scsi_tmo(mpp);
1eb31d
 	/*
1eb31d
Index: multipath-tools-130222/libmultipath/defaults.h
1eb31d
===================================================================
1eb31d
--- multipath-tools-130222.orig/libmultipath/defaults.h
1eb31d
+++ multipath-tools-130222/libmultipath/defaults.h
1eb31d
@@ -19,6 +19,7 @@
1eb31d
 #define DEFAULT_FAST_IO_FAIL	5
1eb31d
 #define DEFAULT_RETAIN_HWHANDLER RETAIN_HWHANDLER_OFF
1eb31d
 #define DEFAULT_DETECT_PRIO DETECT_PRIO_OFF
1eb31d
+#define DEFAULT_DEFERRED_REMOVE DEFERRED_REMOVE_OFF
1eb31d
 
1eb31d
 #define DEFAULT_CHECKINT	5
1eb31d
 #define MAX_CHECKINT(a)		(a << 2)
1eb31d
Index: multipath-tools-130222/libmultipath/devmapper.c
1eb31d
===================================================================
1eb31d
--- multipath-tools-130222.orig/libmultipath/devmapper.c
1eb31d
+++ multipath-tools-130222/libmultipath/devmapper.c
ecd2a9
@@ -32,6 +32,8 @@
ecd2a9
 #define UUID_PREFIX "mpath-"
ecd2a9
 #define UUID_PREFIX_LEN 6
ecd2a9
 
ecd2a9
+static int dm_cancel_remove_partmaps(const char * mapname);
ecd2a9
+
ecd2a9
 #ifndef LIBDM_API_COOKIE
ecd2a9
 static inline int dm_task_set_cookie(struct dm_task *dmt, uint32_t *c, int a)
ecd2a9
 {
ecd2a9
@@ -103,7 +105,9 @@ dm_lib_prereq (void)
1eb31d
 {
1eb31d
 	char version[64];
1eb31d
 	int v[3];
1eb31d
-#if defined(DM_SUBSYSTEM_UDEV_FLAG0)
1eb31d
+#if defined(LIBDM_API_DEFERRED)
1eb31d
+	int minv[3] = {1, 2, 89};
1eb31d
+#elif defined(DM_SUBSYSTEM_UDEV_FLAG0)
1eb31d
 	int minv[3] = {1, 2, 82};
1eb31d
 #elif defined(LIBDM_API_COOKIE)
1eb31d
 	int minv[3] = {1, 2, 38};
ecd2a9
@@ -201,8 +205,10 @@ dm_prereq (void)
ecd2a9
 	return dm_drv_prereq();
1eb31d
 }
1eb31d
 
ecd2a9
+#define do_deferred(x) ((x) == DEFERRED_REMOVE_ON || (x) == DEFERRED_REMOVE_IN_PROGRESS)
ecd2a9
+
1eb31d
 static int
1eb31d
-dm_simplecmd (int task, const char *name, int no_flush, int need_sync, uint16_t udev_flags) {
1eb31d
+dm_simplecmd (int task, const char *name, int no_flush, int need_sync, uint16_t udev_flags, int deferred_remove) {
1eb31d
 	int r = 0;
1eb31d
 	int udev_wait_flag = (need_sync && (task == DM_DEVICE_RESUME ||
1eb31d
 					    task == DM_DEVICE_REMOVE));
ecd2a9
@@ -220,7 +226,10 @@ dm_simplecmd (int task, const char *name
1eb31d
 	if (no_flush)
1eb31d
 		dm_task_no_flush(dmt);		/* for DM_DEVICE_SUSPEND/RESUME */
1eb31d
 #endif
1eb31d
-
1eb31d
+#ifdef LIBDM_API_DEFERRED
ecd2a9
+	if (do_deferred(deferred_remove))
1eb31d
+		dm_task_deferred_remove(dmt);
1eb31d
+#endif
1eb31d
 	if (udev_wait_flag && !dm_task_set_cookie(dmt, &conf->cookie, ((conf->daemon)? DM_UDEV_DISABLE_LIBRARY_FALLBACK : 0) | udev_flags))
1eb31d
 		goto out;
1eb31d
 	r = dm_task_run (dmt);
ecd2a9
@@ -232,12 +241,18 @@ dm_simplecmd (int task, const char *name
1eb31d
 
1eb31d
 extern int
1eb31d
 dm_simplecmd_flush (int task, const char *name, int needsync, uint16_t udev_flags) {
1eb31d
-	return dm_simplecmd(task, name, 0, needsync, udev_flags);
1eb31d
+	return dm_simplecmd(task, name, 0, needsync, udev_flags, 0);
1eb31d
 }
1eb31d
 
1eb31d
 extern int
1eb31d
 dm_simplecmd_noflush (int task, const char *name, uint16_t udev_flags) {
1eb31d
-	return dm_simplecmd(task, name, 1, 1, udev_flags);
1eb31d
+	return dm_simplecmd(task, name, 1, 1, udev_flags, 0);
1eb31d
+}
1eb31d
+
ecd2a9
+static int
1eb31d
+dm_device_remove (const char *name, int needsync, int deferred_remove) {
1eb31d
+	return dm_simplecmd(DM_DEVICE_REMOVE, name, 0, needsync, 0,
1eb31d
+			    deferred_remove);
1eb31d
 }
1eb31d
 
1eb31d
 extern int
ecd2a9
@@ -653,7 +668,7 @@ out:
1eb31d
 }
1eb31d
 
1eb31d
 extern int
1eb31d
-_dm_flush_map (const char * mapname, int need_sync)
1eb31d
+_dm_flush_map (const char * mapname, int need_sync, int deferred_remove)
1eb31d
 {
1eb31d
 	int r;
1eb31d
 
ecd2a9
@@ -663,23 +678,46 @@ _dm_flush_map (const char * mapname, int
1eb31d
 	if (dm_type(mapname, TGT_MPATH) <= 0)
1eb31d
 		return 0; /* nothing to do */
1eb31d
 
1eb31d
-	if (dm_remove_partmaps(mapname, need_sync))
1eb31d
+	if (dm_remove_partmaps(mapname, need_sync, deferred_remove))
1eb31d
 		return 1;
1eb31d
 
1eb31d
-	if (dm_get_opencount(mapname)) {
ecd2a9
+	if (!do_deferred(deferred_remove) && dm_get_opencount(mapname)) {
1eb31d
 		condlog(2, "%s: map in use", mapname);
1eb31d
 		return 1;
1eb31d
 	}
1eb31d
 
1eb31d
-	r = dm_simplecmd_flush(DM_DEVICE_REMOVE, mapname, need_sync, 0);
1eb31d
+	r = dm_device_remove(mapname, need_sync, deferred_remove);
1eb31d
 
1eb31d
 	if (r) {
ecd2a9
+		if (do_deferred(deferred_remove) && dm_map_present(mapname)) {
1eb31d
+			condlog(4, "multipath map %s remove deferred",
1eb31d
+				mapname);
1eb31d
+			return 2;
1eb31d
+		}
1eb31d
 		condlog(4, "multipath map %s removed", mapname);
1eb31d
 		return 0;
1eb31d
 	}
1eb31d
 	return 1;
1eb31d
 }
1eb31d
 
1eb31d
+#ifdef LIBDM_API_DEFERRED
1eb31d
+
1eb31d
+int
1eb31d
+dm_flush_map_nopaths(const char * mapname, int deferred_remove)
1eb31d
+{
1eb31d
+	return _dm_flush_map(mapname, 1, deferred_remove);
1eb31d
+}
1eb31d
+
1eb31d
+#else
1eb31d
+
1eb31d
+int
1eb31d
+dm_flush_map_nopaths(const char * mapname, int deferred_remove)
1eb31d
+{
1eb31d
+	return _dm_flush_map(mapname, 1, 0);
1eb31d
+}
1eb31d
+
1eb31d
+#endif
1eb31d
+
1eb31d
 extern int
1eb31d
 dm_suspend_and_flush_map (const char * mapname)
1eb31d
 {
ecd2a9
@@ -1076,6 +1114,7 @@ out:
1eb31d
 
1eb31d
 struct remove_data {
1eb31d
 	int need_sync;
1eb31d
+	int deferred_remove;
1eb31d
 };
1eb31d
 
1eb31d
 static int
ecd2a9
@@ -1084,25 +1123,98 @@ remove_partmap(char *name, void *data)
1eb31d
 	struct remove_data *rd = (struct remove_data *)data;
1eb31d
 
1eb31d
 	if (dm_get_opencount(name)) {
1eb31d
-		dm_remove_partmaps(name, rd->need_sync);
1eb31d
-		if (dm_get_opencount(name)) {
1eb31d
+		dm_remove_partmaps(name, rd->need_sync, rd->deferred_remove);
ecd2a9
+		if (!do_deferred(rd->deferred_remove) &&
ecd2a9
+		    dm_get_opencount(name)) {
1eb31d
 			condlog(2, "%s: map in use", name);
1eb31d
 			return 1;
1eb31d
 		}
1eb31d
 	}
1eb31d
 	condlog(4, "partition map %s removed", name);
1eb31d
-	dm_simplecmd_flush(DM_DEVICE_REMOVE, name,
1eb31d
-			   rd->need_sync, 0);
1eb31d
+	dm_device_remove(name, rd->need_sync, rd->deferred_remove);
1eb31d
 	return 0;
1eb31d
 }
1eb31d
 
1eb31d
 int
1eb31d
-dm_remove_partmaps (const char * mapname, int need_sync)
1eb31d
+dm_remove_partmaps (const char * mapname, int need_sync, int deferred_remove)
1eb31d
 {
1eb31d
-	struct remove_data rd = { need_sync };
1eb31d
+	struct remove_data rd = { need_sync, deferred_remove };
1eb31d
 	return do_foreach_partmaps(mapname, remove_partmap, &rd);
1eb31d
 }
1eb31d
 
1eb31d
+#ifdef LIBDM_API_DEFERRED
1eb31d
+
1eb31d
+static int
1eb31d
+cancel_remove_partmap (char *name, void *unused)
1eb31d
+{
ecd2a9
+	if (dm_get_opencount(name))
ecd2a9
+		dm_cancel_remove_partmaps(name);
1eb31d
+	if (dm_message(name, "@cancel_deferred_remove") != 0)
1eb31d
+		condlog(0, "%s: can't cancel deferred remove: %s", name,
1eb31d
+			strerror(errno));
1eb31d
+	return 0;
1eb31d
+}
1eb31d
+
1eb31d
+static int
1eb31d
+dm_get_deferred_remove (char * mapname)
1eb31d
+{
1eb31d
+	int r = -1;
1eb31d
+	struct dm_task *dmt;
1eb31d
+	struct dm_info info;
1eb31d
+
1eb31d
+	if (!(dmt = dm_task_create(DM_DEVICE_INFO)))
1eb31d
+		return -1;
1eb31d
+
1eb31d
+	if (!dm_task_set_name(dmt, mapname))
1eb31d
+		goto out;
1eb31d
+
1eb31d
+	if (!dm_task_run(dmt))
1eb31d
+		goto out;
1eb31d
+
1eb31d
+	if (!dm_task_get_info(dmt, &info))
1eb31d
+		goto out;
1eb31d
+
1eb31d
+	r = info.deferred_remove;
1eb31d
+out:
1eb31d
+	dm_task_destroy(dmt);
1eb31d
+	return r;
1eb31d
+}
1eb31d
+
ecd2a9
+static int
ecd2a9
+dm_cancel_remove_partmaps(const char * mapname) {
ecd2a9
+	return do_foreach_partmaps(mapname, cancel_remove_partmap, NULL);
ecd2a9
+}
ecd2a9
+
1eb31d
+int
1eb31d
+dm_cancel_deferred_remove (struct multipath *mpp)
1eb31d
+{
1eb31d
+	int r = 0;
1eb31d
+
1eb31d
+	if (!dm_get_deferred_remove(mpp->alias))
1eb31d
+		return 0;
1eb31d
+	if (mpp->deferred_remove == DEFERRED_REMOVE_IN_PROGRESS)
1eb31d
+		mpp->deferred_remove = DEFERRED_REMOVE_ON;
1eb31d
+
ecd2a9
+	dm_cancel_remove_partmaps(mpp->alias);
1eb31d
+	r = dm_message(mpp->alias, "@cancel_deferred_remove");
1eb31d
+	if (r)
1eb31d
+		condlog(0, "%s: can't cancel deferred remove: %s", mpp->alias,
1eb31d
+			strerror(errno));
1eb31d
+	else
1eb31d
+		condlog(2, "%s: canceled deferred remove", mpp->alias);
1eb31d
+	return r;
1eb31d
+}
1eb31d
+
1eb31d
+#else
1eb31d
+
1eb31d
+int
1eb31d
+dm_cancel_deferred_remove (struct multipath *mpp)
1eb31d
+{
1eb31d
+	return 0;
1eb31d
+}
1eb31d
+
1eb31d
+#endif
1eb31d
+
1eb31d
 static struct dm_info *
1eb31d
 alloc_dminfo (void)
1eb31d
 {
1eb31d
Index: multipath-tools-130222/libmultipath/devmapper.h
1eb31d
===================================================================
1eb31d
--- multipath-tools-130222.orig/libmultipath/devmapper.h
1eb31d
+++ multipath-tools-130222/libmultipath/devmapper.h
ecd2a9
@@ -23,9 +23,11 @@ int dm_map_present (const char *);
1eb31d
 int dm_get_map(const char *, unsigned long long *, char *);
1eb31d
 int dm_get_status(char *, char *);
1eb31d
 int dm_type(const char *, char *);
1eb31d
-int _dm_flush_map (const char *, int);
1eb31d
-#define dm_flush_map(mapname) _dm_flush_map(mapname, 1)
1eb31d
-#define dm_flush_map_nosync(mapname) _dm_flush_map(mapname, 0)
1eb31d
+int _dm_flush_map (const char *, int, int);
1eb31d
+int dm_flush_map_nopaths(const char * mapname, int deferred_remove);
1eb31d
+#define dm_flush_map(mapname) _dm_flush_map(mapname, 1, 0)
1eb31d
+#define dm_flush_map_nosync(mapname) _dm_flush_map(mapname, 0, 0)
1eb31d
+int dm_cancel_deferred_remove(struct multipath *mpp);
1eb31d
 int dm_suspend_and_flush_map(const char * mapname);
1eb31d
 int dm_flush_maps (void);
1eb31d
 int dm_fail_path(char * mapname, char * path);
ecd2a9
@@ -40,7 +42,8 @@ int dm_geteventnr (char *name);
1eb31d
 int dm_get_major (char *name);
1eb31d
 int dm_get_minor (char *name);
1eb31d
 char * dm_mapname(int major, int minor);
1eb31d
-int dm_remove_partmaps (const char * mapname, int need_sync);
1eb31d
+int dm_remove_partmaps (const char * mapname, int need_sync,
1eb31d
+			int deferred_remove);
1eb31d
 int dm_get_uuid(char *name, char *uuid);
1eb31d
 int dm_get_info (char * mapname, struct dm_info ** dmi);
1eb31d
 int dm_rename (char * old, char * new);
1eb31d
Index: multipath-tools-130222/libmultipath/dict.c
1eb31d
===================================================================
1eb31d
--- multipath-tools-130222.orig/libmultipath/dict.c
1eb31d
+++ multipath-tools-130222/libmultipath/dict.c
1eb31d
@@ -738,6 +738,29 @@ def_force_sync_handler(vector strvec)
1eb31d
 	return 0;
1eb31d
 }
1eb31d
 
1eb31d
+static int
1eb31d
+def_deferred_remove_handler(vector strvec)
1eb31d
+{
1eb31d
+	char * buff;
1eb31d
+
1eb31d
+	buff = set_value(strvec);
1eb31d
+
1eb31d
+	if (!buff)
1eb31d
+		return 1;
1eb31d
+
1eb31d
+	if ((strlen(buff) == 2 && !strcmp(buff, "no")) ||
1eb31d
+	    (strlen(buff) == 1 && !strcmp(buff, "0")))
1eb31d
+		conf->deferred_remove = DEFERRED_REMOVE_OFF;
1eb31d
+	else if ((strlen(buff) == 3 && !strcmp(buff, "yes")) ||
1eb31d
+		 (strlen(buff) == 1 && !strcmp(buff, "1")))
1eb31d
+		conf->deferred_remove = DEFERRED_REMOVE_ON;
1eb31d
+	else
1eb31d
+		conf->deferred_remove = DEFAULT_DEFERRED_REMOVE;
1eb31d
+
1eb31d
+	FREE(buff);
1eb31d
+	return 0;
1eb31d
+}
1eb31d
+
1eb31d
 /*
1eb31d
  * blacklist block handlers
1eb31d
  */
1eb31d
@@ -1445,6 +1468,33 @@ hw_detect_prio_handler(vector strvec)
1eb31d
 	return 0;
1eb31d
 }
1eb31d
 
1eb31d
+static int
1eb31d
+hw_deferred_remove_handler(vector strvec)
1eb31d
+{
1eb31d
+	struct hwentry *hwe = VECTOR_LAST_SLOT(conf->hwtable);
1eb31d
+	char * buff;
1eb31d
+
1eb31d
+	if (!hwe)
1eb31d
+		return 1;
1eb31d
+
1eb31d
+	buff = set_value(strvec);
1eb31d
+
1eb31d
+	if (!buff)
1eb31d
+		return 1;
1eb31d
+
1eb31d
+	if ((strlen(buff) == 2 && !strcmp(buff, "no")) ||
1eb31d
+	    (strlen(buff) == 1 && !strcmp(buff, "0")))
1eb31d
+		hwe->deferred_remove = DEFERRED_REMOVE_OFF;
1eb31d
+	else if ((strlen(buff) == 3 && !strcmp(buff, "yes")) ||
1eb31d
+		 (strlen(buff) == 1 && !strcmp(buff, "1")))
1eb31d
+		hwe->deferred_remove = DEFERRED_REMOVE_ON;
1eb31d
+	else
1eb31d
+		hwe->deferred_remove = DEFERRED_REMOVE_UNDEF;
1eb31d
+
1eb31d
+	FREE(buff);
1eb31d
+	return 0;
1eb31d
+}
1eb31d
+
1eb31d
 /*
1eb31d
  * multipaths block handlers
1eb31d
  */
1eb31d
@@ -1920,6 +1970,32 @@ mp_names_handler(vector strvec)
1eb31d
 	return 0;
1eb31d
 }
1eb31d
 
1eb31d
+static int
1eb31d
+mp_deferred_remove_handler(vector strvec)
1eb31d
+{
1eb31d
+	struct mpentry *mpe = VECTOR_LAST_SLOT(conf->mptable);
1eb31d
+	char * buff;
1eb31d
+
1eb31d
+	if (!mpe)
1eb31d
+		return 1;
1eb31d
+
1eb31d
+	buff = set_value(strvec);
1eb31d
+	if (!buff)
1eb31d
+		return 1;
1eb31d
+
1eb31d
+	if ((strlen(buff) == 2 && strcmp(buff, "no") == 0) ||
1eb31d
+	    (strlen(buff) == 1 && strcmp(buff, "0") == 0))
1eb31d
+		mpe->deferred_remove = DEFERRED_REMOVE_OFF;
1eb31d
+	else if ((strlen(buff) == 3 && strcmp(buff, "yes") == 0) ||
1eb31d
+		 (strlen(buff) == 1 && strcmp(buff, "1") == 0))
1eb31d
+		mpe->deferred_remove = DEFERRED_REMOVE_ON;
1eb31d
+	else
1eb31d
+		mpe->deferred_remove = DEFERRED_REMOVE_UNDEF;
1eb31d
+
1eb31d
+	FREE(buff);
1eb31d
+	return 0;
1eb31d
+}
1eb31d
+
1eb31d
 /*
1eb31d
  * config file keywords printing
1eb31d
  */
1eb31d
@@ -2165,7 +2241,7 @@ snprint_mp_reservation_key (char * buff,
1eb31d
 	return snprintf(buff, len, "0x%" PRIx64, prkey);
1eb31d
 }
1eb31d
 
1eb31d
-	static int
1eb31d
+static int
1eb31d
 snprint_mp_user_friendly_names (char * buff, int len, void * data)
1eb31d
 {
1eb31d
 	struct mpentry * mpe = (struct mpentry *)data;
1eb31d
@@ -2179,6 +2255,19 @@ snprint_mp_user_friendly_names (char * b
1eb31d
 }
1eb31d
 
1eb31d
 static int
1eb31d
+snprint_mp_deferred_remove (char * buff, int len, void * data)
1eb31d
+{
1eb31d
+	struct mpentry * mpe = (struct mpentry *)data;
1eb31d
+
1eb31d
+	if (mpe->deferred_remove == DEFERRED_REMOVE_UNDEF)
1eb31d
+		return 0;
1eb31d
+	else if (mpe->deferred_remove == DEFERRED_REMOVE_OFF)
1eb31d
+		return snprintf(buff, len, "no");
1eb31d
+	else
1eb31d
+		return snprintf(buff, len, "yes");
1eb31d
+}
1eb31d
+
1eb31d
+static int
1eb31d
 snprint_hw_fast_io_fail(char * buff, int len, void * data)
1eb31d
 {
1eb31d
 	struct hwentry * hwe = (struct hwentry *)data;
1eb31d
@@ -2507,6 +2596,19 @@ snprint_hw_retain_hwhandler_handler(char
1eb31d
 }
1eb31d
 
1eb31d
 static int
1eb31d
+snprint_hw_deferred_remove(char * buff, int len, void * data)
1eb31d
+{
1eb31d
+	struct hwentry * hwe = (struct hwentry *)data;
1eb31d
+
1eb31d
+	if (hwe->deferred_remove == DEFERRED_REMOVE_ON)
1eb31d
+		return snprintf(buff, len, "yes");
1eb31d
+	else if (hwe->deferred_remove == DEFERRED_REMOVE_OFF)
1eb31d
+		return snprintf(buff, len, "no");
1eb31d
+	else
1eb31d
+		return 0;
1eb31d
+}
1eb31d
+
1eb31d
+static int
1eb31d
 snprint_detect_prio(char * buff, int len, void * data)
1eb31d
 {
1eb31d
 	struct hwentry * hwe = (struct hwentry *)data;
1eb31d
@@ -2900,6 +3002,15 @@ snprint_def_force_sync(char * buff, int
1eb31d
 }
1eb31d
 
1eb31d
 static int
1eb31d
+snprint_def_deferred_remove(char * buff, int len, void * data)
1eb31d
+{
1eb31d
+	if (conf->deferred_remove == DEFERRED_REMOVE_ON)
1eb31d
+		return snprintf(buff, len, "yes");
1eb31d
+	else
1eb31d
+		return snprintf(buff, len, "no");
1eb31d
+}
1eb31d
+
1eb31d
+static int
1eb31d
 snprint_ble_simple (char * buff, int len, void * data)
1eb31d
 {
1eb31d
 	struct blentry * ble = (struct blentry *)data;
1eb31d
@@ -2968,6 +3079,7 @@ init_keywords(void)
1eb31d
 	install_keyword("detect_prio", &def_detect_prio_handler, &snprint_def_detect_prio);
1eb31d
 	install_keyword("hw_str_match", &def_hw_strmatch_handler, &snprint_def_hw_strmatch);
1eb31d
 	install_keyword("force_sync", &def_force_sync_handler, &snprint_def_force_sync);
1eb31d
+	install_keyword("deferred_remove", &def_deferred_remove_handler, &snprint_def_deferred_remove);
1eb31d
 	__deprecated install_keyword("default_selector", &def_selector_handler, NULL);
1eb31d
 	__deprecated install_keyword("default_path_grouping_policy", &def_pgpolicy_handler, NULL);
1eb31d
 	__deprecated install_keyword("default_uid_attribute", &def_uid_attribute_handler, NULL);
1eb31d
@@ -3032,6 +3144,7 @@ init_keywords(void)
1eb31d
 	install_keyword("user_friendly_names", &hw_names_handler, &snprint_hw_user_friendly_names);
1eb31d
 	install_keyword("retain_attached_hw_handler", &hw_retain_hwhandler_handler, &snprint_hw_retain_hwhandler_handler);
1eb31d
 	install_keyword("detect_prio", &hw_detect_prio_handler, &snprint_detect_prio);
1eb31d
+	install_keyword("deferred_remove", &hw_deferred_remove_handler, &snprint_hw_deferred_remove);
1eb31d
 	install_sublevel_end();
1eb31d
 
1eb31d
 	install_keyword_root("multipaths", &multipaths_handler);
1eb31d
@@ -3056,5 +3169,6 @@ init_keywords(void)
1eb31d
 	install_keyword("gid", &mp_gid_handler, &snprint_mp_gid);
1eb31d
 	install_keyword("reservation_key", &mp_reservation_key_handler, &snprint_mp_reservation_key);
1eb31d
 	install_keyword("user_friendly_names", &mp_names_handler, &snprint_mp_user_friendly_names);
1eb31d
+	install_keyword("deferred_remove", &mp_deferred_remove_handler, &snprint_mp_deferred_remove);
1eb31d
 	install_sublevel_end();
1eb31d
 }
1eb31d
Index: multipath-tools-130222/libmultipath/propsel.c
1eb31d
===================================================================
1eb31d
--- multipath-tools-130222.orig/libmultipath/propsel.c
1eb31d
+++ multipath-tools-130222/libmultipath/propsel.c
1eb31d
@@ -744,6 +744,34 @@ select_retain_hwhandler (struct multipat
1eb31d
 }
1eb31d
 
1eb31d
 extern int
1eb31d
+select_deferred_remove (struct multipath *mp)
1eb31d
+{
1eb31d
+	if (mp->deferred_remove == DEFERRED_REMOVE_IN_PROGRESS) {
1eb31d
+		condlog(3, "%s: deferred_remove in progress", mp->alias);
1eb31d
+		return 0;
1eb31d
+	}
1eb31d
+	if (mp->mpe && mp->mpe->deferred_remove) {
1eb31d
+		mp->deferred_remove = mp->mpe->deferred_remove;
1eb31d
+		condlog(3, "%s: deferred_remove = %i (multipath setting)",
1eb31d
+			mp->alias, mp->deferred_remove);
1eb31d
+		return 0;
1eb31d
+	}
1eb31d
+	if (mp->hwe && mp->hwe->deferred_remove) {
1eb31d
+		mp->deferred_remove = mp->hwe->deferred_remove;
1eb31d
+		condlog(3, "%s: deferred_remove = %d (controller default)", mp->alias, mp->deferred_remove);
1eb31d
+		return 0;
1eb31d
+	}
1eb31d
+	if (conf->deferred_remove) {
1eb31d
+		mp->deferred_remove = conf->deferred_remove;
1eb31d
+		condlog(3, "%s: deferred_remove = %d (config file default)", mp->alias, mp->deferred_remove);
1eb31d
+		return 0;
1eb31d
+	}
1eb31d
+	mp->deferred_remove = DEFAULT_DEFERRED_REMOVE;
1eb31d
+	condlog(3, "%s: deferred_remove = %d (compiled in default)", mp->alias, mp->deferred_remove);
1eb31d
+	return 0;
1eb31d
+}
1eb31d
+
1eb31d
+extern int
1eb31d
 select_detect_prio (struct path * pp)
1eb31d
 {
1eb31d
 	if (pp->hwe && pp->hwe->detect_prio) {
1eb31d
Index: multipath-tools-130222/libmultipath/propsel.h
1eb31d
===================================================================
1eb31d
--- multipath-tools-130222.orig/libmultipath/propsel.h
1eb31d
+++ multipath-tools-130222/libmultipath/propsel.h
1eb31d
@@ -20,3 +20,4 @@ int select_dev_loss(struct multipath *mp
1eb31d
 int select_reservation_key(struct multipath *mp);
1eb31d
 int select_retain_hwhandler (struct multipath * mp);
1eb31d
 int select_detect_prio(struct path * pp);
1eb31d
+int select_deferred_remove(struct multipath *mp);
1eb31d
Index: multipath-tools-130222/libmultipath/structs.h
1eb31d
===================================================================
1eb31d
--- multipath-tools-130222.orig/libmultipath/structs.h
1eb31d
+++ multipath-tools-130222/libmultipath/structs.h
1eb31d
@@ -114,6 +114,13 @@ enum detect_prio_states {
1eb31d
 	DETECT_PRIO_ON,
1eb31d
 };
1eb31d
 
1eb31d
+enum deferred_remove_states {
1eb31d
+	DEFERRED_REMOVE_UNDEF,
1eb31d
+	DEFERRED_REMOVE_OFF,
1eb31d
+	DEFERRED_REMOVE_ON,
1eb31d
+	DEFERRED_REMOVE_IN_PROGRESS,
1eb31d
+};
1eb31d
+
1eb31d
 enum scsi_protocol {
1eb31d
 	SCSI_PROTOCOL_FCP = 0,	/* Fibre Channel */
1eb31d
 	SCSI_PROTOCOL_SPI = 1,	/* parallel SCSI */
1eb31d
@@ -207,6 +214,7 @@ struct multipath {
1eb31d
 	int attribute_flags;
1eb31d
 	int fast_io_fail;
1eb31d
 	int retain_hwhandler;
1eb31d
+	int deferred_remove;
1eb31d
 	unsigned int dev_loss;
1eb31d
 	uid_t uid;
1eb31d
 	gid_t gid;
1eb31d
Index: multipath-tools-130222/multipathd/main.c
1eb31d
===================================================================
1eb31d
--- multipath-tools-130222.orig/multipathd/main.c
1eb31d
+++ multipath-tools-130222/multipathd/main.c
1eb31d
@@ -214,19 +214,30 @@ sync_maps_state(vector mpvec)
1eb31d
 }
1eb31d
 
1eb31d
 static int
1eb31d
-flush_map(struct multipath * mpp, struct vectors * vecs)
1eb31d
+flush_map(struct multipath * mpp, struct vectors * vecs, int nopaths)
1eb31d
 {
1eb31d
+	int r;
1eb31d
+
1eb31d
+	if (nopaths)
1eb31d
+		r = dm_flush_map_nopaths(mpp->alias, mpp->deferred_remove);
1eb31d
+	else
1eb31d
+		r = dm_flush_map(mpp->alias);
1eb31d
 	/*
1eb31d
 	 * clear references to this map before flushing so we can ignore
1eb31d
 	 * the spurious uevent we may generate with the dm_flush_map call below
1eb31d
 	 */
1eb31d
-	if (dm_flush_map(mpp->alias)) {
1eb31d
+	if (r) {
1eb31d
 		/*
1eb31d
 		 * May not really be an error -- if the map was already flushed
1eb31d
 		 * from the device mapper by dmsetup(8) for instance.
1eb31d
 		 */
1eb31d
-		condlog(0, "%s: can't flush", mpp->alias);
1eb31d
-		return 1;
1eb31d
+		if (r == 1)
1eb31d
+			condlog(0, "%s: can't flush", mpp->alias);
1eb31d
+		else {
1eb31d
+			condlog(2, "%s: devmap deferred remove", mpp->alias);
1eb31d
+			mpp->deferred_remove = DEFERRED_REMOVE_IN_PROGRESS;
1eb31d
+		}
1eb31d
+		return r;
1eb31d
 	}
1eb31d
 	else {
1eb31d
 		dm_lib_release();
1eb31d
@@ -372,7 +383,7 @@ ev_remove_map (char * devname, char * al
1eb31d
 			mpp->alias, mpp->dmi->minor, minor);
1eb31d
 		return 0;
1eb31d
 	}
1eb31d
-	return flush_map(mpp, vecs);
1eb31d
+	return flush_map(mpp, vecs, 0);
1eb31d
 }
1eb31d
 
1eb31d
 static int
1eb31d
@@ -628,7 +639,7 @@ ev_remove_path (struct path *pp, struct
1eb31d
 				mpp->flush_on_last_del = FLUSH_IN_PROGRESS;
1eb31d
 				dm_queue_if_no_path(mpp->alias, 0);
1eb31d
 			}
1eb31d
-			if (!flush_map(mpp, vecs)) {
1eb31d
+			if (!flush_map(mpp, vecs, 1)) {
1eb31d
 				condlog(2, "%s: removed map after"
1eb31d
 					" removing all paths",
1eb31d
 					alias);
1eb31d
Index: multipath-tools-130222/libmultipath/structs_vec.c
1eb31d
===================================================================
1eb31d
--- multipath-tools-130222.orig/libmultipath/structs_vec.c
1eb31d
+++ multipath-tools-130222/libmultipath/structs_vec.c
1eb31d
@@ -392,6 +392,8 @@ __setup_multipath (struct vectors * vecs
1eb31d
 		set_no_path_retry(mpp);
1eb31d
 		select_pg_timeout(mpp);
1eb31d
 		select_flush_on_last_del(mpp);
1eb31d
+		if (VECTOR_SIZE(mpp->paths) != 0)
1eb31d
+			dm_cancel_deferred_remove(mpp);
1eb31d
 	}
1eb31d
 
1eb31d
 	return 0;
1eb31d
@@ -565,7 +567,6 @@ int update_multipath (struct vectors *ve
1eb31d
 			}
1eb31d
 		}
1eb31d
 	}
1eb31d
-
1eb31d
 	return 0;
1eb31d
 }
1eb31d
 
1eb31d
Index: multipath-tools-130222/multipath/multipath.conf.5
1eb31d
===================================================================
1eb31d
--- multipath-tools-130222.orig/multipath/multipath.conf.5
1eb31d
+++ multipath-tools-130222/multipath/multipath.conf.5
1eb31d
@@ -420,6 +420,16 @@ only one checker will run at a time.  Th
1eb31d
 multipathd checkers running in parallel causes significant CPU pressure. The
1eb31d
 Default is
1eb31d
 .I no
1eb31d
+.TP
1eb31d
+.B deferred_remove
1eb31d
+If set to
1eb31d
+.I yes
1eb31d
+, multipathd will do a deferred remove instead of a regular remove when the
1eb31d
+last path device has been deleted.  This means that if the multipath device is
1eb31d
+still in use, it will be freed when the last user closes it.  If path is added
1eb31d
+to the multipath device before the last user closes it, the deferred remove
1eb31d
+will be canceled. Default is
1eb31d
+.I no
1eb31d
 .
1eb31d
 .SH "blacklist section"
1eb31d
 The
1eb31d
@@ -521,6 +531,8 @@ section:
1eb31d
 .B features
1eb31d
 .TP
1eb31d
 .B reservation_key
1eb31d
+.TP
1eb31d
+.B deferred_remove
1eb31d
 .RE
1eb31d
 .PD
1eb31d
 .LP
1eb31d
@@ -611,6 +623,8 @@ section:
1eb31d
 .B retain_attached_hw_handler
1eb31d
 .TP
1eb31d
 .B detect_prio
1eb31d
+.TP
1eb31d
+.B deferred_remove
1eb31d
 .RE
1eb31d
 .PD
1eb31d
 .LP
1eb31d
Index: multipath-tools-130222/libmultipath/Makefile
1eb31d
===================================================================
1eb31d
--- multipath-tools-130222.orig/libmultipath/Makefile
1eb31d
+++ multipath-tools-130222/libmultipath/Makefile
1eb31d
@@ -36,6 +36,12 @@ ifneq ($(strip $(LIBUDEV_API_RECVBUF)),0
1eb31d
 	CFLAGS += -DLIBUDEV_API_RECVBUF
1eb31d
 endif
1eb31d
 
1eb31d
+LIBDM_API_DEFERRED = $(shell grep -Ecs '^[a-z]*[[:space:]]+dm_task_deferred_remove' /usr/include/libdevmapper.h)
1eb31d
+
1eb31d
+ifneq ($(strip $(LIBDM_API_DEFERRED)),0)
1eb31d
+	CFLAGS += -DLIBDM_API_DEFERRED
1eb31d
+endif
1eb31d
+
1eb31d
 
1eb31d
 all: $(LIBS)
1eb31d