]>
Commit | Line | Data |
---|---|---|
2cb7cef9 BS |
1 | Patch-mainline: 2.6.30 |
2 | References: bnc#486430 | |
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 | |
7 | ||
8 | upgrade_mode() sets bdev to NULL temporarily, and does not have any | |
9 | locking to exclude anything from seeing that NULL. | |
10 | ||
11 | In dm_table_any_congested() bdev_get_queue() can dereference that NULL and | |
12 | cause a reported oops. | |
13 | ||
14 | Fix this by not changing that field during the mode upgrade. | |
15 | ||
16 | Cc: stable@kernel.org | |
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> | |
20 | ||
21 | --- | |
22 | drivers/md/dm-table.c | 32 +++++++++++++++++--------------- | |
23 | 1 file changed, 17 insertions(+), 15 deletions(-) | |
24 | ||
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 | |
28 | } | |
29 | ||
30 | /* | |
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 | |
34 | - * device. | |
35 | + * device and not to touch the existing bdev field in case | |
36 | + * it is accessed concurrently inside dm_table_any_congested(). | |
37 | */ | |
38 | static int upgrade_mode(struct dm_dev *dd, int new_mode, struct mapped_device *md) | |
39 | { | |
40 | int r; | |
41 | - struct dm_dev dd_copy; | |
42 | - dev_t dev = dd->bdev->bd_dev; | |
43 | + struct dm_dev dd_new, dd_old; | |
44 | ||
45 | - dd_copy = *dd; | |
46 | + dd_new = dd_old = *dd; | |
47 | ||
48 | - dd->mode = new_mode; | |
49 | - dd->bdev = NULL; | |
50 | - r = open_dev(dd, dev, md); | |
51 | + dd_new.mode = new_mode; | |
52 | + dd_new.bdev = NULL; | |
53 | + | |
54 | + r = open_dev(&dd_new, dd->bdev->bd_dev, md); | |
55 | if (r == -EROFS) { | |
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); | |
60 | } | |
61 | - if (!r) | |
62 | - close_dev(&dd_copy, md); | |
63 | - else | |
64 | - *dd = dd_copy; | |
65 | + if (r) | |
66 | + return r; | |
67 | ||
68 | - return r; | |
69 | + dd->mode |= new_mode; | |
70 | + close_dev(&dd_old, md); | |
71 | + | |
72 | + return 0; | |
73 | } | |
74 | ||
75 | /* |