diff options
author | Chris Leech <christopher.leech@intel.com> | 2009-02-27 13:55:02 -0500 |
---|---|---|
committer | James Bottomley <James.Bottomley@HansenPartnership.com> | 2009-03-06 16:39:34 -0500 |
commit | 6755db1cd4587084be85f860b7aa7c0cc9d776dc (patch) | |
tree | cdc50e934f61d73a7aa8f5fdc5c454e0375071b9 | |
parent | bc0e17f691085315ae9303eb5b0883fe16dfe6b1 (diff) |
[SCSI] libfc: rport retry on LS_RJT from certain ELS
This allows any rport ELS to retry on LS_RJT.
The rport error handling would only retry on resource allocation failures
and exchange timeouts. I have a target that will occasionally reject PLOGI
when we do a quick LOGO/PLOGI. When a critical ELS was rejected, libfc would
fail silently leaving the rport in a dead state.
The retry count and delay are managed by fc_rport_error_retry. If the retry
count is exceeded fc_rport_error will be called. When retrying is not the
correct course of action, fc_rport_error can be called directly.
Signed-off-by: Chris Leech <christopher.leech@intel.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
-rw-r--r-- | drivers/scsi/libfc/fc_exch.c | 2 | ||||
-rw-r--r-- | drivers/scsi/libfc/fc_rport.c | 111 | ||||
-rw-r--r-- | include/scsi/fc/fc_fs.h | 5 |
3 files changed, 69 insertions, 49 deletions
diff --git a/drivers/scsi/libfc/fc_exch.c b/drivers/scsi/libfc/fc_exch.c index dd269e5f953e..8c4018956d4c 100644 --- a/drivers/scsi/libfc/fc_exch.c +++ b/drivers/scsi/libfc/fc_exch.c | |||
@@ -32,8 +32,6 @@ | |||
32 | #include <scsi/libfc.h> | 32 | #include <scsi/libfc.h> |
33 | #include <scsi/fc_encode.h> | 33 | #include <scsi/fc_encode.h> |
34 | 34 | ||
35 | #define FC_DEF_R_A_TOV (10 * 1000) /* resource allocation timeout */ | ||
36 | |||
37 | /* | 35 | /* |
38 | * fc_exch_debug can be set in debugger or at compile time to get more logs. | 36 | * fc_exch_debug can be set in debugger or at compile time to get more logs. |
39 | */ | 37 | */ |
diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c index 717575934152..600a8fffa940 100644 --- a/drivers/scsi/libfc/fc_rport.c +++ b/drivers/scsi/libfc/fc_rport.c | |||
@@ -81,6 +81,7 @@ static void fc_rport_recv_logo_req(struct fc_rport *, | |||
81 | struct fc_seq *, struct fc_frame *); | 81 | struct fc_seq *, struct fc_frame *); |
82 | static void fc_rport_timeout(struct work_struct *); | 82 | static void fc_rport_timeout(struct work_struct *); |
83 | static void fc_rport_error(struct fc_rport *, struct fc_frame *); | 83 | static void fc_rport_error(struct fc_rport *, struct fc_frame *); |
84 | static void fc_rport_error_retry(struct fc_rport *, struct fc_frame *); | ||
84 | static void fc_rport_work(struct work_struct *); | 85 | static void fc_rport_work(struct work_struct *); |
85 | 86 | ||
86 | static const char *fc_rport_state_names[] = { | 87 | static const char *fc_rport_state_names[] = { |
@@ -410,58 +411,74 @@ static void fc_rport_timeout(struct work_struct *work) | |||
410 | } | 411 | } |
411 | 412 | ||
412 | /** | 413 | /** |
413 | * fc_rport_error - Handler for any errors | 414 | * fc_rport_error - Error handler, called once retries have been exhausted |
414 | * @rport: The fc_rport object | 415 | * @rport: The fc_rport object |
415 | * @fp: The frame pointer | 416 | * @fp: The frame pointer |
416 | * | 417 | * |
417 | * If the error was caused by a resource allocation failure | ||
418 | * then wait for half a second and retry, otherwise retry | ||
419 | * immediately. | ||
420 | * | ||
421 | * Locking Note: The rport lock is expected to be held before | 418 | * Locking Note: The rport lock is expected to be held before |
422 | * calling this routine | 419 | * calling this routine |
423 | */ | 420 | */ |
424 | static void fc_rport_error(struct fc_rport *rport, struct fc_frame *fp) | 421 | static void fc_rport_error(struct fc_rport *rport, struct fc_frame *fp) |
425 | { | 422 | { |
426 | struct fc_rport_libfc_priv *rdata = rport->dd_data; | 423 | struct fc_rport_libfc_priv *rdata = rport->dd_data; |
427 | unsigned long delay = 0; | ||
428 | 424 | ||
429 | FC_DEBUG_RPORT("Error %ld in state %s, retries %d\n", | 425 | FC_DEBUG_RPORT("Error %ld in state %s, retries %d\n", |
430 | PTR_ERR(fp), fc_rport_state(rport), rdata->retries); | 426 | PTR_ERR(fp), fc_rport_state(rport), rdata->retries); |
431 | 427 | ||
432 | if (!fp || PTR_ERR(fp) == -FC_EX_TIMEOUT) { | 428 | switch (rdata->rp_state) { |
433 | /* | 429 | case RPORT_ST_PLOGI: |
434 | * Memory allocation failure, or the exchange timed out. | 430 | case RPORT_ST_PRLI: |
435 | * Retry after delay | 431 | case RPORT_ST_LOGO: |
436 | */ | 432 | rdata->event = RPORT_EV_FAILED; |
437 | if (rdata->retries < rdata->local_port->max_retry_count) { | 433 | queue_work(rport_event_queue, |
438 | rdata->retries++; | 434 | &rdata->event_work); |
439 | if (!fp) | 435 | break; |
440 | delay = msecs_to_jiffies(500); | 436 | case RPORT_ST_RTV: |
441 | get_device(&rport->dev); | 437 | fc_rport_enter_ready(rport); |
442 | schedule_delayed_work(&rdata->retry_work, delay); | 438 | break; |
443 | } else { | 439 | case RPORT_ST_NONE: |
444 | switch (rdata->rp_state) { | 440 | case RPORT_ST_READY: |
445 | case RPORT_ST_PLOGI: | 441 | case RPORT_ST_INIT: |
446 | case RPORT_ST_PRLI: | 442 | break; |
447 | case RPORT_ST_LOGO: | ||
448 | rdata->event = RPORT_EV_FAILED; | ||
449 | queue_work(rport_event_queue, | ||
450 | &rdata->event_work); | ||
451 | break; | ||
452 | case RPORT_ST_RTV: | ||
453 | fc_rport_enter_ready(rport); | ||
454 | break; | ||
455 | case RPORT_ST_NONE: | ||
456 | case RPORT_ST_READY: | ||
457 | case RPORT_ST_INIT: | ||
458 | break; | ||
459 | } | ||
460 | } | ||
461 | } | 443 | } |
462 | } | 444 | } |
463 | 445 | ||
464 | /** | 446 | /** |
447 | * fc_rport_error_retry - Error handler when retries are desired | ||
448 | * @rport: The fc_rport object | ||
449 | * @fp: The frame pointer | ||
450 | * | ||
451 | * If the error was an exchange timeout retry immediately, | ||
452 | * otherwise wait for E_D_TOV. | ||
453 | * | ||
454 | * Locking Note: The rport lock is expected to be held before | ||
455 | * calling this routine | ||
456 | */ | ||
457 | static void fc_rport_error_retry(struct fc_rport *rport, struct fc_frame *fp) | ||
458 | { | ||
459 | struct fc_rport_libfc_priv *rdata = rport->dd_data; | ||
460 | unsigned long delay = FC_DEF_E_D_TOV; | ||
461 | |||
462 | /* make sure this isn't an FC_EX_CLOSED error, never retry those */ | ||
463 | if (PTR_ERR(fp) == -FC_EX_CLOSED) | ||
464 | return fc_rport_error(rport, fp); | ||
465 | |||
466 | if (rdata->retries < rdata->local_port->max_retry_count) { | ||
467 | FC_DEBUG_RPORT("Error %ld in state %s, retrying\n", | ||
468 | PTR_ERR(fp), fc_rport_state(rport)); | ||
469 | rdata->retries++; | ||
470 | /* no additional delay on exchange timeouts */ | ||
471 | if (PTR_ERR(fp) == -FC_EX_TIMEOUT) | ||
472 | delay = 0; | ||
473 | get_device(&rport->dev); | ||
474 | schedule_delayed_work(&rdata->retry_work, delay); | ||
475 | return; | ||
476 | } | ||
477 | |||
478 | return fc_rport_error(rport, fp); | ||
479 | } | ||
480 | |||
481 | /** | ||
465 | * fc_rport_plogi_recv_resp - Handle incoming ELS PLOGI response | 482 | * fc_rport_plogi_recv_resp - Handle incoming ELS PLOGI response |
466 | * @sp: current sequence in the PLOGI exchange | 483 | * @sp: current sequence in the PLOGI exchange |
467 | * @fp: response frame | 484 | * @fp: response frame |
@@ -495,7 +512,7 @@ static void fc_rport_plogi_resp(struct fc_seq *sp, struct fc_frame *fp, | |||
495 | } | 512 | } |
496 | 513 | ||
497 | if (IS_ERR(fp)) { | 514 | if (IS_ERR(fp)) { |
498 | fc_rport_error(rport, fp); | 515 | fc_rport_error_retry(rport, fp); |
499 | goto err; | 516 | goto err; |
500 | } | 517 | } |
501 | 518 | ||
@@ -527,7 +544,7 @@ static void fc_rport_plogi_resp(struct fc_seq *sp, struct fc_frame *fp, | |||
527 | else | 544 | else |
528 | fc_rport_enter_prli(rport); | 545 | fc_rport_enter_prli(rport); |
529 | } else | 546 | } else |
530 | fc_rport_error(rport, fp); | 547 | fc_rport_error_retry(rport, fp); |
531 | 548 | ||
532 | out: | 549 | out: |
533 | fc_frame_free(fp); | 550 | fc_frame_free(fp); |
@@ -557,14 +574,14 @@ static void fc_rport_enter_plogi(struct fc_rport *rport) | |||
557 | rport->maxframe_size = FC_MIN_MAX_PAYLOAD; | 574 | rport->maxframe_size = FC_MIN_MAX_PAYLOAD; |
558 | fp = fc_frame_alloc(lport, sizeof(struct fc_els_flogi)); | 575 | fp = fc_frame_alloc(lport, sizeof(struct fc_els_flogi)); |
559 | if (!fp) { | 576 | if (!fp) { |
560 | fc_rport_error(rport, fp); | 577 | fc_rport_error_retry(rport, fp); |
561 | return; | 578 | return; |
562 | } | 579 | } |
563 | rdata->e_d_tov = lport->e_d_tov; | 580 | rdata->e_d_tov = lport->e_d_tov; |
564 | 581 | ||
565 | if (!lport->tt.elsct_send(lport, rport, fp, ELS_PLOGI, | 582 | if (!lport->tt.elsct_send(lport, rport, fp, ELS_PLOGI, |
566 | fc_rport_plogi_resp, rport, lport->e_d_tov)) | 583 | fc_rport_plogi_resp, rport, lport->e_d_tov)) |
567 | fc_rport_error(rport, fp); | 584 | fc_rport_error_retry(rport, fp); |
568 | else | 585 | else |
569 | get_device(&rport->dev); | 586 | get_device(&rport->dev); |
570 | } | 587 | } |
@@ -604,7 +621,7 @@ static void fc_rport_prli_resp(struct fc_seq *sp, struct fc_frame *fp, | |||
604 | } | 621 | } |
605 | 622 | ||
606 | if (IS_ERR(fp)) { | 623 | if (IS_ERR(fp)) { |
607 | fc_rport_error(rport, fp); | 624 | fc_rport_error_retry(rport, fp); |
608 | goto err; | 625 | goto err; |
609 | } | 626 | } |
610 | 627 | ||
@@ -662,7 +679,7 @@ static void fc_rport_logo_resp(struct fc_seq *sp, struct fc_frame *fp, | |||
662 | rport->port_id); | 679 | rport->port_id); |
663 | 680 | ||
664 | if (IS_ERR(fp)) { | 681 | if (IS_ERR(fp)) { |
665 | fc_rport_error(rport, fp); | 682 | fc_rport_error_retry(rport, fp); |
666 | goto err; | 683 | goto err; |
667 | } | 684 | } |
668 | 685 | ||
@@ -712,13 +729,13 @@ static void fc_rport_enter_prli(struct fc_rport *rport) | |||
712 | 729 | ||
713 | fp = fc_frame_alloc(lport, sizeof(*pp)); | 730 | fp = fc_frame_alloc(lport, sizeof(*pp)); |
714 | if (!fp) { | 731 | if (!fp) { |
715 | fc_rport_error(rport, fp); | 732 | fc_rport_error_retry(rport, fp); |
716 | return; | 733 | return; |
717 | } | 734 | } |
718 | 735 | ||
719 | if (!lport->tt.elsct_send(lport, rport, fp, ELS_PRLI, | 736 | if (!lport->tt.elsct_send(lport, rport, fp, ELS_PRLI, |
720 | fc_rport_prli_resp, rport, lport->e_d_tov)) | 737 | fc_rport_prli_resp, rport, lport->e_d_tov)) |
721 | fc_rport_error(rport, fp); | 738 | fc_rport_error_retry(rport, fp); |
722 | else | 739 | else |
723 | get_device(&rport->dev); | 740 | get_device(&rport->dev); |
724 | } | 741 | } |
@@ -809,13 +826,13 @@ static void fc_rport_enter_rtv(struct fc_rport *rport) | |||
809 | 826 | ||
810 | fp = fc_frame_alloc(lport, sizeof(struct fc_els_rtv)); | 827 | fp = fc_frame_alloc(lport, sizeof(struct fc_els_rtv)); |
811 | if (!fp) { | 828 | if (!fp) { |
812 | fc_rport_error(rport, fp); | 829 | fc_rport_error_retry(rport, fp); |
813 | return; | 830 | return; |
814 | } | 831 | } |
815 | 832 | ||
816 | if (!lport->tt.elsct_send(lport, rport, fp, ELS_RTV, | 833 | if (!lport->tt.elsct_send(lport, rport, fp, ELS_RTV, |
817 | fc_rport_rtv_resp, rport, lport->e_d_tov)) | 834 | fc_rport_rtv_resp, rport, lport->e_d_tov)) |
818 | fc_rport_error(rport, fp); | 835 | fc_rport_error_retry(rport, fp); |
819 | else | 836 | else |
820 | get_device(&rport->dev); | 837 | get_device(&rport->dev); |
821 | } | 838 | } |
@@ -840,13 +857,13 @@ static void fc_rport_enter_logo(struct fc_rport *rport) | |||
840 | 857 | ||
841 | fp = fc_frame_alloc(lport, sizeof(struct fc_els_logo)); | 858 | fp = fc_frame_alloc(lport, sizeof(struct fc_els_logo)); |
842 | if (!fp) { | 859 | if (!fp) { |
843 | fc_rport_error(rport, fp); | 860 | fc_rport_error_retry(rport, fp); |
844 | return; | 861 | return; |
845 | } | 862 | } |
846 | 863 | ||
847 | if (!lport->tt.elsct_send(lport, rport, fp, ELS_LOGO, | 864 | if (!lport->tt.elsct_send(lport, rport, fp, ELS_LOGO, |
848 | fc_rport_logo_resp, rport, lport->e_d_tov)) | 865 | fc_rport_logo_resp, rport, lport->e_d_tov)) |
849 | fc_rport_error(rport, fp); | 866 | fc_rport_error_retry(rport, fp); |
850 | else | 867 | else |
851 | get_device(&rport->dev); | 868 | get_device(&rport->dev); |
852 | } | 869 | } |
diff --git a/include/scsi/fc/fc_fs.h b/include/scsi/fc/fc_fs.h index 3e4801d2bdbb..1b7af3a64c7c 100644 --- a/include/scsi/fc/fc_fs.h +++ b/include/scsi/fc/fc_fs.h | |||
@@ -337,4 +337,9 @@ enum fc_pf_rjt_reason { | |||
337 | FC_RJT_VENDOR = 0xff, /* vendor specific reject */ | 337 | FC_RJT_VENDOR = 0xff, /* vendor specific reject */ |
338 | }; | 338 | }; |
339 | 339 | ||
340 | /* default timeout values */ | ||
341 | |||
342 | #define FC_DEF_E_D_TOV 2000UL | ||
343 | #define FC_DEF_R_A_TOV 10000UL | ||
344 | |||
340 | #endif /* _FC_FS_H_ */ | 345 | #endif /* _FC_FS_H_ */ |