diff options
author | Chandra Seetharaman <sekharan@us.ibm.com> | 2009-06-26 22:30:06 -0400 |
---|---|---|
committer | James Bottomley <James.Bottomley@suse.de> | 2009-08-22 18:51:51 -0400 |
commit | 6c10db72c94818573552fd71c89540da325efdfb (patch) | |
tree | 6afa9b0431d22638a1a11dfe3c5924b8e1a8e2f3 /drivers/scsi/device_handler | |
parent | b4567ca6304a9b31cb2eae62f812e9eb9badcb60 (diff) |
[SCSI] scsi_dh: Reference count scsi_dh_attach
Problem reported: http://marc.info/?l=dm-devel&m=124585978305866&w=2
scsi_dh does not do a refernce count for attach/detach, and this affects
the way it is supposed to work with multipath when a device is not
in the dev_list of the hardware handler.
This patch adds a reference count that counts each attach.
Signed-off-by: Chandra Seetharaman <sekharan@us.ibm.com>
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
Signed-off-by: James Bottomley <James.Bottomley@suse.de>
Diffstat (limited to 'drivers/scsi/device_handler')
-rw-r--r-- | drivers/scsi/device_handler/scsi_dh.c | 23 |
1 files changed, 16 insertions, 7 deletions
diff --git a/drivers/scsi/device_handler/scsi_dh.c b/drivers/scsi/device_handler/scsi_dh.c index a518f2eff19a..53a7385e1b4d 100644 --- a/drivers/scsi/device_handler/scsi_dh.c +++ b/drivers/scsi/device_handler/scsi_dh.c | |||
@@ -153,12 +153,24 @@ static int scsi_dh_handler_attach(struct scsi_device *sdev, | |||
153 | if (sdev->scsi_dh_data) { | 153 | if (sdev->scsi_dh_data) { |
154 | if (sdev->scsi_dh_data->scsi_dh != scsi_dh) | 154 | if (sdev->scsi_dh_data->scsi_dh != scsi_dh) |
155 | err = -EBUSY; | 155 | err = -EBUSY; |
156 | } else if (scsi_dh->attach) | 156 | else |
157 | kref_get(&sdev->scsi_dh_data->kref); | ||
158 | } else if (scsi_dh->attach) { | ||
157 | err = scsi_dh->attach(sdev); | 159 | err = scsi_dh->attach(sdev); |
158 | 160 | if (!err) { | |
161 | kref_init(&sdev->scsi_dh_data->kref); | ||
162 | sdev->scsi_dh_data->sdev = sdev; | ||
163 | } | ||
164 | } | ||
159 | return err; | 165 | return err; |
160 | } | 166 | } |
161 | 167 | ||
168 | static void __detach_handler (struct kref *kref) | ||
169 | { | ||
170 | struct scsi_dh_data *scsi_dh_data = container_of(kref, struct scsi_dh_data, kref); | ||
171 | scsi_dh_data->scsi_dh->detach(scsi_dh_data->sdev); | ||
172 | } | ||
173 | |||
162 | /* | 174 | /* |
163 | * scsi_dh_handler_detach - Detach a device handler from a device | 175 | * scsi_dh_handler_detach - Detach a device handler from a device |
164 | * @sdev - SCSI device the device handler should be detached from | 176 | * @sdev - SCSI device the device handler should be detached from |
@@ -180,7 +192,7 @@ static void scsi_dh_handler_detach(struct scsi_device *sdev, | |||
180 | scsi_dh = sdev->scsi_dh_data->scsi_dh; | 192 | scsi_dh = sdev->scsi_dh_data->scsi_dh; |
181 | 193 | ||
182 | if (scsi_dh && scsi_dh->detach) | 194 | if (scsi_dh && scsi_dh->detach) |
183 | scsi_dh->detach(sdev); | 195 | kref_put(&sdev->scsi_dh_data->kref, __detach_handler); |
184 | } | 196 | } |
185 | 197 | ||
186 | /* | 198 | /* |
@@ -474,7 +486,6 @@ int scsi_dh_attach(struct request_queue *q, const char *name) | |||
474 | 486 | ||
475 | if (!err) { | 487 | if (!err) { |
476 | err = scsi_dh_handler_attach(sdev, scsi_dh); | 488 | err = scsi_dh_handler_attach(sdev, scsi_dh); |
477 | |||
478 | put_device(&sdev->sdev_gendev); | 489 | put_device(&sdev->sdev_gendev); |
479 | } | 490 | } |
480 | return err; | 491 | return err; |
@@ -505,10 +516,8 @@ void scsi_dh_detach(struct request_queue *q) | |||
505 | return; | 516 | return; |
506 | 517 | ||
507 | if (sdev->scsi_dh_data) { | 518 | if (sdev->scsi_dh_data) { |
508 | /* if sdev is not on internal list, detach */ | ||
509 | scsi_dh = sdev->scsi_dh_data->scsi_dh; | 519 | scsi_dh = sdev->scsi_dh_data->scsi_dh; |
510 | if (!device_handler_match(scsi_dh, sdev)) | 520 | scsi_dh_handler_detach(sdev, scsi_dh); |
511 | scsi_dh_handler_detach(sdev, scsi_dh); | ||
512 | } | 521 | } |
513 | put_device(&sdev->sdev_gendev); | 522 | put_device(&sdev->sdev_gendev); |
514 | } | 523 | } |