From: Mikulas Patocka <[email-masked]>

This patch prevents the multipath target from changing the device
handler once it has been set on a device.  This avoids a kernel crash
that can happen when changing it.

When we reload a multipath device, there are two instances of the
multipath target - the first instance that is active and the second
instance that is being constructed with "ctr" method.

If the multipath constructor finds out that the device is using a
different device handler, it detaches the existing handler and attaches a
new handler. However, the first instance of the multipath target still
exists and processes requests. If the first instance sends some
path-management request with scsi_dh_activate and the second instance
detaches the device handler while the path-management request is in
flight, a crash happens. The reason for the crash is that the endio
routine for the path-management request is working with structures that
were freed when the handler was detached.

There is no practical need to change device handlers on an active device,
so this patch disables it.

References:
  http://bugzilla.redhat.com/912245 
  http://bugzilla.redhat.com/902595

Signed-off-by: Mikulas Patocka <[email-masked]>
Cc: [email-masked]
Acked-by: Mike Snitzer <[email-masked]>
Signed-off-by: Alasdair G Kergon <[email-masked]>

// FIXME Make relevant parts of bugzillas (with kernel error messages) public

---
 drivers/md/dm-mpath.c |   13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

Index: linux/drivers/md/dm-mpath.c
===================================================================
--- linux.orig/drivers/md/dm-mpath.c
+++ linux/drivers/md/dm-mpath.c
@@ -618,12 +618,13 @@ static struct pgpath *parse_path(struct 
 		 */
 		r = scsi_dh_attach(q, m->hw_handler_name);
 		if (r == -EBUSY) {
-			/*
-			 * Already attached to different hw_handler:
-			 * try to reattach with correct one.
-			 */
-			scsi_dh_detach(q);
-			r = scsi_dh_attach(q, m->hw_handler_name);
+			ti->error = "a different device handler is already attached";
+			attached_handler_name = scsi_dh_attached_handler_name(q, GFP_KERNEL);
+			DMERR("%s: Device handler %s already attached. "
+			      "Deactivate device to change device handler.",
+				p->path.dev->name, attached_handler_name);
+			kfree(attached_handler_name);
+			goto bad;
 		}
 
 		if (r < 0) {