diff options
author | Kiyoshi Ueda <k-ueda@ct.jp.nec.com> | 2010-08-11 23:13:55 -0400 |
---|---|---|
committer | Alasdair G Kergon <agk@redhat.com> | 2010-08-11 23:13:55 -0400 |
commit | 98f332855effef02aeb738e4d62e9a5b903c52fd (patch) | |
tree | b10d7f632022415ddb047c2bacbaa1f861a76161 /drivers/md/dm-ioctl.c | |
parent | abdc568b0540bec6d3e0afebac496adef1189b77 (diff) |
dm ioctl: release _hash_lock between devices in remove_all
This patch changes dm_hash_remove_all() to release _hash_lock when
removing a device. After removing the device, dm_hash_remove_all()
takes _hash_lock and searches the hash from scratch again.
This patch is a preparation for the next patch, which changes device
deletion code to wait for md reference to be 0. Without this patch,
the wait in the next patch may cause AB-BA deadlock:
CPU0 CPU1
-----------------------------------------------------------------------
dm_hash_remove_all()
down_write(_hash_lock)
table_status()
md = find_device()
dm_get(md)
<increment md->holders>
dm_get_live_or_inactive_table()
dm_get_inactive_table()
down_write(_hash_lock)
<in the md deletion code>
<wait for md->holders to be 0>
Signed-off-by: Kiyoshi Ueda <k-ueda@ct.jp.nec.com>
Signed-off-by: Jun'ichi Nomura <j-nomura@ce.jp.nec.com>
Cc: stable@kernel.org
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 | 44 |
1 files changed, 25 insertions, 19 deletions
diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c index 79ee5ba217f2..6a6d475f8e80 100644 --- a/drivers/md/dm-ioctl.c +++ b/drivers/md/dm-ioctl.c | |||
@@ -249,40 +249,46 @@ static void __hash_remove(struct hash_cell *hc) | |||
249 | 249 | ||
250 | static void dm_hash_remove_all(int keep_open_devices) | 250 | static void dm_hash_remove_all(int keep_open_devices) |
251 | { | 251 | { |
252 | int i, dev_skipped, dev_removed; | 252 | int i, dev_skipped; |
253 | struct hash_cell *hc; | 253 | struct hash_cell *hc; |
254 | struct list_head *tmp, *n; | 254 | struct mapped_device *md; |
255 | |||
256 | retry: | ||
257 | dev_skipped = 0; | ||
255 | 258 | ||
256 | down_write(&_hash_lock); | 259 | down_write(&_hash_lock); |
257 | 260 | ||
258 | retry: | ||
259 | dev_skipped = dev_removed = 0; | ||
260 | for (i = 0; i < NUM_BUCKETS; i++) { | 261 | for (i = 0; i < NUM_BUCKETS; i++) { |
261 | list_for_each_safe (tmp, n, _name_buckets + i) { | 262 | list_for_each_entry(hc, _name_buckets + i, name_list) { |
262 | hc = list_entry(tmp, struct hash_cell, name_list); | 263 | md = hc->md; |
264 | dm_get(md); | ||
263 | 265 | ||
264 | if (keep_open_devices && | 266 | if (keep_open_devices && dm_lock_for_deletion(md)) { |
265 | dm_lock_for_deletion(hc->md)) { | 267 | dm_put(md); |
266 | dev_skipped++; | 268 | dev_skipped++; |
267 | continue; | 269 | continue; |
268 | } | 270 | } |
271 | |||
269 | __hash_remove(hc); | 272 | __hash_remove(hc); |
270 | dev_removed = 1; | ||
271 | } | ||
272 | } | ||
273 | 273 | ||
274 | /* | 274 | up_write(&_hash_lock); |
275 | * Some mapped devices may be using other mapped devices, so if any | ||
276 | * still exist, repeat until we make no further progress. | ||
277 | */ | ||
278 | if (dev_skipped) { | ||
279 | if (dev_removed) | ||
280 | goto retry; | ||
281 | 275 | ||
282 | DMWARN("remove_all left %d open device(s)", dev_skipped); | 276 | dm_put(md); |
277 | |||
278 | /* | ||
279 | * Some mapped devices may be using other mapped | ||
280 | * devices, so repeat until we make no further | ||
281 | * progress. If a new mapped device is created | ||
282 | * here it will also get removed. | ||
283 | */ | ||
284 | goto retry; | ||
285 | } | ||
283 | } | 286 | } |
284 | 287 | ||
285 | up_write(&_hash_lock); | 288 | up_write(&_hash_lock); |
289 | |||
290 | if (dev_skipped) | ||
291 | DMWARN("remove_all left %d open device(s)", dev_skipped); | ||
286 | } | 292 | } |
287 | 293 | ||
288 | static struct mapped_device *dm_hash_rename(struct dm_ioctl *param, | 294 | static struct mapped_device *dm_hash_rename(struct dm_ioctl *param, |