aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/md
diff options
context:
space:
mode:
authorChandra Seetharaman <sekharan@us.ibm.com>2008-10-01 09:39:27 -0400
committerAlasdair G Kergon <agk@redhat.com>2008-10-01 09:39:27 -0400
commit7253a33434245ee8243897559188186df65f3611 (patch)
tree24156c85ddd38319ab92ae56a5c585c0f2e273cc /drivers/md
parentb01cd5ac43b00c49759c126c21e7d22c7e80b245 (diff)
dm mpath: add missing path switching locking
Moving the path activation to workqueue along with scsi_dh patches introduced a race. It is due to the fact that the current_pgpath (in the multipath data structure) can be modified if changes happen in any of the paths leading to the lun. If the changes lead to current_pgpath being set to NULL, then it leads to the invalid access which results in the panic below. This patch fixes that by storing the pgpath to activate in the multipath data structure and properly protecting it. Note that if activate_path is called twice in succession with different pgpath, with the second one being called before the first one is done, then activate path will be called twice for the second pgpath, which is fine. Unable to handle kernel paging request for data at address 0x00000020 Faulting instruction address: 0xd000000000aa1844 cpu 0x1: Vector: 300 (Data Access) at [c00000006b987a80] pc: d000000000aa1844: .activate_path+0x30/0x218 [dm_multipath] lr: c000000000087a2c: .run_workqueue+0x114/0x204 sp: c00000006b987d00 msr: 8000000000009032 dar: 20 dsisr: 40000000 current = 0xc0000000676bb3f0 paca = 0xc0000000006f3680 pid = 2528, comm = kmpath_handlerd enter ? for help [c00000006b987da0] c000000000087a2c .run_workqueue+0x114/0x204 [c00000006b987e40] c000000000088b58 .worker_thread+0x120/0x144 [c00000006b987f00] c00000000008ca70 .kthread+0x78/0xc4 [c00000006b987f90] c000000000027cc8 .kernel_thread+0x4c/0x68 Signed-off-by: Chandra Seetharaman <sekharan@us.ibm.com> Signed-off-by: Alasdair G Kergon <agk@redhat.com>
Diffstat (limited to 'drivers/md')
-rw-r--r--drivers/md/dm-mpath.c16
1 files changed, 15 insertions, 1 deletions
diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c
index 71dd65aa31b6..c2fcf28b4c70 100644
--- a/drivers/md/dm-mpath.c
+++ b/drivers/md/dm-mpath.c
@@ -63,6 +63,7 @@ struct multipath {
63 63
64 const char *hw_handler_name; 64 const char *hw_handler_name;
65 struct work_struct activate_path; 65 struct work_struct activate_path;
66 struct pgpath *pgpath_to_activate;
66 unsigned nr_priority_groups; 67 unsigned nr_priority_groups;
67 struct list_head priority_groups; 68 struct list_head priority_groups;
68 unsigned pg_init_required; /* pg_init needs calling? */ 69 unsigned pg_init_required; /* pg_init needs calling? */
@@ -146,6 +147,7 @@ static struct priority_group *alloc_priority_group(void)
146 147
147static void free_pgpaths(struct list_head *pgpaths, struct dm_target *ti) 148static void free_pgpaths(struct list_head *pgpaths, struct dm_target *ti)
148{ 149{
150 unsigned long flags;
149 struct pgpath *pgpath, *tmp; 151 struct pgpath *pgpath, *tmp;
150 struct multipath *m = ti->private; 152 struct multipath *m = ti->private;
151 153
@@ -154,6 +156,10 @@ static void free_pgpaths(struct list_head *pgpaths, struct dm_target *ti)
154 if (m->hw_handler_name) 156 if (m->hw_handler_name)
155 scsi_dh_detach(bdev_get_queue(pgpath->path.dev->bdev)); 157 scsi_dh_detach(bdev_get_queue(pgpath->path.dev->bdev));
156 dm_put_device(ti, pgpath->path.dev); 158 dm_put_device(ti, pgpath->path.dev);
159 spin_lock_irqsave(&m->lock, flags);
160 if (m->pgpath_to_activate == pgpath)
161 m->pgpath_to_activate = NULL;
162 spin_unlock_irqrestore(&m->lock, flags);
157 free_pgpath(pgpath); 163 free_pgpath(pgpath);
158 } 164 }
159} 165}
@@ -421,6 +427,7 @@ static void process_queued_ios(struct work_struct *work)
421 __choose_pgpath(m); 427 __choose_pgpath(m);
422 428
423 pgpath = m->current_pgpath; 429 pgpath = m->current_pgpath;
430 m->pgpath_to_activate = m->current_pgpath;
424 431
425 if ((pgpath && !m->queue_io) || 432 if ((pgpath && !m->queue_io) ||
426 (!pgpath && !m->queue_if_no_path)) 433 (!pgpath && !m->queue_if_no_path))
@@ -1093,8 +1100,15 @@ static void activate_path(struct work_struct *work)
1093 int ret; 1100 int ret;
1094 struct multipath *m = 1101 struct multipath *m =
1095 container_of(work, struct multipath, activate_path); 1102 container_of(work, struct multipath, activate_path);
1096 struct dm_path *path = &m->current_pgpath->path; 1103 struct dm_path *path;
1104 unsigned long flags;
1097 1105
1106 spin_lock_irqsave(&m->lock, flags);
1107 path = &m->pgpath_to_activate->path;
1108 m->pgpath_to_activate = NULL;
1109 spin_unlock_irqrestore(&m->lock, flags);
1110 if (!path)
1111 return;
1098 ret = scsi_dh_activate(bdev_get_queue(path->dev->bdev)); 1112 ret = scsi_dh_activate(bdev_get_queue(path->dev->bdev));
1099 pg_init_done(path, ret); 1113 pg_init_done(path, ret);
1100} 1114}