Commit 7253a33434245ee8243897559188186df65f3611

Authored by Chandra Seetharaman
Committed by Alasdair G Kergon
1 parent b01cd5ac43

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>

Showing 1 changed file with 15 additions and 1 deletions Side-by-side Diff

drivers/md/dm-mpath.c
... ... @@ -63,6 +63,7 @@
63 63  
64 64 const char *hw_handler_name;
65 65 struct work_struct activate_path;
  66 + struct pgpath *pgpath_to_activate;
66 67 unsigned nr_priority_groups;
67 68 struct list_head priority_groups;
68 69 unsigned pg_init_required; /* pg_init needs calling? */
... ... @@ -146,6 +147,7 @@
146 147  
147 148 static void free_pgpaths(struct list_head *pgpaths, struct dm_target *ti)
148 149 {
  150 + unsigned long flags;
149 151 struct pgpath *pgpath, *tmp;
150 152 struct multipath *m = ti->private;
151 153  
... ... @@ -154,6 +156,10 @@
154 156 if (m->hw_handler_name)
155 157 scsi_dh_detach(bdev_get_queue(pgpath->path.dev->bdev));
156 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 163 free_pgpath(pgpath);
158 164 }
159 165 }
... ... @@ -421,6 +427,7 @@
421 427 __choose_pgpath(m);
422 428  
423 429 pgpath = m->current_pgpath;
  430 + m->pgpath_to_activate = m->current_pgpath;
424 431  
425 432 if ((pgpath && !m->queue_io) ||
426 433 (!pgpath && !m->queue_if_no_path))
427 434  
... ... @@ -1093,8 +1100,15 @@
1093 1100 int ret;
1094 1101 struct multipath *m =
1095 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 1112 ret = scsi_dh_activate(bdev_get_queue(path->dev->bdev));
1099 1113 pg_init_done(path, ret);
1100 1114 }