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>
This commit is contained in:
committed by
Alasdair G Kergon
parent
b34578a484
commit
60c856c8e2
@@ -229,19 +229,21 @@ static void __insert_origin(struct origin *o)
|
|||||||
*/
|
*/
|
||||||
static int register_snapshot(struct dm_snapshot *snap)
|
static int register_snapshot(struct dm_snapshot *snap)
|
||||||
{
|
{
|
||||||
struct origin *o;
|
struct origin *o, *new_o;
|
||||||
struct block_device *bdev = snap->origin->bdev;
|
struct block_device *bdev = snap->origin->bdev;
|
||||||
|
|
||||||
|
new_o = kmalloc(sizeof(*new_o), GFP_KERNEL);
|
||||||
|
if (!new_o)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
down_write(&_origins_lock);
|
down_write(&_origins_lock);
|
||||||
o = __lookup_origin(bdev);
|
o = __lookup_origin(bdev);
|
||||||
|
|
||||||
if (!o) {
|
if (o)
|
||||||
|
kfree(new_o);
|
||||||
|
else {
|
||||||
/* New origin */
|
/* New origin */
|
||||||
o = kmalloc(sizeof(*o), GFP_KERNEL);
|
o = new_o;
|
||||||
if (!o) {
|
|
||||||
up_write(&_origins_lock);
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Initialise the struct */
|
/* Initialise the struct */
|
||||||
INIT_LIST_HEAD(&o->snapshots);
|
INIT_LIST_HEAD(&o->snapshots);
|
||||||
|
Reference in New Issue
Block a user