summaryrefslogtreecommitdiffstats
path: root/drivers/scsi/libfc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/scsi/libfc')
-rw-r--r--drivers/scsi/libfc/fc_disc.c54
-rw-r--r--drivers/scsi/libfc/fc_elsct.c2
-rw-r--r--drivers/scsi/libfc/fc_fcp.c7
-rw-r--r--drivers/scsi/libfc/fc_lport.c74
-rw-r--r--drivers/scsi/libfc/fc_rport.c82
5 files changed, 150 insertions, 69 deletions
diff --git a/drivers/scsi/libfc/fc_disc.c b/drivers/scsi/libfc/fc_disc.c
index e57556ea5b48..4c880656990b 100644
--- a/drivers/scsi/libfc/fc_disc.c
+++ b/drivers/scsi/libfc/fc_disc.c
@@ -113,6 +113,11 @@ void fc_disc_stop_rports(struct fc_disc *disc)
lport->tt.rport_logoff(rport);
}
+ list_for_each_entry_safe(rdata, next, &disc->rogue_rports, peers) {
+ rport = PRIV_TO_RPORT(rdata);
+ lport->tt.rport_logoff(rport);
+ }
+
mutex_unlock(&disc->disc_mutex);
}
@@ -131,23 +136,32 @@ static void fc_disc_rport_callback(struct fc_lport *lport,
{
struct fc_rport_libfc_priv *rdata = rport->dd_data;
struct fc_disc *disc = &lport->disc;
- int found = 0;
FC_DEBUG_DISC("Received a %d event for port (%6x)\n", event,
rport->port_id);
- if (event == RPORT_EV_CREATED) {
+ switch (event) {
+ case RPORT_EV_CREATED:
if (disc) {
- found = 1;
mutex_lock(&disc->disc_mutex);
list_add_tail(&rdata->peers, &disc->rports);
mutex_unlock(&disc->disc_mutex);
}
+ break;
+ case RPORT_EV_LOGO:
+ case RPORT_EV_FAILED:
+ case RPORT_EV_STOP:
+ mutex_lock(&disc->disc_mutex);
+ mutex_lock(&rdata->rp_mutex);
+ if (rdata->trans_state == FC_PORTSTATE_ROGUE)
+ list_del(&rdata->peers);
+ mutex_unlock(&rdata->rp_mutex);
+ mutex_unlock(&disc->disc_mutex);
+ break;
+ default:
+ break;
}
- if (!found)
- FC_DEBUG_DISC("The rport (%6x) is not maintained "
- "by the discovery layer\n", rport->port_id);
}
/**
@@ -439,6 +453,7 @@ static int fc_disc_new_target(struct fc_disc *disc,
rdata = rport->dd_data;
rdata->ops = &fc_disc_rport_ops;
rdata->rp_state = RPORT_ST_INIT;
+ list_add_tail(&rdata->peers, &disc->rogue_rports);
lport->tt.rport_login(rport);
}
}
@@ -461,21 +476,29 @@ static void fc_disc_del_target(struct fc_disc *disc, struct fc_rport *rport)
/**
* fc_disc_done() - Discovery has been completed
* @disc: FC discovery context
+ * Locking Note: This function expects that the disc mutex is locked before
+ * it is called. The discovery callback is then made with the lock released,
+ * and the lock is re-taken before returning from this function
*/
static void fc_disc_done(struct fc_disc *disc)
{
struct fc_lport *lport = disc->lport;
+ enum fc_disc_event event;
FC_DEBUG_DISC("Discovery complete for port (%6x)\n",
fc_host_port_id(lport->host));
- disc->disc_callback(lport, disc->event);
+ event = disc->event;
disc->event = DISC_EV_NONE;
if (disc->requested)
fc_disc_gpn_ft_req(disc);
else
disc->pending = 0;
+
+ mutex_unlock(&disc->disc_mutex);
+ disc->disc_callback(lport, event);
+ mutex_lock(&disc->disc_mutex);
}
/**
@@ -622,6 +645,8 @@ static int fc_disc_gpn_ft_parse(struct fc_disc *disc, void *buf, size_t len)
rdata = rport->dd_data;
rdata->ops = &fc_disc_rport_ops;
rdata->local_port = lport;
+ list_add_tail(&rdata->peers,
+ &disc->rogue_rports);
lport->tt.rport_login(rport);
} else
FC_DBG("Failed to allocate memory for "
@@ -681,8 +706,8 @@ static void fc_disc_timeout(struct work_struct *work)
* @fp: response frame
* @lp_arg: Fibre Channel host port instance
*
- * Locking Note: This function expects that the disc_mutex is locked
- * before it is called.
+ * Locking Note: This function is called without disc mutex held, and
+ * should do all its processing with the mutex held
*/
static void fc_disc_gpn_ft_resp(struct fc_seq *sp, struct fc_frame *fp,
void *disc_arg)
@@ -695,11 +720,13 @@ static void fc_disc_gpn_ft_resp(struct fc_seq *sp, struct fc_frame *fp,
unsigned int len;
int error;
+ mutex_lock(&disc->disc_mutex);
FC_DEBUG_DISC("Received a GPN_FT response on port (%6x)\n",
fc_host_port_id(disc->lport->host));
if (IS_ERR(fp)) {
fc_disc_error(disc, fp);
+ mutex_unlock(&disc->disc_mutex);
return;
}
@@ -744,6 +771,8 @@ static void fc_disc_gpn_ft_resp(struct fc_seq *sp, struct fc_frame *fp,
disc->seq_count++;
}
fc_frame_free(fp);
+
+ mutex_unlock(&disc->disc_mutex);
}
/**
@@ -757,7 +786,6 @@ static void fc_disc_gpn_ft_resp(struct fc_seq *sp, struct fc_frame *fp,
static void fc_disc_single(struct fc_disc *disc, struct fc_disc_port *dp)
{
struct fc_lport *lport;
- struct fc_rport *rport;
struct fc_rport *new_rport;
struct fc_rport_libfc_priv *rdata;
@@ -766,15 +794,12 @@ static void fc_disc_single(struct fc_disc *disc, struct fc_disc_port *dp)
if (dp->ids.port_id == fc_host_port_id(lport->host))
goto out;
- rport = lport->tt.rport_lookup(lport, dp->ids.port_id);
- if (rport)
- fc_disc_del_target(disc, rport);
-
new_rport = lport->tt.rport_create(dp);
if (new_rport) {
rdata = new_rport->dd_data;
rdata->ops = &fc_disc_rport_ops;
kfree(dp);
+ list_add_tail(&rdata->peers, &disc->rogue_rports);
lport->tt.rport_login(new_rport);
}
return;
@@ -836,6 +861,7 @@ int fc_disc_init(struct fc_lport *lport)
INIT_DELAYED_WORK(&disc->disc_work, fc_disc_timeout);
mutex_init(&disc->disc_mutex);
INIT_LIST_HEAD(&disc->rports);
+ INIT_LIST_HEAD(&disc->rogue_rports);
disc->lport = lport;
disc->delay = FC_DISC_DELAY;
diff --git a/drivers/scsi/libfc/fc_elsct.c b/drivers/scsi/libfc/fc_elsct.c
index dd47fe619d1e..5878b34bff18 100644
--- a/drivers/scsi/libfc/fc_elsct.c
+++ b/drivers/scsi/libfc/fc_elsct.c
@@ -41,7 +41,7 @@ static struct fc_seq *fc_elsct_send(struct fc_lport *lport,
void *arg, u32 timer_msec)
{
enum fc_rctl r_ctl;
- u32 did;
+ u32 did = FC_FID_NONE;
enum fc_fh_type fh_type;
int rc;
diff --git a/drivers/scsi/libfc/fc_fcp.c b/drivers/scsi/libfc/fc_fcp.c
index f555ae99ad40..521f996f9b13 100644
--- a/drivers/scsi/libfc/fc_fcp.c
+++ b/drivers/scsi/libfc/fc_fcp.c
@@ -713,7 +713,7 @@ done:
static void fc_fcp_recv(struct fc_seq *seq, struct fc_frame *fp, void *arg)
{
struct fc_fcp_pkt *fsp = (struct fc_fcp_pkt *)arg;
- struct fc_lport *lp;
+ struct fc_lport *lport = fsp->lp;
struct fc_frame_header *fh;
struct fcp_txrdy *dd;
u8 r_ctl;
@@ -724,9 +724,8 @@ static void fc_fcp_recv(struct fc_seq *seq, struct fc_frame *fp, void *arg)
fh = fc_frame_header_get(fp);
r_ctl = fh->fh_r_ctl;
- lp = fsp->lp;
- if (!(lp->state & LPORT_ST_READY))
+ if (!(lport->state & LPORT_ST_READY))
goto out;
if (fc_fcp_lock_pkt(fsp))
goto out;
@@ -779,7 +778,7 @@ errout:
if (IS_ERR(fp))
fc_fcp_error(fsp, fp);
else if (rc == -ENOMEM)
- fc_fcp_reduce_can_queue(lp);
+ fc_fcp_reduce_can_queue(lport);
}
static void fc_fcp_resp(struct fc_fcp_pkt *fsp, struct fc_frame *fp)
diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c
index b8178ef398d7..e0c247724d2b 100644
--- a/drivers/scsi/libfc/fc_lport.c
+++ b/drivers/scsi/libfc/fc_lport.c
@@ -618,6 +618,11 @@ int fc_fabric_logoff(struct fc_lport *lport)
{
lport->tt.disc_stop_final(lport);
mutex_lock(&lport->lp_mutex);
+ if (lport->dns_rp)
+ lport->tt.rport_logoff(lport->dns_rp);
+ mutex_unlock(&lport->lp_mutex);
+ lport->tt.rport_flush_queue();
+ mutex_lock(&lport->lp_mutex);
fc_lport_enter_logo(lport);
mutex_unlock(&lport->lp_mutex);
cancel_delayed_work_sync(&lport->retry_work);
@@ -639,7 +644,12 @@ EXPORT_SYMBOL(fc_fabric_logoff);
*/
int fc_lport_destroy(struct fc_lport *lport)
{
+ mutex_lock(&lport->lp_mutex);
+ lport->state = LPORT_ST_NONE;
+ lport->link_up = 0;
lport->tt.frame_send = fc_frame_drop;
+ mutex_unlock(&lport->lp_mutex);
+
lport->tt.fcp_abort_io(lport);
lport->tt.exch_mgr_reset(lport, 0, 0);
return 0;
@@ -1032,17 +1042,19 @@ static void fc_lport_rft_id_resp(struct fc_seq *sp, struct fc_frame *fp,
FC_DEBUG_LPORT("Received a RFT_ID response\n");
- if (IS_ERR(fp)) {
- fc_lport_error(lport, fp);
- goto err;
- }
-
if (lport->state != LPORT_ST_RFT_ID) {
FC_DBG("Received a RFT_ID response, but in state %s\n",
fc_lport_state(lport));
+ if (IS_ERR(fp))
+ goto err;
goto out;
}
+ if (IS_ERR(fp)) {
+ fc_lport_error(lport, fp);
+ goto err;
+ }
+
fh = fc_frame_header_get(fp);
ct = fc_frame_payload_get(fp, sizeof(*ct));
@@ -1084,17 +1096,19 @@ static void fc_lport_rpn_id_resp(struct fc_seq *sp, struct fc_frame *fp,
FC_DEBUG_LPORT("Received a RPN_ID response\n");
- if (IS_ERR(fp)) {
- fc_lport_error(lport, fp);
- goto err;
- }
-
if (lport->state != LPORT_ST_RPN_ID) {
FC_DBG("Received a RPN_ID response, but in state %s\n",
fc_lport_state(lport));
+ if (IS_ERR(fp))
+ goto err;
goto out;
}
+ if (IS_ERR(fp)) {
+ fc_lport_error(lport, fp);
+ goto err;
+ }
+
fh = fc_frame_header_get(fp);
ct = fc_frame_payload_get(fp, sizeof(*ct));
if (fh && ct && fh->fh_type == FC_TYPE_CT &&
@@ -1134,17 +1148,19 @@ static void fc_lport_scr_resp(struct fc_seq *sp, struct fc_frame *fp,
FC_DEBUG_LPORT("Received a SCR response\n");
- if (IS_ERR(fp)) {
- fc_lport_error(lport, fp);
- goto err;
- }
-
if (lport->state != LPORT_ST_SCR) {
FC_DBG("Received a SCR response, but in state %s\n",
fc_lport_state(lport));
+ if (IS_ERR(fp))
+ goto err;
goto out;
}
+ if (IS_ERR(fp)) {
+ fc_lport_error(lport, fp);
+ goto err;
+ }
+
op = fc_frame_payload_op(fp);
if (op == ELS_LS_ACC)
fc_lport_enter_ready(lport);
@@ -1360,17 +1376,19 @@ static void fc_lport_logo_resp(struct fc_seq *sp, struct fc_frame *fp,
FC_DEBUG_LPORT("Received a LOGO response\n");
- if (IS_ERR(fp)) {
- fc_lport_error(lport, fp);
- goto err;
- }
-
if (lport->state != LPORT_ST_LOGO) {
FC_DBG("Received a LOGO response, but in state %s\n",
fc_lport_state(lport));
+ if (IS_ERR(fp))
+ goto err;
goto out;
}
+ if (IS_ERR(fp)) {
+ fc_lport_error(lport, fp);
+ goto err;
+ }
+
op = fc_frame_payload_op(fp);
if (op == ELS_LS_ACC)
fc_lport_enter_reset(lport);
@@ -1400,10 +1418,6 @@ static void fc_lport_enter_logo(struct fc_lport *lport)
fc_lport_state_enter(lport, LPORT_ST_LOGO);
- /* DNS session should be closed so we can release it here */
- if (lport->dns_rp)
- lport->tt.rport_logoff(lport->dns_rp);
-
fp = fc_frame_alloc(lport, sizeof(*logo));
if (!fp) {
fc_lport_error(lport, fp);
@@ -1444,17 +1458,19 @@ static void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp,
FC_DEBUG_LPORT("Received a FLOGI response\n");
- if (IS_ERR(fp)) {
- fc_lport_error(lport, fp);
- goto err;
- }
-
if (lport->state != LPORT_ST_FLOGI) {
FC_DBG("Received a FLOGI response, but in state %s\n",
fc_lport_state(lport));
+ if (IS_ERR(fp))
+ goto err;
goto out;
}
+ if (IS_ERR(fp)) {
+ fc_lport_error(lport, fp);
+ goto err;
+ }
+
fh = fc_frame_header_get(fp);
did = ntoh24(fh->fh_d_id);
if (fc_frame_payload_op(fp) == ELS_LS_ACC && did != 0) {
diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c
index 0472bb73221e..747d73c5c8af 100644
--- a/drivers/scsi/libfc/fc_rport.c
+++ b/drivers/scsi/libfc/fc_rport.c
@@ -267,6 +267,10 @@ static void fc_rport_work(struct work_struct *work)
"(%6x).\n", ids.port_id);
event = RPORT_EV_FAILED;
}
+ if (rport->port_id != FC_FID_DIR_SERV)
+ if (rport_ops->event_callback)
+ rport_ops->event_callback(lport, rport,
+ RPORT_EV_FAILED);
put_device(&rport->dev);
rport = new_rport;
rdata = new_rport->dd_data;
@@ -325,11 +329,20 @@ int fc_rport_login(struct fc_rport *rport)
int fc_rport_logoff(struct fc_rport *rport)
{
struct fc_rport_libfc_priv *rdata = rport->dd_data;
+ struct fc_lport *lport = rdata->local_port;
mutex_lock(&rdata->rp_mutex);
FC_DEBUG_RPORT("Remove port (%6x)\n", rport->port_id);
+ if (rdata->rp_state == RPORT_ST_NONE) {
+ FC_DEBUG_RPORT("(%6x): Port (%6x) in NONE state,"
+ " not removing", fc_host_port_id(lport->host),
+ rport->port_id);
+ mutex_unlock(&rdata->rp_mutex);
+ goto out;
+ }
+
fc_rport_enter_logo(rport);
/*
@@ -349,6 +362,7 @@ int fc_rport_logoff(struct fc_rport *rport)
mutex_unlock(&rdata->rp_mutex);
+out:
return 0;
}
@@ -430,6 +444,7 @@ static void fc_rport_error(struct fc_rport *rport, struct fc_frame *fp)
case RPORT_ST_PRLI:
case RPORT_ST_LOGO:
rdata->event = RPORT_EV_FAILED;
+ fc_rport_state_enter(rport, RPORT_ST_NONE);
queue_work(rport_event_queue,
&rdata->event_work);
break;
@@ -494,7 +509,7 @@ static void fc_rport_plogi_resp(struct fc_seq *sp, struct fc_frame *fp,
struct fc_rport *rport = rp_arg;
struct fc_rport_libfc_priv *rdata = rport->dd_data;
struct fc_lport *lport = rdata->local_port;
- struct fc_els_flogi *plp;
+ struct fc_els_flogi *plp = NULL;
unsigned int tov;
u16 csp_seq;
u16 cssp_seq;
@@ -505,17 +520,19 @@ static void fc_rport_plogi_resp(struct fc_seq *sp, struct fc_frame *fp,
FC_DEBUG_RPORT("Received a PLOGI response from port (%6x)\n",
rport->port_id);
- if (IS_ERR(fp)) {
- fc_rport_error_retry(rport, fp);
- goto err;
- }
-
if (rdata->rp_state != RPORT_ST_PLOGI) {
FC_DBG("Received a PLOGI response, but in state %s\n",
fc_rport_state(rport));
+ if (IS_ERR(fp))
+ goto err;
goto out;
}
+ if (IS_ERR(fp)) {
+ fc_rport_error_retry(rport, fp);
+ goto err;
+ }
+
op = fc_frame_payload_op(fp);
if (op == ELS_LS_ACC &&
(plp = fc_frame_payload_get(fp, sizeof(*plp))) != NULL) {
@@ -614,17 +631,19 @@ static void fc_rport_prli_resp(struct fc_seq *sp, struct fc_frame *fp,
FC_DEBUG_RPORT("Received a PRLI response from port (%6x)\n",
rport->port_id);
- if (IS_ERR(fp)) {
- fc_rport_error_retry(rport, fp);
- goto err;
- }
-
if (rdata->rp_state != RPORT_ST_PRLI) {
FC_DBG("Received a PRLI response, but in state %s\n",
fc_rport_state(rport));
+ if (IS_ERR(fp))
+ goto err;
goto out;
}
+ if (IS_ERR(fp)) {
+ fc_rport_error_retry(rport, fp);
+ goto err;
+ }
+
op = fc_frame_payload_op(fp);
if (op == ELS_LS_ACC) {
pp = fc_frame_payload_get(fp, sizeof(*pp));
@@ -646,6 +665,7 @@ static void fc_rport_prli_resp(struct fc_seq *sp, struct fc_frame *fp,
} else {
FC_DBG("Bad ELS response\n");
rdata->event = RPORT_EV_FAILED;
+ fc_rport_state_enter(rport, RPORT_ST_NONE);
queue_work(rport_event_queue, &rdata->event_work);
}
@@ -678,23 +698,26 @@ static void fc_rport_logo_resp(struct fc_seq *sp, struct fc_frame *fp,
FC_DEBUG_RPORT("Received a LOGO response from port (%6x)\n",
rport->port_id);
- if (IS_ERR(fp)) {
- fc_rport_error_retry(rport, fp);
- goto err;
- }
-
if (rdata->rp_state != RPORT_ST_LOGO) {
FC_DEBUG_RPORT("Received a LOGO response, but in state %s\n",
fc_rport_state(rport));
+ if (IS_ERR(fp))
+ goto err;
goto out;
}
+ if (IS_ERR(fp)) {
+ fc_rport_error_retry(rport, fp);
+ goto err;
+ }
+
op = fc_frame_payload_op(fp);
if (op == ELS_LS_ACC) {
fc_rport_enter_rtv(rport);
} else {
FC_DBG("Bad ELS response\n");
rdata->event = RPORT_EV_LOGO;
+ fc_rport_state_enter(rport, RPORT_ST_NONE);
queue_work(rport_event_queue, &rdata->event_work);
}
@@ -764,17 +787,19 @@ static void fc_rport_rtv_resp(struct fc_seq *sp, struct fc_frame *fp,
FC_DEBUG_RPORT("Received a RTV response from port (%6x)\n",
rport->port_id);
- if (IS_ERR(fp)) {
- fc_rport_error(rport, fp);
- goto err;
- }
-
if (rdata->rp_state != RPORT_ST_RTV) {
FC_DBG("Received a RTV response, but in state %s\n",
fc_rport_state(rport));
+ if (IS_ERR(fp))
+ goto err;
goto out;
}
+ if (IS_ERR(fp)) {
+ fc_rport_error(rport, fp);
+ goto err;
+ }
+
op = fc_frame_payload_op(fp);
if (op == ELS_LS_ACC) {
struct fc_els_rtv_acc *rtv;
@@ -1007,6 +1032,8 @@ static void fc_rport_recv_plogi_req(struct fc_rport *rport,
default:
FC_DEBUG_RPORT("incoming PLOGI from %x in unexpected "
"state %d\n", sid, rdata->rp_state);
+ fc_frame_free(fp);
+ return;
break;
}
@@ -1098,6 +1125,8 @@ static void fc_rport_recv_prli_req(struct fc_rport *rport,
reason = ELS_RJT_NONE;
break;
default:
+ fc_frame_free(rx_fp);
+ return;
break;
}
len = fr_len(rx_fp) - sizeof(*fh);
@@ -1227,6 +1256,11 @@ static void fc_rport_recv_prlo_req(struct fc_rport *rport, struct fc_seq *sp,
"while in state %s\n", ntoh24(fh->fh_s_id),
fc_rport_state(rport));
+ if (rdata->rp_state == RPORT_ST_NONE) {
+ fc_frame_free(fp);
+ return;
+ }
+
rjt_data.fp = NULL;
rjt_data.reason = ELS_RJT_UNAB;
rjt_data.explan = ELS_EXPL_NONE;
@@ -1256,7 +1290,13 @@ static void fc_rport_recv_logo_req(struct fc_rport *rport, struct fc_seq *sp,
"while in state %s\n", ntoh24(fh->fh_s_id),
fc_rport_state(rport));
+ if (rdata->rp_state == RPORT_ST_NONE) {
+ fc_frame_free(fp);
+ return;
+ }
+
rdata->event = RPORT_EV_LOGO;
+ fc_rport_state_enter(rport, RPORT_ST_NONE);
queue_work(rport_event_queue, &rdata->event_work);
lport->tt.seq_els_rsp_send(sp, ELS_LS_ACC, NULL);