diff options
author | Dan Williams <dan.j.williams@intel.com> | 2011-02-18 12:25:07 -0500 |
---|---|---|
committer | Dan Williams <dan.j.williams@intel.com> | 2011-07-03 06:55:27 -0400 |
commit | 0cf89d1d27c1bdd0abf1714096f98ea44704dcff (patch) | |
tree | 0bee9fa05aba8a6e1fe4b6b50a3bfc6d0793356f /drivers/scsi/isci | |
parent | c7ef4031f01301298bbaba2666740183cd399f8c (diff) |
isci: cleanup "starting" state handling
The lldd actively disallows requests in the "starting" state. Retrying
or holding off commands in this state is sub-optimal:
1/ it adds another state check to the fast path
2/ retrying can cause libsas to give up
However, isci's ->lldd_dev_found() routine already waits for controller
start to complete before allowing further progress. Checking the
"starting" state in isci_task_execute_task and the isr is redundant and
misleading. Clean this up and introduce a controller-wide event queue
to start reeling in "completion" proliferation in the driver.
The "stopping" state cleanups are in a similar vein, rely on the the isr
and other paths being precluded from occurring rather than implementing
state checking logic.
Reported-by: Christoph Hellwig <hch@infradead.org>
Cc: Jeff Garzik <jeff@garzik.org>
Signed-off-by: Edmund Nadolski <edmund.nadolski@intel.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Diffstat (limited to 'drivers/scsi/isci')
-rw-r--r-- | drivers/scsi/isci/host.c | 145 | ||||
-rw-r--r-- | drivers/scsi/isci/host.h | 17 | ||||
-rw-r--r-- | drivers/scsi/isci/remote_device.c | 4 | ||||
-rw-r--r-- | drivers/scsi/isci/task.c | 3 |
4 files changed, 67 insertions, 102 deletions
diff --git a/drivers/scsi/isci/host.c b/drivers/scsi/isci/host.c index b66e62027247..dbdc3bab9ca3 100644 --- a/drivers/scsi/isci/host.c +++ b/drivers/scsi/isci/host.c | |||
@@ -67,15 +67,8 @@ irqreturn_t isci_msix_isr(int vec, void *data) | |||
67 | struct isci_host *ihost = data; | 67 | struct isci_host *ihost = data; |
68 | struct scic_sds_controller *scic = ihost->core_controller; | 68 | struct scic_sds_controller *scic = ihost->core_controller; |
69 | 69 | ||
70 | if (isci_host_get_state(ihost) != isci_starting) { | 70 | if (scic_sds_controller_isr(scic)) |
71 | if (scic_sds_controller_isr(scic)) { | 71 | tasklet_schedule(&ihost->completion_tasklet); |
72 | if (isci_host_get_state(ihost) != isci_stopped) | ||
73 | tasklet_schedule(&ihost->completion_tasklet); | ||
74 | else | ||
75 | dev_dbg(&ihost->pdev->dev, | ||
76 | "%s: controller stopped\n", __func__); | ||
77 | } | ||
78 | } | ||
79 | 72 | ||
80 | return IRQ_HANDLED; | 73 | return IRQ_HANDLED; |
81 | } | 74 | } |
@@ -89,22 +82,10 @@ irqreturn_t isci_intx_isr(int vec, void *data) | |||
89 | for_each_isci_host(ihost, pdev) { | 82 | for_each_isci_host(ihost, pdev) { |
90 | struct scic_sds_controller *scic = ihost->core_controller; | 83 | struct scic_sds_controller *scic = ihost->core_controller; |
91 | 84 | ||
92 | if (isci_host_get_state(ihost) != isci_starting) { | 85 | if (scic_sds_controller_isr(scic)) { |
93 | if (scic_sds_controller_isr(scic)) { | 86 | tasklet_schedule(&ihost->completion_tasklet); |
94 | if (isci_host_get_state(ihost) != isci_stopped) | 87 | ret = IRQ_HANDLED; |
95 | tasklet_schedule(&ihost->completion_tasklet); | 88 | } |
96 | else | ||
97 | dev_dbg(&ihost->pdev->dev, | ||
98 | "%s: controller stopped\n", | ||
99 | __func__); | ||
100 | ret = IRQ_HANDLED; | ||
101 | } | ||
102 | } else | ||
103 | dev_warn(&ihost->pdev->dev, | ||
104 | "%s: get_handler_methods failed, " | ||
105 | "ihost->status = 0x%x\n", | ||
106 | __func__, | ||
107 | isci_host_get_state(ihost)); | ||
108 | } | 89 | } |
109 | return ret; | 90 | return ret; |
110 | } | 91 | } |
@@ -118,26 +99,19 @@ irqreturn_t isci_intx_isr(int vec, void *data) | |||
118 | * core library. | 99 | * core library. |
119 | * | 100 | * |
120 | */ | 101 | */ |
121 | void isci_host_start_complete( | 102 | void isci_host_start_complete(struct isci_host *ihost, enum sci_status completion_status) |
122 | struct isci_host *isci_host, | ||
123 | enum sci_status completion_status) | ||
124 | { | 103 | { |
125 | if (completion_status == SCI_SUCCESS) { | 104 | if (completion_status != SCI_SUCCESS) |
126 | dev_dbg(&isci_host->pdev->dev, | 105 | dev_info(&ihost->pdev->dev, |
127 | "%s: completion_status: SCI_SUCCESS\n", __func__); | 106 | "controller start timed out, continuing...\n"); |
128 | isci_host_change_state(isci_host, isci_ready); | 107 | isci_host_change_state(ihost, isci_ready); |
129 | complete_all(&isci_host->start_complete); | 108 | clear_bit(IHOST_START_PENDING, &ihost->flags); |
130 | } else | 109 | wake_up(&ihost->eventq); |
131 | dev_err(&isci_host->pdev->dev, | ||
132 | "controller start failed with " | ||
133 | "completion_status = 0x%x;", | ||
134 | completion_status); | ||
135 | |||
136 | } | 110 | } |
137 | 111 | ||
138 | int isci_host_scan_finished(struct Scsi_Host *shost, unsigned long time) | 112 | int isci_host_scan_finished(struct Scsi_Host *shost, unsigned long time) |
139 | { | 113 | { |
140 | struct isci_host *isci_host = isci_host_from_sas_ha(SHOST_TO_SAS_HA(shost)); | 114 | struct isci_host *ihost = isci_host_from_sas_ha(SHOST_TO_SAS_HA(shost)); |
141 | 115 | ||
142 | /** | 116 | /** |
143 | * check interrupt_handler's status and call completion_handler if true, | 117 | * check interrupt_handler's status and call completion_handler if true, |
@@ -148,61 +122,44 @@ int isci_host_scan_finished(struct Scsi_Host *shost, unsigned long time) | |||
148 | * continue to return zero from thee scan_finished routine until | 122 | * continue to return zero from thee scan_finished routine until |
149 | * the scic_cb_controller_start_complete() call comes from the core. | 123 | * the scic_cb_controller_start_complete() call comes from the core. |
150 | **/ | 124 | **/ |
151 | if (scic_sds_controller_isr(isci_host->core_controller)) | 125 | if (scic_sds_controller_isr(ihost->core_controller)) |
152 | scic_sds_controller_completion_handler(isci_host->core_controller); | 126 | scic_sds_controller_completion_handler(ihost->core_controller); |
153 | 127 | ||
154 | if (isci_starting == isci_host_get_state(isci_host) | 128 | if (test_bit(IHOST_START_PENDING, &ihost->flags) && time < HZ*10) { |
155 | && time < (HZ * 10)) { | 129 | dev_dbg(&ihost->pdev->dev, |
156 | dev_dbg(&isci_host->pdev->dev, | 130 | "%s: ihost->status = %d, time = %ld\n", |
157 | "%s: isci_host->status = %d, time = %ld\n", | 131 | __func__, isci_host_get_state(ihost), time); |
158 | __func__, isci_host_get_state(isci_host), time); | ||
159 | return 0; | 132 | return 0; |
160 | } | 133 | } |
161 | 134 | ||
162 | 135 | ||
163 | dev_dbg(&isci_host->pdev->dev, | 136 | dev_dbg(&ihost->pdev->dev, |
164 | "%s: isci_host->status = %d, time = %ld\n", | 137 | "%s: ihost->status = %d, time = %ld\n", |
165 | __func__, isci_host_get_state(isci_host), time); | 138 | __func__, isci_host_get_state(ihost), time); |
166 | 139 | ||
167 | scic_controller_enable_interrupts(isci_host->core_controller); | 140 | scic_controller_enable_interrupts(ihost->core_controller); |
168 | 141 | ||
169 | return 1; | 142 | return 1; |
170 | 143 | ||
171 | } | 144 | } |
172 | 145 | ||
173 | |||
174 | /** | ||
175 | * isci_host_scan_start() - This function is one of the SCSI Host Template | ||
176 | * function, called by the SCSI mid layer berfore a target scan begins. The | ||
177 | * core library controller start routine is called from here. | ||
178 | * @shost: This parameter specifies the SCSI host to be scanned | ||
179 | * | ||
180 | */ | ||
181 | void isci_host_scan_start(struct Scsi_Host *shost) | 146 | void isci_host_scan_start(struct Scsi_Host *shost) |
182 | { | 147 | { |
183 | struct isci_host *isci_host; | 148 | struct isci_host *ihost = isci_host_from_sas_ha(SHOST_TO_SAS_HA(shost)); |
184 | 149 | struct scic_sds_controller *scic = ihost->core_controller; | |
185 | isci_host = isci_host_from_sas_ha(SHOST_TO_SAS_HA(shost)); | 150 | unsigned long tmo = scic_controller_get_suggested_start_timeout(scic); |
186 | isci_host_change_state(isci_host, isci_starting); | ||
187 | 151 | ||
188 | scic_controller_disable_interrupts(isci_host->core_controller); | 152 | set_bit(IHOST_START_PENDING, &ihost->flags); |
189 | init_completion(&isci_host->start_complete); | 153 | scic_controller_disable_interrupts(ihost->core_controller); |
190 | scic_controller_start( | 154 | scic_controller_start(scic, tmo); |
191 | isci_host->core_controller, | ||
192 | scic_controller_get_suggested_start_timeout( | ||
193 | isci_host->core_controller) | ||
194 | ); | ||
195 | } | 155 | } |
196 | 156 | ||
197 | void isci_host_stop_complete( | 157 | void isci_host_stop_complete(struct isci_host *ihost, enum sci_status completion_status) |
198 | struct isci_host *isci_host, | ||
199 | enum sci_status completion_status) | ||
200 | { | 158 | { |
201 | isci_host_change_state(isci_host, isci_stopped); | 159 | isci_host_change_state(ihost, isci_stopped); |
202 | scic_controller_disable_interrupts( | 160 | scic_controller_disable_interrupts(ihost->core_controller); |
203 | isci_host->core_controller | 161 | clear_bit(IHOST_STOP_PENDING, &ihost->flags); |
204 | ); | 162 | wake_up(&ihost->eventq); |
205 | complete(&isci_host->stop_complete); | ||
206 | } | 163 | } |
207 | 164 | ||
208 | static struct coherent_memory_info *isci_host_alloc_mdl_struct( | 165 | static struct coherent_memory_info *isci_host_alloc_mdl_struct( |
@@ -370,31 +327,26 @@ static void isci_host_completion_routine(unsigned long data) | |||
370 | 327 | ||
371 | } | 328 | } |
372 | 329 | ||
373 | void isci_host_deinit( | 330 | void isci_host_deinit(struct isci_host *ihost) |
374 | struct isci_host *isci_host) | ||
375 | { | 331 | { |
332 | struct scic_sds_controller *scic = ihost->core_controller; | ||
376 | int i; | 333 | int i; |
377 | 334 | ||
378 | isci_host_change_state(isci_host, isci_stopping); | 335 | isci_host_change_state(ihost, isci_stopping); |
379 | for (i = 0; i < SCI_MAX_PORTS; i++) { | 336 | for (i = 0; i < SCI_MAX_PORTS; i++) { |
380 | struct isci_port *port = &isci_host->isci_ports[i]; | 337 | struct isci_port *port = &ihost->isci_ports[i]; |
381 | struct isci_remote_device *device, *tmpdev; | 338 | struct isci_remote_device *idev, *d; |
382 | list_for_each_entry_safe(device, tmpdev, | 339 | |
383 | &port->remote_dev_list, node) { | 340 | list_for_each_entry_safe(idev, d, &port->remote_dev_list, node) { |
384 | isci_remote_device_change_state(device, isci_stopping); | 341 | isci_remote_device_change_state(idev, isci_stopping); |
385 | isci_remote_device_stop(device); | 342 | isci_remote_device_stop(idev); |
386 | } | 343 | } |
387 | } | 344 | } |
388 | 345 | ||
389 | /* stop the comtroller and wait for completion. */ | 346 | set_bit(IHOST_STOP_PENDING, &ihost->flags); |
390 | init_completion(&isci_host->stop_complete); | 347 | scic_controller_stop(scic, SCIC_CONTROLLER_STOP_TIMEOUT); |
391 | scic_controller_stop( | 348 | wait_for_stop(ihost); |
392 | isci_host->core_controller, | 349 | scic_controller_reset(scic); |
393 | SCIC_CONTROLLER_STOP_TIMEOUT | ||
394 | ); | ||
395 | wait_for_completion(&isci_host->stop_complete); | ||
396 | /* next, reset the controller. */ | ||
397 | scic_controller_reset(isci_host->core_controller); | ||
398 | } | 350 | } |
399 | 351 | ||
400 | static int isci_verify_firmware(const struct firmware *fw, | 352 | static int isci_verify_firmware(const struct firmware *fw, |
@@ -506,6 +458,7 @@ int isci_host_init(struct isci_host *isci_host) | |||
506 | spin_lock_init(&isci_host->state_lock); | 458 | spin_lock_init(&isci_host->state_lock); |
507 | spin_lock_init(&isci_host->scic_lock); | 459 | spin_lock_init(&isci_host->scic_lock); |
508 | spin_lock_init(&isci_host->queue_lock); | 460 | spin_lock_init(&isci_host->queue_lock); |
461 | init_waitqueue_head(&isci_host->eventq); | ||
509 | 462 | ||
510 | isci_host_change_state(isci_host, isci_starting); | 463 | isci_host_change_state(isci_host, isci_starting); |
511 | isci_host->can_queue = ISCI_CAN_QUEUE_VAL; | 464 | isci_host->can_queue = ISCI_CAN_QUEUE_VAL; |
diff --git a/drivers/scsi/isci/host.h b/drivers/scsi/isci/host.h index 421d3debdaed..26768c5bbe01 100644 --- a/drivers/scsi/isci/host.h +++ b/drivers/scsi/isci/host.h | |||
@@ -109,13 +109,15 @@ struct isci_host { | |||
109 | u8 sas_addr[SAS_ADDR_SIZE]; | 109 | u8 sas_addr[SAS_ADDR_SIZE]; |
110 | 110 | ||
111 | enum isci_status status; | 111 | enum isci_status status; |
112 | #define IHOST_START_PENDING 0 | ||
113 | #define IHOST_STOP_PENDING 1 | ||
114 | unsigned long flags; | ||
115 | wait_queue_head_t eventq; | ||
112 | struct Scsi_Host *shost; | 116 | struct Scsi_Host *shost; |
113 | struct tasklet_struct completion_tasklet; | 117 | struct tasklet_struct completion_tasklet; |
114 | struct list_head mdl_struct_list; | 118 | struct list_head mdl_struct_list; |
115 | struct list_head requests_to_complete; | 119 | struct list_head requests_to_complete; |
116 | struct list_head requests_to_abort; | 120 | struct list_head requests_to_abort; |
117 | struct completion stop_complete; | ||
118 | struct completion start_complete; | ||
119 | spinlock_t scic_lock; | 121 | spinlock_t scic_lock; |
120 | struct isci_host *next; | 122 | struct isci_host *next; |
121 | }; | 123 | }; |
@@ -202,6 +204,17 @@ static inline void isci_host_can_dequeue( | |||
202 | spin_unlock_irqrestore(&isci_host->queue_lock, flags); | 204 | spin_unlock_irqrestore(&isci_host->queue_lock, flags); |
203 | } | 205 | } |
204 | 206 | ||
207 | static inline void wait_for_start(struct isci_host *ihost) | ||
208 | { | ||
209 | wait_event(ihost->eventq, !test_bit(IHOST_START_PENDING, &ihost->flags)); | ||
210 | } | ||
211 | |||
212 | static inline void wait_for_stop(struct isci_host *ihost) | ||
213 | { | ||
214 | wait_event(ihost->eventq, !test_bit(IHOST_STOP_PENDING, &ihost->flags)); | ||
215 | } | ||
216 | |||
217 | |||
205 | /** | 218 | /** |
206 | * isci_host_from_sas_ha() - This accessor retrieves the isci_host object | 219 | * isci_host_from_sas_ha() - This accessor retrieves the isci_host object |
207 | * reference from the Linux sas_ha_struct reference. | 220 | * reference from the Linux sas_ha_struct reference. |
diff --git a/drivers/scsi/isci/remote_device.c b/drivers/scsi/isci/remote_device.c index dbf3c82f6195..936f22957e5b 100644 --- a/drivers/scsi/isci/remote_device.c +++ b/drivers/scsi/isci/remote_device.c | |||
@@ -507,6 +507,8 @@ int isci_remote_device_found(struct domain_device *domain_dev) | |||
507 | dev_dbg(&isci_host->pdev->dev, | 507 | dev_dbg(&isci_host->pdev->dev, |
508 | "%s: domain_device = %p\n", __func__, domain_dev); | 508 | "%s: domain_device = %p\n", __func__, domain_dev); |
509 | 509 | ||
510 | wait_for_start(isci_host); | ||
511 | |||
510 | sas_port = domain_dev->port; | 512 | sas_port = domain_dev->port; |
511 | sas_phy = list_first_entry(&sas_port->phy_list, struct asd_sas_phy, | 513 | sas_phy = list_first_entry(&sas_port->phy_list, struct asd_sas_phy, |
512 | port_phy_el); | 514 | port_phy_el); |
@@ -560,8 +562,6 @@ int isci_remote_device_found(struct domain_device *domain_dev) | |||
560 | return -ENODEV; | 562 | return -ENODEV; |
561 | } | 563 | } |
562 | 564 | ||
563 | wait_for_completion(&isci_host->start_complete); | ||
564 | |||
565 | return 0; | 565 | return 0; |
566 | } | 566 | } |
567 | /** | 567 | /** |
diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c index 5e6f55863407..6f98f6c74efb 100644 --- a/drivers/scsi/isci/task.c +++ b/drivers/scsi/isci/task.c | |||
@@ -164,8 +164,7 @@ int isci_task_execute_task(struct sas_task *task, int num, gfp_t gfp_flags) | |||
164 | * for the quiesce spinlock. | 164 | * for the quiesce spinlock. |
165 | */ | 165 | */ |
166 | 166 | ||
167 | if (isci_host_get_state(isci_host) == isci_starting || | 167 | if ((device && ((isci_remote_device_get_state(device) == isci_ready) || |
168 | (device && ((isci_remote_device_get_state(device) == isci_ready) || | ||
169 | (isci_remote_device_get_state(device) == isci_host_quiesce)))) { | 168 | (isci_remote_device_get_state(device) == isci_host_quiesce)))) { |
170 | 169 | ||
171 | /* Forces a retry from scsi mid layer. */ | 170 | /* Forces a retry from scsi mid layer. */ |