diff options
author | Dan Williams <dan.j.williams@intel.com> | 2011-12-20 04:03:48 -0500 |
---|---|---|
committer | James Bottomley <JBottomley@Parallels.com> | 2012-02-19 15:11:33 -0500 |
commit | 0b3e09da1350397f3f8b6fd839ab455b0b587451 (patch) | |
tree | f822bb4dcaa52d5d568104932ee55ee136dcf039 /drivers/scsi/libsas | |
parent | b52df4174dff7e587f6fbfb21e3c2cb57109e5cf (diff) |
[SCSI] libsas: perform sas-transport resets in shost->workq context
Extend the sas transport class to allow transport users to attach extra
data to a sas_phy (->hostdata). Use this area in libsas to move resets
to workq context in preparation for scheduling ata device resets through
libata-eh.
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
Diffstat (limited to 'drivers/scsi/libsas')
-rw-r--r-- | drivers/scsi/libsas/sas_event.c | 2 | ||||
-rw-r--r-- | drivers/scsi/libsas/sas_init.c | 59 | ||||
-rw-r--r-- | drivers/scsi/libsas/sas_internal.h | 10 |
3 files changed, 69 insertions, 2 deletions
diff --git a/drivers/scsi/libsas/sas_event.c b/drivers/scsi/libsas/sas_event.c index 933d757499b5..dbfaceeea0f7 100644 --- a/drivers/scsi/libsas/sas_event.c +++ b/drivers/scsi/libsas/sas_event.c | |||
@@ -27,7 +27,7 @@ | |||
27 | #include "sas_internal.h" | 27 | #include "sas_internal.h" |
28 | #include "sas_dump.h" | 28 | #include "sas_dump.h" |
29 | 29 | ||
30 | static void sas_queue_work(struct sas_ha_struct *ha, struct work_struct *work) | 30 | void sas_queue_work(struct sas_ha_struct *ha, struct work_struct *work) |
31 | { | 31 | { |
32 | if (!test_bit(SAS_HA_REGISTERED, &ha->state)) | 32 | if (!test_bit(SAS_HA_REGISTERED, &ha->state)) |
33 | return; | 33 | return; |
diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c index e17fe35af30c..cb65adf4ab16 100644 --- a/drivers/scsi/libsas/sas_init.c +++ b/drivers/scsi/libsas/sas_init.c | |||
@@ -290,9 +290,66 @@ int sas_set_phy_speed(struct sas_phy *phy, | |||
290 | return ret; | 290 | return ret; |
291 | } | 291 | } |
292 | 292 | ||
293 | static void sas_phy_release(struct sas_phy *phy) | ||
294 | { | ||
295 | kfree(phy->hostdata); | ||
296 | phy->hostdata = NULL; | ||
297 | } | ||
298 | |||
299 | static void phy_reset_work(struct work_struct *work) | ||
300 | { | ||
301 | struct sas_phy_data *d = container_of(work, typeof(*d), reset_work); | ||
302 | |||
303 | d->reset_result = sas_phy_reset(d->phy, d->hard_reset); | ||
304 | } | ||
305 | |||
306 | static int sas_phy_setup(struct sas_phy *phy) | ||
307 | { | ||
308 | struct sas_phy_data *d = kzalloc(sizeof(*d), GFP_KERNEL); | ||
309 | |||
310 | if (!d) | ||
311 | return -ENOMEM; | ||
312 | |||
313 | mutex_init(&d->event_lock); | ||
314 | INIT_WORK(&d->reset_work, phy_reset_work); | ||
315 | d->phy = phy; | ||
316 | phy->hostdata = d; | ||
317 | |||
318 | return 0; | ||
319 | } | ||
320 | |||
321 | static int queue_phy_reset(struct sas_phy *phy, int hard_reset) | ||
322 | { | ||
323 | struct Scsi_Host *shost = dev_to_shost(phy->dev.parent); | ||
324 | struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost); | ||
325 | struct sas_phy_data *d = phy->hostdata; | ||
326 | int rc; | ||
327 | |||
328 | if (!d) | ||
329 | return -ENOMEM; | ||
330 | |||
331 | /* libsas workqueue coordinates ata-eh reset with discovery */ | ||
332 | mutex_lock(&d->event_lock); | ||
333 | d->reset_result = 0; | ||
334 | d->hard_reset = hard_reset; | ||
335 | |||
336 | spin_lock_irq(&ha->state_lock); | ||
337 | sas_queue_work(ha, &d->reset_work); | ||
338 | spin_unlock_irq(&ha->state_lock); | ||
339 | |||
340 | rc = sas_drain_work(ha); | ||
341 | if (rc == 0) | ||
342 | rc = d->reset_result; | ||
343 | mutex_unlock(&d->event_lock); | ||
344 | |||
345 | return rc; | ||
346 | } | ||
347 | |||
293 | static struct sas_function_template sft = { | 348 | static struct sas_function_template sft = { |
294 | .phy_enable = sas_phy_enable, | 349 | .phy_enable = sas_phy_enable, |
295 | .phy_reset = sas_phy_reset, | 350 | .phy_reset = queue_phy_reset, |
351 | .phy_setup = sas_phy_setup, | ||
352 | .phy_release = sas_phy_release, | ||
296 | .set_phy_speed = sas_set_phy_speed, | 353 | .set_phy_speed = sas_set_phy_speed, |
297 | .get_linkerrors = sas_get_linkerrors, | 354 | .get_linkerrors = sas_get_linkerrors, |
298 | .smp_handler = sas_smp_handler, | 355 | .smp_handler = sas_smp_handler, |
diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h index 662ffcba99d2..9ba65e0c6f91 100644 --- a/drivers/scsi/libsas/sas_internal.h +++ b/drivers/scsi/libsas/sas_internal.h | |||
@@ -38,6 +38,15 @@ | |||
38 | #define TO_SAS_TASK(_scsi_cmd) ((void *)(_scsi_cmd)->host_scribble) | 38 | #define TO_SAS_TASK(_scsi_cmd) ((void *)(_scsi_cmd)->host_scribble) |
39 | #define ASSIGN_SAS_TASK(_sc, _t) do { (_sc)->host_scribble = (void *) _t; } while (0) | 39 | #define ASSIGN_SAS_TASK(_sc, _t) do { (_sc)->host_scribble = (void *) _t; } while (0) |
40 | 40 | ||
41 | struct sas_phy_data { | ||
42 | /* let reset be performed in sas_queue_work() context */ | ||
43 | struct sas_phy *phy; | ||
44 | struct mutex event_lock; | ||
45 | int hard_reset; | ||
46 | int reset_result; | ||
47 | struct work_struct reset_work; | ||
48 | }; | ||
49 | |||
41 | void sas_scsi_recover_host(struct Scsi_Host *shost); | 50 | void sas_scsi_recover_host(struct Scsi_Host *shost); |
42 | 51 | ||
43 | int sas_show_class(enum sas_class class, char *buf); | 52 | int sas_show_class(enum sas_class class, char *buf); |
@@ -66,6 +75,7 @@ void sas_porte_broadcast_rcvd(struct work_struct *work); | |||
66 | void sas_porte_link_reset_err(struct work_struct *work); | 75 | void sas_porte_link_reset_err(struct work_struct *work); |
67 | void sas_porte_timer_event(struct work_struct *work); | 76 | void sas_porte_timer_event(struct work_struct *work); |
68 | void sas_porte_hard_reset(struct work_struct *work); | 77 | void sas_porte_hard_reset(struct work_struct *work); |
78 | void sas_queue_work(struct sas_ha_struct *ha, struct work_struct *work); | ||
69 | 79 | ||
70 | int sas_notify_lldd_dev_found(struct domain_device *); | 80 | int sas_notify_lldd_dev_found(struct domain_device *); |
71 | void sas_notify_lldd_dev_gone(struct domain_device *); | 81 | void sas_notify_lldd_dev_gone(struct domain_device *); |