diff options
author | Christoph Hellwig <hch@lst.de> | 2015-08-27 08:16:54 -0400 |
---|---|---|
committer | James Bottomley <JBottomley@Odin.com> | 2015-08-28 16:14:54 -0400 |
commit | 1bab0de0274fbe8c8ac92179e6705584c55ed169 (patch) | |
tree | 3a2bbd51706e6ec0a279cbc0011d996de7320aa7 /drivers/scsi/device_handler/scsi_dh.c | |
parent | 36dd5acd196574d41de3e81d8264df475bbb7123 (diff) |
dm-mpath, scsi_dh: don't let dm detach device handlers
While allowing dm-mpath to attach device handlers is a functionality we need
for backwards compatibility reason there is no reason to reference count
them and detach them if dm-mpath stops using the device for some reason.
If the device handler works for the given device it can just stay attached,
and we can take the retain_hw_handler codepath.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Acked-by: Mike Snitzer <snitzer@redhat.com>
Acked-by: Hannes Reinecke <hare@Suse.de>
Signed-off-by: James Bottomley <JBottomley@Odin.com>
Diffstat (limited to 'drivers/scsi/device_handler/scsi_dh.c')
-rw-r--r-- | drivers/scsi/device_handler/scsi_dh.c | 96 |
1 files changed, 22 insertions, 74 deletions
diff --git a/drivers/scsi/device_handler/scsi_dh.c b/drivers/scsi/device_handler/scsi_dh.c index 1efebc9eedfb..869b5bd1ad25 100644 --- a/drivers/scsi/device_handler/scsi_dh.c +++ b/drivers/scsi/device_handler/scsi_dh.c | |||
@@ -100,14 +100,6 @@ static int scsi_dh_handler_attach(struct scsi_device *sdev, | |||
100 | { | 100 | { |
101 | struct scsi_dh_data *d; | 101 | struct scsi_dh_data *d; |
102 | 102 | ||
103 | if (sdev->scsi_dh_data) { | ||
104 | if (sdev->scsi_dh_data->scsi_dh != scsi_dh) | ||
105 | return -EBUSY; | ||
106 | |||
107 | kref_get(&sdev->scsi_dh_data->kref); | ||
108 | return 0; | ||
109 | } | ||
110 | |||
111 | if (!try_module_get(scsi_dh->module)) | 103 | if (!try_module_get(scsi_dh->module)) |
112 | return -EINVAL; | 104 | return -EINVAL; |
113 | 105 | ||
@@ -120,7 +112,6 @@ static int scsi_dh_handler_attach(struct scsi_device *sdev, | |||
120 | } | 112 | } |
121 | 113 | ||
122 | d->scsi_dh = scsi_dh; | 114 | d->scsi_dh = scsi_dh; |
123 | kref_init(&d->kref); | ||
124 | d->sdev = sdev; | 115 | d->sdev = sdev; |
125 | 116 | ||
126 | spin_lock_irq(sdev->request_queue->queue_lock); | 117 | spin_lock_irq(sdev->request_queue->queue_lock); |
@@ -129,12 +120,14 @@ static int scsi_dh_handler_attach(struct scsi_device *sdev, | |||
129 | return 0; | 120 | return 0; |
130 | } | 121 | } |
131 | 122 | ||
132 | static void __detach_handler (struct kref *kref) | 123 | /* |
124 | * scsi_dh_handler_detach - Detach a device handler from a device | ||
125 | * @sdev - SCSI device the device handler should be detached from | ||
126 | */ | ||
127 | static void scsi_dh_handler_detach(struct scsi_device *sdev) | ||
133 | { | 128 | { |
134 | struct scsi_dh_data *scsi_dh_data = | 129 | struct scsi_dh_data *scsi_dh_data = sdev->scsi_dh_data; |
135 | container_of(kref, struct scsi_dh_data, kref); | ||
136 | struct scsi_device_handler *scsi_dh = scsi_dh_data->scsi_dh; | 130 | struct scsi_device_handler *scsi_dh = scsi_dh_data->scsi_dh; |
137 | struct scsi_device *sdev = scsi_dh_data->sdev; | ||
138 | 131 | ||
139 | scsi_dh->detach(sdev); | 132 | scsi_dh->detach(sdev); |
140 | 133 | ||
@@ -147,30 +140,6 @@ static void __detach_handler (struct kref *kref) | |||
147 | } | 140 | } |
148 | 141 | ||
149 | /* | 142 | /* |
150 | * scsi_dh_handler_detach - Detach a device handler from a device | ||
151 | * @sdev - SCSI device the device handler should be detached from | ||
152 | * @scsi_dh - Device handler to be detached | ||
153 | * | ||
154 | * Detach from a device handler. If a device handler is specified, | ||
155 | * only detach if the currently attached handler matches @scsi_dh. | ||
156 | */ | ||
157 | static void scsi_dh_handler_detach(struct scsi_device *sdev, | ||
158 | struct scsi_device_handler *scsi_dh) | ||
159 | { | ||
160 | if (!sdev->scsi_dh_data) | ||
161 | return; | ||
162 | |||
163 | if (scsi_dh && scsi_dh != sdev->scsi_dh_data->scsi_dh) | ||
164 | return; | ||
165 | |||
166 | if (!scsi_dh) | ||
167 | scsi_dh = sdev->scsi_dh_data->scsi_dh; | ||
168 | |||
169 | if (scsi_dh) | ||
170 | kref_put(&sdev->scsi_dh_data->kref, __detach_handler); | ||
171 | } | ||
172 | |||
173 | /* | ||
174 | * Functions for sysfs attribute 'dh_state' | 143 | * Functions for sysfs attribute 'dh_state' |
175 | */ | 144 | */ |
176 | static ssize_t | 145 | static ssize_t |
@@ -198,7 +167,7 @@ store_dh_state(struct device *dev, struct device_attribute *attr, | |||
198 | /* | 167 | /* |
199 | * Detach from a device handler | 168 | * Detach from a device handler |
200 | */ | 169 | */ |
201 | scsi_dh_handler_detach(sdev, scsi_dh); | 170 | scsi_dh_handler_detach(sdev); |
202 | err = 0; | 171 | err = 0; |
203 | } else if (!strncmp(buf, "activate", 8)) { | 172 | } else if (!strncmp(buf, "activate", 8)) { |
204 | /* | 173 | /* |
@@ -290,7 +259,8 @@ static int scsi_dh_notifier(struct notifier_block *nb, | |||
290 | err = scsi_dh_handler_attach(sdev, devinfo); | 259 | err = scsi_dh_handler_attach(sdev, devinfo); |
291 | } else if (action == BUS_NOTIFY_DEL_DEVICE) { | 260 | } else if (action == BUS_NOTIFY_DEL_DEVICE) { |
292 | device_remove_file(dev, &scsi_dh_state_attr); | 261 | device_remove_file(dev, &scsi_dh_state_attr); |
293 | scsi_dh_handler_detach(sdev, NULL); | 262 | if (sdev->scsi_dh_data) |
263 | scsi_dh_handler_detach(sdev); | ||
294 | } | 264 | } |
295 | return err; | 265 | return err; |
296 | } | 266 | } |
@@ -335,7 +305,8 @@ static int scsi_dh_notifier_remove(struct device *dev, void *data) | |||
335 | 305 | ||
336 | sdev = to_scsi_device(dev); | 306 | sdev = to_scsi_device(dev); |
337 | 307 | ||
338 | scsi_dh_handler_detach(sdev, scsi_dh); | 308 | if (sdev->scsi_dh_data && sdev->scsi_dh_data->scsi_dh == scsi_dh) |
309 | scsi_dh_handler_detach(sdev); | ||
339 | 310 | ||
340 | put_device(dev); | 311 | put_device(dev); |
341 | 312 | ||
@@ -517,45 +488,22 @@ int scsi_dh_attach(struct request_queue *q, const char *name) | |||
517 | err = -ENODEV; | 488 | err = -ENODEV; |
518 | spin_unlock_irqrestore(q->queue_lock, flags); | 489 | spin_unlock_irqrestore(q->queue_lock, flags); |
519 | 490 | ||
520 | if (!err) { | 491 | if (err) |
521 | err = scsi_dh_handler_attach(sdev, scsi_dh); | 492 | return err; |
522 | put_device(&sdev->sdev_gendev); | ||
523 | } | ||
524 | return err; | ||
525 | } | ||
526 | EXPORT_SYMBOL_GPL(scsi_dh_attach); | ||
527 | |||
528 | /* | ||
529 | * scsi_dh_detach - Detach device handler | ||
530 | * @q - Request queue that is associated with the scsi_device | ||
531 | * the handler should be detached from | ||
532 | * | ||
533 | * This function will detach the device handler only | ||
534 | * if the sdev is not part of the internal list, ie | ||
535 | * if it has been attached manually. | ||
536 | */ | ||
537 | void scsi_dh_detach(struct request_queue *q) | ||
538 | { | ||
539 | unsigned long flags; | ||
540 | struct scsi_device *sdev; | ||
541 | struct scsi_device_handler *scsi_dh = NULL; | ||
542 | |||
543 | spin_lock_irqsave(q->queue_lock, flags); | ||
544 | sdev = q->queuedata; | ||
545 | if (!sdev || !get_device(&sdev->sdev_gendev)) | ||
546 | sdev = NULL; | ||
547 | spin_unlock_irqrestore(q->queue_lock, flags); | ||
548 | |||
549 | if (!sdev) | ||
550 | return; | ||
551 | 493 | ||
552 | if (sdev->scsi_dh_data) { | 494 | if (sdev->scsi_dh_data) { |
553 | scsi_dh = sdev->scsi_dh_data->scsi_dh; | 495 | if (sdev->scsi_dh_data->scsi_dh != scsi_dh) |
554 | scsi_dh_handler_detach(sdev, scsi_dh); | 496 | err = -EBUSY; |
497 | goto out_put_device; | ||
555 | } | 498 | } |
499 | |||
500 | err = scsi_dh_handler_attach(sdev, scsi_dh); | ||
501 | |||
502 | out_put_device: | ||
556 | put_device(&sdev->sdev_gendev); | 503 | put_device(&sdev->sdev_gendev); |
504 | return err; | ||
557 | } | 505 | } |
558 | EXPORT_SYMBOL_GPL(scsi_dh_detach); | 506 | EXPORT_SYMBOL_GPL(scsi_dh_attach); |
559 | 507 | ||
560 | /* | 508 | /* |
561 | * scsi_dh_attached_handler_name - Get attached device handler's name | 509 | * scsi_dh_attached_handler_name - Get attached device handler's name |