diff options
Diffstat (limited to 'drivers/scsi/libfc/fc_lport.c')
| -rw-r--r-- | drivers/scsi/libfc/fc_lport.c | 504 | 
1 files changed, 407 insertions, 97 deletions
diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c index 9be63edbf8f..e01a29863c3 100644 --- a/drivers/scsi/libfc/fc_lport.c +++ b/drivers/scsi/libfc/fc_lport.c @@ -52,7 +52,7 @@   * while making the callback. To ensure that the rport is not free'd while   * processing the callback the rport callbacks are serialized through a   * single-threaded workqueue. An rport would never be free'd while in a - * callback handler becuase no other rport work in this queue can be executed + * callback handler because no other rport work in this queue can be executed   * at the same time.   *   * When discovery succeeds or fails a callback is made to the lport as @@ -88,6 +88,8 @@   */  #include <linux/timer.h> +#include <linux/delay.h> +#include <linux/module.h>  #include <linux/slab.h>  #include <asm/unaligned.h> @@ -114,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", @@ -124,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", @@ -163,7 +172,7 @@ static int fc_frame_drop(struct fc_lport *lport, struct fc_frame *fp)   * fc_lport_rport_callback() - Event handler for rport events   * @lport: The lport which is receiving the event   * @rdata: private remote port data - * @event: The event that occured + * @event: The event that occurred   *   * Locking Note: The rport lock should not be held when calling   *		 this function. @@ -181,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); @@ -194,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; @@ -284,43 +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; -		fcoe_stats->fcp_input_megabytes += stats->InputMegabytes; -		fcoe_stats->fcp_output_megabytes += stats->OutputMegabytes; -		fcoe_stats->link_failure_count += stats->LinkFailureCount; +		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; +		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->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); @@ -375,7 +401,7 @@ static void fc_lport_add_fc4_type(struct fc_lport *lport, enum fc_fh_type type)  /**   * fc_lport_recv_rlir_req() - Handle received Registered Link Incident Report. - * @lport: Fibre Channel local port recieving the RLIR + * @lport: Fibre Channel local port receiving the RLIR   * @fp:	   The RLIR request frame   *   * Locking Note: The lport lock is expected to be held before calling @@ -392,7 +418,7 @@ static void fc_lport_recv_rlir_req(struct fc_lport *lport, struct fc_frame *fp)  /**   * fc_lport_recv_echo_req() - Handle received ECHO request - * @lport: The local port recieving the ECHO + * @lport: The local port receiving the ECHO   * @fp:	   ECHO request frame   *   * Locking Note: The lport lock is expected to be held before calling @@ -428,7 +454,7 @@ static void fc_lport_recv_echo_req(struct fc_lport *lport,  /**   * fc_lport_recv_rnid_req() - Handle received Request Node ID data request - * @lport: The local port recieving the RNID + * @lport: The local port receiving the RNID   * @fp:	   The RNID request frame   *   * Locking Note: The lport lock is expected to be held before calling @@ -487,10 +513,10 @@ static void fc_lport_recv_rnid_req(struct fc_lport *lport,  /**   * fc_lport_recv_logo_req() - Handle received fabric LOGO request - * @lport: The local port recieving the LOGO + * @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) @@ -629,6 +655,8 @@ 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;  }  EXPORT_SYMBOL(fc_lport_destroy); @@ -670,7 +698,8 @@ EXPORT_SYMBOL(fc_set_mfs);   * @lport: The local port receiving the event   * @event: The discovery event   */ -void fc_lport_disc_callback(struct fc_lport *lport, enum fc_disc_event event) +static void fc_lport_disc_callback(struct fc_lport *lport, +				   enum fc_disc_event event)  {  	switch (event) {  	case DISC_EV_SUCCESS: @@ -766,7 +795,7 @@ EXPORT_SYMBOL(fc_lport_set_local_id);  /**   * fc_lport_recv_flogi_req() - Receive a FLOGI request - * @lport: The local port that recieved the request + * @lport: The local port that received the request   * @rx_fp: The FLOGI frame   *   * A received FLOGI request indicates a point-to-point connection. @@ -845,7 +874,7 @@ out:  }  /** - * fc_lport_recv_req() - The generic lport request handler + * fc_lport_recv_els_req() - The generic lport ELS request handler   * @lport: The local port that received the request   * @fp:	   The request frame   * @@ -853,11 +882,11 @@ out:   * if an rport should handle the request.   *   * Locking Note: This function should not be called with the lport - *		 lock held becuase it will grab the lock. + *		 lock held because it will grab the lock.   */ -static void fc_lport_recv_req(struct fc_lport *lport, struct fc_frame *fp) +static void fc_lport_recv_els_req(struct fc_lport *lport, +				  struct fc_frame *fp)  { -	struct fc_frame_header *fh = fc_frame_header_get(fp);  	void (*recv)(struct fc_lport *, struct fc_frame *);  	mutex_lock(&lport->lp_mutex); @@ -869,8 +898,7 @@ static void fc_lport_recv_req(struct fc_lport *lport, struct fc_frame *fp)  	 */  	if (!lport->link_up)  		fc_frame_free(fp); -	else if (fh->fh_type == FC_TYPE_ELS && -		 fh->fh_r_ctl == FC_RCTL_ELS_REQ) { +	else {  		/*  		 * Check opcode.  		 */ @@ -899,14 +927,63 @@ static void fc_lport_recv_req(struct fc_lport *lport, struct fc_frame *fp)  		}  		recv(lport, fp); -	} else { -		FC_LPORT_DBG(lport, "dropping invalid frame (eof %x)\n", -			     fr_eof(fp)); -		fc_frame_free(fp);  	}  	mutex_unlock(&lport->lp_mutex);  } +static int fc_lport_els_prli(struct fc_rport_priv *rdata, u32 spp_len, +			     const struct fc_els_spp *spp_in, +			     struct fc_els_spp *spp_out) +{ +	return FC_SPP_RESP_INVL; +} + +struct fc4_prov fc_lport_els_prov = { +	.prli = fc_lport_els_prli, +	.recv = fc_lport_recv_els_req, +}; + +/** + * fc_lport_recv_req() - The generic lport request handler + * @lport: The lport that received the request + * @fp: The frame the request is in + * + * Locking Note: This function should not be called with the lport + *		 lock held because it may grab the lock. + */ +static void fc_lport_recv_req(struct fc_lport *lport, +			      struct fc_frame *fp) +{ +	struct fc_frame_header *fh = fc_frame_header_get(fp); +	struct fc_seq *sp = fr_seq(fp); +	struct fc4_prov *prov; + +	/* +	 * Use RCU read lock and module_lock to be sure module doesn't +	 * deregister and get unloaded while we're calling it. +	 * try_module_get() is inlined and accepts a NULL parameter. +	 * Only ELSes and FCP target ops should come through here. +	 * The locking is unfortunate, and a better scheme is being sought. +	 */ + +	rcu_read_lock(); +	if (fh->fh_type >= FC_FC4_PROV_SIZE) +		goto drop; +	prov = rcu_dereference(fc_passive_prov[fh->fh_type]); +	if (!prov || !try_module_get(prov->module)) +		goto drop; +	rcu_read_unlock(); +	prov->recv(lport, fp); +	module_put(prov->module); +	return; +drop: +	rcu_read_unlock(); +	FC_LPORT_DBG(lport, "dropping unexpected frame type %x\n", fh->fh_type); +	fc_frame_free(fp); +	if (sp) +		lport->tt.exch_done(sp); +} +  /**   * fc_lport_reset() - Reset a local port   * @lport: The local port which should be reset @@ -973,6 +1050,8 @@ static void fc_lport_enter_reset(struct fc_lport *lport)  			fc_vport_set_state(lport->vport, FC_VPORT_LINKDOWN);  	}  	fc_lport_state_enter(lport, LPORT_ST_RESET); +	fc_host_post_event(lport->host, fc_get_event_number(), +			   FCH_EVT_LIPRESET, 0);  	fc_vports_linkchange(lport);  	fc_lport_reset_locked(lport);  	if (lport->link_up) @@ -1009,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) @@ -1091,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 */ @@ -1106,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 @@ -1282,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   */ @@ -1295,10 +1573,8 @@ static void fc_lport_timeout(struct work_struct *work)  	switch (lport->state) {  	case LPORT_ST_DISABLED: -		WARN_ON(1);  		break;  	case LPORT_ST_READY: -		WARN_ON(1);  		break;  	case LPORT_ST_RESET:  		break; @@ -1315,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; @@ -1419,6 +1705,7 @@ void fc_lport_flogi_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_els_flogi *flp;  	u32 did;  	u16 csp_flags; @@ -1446,49 +1733,66 @@ void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp,  		goto err;  	} +	fh = fc_frame_header_get(fp);  	did = fc_frame_did(fp); -	if (fc_frame_payload_op(fp) == ELS_LS_ACC && did) { -		flp = fc_frame_payload_get(fp, sizeof(*flp)); -		if (flp) { -			mfs = ntohs(flp->fl_csp.sp_bb_data) & -				FC_SP_BB_DATA_MASK; -			if (mfs >= FC_SP_MIN_MAX_PAYLOAD && -			    mfs < lport->mfs) -				lport->mfs = 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); -			if (csp_flags & FC_SP_FT_EDTR) -				e_d_tov /= 1000000; - -			lport->npiv_enabled = !!(csp_flags & FC_SP_FT_NPIV_ACC); - -			if ((csp_flags & FC_SP_FT_FPORT) == 0) { -				if (e_d_tov > lport->e_d_tov) -					lport->e_d_tov = e_d_tov; -				lport->r_a_tov = 2 * e_d_tov; -				fc_lport_set_port_id(lport, did, fp); -				printk(KERN_INFO "host%d: libfc: " -				       "Port (%6.6x) entered " -				       "point-to-point mode\n", -				       lport->host->host_no, did); -				fc_lport_ptp_setup(lport, fc_frame_sid(fp), -						   get_unaligned_be64( -							   &flp->fl_wwpn), -						   get_unaligned_be64( -							   &flp->fl_wwnn)); -			} else { -				lport->e_d_tov = e_d_tov; -				lport->r_a_tov = r_a_tov; -				fc_host_fabric_name(lport->host) = -					get_unaligned_be64(&flp->fl_wwnn); -				fc_lport_set_port_id(lport, did, fp); -				fc_lport_enter_dns(lport); -			} -		} -	} else { -		FC_LPORT_DBG(lport, "FLOGI RJT or bad response\n"); +	if (fh->fh_r_ctl != FC_RCTL_ELS_REP || did == 0 || +	    fc_frame_payload_op(fp) != ELS_LS_ACC) { +		FC_LPORT_DBG(lport, "FLOGI not accepted or bad response\n");  		fc_lport_error(lport, fp); +		goto err; +	} + +	flp = fc_frame_payload_get(fp, sizeof(*flp)); +	if (!flp) { +		FC_LPORT_DBG(lport, "FLOGI bad response\n"); +		fc_lport_error(lport, fp); +		goto err; +	} + +	mfs = ntohs(flp->fl_csp.sp_bb_data) & +		FC_SP_BB_DATA_MASK; + +	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); +	if (csp_flags & FC_SP_FT_EDTR) +		e_d_tov /= 1000000; + +	lport->npiv_enabled = !!(csp_flags & FC_SP_FT_NPIV_ACC); + +	if ((csp_flags & FC_SP_FT_FPORT) == 0) { +		if (e_d_tov > lport->e_d_tov) +			lport->e_d_tov = e_d_tov; +		lport->r_a_tov = 2 * e_d_tov; +		fc_lport_set_port_id(lport, did, fp); +		printk(KERN_INFO "host%d: libfc: " +		       "Port (%6.6x) entered " +		       "point-to-point mode\n", +		       lport->host->host_no, did); +		fc_lport_ptp_setup(lport, fc_frame_sid(fp), +				   get_unaligned_be64( +					   &flp->fl_wwpn), +				   get_unaligned_be64( +					   &flp->fl_wwnn)); +	} else { +		lport->e_d_tov = e_d_tov; +		lport->r_a_tov = r_a_tov; +		fc_host_fabric_name(lport->host) = +			get_unaligned_be64(&flp->fl_wwnn); +		fc_lport_set_port_id(lport, did, fp); +		fc_lport_enter_dns(lport);  	}  out: @@ -1505,7 +1809,7 @@ EXPORT_SYMBOL(fc_lport_flogi_resp);   * Locking Note: The lport lock is expected to be held before calling   * this routine.   */ -void fc_lport_enter_flogi(struct fc_lport *lport) +static void fc_lport_enter_flogi(struct fc_lport *lport)  {  	struct fc_frame *fp; @@ -1545,6 +1849,7 @@ int fc_lport_config(struct fc_lport *lport)  	fc_lport_add_fc4_type(lport, FC_TYPE_FCP);  	fc_lport_add_fc4_type(lport, FC_TYPE_CT); +	fc_fc4_conf_lport_params(lport, FC_TYPE_FCP);  	return 0;  } @@ -1582,6 +1887,7 @@ int fc_lport_init(struct fc_lport *lport)  		fc_host_supported_speeds(lport->host) |= FC_PORTSPEED_1GBIT;  	if (lport->link_supported_speeds & FC_PORTSPEED_10GBIT)  		fc_host_supported_speeds(lport->host) |= FC_PORTSPEED_10GBIT; +	fc_fc4_add_lport(lport);  	return 0;  } @@ -1632,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)) == @@ -1703,8 +2009,10 @@ static int fc_lport_els_request(struct fc_bsg_job *job,  	info->sg = job->reply_payload.sg_list;  	if (!lport->tt.exch_seq_send(lport, fp, fc_lport_bsg_resp, -				     NULL, info, tov)) +				     NULL, info, tov)) { +		kfree(info);  		return -ECOMM; +	}  	return 0;  } @@ -1762,8 +2070,10 @@ static int fc_lport_ct_request(struct fc_bsg_job *job,  	info->sg = job->reply_payload.sg_list;  	if (!lport->tt.exch_seq_send(lport, fp, fc_lport_bsg_resp, -				     NULL, info, tov)) +				     NULL, info, tov)) { +		kfree(info);  		return -ECOMM; +	}  	return 0;  }  | 
