diff options
Diffstat (limited to 'drivers/scsi/fcoe/fcoe.c')
| -rw-r--r-- | drivers/scsi/fcoe/fcoe.c | 194 | 
1 files changed, 128 insertions, 66 deletions
diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index f01b9b44e8a..ba75a98c960 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -74,6 +74,7 @@ static int fcoe_rcv(struct sk_buff *, struct net_device *,  static int fcoe_percpu_receive_thread(void *);  static void fcoe_clean_pending_queue(struct fc_lport *);  static void fcoe_percpu_clean(struct fc_lport *); +static int fcoe_link_speed_update(struct fc_lport *);  static int fcoe_link_ok(struct fc_lport *);  static struct fc_lport *fcoe_hostlist_lookup(const struct net_device *); @@ -146,6 +147,7 @@ static int fcoe_vport_destroy(struct fc_vport *);  static int fcoe_vport_create(struct fc_vport *, bool disabled);  static int fcoe_vport_disable(struct fc_vport *, bool disable);  static void fcoe_set_vport_symbolic_name(struct fc_vport *); +static void fcoe_set_port_id(struct fc_lport *, u32, struct fc_frame *);  static struct libfc_function_template fcoe_libfc_fcn_templ = {  	.frame_send = fcoe_xmit, @@ -153,6 +155,7 @@ static struct libfc_function_template fcoe_libfc_fcn_templ = {  	.ddp_done = fcoe_ddp_done,  	.elsct_send = fcoe_elsct_send,  	.get_lesb = fcoe_get_lesb, +	.lport_set_port_id = fcoe_set_port_id,  };  struct fc_function_template fcoe_transport_function = { @@ -629,6 +632,8 @@ static int fcoe_netdev_config(struct fc_lport *lport, struct net_device *netdev)  	port->fcoe_pending_queue_active = 0;  	setup_timer(&port->timer, fcoe_queue_timer, (unsigned long)lport); +	fcoe_link_speed_update(lport); +  	if (!lport->vport) {  		/*  		 * Use NAA 1&2 (FC-FS Rev. 2.0, Sec. 15) to generate WWNN/WWPN: @@ -653,15 +658,13 @@ static int fcoe_netdev_config(struct fc_lport *lport, struct net_device *netdev)  /**   * fcoe_shost_config() - Set up the SCSI host associated with a local port   * @lport: The local port - * @shost: The SCSI host to associate with the local port   * @dev:   The device associated with the SCSI host   *   * Must be called after fcoe_lport_config() and fcoe_netdev_config()   *   * Returns: 0 for success   */ -static int fcoe_shost_config(struct fc_lport *lport, struct Scsi_Host *shost, -			     struct device *dev) +static int fcoe_shost_config(struct fc_lport *lport, struct device *dev)  {  	int rc = 0; @@ -669,6 +672,8 @@ static int fcoe_shost_config(struct fc_lport *lport, struct Scsi_Host *shost,  	lport->host->max_lun = FCOE_MAX_LUN;  	lport->host->max_id = FCOE_MAX_FCP_TARGET;  	lport->host->max_channel = 0; +	lport->host->max_cmd_len = FCOE_MAX_CMD_LEN; +  	if (lport->vport)  		lport->host->transportt = fcoe_vport_transport_template;  	else @@ -796,6 +801,12 @@ skip_oem:  /**   * fcoe_if_destroy() - Tear down a SW FCoE instance   * @lport: The local port to be destroyed + * + * Locking: must be called with the RTNL mutex held and RTNL mutex + * needed to be dropped by this function since not dropping RTNL + * would cause circular locking warning on synchronous fip worker + * cancelling thru fcoe_interface_put invoked by this function. + *   */  static void fcoe_if_destroy(struct fc_lport *lport)  { @@ -818,7 +829,6 @@ static void fcoe_if_destroy(struct fc_lport *lport)  	/* Free existing transmit skbs */  	fcoe_clean_pending_queue(lport); -	rtnl_lock();  	if (!is_zero_ether_addr(port->data_src_addr))  		dev_unicast_delete(netdev, port->data_src_addr);  	rtnl_unlock(); @@ -841,6 +851,7 @@ static void fcoe_if_destroy(struct fc_lport *lport)  	/* Release the Scsi_Host */  	scsi_host_put(lport->host); +	module_put(THIS_MODULE);  }  /** @@ -897,7 +908,6 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe,  	struct net_device *netdev = fcoe->netdev;  	struct fc_lport *lport = NULL;  	struct fcoe_port *port; -	struct Scsi_Host *shost;  	int rc;  	/*  	 * parent is only a vport if npiv is 1, @@ -919,7 +929,6 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe,  		rc = -ENOMEM;  		goto out;  	} -	shost = lport->host;  	port = lport_priv(lport);  	port->lport = lport;  	port->fcoe = fcoe; @@ -934,7 +943,8 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe,  	}  	if (npiv) { -		FCOE_NETDEV_DBG(netdev, "Setting vport names, 0x%llX 0x%llX\n", +		FCOE_NETDEV_DBG(netdev, "Setting vport names, " +				"%16.16llx %16.16llx\n",  				vport->node_name, vport->port_name);  		fc_set_wwnn(lport, vport->node_name);  		fc_set_wwpn(lport, vport->port_name); @@ -949,7 +959,7 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe,  	}  	/* configure lport scsi host properties */ -	rc = fcoe_shost_config(lport, shost, parent); +	rc = fcoe_shost_config(lport, parent);  	if (rc) {  		FCOE_NETDEV_DBG(netdev, "Could not configure shost for the "  				"interface\n"); @@ -1073,7 +1083,7 @@ static void fcoe_percpu_thread_destroy(unsigned int cpu)  	struct sk_buff *skb;  #ifdef CONFIG_SMP  	struct fcoe_percpu_s *p0; -	unsigned targ_cpu = smp_processor_id(); +	unsigned targ_cpu = get_cpu();  #endif /* CONFIG_SMP */  	FCOE_DBG("Destroying receive thread for CPU %d\n", cpu); @@ -1129,6 +1139,7 @@ static void fcoe_percpu_thread_destroy(unsigned int cpu)  			kfree_skb(skb);  		spin_unlock_bh(&p->fcoe_rx_list.lock);  	} +	put_cpu();  #else  	/*  	 * This a non-SMP scenario where the singular Rx thread is @@ -1297,8 +1308,8 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *netdev,  	return 0;  err: -	fc_lport_get_stats(lport)->ErrorFrames++; - +	per_cpu_ptr(lport->dev_stats, get_cpu())->ErrorFrames++; +	put_cpu();  err2:  	kfree_skb(skb);  	return -1; @@ -1444,7 +1455,7 @@ int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp)  		return 0;  	} -	if (unlikely(fh->fh_r_ctl == FC_RCTL_ELS_REQ) && +	if (unlikely(fh->fh_type == FC_TYPE_ELS) &&  	    fcoe_ctlr_els_send(&fcoe->ctlr, lport, skb))  		return 0; @@ -1527,9 +1538,10 @@ int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp)  		skb_shinfo(skb)->gso_size = 0;  	}  	/* update tx stats: regardless if LLD fails */ -	stats = fc_lport_get_stats(lport); +	stats = per_cpu_ptr(lport->dev_stats, get_cpu());  	stats->TxFrames++;  	stats->TxWords += wlen; +	put_cpu();  	/* send down to lld */  	fr_dev(fp) = lport; @@ -1563,7 +1575,6 @@ static void fcoe_recv_frame(struct sk_buff *skb)  	struct fc_frame_header *fh;  	struct fcoe_crc_eof crc_eof;  	struct fc_frame *fp; -	u8 *mac = NULL;  	struct fcoe_port *port;  	struct fcoe_hdr *hp; @@ -1583,13 +1594,9 @@ static void fcoe_recv_frame(struct sk_buff *skb)  			skb_end_pointer(skb), skb->csum,  			skb->dev ? skb->dev->name : "<NULL>"); -	/* -	 * Save source MAC address before discarding header. -	 */  	port = lport_priv(lport);  	if (skb_is_nonlinear(skb))  		skb_linearize(skb);	/* not ideal */ -	mac = eth_hdr(skb)->h_source;  	/*  	 * Frame length checks and setting up the header pointers @@ -1598,7 +1605,7 @@ static void fcoe_recv_frame(struct sk_buff *skb)  	hp = (struct fcoe_hdr *) skb_network_header(skb);  	fh = (struct fc_frame_header *) skb_transport_header(skb); -	stats = fc_lport_get_stats(lport); +	stats = per_cpu_ptr(lport->dev_stats, get_cpu());  	if (unlikely(FC_FCOE_DECAPS_VER(hp) != FC_FCOE_VER)) {  		if (stats->ErrorFrames < 5)  			printk(KERN_WARNING "fcoe: FCoE version " @@ -1607,9 +1614,7 @@ static void fcoe_recv_frame(struct sk_buff *skb)  			       "initiator supports version "  			       "%x\n", FC_FCOE_DECAPS_VER(hp),  			       FC_FCOE_VER); -		stats->ErrorFrames++; -		kfree_skb(skb); -		return; +		goto drop;  	}  	skb_pull(skb, sizeof(struct fcoe_hdr)); @@ -1624,16 +1629,12 @@ static void fcoe_recv_frame(struct sk_buff *skb)  	fr_sof(fp) = hp->fcoe_sof;  	/* Copy out the CRC and EOF trailer for access */ -	if (skb_copy_bits(skb, fr_len, &crc_eof, sizeof(crc_eof))) { -		kfree_skb(skb); -		return; -	} +	if (skb_copy_bits(skb, fr_len, &crc_eof, sizeof(crc_eof))) +		goto drop;  	fr_eof(fp) = crc_eof.fcoe_eof;  	fr_crc(fp) = crc_eof.fcoe_crc32; -	if (pskb_trim(skb, fr_len)) { -		kfree_skb(skb); -		return; -	} +	if (pskb_trim(skb, fr_len)) +		goto drop;  	/*  	 * We only check CRC if no offload is available and if it is @@ -1647,25 +1648,27 @@ static void fcoe_recv_frame(struct sk_buff *skb)  		fr_flags(fp) |= FCPHF_CRC_UNCHECKED;  	fh = fc_frame_header_get(fp); -	if (fh->fh_r_ctl == FC_RCTL_DD_SOL_DATA && -	    fh->fh_type == FC_TYPE_FCP) { -		fc_exch_recv(lport, fp); -		return; -	} -	if (fr_flags(fp) & FCPHF_CRC_UNCHECKED) { +	if ((fh->fh_r_ctl != FC_RCTL_DD_SOL_DATA || +	    fh->fh_type != FC_TYPE_FCP) && +	    (fr_flags(fp) & FCPHF_CRC_UNCHECKED)) {  		if (le32_to_cpu(fr_crc(fp)) !=  		    ~crc32(~0, skb->data, fr_len)) {  			if (stats->InvalidCRCCount < 5)  				printk(KERN_WARNING "fcoe: dropping "  				       "frame with CRC error\n");  			stats->InvalidCRCCount++; -			stats->ErrorFrames++; -			fc_frame_free(fp); -			return; +			goto drop;  		}  		fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED;  	} +	put_cpu();  	fc_exch_recv(lport, fp); +	return; + +drop: +	stats->ErrorFrames++; +	put_cpu(); +	kfree_skb(skb);  }  /** @@ -1835,11 +1838,15 @@ static int fcoe_device_notification(struct notifier_block *notifier,  		FCOE_NETDEV_DBG(netdev, "Unknown event %ld "  				"from netdev netlink\n", event);  	} + +	fcoe_link_speed_update(lport); +  	if (link_possible && !fcoe_link_ok(lport))  		fcoe_ctlr_link_up(&fcoe->ctlr);  	else if (fcoe_ctlr_link_down(&fcoe->ctlr)) { -		stats = fc_lport_get_stats(lport); +		stats = per_cpu_ptr(lport->dev_stats, get_cpu());  		stats->LinkFailureCount++; +		put_cpu();  		fcoe_clean_pending_queue(lport);  	}  out: @@ -1901,13 +1908,19 @@ static int fcoe_disable(const char *buffer, struct kernel_param *kp)  		goto out_nodev;  	} -	rtnl_lock(); +	if (!rtnl_trylock()) { +		dev_put(netdev); +		mutex_unlock(&fcoe_config_mutex); +		return restart_syscall(); +	} +  	fcoe = fcoe_hostlist_lookup_port(netdev);  	rtnl_unlock(); -	if (fcoe) +	if (fcoe) {  		fc_fabric_logoff(fcoe->ctlr.lp); -	else +		fcoe_ctlr_link_down(&fcoe->ctlr); +	} else  		rc = -ENODEV;  	dev_put(netdev); @@ -1950,13 +1963,20 @@ static int fcoe_enable(const char *buffer, struct kernel_param *kp)  		goto out_nodev;  	} -	rtnl_lock(); +	if (!rtnl_trylock()) { +		dev_put(netdev); +		mutex_unlock(&fcoe_config_mutex); +		return restart_syscall(); +	} +  	fcoe = fcoe_hostlist_lookup_port(netdev);  	rtnl_unlock(); -	if (fcoe) +	if (fcoe) { +		if (!fcoe_link_ok(fcoe->ctlr.lp)) +			fcoe_ctlr_link_up(&fcoe->ctlr);  		rc = fc_fabric_login(fcoe->ctlr.lp); -	else +	} else  		rc = -ENODEV;  	dev_put(netdev); @@ -1999,7 +2019,12 @@ static int fcoe_destroy(const char *buffer, struct kernel_param *kp)  		goto out_nodev;  	} -	rtnl_lock(); +	if (!rtnl_trylock()) { +		dev_put(netdev); +		mutex_unlock(&fcoe_config_mutex); +		return restart_syscall(); +	} +  	fcoe = fcoe_hostlist_lookup_port(netdev);  	if (!fcoe) {  		rtnl_unlock(); @@ -2008,9 +2033,8 @@ static int fcoe_destroy(const char *buffer, struct kernel_param *kp)  	}  	list_del(&fcoe->list);  	fcoe_interface_cleanup(fcoe); -	rtnl_unlock(); +	/* RTNL mutex is dropped by fcoe_if_destroy */  	fcoe_if_destroy(fcoe->ctlr.lp); -	module_put(THIS_MODULE);  out_putdev:  	dev_put(netdev); @@ -2029,6 +2053,8 @@ static void fcoe_destroy_work(struct work_struct *work)  	port = container_of(work, struct fcoe_port, destroy_work);  	mutex_lock(&fcoe_config_mutex); +	rtnl_lock(); +	/* RTNL mutex is dropped by fcoe_if_destroy */  	fcoe_if_destroy(port->lport);  	mutex_unlock(&fcoe_config_mutex);  } @@ -2050,6 +2076,12 @@ static int fcoe_create(const char *buffer, struct kernel_param *kp)  	struct net_device *netdev;  	mutex_lock(&fcoe_config_mutex); + +	if (!rtnl_trylock()) { +		mutex_unlock(&fcoe_config_mutex); +		return restart_syscall(); +	} +  #ifdef CONFIG_FCOE_MODULE  	/*  	 * Make sure the module has been initialized, and is not about to be @@ -2058,7 +2090,7 @@ static int fcoe_create(const char *buffer, struct kernel_param *kp)  	 */  	if (THIS_MODULE->state != MODULE_STATE_LIVE) {  		rc = -ENODEV; -		goto out_nodev; +		goto out_nomod;  	}  #endif @@ -2067,7 +2099,6 @@ static int fcoe_create(const char *buffer, struct kernel_param *kp)  		goto out_nomod;  	} -	rtnl_lock();  	netdev = fcoe_if_to_netdev(buffer);  	if (!netdev) {  		rc = -ENODEV; @@ -2122,35 +2153,27 @@ out_free:  out_putdev:  	dev_put(netdev);  out_nodev: -	rtnl_unlock();  	module_put(THIS_MODULE);  out_nomod: +	rtnl_unlock();  	mutex_unlock(&fcoe_config_mutex);  	return rc;  }  /** - * fcoe_link_ok() - Check if the link is OK for a local port - * @lport: The local port to check link on - * - * Any permanently-disqualifying conditions have been previously checked. - * This also updates the speed setting, which may change with link for 100/1000. - * - * This function should probably be checking for PAUSE support at some point - * in the future. Currently Per-priority-pause is not determinable using - * ethtool, so we shouldn't be restrictive until that problem is resolved. - * - * Returns: 0 if link is OK for use by FCoE. + * fcoe_link_speed_update() - Update the supported and actual link speeds + * @lport: The local port to update speeds for   * + * Returns: 0 if the ethtool query was successful + *          -1 if the ethtool query failed   */ -int fcoe_link_ok(struct fc_lport *lport) +int fcoe_link_speed_update(struct fc_lport *lport)  {  	struct fcoe_port *port = lport_priv(lport);  	struct net_device *netdev = port->fcoe->netdev;  	struct ethtool_cmd ecmd = { ETHTOOL_GSET }; -	if ((netdev->flags & IFF_UP) && netif_carrier_ok(netdev) && -	    (!dev_ethtool_get_settings(netdev, &ecmd))) { +	if (!dev_ethtool_get_settings(netdev, &ecmd)) {  		lport->link_supported_speeds &=  			~(FC_PORTSPEED_1GBIT | FC_PORTSPEED_10GBIT);  		if (ecmd.supported & (SUPPORTED_1000baseT_Half | @@ -2170,6 +2193,23 @@ int fcoe_link_ok(struct fc_lport *lport)  }  /** + * fcoe_link_ok() - Check if the link is OK for a local port + * @lport: The local port to check link on + * + * Returns: 0 if link is UP and OK, -1 if not + * + */ +int fcoe_link_ok(struct fc_lport *lport) +{ +	struct fcoe_port *port = lport_priv(lport); +	struct net_device *netdev = port->fcoe->netdev; + +	if (netif_oper_up(netdev)) +		return 0; +	return -1; +} + +/**   * fcoe_percpu_clean() - Clear all pending skbs for an local port   * @lport: The local port whose skbs are to be cleared   * @@ -2631,3 +2671,25 @@ static void fcoe_get_lesb(struct fc_lport *lport,  	lesb->lesb_miss_fka = htonl(mdac);  	lesb->lesb_fcs_error = htonl(dev_get_stats(netdev)->rx_crc_errors);  } + +/** + * fcoe_set_port_id() - Callback from libfc when Port_ID is set. + * @lport: the local port + * @port_id: the port ID + * @fp: the received frame, if any, that caused the port_id to be set. + * + * This routine handles the case where we received a FLOGI and are + * entering point-to-point mode.  We need to call fcoe_ctlr_recv_flogi() + * so it can set the non-mapped mode and gateway address. + * + * The FLOGI LS_ACC is handled by fcoe_flogi_resp(). + */ +static void fcoe_set_port_id(struct fc_lport *lport, +			     u32 port_id, struct fc_frame *fp) +{ +	struct fcoe_port *port = lport_priv(lport); +	struct fcoe_interface *fcoe = port->fcoe; + +	if (fp && fc_frame_payload_op(fp) == ELS_FLOGI) +		fcoe_ctlr_recv_flogi(&fcoe->ctlr, lport, fp); +}  |