diff options
Diffstat (limited to 'drivers/scsi/libfc/fc_lport.c')
| -rw-r--r-- | drivers/scsi/libfc/fc_lport.c | 314 |
1 files changed, 277 insertions, 37 deletions
diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c index 83750ebb527..e01a29863c3 100644 --- a/drivers/scsi/libfc/fc_lport.c +++ b/drivers/scsi/libfc/fc_lport.c @@ -116,6 +116,8 @@ static void fc_lport_enter_ns(struct fc_lport *, enum fc_lport_state); static void fc_lport_enter_scr(struct fc_lport *); static void fc_lport_enter_ready(struct fc_lport *); static void fc_lport_enter_logo(struct fc_lport *); +static void fc_lport_enter_fdmi(struct fc_lport *lport); +static void fc_lport_enter_ms(struct fc_lport *, enum fc_lport_state); static const char *fc_lport_state_names[] = { [LPORT_ST_DISABLED] = "disabled", @@ -126,6 +128,11 @@ static const char *fc_lport_state_names[] = { [LPORT_ST_RSPN_ID] = "RSPN_ID", [LPORT_ST_RFT_ID] = "RFT_ID", [LPORT_ST_RFF_ID] = "RFF_ID", + [LPORT_ST_FDMI] = "FDMI", + [LPORT_ST_RHBA] = "RHBA", + [LPORT_ST_RPA] = "RPA", + [LPORT_ST_DHBA] = "DHBA", + [LPORT_ST_DPRT] = "DPRT", [LPORT_ST_SCR] = "SCR", [LPORT_ST_READY] = "Ready", [LPORT_ST_LOGO] = "LOGO", @@ -183,11 +190,14 @@ static void fc_lport_rport_callback(struct fc_lport *lport, if (lport->state == LPORT_ST_DNS) { lport->dns_rdata = rdata; fc_lport_enter_ns(lport, LPORT_ST_RNN_ID); + } else if (lport->state == LPORT_ST_FDMI) { + lport->ms_rdata = rdata; + fc_lport_enter_ms(lport, LPORT_ST_DHBA); } else { FC_LPORT_DBG(lport, "Received an READY event " "on port (%6.6x) for the directory " "server, but the lport is not " - "in the DNS state, it's in the " + "in the DNS or FDMI state, it's in the " "%d state", rdata->ids.port_id, lport->state); lport->tt.rport_logoff(rdata); @@ -196,7 +206,10 @@ static void fc_lport_rport_callback(struct fc_lport *lport, case RPORT_EV_LOGO: case RPORT_EV_FAILED: case RPORT_EV_STOP: - lport->dns_rdata = NULL; + if (rdata->ids.port_id == FC_FID_DIR_SERV) + lport->dns_rdata = NULL; + else if (rdata->ids.port_id == FC_FID_MGMT_SERV) + lport->ms_rdata = NULL; break; case RPORT_EV_NONE: break; @@ -286,47 +299,54 @@ EXPORT_SYMBOL(fc_get_host_speed); */ struct fc_host_statistics *fc_get_host_stats(struct Scsi_Host *shost) { - struct fc_host_statistics *fcoe_stats; + struct fc_host_statistics *fc_stats; struct fc_lport *lport = shost_priv(shost); struct timespec v0, v1; unsigned int cpu; u64 fcp_in_bytes = 0; u64 fcp_out_bytes = 0; - fcoe_stats = &lport->host_stats; - memset(fcoe_stats, 0, sizeof(struct fc_host_statistics)); + fc_stats = &lport->host_stats; + memset(fc_stats, 0, sizeof(struct fc_host_statistics)); jiffies_to_timespec(jiffies, &v0); jiffies_to_timespec(lport->boot_time, &v1); - fcoe_stats->seconds_since_last_reset = (v0.tv_sec - v1.tv_sec); + fc_stats->seconds_since_last_reset = (v0.tv_sec - v1.tv_sec); for_each_possible_cpu(cpu) { - struct fcoe_dev_stats *stats; - - stats = per_cpu_ptr(lport->dev_stats, cpu); - - fcoe_stats->tx_frames += stats->TxFrames; - fcoe_stats->tx_words += stats->TxWords; - fcoe_stats->rx_frames += stats->RxFrames; - fcoe_stats->rx_words += stats->RxWords; - fcoe_stats->error_frames += stats->ErrorFrames; - fcoe_stats->invalid_crc_count += stats->InvalidCRCCount; - fcoe_stats->fcp_input_requests += stats->InputRequests; - fcoe_stats->fcp_output_requests += stats->OutputRequests; - fcoe_stats->fcp_control_requests += stats->ControlRequests; + struct fc_stats *stats; + + stats = per_cpu_ptr(lport->stats, cpu); + + fc_stats->tx_frames += stats->TxFrames; + fc_stats->tx_words += stats->TxWords; + fc_stats->rx_frames += stats->RxFrames; + fc_stats->rx_words += stats->RxWords; + fc_stats->error_frames += stats->ErrorFrames; + fc_stats->invalid_crc_count += stats->InvalidCRCCount; + fc_stats->fcp_input_requests += stats->InputRequests; + fc_stats->fcp_output_requests += stats->OutputRequests; + fc_stats->fcp_control_requests += stats->ControlRequests; fcp_in_bytes += stats->InputBytes; fcp_out_bytes += stats->OutputBytes; - fcoe_stats->link_failure_count += stats->LinkFailureCount; + fc_stats->fcp_packet_alloc_failures += stats->FcpPktAllocFails; + fc_stats->fcp_packet_aborts += stats->FcpPktAborts; + fc_stats->fcp_frame_alloc_failures += stats->FcpFrameAllocFails; + fc_stats->link_failure_count += stats->LinkFailureCount; } - fcoe_stats->fcp_input_megabytes = div_u64(fcp_in_bytes, 1000000); - fcoe_stats->fcp_output_megabytes = div_u64(fcp_out_bytes, 1000000); - fcoe_stats->lip_count = -1; - fcoe_stats->nos_count = -1; - fcoe_stats->loss_of_sync_count = -1; - fcoe_stats->loss_of_signal_count = -1; - fcoe_stats->prim_seq_protocol_err_count = -1; - fcoe_stats->dumped_frames = -1; - return fcoe_stats; + fc_stats->fcp_input_megabytes = div_u64(fcp_in_bytes, 1000000); + fc_stats->fcp_output_megabytes = div_u64(fcp_out_bytes, 1000000); + fc_stats->lip_count = -1; + fc_stats->nos_count = -1; + fc_stats->loss_of_sync_count = -1; + fc_stats->loss_of_signal_count = -1; + fc_stats->prim_seq_protocol_err_count = -1; + fc_stats->dumped_frames = -1; + + /* update exches stats */ + fc_exch_update_stats(lport); + + return fc_stats; } EXPORT_SYMBOL(fc_get_host_stats); @@ -496,7 +516,7 @@ static void fc_lport_recv_rnid_req(struct fc_lport *lport, * @lport: The local port receiving the LOGO * @fp: The LOGO request frame * - * Locking Note: The lport lock is exected to be held before calling + * Locking Note: The lport lock is expected to be held before calling * this function. */ static void fc_lport_recv_logo_req(struct fc_lport *lport, struct fc_frame *fp) @@ -635,6 +655,7 @@ int fc_lport_destroy(struct fc_lport *lport) lport->tt.fcp_abort_io(lport); lport->tt.disc_stop_final(lport); lport->tt.exch_mgr_reset(lport, 0, 0); + cancel_delayed_work_sync(&lport->retry_work); fc_fc4_del_lport(lport); return 0; } @@ -959,7 +980,8 @@ drop: rcu_read_unlock(); FC_LPORT_DBG(lport, "dropping unexpected frame type %x\n", fh->fh_type); fc_frame_free(fp); - lport->tt.exch_done(sp); + if (sp) + lport->tt.exch_done(sp); } /** @@ -1066,7 +1088,7 @@ static void fc_lport_error(struct fc_lport *lport, struct fc_frame *fp) { unsigned long delay = 0; FC_LPORT_DBG(lport, "Error %ld in state %s, retries %d\n", - PTR_ERR(fp), fc_lport_state(lport), + IS_ERR(fp) ? -PTR_ERR(fp) : 0, fc_lport_state(lport), lport->retry_count); if (PTR_ERR(fp) == -FC_EX_CLOSED) @@ -1148,7 +1170,10 @@ static void fc_lport_ns_resp(struct fc_seq *sp, struct fc_frame *fp, fc_lport_enter_ns(lport, LPORT_ST_RFF_ID); break; case LPORT_ST_RFF_ID: - fc_lport_enter_scr(lport); + if (lport->fdmi_enabled) + fc_lport_enter_fdmi(lport); + else + fc_lport_enter_scr(lport); break; default: /* should have already been caught by state checks */ @@ -1163,6 +1188,85 @@ err: } /** + * fc_lport_ms_resp() - Handle response to a management server + * exchange + * @sp: current sequence in exchange + * @fp: response frame + * @lp_arg: Fibre Channel host port instance + * + * Locking Note: This function will be called without the lport lock + * held, but it will lock, call an _enter_* function or fc_lport_error() + * and then unlock the lport. + */ +static void fc_lport_ms_resp(struct fc_seq *sp, struct fc_frame *fp, + void *lp_arg) +{ + struct fc_lport *lport = lp_arg; + struct fc_frame_header *fh; + struct fc_ct_hdr *ct; + + FC_LPORT_DBG(lport, "Received a ms %s\n", fc_els_resp_type(fp)); + + if (fp == ERR_PTR(-FC_EX_CLOSED)) + return; + + mutex_lock(&lport->lp_mutex); + + if (lport->state < LPORT_ST_RHBA || lport->state > LPORT_ST_DPRT) { + FC_LPORT_DBG(lport, "Received a management server 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 && + ct->ct_fs_type == FC_FST_MGMT && + ct->ct_fs_subtype == FC_FDMI_SUBTYPE) { + FC_LPORT_DBG(lport, "Received a management server response, " + "reason=%d explain=%d\n", + ct->ct_reason, + ct->ct_explan); + + switch (lport->state) { + case LPORT_ST_RHBA: + if (ntohs(ct->ct_cmd) == FC_FS_ACC) + fc_lport_enter_ms(lport, LPORT_ST_RPA); + else /* Error Skip RPA */ + fc_lport_enter_scr(lport); + break; + case LPORT_ST_RPA: + fc_lport_enter_scr(lport); + break; + case LPORT_ST_DPRT: + fc_lport_enter_ms(lport, LPORT_ST_RHBA); + break; + case LPORT_ST_DHBA: + fc_lport_enter_ms(lport, LPORT_ST_DPRT); + break; + default: + /* should have already been caught by state checks */ + break; + } + } else { + /* Invalid Frame? */ + fc_lport_error(lport, fp); + } +out: + fc_frame_free(fp); +err: + mutex_unlock(&lport->lp_mutex); +} + +/** * fc_lport_scr_resp() - Handle response to State Change Register (SCR) request * @sp: current sequence in SCR exchange * @fp: response frame @@ -1339,6 +1443,123 @@ err: } /** + * fc_lport_enter_ms() - management server commands + * @lport: Fibre Channel local port to register + * + * Locking Note: The lport lock is expected to be held before calling + * this routine. + */ +static void fc_lport_enter_ms(struct fc_lport *lport, enum fc_lport_state state) +{ + struct fc_frame *fp; + enum fc_fdmi_req cmd; + int size = sizeof(struct fc_ct_hdr); + size_t len; + int numattrs; + + FC_LPORT_DBG(lport, "Entered %s state from %s state\n", + fc_lport_state_names[state], + fc_lport_state(lport)); + + fc_lport_state_enter(lport, state); + + switch (state) { + case LPORT_ST_RHBA: + cmd = FC_FDMI_RHBA; + /* Number of HBA Attributes */ + numattrs = 10; + len = sizeof(struct fc_fdmi_rhba); + len -= sizeof(struct fc_fdmi_attr_entry); + len += (numattrs * FC_FDMI_ATTR_ENTRY_HEADER_LEN); + len += FC_FDMI_HBA_ATTR_NODENAME_LEN; + len += FC_FDMI_HBA_ATTR_MANUFACTURER_LEN; + len += FC_FDMI_HBA_ATTR_SERIALNUMBER_LEN; + len += FC_FDMI_HBA_ATTR_MODEL_LEN; + len += FC_FDMI_HBA_ATTR_MODELDESCR_LEN; + len += FC_FDMI_HBA_ATTR_HARDWAREVERSION_LEN; + len += FC_FDMI_HBA_ATTR_DRIVERVERSION_LEN; + len += FC_FDMI_HBA_ATTR_OPTIONROMVERSION_LEN; + len += FC_FDMI_HBA_ATTR_FIRMWAREVERSION_LEN; + len += FC_FDMI_HBA_ATTR_OSNAMEVERSION_LEN; + + size += len; + break; + case LPORT_ST_RPA: + cmd = FC_FDMI_RPA; + /* Number of Port Attributes */ + numattrs = 6; + len = sizeof(struct fc_fdmi_rpa); + len -= sizeof(struct fc_fdmi_attr_entry); + len += (numattrs * FC_FDMI_ATTR_ENTRY_HEADER_LEN); + len += FC_FDMI_PORT_ATTR_FC4TYPES_LEN; + len += FC_FDMI_PORT_ATTR_SUPPORTEDSPEED_LEN; + len += FC_FDMI_PORT_ATTR_CURRENTPORTSPEED_LEN; + len += FC_FDMI_PORT_ATTR_MAXFRAMESIZE_LEN; + len += FC_FDMI_PORT_ATTR_OSDEVICENAME_LEN; + len += FC_FDMI_PORT_ATTR_HOSTNAME_LEN; + + size += len; + break; + case LPORT_ST_DPRT: + cmd = FC_FDMI_DPRT; + len = sizeof(struct fc_fdmi_dprt); + size += len; + break; + case LPORT_ST_DHBA: + cmd = FC_FDMI_DHBA; + len = sizeof(struct fc_fdmi_dhba); + size += len; + break; + default: + fc_lport_error(lport, NULL); + return; + } + + FC_LPORT_DBG(lport, "Cmd=0x%x Len %d size %d\n", + cmd, (int)len, size); + fp = fc_frame_alloc(lport, size); + if (!fp) { + fc_lport_error(lport, fp); + return; + } + + if (!lport->tt.elsct_send(lport, FC_FID_MGMT_SERV, fp, cmd, + fc_lport_ms_resp, + lport, 3 * lport->r_a_tov)) + fc_lport_error(lport, fp); +} + +/** + * fc_rport_enter_fdmi() - Create a fc_rport for the management server + * @lport: The local port requesting a remote port for the management server + * + * Locking Note: The lport lock is expected to be held before calling + * this routine. + */ +static void fc_lport_enter_fdmi(struct fc_lport *lport) +{ + struct fc_rport_priv *rdata; + + FC_LPORT_DBG(lport, "Entered FDMI state from %s state\n", + fc_lport_state(lport)); + + fc_lport_state_enter(lport, LPORT_ST_FDMI); + + mutex_lock(&lport->disc.disc_mutex); + rdata = lport->tt.rport_create(lport, FC_FID_MGMT_SERV); + mutex_unlock(&lport->disc.disc_mutex); + if (!rdata) + goto err; + + rdata->ops = &fc_lport_rport_ops; + lport->tt.rport_login(rdata); + return; + +err: + fc_lport_error(lport, NULL); +} + +/** * fc_lport_timeout() - Handler for the retry_work timer * @work: The work struct of the local port */ @@ -1352,7 +1573,6 @@ static void fc_lport_timeout(struct work_struct *work) switch (lport->state) { case LPORT_ST_DISABLED: - WARN_ON(1); break; case LPORT_ST_READY: break; @@ -1371,6 +1591,16 @@ static void fc_lport_timeout(struct work_struct *work) case LPORT_ST_RFF_ID: fc_lport_enter_ns(lport, lport->state); break; + case LPORT_ST_FDMI: + fc_lport_enter_fdmi(lport); + break; + case LPORT_ST_RHBA: + case LPORT_ST_RPA: + case LPORT_ST_DHBA: + case LPORT_ST_DPRT: + FC_LPORT_DBG(lport, "Skipping lport state %s to SCR\n", + fc_lport_state(lport)); + /* fall thru */ case LPORT_ST_SCR: fc_lport_enter_scr(lport); break; @@ -1521,9 +1751,19 @@ void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp, mfs = ntohs(flp->fl_csp.sp_bb_data) & FC_SP_BB_DATA_MASK; - if (mfs >= FC_SP_MIN_MAX_PAYLOAD && - mfs < lport->mfs) + + if (mfs < FC_SP_MIN_MAX_PAYLOAD || mfs > FC_SP_MAX_MAX_PAYLOAD) { + FC_LPORT_DBG(lport, "FLOGI bad mfs:%hu response, " + "lport->mfs:%hu\n", mfs, lport->mfs); + fc_lport_error(lport, fp); + goto err; + } + + if (mfs <= lport->mfs) { lport->mfs = mfs; + fc_host_maxframe_size(lport->host) = mfs; + } + csp_flags = ntohs(flp->fl_csp.sp_features); r_a_tov = ntohl(flp->fl_csp.sp_r_a_tov); e_d_tov = ntohl(flp->fl_csp.sp_e_d_tov); @@ -1698,7 +1938,7 @@ static void fc_lport_bsg_resp(struct fc_seq *sp, struct fc_frame *fp, job->reply->reply_payload_rcv_len += fc_copy_buffer_to_sglist(buf, len, info->sg, &info->nents, - &info->offset, KM_BIO_SRC_IRQ, NULL); + &info->offset, NULL); if (fr_eof(fp) == FC_EOF_T && (ntoh24(fh->fh_f_ctl) & (FC_FC_LAST_SEQ | FC_FC_END_SEQ)) == |
