From: Peter Rajnoha <[email-masked]>

this is a kernel side patch that adds support for cookie variables.
A cookie is a random unsigned 32 bit value generated in userspace
and passed into the DM through ioctl (reusing the event_nr field to
avoid the change in the interface). These values are sent back in
CHANGE and REMOVE uevents. This way we can easily pair a process
waiting for udev's rule completion and the udev event itself. Also,
we can use the cookie value to group the actions and related uevents
together, so we know which uevents belong to actions done on one
DM tree.

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

---
 drivers/md/dm-ioctl.c    |   16 +++++++++++-----
 drivers/md/dm-sysfs.c    |    4 ++++
 drivers/md/dm.c          |   15 ++++++++++++++-
 drivers/md/dm.h          |    2 ++
 include/linux/dm-ioctl.h |    5 +++--
 5 files changed, 34 insertions(+), 8 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-08 22:27:59.000000000 +0100
+++ linux-2.6.30-rc1/drivers/md/dm-ioctl.c	2009-04-09 12:07:22.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,6 +333,7 @@ static int dm_hash_rename(const char *ol
 		dm_table_put(table);
 	}
 
+	dm_set_cookie(hc->md, cookie);
 	dm_kobject_uevent(hc->md);
 
 	dm_put(hc->md);
@@ -678,9 +679,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);
+	up_write(&_hash_lock);
 	param->data_size = 0;
 	return 0;
 }
@@ -715,7 +718,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)
@@ -814,8 +817,6 @@ static int do_resume(struct dm_ioctl *pa
 	hc->new_map = NULL;
 	param->flags &= ~DM_INACTIVE_PRESENT_FLAG;
 
-	up_write(&_hash_lock);
-
 	/* Do we need to load a new map ? */
 	if (new_map) {
 		/* Suspend if it isn't already suspended */
@@ -839,13 +840,18 @@ 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);
 
 	if (!r)
 		r = __dev_status(md, param);
 
 	dm_put(md);
+	up_write(&_hash_lock);
 	return r;
 }
 
Index: linux-2.6.30-rc1/drivers/md/dm-sysfs.c
===================================================================
--- linux-2.6.30-rc1.orig/drivers/md/dm-sysfs.c	2009-01-06 03:57:48.000000000 +0000
+++ linux-2.6.30-rc1/drivers/md/dm-sysfs.c	2009-04-09 12:07:22.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 12:06:59.000000000 +0100
+++ linux-2.6.30-rc1/drivers/md/dm.c	2009-04-09 12:07:22.000000000 +0100
@@ -154,6 +154,7 @@ struct mapped_device {
 	atomic_t uevent_seq;
 	struct list_head uevent_list;
 	spinlock_t uevent_lock; /* Protect access to uevent_list */
+	char cookie[24];
 
 	/*
 	 * freeze/thaw support require holding onto a super block
@@ -1726,7 +1727,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 +1766,16 @@ struct gendisk *dm_disk(struct mapped_de
 	return md->disk;
 }
 
+void dm_set_cookie(struct mapped_device *md, uint32_t cookie)
+{
+	snprintf(md->cookie, sizeof(md->cookie), "COOKIE=%u", (unsigned) cookie);
+}
+
+char *dm_get_cookie(struct mapped_device *md)
+{
+	return md->cookie;
+}
+
 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-08 22:28:01.000000000 +0100
+++ linux-2.6.30-rc1/drivers/md/dm.h	2009-04-09 12:07:22.000000000 +0100
@@ -93,6 +93,8 @@ 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);
 
 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-02 16:42:45.000000000 +0100
+++ linux-2.6.30-rc1/include/linux/dm-ioctl.h	2009-04-09 12:47:48.000000000 +0100
@@ -124,6 +124,7 @@ struct dm_ioctl {
 	__s32 open_count;	/* out */
 	__u32 flags;		/* in/out */
 	__u32 event_nr;      	/* in/out */
+				/* Holds a cookie for udev with create, rename and remove */
 	__u32 padding;
 
 	__u64 dev;		/* in/out */
@@ -256,9 +257,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 */