diff options
author | Jeff Mahoney <jeffm@suse.com> | 2006-06-26 03:27:24 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-06-26 12:58:35 -0400 |
commit | 7ec75f254742766eb729f1d1024a5b4e6692fc5f (patch) | |
tree | 67cf98f49397c4f7a71e12afc82c04c7dedbe17f | |
parent | fba9f90e568e17c326257ee293207b75880b39d6 (diff) |
[PATCH] dm: fix mapped device ref counting
To avoid races, _minor_lock must be held while changing mapped device
reference counts.
There are a few paths where a mapped_device pointer is returned before a
reference is taken. This patch fixes them.
[akpm: too late for 2.6.17 - suitable for 2.6.17.x after it has settled]
Signed-off-by: Jeff Mahoney <jeffm@suse.com>
Signed-off-by: Alasdair G Kergon <agk@redhat.com>
Cc: <stable@kernel.org>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r-- | drivers/md/dm-ioctl.c | 34 |
1 files changed, 24 insertions, 10 deletions
diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c index 8edd6435414d..f7e743691aa8 100644 --- a/drivers/md/dm-ioctl.c +++ b/drivers/md/dm-ioctl.c | |||
@@ -102,8 +102,10 @@ static struct hash_cell *__get_name_cell(const char *str) | |||
102 | unsigned int h = hash_str(str); | 102 | unsigned int h = hash_str(str); |
103 | 103 | ||
104 | list_for_each_entry (hc, _name_buckets + h, name_list) | 104 | list_for_each_entry (hc, _name_buckets + h, name_list) |
105 | if (!strcmp(hc->name, str)) | 105 | if (!strcmp(hc->name, str)) { |
106 | dm_get(hc->md); | ||
106 | return hc; | 107 | return hc; |
108 | } | ||
107 | 109 | ||
108 | return NULL; | 110 | return NULL; |
109 | } | 111 | } |
@@ -114,8 +116,10 @@ static struct hash_cell *__get_uuid_cell(const char *str) | |||
114 | unsigned int h = hash_str(str); | 116 | unsigned int h = hash_str(str); |
115 | 117 | ||
116 | list_for_each_entry (hc, _uuid_buckets + h, uuid_list) | 118 | list_for_each_entry (hc, _uuid_buckets + h, uuid_list) |
117 | if (!strcmp(hc->uuid, str)) | 119 | if (!strcmp(hc->uuid, str)) { |
120 | dm_get(hc->md); | ||
118 | return hc; | 121 | return hc; |
122 | } | ||
119 | 123 | ||
120 | return NULL; | 124 | return NULL; |
121 | } | 125 | } |
@@ -191,7 +195,7 @@ static int unregister_with_devfs(struct hash_cell *hc) | |||
191 | */ | 195 | */ |
192 | static int dm_hash_insert(const char *name, const char *uuid, struct mapped_device *md) | 196 | static int dm_hash_insert(const char *name, const char *uuid, struct mapped_device *md) |
193 | { | 197 | { |
194 | struct hash_cell *cell; | 198 | struct hash_cell *cell, *hc; |
195 | 199 | ||
196 | /* | 200 | /* |
197 | * Allocate the new cells. | 201 | * Allocate the new cells. |
@@ -204,14 +208,19 @@ static int dm_hash_insert(const char *name, const char *uuid, struct mapped_devi | |||
204 | * Insert the cell into both hash tables. | 208 | * Insert the cell into both hash tables. |
205 | */ | 209 | */ |
206 | down_write(&_hash_lock); | 210 | down_write(&_hash_lock); |
207 | if (__get_name_cell(name)) | 211 | hc = __get_name_cell(name); |
212 | if (hc) { | ||
213 | dm_put(hc->md); | ||
208 | goto bad; | 214 | goto bad; |
215 | } | ||
209 | 216 | ||
210 | list_add(&cell->name_list, _name_buckets + hash_str(name)); | 217 | list_add(&cell->name_list, _name_buckets + hash_str(name)); |
211 | 218 | ||
212 | if (uuid) { | 219 | if (uuid) { |
213 | if (__get_uuid_cell(uuid)) { | 220 | hc = __get_uuid_cell(uuid); |
221 | if (hc) { | ||
214 | list_del(&cell->name_list); | 222 | list_del(&cell->name_list); |
223 | dm_put(hc->md); | ||
215 | goto bad; | 224 | goto bad; |
216 | } | 225 | } |
217 | list_add(&cell->uuid_list, _uuid_buckets + hash_str(uuid)); | 226 | list_add(&cell->uuid_list, _uuid_buckets + hash_str(uuid)); |
@@ -289,6 +298,7 @@ static int dm_hash_rename(const char *old, const char *new) | |||
289 | if (hc) { | 298 | if (hc) { |
290 | DMWARN("asked to rename to an already existing name %s -> %s", | 299 | DMWARN("asked to rename to an already existing name %s -> %s", |
291 | old, new); | 300 | old, new); |
301 | dm_put(hc->md); | ||
292 | up_write(&_hash_lock); | 302 | up_write(&_hash_lock); |
293 | kfree(new_name); | 303 | kfree(new_name); |
294 | return -EBUSY; | 304 | return -EBUSY; |
@@ -328,6 +338,7 @@ static int dm_hash_rename(const char *old, const char *new) | |||
328 | dm_table_put(table); | 338 | dm_table_put(table); |
329 | } | 339 | } |
330 | 340 | ||
341 | dm_put(hc->md); | ||
331 | up_write(&_hash_lock); | 342 | up_write(&_hash_lock); |
332 | kfree(old_name); | 343 | kfree(old_name); |
333 | return 0; | 344 | return 0; |
@@ -611,10 +622,8 @@ static struct hash_cell *__find_device_hash_cell(struct dm_ioctl *param) | |||
611 | return __get_name_cell(param->name); | 622 | return __get_name_cell(param->name); |
612 | 623 | ||
613 | md = dm_get_md(huge_decode_dev(param->dev)); | 624 | md = dm_get_md(huge_decode_dev(param->dev)); |
614 | if (md) { | 625 | if (md) |
615 | mdptr = dm_get_mdptr(md); | 626 | mdptr = dm_get_mdptr(md); |
616 | dm_put(md); | ||
617 | } | ||
618 | 627 | ||
619 | return mdptr; | 628 | return mdptr; |
620 | } | 629 | } |
@@ -628,7 +637,6 @@ static struct mapped_device *find_device(struct dm_ioctl *param) | |||
628 | hc = __find_device_hash_cell(param); | 637 | hc = __find_device_hash_cell(param); |
629 | if (hc) { | 638 | if (hc) { |
630 | md = hc->md; | 639 | md = hc->md; |
631 | dm_get(md); | ||
632 | 640 | ||
633 | /* | 641 | /* |
634 | * Sneakily write in both the name and the uuid | 642 | * Sneakily write in both the name and the uuid |
@@ -653,6 +661,7 @@ static struct mapped_device *find_device(struct dm_ioctl *param) | |||
653 | static int dev_remove(struct dm_ioctl *param, size_t param_size) | 661 | static int dev_remove(struct dm_ioctl *param, size_t param_size) |
654 | { | 662 | { |
655 | struct hash_cell *hc; | 663 | struct hash_cell *hc; |
664 | struct mapped_device *md; | ||
656 | 665 | ||
657 | down_write(&_hash_lock); | 666 | down_write(&_hash_lock); |
658 | hc = __find_device_hash_cell(param); | 667 | hc = __find_device_hash_cell(param); |
@@ -663,8 +672,11 @@ static int dev_remove(struct dm_ioctl *param, size_t param_size) | |||
663 | return -ENXIO; | 672 | return -ENXIO; |
664 | } | 673 | } |
665 | 674 | ||
675 | md = hc->md; | ||
676 | |||
666 | __hash_remove(hc); | 677 | __hash_remove(hc); |
667 | up_write(&_hash_lock); | 678 | up_write(&_hash_lock); |
679 | dm_put(md); | ||
668 | param->data_size = 0; | 680 | param->data_size = 0; |
669 | return 0; | 681 | return 0; |
670 | } | 682 | } |
@@ -790,7 +802,6 @@ static int do_resume(struct dm_ioctl *param) | |||
790 | } | 802 | } |
791 | 803 | ||
792 | md = hc->md; | 804 | md = hc->md; |
793 | dm_get(md); | ||
794 | 805 | ||
795 | new_map = hc->new_map; | 806 | new_map = hc->new_map; |
796 | hc->new_map = NULL; | 807 | hc->new_map = NULL; |
@@ -1078,6 +1089,7 @@ static int table_clear(struct dm_ioctl *param, size_t param_size) | |||
1078 | { | 1089 | { |
1079 | int r; | 1090 | int r; |
1080 | struct hash_cell *hc; | 1091 | struct hash_cell *hc; |
1092 | struct mapped_device *md; | ||
1081 | 1093 | ||
1082 | down_write(&_hash_lock); | 1094 | down_write(&_hash_lock); |
1083 | 1095 | ||
@@ -1096,7 +1108,9 @@ static int table_clear(struct dm_ioctl *param, size_t param_size) | |||
1096 | param->flags &= ~DM_INACTIVE_PRESENT_FLAG; | 1108 | param->flags &= ~DM_INACTIVE_PRESENT_FLAG; |
1097 | 1109 | ||
1098 | r = __dev_status(hc->md, param); | 1110 | r = __dev_status(hc->md, param); |
1111 | md = hc->md; | ||
1099 | up_write(&_hash_lock); | 1112 | up_write(&_hash_lock); |
1113 | dm_put(md); | ||
1100 | return r; | 1114 | return r; |
1101 | } | 1115 | } |
1102 | 1116 | ||