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; |
