Commit ae11b1b36da726a8a93409b896704edc6b4f3402
Committed by
James Bottomley
1 parent
057ea7c968
Exists in
master
and in
7 other branches
[SCSI] scsi_dh: attach to hardware handler from dm-mpath
multipath keeps a separate device table which may be more current than the built-in one. So we should make sure to always call ->attach whenever a multipath map with hardware handler is instantiated. And we should call ->detach on removal, too. [sekharan: update as per comments from agk] Signed-off-by: Hannes Reinecke <hare@suse.de> Signed-off-by: Chandra Seetharaman <sekharan@us.ibm.com> Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
Showing 3 changed files with 87 additions and 0 deletions Side-by-side Diff
drivers/md/dm-mpath.c
... | ... | @@ -147,9 +147,12 @@ |
147 | 147 | static void free_pgpaths(struct list_head *pgpaths, struct dm_target *ti) |
148 | 148 | { |
149 | 149 | struct pgpath *pgpath, *tmp; |
150 | + struct multipath *m = ti->private; | |
150 | 151 | |
151 | 152 | list_for_each_entry_safe(pgpath, tmp, pgpaths, list) { |
152 | 153 | list_del(&pgpath->list); |
154 | + if (m->hw_handler_name) | |
155 | + scsi_dh_detach(bdev_get_queue(pgpath->path.dev->bdev)); | |
153 | 156 | dm_put_device(ti, pgpath->path.dev); |
154 | 157 | free_pgpath(pgpath); |
155 | 158 | } |
... | ... | @@ -548,6 +551,7 @@ |
548 | 551 | { |
549 | 552 | int r; |
550 | 553 | struct pgpath *p; |
554 | + struct multipath *m = ti->private; | |
551 | 555 | |
552 | 556 | /* we need at least a path arg */ |
553 | 557 | if (as->argc < 1) { |
... | ... | @@ -564,6 +568,15 @@ |
564 | 568 | if (r) { |
565 | 569 | ti->error = "error getting device"; |
566 | 570 | goto bad; |
571 | + } | |
572 | + | |
573 | + if (m->hw_handler_name) { | |
574 | + r = scsi_dh_attach(bdev_get_queue(p->path.dev->bdev), | |
575 | + m->hw_handler_name); | |
576 | + if (r < 0) { | |
577 | + dm_put_device(ti, p->path.dev); | |
578 | + goto bad; | |
579 | + } | |
567 | 580 | } |
568 | 581 | |
569 | 582 | r = ps->type->add_path(ps, &p->path, as->argc, as->argv, &ti->error); |
drivers/scsi/device_handler/scsi_dh.c
... | ... | @@ -369,6 +369,70 @@ |
369 | 369 | } |
370 | 370 | EXPORT_SYMBOL_GPL(scsi_dh_handler_exist); |
371 | 371 | |
372 | +/* | |
373 | + * scsi_dh_handler_attach - Attach device handler | |
374 | + * @sdev - sdev the handler should be attached to | |
375 | + * @name - name of the handler to attach | |
376 | + */ | |
377 | +int scsi_dh_attach(struct request_queue *q, const char *name) | |
378 | +{ | |
379 | + unsigned long flags; | |
380 | + struct scsi_device *sdev; | |
381 | + struct scsi_device_handler *scsi_dh; | |
382 | + int err = 0; | |
383 | + | |
384 | + scsi_dh = get_device_handler(name); | |
385 | + if (!scsi_dh) | |
386 | + return -EINVAL; | |
387 | + | |
388 | + spin_lock_irqsave(q->queue_lock, flags); | |
389 | + sdev = q->queuedata; | |
390 | + if (!sdev || !get_device(&sdev->sdev_gendev)) | |
391 | + err = -ENODEV; | |
392 | + spin_unlock_irqrestore(q->queue_lock, flags); | |
393 | + | |
394 | + if (!err) { | |
395 | + err = scsi_dh_handler_attach(sdev, scsi_dh); | |
396 | + | |
397 | + put_device(&sdev->sdev_gendev); | |
398 | + } | |
399 | + return err; | |
400 | +} | |
401 | +EXPORT_SYMBOL_GPL(scsi_dh_attach); | |
402 | + | |
403 | +/* | |
404 | + * scsi_dh_handler_detach - Detach device handler | |
405 | + * @sdev - sdev the handler should be detached from | |
406 | + * | |
407 | + * This function will detach the device handler only | |
408 | + * if the sdev is not part of the internal list, ie | |
409 | + * if it has been attached manually. | |
410 | + */ | |
411 | +void scsi_dh_detach(struct request_queue *q) | |
412 | +{ | |
413 | + unsigned long flags; | |
414 | + struct scsi_device *sdev; | |
415 | + struct scsi_device_handler *scsi_dh = NULL; | |
416 | + | |
417 | + spin_lock_irqsave(q->queue_lock, flags); | |
418 | + sdev = q->queuedata; | |
419 | + if (!sdev || !get_device(&sdev->sdev_gendev)) | |
420 | + sdev = NULL; | |
421 | + spin_unlock_irqrestore(q->queue_lock, flags); | |
422 | + | |
423 | + if (!sdev) | |
424 | + return; | |
425 | + | |
426 | + if (sdev->scsi_dh_data) { | |
427 | + /* if sdev is not on internal list, detach */ | |
428 | + scsi_dh = sdev->scsi_dh_data->scsi_dh; | |
429 | + if (!device_handler_match(scsi_dh, sdev)) | |
430 | + scsi_dh_handler_detach(sdev, scsi_dh); | |
431 | + } | |
432 | + put_device(&sdev->sdev_gendev); | |
433 | +} | |
434 | +EXPORT_SYMBOL_GPL(scsi_dh_detach); | |
435 | + | |
372 | 436 | static struct notifier_block scsi_dh_nb = { |
373 | 437 | .notifier_call = scsi_dh_notifier |
374 | 438 | }; |
include/scsi/scsi_dh.h
... | ... | @@ -58,6 +58,8 @@ |
58 | 58 | #if defined(CONFIG_SCSI_DH) || defined(CONFIG_SCSI_DH_MODULE) |
59 | 59 | extern int scsi_dh_activate(struct request_queue *); |
60 | 60 | extern int scsi_dh_handler_exist(const char *); |
61 | +extern int scsi_dh_attach(struct request_queue *, const char *); | |
62 | +extern void scsi_dh_detach(struct request_queue *); | |
61 | 63 | #else |
62 | 64 | static inline int scsi_dh_activate(struct request_queue *req) |
63 | 65 | { |
... | ... | @@ -66,6 +68,14 @@ |
66 | 68 | static inline int scsi_dh_handler_exist(const char *name) |
67 | 69 | { |
68 | 70 | return 0; |
71 | +} | |
72 | +static inline int scsi_dh_attach(struct request_queue *req, const char *name) | |
73 | +{ | |
74 | + return SCSI_DH_NOSYS; | |
75 | +} | |
76 | +static inline void scsi_dh_detach(struct request_queue *q) | |
77 | +{ | |
78 | + return; | |
69 | 79 | } |
70 | 80 | #endif |