3 X-Git: 570b9d968bf9b16974252ef7cbce73fa6dac34f3 Mon Sep 17 00:00:00 2001
4 From: Alasdair G Kergon <agk@redhat.com>
5 Date: Thu, 2 Apr 2009 19:55:28 +0100
6 Subject: [PATCH] dm table: fix upgrade mode race
8 upgrade_mode() sets bdev to NULL temporarily, and does not have any
9 locking to exclude anything from seeing that NULL.
11 In dm_table_any_congested() bdev_get_queue() can dereference that NULL and
12 cause a reported oops.
14 Fix this by not changing that field during the mode upgrade.
17 Cc: Neil Brown <neilb@suse.de>
18 Signed-off-by: Alasdair G Kergon <agk@redhat.com>
19 Acked-by: Neil Brown <neilb@suse.de>
22 drivers/md/dm-table.c | 32 +++++++++++++++++---------------
23 1 file changed, 17 insertions(+), 15 deletions(-)
25 --- linux-2.6.27-SLE11_BRANCH.orig/drivers/md/dm-table.c
26 +++ linux-2.6.27-SLE11_BRANCH/drivers/md/dm-table.c
27 @@ -411,31 +411,33 @@ static int check_device_area(struct dm_d
31 - * This upgrades the mode on an already open dm_dev. Being
32 + * This upgrades the mode on an already open dm_dev, being
33 * careful to leave things as they were if we fail to reopen the
35 + * device and not to touch the existing bdev field in case
36 + * it is accessed concurrently inside dm_table_any_congested().
38 static int upgrade_mode(struct dm_dev *dd, int new_mode, struct mapped_device *md)
41 - struct dm_dev dd_copy;
42 - dev_t dev = dd->bdev->bd_dev;
43 + struct dm_dev dd_new, dd_old;
46 + dd_new = dd_old = *dd;
48 - dd->mode = new_mode;
50 - r = open_dev(dd, dev, md);
51 + dd_new.mode = new_mode;
54 + r = open_dev(&dd_new, dd->bdev->bd_dev, md);
56 - dd->mode &= ~FMODE_WRITE;
57 - r = open_dev(dd, dev, md);
58 + dd_new.mode &= ~FMODE_WRITE;
59 + r = open_dev(&dd_new, dd->bdev->bd_dev, md);
62 - close_dev(&dd_copy, md);
69 + dd->mode |= new_mode;
70 + close_dev(&dd_old, md);