1
0
Fork 0

[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>
hifive-unleashed-5.1
Chris Leech 2009-02-27 10:55:02 -08:00 committed by James Bottomley
parent bc0e17f691
commit 6755db1cd4
3 changed files with 69 additions and 49 deletions

View File

@ -32,8 +32,6 @@
#include <scsi/libfc.h> #include <scsi/libfc.h>
#include <scsi/fc_encode.h> #include <scsi/fc_encode.h>
#define FC_DEF_R_A_TOV (10 * 1000) /* resource allocation timeout */
/* /*
* fc_exch_debug can be set in debugger or at compile time to get more logs. * fc_exch_debug can be set in debugger or at compile time to get more logs.
*/ */

View File

@ -81,6 +81,7 @@ static void fc_rport_recv_logo_req(struct fc_rport *,
struct fc_seq *, struct fc_frame *); struct fc_seq *, struct fc_frame *);
static void fc_rport_timeout(struct work_struct *); static void fc_rport_timeout(struct work_struct *);
static void fc_rport_error(struct fc_rport *, struct fc_frame *); static void fc_rport_error(struct fc_rport *, struct fc_frame *);
static void fc_rport_error_retry(struct fc_rport *, struct fc_frame *);
static void fc_rport_work(struct work_struct *); static void fc_rport_work(struct work_struct *);
static const char *fc_rport_state_names[] = { static const char *fc_rport_state_names[] = {
@ -410,57 +411,73 @@ static void fc_rport_timeout(struct work_struct *work)
} }
/** /**
* fc_rport_error - Handler for any errors * fc_rport_error - Error handler, called once retries have been exhausted
* @rport: The fc_rport object * @rport: The fc_rport object
* @fp: The frame pointer * @fp: The frame pointer
* *
* If the error was caused by a resource allocation failure
* then wait for half a second and retry, otherwise retry
* immediately.
*
* Locking Note: The rport lock is expected to be held before * Locking Note: The rport lock is expected to be held before
* calling this routine * calling this routine
*/ */
static void fc_rport_error(struct fc_rport *rport, struct fc_frame *fp) static void fc_rport_error(struct fc_rport *rport, struct fc_frame *fp)
{ {
struct fc_rport_libfc_priv *rdata = rport->dd_data; struct fc_rport_libfc_priv *rdata = rport->dd_data;
unsigned long delay = 0;
FC_DEBUG_RPORT("Error %ld in state %s, retries %d\n", FC_DEBUG_RPORT("Error %ld in state %s, retries %d\n",
PTR_ERR(fp), fc_rport_state(rport), rdata->retries); PTR_ERR(fp), fc_rport_state(rport), rdata->retries);
if (!fp || PTR_ERR(fp) == -FC_EX_TIMEOUT) { switch (rdata->rp_state) {
/* case RPORT_ST_PLOGI:
* Memory allocation failure, or the exchange timed out. case RPORT_ST_PRLI:
* Retry after delay case RPORT_ST_LOGO:
*/ rdata->event = RPORT_EV_FAILED;
if (rdata->retries < rdata->local_port->max_retry_count) { queue_work(rport_event_queue,
rdata->retries++; &rdata->event_work);
if (!fp) break;
delay = msecs_to_jiffies(500); case RPORT_ST_RTV:
get_device(&rport->dev); fc_rport_enter_ready(rport);
schedule_delayed_work(&rdata->retry_work, delay); break;
} else { case RPORT_ST_NONE:
switch (rdata->rp_state) { case RPORT_ST_READY:
case RPORT_ST_PLOGI: case RPORT_ST_INIT:
case RPORT_ST_PRLI: break;
case RPORT_ST_LOGO:
rdata->event = RPORT_EV_FAILED;
queue_work(rport_event_queue,
&rdata->event_work);
break;
case RPORT_ST_RTV:
fc_rport_enter_ready(rport);
break;
case RPORT_ST_NONE:
case RPORT_ST_READY:
case RPORT_ST_INIT:
break;
}
}
} }
} }
/**
* fc_rport_error_retry - Error handler when retries are desired
* @rport: The fc_rport object
* @fp: The frame pointer
*
* If the error was an exchange timeout retry immediately,
* otherwise wait for E_D_TOV.
*
* Locking Note: The rport lock is expected to be held before
* calling this routine
*/
static void fc_rport_error_retry(struct fc_rport *rport, struct fc_frame *fp)
{
struct fc_rport_libfc_priv *rdata = rport->dd_data;
unsigned long delay = FC_DEF_E_D_TOV;
/* make sure this isn't an FC_EX_CLOSED error, never retry those */
if (PTR_ERR(fp) == -FC_EX_CLOSED)
return fc_rport_error(rport, fp);
if (rdata->retries < rdata->local_port->max_retry_count) {
FC_DEBUG_RPORT("Error %ld in state %s, retrying\n",
PTR_ERR(fp), fc_rport_state(rport));
rdata->retries++;
/* no additional delay on exchange timeouts */
if (PTR_ERR(fp) == -FC_EX_TIMEOUT)
delay = 0;
get_device(&rport->dev);
schedule_delayed_work(&rdata->retry_work, delay);
return;
}
return fc_rport_error(rport, fp);
}
/** /**
* fc_rport_plogi_recv_resp - Handle incoming ELS PLOGI response * fc_rport_plogi_recv_resp - Handle incoming ELS PLOGI response
* @sp: current sequence in the PLOGI exchange * @sp: current sequence in the PLOGI exchange
@ -495,7 +512,7 @@ static void fc_rport_plogi_resp(struct fc_seq *sp, struct fc_frame *fp,
} }
if (IS_ERR(fp)) { if (IS_ERR(fp)) {
fc_rport_error(rport, fp); fc_rport_error_retry(rport, fp);
goto err; goto err;
} }
@ -527,7 +544,7 @@ static void fc_rport_plogi_resp(struct fc_seq *sp, struct fc_frame *fp,
else else
fc_rport_enter_prli(rport); fc_rport_enter_prli(rport);
} else } else
fc_rport_error(rport, fp); fc_rport_error_retry(rport, fp);
out: out:
fc_frame_free(fp); fc_frame_free(fp);
@ -557,14 +574,14 @@ static void fc_rport_enter_plogi(struct fc_rport *rport)
rport->maxframe_size = FC_MIN_MAX_PAYLOAD; rport->maxframe_size = FC_MIN_MAX_PAYLOAD;
fp = fc_frame_alloc(lport, sizeof(struct fc_els_flogi)); fp = fc_frame_alloc(lport, sizeof(struct fc_els_flogi));
if (!fp) { if (!fp) {
fc_rport_error(rport, fp); fc_rport_error_retry(rport, fp);
return; return;
} }
rdata->e_d_tov = lport->e_d_tov; rdata->e_d_tov = lport->e_d_tov;
if (!lport->tt.elsct_send(lport, rport, fp, ELS_PLOGI, if (!lport->tt.elsct_send(lport, rport, fp, ELS_PLOGI,
fc_rport_plogi_resp, rport, lport->e_d_tov)) fc_rport_plogi_resp, rport, lport->e_d_tov))
fc_rport_error(rport, fp); fc_rport_error_retry(rport, fp);
else else
get_device(&rport->dev); get_device(&rport->dev);
} }
@ -604,7 +621,7 @@ static void fc_rport_prli_resp(struct fc_seq *sp, struct fc_frame *fp,
} }
if (IS_ERR(fp)) { if (IS_ERR(fp)) {
fc_rport_error(rport, fp); fc_rport_error_retry(rport, fp);
goto err; goto err;
} }
@ -662,7 +679,7 @@ static void fc_rport_logo_resp(struct fc_seq *sp, struct fc_frame *fp,
rport->port_id); rport->port_id);
if (IS_ERR(fp)) { if (IS_ERR(fp)) {
fc_rport_error(rport, fp); fc_rport_error_retry(rport, fp);
goto err; goto err;
} }
@ -712,13 +729,13 @@ static void fc_rport_enter_prli(struct fc_rport *rport)
fp = fc_frame_alloc(lport, sizeof(*pp)); fp = fc_frame_alloc(lport, sizeof(*pp));
if (!fp) { if (!fp) {
fc_rport_error(rport, fp); fc_rport_error_retry(rport, fp);
return; return;
} }
if (!lport->tt.elsct_send(lport, rport, fp, ELS_PRLI, if (!lport->tt.elsct_send(lport, rport, fp, ELS_PRLI,
fc_rport_prli_resp, rport, lport->e_d_tov)) fc_rport_prli_resp, rport, lport->e_d_tov))
fc_rport_error(rport, fp); fc_rport_error_retry(rport, fp);
else else
get_device(&rport->dev); get_device(&rport->dev);
} }
@ -809,13 +826,13 @@ static void fc_rport_enter_rtv(struct fc_rport *rport)
fp = fc_frame_alloc(lport, sizeof(struct fc_els_rtv)); fp = fc_frame_alloc(lport, sizeof(struct fc_els_rtv));
if (!fp) { if (!fp) {
fc_rport_error(rport, fp); fc_rport_error_retry(rport, fp);
return; return;
} }
if (!lport->tt.elsct_send(lport, rport, fp, ELS_RTV, if (!lport->tt.elsct_send(lport, rport, fp, ELS_RTV,
fc_rport_rtv_resp, rport, lport->e_d_tov)) fc_rport_rtv_resp, rport, lport->e_d_tov))
fc_rport_error(rport, fp); fc_rport_error_retry(rport, fp);
else else
get_device(&rport->dev); get_device(&rport->dev);
} }
@ -840,13 +857,13 @@ static void fc_rport_enter_logo(struct fc_rport *rport)
fp = fc_frame_alloc(lport, sizeof(struct fc_els_logo)); fp = fc_frame_alloc(lport, sizeof(struct fc_els_logo));
if (!fp) { if (!fp) {
fc_rport_error(rport, fp); fc_rport_error_retry(rport, fp);
return; return;
} }
if (!lport->tt.elsct_send(lport, rport, fp, ELS_LOGO, if (!lport->tt.elsct_send(lport, rport, fp, ELS_LOGO,
fc_rport_logo_resp, rport, lport->e_d_tov)) fc_rport_logo_resp, rport, lport->e_d_tov))
fc_rport_error(rport, fp); fc_rport_error_retry(rport, fp);
else else
get_device(&rport->dev); get_device(&rport->dev);
} }

View File

@ -337,4 +337,9 @@ enum fc_pf_rjt_reason {
FC_RJT_VENDOR = 0xff, /* vendor specific reject */ FC_RJT_VENDOR = 0xff, /* vendor specific reject */
}; };
/* default timeout values */
#define FC_DEF_E_D_TOV 2000UL
#define FC_DEF_R_A_TOV 10000UL
#endif /* _FC_FS_H_ */ #endif /* _FC_FS_H_ */