From: Peter Rajnoha <[email-masked]>

Add support for passing a 32 bit "cookie" into the kernel with the
DM_SUSPEND, DM_DEV_RENAME and DM_DEV_REMOVE ioctls.  The (unsigned)
value of this cookie is returned to userspace alongside the uevents
issued by these ioctls in the variable DM_COOKIE.

This means the userspace process issuing these ioctls can be notified
by udev after udev has completed any actions triggered.

To minimise the interface extension, we pass the cookie into the
kernel in the event_nr field which is otherwise unused when calling
these ioctls.  Incrementing the version number allows userspace to
determine in advance whether or not the kernel supports the cookie.
If the kernel does support this but userspace does not, there should
be no impact as the new variable will just get ignored.

Signed-off-by: Peter Rajnoha <[email-masked]>
Signed-off-by: Alasdair G Kergon <[email-masked]>

---
 drivers/md/dm-ioctl.c    |   14 ++++++++++++--
 drivers/md/dm-sysfs.c    |    4 ++++
 drivers/md/dm.c          |   33 ++++++++++++++++++++++++++++++++-
 drivers/md/dm.h          |    3 +++
 include/linux/dm-ioctl.h |   14 ++++++++++++--
 5 files changed, 63 insertions(+), 5 deletions(-)

Index: linux-2.6.30-rc1/drivers/md/dm-ioctl.c
===================================================================
--- linux-2.6.30-rc1.orig/drivers/md/dm-ioctl.c	2009-04-09 21:57:38.000000000 +0100
+++ linux-2.6.30-rc1/drivers/md/dm-ioctl.c	2009-04-09 21:57:50.000000000 +0100
@@ -276,7 +276,7 @@ retry:
 	up_write(&_hash_lock);
 }
 
-static int dm_hash_rename(const char *old, const char *new)
+static int dm_hash_rename(uint32_t cookie, const char *old, const char *new)
 {
 	char *new_name, *old_name;
 	struct hash_cell *hc;
@@ -333,7 +333,9 @@ static int dm_hash_rename(const char *ol
 		dm_table_put(table);
 	}
 
+	dm_set_cookie(hc->md, cookie);
 	dm_kobject_uevent(hc->md);
+	dm_clear_cookie(hc->md);
 
 	dm_put(hc->md);
 	up_write(&_hash_lock);
@@ -678,9 +680,11 @@ static int dev_remove(struct dm_ioctl *p
 		return r;
 	}
 
+	dm_set_cookie(md, param->event_nr);
 	__hash_remove(hc);
 	up_write(&_hash_lock);
 	dm_put(md);
+	dm_clear_cookie(md);
 	param->data_size = 0;
 	return 0;
 }
@@ -715,7 +719,7 @@ static int dev_rename(struct dm_ioctl *p
 		return r;
 
 	param->data_size = 0;
-	return dm_hash_rename(param->name, new_name);
+	return dm_hash_rename(param->event_nr, param->name, new_name);
 }
 
 static int dev_set_geometry(struct dm_ioctl *param, size_t param_size)
@@ -839,8 +843,14 @@ static int do_resume(struct dm_ioctl *pa
 			set_disk_ro(dm_disk(md), 1);
 	}
 
+	dm_set_cookie(md, param->event_nr);
+
 	if (dm_suspended(md))
 		r = dm_resume(md);
+	else
+		dm_kobject_uevent(md);
+
+	dm_clear_cookie(md);
 
 	if (!r)
 		r = __dev_status(md, param);
Index: linux-2.6.30-rc1/drivers/md/dm-sysfs.c
===================================================================
--- linux-2.6.30-rc1.orig/drivers/md/dm-sysfs.c	2009-04-09 21:56:03.000000000 +0100
+++ linux-2.6.30-rc1/drivers/md/dm-sysfs.c	2009-04-09 21:57:50.000000000 +0100
@@ -95,5 +95,9 @@ int dm_sysfs_init(struct mapped_device *
  */
 void dm_sysfs_exit(struct mapped_device *md)
 {
+	char *envp[] = {dm_get_cookie(md), NULL};
+	struct kobject *kobj = &disk_to_dev(dm_disk(md))->kobj;
+
+	kobject_uevent_env(kobj, KOBJ_REMOVE, envp);
 	kobject_put(dm_kobject(md));
 }
Index: linux-2.6.30-rc1/drivers/md/dm.c
===================================================================
--- linux-2.6.30-rc1.orig/drivers/md/dm.c	2009-04-09 21:57:48.000000000 +0100
+++ linux-2.6.30-rc1/drivers/md/dm.c	2009-04-09 21:57:50.000000000 +0100
@@ -24,6 +24,13 @@
 
 #define DM_MSG_PREFIX "core"
 
+/*
+ * Cookies are numeric values sent with CHANGE and REMOVE
+ * uevents while resuming, removing or renaming the device.
+ */
+#define DM_COOKIE_ENV_VAR_NAME "DM_COOKIE"
+#define DM_COOKIE_LENGTH 24
+
 static const char *_name = DM_NAME;
 
 static unsigned int major = 0;
@@ -102,6 +109,7 @@ union map_info *dm_get_mapinfo(struct bi
 struct mapped_device {
 	struct rw_semaphore io_lock;
 	struct mutex suspend_lock;
+	struct mutex cookie_lock;
 	rwlock_t map_lock;
 	atomic_t holders;
 	atomic_t open_count;
@@ -154,6 +162,7 @@ struct mapped_device {
 	atomic_t uevent_seq;
 	struct list_head uevent_list;
 	spinlock_t uevent_lock; /* Protect access to uevent_list */
+	char cookie[DM_COOKIE_LENGTH];
 
 	/*
 	 * freeze/thaw support require holding onto a super block
@@ -1117,6 +1126,7 @@ static struct mapped_device *alloc_dev(i
 
 	init_rwsem(&md->io_lock);
 	mutex_init(&md->suspend_lock);
+	mutex_init(&md->cookie_lock);
 	spin_lock_init(&md->deferred_lock);
 	rwlock_init(&md->map_lock);
 	atomic_set(&md->holders, 1);
@@ -1726,7 +1736,9 @@ out:
  *---------------------------------------------------------------*/
 void dm_kobject_uevent(struct mapped_device *md)
 {
-	kobject_uevent(&disk_to_dev(md->disk)->kobj, KOBJ_CHANGE);
+	char *envp[] = {dm_get_cookie(md), NULL};
+
+	kobject_uevent_env(&disk_to_dev(md->disk)->kobj, KOBJ_CHANGE, envp);
 }
 
 uint32_t dm_next_uevent_seq(struct mapped_device *md)
@@ -1763,6 +1775,25 @@ struct gendisk *dm_disk(struct mapped_de
 	return md->disk;
 }
 
+void dm_set_cookie(struct mapped_device *md, uint32_t cookie)
+{
+	mutex_lock(&md->cookie_lock);
+	snprintf(md->cookie, DM_COOKIE_LENGTH, "%s=%u",
+		 DM_COOKIE_ENV_VAR_NAME, (unsigned) cookie);
+}
+
+char *dm_get_cookie(struct mapped_device *md)
+{
+	return md->cookie;
+}
+
+void dm_clear_cookie(struct mapped_device *md)
+{
+	snprintf(md->cookie, DM_COOKIE_LENGTH, "%s=",
+		 DM_COOKIE_ENV_VAR_NAME);
+	mutex_unlock(&md->cookie_lock);
+}
+
 struct kobject *dm_kobject(struct mapped_device *md)
 {
 	return &md->kobj;
Index: linux-2.6.30-rc1/drivers/md/dm.h
===================================================================
--- linux-2.6.30-rc1.orig/drivers/md/dm.h	2009-04-09 21:57:38.000000000 +0100
+++ linux-2.6.30-rc1/drivers/md/dm.h	2009-04-09 21:57:50.000000000 +0100
@@ -93,6 +93,9 @@ int dm_open_count(struct mapped_device *
 int dm_lock_for_deletion(struct mapped_device *md);
 
 void dm_kobject_uevent(struct mapped_device *md);
+void dm_set_cookie(struct mapped_device *md, uint32_t cookie);
+char *dm_get_cookie(struct mapped_device *md);
+void dm_clear_cookie(struct mapped_device *md);
 
 int dm_kcopyd_init(void);
 void dm_kcopyd_exit(void);
Index: linux-2.6.30-rc1/include/linux/dm-ioctl.h
===================================================================
--- linux-2.6.30-rc1.orig/include/linux/dm-ioctl.h	2009-04-09 21:56:03.000000000 +0100
+++ linux-2.6.30-rc1/include/linux/dm-ioctl.h	2009-04-09 21:57:50.000000000 +0100
@@ -123,6 +123,16 @@ struct dm_ioctl {
 	__u32 target_count;	/* in/out */
 	__s32 open_count;	/* out */
 	__u32 flags;		/* in/out */
+
+	/*
+	 * event_nr holds either the event number (input and output) or the
+	 * udev cookie value (input only).
+	 * The DM_DEV_WAIT ioctl takes an event number as input.
+	 * The DM_SUSPEND, DM_DEV_REMOVE and DM_DEV_RENAME ioctls
+	 * use the field as a cookie to return in the DM_COOKIE
+	 * variable with the uevents they issue.
+	 * For output, the ioctls return the event number, not the cookie.
+	 */
 	__u32 event_nr;      	/* in/out */
 	__u32 padding;
 
@@ -256,9 +266,9 @@ enum {
 #define DM_DEV_SET_GEOMETRY	_IOWR(DM_IOCTL, DM_DEV_SET_GEOMETRY_CMD, struct dm_ioctl)
 
 #define DM_VERSION_MAJOR	4
-#define DM_VERSION_MINOR	14
+#define DM_VERSION_MINOR	15
 #define DM_VERSION_PATCHLEVEL	0
-#define DM_VERSION_EXTRA	"-ioctl (2008-04-23)"
+#define DM_VERSION_EXTRA	"-ioctl (2009-04-01)"
 
 /* Status bits */
 #define DM_READONLY_FLAG	(1 << 0) /* In/Out */