diff options
author | Mikulas Patocka <mpatocka@redhat.com> | 2009-12-10 18:51:52 -0500 |
---|---|---|
committer | Alasdair G Kergon <agk@redhat.com> | 2009-12-10 18:51:52 -0500 |
commit | 6076905b5ef39e0ea58db32583c9e0036c05e47b (patch) | |
tree | 918a8da9778d93e8ff6aff6497eb698d7a45edab /drivers/md/dm-ioctl.c | |
parent | 3067e02f8f3ae2f3f02ba76400d03b8bcb4942b0 (diff) |
dm: avoid _hash_lock deadlock
Fix a reported deadlock if there are still unprocessed multipath events
on a device that is being removed.
_hash_lock is held during dev_remove while trying to send the
outstanding events. Sending the events requests the _hash_lock
again in dm_copy_name_and_uuid.
This patch introduces a separate lock around regions that modify the
link to the hash table (dm_set_mdptr) or the name or uuid so that
dm_copy_name_and_uuid no longer needs _hash_lock.
Additionally, dm_copy_name_and_uuid can only be called if md exists
so we can drop the dm_get() and dm_put() which can lead to a BUG()
while md is being freed.
The deadlock:
#0 [ffff8106298dfb48] schedule at ffffffff80063035
#1 [ffff8106298dfc20] __down_read at ffffffff8006475d
#2 [ffff8106298dfc60] dm_copy_name_and_uuid at ffffffff8824f740
#3 [ffff8106298dfc90] dm_send_uevents at ffffffff88252685
#4 [ffff8106298dfcd0] event_callback at ffffffff8824c678
#5 [ffff8106298dfd00] dm_table_event at ffffffff8824dd01
#6 [ffff8106298dfd10] __hash_remove at ffffffff882507ad
#7 [ffff8106298dfd30] dev_remove at ffffffff88250865
#8 [ffff8106298dfd60] ctl_ioctl at ffffffff88250d80
#9 [ffff8106298dfee0] do_ioctl at ffffffff800418c4
#10 [ffff8106298dff00] vfs_ioctl at ffffffff8002fab9
#11 [ffff8106298dff40] sys_ioctl at ffffffff8004bdaf
#12 [ffff8106298dff80] tracesys at ffffffff8005d28d (via system_call)
Cc: stable@kernel.org
Reported-by: guy keren <choo@actcom.co.il>
Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
Signed-off-by: Alasdair G Kergon <agk@redhat.com>
Diffstat (limited to 'drivers/md/dm-ioctl.c')
-rw-r--r-- | drivers/md/dm-ioctl.c | 17 |
1 files changed, 13 insertions, 4 deletions
diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c index a67942931582..d19854c98184 100644 --- a/drivers/md/dm-ioctl.c +++ b/drivers/md/dm-ioctl.c | |||
@@ -56,6 +56,11 @@ static void dm_hash_remove_all(int keep_open_devices); | |||
56 | */ | 56 | */ |
57 | static DECLARE_RWSEM(_hash_lock); | 57 | static DECLARE_RWSEM(_hash_lock); |
58 | 58 | ||
59 | /* | ||
60 | * Protects use of mdptr to obtain hash cell name and uuid from mapped device. | ||
61 | */ | ||
62 | static DEFINE_MUTEX(dm_hash_cells_mutex); | ||
63 | |||
59 | static void init_buckets(struct list_head *buckets) | 64 | static void init_buckets(struct list_head *buckets) |
60 | { | 65 | { |
61 | unsigned int i; | 66 | unsigned int i; |
@@ -206,7 +211,9 @@ static int dm_hash_insert(const char *name, const char *uuid, struct mapped_devi | |||
206 | list_add(&cell->uuid_list, _uuid_buckets + hash_str(uuid)); | 211 | list_add(&cell->uuid_list, _uuid_buckets + hash_str(uuid)); |
207 | } | 212 | } |
208 | dm_get(md); | 213 | dm_get(md); |
214 | mutex_lock(&dm_hash_cells_mutex); | ||
209 | dm_set_mdptr(md, cell); | 215 | dm_set_mdptr(md, cell); |
216 | mutex_unlock(&dm_hash_cells_mutex); | ||
210 | up_write(&_hash_lock); | 217 | up_write(&_hash_lock); |
211 | 218 | ||
212 | return 0; | 219 | return 0; |
@@ -224,7 +231,9 @@ static void __hash_remove(struct hash_cell *hc) | |||
224 | /* remove from the dev hash */ | 231 | /* remove from the dev hash */ |
225 | list_del(&hc->uuid_list); | 232 | list_del(&hc->uuid_list); |
226 | list_del(&hc->name_list); | 233 | list_del(&hc->name_list); |
234 | mutex_lock(&dm_hash_cells_mutex); | ||
227 | dm_set_mdptr(hc->md, NULL); | 235 | dm_set_mdptr(hc->md, NULL); |
236 | mutex_unlock(&dm_hash_cells_mutex); | ||
228 | 237 | ||
229 | table = dm_get_table(hc->md); | 238 | table = dm_get_table(hc->md); |
230 | if (table) { | 239 | if (table) { |
@@ -321,7 +330,9 @@ static int dm_hash_rename(uint32_t cookie, const char *old, const char *new) | |||
321 | */ | 330 | */ |
322 | list_del(&hc->name_list); | 331 | list_del(&hc->name_list); |
323 | old_name = hc->name; | 332 | old_name = hc->name; |
333 | mutex_lock(&dm_hash_cells_mutex); | ||
324 | hc->name = new_name; | 334 | hc->name = new_name; |
335 | mutex_unlock(&dm_hash_cells_mutex); | ||
325 | list_add(&hc->name_list, _name_buckets + hash_str(new_name)); | 336 | list_add(&hc->name_list, _name_buckets + hash_str(new_name)); |
326 | 337 | ||
327 | /* | 338 | /* |
@@ -1582,8 +1593,7 @@ int dm_copy_name_and_uuid(struct mapped_device *md, char *name, char *uuid) | |||
1582 | if (!md) | 1593 | if (!md) |
1583 | return -ENXIO; | 1594 | return -ENXIO; |
1584 | 1595 | ||
1585 | dm_get(md); | 1596 | mutex_lock(&dm_hash_cells_mutex); |
1586 | down_read(&_hash_lock); | ||
1587 | hc = dm_get_mdptr(md); | 1597 | hc = dm_get_mdptr(md); |
1588 | if (!hc || hc->md != md) { | 1598 | if (!hc || hc->md != md) { |
1589 | r = -ENXIO; | 1599 | r = -ENXIO; |
@@ -1596,8 +1606,7 @@ int dm_copy_name_and_uuid(struct mapped_device *md, char *name, char *uuid) | |||
1596 | strcpy(uuid, hc->uuid ? : ""); | 1606 | strcpy(uuid, hc->uuid ? : ""); |
1597 | 1607 | ||
1598 | out: | 1608 | out: |
1599 | up_read(&_hash_lock); | 1609 | mutex_unlock(&dm_hash_cells_mutex); |
1600 | dm_put(md); | ||
1601 | 1610 | ||
1602 | return r; | 1611 | return r; |
1603 | } | 1612 | } |