diff options
author | James Smart <James.Smart@Emulex.Com> | 2006-04-10 10:14:05 -0400 |
---|---|---|
committer | James Bottomley <jejb@mulgrave.il.steeleye.com> | 2006-04-13 14:25:16 -0400 |
commit | aedf349773e5877d716a89368d512b9baa3e8c7b (patch) | |
tree | acc08d181b97c434c70c7ad984e262813067ba89 /include | |
parent | b0d2364887e94c880680f1e17943cd660bcf8979 (diff) |
[SCSI] FC transport: fixes for workq deadlocks
As previously reported via Michael Reed, the FC transport took a hit
in 2.6.15 (perhaps a little earlier) when we solved a recursion error.
There are 2 deadlocks occurring:
- With scan and the delete items sharing the same workq, flushing the
workq for the delete code was getting it stalled behind a very long
running scan code path.
- There's a deadlock where scsi_remove_target() has to sit behind
scsi_scan_target() due to contention over the scan_lock().
This patch resolves the 1st deadlock and significantly reduces the
odds of the second. So far, we have only replicated the 2nd deadlock
on a highly-parallel SMP system. More on the 2nd deadlock in a following
email.
This patch reworks the transport to:
- Only use the scsi host workq for scanning
- Use 2 other workq's internally. One for deletions, the other for
scheduled deletions. Originally, we tried this with a single workq,
but the occassional flushes of the scheduled queues was hitting the
second deadlock with a slightly higher frequency. In the future, we'll
look at the LLDD's and the transport to see if we can get rid of this
extra overhead.
- When moving to the other workq's we tightened up some object states
and some lock handling.
- Properly syncs adds/deletes
- minor code cleanups
- directly reference fc_host_attrs, rather than through attribute
macros
- flush the right workq on delayed work cancel failures.
Large kudos to Michael Reed who has been working this issue for the last
month.
Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
Diffstat (limited to 'include')
-rw-r--r-- | include/scsi/scsi_transport_fc.h | 41 |
1 files changed, 30 insertions, 11 deletions
diff --git a/include/scsi/scsi_transport_fc.h b/include/scsi/scsi_transport_fc.h index cf3fec8be1e3..5626225bd3ae 100644 --- a/include/scsi/scsi_transport_fc.h +++ b/include/scsi/scsi_transport_fc.h | |||
@@ -202,12 +202,19 @@ struct fc_rport { /* aka fc_starget_attrs */ | |||
202 | /* internal data */ | 202 | /* internal data */ |
203 | unsigned int channel; | 203 | unsigned int channel; |
204 | u32 number; | 204 | u32 number; |
205 | u8 flags; | ||
205 | struct list_head peers; | 206 | struct list_head peers; |
206 | struct device dev; | 207 | struct device dev; |
207 | struct work_struct dev_loss_work; | 208 | struct work_struct dev_loss_work; |
208 | struct work_struct scan_work; | 209 | struct work_struct scan_work; |
210 | struct work_struct stgt_delete_work; | ||
211 | struct work_struct rport_delete_work; | ||
209 | } __attribute__((aligned(sizeof(unsigned long)))); | 212 | } __attribute__((aligned(sizeof(unsigned long)))); |
210 | 213 | ||
214 | /* bit field values for struct fc_rport "flags" field: */ | ||
215 | #define FC_RPORT_DEVLOSS_PENDING 0x01 | ||
216 | #define FC_RPORT_SCAN_PENDING 0x02 | ||
217 | |||
211 | #define dev_to_rport(d) \ | 218 | #define dev_to_rport(d) \ |
212 | container_of(d, struct fc_rport, dev) | 219 | container_of(d, struct fc_rport, dev) |
213 | #define transport_class_to_rport(classdev) \ | 220 | #define transport_class_to_rport(classdev) \ |
@@ -327,13 +334,16 @@ struct fc_host_attrs { | |||
327 | struct list_head rport_bindings; | 334 | struct list_head rport_bindings; |
328 | u32 next_rport_number; | 335 | u32 next_rport_number; |
329 | u32 next_target_id; | 336 | u32 next_target_id; |
330 | u8 flags; | ||
331 | struct work_struct rport_del_work; | ||
332 | }; | ||
333 | 337 | ||
334 | /* values for struct fc_host_attrs "flags" field: */ | 338 | /* work queues for rport state manipulation */ |
335 | #define FC_SHOST_RPORT_DEL_SCHEDULED 0x01 | 339 | char work_q_name[KOBJ_NAME_LEN]; |
340 | struct workqueue_struct *work_q; | ||
341 | char devloss_work_q_name[KOBJ_NAME_LEN]; | ||
342 | struct workqueue_struct *devloss_work_q; | ||
343 | }; | ||
336 | 344 | ||
345 | #define shost_to_fc_host(x) \ | ||
346 | ((struct fc_host_attrs *)(x)->shost_data) | ||
337 | 347 | ||
338 | #define fc_host_node_name(x) \ | 348 | #define fc_host_node_name(x) \ |
339 | (((struct fc_host_attrs *)(x)->shost_data)->node_name) | 349 | (((struct fc_host_attrs *)(x)->shost_data)->node_name) |
@@ -375,10 +385,14 @@ struct fc_host_attrs { | |||
375 | (((struct fc_host_attrs *)(x)->shost_data)->next_rport_number) | 385 | (((struct fc_host_attrs *)(x)->shost_data)->next_rport_number) |
376 | #define fc_host_next_target_id(x) \ | 386 | #define fc_host_next_target_id(x) \ |
377 | (((struct fc_host_attrs *)(x)->shost_data)->next_target_id) | 387 | (((struct fc_host_attrs *)(x)->shost_data)->next_target_id) |
378 | #define fc_host_flags(x) \ | 388 | #define fc_host_work_q_name(x) \ |
379 | (((struct fc_host_attrs *)(x)->shost_data)->flags) | 389 | (((struct fc_host_attrs *)(x)->shost_data)->work_q_name) |
380 | #define fc_host_rport_del_work(x) \ | 390 | #define fc_host_work_q(x) \ |
381 | (((struct fc_host_attrs *)(x)->shost_data)->rport_del_work) | 391 | (((struct fc_host_attrs *)(x)->shost_data)->work_q) |
392 | #define fc_host_devloss_work_q_name(x) \ | ||
393 | (((struct fc_host_attrs *)(x)->shost_data)->devloss_work_q_name) | ||
394 | #define fc_host_devloss_work_q(x) \ | ||
395 | (((struct fc_host_attrs *)(x)->shost_data)->devloss_work_q) | ||
382 | 396 | ||
383 | 397 | ||
384 | /* The functions by which the transport class and the driver communicate */ | 398 | /* The functions by which the transport class and the driver communicate */ |
@@ -461,10 +475,15 @@ fc_remote_port_chkready(struct fc_rport *rport) | |||
461 | 475 | ||
462 | switch (rport->port_state) { | 476 | switch (rport->port_state) { |
463 | case FC_PORTSTATE_ONLINE: | 477 | case FC_PORTSTATE_ONLINE: |
464 | result = 0; | 478 | if (rport->roles & FC_RPORT_ROLE_FCP_TARGET) |
479 | result = 0; | ||
480 | else if (rport->flags & FC_RPORT_DEVLOSS_PENDING) | ||
481 | result = DID_IMM_RETRY << 16; | ||
482 | else | ||
483 | result = DID_NO_CONNECT << 16; | ||
465 | break; | 484 | break; |
466 | case FC_PORTSTATE_BLOCKED: | 485 | case FC_PORTSTATE_BLOCKED: |
467 | result = DID_BUS_BUSY << 16; | 486 | result = DID_IMM_RETRY << 16; |
468 | break; | 487 | break; |
469 | default: | 488 | default: |
470 | result = DID_NO_CONNECT << 16; | 489 | result = DID_NO_CONNECT << 16; |