diff options
author | Mikulas Patocka <mpatocka@redhat.com> | 2014-01-06 23:01:22 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2014-02-13 16:48:01 -0500 |
commit | eef2b6df86323b5c9eaee154174afb1976741f38 (patch) | |
tree | 65bd724a740d630ead573d12dbb49e93f8536b6b /drivers/md | |
parent | 025d61e0f163e55a7a7b08975640c5c75c2690b8 (diff) |
dm: wait until embedded kobject is released before destroying a device
commit be35f486108227e10fe5d96fd42fb2b344c59983 upstream.
There may be other parts of the kernel holding a reference on the dm
kobject. We must wait until all references are dropped before
deallocating the mapped_device structure.
The dm_kobject_release method signals that all references are dropped
via completion. But dm_kobject_release doesn't free the kobject (which
is embedded in the mapped_device structure).
This is the sequence of operations:
* when destroying a DM device, call kobject_put from dm_sysfs_exit
* wait until all users stop using the kobject, when it happens the
release method is called
* the release method signals the completion and should return without
delay
* the dm device removal code that waits on the completion continues
* the dm device removal code drops the dm_mod reference the device had
* the dm device removal code frees the mapped_device structure that
contains the kobject
Using kobject this way should avoid the module unload race that was
mentioned at the beginning of this thread:
https://lkml.org/lkml/2014/1/4/83
Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
Signed-off-by: Mike Snitzer <snitzer@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/md')
-rw-r--r-- | drivers/md/dm-sysfs.c | 10 | ||||
-rw-r--r-- | drivers/md/dm.c | 11 | ||||
-rw-r--r-- | drivers/md/dm.h | 2 |
3 files changed, 22 insertions, 1 deletions
diff --git a/drivers/md/dm-sysfs.c b/drivers/md/dm-sysfs.c index 84d2b91e4efb..e0cc5d6a9e46 100644 --- a/drivers/md/dm-sysfs.c +++ b/drivers/md/dm-sysfs.c | |||
@@ -79,6 +79,11 @@ static const struct sysfs_ops dm_sysfs_ops = { | |||
79 | .show = dm_attr_show, | 79 | .show = dm_attr_show, |
80 | }; | 80 | }; |
81 | 81 | ||
82 | static void dm_kobject_release(struct kobject *kobj) | ||
83 | { | ||
84 | complete(dm_get_completion_from_kobject(kobj)); | ||
85 | } | ||
86 | |||
82 | /* | 87 | /* |
83 | * dm kobject is embedded in mapped_device structure | 88 | * dm kobject is embedded in mapped_device structure |
84 | * no need to define release function here | 89 | * no need to define release function here |
@@ -86,6 +91,7 @@ static const struct sysfs_ops dm_sysfs_ops = { | |||
86 | static struct kobj_type dm_ktype = { | 91 | static struct kobj_type dm_ktype = { |
87 | .sysfs_ops = &dm_sysfs_ops, | 92 | .sysfs_ops = &dm_sysfs_ops, |
88 | .default_attrs = dm_attrs, | 93 | .default_attrs = dm_attrs, |
94 | .release = dm_kobject_release, | ||
89 | }; | 95 | }; |
90 | 96 | ||
91 | /* | 97 | /* |
@@ -104,5 +110,7 @@ int dm_sysfs_init(struct mapped_device *md) | |||
104 | */ | 110 | */ |
105 | void dm_sysfs_exit(struct mapped_device *md) | 111 | void dm_sysfs_exit(struct mapped_device *md) |
106 | { | 112 | { |
107 | kobject_put(dm_kobject(md)); | 113 | struct kobject *kobj = dm_kobject(md); |
114 | kobject_put(kobj); | ||
115 | wait_for_completion(dm_get_completion_from_kobject(kobj)); | ||
108 | } | 116 | } |
diff --git a/drivers/md/dm.c b/drivers/md/dm.c index 1c13071a81be..d2523fedca3c 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c | |||
@@ -187,6 +187,9 @@ struct mapped_device { | |||
187 | /* sysfs handle */ | 187 | /* sysfs handle */ |
188 | struct kobject kobj; | 188 | struct kobject kobj; |
189 | 189 | ||
190 | /* wait until the kobject is released */ | ||
191 | struct completion kobj_completion; | ||
192 | |||
190 | /* zero-length flush that will be cloned and submitted to targets */ | 193 | /* zero-length flush that will be cloned and submitted to targets */ |
191 | struct bio flush_bio; | 194 | struct bio flush_bio; |
192 | }; | 195 | }; |
@@ -1904,6 +1907,7 @@ static struct mapped_device *alloc_dev(int minor) | |||
1904 | init_waitqueue_head(&md->wait); | 1907 | init_waitqueue_head(&md->wait); |
1905 | INIT_WORK(&md->work, dm_wq_work); | 1908 | INIT_WORK(&md->work, dm_wq_work); |
1906 | init_waitqueue_head(&md->eventq); | 1909 | init_waitqueue_head(&md->eventq); |
1910 | init_completion(&md->kobj_completion); | ||
1907 | 1911 | ||
1908 | md->disk->major = _major; | 1912 | md->disk->major = _major; |
1909 | md->disk->first_minor = minor; | 1913 | md->disk->first_minor = minor; |
@@ -2758,6 +2762,13 @@ struct mapped_device *dm_get_from_kobject(struct kobject *kobj) | |||
2758 | return md; | 2762 | return md; |
2759 | } | 2763 | } |
2760 | 2764 | ||
2765 | struct completion *dm_get_completion_from_kobject(struct kobject *kobj) | ||
2766 | { | ||
2767 | struct mapped_device *md = container_of(kobj, struct mapped_device, kobj); | ||
2768 | |||
2769 | return &md->kobj_completion; | ||
2770 | } | ||
2771 | |||
2761 | int dm_suspended_md(struct mapped_device *md) | 2772 | int dm_suspended_md(struct mapped_device *md) |
2762 | { | 2773 | { |
2763 | return test_bit(DMF_SUSPENDED, &md->flags); | 2774 | return test_bit(DMF_SUSPENDED, &md->flags); |
diff --git a/drivers/md/dm.h b/drivers/md/dm.h index 45b97da1bd06..66cfb7d8d157 100644 --- a/drivers/md/dm.h +++ b/drivers/md/dm.h | |||
@@ -15,6 +15,7 @@ | |||
15 | #include <linux/list.h> | 15 | #include <linux/list.h> |
16 | #include <linux/blkdev.h> | 16 | #include <linux/blkdev.h> |
17 | #include <linux/hdreg.h> | 17 | #include <linux/hdreg.h> |
18 | #include <linux/completion.h> | ||
18 | 19 | ||
19 | /* | 20 | /* |
20 | * Suspend feature flags | 21 | * Suspend feature flags |
@@ -129,6 +130,7 @@ int dm_sysfs_init(struct mapped_device *md); | |||
129 | void dm_sysfs_exit(struct mapped_device *md); | 130 | void dm_sysfs_exit(struct mapped_device *md); |
130 | struct kobject *dm_kobject(struct mapped_device *md); | 131 | struct kobject *dm_kobject(struct mapped_device *md); |
131 | struct mapped_device *dm_get_from_kobject(struct kobject *kobj); | 132 | struct mapped_device *dm_get_from_kobject(struct kobject *kobj); |
133 | struct completion *dm_get_completion_from_kobject(struct kobject *kobj); | ||
132 | 134 | ||
133 | /* | 135 | /* |
134 | * Targets for linear and striped mappings | 136 | * Targets for linear and striped mappings |