diff options
author | Mikulas Patocka <mpatocka@redhat.com> | 2008-10-30 09:33:12 -0400 |
---|---|---|
committer | Alasdair G Kergon <agk@redhat.com> | 2008-10-30 09:33:12 -0400 |
commit | 60c856c8e2f57a3f69c505735ef66e3719ea0bd6 (patch) | |
tree | a6295bec9c1fa01885ef14befa4ae3618f51ca57 /drivers/md/dm-snap.c | |
parent | b34578a48459ed1bd5396631aaa4a65d6bcc7726 (diff) |
dm snapshot: fix register_snapshot deadlock
register_snapshot() performs a GFP_KERNEL allocation while holding
_origins_lock for write, but that could write out dirty pages onto a
device that attempts to acquire _origins_lock for read, resulting in
deadlock.
So move the allocation up before taking the lock.
This path is not performance-critical, so it doesn't matter that we
allocate memory and free it if we find that we won't need it.
Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
Signed-off-by: Alasdair G Kergon <agk@redhat.com>
Diffstat (limited to 'drivers/md/dm-snap.c')
-rw-r--r-- | drivers/md/dm-snap.c | 16 |
1 files changed, 9 insertions, 7 deletions
diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c index b2d9d1ac28ad..746603b42f86 100644 --- a/drivers/md/dm-snap.c +++ b/drivers/md/dm-snap.c | |||
@@ -229,19 +229,21 @@ static void __insert_origin(struct origin *o) | |||
229 | */ | 229 | */ |
230 | static int register_snapshot(struct dm_snapshot *snap) | 230 | static int register_snapshot(struct dm_snapshot *snap) |
231 | { | 231 | { |
232 | struct origin *o; | 232 | struct origin *o, *new_o; |
233 | struct block_device *bdev = snap->origin->bdev; | 233 | struct block_device *bdev = snap->origin->bdev; |
234 | 234 | ||
235 | new_o = kmalloc(sizeof(*new_o), GFP_KERNEL); | ||
236 | if (!new_o) | ||
237 | return -ENOMEM; | ||
238 | |||
235 | down_write(&_origins_lock); | 239 | down_write(&_origins_lock); |
236 | o = __lookup_origin(bdev); | 240 | o = __lookup_origin(bdev); |
237 | 241 | ||
238 | if (!o) { | 242 | if (o) |
243 | kfree(new_o); | ||
244 | else { | ||
239 | /* New origin */ | 245 | /* New origin */ |
240 | o = kmalloc(sizeof(*o), GFP_KERNEL); | 246 | o = new_o; |
241 | if (!o) { | ||
242 | up_write(&_origins_lock); | ||
243 | return -ENOMEM; | ||
244 | } | ||
245 | 247 | ||
246 | /* Initialise the struct */ | 248 | /* Initialise the struct */ |
247 | INIT_LIST_HEAD(&o->snapshots); | 249 | INIT_LIST_HEAD(&o->snapshots); |