diff options
author | Hannes Reinecke <hare@suse.de> | 2008-07-17 20:49:02 -0400 |
---|---|---|
committer | James Bottomley <James.Bottomley@HansenPartnership.com> | 2008-07-26 15:14:53 -0400 |
commit | ae11b1b36da726a8a93409b896704edc6b4f3402 (patch) | |
tree | 737e3602cb426c87f64ad26ccbfc12ce707d7b9e /drivers | |
parent | 057ea7c9683c3d684128cced796f03c179ecf1c2 (diff) |
[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>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/md/dm-mpath.c | 13 | ||||
-rw-r--r-- | drivers/scsi/device_handler/scsi_dh.c | 64 |
2 files changed, 77 insertions, 0 deletions
diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c index fea966d66f98..71dd65aa31b6 100644 --- a/drivers/md/dm-mpath.c +++ b/drivers/md/dm-mpath.c | |||
@@ -147,9 +147,12 @@ static struct priority_group *alloc_priority_group(void) | |||
147 | static void free_pgpaths(struct list_head *pgpaths, struct dm_target *ti) | 147 | static void free_pgpaths(struct list_head *pgpaths, struct dm_target *ti) |
148 | { | 148 | { |
149 | struct pgpath *pgpath, *tmp; | 149 | struct pgpath *pgpath, *tmp; |
150 | struct multipath *m = ti->private; | ||
150 | 151 | ||
151 | list_for_each_entry_safe(pgpath, tmp, pgpaths, list) { | 152 | list_for_each_entry_safe(pgpath, tmp, pgpaths, list) { |
152 | list_del(&pgpath->list); | 153 | list_del(&pgpath->list); |
154 | if (m->hw_handler_name) | ||
155 | scsi_dh_detach(bdev_get_queue(pgpath->path.dev->bdev)); | ||
153 | dm_put_device(ti, pgpath->path.dev); | 156 | dm_put_device(ti, pgpath->path.dev); |
154 | free_pgpath(pgpath); | 157 | free_pgpath(pgpath); |
155 | } | 158 | } |
@@ -548,6 +551,7 @@ static struct pgpath *parse_path(struct arg_set *as, struct path_selector *ps, | |||
548 | { | 551 | { |
549 | int r; | 552 | int r; |
550 | struct pgpath *p; | 553 | struct pgpath *p; |
554 | struct multipath *m = ti->private; | ||
551 | 555 | ||
552 | /* we need at least a path arg */ | 556 | /* we need at least a path arg */ |
553 | if (as->argc < 1) { | 557 | if (as->argc < 1) { |
@@ -566,6 +570,15 @@ static struct pgpath *parse_path(struct arg_set *as, struct path_selector *ps, | |||
566 | goto bad; | 570 | goto bad; |
567 | } | 571 | } |
568 | 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 | } | ||
580 | } | ||
581 | |||
569 | r = ps->type->add_path(ps, &p->path, as->argc, as->argv, &ti->error); | 582 | r = ps->type->add_path(ps, &p->path, as->argc, as->argv, &ti->error); |
570 | if (r) { | 583 | if (r) { |
571 | dm_put_device(ti, p->path.dev); | 584 | dm_put_device(ti, p->path.dev); |
diff --git a/drivers/scsi/device_handler/scsi_dh.c b/drivers/scsi/device_handler/scsi_dh.c index e90df9dd3e65..a9159681ff27 100644 --- a/drivers/scsi/device_handler/scsi_dh.c +++ b/drivers/scsi/device_handler/scsi_dh.c | |||
@@ -369,6 +369,70 @@ int scsi_dh_handler_exist(const char *name) | |||
369 | } | 369 | } |
370 | EXPORT_SYMBOL_GPL(scsi_dh_handler_exist); | 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 | static struct notifier_block scsi_dh_nb = { | 436 | static struct notifier_block scsi_dh_nb = { |
373 | .notifier_call = scsi_dh_notifier | 437 | .notifier_call = scsi_dh_notifier |
374 | }; | 438 | }; |