diff options
Diffstat (limited to 'drivers/scsi/fcoe')
-rw-r--r-- | drivers/scsi/fcoe/fcoe.c | 1376 | ||||
-rw-r--r-- | drivers/scsi/fcoe/fcoe.h | 82 | ||||
-rw-r--r-- | drivers/scsi/fcoe/libfcoe.c | 433 |
3 files changed, 1208 insertions, 683 deletions
diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index 704b8e03494..a30ffaa1222 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -66,14 +66,14 @@ LIST_HEAD(fcoe_hostlist); DEFINE_PER_CPU(struct fcoe_percpu_s, fcoe_percpu); /* Function Prototypes */ -static int fcoe_reset(struct Scsi_Host *shost); +static int fcoe_reset(struct Scsi_Host *); static int fcoe_xmit(struct fc_lport *, struct fc_frame *); static int fcoe_rcv(struct sk_buff *, struct net_device *, struct packet_type *, struct net_device *); -static int fcoe_percpu_receive_thread(void *arg); -static void fcoe_clean_pending_queue(struct fc_lport *lp); -static void fcoe_percpu_clean(struct fc_lport *lp); -static int fcoe_link_ok(struct fc_lport *lp); +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_ok(struct fc_lport *); static struct fc_lport *fcoe_hostlist_lookup(const struct net_device *); static int fcoe_hostlist_add(const struct fc_lport *); @@ -82,15 +82,69 @@ static void fcoe_check_wait_queue(struct fc_lport *, struct sk_buff *); static int fcoe_device_notification(struct notifier_block *, ulong, void *); static void fcoe_dev_setup(void); static void fcoe_dev_cleanup(void); -static struct fcoe_interface * - fcoe_hostlist_lookup_port(const struct net_device *dev); +static struct fcoe_interface +*fcoe_hostlist_lookup_port(const struct net_device *); + +static int fcoe_fip_recv(struct sk_buff *, struct net_device *, + struct packet_type *, struct net_device *); + +static void fcoe_fip_send(struct fcoe_ctlr *, struct sk_buff *); +static void fcoe_update_src_mac(struct fc_lport *, u8 *); +static u8 *fcoe_get_src_mac(struct fc_lport *); +static void fcoe_destroy_work(struct work_struct *); + +static int fcoe_ddp_setup(struct fc_lport *, u16, struct scatterlist *, + unsigned int); +static int fcoe_ddp_done(struct fc_lport *, u16); + +static int fcoe_cpu_callback(struct notifier_block *, unsigned long, void *); + +static int fcoe_create(const char *, struct kernel_param *); +static int fcoe_destroy(const char *, struct kernel_param *); + +static struct fc_seq *fcoe_elsct_send(struct fc_lport *, + u32 did, struct fc_frame *, + unsigned int op, + void (*resp)(struct fc_seq *, + struct fc_frame *, + void *), + void *, u32 timeout); +static void fcoe_recv_frame(struct sk_buff *skb); + +static void fcoe_get_lesb(struct fc_lport *, struct fc_els_lesb *); + +module_param_call(create, fcoe_create, NULL, NULL, S_IWUSR); +__MODULE_PARM_TYPE(create, "string"); +MODULE_PARM_DESC(create, "Create fcoe fcoe using net device passed in."); +module_param_call(destroy, fcoe_destroy, NULL, NULL, S_IWUSR); +__MODULE_PARM_TYPE(destroy, "string"); +MODULE_PARM_DESC(destroy, "Destroy fcoe fcoe"); -/* notification function from net device */ +/* notification function for packets from net device */ static struct notifier_block fcoe_notifier = { .notifier_call = fcoe_device_notification, }; -static struct scsi_transport_template *scsi_transport_fcoe_sw; +/* notification function for CPU hotplug events */ +static struct notifier_block fcoe_cpu_notifier = { + .notifier_call = fcoe_cpu_callback, +}; + +static struct scsi_transport_template *fcoe_transport_template; +static struct scsi_transport_template *fcoe_vport_transport_template; + +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 struct libfc_function_template fcoe_libfc_fcn_templ = { + .frame_send = fcoe_xmit, + .ddp_setup = fcoe_ddp_setup, + .ddp_done = fcoe_ddp_done, + .elsct_send = fcoe_elsct_send, + .get_lesb = fcoe_get_lesb, +}; struct fc_function_template fcoe_transport_function = { .show_host_node_name = 1, @@ -123,6 +177,48 @@ struct fc_function_template fcoe_transport_function = { .issue_fc_host_lip = fcoe_reset, .terminate_rport_io = fc_rport_terminate_io, + + .vport_create = fcoe_vport_create, + .vport_delete = fcoe_vport_destroy, + .vport_disable = fcoe_vport_disable, + .set_vport_symbolic_name = fcoe_set_vport_symbolic_name, + + .bsg_request = fc_lport_bsg_request, +}; + +struct fc_function_template fcoe_vport_transport_function = { + .show_host_node_name = 1, + .show_host_port_name = 1, + .show_host_supported_classes = 1, + .show_host_supported_fc4s = 1, + .show_host_active_fc4s = 1, + .show_host_maxframe_size = 1, + + .show_host_port_id = 1, + .show_host_supported_speeds = 1, + .get_host_speed = fc_get_host_speed, + .show_host_speed = 1, + .show_host_port_type = 1, + .get_host_port_state = fc_get_host_port_state, + .show_host_port_state = 1, + .show_host_symbolic_name = 1, + + .dd_fcrport_size = sizeof(struct fc_rport_libfc_priv), + .show_rport_maxframe_size = 1, + .show_rport_supported_classes = 1, + + .show_host_fabric_name = 1, + .show_starget_node_name = 1, + .show_starget_port_name = 1, + .show_starget_port_id = 1, + .set_rport_dev_loss_tmo = fc_set_rport_loss_tmo, + .show_rport_dev_loss_tmo = 1, + .get_fc_host_stats = fc_get_host_stats, + .issue_fc_host_lip = fcoe_reset, + + .terminate_rport_io = fc_rport_terminate_io, + + .bsg_request = fc_lport_bsg_request, }; static struct scsi_host_template fcoe_shost_template = { @@ -137,20 +233,17 @@ static struct scsi_host_template fcoe_shost_template = { .change_queue_depth = fc_change_queue_depth, .change_queue_type = fc_change_queue_type, .this_id = -1, - .cmd_per_lun = 32, + .cmd_per_lun = 3, .can_queue = FCOE_MAX_OUTSTANDING_COMMANDS, .use_clustering = ENABLE_CLUSTERING, .sg_tablesize = SG_ALL, .max_sectors = 0xffff, }; -static int fcoe_fip_recv(struct sk_buff *skb, struct net_device *dev, - struct packet_type *ptype, - struct net_device *orig_dev); /** - * fcoe_interface_setup() - * @fcoe: new fcoe_interface - * @netdev : ptr to the associated netdevice struct + * fcoe_interface_setup() - Setup a FCoE interface + * @fcoe: The new FCoE interface + * @netdev: The net device that the fcoe interface is on * * Returns : 0 for success * Locking: must be called with the RTNL mutex held @@ -160,23 +253,36 @@ static int fcoe_interface_setup(struct fcoe_interface *fcoe, { struct fcoe_ctlr *fip = &fcoe->ctlr; struct netdev_hw_addr *ha; + struct net_device *real_dev; u8 flogi_maddr[ETH_ALEN]; + const struct net_device_ops *ops; fcoe->netdev = netdev; + /* Let LLD initialize for FCoE */ + ops = netdev->netdev_ops; + if (ops->ndo_fcoe_enable) { + if (ops->ndo_fcoe_enable(netdev)) + FCOE_NETDEV_DBG(netdev, "Failed to enable FCoE" + " specific feature for LLD.\n"); + } + /* Do not support for bonding device */ if ((netdev->priv_flags & IFF_MASTER_ALB) || (netdev->priv_flags & IFF_SLAVE_INACTIVE) || (netdev->priv_flags & IFF_MASTER_8023AD)) { + FCOE_NETDEV_DBG(netdev, "Bonded interfaces not supported\n"); return -EOPNOTSUPP; } /* look for SAN MAC address, if multiple SAN MACs exist, only * use the first one for SPMA */ + real_dev = (netdev->priv_flags & IFF_802_1Q_VLAN) ? + vlan_dev_real_dev(netdev) : netdev; rcu_read_lock(); - for_each_dev_addr(netdev, ha) { + for_each_dev_addr(real_dev, ha) { if ((ha->type == NETDEV_HW_ADDR_T_SAN) && - (is_valid_ether_addr(fip->ctl_src_addr))) { + (is_valid_ether_addr(ha->addr))) { memcpy(fip->ctl_src_addr, ha->addr, ETH_ALEN); fip->spma = 1; break; @@ -216,19 +322,16 @@ static int fcoe_interface_setup(struct fcoe_interface *fcoe, return 0; } -static void fcoe_fip_send(struct fcoe_ctlr *fip, struct sk_buff *skb); -static void fcoe_update_src_mac(struct fcoe_ctlr *fip, u8 *old, u8 *new); -static void fcoe_destroy_work(struct work_struct *work); - /** - * fcoe_interface_create() - * @netdev: network interface + * fcoe_interface_create() - Create a FCoE interface on a net device + * @netdev: The net device to create the FCoE interface on * * Returns: pointer to a struct fcoe_interface or NULL on error */ static struct fcoe_interface *fcoe_interface_create(struct net_device *netdev) { struct fcoe_interface *fcoe; + int err; fcoe = kzalloc(sizeof(*fcoe), GFP_KERNEL); if (!fcoe) { @@ -245,15 +348,22 @@ static struct fcoe_interface *fcoe_interface_create(struct net_device *netdev) fcoe_ctlr_init(&fcoe->ctlr); fcoe->ctlr.send = fcoe_fip_send; fcoe->ctlr.update_mac = fcoe_update_src_mac; + fcoe->ctlr.get_src_addr = fcoe_get_src_mac; - fcoe_interface_setup(fcoe, netdev); + err = fcoe_interface_setup(fcoe, netdev); + if (err) { + fcoe_ctlr_destroy(&fcoe->ctlr); + kfree(fcoe); + dev_put(netdev); + return NULL; + } return fcoe; } /** - * fcoe_interface_cleanup() - clean up netdev configurations - * @fcoe: + * fcoe_interface_cleanup() - Clean up a FCoE interface + * @fcoe: The FCoE interface to be cleaned up * * Caller must be holding the RTNL mutex */ @@ -262,6 +372,7 @@ void fcoe_interface_cleanup(struct fcoe_interface *fcoe) struct net_device *netdev = fcoe->netdev; struct fcoe_ctlr *fip = &fcoe->ctlr; u8 flogi_maddr[ETH_ALEN]; + const struct net_device_ops *ops; /* * Don't listen for Ethernet packets anymore. @@ -276,16 +387,22 @@ void fcoe_interface_cleanup(struct fcoe_interface *fcoe) /* Delete secondary MAC addresses */ memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN); dev_unicast_delete(netdev, flogi_maddr); - if (!is_zero_ether_addr(fip->data_src_addr)) - dev_unicast_delete(netdev, fip->data_src_addr); if (fip->spma) dev_unicast_delete(netdev, fip->ctl_src_addr); dev_mc_delete(netdev, FIP_ALL_ENODE_MACS, ETH_ALEN, 0); + + /* Tell the LLD we are done w/ FCoE */ + ops = netdev->netdev_ops; + if (ops->ndo_fcoe_disable) { + if (ops->ndo_fcoe_disable(netdev)) + FCOE_NETDEV_DBG(netdev, "Failed to disable FCoE" + " specific feature for LLD.\n"); + } } /** * fcoe_interface_release() - fcoe_port kref release function - * @kref: embedded reference count in an fcoe_interface struct + * @kref: Embedded reference count in an fcoe_interface struct */ static void fcoe_interface_release(struct kref *kref) { @@ -301,8 +418,8 @@ static void fcoe_interface_release(struct kref *kref) } /** - * fcoe_interface_get() - * @fcoe: + * fcoe_interface_get() - Get a reference to a FCoE interface + * @fcoe: The FCoE interface to be held */ static inline void fcoe_interface_get(struct fcoe_interface *fcoe) { @@ -310,8 +427,8 @@ static inline void fcoe_interface_get(struct fcoe_interface *fcoe) } /** - * fcoe_interface_put() - * @fcoe: + * fcoe_interface_put() - Put a reference to a FCoE interface + * @fcoe: The FCoE interface to be released */ static inline void fcoe_interface_put(struct fcoe_interface *fcoe) { @@ -319,15 +436,16 @@ static inline void fcoe_interface_put(struct fcoe_interface *fcoe) } /** - * fcoe_fip_recv - handle a received FIP frame. - * @skb: the receive skb - * @dev: associated &net_device - * @ptype: the &packet_type structure which was used to register this handler. - * @orig_dev: original receive &net_device, in case @dev is a bond. + * fcoe_fip_recv() - Handler for received FIP frames + * @skb: The receive skb + * @netdev: The associated net device + * @ptype: The packet_type structure which was used to register this handler + * @orig_dev: The original net_device the the skb was received on. + * (in case dev is a bond) * * Returns: 0 for success */ -static int fcoe_fip_recv(struct sk_buff *skb, struct net_device *dev, +static int fcoe_fip_recv(struct sk_buff *skb, struct net_device *netdev, struct packet_type *ptype, struct net_device *orig_dev) { @@ -339,9 +457,9 @@ static int fcoe_fip_recv(struct sk_buff *skb, struct net_device *dev, } /** - * fcoe_fip_send() - send an Ethernet-encapsulated FIP frame. - * @fip: FCoE controller. - * @skb: FIP Packet. + * fcoe_fip_send() - Send an Ethernet-encapsulated FIP frame + * @fip: The FCoE controller + * @skb: The FIP packet to be sent */ static void fcoe_fip_send(struct fcoe_ctlr *fip, struct sk_buff *skb) { @@ -350,88 +468,101 @@ static void fcoe_fip_send(struct fcoe_ctlr *fip, struct sk_buff *skb) } /** - * fcoe_update_src_mac() - Update Ethernet MAC filters. - * @fip: FCoE controller. - * @old: Unicast MAC address to delete if the MAC is non-zero. - * @new: Unicast MAC address to add. + * fcoe_update_src_mac() - Update the Ethernet MAC filters + * @lport: The local port to update the source MAC on + * @addr: Unicast MAC address to add * * Remove any previously-set unicast MAC filter. * Add secondary FCoE MAC address filter for our OUI. */ -static void fcoe_update_src_mac(struct fcoe_ctlr *fip, u8 *old, u8 *new) +static void fcoe_update_src_mac(struct fc_lport *lport, u8 *addr) { - struct fcoe_interface *fcoe; + struct fcoe_port *port = lport_priv(lport); + struct fcoe_interface *fcoe = port->fcoe; - fcoe = fcoe_from_ctlr(fip); rtnl_lock(); - if (!is_zero_ether_addr(old)) - dev_unicast_delete(fcoe->netdev, old); - dev_unicast_add(fcoe->netdev, new); + if (!is_zero_ether_addr(port->data_src_addr)) + dev_unicast_delete(fcoe->netdev, port->data_src_addr); + if (!is_zero_ether_addr(addr)) + dev_unicast_add(fcoe->netdev, addr); + memcpy(port->data_src_addr, addr, ETH_ALEN); rtnl_unlock(); } /** - * fcoe_lport_config() - sets up the fc_lport - * @lp: ptr to the fc_lport + * fcoe_get_src_mac() - return the Ethernet source address for an lport + * @lport: libfc lport + */ +static u8 *fcoe_get_src_mac(struct fc_lport *lport) +{ + struct fcoe_port *port = lport_priv(lport); + + return port->data_src_addr; +} + +/** + * fcoe_lport_config() - Set up a local port + * @lport: The local port to be setup * * Returns: 0 for success */ -static int fcoe_lport_config(struct fc_lport *lp) +static int fcoe_lport_config(struct fc_lport *lport) { - lp->link_up = 0; - lp->qfull = 0; - lp->max_retry_count = 3; - lp->max_rport_retry_count = 3; - lp->e_d_tov = 2 * 1000; /* FC-FS default */ - lp->r_a_tov = 2 * 2 * 1000; - lp->service_params = (FCP_SPPF_INIT_FCN | FCP_SPPF_RD_XRDY_DIS | - FCP_SPPF_RETRY | FCP_SPPF_CONF_COMPL); - - fc_lport_init_stats(lp); + lport->link_up = 0; + lport->qfull = 0; + lport->max_retry_count = 3; + lport->max_rport_retry_count = 3; + lport->e_d_tov = 2 * 1000; /* FC-FS default */ + lport->r_a_tov = 2 * 2 * 1000; + lport->service_params = (FCP_SPPF_INIT_FCN | FCP_SPPF_RD_XRDY_DIS | + FCP_SPPF_RETRY | FCP_SPPF_CONF_COMPL); + lport->does_npiv = 1; + + fc_lport_init_stats(lport); /* lport fc_lport related configuration */ - fc_lport_config(lp); + fc_lport_config(lport); /* offload related configuration */ - lp->crc_offload = 0; - lp->seq_offload = 0; - lp->lro_enabled = 0; - lp->lro_xid = 0; - lp->lso_max = 0; + lport->crc_offload = 0; + lport->seq_offload = 0; + lport->lro_enabled = 0; + lport->lro_xid = 0; + lport->lso_max = 0; return 0; } /** - * fcoe_queue_timer() - fcoe queue timer - * @lp: the fc_lport pointer + * fcoe_queue_timer() - The fcoe queue timer + * @lport: The local port * * Calls fcoe_check_wait_queue on timeout - * */ -static void fcoe_queue_timer(ulong lp) +static void fcoe_queue_timer(ulong lport) { - fcoe_check_wait_queue((struct fc_lport *)lp, NULL); + fcoe_check_wait_queue((struct fc_lport *)lport, NULL); } /** - * fcoe_netdev_config() - Set up netdev for SW FCoE - * @lp : ptr to the fc_lport - * @netdev : ptr to the associated netdevice struct + * fcoe_netdev_config() - Set up net devive for SW FCoE + * @lport: The local port that is associated with the net device + * @netdev: The associated net device * - * Must be called after fcoe_lport_config() as it will use lport mutex + * Must be called after fcoe_lport_config() as it will use local port mutex * - * Returns : 0 for success + * Returns: 0 for success */ -static int fcoe_netdev_config(struct fc_lport *lp, struct net_device *netdev) +static int fcoe_netdev_config(struct fc_lport *lport, struct net_device *netdev) { u32 mfs; u64 wwnn, wwpn; struct fcoe_interface *fcoe; struct fcoe_port *port; + int vid = 0; /* Setup lport private data to point to fcoe softc */ - port = lport_priv(lp); + port = lport_priv(lport); fcoe = port->fcoe; /* @@ -439,86 +570,112 @@ static int fcoe_netdev_config(struct fc_lport *lp, struct net_device *netdev) * user-configured limit. If the MFS is too low, fcoe_link_ok() * will return 0, so do this first. */ - mfs = netdev->mtu - (sizeof(struct fcoe_hdr) + - sizeof(struct fcoe_crc_eof)); - if (fc_set_mfs(lp, mfs)) + mfs = netdev->mtu; + if (netdev->features & NETIF_F_FCOE_MTU) { + mfs = FCOE_MTU; + FCOE_NETDEV_DBG(netdev, "Supports FCOE_MTU of %d bytes\n", mfs); + } + mfs -= (sizeof(struct fcoe_hdr) + sizeof(struct fcoe_crc_eof)); + if (fc_set_mfs(lport, mfs)) return -EINVAL; /* offload features support */ if (netdev->features & NETIF_F_SG) - lp->sg_supp = 1; + lport->sg_supp = 1; if (netdev->features & NETIF_F_FCOE_CRC) { - lp->crc_offload = 1; + lport->crc_offload = 1; FCOE_NETDEV_DBG(netdev, "Supports FCCRC offload\n"); } if (netdev->features & NETIF_F_FSO) { - lp->seq_offload = 1; - lp->lso_max = netdev->gso_max_size; + lport->seq_offload = 1; + lport->lso_max = netdev->gso_max_size; FCOE_NETDEV_DBG(netdev, "Supports LSO for max len 0x%x\n", - lp->lso_max); + lport->lso_max); } if (netdev->fcoe_ddp_xid) { - lp->lro_enabled = 1; - lp->lro_xid = netdev->fcoe_ddp_xid; + lport->lro_enabled = 1; + lport->lro_xid = netdev->fcoe_ddp_xid; FCOE_NETDEV_DBG(netdev, "Supports LRO for max xid 0x%x\n", - lp->lro_xid); + lport->lro_xid); } skb_queue_head_init(&port->fcoe_pending_queue); port->fcoe_pending_queue_active = 0; - setup_timer(&port->timer, fcoe_queue_timer, (unsigned long)lp); + setup_timer(&port->timer, fcoe_queue_timer, (unsigned long)lport); - wwnn = fcoe_wwn_from_mac(netdev->dev_addr, 1, 0); - fc_set_wwnn(lp, wwnn); - /* XXX - 3rd arg needs to be vlan id */ - wwpn = fcoe_wwn_from_mac(netdev->dev_addr, 2, 0); - fc_set_wwpn(lp, wwpn); + if (!lport->vport) { + /* + * Use NAA 1&2 (FC-FS Rev. 2.0, Sec. 15) to generate WWNN/WWPN: + * For WWNN, we use NAA 1 w/ bit 27-16 of word 0 as 0. + * For WWPN, we use NAA 2 w/ bit 27-16 of word 0 from VLAN ID + */ + if (netdev->priv_flags & IFF_802_1Q_VLAN) + vid = vlan_dev_vlan_id(netdev); + wwnn = fcoe_wwn_from_mac(fcoe->ctlr.ctl_src_addr, 1, 0); + fc_set_wwnn(lport, wwnn); + wwpn = fcoe_wwn_from_mac(fcoe->ctlr.ctl_src_addr, 2, vid); + fc_set_wwpn(lport, wwpn); + } return 0; } /** - * fcoe_shost_config() - Sets up fc_lport->host - * @lp : ptr to the fc_lport - * @shost : ptr to the associated scsi host - * @dev : device associated to scsi host + * 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 + * Returns: 0 for success */ -static int fcoe_shost_config(struct fc_lport *lp, struct Scsi_Host *shost, - struct device *dev) +static int fcoe_shost_config(struct fc_lport *lport, struct Scsi_Host *shost, + struct device *dev) { int rc = 0; /* lport scsi host config */ - lp->host = shost; - - lp->host->max_lun = FCOE_MAX_LUN; - lp->host->max_id = FCOE_MAX_FCP_TARGET; - lp->host->max_channel = 0; - lp->host->transportt = scsi_transport_fcoe_sw; + lport->host->max_lun = FCOE_MAX_LUN; + lport->host->max_id = FCOE_MAX_FCP_TARGET; + lport->host->max_channel = 0; + if (lport->vport) + lport->host->transportt = fcoe_vport_transport_template; + else + lport->host->transportt = fcoe_transport_template; /* add the new host to the SCSI-ml */ - rc = scsi_add_host(lp->host, dev); + rc = scsi_add_host(lport->host, dev); if (rc) { - FCOE_NETDEV_DBG(fcoe_netdev(lp), "fcoe_shost_config: " + FCOE_NETDEV_DBG(fcoe_netdev(lport), "fcoe_shost_config: " "error on scsi_add_host\n"); return rc; } - sprintf(fc_host_symbolic_name(lp->host), "%s v%s over %s", - FCOE_NAME, FCOE_VERSION, - fcoe_netdev(lp)->name); + + if (!lport->vport) + fc_host_max_npiv_vports(lport->host) = USHORT_MAX; + + snprintf(fc_host_symbolic_name(lport->host), FC_SYMBOLIC_NAME_SIZE, + "%s v%s over %s", FCOE_NAME, FCOE_VERSION, + fcoe_netdev(lport)->name); return 0; } -/* - * fcoe_oem_match() - match for read types IO - * @fp: the fc_frame for new IO. +/** + * fcoe_oem_match() - The match routine for the offloaded exchange manager + * @fp: The I/O frame + * + * This routine will be associated with an exchange manager (EM). When + * the libfc exchange handling code is looking for an EM to use it will + * call this routine and pass it the frame that it wishes to send. This + * routine will return True if the associated EM is to be used and False + * if the echange code should continue looking for an EM. * - * Returns : true for read types IO, otherwise returns false. + * The offload EM that this routine is associated with will handle any + * packets that are for SCSI read requests. + * + * Returns: True for read types I/O, otherwise returns false. */ bool fcoe_oem_match(struct fc_frame *fp) { @@ -527,14 +684,14 @@ bool fcoe_oem_match(struct fc_frame *fp) } /** - * fcoe_em_config() - allocates em for this lport - * @lp: the fcoe that em is to allocated for + * fcoe_em_config() - Allocate and configure an exchange manager + * @lport: The local port that the new EM will be associated with * - * Returns : 0 on success + * Returns: 0 on success */ -static inline int fcoe_em_config(struct fc_lport *lp) +static inline int fcoe_em_config(struct fc_lport *lport) { - struct fcoe_port *port = lport_priv(lp); + struct fcoe_port *port = lport_priv(lport); struct fcoe_interface *fcoe = port->fcoe; struct fcoe_interface *oldfcoe = NULL; struct net_device *old_real_dev, *cur_real_dev; @@ -545,8 +702,9 @@ static inline int fcoe_em_config(struct fc_lport *lp) * Check if need to allocate an em instance for * offload exchange ids to be shared across all VN_PORTs/lport. */ - if (!lp->lro_enabled || !lp->lro_xid || (lp->lro_xid >= max_xid)) { - lp->lro_xid = 0; + if (!lport->lro_enabled || !lport->lro_xid || + (lport->lro_xid >= max_xid)) { + lport->lro_xid = 0; goto skip_oem; } @@ -572,16 +730,16 @@ static inline int fcoe_em_config(struct fc_lport *lp) } if (fcoe->oem) { - if (!fc_exch_mgr_add(lp, fcoe->oem, fcoe_oem_match)) { + if (!fc_exch_mgr_add(lport, fcoe->oem, fcoe_oem_match)) { printk(KERN_ERR "fcoe_em_config: failed to add " "offload em:%p on interface:%s\n", fcoe->oem, fcoe->netdev->name); return -ENOMEM; } } else { - fcoe->oem = fc_exch_mgr_alloc(lp, FC_CLASS_3, - FCOE_MIN_XID, lp->lro_xid, - fcoe_oem_match); + fcoe->oem = fc_exch_mgr_alloc(lport, FC_CLASS_3, + FCOE_MIN_XID, lport->lro_xid, + fcoe_oem_match); if (!fcoe->oem) { printk(KERN_ERR "fcoe_em_config: failed to allocate " "em for offload exches on interface:%s\n", @@ -593,10 +751,10 @@ static inline int fcoe_em_config(struct fc_lport *lp) /* * Exclude offload EM xid range from next EM xid range. */ - min_xid += lp->lro_xid + 1; + min_xid += lport->lro_xid + 1; skip_oem: - if (!fc_exch_mgr_alloc(lp, FC_CLASS_3, min_xid, max_xid, NULL)) { + if (!fc_exch_mgr_alloc(lport, FC_CLASS_3, min_xid, max_xid, NULL)) { printk(KERN_ERR "fcoe_em_config: failed to " "allocate em on interface %s\n", fcoe->netdev->name); return -ENOMEM; @@ -606,8 +764,8 @@ skip_oem: } /** - * fcoe_if_destroy() - FCoE software HBA tear-down function - * @lport: fc_lport to destroy + * fcoe_if_destroy() - Tear down a SW FCoE instance + * @lport: The local port to be destroyed */ static void fcoe_if_destroy(struct fc_lport *lport) { @@ -630,6 +788,11 @@ 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(); + /* receives may not be stopped until after this */ fcoe_interface_put(fcoe); @@ -650,82 +813,89 @@ static void fcoe_if_destroy(struct fc_lport *lport) scsi_host_put(lport->host); } -/* - * fcoe_ddp_setup - calls LLD's ddp_setup through net_device - * @lp: the corresponding fc_lport - * @xid: the exchange id for this ddp transfer - * @sgl: the scatterlist describing this transfer - * @sgc: number of sg items +/** + * fcoe_ddp_setup() - Call a LLD's ddp_setup through the net device + * @lport: The local port to setup DDP for + * @xid: The exchange ID for this DDP transfer + * @sgl: The scatterlist describing this transfer + * @sgc: The number of sg items * - * Returns : 0 no ddp + * Returns: 0 if the DDP context was not configured */ -static int fcoe_ddp_setup(struct fc_lport *lp, u16 xid, - struct scatterlist *sgl, unsigned int sgc) +static int fcoe_ddp_setup(struct fc_lport *lport, u16 xid, + struct scatterlist *sgl, unsigned int sgc) { - struct net_device *n = fcoe_netdev(lp); + struct net_device *netdev = fcoe_netdev(lport); - if (n->netdev_ops && n->netdev_ops->ndo_fcoe_ddp_setup) - return n->netdev_ops->ndo_fcoe_ddp_setup(n, xid, sgl, sgc); + if (netdev->netdev_ops->ndo_fcoe_ddp_setup) + return netdev->netdev_ops->ndo_fcoe_ddp_setup(netdev, + xid, sgl, + sgc); return 0; } -/* - * fcoe_ddp_done - calls LLD's ddp_done through net_device - * @lp: the corresponding fc_lport - * @xid: the exchange id for this ddp transfer +/** + * fcoe_ddp_done() - Call a LLD's ddp_done through the net device + * @lport: The local port to complete DDP on + * @xid: The exchange ID for this DDP transfer * - * Returns : the length of data that have been completed by ddp + * Returns: the length of data that have been completed by DDP */ -static int fcoe_ddp_done(struct fc_lport *lp, u16 xid) +static int fcoe_ddp_done(struct fc_lport *lport, u16 xid) { - struct net_device *n = fcoe_netdev(lp); + struct net_device *netdev = fcoe_netdev(lport); - if (n->netdev_ops && n->netdev_ops->ndo_fcoe_ddp_done) - return n->netdev_ops->ndo_fcoe_ddp_done(n, xid); + if (netdev->netdev_ops->ndo_fcoe_ddp_done) + return netdev->netdev_ops->ndo_fcoe_ddp_done(netdev, xid); return 0; } -static struct libfc_function_template fcoe_libfc_fcn_templ = { - .frame_send = fcoe_xmit, - .ddp_setup = fcoe_ddp_setup, - .ddp_done = fcoe_ddp_done, -}; - /** - * fcoe_if_create() - this function creates the fcoe port - * @fcoe: fcoe_interface structure to create an fc_lport instance on - * @parent: device pointer to be the parent in sysfs for the SCSI host + * fcoe_if_create() - Create a FCoE instance on an interface + * @fcoe: The FCoE interface to create a local port on + * @parent: The device pointer to be the parent in sysfs for the SCSI host + * @npiv: Indicates if the port is a vport or not * - * Creates fc_lport struct and scsi_host for lport, configures lport. + * Creates a fc_lport instance and a Scsi_Host instance and configure them. * - * Returns : The allocated fc_lport or an error pointer + * Returns: The allocated fc_lport or an error pointer */ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe, - struct device *parent) + struct device *parent, int npiv) { - int rc; + struct net_device *netdev = fcoe->netdev; struct fc_lport *lport = NULL; struct fcoe_port *port; struct Scsi_Host *shost; - struct net_device *netdev = fcoe->netdev; + int rc; + /* + * parent is only a vport if npiv is 1, + * but we'll only use vport in that case so go ahead and set it + */ + struct fc_vport *vport = dev_to_vport(parent); FCOE_NETDEV_DBG(netdev, "Create Interface\n"); - shost = libfc_host_alloc(&fcoe_shost_template, - sizeof(struct fcoe_port)); - if (!shost) { + if (!npiv) { + lport = libfc_host_alloc(&fcoe_shost_template, + sizeof(struct fcoe_port)); + } else { + lport = libfc_vport_create(vport, + sizeof(struct fcoe_port)); + } + if (!lport) { FCOE_NETDEV_DBG(netdev, "Could not allocate host structure\n"); rc = -ENOMEM; goto out; } - lport = shost_priv(shost); + shost = lport->host; port = lport_priv(lport); port->lport = lport; port->fcoe = fcoe; INIT_WORK(&port->destroy_work, fcoe_destroy_work); - /* configure fc_lport, e.g., em */ + /* configure a fc_lport including the exchange manager */ rc = fcoe_lport_config(lport); if (rc) { FCOE_NETDEV_DBG(netdev, "Could not configure lport for the " @@ -733,6 +903,13 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe, goto out_host_put; } + if (npiv) { + FCOE_NETDEV_DBG(netdev, "Setting vport names, 0x%llX 0x%llX\n", + vport->node_name, vport->port_name); + fc_set_wwnn(lport, vport->node_name); + fc_set_wwpn(lport, vport->port_name); + } + /* configure lport network properties */ rc = fcoe_netdev_config(lport, netdev); if (rc) { @@ -757,21 +934,24 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe, goto out_lp_destroy; } - /* - * fcoe_em_alloc() and fcoe_hostlist_add() both - * need to be atomic with respect to other changes to the hostlist - * since fcoe_em_alloc() looks for an existing EM - * instance on host list updated by fcoe_hostlist_add(). - * - * This is currently handled through the fcoe_config_mutex begin held. - */ + if (!npiv) { + /* + * fcoe_em_alloc() and fcoe_hostlist_add() both + * need to be atomic with respect to other changes to the + * hostlist since fcoe_em_alloc() looks for an existing EM + * instance on host list updated by fcoe_hostlist_add(). + * + * This is currently handled through the fcoe_config_mutex + * begin held. + */ - /* lport exch manager allocation */ - rc = fcoe_em_config(lport); - if (rc) { - FCOE_NETDEV_DBG(netdev, "Could not configure the EM for the " - "interface\n"); - goto out_lp_destroy; + /* lport exch manager allocation */ + rc = fcoe_em_config(lport); + if (rc) { + FCOE_NETDEV_DBG(netdev, "Could not configure the EM " + "for the interface\n"); + goto out_lp_destroy; + } } fcoe_interface_get(fcoe); @@ -786,17 +966,20 @@ out: } /** - * fcoe_if_init() - attach to scsi transport + * fcoe_if_init() - Initialization routine for fcoe.ko + * + * Attaches the SW FCoE transport to the FC transport * - * Returns : 0 on success + * Returns: 0 on success */ static int __init fcoe_if_init(void) { /* attach to scsi transport */ - scsi_transport_fcoe_sw = - fc_attach_transport(&fcoe_transport_function); + fcoe_transport_template = fc_attach_transport(&fcoe_transport_function); + fcoe_vport_transport_template = + fc_attach_transport(&fcoe_vport_transport_function); - if (!scsi_transport_fcoe_sw) { + if (!fcoe_transport_template) { printk(KERN_ERR "fcoe: Failed to attach to the FC transport\n"); return -ENODEV; } @@ -805,20 +988,24 @@ static int __init fcoe_if_init(void) } /** - * fcoe_if_exit() - detach from scsi transport + * fcoe_if_exit() - Tear down fcoe.ko + * + * Detaches the SW FCoE transport from the FC transport * - * Returns : 0 on success + * Returns: 0 on success */ int __exit fcoe_if_exit(void) { - fc_release_transport(scsi_transport_fcoe_sw); - scsi_transport_fcoe_sw = NULL; + fc_release_transport(fcoe_transport_template); + fc_release_transport(fcoe_vport_transport_template); + fcoe_transport_template = NULL; + fcoe_vport_transport_template = NULL; return 0; } /** - * fcoe_percpu_thread_create() - Create a receive thread for an online cpu - * @cpu: cpu index for the online cpu + * fcoe_percpu_thread_create() - Create a receive thread for an online CPU + * @cpu: The CPU index of the CPU to create a receive thread for */ static void fcoe_percpu_thread_create(unsigned int cpu) { @@ -841,8 +1028,8 @@ static void fcoe_percpu_thread_create(unsigned int cpu) } /** - * fcoe_percpu_thread_destroy() - removes the rx thread for the given cpu - * @cpu: cpu index the rx thread is to be removed + * fcoe_percpu_thread_destroy() - Remove the receive thread of a CPU + * @cpu: The CPU index of the CPU whose receive thread is to be destroyed * * Destroys a per-CPU Rx thread. Any pending skbs are moved to the * current CPU's Rx thread. If the thread being destroyed is bound to @@ -890,7 +1077,7 @@ static void fcoe_percpu_thread_destroy(unsigned int cpu) } else { /* * The targeted CPU is not initialized and cannot accept - * new skbs. Unlock the targeted CPU and drop the skbs + * new skbs. Unlock the targeted CPU and drop the skbs * on the CPU that is going offline. */ while ((skb = __skb_dequeue(&p->fcoe_rx_list)) != NULL) @@ -931,12 +1118,12 @@ static void fcoe_percpu_thread_destroy(unsigned int cpu) } /** - * fcoe_cpu_callback() - fcoe cpu hotplug event callback - * @nfb: callback data block - * @action: event triggering the callback - * @hcpu: index for the cpu of this event + * fcoe_cpu_callback() - Handler for CPU hotplug events + * @nfb: The callback data block + * @action: The event triggering the callback + * @hcpu: The index of the CPU that the event is for * - * This creates or destroys per cpu data for fcoe + * This creates or destroys per-CPU data for fcoe * * Returns NOTIFY_OK always. */ @@ -962,25 +1149,22 @@ static int fcoe_cpu_callback(struct notifier_block *nfb, return NOTIFY_OK; } -static struct notifier_block fcoe_cpu_notifier = { - .notifier_call = fcoe_cpu_callback, -}; - /** - * fcoe_rcv() - this is the fcoe receive function called by NET_RX_SOFTIRQ - * @skb: the receive skb - * @dev: associated net device - * @ptype: context - * @olddev: last device + * fcoe_rcv() - Receive packets from a net device + * @skb: The received packet + * @netdev: The net device that the packet was received on + * @ptype: The packet type context + * @olddev: The last device net device * - * this function will receive the packet and build fc frame and pass it up + * This routine is called by NET_RX_SOFTIRQ. It receives a packet, builds a + * FC frame and passes the frame to libfc. * * Returns: 0 for success */ -int fcoe_rcv(struct sk_buff *skb, struct net_device *dev, +int fcoe_rcv(struct sk_buff *skb, struct net_device *netdev, struct packet_type *ptype, struct net_device *olddev) { - struct fc_lport *lp; + struct fc_lport *lport; struct fcoe_rcv_info *fr; struct fcoe_interface *fcoe; struct fc_frame_header *fh; @@ -988,15 +1172,15 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *dev, unsigned int cpu; fcoe = container_of(ptype, struct fcoe_interface, fcoe_packet_type); - lp = fcoe->ctlr.lp; - if (unlikely(lp == NULL)) { - FCOE_NETDEV_DBG(dev, "Cannot find hba structure"); + lport = fcoe->ctlr.lp; + if (unlikely(!lport)) { + FCOE_NETDEV_DBG(netdev, "Cannot find hba structure"); goto err2; } - if (!lp->link_up) + if (!lport->link_up) goto err2; - FCOE_NETDEV_DBG(dev, "skb_info: len:%d data_len:%d head:%p " + FCOE_NETDEV_DBG(netdev, "skb_info: len:%d data_len:%d head:%p " "data:%p tail:%p end:%p sum:%d dev:%s", skb->len, skb->data_len, skb->head, skb->data, skb_tail_pointer(skb), skb_end_pointer(skb), @@ -1004,7 +1188,7 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *dev, /* check for FCOE packet type */ if (unlikely(eth_hdr(skb)->h_proto != htons(ETH_P_FCOE))) { - FCOE_NETDEV_DBG(dev, "Wrong FC type frame"); + FCOE_NETDEV_DBG(netdev, "Wrong FC type frame"); goto err; } @@ -1013,14 +1197,14 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *dev, * and FC headers are pulled into the linear data area. */ if (unlikely((skb->len < FCOE_MIN_FRAME) || - !pskb_may_pull(skb, FCOE_HEADER_LEN))) + !pskb_may_pull(skb, FCOE_HEADER_LEN))) goto err; skb_set_transport_header(skb, sizeof(struct fcoe_hdr)); fh = (struct fc_frame_header *) skb_transport_header(skb); fr = fcoe_dev_from_skb(skb); - fr->fr_dev = lp; + fr->fr_dev = lport; fr->ptype = ptype; /* @@ -1042,7 +1226,7 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *dev, * the first CPU now. For non-SMP systems this * will check the same CPU twice. */ - FCOE_NETDEV_DBG(dev, "CPU is online, but no receive thread " + FCOE_NETDEV_DBG(netdev, "CPU is online, but no receive thread " "ready for incoming skb- using first online " "CPU.\n"); @@ -1061,15 +1245,29 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *dev, * this skb. We also have this receive thread locked, * so we're free to queue skbs into it's queue. */ - __skb_queue_tail(&fps->fcoe_rx_list, skb); - if (fps->fcoe_rx_list.qlen == 1) - wake_up_process(fps->thread); - spin_unlock_bh(&fps->fcoe_rx_list.lock); + /* If this is a SCSI-FCP frame, and this is already executing on the + * correct CPU, and the queue for this CPU is empty, then go ahead + * and process the frame directly in the softirq context. + * This lets us process completions without context switching from the + * NET_RX softirq, to our receive processing thread, and then back to + * BLOCK softirq context. + */ + if (fh->fh_type == FC_TYPE_FCP && + cpu == smp_processor_id() && + skb_queue_empty(&fps->fcoe_rx_list)) { + spin_unlock_bh(&fps->fcoe_rx_list.lock); + fcoe_recv_frame(skb); + } else { + __skb_queue_tail(&fps->fcoe_rx_list, skb); + if (fps->fcoe_rx_list.qlen == 1) + wake_up_process(fps->thread); + spin_unlock_bh(&fps->fcoe_rx_list.lock); + } return 0; err: - fc_lport_get_stats(lp)->ErrorFrames++; + fc_lport_get_stats(lport)->ErrorFrames++; err2: kfree_skb(skb); @@ -1077,17 +1275,21 @@ err2: } /** - * fcoe_start_io() - pass to netdev to start xmit for fcoe - * @skb: the skb to be xmitted + * fcoe_start_io() - Start FCoE I/O + * @skb: The packet to be transmitted + * + * This routine is called from the net device to start transmitting + * FCoE packets. * * Returns: 0 for success */ static inline int fcoe_start_io(struct sk_buff *skb) { + struct sk_buff *nskb; int rc; - skb_get(skb); - rc = dev_queue_xmit(skb); + nskb = skb_clone(skb, GFP_ATOMIC); + rc = dev_queue_xmit(nskb); if (rc != 0) return rc; kfree_skb(skb); @@ -1095,9 +1297,15 @@ static inline int fcoe_start_io(struct sk_buff *skb) } /** - * fcoe_get_paged_crc_eof() - in case we need to alloc a page for crc_eof - * @skb: the skb to be xmitted - * @tlen: total len + * fcoe_get_paged_crc_eof() - Allocate a page to be used for the trailer CRC + * @skb: The packet to be transmitted + * @tlen: The total length of the trailer + * + * This routine allocates a page for frame trailers. The page is re-used if + * there is enough room left on it for the current trailer. If there isn't + * enough buffer left a new page is allocated for the trailer. Reference to + * the page from this function as well as the skbs using the page fragments + * ensure that the page is freed at the appropriate time. * * Returns: 0 for success */ @@ -1136,11 +1344,12 @@ static int fcoe_get_paged_crc_eof(struct sk_buff *skb, int tlen) } /** - * fcoe_fc_crc() - calculates FC CRC in this fcoe skb - * @fp: the fc_frame containing data to be checksummed + * fcoe_fc_crc() - Calculates the CRC for a given frame + * @fp: The frame to be checksumed + * + * This uses crc32() routine to calculate the CRC for a frame * - * This uses crc32() to calculate the crc for port frame - * Return : 32 bit crc + * Return: The 32 bit CRC value */ u32 fcoe_fc_crc(struct fc_frame *fp) { @@ -1171,13 +1380,13 @@ u32 fcoe_fc_crc(struct fc_frame *fp) } /** - * fcoe_xmit() - FCoE frame transmit function - * @lp: the associated local fcoe - * @fp: the fc_frame to be transmitted + * fcoe_xmit() - Transmit a FCoE frame + * @lport: The local port that the frame is to be transmitted for + * @fp: The frame to be transmitted * - * Return : 0 for success + * Return: 0 for success */ -int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp) +int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp) { int wlen; u32 crc; @@ -1189,7 +1398,7 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp) unsigned int hlen; /* header length implies the version */ unsigned int tlen; /* trailer length */ unsigned int elen; /* eth header, may include vlan */ - struct fcoe_port *port = lport_priv(lp); + struct fcoe_port *port = lport_priv(lport); struct fcoe_interface *fcoe = port->fcoe; u8 sof, eof; struct fcoe_hdr *hp; @@ -1200,13 +1409,13 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp) skb = fp_skb(fp); wlen = skb->len / FCOE_WORD_TO_BYTE; - if (!lp->link_up) { + if (!lport->link_up) { kfree_skb(skb); return 0; } if (unlikely(fh->fh_r_ctl == FC_RCTL_ELS_REQ) && - fcoe_ctlr_els_send(&fcoe->ctlr, skb)) + fcoe_ctlr_els_send(&fcoe->ctlr, lport, skb)) return 0; sof = fr_sof(fp); @@ -1218,7 +1427,7 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp) wlen = (skb->len - tlen + sizeof(crc)) / FCOE_WORD_TO_BYTE; /* crc offload */ - if (likely(lp->crc_offload)) { + if (likely(lport->crc_offload)) { skb->ip_summed = CHECKSUM_PARTIAL; skb->csum_start = skb_headroom(skb); skb->csum_offset = skb->len; @@ -1271,7 +1480,7 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp) if (unlikely(fcoe->ctlr.flogi_oxid != FC_XID_UNKNOWN)) memcpy(eh->h_source, fcoe->ctlr.ctl_src_addr, ETH_ALEN); else - memcpy(eh->h_source, fcoe->ctlr.data_src_addr, ETH_ALEN); + memcpy(eh->h_source, port->data_src_addr, ETH_ALEN); hp = (struct fcoe_hdr *)(eh + 1); memset(hp, 0, sizeof(*hp)); @@ -1280,7 +1489,7 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp) hp->fcoe_sof = sof; /* fcoe lso, mss is in max_payload which is non-zero for FCP data */ - if (lp->seq_offload && fr_max_payload(fp)) { + if (lport->seq_offload && fr_max_payload(fp)) { skb_shinfo(skb)->gso_type = SKB_GSO_FCOE; skb_shinfo(skb)->gso_size = fr_max_payload(fp); } else { @@ -1288,23 +1497,23 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp) skb_shinfo(skb)->gso_size = 0; } /* update tx stats: regardless if LLD fails */ - stats = fc_lport_get_stats(lp); + stats = fc_lport_get_stats(lport); stats->TxFrames++; stats->TxWords += wlen; /* send down to lld */ - fr_dev(fp) = lp; + fr_dev(fp) = lport; if (port->fcoe_pending_queue.qlen) - fcoe_check_wait_queue(lp, skb); + fcoe_check_wait_queue(lport, skb); else if (fcoe_start_io(skb)) - fcoe_check_wait_queue(lp, skb); + fcoe_check_wait_queue(lport, skb); return 0; } /** - * fcoe_percpu_flush_done() - Indicate percpu queue flush completion. - * @skb: the skb being completed. + * fcoe_percpu_flush_done() - Indicate per-CPU queue flush completion + * @skb: The completed skb (argument required by destructor) */ static void fcoe_percpu_flush_done(struct sk_buff *skb) { @@ -1312,26 +1521,134 @@ static void fcoe_percpu_flush_done(struct sk_buff *skb) } /** - * fcoe_percpu_receive_thread() - recv thread per cpu - * @arg: ptr to the fcoe per cpu struct - * - * Return: 0 for success + * fcoe_recv_frame() - process a single received frame + * @skb: frame to process */ -int fcoe_percpu_receive_thread(void *arg) +static void fcoe_recv_frame(struct sk_buff *skb) { - struct fcoe_percpu_s *p = arg; u32 fr_len; - struct fc_lport *lp; + struct fc_lport *lport; struct fcoe_rcv_info *fr; struct fcoe_dev_stats *stats; struct fc_frame_header *fh; - struct sk_buff *skb; struct fcoe_crc_eof crc_eof; struct fc_frame *fp; u8 *mac = NULL; struct fcoe_port *port; struct fcoe_hdr *hp; + fr = fcoe_dev_from_skb(skb); + lport = fr->fr_dev; + if (unlikely(!lport)) { + if (skb->destructor != fcoe_percpu_flush_done) + FCOE_NETDEV_DBG(skb->dev, "NULL lport in skb"); + kfree_skb(skb); + return; + } + + FCOE_NETDEV_DBG(skb->dev, "skb_info: len:%d data_len:%d " + "head:%p data:%p tail:%p end:%p sum:%d dev:%s", + skb->len, skb->data_len, + skb->head, skb->data, skb_tail_pointer(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 + * was done in fcoe_rcv already. + */ + hp = (struct fcoe_hdr *) skb_network_header(skb); + fh = (struct fc_frame_header *) skb_transport_header(skb); + + stats = fc_lport_get_stats(lport); + if (unlikely(FC_FCOE_DECAPS_VER(hp) != FC_FCOE_VER)) { + if (stats->ErrorFrames < 5) + printk(KERN_WARNING "fcoe: FCoE version " + "mismatch: The frame has " + "version %x, but the " + "initiator supports version " + "%x\n", FC_FCOE_DECAPS_VER(hp), + FC_FCOE_VER); + stats->ErrorFrames++; + kfree_skb(skb); + return; + } + + skb_pull(skb, sizeof(struct fcoe_hdr)); + fr_len = skb->len - sizeof(struct fcoe_crc_eof); + + stats->RxFrames++; + stats->RxWords += fr_len / FCOE_WORD_TO_BYTE; + + fp = (struct fc_frame *)skb; + fc_frame_init(fp); + fr_dev(fp) = lport; + 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; + } + fr_eof(fp) = crc_eof.fcoe_eof; + fr_crc(fp) = crc_eof.fcoe_crc32; + if (pskb_trim(skb, fr_len)) { + kfree_skb(skb); + return; + } + + /* + * We only check CRC if no offload is available and if it is + * it's solicited data, in which case, the FCP layer would + * check it during the copy. + */ + if (lport->crc_offload && + skb->ip_summed == CHECKSUM_UNNECESSARY) + fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED; + else + 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 (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; + } + fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED; + } + fc_exch_recv(lport, fp); +} + +/** + * fcoe_percpu_receive_thread() - The per-CPU packet receive thread + * @arg: The per-CPU context + * + * Return: 0 for success + */ +int fcoe_percpu_receive_thread(void *arg) +{ + struct fcoe_percpu_s *p = arg; + struct sk_buff *skb; + set_user_nice(current, -20); while (!kthread_should_stop()) { @@ -1347,129 +1664,27 @@ int fcoe_percpu_receive_thread(void *arg) spin_lock_bh(&p->fcoe_rx_list.lock); } spin_unlock_bh(&p->fcoe_rx_list.lock); - fr = fcoe_dev_from_skb(skb); - lp = fr->fr_dev; - if (unlikely(lp == NULL)) { - if (skb->destructor != fcoe_percpu_flush_done) - FCOE_NETDEV_DBG(skb->dev, "NULL lport in skb"); - kfree_skb(skb); - continue; - } - - FCOE_NETDEV_DBG(skb->dev, "skb_info: len:%d data_len:%d " - "head:%p data:%p tail:%p end:%p sum:%d dev:%s", - skb->len, skb->data_len, - skb->head, skb->data, skb_tail_pointer(skb), - skb_end_pointer(skb), skb->csum, - skb->dev ? skb->dev->name : "<NULL>"); - - /* - * Save source MAC address before discarding header. - */ - port = lport_priv(lp); - 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 - * was done in fcoe_rcv already. - */ - hp = (struct fcoe_hdr *) skb_network_header(skb); - fh = (struct fc_frame_header *) skb_transport_header(skb); - - stats = fc_lport_get_stats(lp); - if (unlikely(FC_FCOE_DECAPS_VER(hp) != FC_FCOE_VER)) { - if (stats->ErrorFrames < 5) - printk(KERN_WARNING "fcoe: FCoE version " - "mismatch: The frame has " - "version %x, but the " - "initiator supports version " - "%x\n", FC_FCOE_DECAPS_VER(hp), - FC_FCOE_VER); - stats->ErrorFrames++; - kfree_skb(skb); - continue; - } - - skb_pull(skb, sizeof(struct fcoe_hdr)); - fr_len = skb->len - sizeof(struct fcoe_crc_eof); - - stats->RxFrames++; - stats->RxWords += fr_len / FCOE_WORD_TO_BYTE; - - fp = (struct fc_frame *)skb; - fc_frame_init(fp); - fr_dev(fp) = lp; - 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); - continue; - } - fr_eof(fp) = crc_eof.fcoe_eof; - fr_crc(fp) = crc_eof.fcoe_crc32; - if (pskb_trim(skb, fr_len)) { - kfree_skb(skb); - continue; - } - - /* - * We only check CRC if no offload is available and if it is - * it's solicited data, in which case, the FCP layer would - * check it during the copy. - */ - if (lp->crc_offload && skb->ip_summed == CHECKSUM_UNNECESSARY) - fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED; - else - 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(lp, fp); - continue; - } - if (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); - continue; - } - fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED; - } - if (unlikely(port->fcoe->ctlr.flogi_oxid != FC_XID_UNKNOWN) && - fcoe_ctlr_recv_flogi(&port->fcoe->ctlr, fp, mac)) { - fc_frame_free(fp); - continue; - } - fc_exch_recv(lp, fp); + fcoe_recv_frame(skb); } return 0; } /** - * fcoe_check_wait_queue() - attempt to clear the transmit backlog - * @lp: the fc_lport + * fcoe_check_wait_queue() - Attempt to clear the transmit backlog + * @lport: The local port whose backlog is to be cleared * - * This empties the wait_queue, dequeue the head of the wait_queue queue - * and calls fcoe_start_io() for each packet, if all skb have been - * transmitted, return qlen or -1 if a error occurs, then restore - * wait_queue and try again later. + * This empties the wait_queue, dequeues the head of the wait_queue queue + * and calls fcoe_start_io() for each packet. If all skb have been + * transmitted it returns the qlen. If an error occurs it restores + * wait_queue (to try again later) and returns -1. * - * The wait_queue is used when the skb transmit fails. skb will go - * in the wait_queue which will be emptied by the timer function or + * The wait_queue is used when the skb transmit fails. The failed skb + * will go in the wait_queue which will be emptied by the timer function or * by the next skb transmit. */ -static void fcoe_check_wait_queue(struct fc_lport *lp, struct sk_buff *skb) +static void fcoe_check_wait_queue(struct fc_lport *lport, struct sk_buff *skb) { - struct fcoe_port *port = lport_priv(lp); + struct fcoe_port *port = lport_priv(lport); int rc; spin_lock_bh(&port->fcoe_pending_queue.lock); @@ -1501,19 +1716,19 @@ static void fcoe_check_wait_queue(struct fc_lport *lp, struct sk_buff *skb) } if (port->fcoe_pending_queue.qlen < FCOE_LOW_QUEUE_DEPTH) - lp->qfull = 0; + lport->qfull = 0; if (port->fcoe_pending_queue.qlen && !timer_pending(&port->timer)) mod_timer(&port->timer, jiffies + 2); port->fcoe_pending_queue_active = 0; out: if (port->fcoe_pending_queue.qlen > FCOE_MAX_QUEUE_DEPTH) - lp->qfull = 1; + lport->qfull = 1; spin_unlock_bh(&port->fcoe_pending_queue.lock); return; } /** - * fcoe_dev_setup() - setup link change notification interface + * fcoe_dev_setup() - Setup the link change notification interface */ static void fcoe_dev_setup(void) { @@ -1521,7 +1736,7 @@ static void fcoe_dev_setup(void) } /** - * fcoe_dev_cleanup() - cleanup link change notification interface + * fcoe_dev_cleanup() - Cleanup the link change notification interface */ static void fcoe_dev_cleanup(void) { @@ -1529,19 +1744,19 @@ static void fcoe_dev_cleanup(void) } /** - * fcoe_device_notification() - netdev event notification callback - * @notifier: context of the notification - * @event: type of event - * @ptr: fixed array for output parsed ifname + * fcoe_device_notification() - Handler for net device events + * @notifier: The context of the notification + * @event: The type of event + * @ptr: The net device that the event was on * - * This function is called by the ethernet driver in case of link change event + * This function is called by the Ethernet driver in case of link change event. * * Returns: 0 for success */ static int fcoe_device_notification(struct notifier_block *notifier, ulong event, void *ptr) { - struct fc_lport *lp = NULL; + struct fc_lport *lport = NULL; struct net_device *netdev = ptr; struct fcoe_interface *fcoe; struct fcoe_port *port; @@ -1552,11 +1767,11 @@ static int fcoe_device_notification(struct notifier_block *notifier, list_for_each_entry(fcoe, &fcoe_hostlist, list) { if (fcoe->netdev == netdev) { - lp = fcoe->ctlr.lp; + lport = fcoe->ctlr.lp; break; } } - if (lp == NULL) { + if (!lport) { rc = NOTIFY_DONE; goto out; } @@ -1570,10 +1785,12 @@ static int fcoe_device_notification(struct notifier_block *notifier, case NETDEV_CHANGE: break; case NETDEV_CHANGEMTU: + if (netdev->features & NETIF_F_FCOE_MTU) + break; mfs = netdev->mtu - (sizeof(struct fcoe_hdr) + sizeof(struct fcoe_crc_eof)); if (mfs >= FC_MIN_MAX_FRAME) - fc_set_mfs(lp, mfs); + fc_set_mfs(lport, mfs); break; case NETDEV_REGISTER: break; @@ -1588,22 +1805,22 @@ static int fcoe_device_notification(struct notifier_block *notifier, FCOE_NETDEV_DBG(netdev, "Unknown event %ld " "from netdev netlink\n", event); } - if (link_possible && !fcoe_link_ok(lp)) + 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(lp); + stats = fc_lport_get_stats(lport); stats->LinkFailureCount++; - fcoe_clean_pending_queue(lp); + fcoe_clean_pending_queue(lport); } out: return rc; } /** - * fcoe_if_to_netdev() - parse a name buffer to get netdev - * @buffer: incoming buffer to be copied + * fcoe_if_to_netdev() - Parse a name buffer to get a net device + * @buffer: The name of the net device * - * Returns: NULL or ptr to net_device + * Returns: NULL or a ptr to net_device */ static struct net_device *fcoe_if_to_netdev(const char *buffer) { @@ -1621,9 +1838,11 @@ static struct net_device *fcoe_if_to_netdev(const char *buffer) } /** - * fcoe_destroy() - handles the destroy from sysfs - * @buffer: expected to be an eth if name - * @kp: associated kernel param + * fcoe_destroy() - Destroy a FCoE interface + * @buffer: The name of the Ethernet interface to be destroyed + * @kp: The associated kernel parameter + * + * Called from sysfs. * * Returns: 0 for success */ @@ -1631,7 +1850,7 @@ static int fcoe_destroy(const char *buffer, struct kernel_param *kp) { struct fcoe_interface *fcoe; struct net_device *netdev; - int rc; + int rc = 0; mutex_lock(&fcoe_config_mutex); #ifdef CONFIG_FCOE_MODULE @@ -1670,6 +1889,10 @@ out_nodev: return rc; } +/** + * fcoe_destroy_work() - Destroy a FCoE port in a deferred work context + * @work: Handle to the FCoE port to be destroyed + */ static void fcoe_destroy_work(struct work_struct *work) { struct fcoe_port *port; @@ -1681,9 +1904,11 @@ static void fcoe_destroy_work(struct work_struct *work) } /** - * fcoe_create() - Handles the create call from sysfs - * @buffer: expected to be an eth if name - * @kp: associated kernel param + * fcoe_create() - Create a fcoe interface + * @buffer: The name of the Ethernet interface to create on + * @kp: The associated kernel param + * + * Called from sysfs. * * Returns: 0 for success */ @@ -1726,7 +1951,7 @@ static int fcoe_create(const char *buffer, struct kernel_param *kp) goto out_putdev; } - lport = fcoe_if_create(fcoe, &netdev->dev); + lport = fcoe_if_create(fcoe, &netdev->dev, 0); if (IS_ERR(lport)) { printk(KERN_ERR "fcoe: Failed to create interface (%s)\n", netdev->name); @@ -1762,16 +1987,9 @@ out_nodev: return rc; } -module_param_call(create, fcoe_create, NULL, NULL, S_IWUSR); -__MODULE_PARM_TYPE(create, "string"); -MODULE_PARM_DESC(create, "Create fcoe fcoe using net device passed in."); -module_param_call(destroy, fcoe_destroy, NULL, NULL, S_IWUSR); -__MODULE_PARM_TYPE(destroy, "string"); -MODULE_PARM_DESC(destroy, "Destroy fcoe fcoe"); - /** - * fcoe_link_ok() - Check if link is ok for the fc_lport - * @lp: ptr to the fc_lport + * 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. @@ -1783,26 +2001,26 @@ MODULE_PARM_DESC(destroy, "Destroy fcoe fcoe"); * Returns: 0 if link is OK for use by FCoE. * */ -int fcoe_link_ok(struct fc_lport *lp) +int fcoe_link_ok(struct fc_lport *lport) { - struct fcoe_port *port = lport_priv(lp); - struct net_device *dev = port->fcoe->netdev; + struct fcoe_port *port = lport_priv(lport); + struct net_device *netdev = port->fcoe->netdev; struct ethtool_cmd ecmd = { ETHTOOL_GSET }; - if ((dev->flags & IFF_UP) && netif_carrier_ok(dev) && - (!dev_ethtool_get_settings(dev, &ecmd))) { - lp->link_supported_speeds &= + if ((netdev->flags & IFF_UP) && netif_carrier_ok(netdev) && + (!dev_ethtool_get_settings(netdev, &ecmd))) { + lport->link_supported_speeds &= ~(FC_PORTSPEED_1GBIT | FC_PORTSPEED_10GBIT); if (ecmd.supported & (SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full)) - lp->link_supported_speeds |= FC_PORTSPEED_1GBIT; + lport->link_supported_speeds |= FC_PORTSPEED_1GBIT; if (ecmd.supported & SUPPORTED_10000baseT_Full) - lp->link_supported_speeds |= + lport->link_supported_speeds |= FC_PORTSPEED_10GBIT; if (ecmd.speed == SPEED_1000) - lp->link_speed = FC_PORTSPEED_1GBIT; + lport->link_speed = FC_PORTSPEED_1GBIT; if (ecmd.speed == SPEED_10000) - lp->link_speed = FC_PORTSPEED_10GBIT; + lport->link_speed = FC_PORTSPEED_10GBIT; return 0; } @@ -1810,8 +2028,8 @@ int fcoe_link_ok(struct fc_lport *lp) } /** - * fcoe_percpu_clean() - Clear the pending skbs for an lport - * @lp: the fc_lport + * fcoe_percpu_clean() - Clear all pending skbs for an local port + * @lport: The local port whose skbs are to be cleared * * Must be called with fcoe_create_mutex held to single-thread completion. * @@ -1820,7 +2038,7 @@ int fcoe_link_ok(struct fc_lport *lp) * there no packets that will be handled by the lport, but also that any * threads already handling packet have returned. */ -void fcoe_percpu_clean(struct fc_lport *lp) +void fcoe_percpu_clean(struct fc_lport *lport) { struct fcoe_percpu_s *pp; struct fcoe_rcv_info *fr; @@ -1838,7 +2056,7 @@ void fcoe_percpu_clean(struct fc_lport *lp) skb = next) { next = skb->next; fr = fcoe_dev_from_skb(skb); - if (fr->fr_dev == lp) { + if (fr->fr_dev == lport) { __skb_unlink(skb, list); kfree_skb(skb); } @@ -1867,13 +2085,11 @@ void fcoe_percpu_clean(struct fc_lport *lp) /** * fcoe_clean_pending_queue() - Dequeue a skb and free it - * @lp: the corresponding fc_lport - * - * Returns: none + * @lport: The local port to dequeue a skb on */ -void fcoe_clean_pending_queue(struct fc_lport *lp) +void fcoe_clean_pending_queue(struct fc_lport *lport) { - struct fcoe_port *port = lport_priv(lp); + struct fcoe_port *port = lport_priv(lport); struct sk_buff *skb; spin_lock_bh(&port->fcoe_pending_queue.lock); @@ -1886,10 +2102,10 @@ void fcoe_clean_pending_queue(struct fc_lport *lp) } /** - * fcoe_reset() - Resets the fcoe - * @shost: shost the reset is from + * fcoe_reset() - Reset a local port + * @shost: The SCSI host associated with the local port to be reset * - * Returns: always 0 + * Returns: Always 0 (return value required by FC transport template) */ int fcoe_reset(struct Scsi_Host *shost) { @@ -1899,30 +2115,33 @@ int fcoe_reset(struct Scsi_Host *shost) } /** - * fcoe_hostlist_lookup_port() - find the corresponding lport by a given device - * @dev: this is currently ptr to net_device + * fcoe_hostlist_lookup_port() - Find the FCoE interface associated with a net device + * @netdev: The net device used as a key + * + * Locking: Must be called with the RNL mutex held. * - * Returns: NULL or the located fcoe_port - * Locking: must be called with the RNL mutex held + * Returns: NULL or the FCoE interface */ static struct fcoe_interface * -fcoe_hostlist_lookup_port(const struct net_device *dev) +fcoe_hostlist_lookup_port(const struct net_device *netdev) { struct fcoe_interface *fcoe; list_for_each_entry(fcoe, &fcoe_hostlist, list) { - if (fcoe->netdev == dev) + if (fcoe->netdev == netdev) return fcoe; } return NULL; } /** - * fcoe_hostlist_lookup() - Find the corresponding lport by netdev - * @netdev: ptr to net_device + * fcoe_hostlist_lookup() - Find the local port associated with a + * given net device + * @netdev: The netdevice used as a key * - * Returns: 0 for success - * Locking: must be called with the RTNL mutex held + * Locking: Must be called with the RTNL mutex held + * + * Returns: NULL or the local port */ static struct fc_lport *fcoe_hostlist_lookup(const struct net_device *netdev) { @@ -1933,11 +2152,13 @@ static struct fc_lport *fcoe_hostlist_lookup(const struct net_device *netdev) } /** - * fcoe_hostlist_add() - Add a lport to lports list - * @lp: ptr to the fc_lport to be added + * fcoe_hostlist_add() - Add the FCoE interface identified by a local + * port to the hostlist + * @lport: The local port that identifies the FCoE interface to be added * - * Returns: 0 for success * Locking: must be called with the RTNL mutex held + * + * Returns: 0 for success */ static int fcoe_hostlist_add(const struct fc_lport *lport) { @@ -1954,15 +2175,15 @@ static int fcoe_hostlist_add(const struct fc_lport *lport) } /** - * fcoe_init() - fcoe module loading initialization + * fcoe_init() - Initialize fcoe.ko * - * Returns 0 on success, negative on failure + * Returns: 0 on success, or a negative value on failure */ static int __init fcoe_init(void) { + struct fcoe_percpu_s *p; unsigned int cpu; int rc = 0; - struct fcoe_percpu_s *p; mutex_lock(&fcoe_config_mutex); @@ -1999,15 +2220,15 @@ out_free: module_init(fcoe_init); /** - * fcoe_exit() - fcoe module unloading cleanup + * fcoe_exit() - Clean up fcoe.ko * - * Returns 0 on success, negative on failure + * Returns: 0 on success or a negative value on failure */ static void __exit fcoe_exit(void) { - unsigned int cpu; struct fcoe_interface *fcoe, *tmp; struct fcoe_port *port; + unsigned int cpu; mutex_lock(&fcoe_config_mutex); @@ -2033,9 +2254,238 @@ static void __exit fcoe_exit(void) /* flush any asyncronous interface destroys, * this should happen after the netdev notifier is unregistered */ flush_scheduled_work(); + /* That will flush out all the N_Ports on the hostlist, but now we + * may have NPIV VN_Ports scheduled for destruction */ + flush_scheduled_work(); /* detach from scsi transport * must happen after all destroys are done, therefor after the flush */ fcoe_if_exit(); } module_exit(fcoe_exit); + +/** + * fcoe_flogi_resp() - FCoE specific FLOGI and FDISC response handler + * @seq: active sequence in the FLOGI or FDISC exchange + * @fp: response frame, or error encoded in a pointer (timeout) + * @arg: pointer the the fcoe_ctlr structure + * + * This handles MAC address managment for FCoE, then passes control on to + * the libfc FLOGI response handler. + */ +static void fcoe_flogi_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg) +{ + struct fcoe_ctlr *fip = arg; + struct fc_exch *exch = fc_seq_exch(seq); + struct fc_lport *lport = exch->lp; + u8 *mac; + + if (IS_ERR(fp)) + goto done; + + mac = fr_cb(fp)->granted_mac; + if (is_zero_ether_addr(mac)) { + /* pre-FIP */ + if (fcoe_ctlr_recv_flogi(fip, lport, fp)) { + fc_frame_free(fp); + return; + } + } + fcoe_update_src_mac(lport, mac); +done: + fc_lport_flogi_resp(seq, fp, lport); +} + +/** + * fcoe_logo_resp() - FCoE specific LOGO response handler + * @seq: active sequence in the LOGO exchange + * @fp: response frame, or error encoded in a pointer (timeout) + * @arg: pointer the the fcoe_ctlr structure + * + * This handles MAC address managment for FCoE, then passes control on to + * the libfc LOGO response handler. + */ +static void fcoe_logo_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg) +{ + struct fc_lport *lport = arg; + static u8 zero_mac[ETH_ALEN] = { 0 }; + + if (!IS_ERR(fp)) + fcoe_update_src_mac(lport, zero_mac); + fc_lport_logo_resp(seq, fp, lport); +} + +/** + * fcoe_elsct_send - FCoE specific ELS handler + * + * This does special case handling of FIP encapsualted ELS exchanges for FCoE, + * using FCoE specific response handlers and passing the FIP controller as + * the argument (the lport is still available from the exchange). + * + * Most of the work here is just handed off to the libfc routine. + */ +static struct fc_seq *fcoe_elsct_send(struct fc_lport *lport, u32 did, + struct fc_frame *fp, unsigned int op, + void (*resp)(struct fc_seq *, + struct fc_frame *, + void *), + void *arg, u32 timeout) +{ + struct fcoe_port *port = lport_priv(lport); + struct fcoe_interface *fcoe = port->fcoe; + struct fcoe_ctlr *fip = &fcoe->ctlr; + struct fc_frame_header *fh = fc_frame_header_get(fp); + + switch (op) { + case ELS_FLOGI: + case ELS_FDISC: + return fc_elsct_send(lport, did, fp, op, fcoe_flogi_resp, + fip, timeout); + case ELS_LOGO: + /* only hook onto fabric logouts, not port logouts */ + if (ntoh24(fh->fh_d_id) != FC_FID_FLOGI) + break; + return fc_elsct_send(lport, did, fp, op, fcoe_logo_resp, + lport, timeout); + } + return fc_elsct_send(lport, did, fp, op, resp, arg, timeout); +} + +/** + * fcoe_vport_create() - create an fc_host/scsi_host for a vport + * @vport: fc_vport object to create a new fc_host for + * @disabled: start the new fc_host in a disabled state by default? + * + * Returns: 0 for success + */ +static int fcoe_vport_create(struct fc_vport *vport, bool disabled) +{ + struct Scsi_Host *shost = vport_to_shost(vport); + struct fc_lport *n_port = shost_priv(shost); + struct fcoe_port *port = lport_priv(n_port); + struct fcoe_interface *fcoe = port->fcoe; + struct net_device *netdev = fcoe->netdev; + struct fc_lport *vn_port; + + mutex_lock(&fcoe_config_mutex); + vn_port = fcoe_if_create(fcoe, &vport->dev, 1); + mutex_unlock(&fcoe_config_mutex); + + if (IS_ERR(vn_port)) { + printk(KERN_ERR "fcoe: fcoe_vport_create(%s) failed\n", + netdev->name); + return -EIO; + } + + if (disabled) { + fc_vport_set_state(vport, FC_VPORT_DISABLED); + } else { + vn_port->boot_time = jiffies; + fc_fabric_login(vn_port); + fc_vport_setlink(vn_port); + } + return 0; +} + +/** + * fcoe_vport_destroy() - destroy the fc_host/scsi_host for a vport + * @vport: fc_vport object that is being destroyed + * + * Returns: 0 for success + */ +static int fcoe_vport_destroy(struct fc_vport *vport) +{ + struct Scsi_Host *shost = vport_to_shost(vport); + struct fc_lport *n_port = shost_priv(shost); + struct fc_lport *vn_port = vport->dd_data; + struct fcoe_port *port = lport_priv(vn_port); + + mutex_lock(&n_port->lp_mutex); + list_del(&vn_port->list); + mutex_unlock(&n_port->lp_mutex); + schedule_work(&port->destroy_work); + return 0; +} + +/** + * fcoe_vport_disable() - change vport state + * @vport: vport to bring online/offline + * @disable: should the vport be disabled? + */ +static int fcoe_vport_disable(struct fc_vport *vport, bool disable) +{ + struct fc_lport *lport = vport->dd_data; + + if (disable) { + fc_vport_set_state(vport, FC_VPORT_DISABLED); + fc_fabric_logoff(lport); + } else { + lport->boot_time = jiffies; + fc_fabric_login(lport); + fc_vport_setlink(lport); + } + + return 0; +} + +/** + * fcoe_vport_set_symbolic_name() - append vport string to symbolic name + * @vport: fc_vport with a new symbolic name string + * + * After generating a new symbolic name string, a new RSPN_ID request is + * sent to the name server. There is no response handler, so if it fails + * for some reason it will not be retried. + */ +static void fcoe_set_vport_symbolic_name(struct fc_vport *vport) +{ + struct fc_lport *lport = vport->dd_data; + struct fc_frame *fp; + size_t len; + + snprintf(fc_host_symbolic_name(lport->host), FC_SYMBOLIC_NAME_SIZE, + "%s v%s over %s : %s", FCOE_NAME, FCOE_VERSION, + fcoe_netdev(lport)->name, vport->symbolic_name); + + if (lport->state != LPORT_ST_READY) + return; + + len = strnlen(fc_host_symbolic_name(lport->host), 255); + fp = fc_frame_alloc(lport, + sizeof(struct fc_ct_hdr) + + sizeof(struct fc_ns_rspn) + len); + if (!fp) + return; + lport->tt.elsct_send(lport, FC_FID_DIR_SERV, fp, FC_NS_RSPN_ID, + NULL, NULL, 3 * lport->r_a_tov); +} + +/** + * fcoe_get_lesb() - Fill the FCoE Link Error Status Block + * @lport: the local port + * @fc_lesb: the link error status block + */ +static void fcoe_get_lesb(struct fc_lport *lport, + struct fc_els_lesb *fc_lesb) +{ + unsigned int cpu; + u32 lfc, vlfc, mdac; + struct fcoe_dev_stats *devst; + struct fcoe_fc_els_lesb *lesb; + struct net_device *netdev = fcoe_netdev(lport); + + lfc = 0; + vlfc = 0; + mdac = 0; + lesb = (struct fcoe_fc_els_lesb *)fc_lesb; + memset(lesb, 0, sizeof(*lesb)); + for_each_possible_cpu(cpu) { + devst = per_cpu_ptr(lport->dev_stats, cpu); + lfc += devst->LinkFailureCount; + vlfc += devst->VLinkFailureCount; + mdac += devst->MissDiscAdvCount; + } + lesb->lesb_link_fail = htonl(lfc); + lesb->lesb_vlink_fail = htonl(vlfc); + lesb->lesb_miss_fka = htonl(mdac); + lesb->lesb_fcs_error = htonl(dev_get_stats(netdev)->rx_crc_errors); +} diff --git a/drivers/scsi/fcoe/fcoe.h b/drivers/scsi/fcoe/fcoe.h index ce7f60fb1bc..c69b2c56c2d 100644 --- a/drivers/scsi/fcoe/fcoe.h +++ b/drivers/scsi/fcoe/fcoe.h @@ -32,7 +32,7 @@ #define FCOE_NAME "fcoe" #define FCOE_VENDOR "Open-FCoE.org" -#define FCOE_MAX_LUN 255 +#define FCOE_MAX_LUN 0xFFFF #define FCOE_MAX_FCP_TARGET 256 #define FCOE_MAX_OUTSTANDING_COMMANDS 1024 @@ -40,11 +40,17 @@ #define FCOE_MIN_XID 0x0000 /* the min xid supported by fcoe_sw */ #define FCOE_MAX_XID 0x0FFF /* the max xid supported by fcoe_sw */ +/* + * Max MTU for FCoE: 14 (FCoE header) + 24 (FC header) + 2112 (max FC payload) + * + 4 (FC CRC) + 4 (FCoE trailer) = 2158 bytes + */ +#define FCOE_MTU 2158 + unsigned int fcoe_debug_logging; module_param_named(debug_logging, fcoe_debug_logging, int, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels"); -#define FCOE_LOGGING 0x01 /* General logging, not categorized */ +#define FCOE_LOGGING 0x01 /* General logging, not categorized */ #define FCOE_NETDEV_LOGGING 0x02 /* Netdevice logging */ #define FCOE_CHECK_LOGGING(LEVEL, CMD) \ @@ -64,8 +70,13 @@ do { \ printk(KERN_INFO "fcoe: %s: " fmt, \ netdev->name, ##args);) -/* - * this percpu struct for fcoe +/** + * struct fcoe_percpu_s - The per-CPU context for FCoE receive threads + * @thread: The thread context + * @fcoe_rx_list: The queue of pending packets to process + * @page: The memory page for calculating frame trailer CRCs + * @crc_eof_offset: The offset into the CRC page pointing to available + * memory for a new trailer */ struct fcoe_percpu_s { struct task_struct *thread; @@ -74,37 +85,62 @@ struct fcoe_percpu_s { int crc_eof_offset; }; -/* - * an FCoE interface, 1:1 with netdev +/** + * struct fcoe_interface - A FCoE interface + * @list: Handle for a list of FCoE interfaces + * @netdev: The associated net device + * @fcoe_packet_type: FCoE packet type + * @fip_packet_type: FIP packet type + * @ctlr: The FCoE controller (for FIP) + * @oem: The offload exchange manager for all local port + * instances associated with this port + * @kref: The kernel reference + * + * This structure is 1:1 with a net devive. */ struct fcoe_interface { - struct list_head list; - struct net_device *netdev; - struct packet_type fcoe_packet_type; - struct packet_type fip_packet_type; - struct fcoe_ctlr ctlr; - struct fc_exch_mgr *oem; /* offload exchange manager */ - struct kref kref; + struct list_head list; + struct net_device *netdev; + struct packet_type fcoe_packet_type; + struct packet_type fip_packet_type; + struct fcoe_ctlr ctlr; + struct fc_exch_mgr *oem; + struct kref kref; }; -/* - * the FCoE private structure that's allocated along with the - * Scsi_Host and libfc fc_lport structures +/** + * struct fcoe_port - The FCoE private structure + * @fcoe: The associated fcoe interface + * @lport: The associated local port + * @fcoe_pending_queue: The pending Rx queue of skbs + * @fcoe_pending_queue_active: Indicates if the pending queue is active + * @timer: The queue timer + * @destroy_work: Handle for work context + * (to prevent RTNL deadlocks) + * @data_srt_addr: Source address for data + * + * An instance of this structure is to be allocated along with the + * Scsi_Host and libfc fc_lport structures. */ struct fcoe_port { struct fcoe_interface *fcoe; - struct fc_lport *lport; - struct sk_buff_head fcoe_pending_queue; - u8 fcoe_pending_queue_active; - struct timer_list timer; /* queue timer */ - struct work_struct destroy_work; /* to prevent rtnl deadlocks */ + struct fc_lport *lport; + struct sk_buff_head fcoe_pending_queue; + u8 fcoe_pending_queue_active; + struct timer_list timer; + struct work_struct destroy_work; + u8 data_src_addr[ETH_ALEN]; }; #define fcoe_from_ctlr(fip) container_of(fip, struct fcoe_interface, ctlr) -static inline struct net_device *fcoe_netdev(const struct fc_lport *lp) +/** + * fcoe_netdev() - Return the net device associated with a local port + * @lport: The local port to get the net device from + */ +static inline struct net_device *fcoe_netdev(const struct fc_lport *lport) { - return ((struct fcoe_port *)lport_priv(lp))->fcoe->netdev; + return ((struct fcoe_port *)lport_priv(lport))->fcoe->netdev; } #endif /* _FCOE_H_ */ diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c index 11ae5c94608..9823291395a 100644 --- a/drivers/scsi/fcoe/libfcoe.c +++ b/drivers/scsi/fcoe/libfcoe.c @@ -59,26 +59,30 @@ unsigned int libfcoe_debug_logging; module_param_named(debug_logging, libfcoe_debug_logging, int, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels"); -#define LIBFCOE_LOGGING 0x01 /* General logging, not categorized */ +#define LIBFCOE_LOGGING 0x01 /* General logging, not categorized */ #define LIBFCOE_FIP_LOGGING 0x02 /* FIP logging */ -#define LIBFCOE_CHECK_LOGGING(LEVEL, CMD) \ -do { \ - if (unlikely(libfcoe_debug_logging & LEVEL)) \ - do { \ - CMD; \ - } while (0); \ +#define LIBFCOE_CHECK_LOGGING(LEVEL, CMD) \ +do { \ + if (unlikely(libfcoe_debug_logging & LEVEL)) \ + do { \ + CMD; \ + } while (0); \ } while (0) #define LIBFCOE_DBG(fmt, args...) \ LIBFCOE_CHECK_LOGGING(LIBFCOE_LOGGING, \ printk(KERN_INFO "libfcoe: " fmt, ##args);) -#define LIBFCOE_FIP_DBG(fmt, args...) \ +#define LIBFCOE_FIP_DBG(fip, fmt, args...) \ LIBFCOE_CHECK_LOGGING(LIBFCOE_FIP_LOGGING, \ - printk(KERN_INFO "fip: " fmt, ##args);) + printk(KERN_INFO "host%d: fip: " fmt, \ + (fip)->lp->host->host_no, ##args);) -/* +/** + * fcoe_ctlr_mtu_valid() - Check if a FCF's MTU is valid + * @fcf: The FCF to check + * * Return non-zero if FCF fcoe_size has been validated. */ static inline int fcoe_ctlr_mtu_valid(const struct fcoe_fcf *fcf) @@ -86,7 +90,10 @@ static inline int fcoe_ctlr_mtu_valid(const struct fcoe_fcf *fcf) return (fcf->flags & FIP_FL_SOL) != 0; } -/* +/** + * fcoe_ctlr_fcf_usable() - Check if a FCF is usable + * @fcf: The FCF to check + * * Return non-zero if the FCF is usable. */ static inline int fcoe_ctlr_fcf_usable(struct fcoe_fcf *fcf) @@ -97,12 +104,13 @@ static inline int fcoe_ctlr_fcf_usable(struct fcoe_fcf *fcf) } /** - * fcoe_ctlr_init() - Initialize the FCoE Controller instance. - * @fip: FCoE controller. + * fcoe_ctlr_init() - Initialize the FCoE Controller instance + * @fip: The FCoE controller to initialize */ void fcoe_ctlr_init(struct fcoe_ctlr *fip) { fip->state = FIP_ST_LINK_WAIT; + fip->mode = FIP_ST_AUTO; INIT_LIST_HEAD(&fip->fcfs); spin_lock_init(&fip->lock); fip->flogi_oxid = FC_XID_UNKNOWN; @@ -114,8 +122,8 @@ void fcoe_ctlr_init(struct fcoe_ctlr *fip) EXPORT_SYMBOL(fcoe_ctlr_init); /** - * fcoe_ctlr_reset_fcfs() - Reset and free all FCFs for a controller. - * @fip: FCoE controller. + * fcoe_ctlr_reset_fcfs() - Reset and free all FCFs for a controller + * @fip: The FCoE controller whose FCFs are to be reset * * Called with &fcoe_ctlr lock held. */ @@ -134,8 +142,8 @@ static void fcoe_ctlr_reset_fcfs(struct fcoe_ctlr *fip) } /** - * fcoe_ctlr_destroy() - Disable and tear-down the FCoE controller. - * @fip: FCoE controller. + * fcoe_ctlr_destroy() - Disable and tear down a FCoE controller + * @fip: The FCoE controller to tear down * * This is called by FCoE drivers before freeing the &fcoe_ctlr. * @@ -148,9 +156,7 @@ static void fcoe_ctlr_reset_fcfs(struct fcoe_ctlr *fip) void fcoe_ctlr_destroy(struct fcoe_ctlr *fip) { cancel_work_sync(&fip->recv_work); - spin_lock_bh(&fip->fip_recv_list.lock); - __skb_queue_purge(&fip->fip_recv_list); - spin_unlock_bh(&fip->fip_recv_list.lock); + skb_queue_purge(&fip->fip_recv_list); spin_lock_bh(&fip->lock); fip->state = FIP_ST_DISABLED; @@ -162,8 +168,8 @@ void fcoe_ctlr_destroy(struct fcoe_ctlr *fip) EXPORT_SYMBOL(fcoe_ctlr_destroy); /** - * fcoe_ctlr_fcoe_size() - Return the maximum FCoE size required for VN_Port. - * @fip: FCoE controller. + * fcoe_ctlr_fcoe_size() - Return the maximum FCoE size required for VN_Port + * @fip: The FCoE controller to get the maximum FCoE size from * * Returns the maximum packet size including the FCoE header and trailer, * but not including any Ethernet or VLAN headers. @@ -180,9 +186,9 @@ static inline u32 fcoe_ctlr_fcoe_size(struct fcoe_ctlr *fip) } /** - * fcoe_ctlr_solicit() - Send a solicitation. - * @fip: FCoE controller. - * @fcf: Destination FCF. If NULL, a multicast solicitation is sent. + * fcoe_ctlr_solicit() - Send a FIP solicitation + * @fip: The FCoE controller to send the solicitation on + * @fcf: The destination FCF (if NULL, a multicast solicitation is sent) */ static void fcoe_ctlr_solicit(struct fcoe_ctlr *fip, struct fcoe_fcf *fcf) { @@ -241,8 +247,8 @@ static void fcoe_ctlr_solicit(struct fcoe_ctlr *fip, struct fcoe_fcf *fcf) } /** - * fcoe_ctlr_link_up() - Start FCoE controller. - * @fip: FCoE controller. + * fcoe_ctlr_link_up() - Start FCoE controller + * @fip: The FCoE controller to start * * Called from the LLD when the network link is ready. */ @@ -255,11 +261,12 @@ void fcoe_ctlr_link_up(struct fcoe_ctlr *fip) spin_unlock_bh(&fip->lock); fc_linkup(fip->lp); } else if (fip->state == FIP_ST_LINK_WAIT) { - fip->state = FIP_ST_AUTO; + fip->state = fip->mode; fip->last_link = 1; fip->link = 1; spin_unlock_bh(&fip->lock); - LIBFCOE_FIP_DBG("%s", "setting AUTO mode.\n"); + if (fip->state == FIP_ST_AUTO) + LIBFCOE_FIP_DBG(fip, "%s", "setting AUTO mode.\n"); fc_linkup(fip->lp); fcoe_ctlr_solicit(fip, NULL); } else @@ -268,45 +275,23 @@ void fcoe_ctlr_link_up(struct fcoe_ctlr *fip) EXPORT_SYMBOL(fcoe_ctlr_link_up); /** - * fcoe_ctlr_reset() - Reset FIP. - * @fip: FCoE controller. - * @new_state: FIP state to be entered. - * - * Returns non-zero if the link was up and now isn't. + * fcoe_ctlr_reset() - Reset a FCoE controller + * @fip: The FCoE controller to reset */ -static int fcoe_ctlr_reset(struct fcoe_ctlr *fip, enum fip_state new_state) +static void fcoe_ctlr_reset(struct fcoe_ctlr *fip) { - struct fc_lport *lp = fip->lp; - int link_dropped; - - spin_lock_bh(&fip->lock); fcoe_ctlr_reset_fcfs(fip); del_timer(&fip->timer); - fip->state = new_state; fip->ctlr_ka_time = 0; fip->port_ka_time = 0; fip->sol_time = 0; fip->flogi_oxid = FC_XID_UNKNOWN; fip->map_dest = 0; - fip->last_link = 0; - link_dropped = fip->link; - fip->link = 0; - spin_unlock_bh(&fip->lock); - - if (link_dropped) - fc_linkdown(lp); - - if (new_state == FIP_ST_ENABLED) { - fcoe_ctlr_solicit(fip, NULL); - fc_linkup(lp); - link_dropped = 0; - } - return link_dropped; } /** - * fcoe_ctlr_link_down() - Stop FCoE controller. - * @fip: FCoE controller. + * fcoe_ctlr_link_down() - Stop a FCoE controller + * @fip: The FCoE controller to be stopped * * Returns non-zero if the link was up and now isn't. * @@ -315,15 +300,29 @@ static int fcoe_ctlr_reset(struct fcoe_ctlr *fip, enum fip_state new_state) */ int fcoe_ctlr_link_down(struct fcoe_ctlr *fip) { - return fcoe_ctlr_reset(fip, FIP_ST_LINK_WAIT); + int link_dropped; + + LIBFCOE_FIP_DBG(fip, "link down.\n"); + spin_lock_bh(&fip->lock); + fcoe_ctlr_reset(fip); + link_dropped = fip->link; + fip->link = 0; + fip->last_link = 0; + fip->state = FIP_ST_LINK_WAIT; + spin_unlock_bh(&fip->lock); + + if (link_dropped) + fc_linkdown(fip->lp); + return link_dropped; } EXPORT_SYMBOL(fcoe_ctlr_link_down); /** - * fcoe_ctlr_send_keep_alive() - Send a keep-alive to the selected FCF. - * @fip: FCoE controller. - * @ports: 0 for controller keep-alive, 1 for port keep-alive. - * @sa: source MAC address. + * fcoe_ctlr_send_keep_alive() - Send a keep-alive to the selected FCF + * @fip: The FCoE controller to send the FKA on + * @lport: libfc fc_lport to send from + * @ports: 0 for controller keep-alive, 1 for port keep-alive + * @sa: The source MAC address * * A controller keep-alive is sent every fka_period (typically 8 seconds). * The source MAC is the native MAC address. @@ -332,7 +331,9 @@ EXPORT_SYMBOL(fcoe_ctlr_link_down); * The source MAC is the assigned mapped source address. * The destination is the FCF's F-port. */ -static void fcoe_ctlr_send_keep_alive(struct fcoe_ctlr *fip, int ports, u8 *sa) +static void fcoe_ctlr_send_keep_alive(struct fcoe_ctlr *fip, + struct fc_lport *lport, + int ports, u8 *sa) { struct sk_buff *skb; struct fip_kal { @@ -350,8 +351,7 @@ static void fcoe_ctlr_send_keep_alive(struct fcoe_ctlr *fip, int ports, u8 *sa) if (!fcf || !fc_host_port_id(lp->host)) return; - len = fcoe_ctlr_fcoe_size(fip) + sizeof(struct ethhdr); - BUG_ON(len < sizeof(*kal) + sizeof(*vn)); + len = sizeof(*kal) + ports * sizeof(*vn); skb = dev_alloc_skb(len); if (!skb) return; @@ -366,7 +366,7 @@ static void fcoe_ctlr_send_keep_alive(struct fcoe_ctlr *fip, int ports, u8 *sa) kal->fip.fip_op = htons(FIP_OP_CTRL); kal->fip.fip_subcode = FIP_SC_KEEP_ALIVE; kal->fip.fip_dl_len = htons((sizeof(kal->mac) + - ports * sizeof(*vn)) / FIP_BPW); + ports * sizeof(*vn)) / FIP_BPW); kal->fip.fip_flags = htons(FIP_FL_FPMA); if (fip->spma) kal->fip.fip_flags |= htons(FIP_FL_SPMA); @@ -374,16 +374,14 @@ static void fcoe_ctlr_send_keep_alive(struct fcoe_ctlr *fip, int ports, u8 *sa) kal->mac.fd_desc.fip_dtype = FIP_DT_MAC; kal->mac.fd_desc.fip_dlen = sizeof(kal->mac) / FIP_BPW; memcpy(kal->mac.fd_mac, fip->ctl_src_addr, ETH_ALEN); - if (ports) { vn = (struct fip_vn_desc *)(kal + 1); vn->fd_desc.fip_dtype = FIP_DT_VN_ID; vn->fd_desc.fip_dlen = sizeof(*vn) / FIP_BPW; - memcpy(vn->fd_mac, fip->data_src_addr, ETH_ALEN); + memcpy(vn->fd_mac, fip->get_src_addr(lport), ETH_ALEN); hton24(vn->fd_fc_id, fc_host_port_id(lp->host)); put_unaligned_be64(lp->wwpn, &vn->fd_wwpn); } - skb_put(skb, len); skb->protocol = htons(ETH_P_FIP); skb_reset_mac_header(skb); @@ -392,10 +390,10 @@ static void fcoe_ctlr_send_keep_alive(struct fcoe_ctlr *fip, int ports, u8 *sa) } /** - * fcoe_ctlr_encaps() - Encapsulate an ELS frame for FIP, without sending it. - * @fip: FCoE controller. - * @dtype: FIP descriptor type for the frame. - * @skb: FCoE ELS frame including FC header but no FCoE headers. + * fcoe_ctlr_encaps() - Encapsulate an ELS frame for FIP, without sending it + * @fip: The FCoE controller for the ELS frame + * @dtype: The FIP descriptor type for the frame + * @skb: The FCoE ELS frame including FC header but no FCoE headers * * Returns non-zero error code on failure. * @@ -405,7 +403,7 @@ static void fcoe_ctlr_send_keep_alive(struct fcoe_ctlr *fip, int ports, u8 *sa) * Headroom includes the FIP encapsulation description, FIP header, and * Ethernet header. The tailroom is for the FIP MAC descriptor. */ -static int fcoe_ctlr_encaps(struct fcoe_ctlr *fip, +static int fcoe_ctlr_encaps(struct fcoe_ctlr *fip, struct fc_lport *lport, u8 dtype, struct sk_buff *skb) { struct fip_encaps_head { @@ -449,8 +447,8 @@ static int fcoe_ctlr_encaps(struct fcoe_ctlr *fip, memset(mac, 0, sizeof(mac)); mac->fd_desc.fip_dtype = FIP_DT_MAC; mac->fd_desc.fip_dlen = sizeof(*mac) / FIP_BPW; - if (dtype != FIP_DT_FLOGI) - memcpy(mac->fd_mac, fip->data_src_addr, ETH_ALEN); + if (dtype != FIP_DT_FLOGI && dtype != FIP_DT_FDISC) + memcpy(mac->fd_mac, fip->get_src_addr(lport), ETH_ALEN); else if (fip->spma) memcpy(mac->fd_mac, fip->ctl_src_addr, ETH_ALEN); @@ -463,6 +461,7 @@ static int fcoe_ctlr_encaps(struct fcoe_ctlr *fip, /** * fcoe_ctlr_els_send() - Send an ELS frame encapsulated by FIP if appropriate. * @fip: FCoE controller. + * @lport: libfc fc_lport to send from * @skb: FCoE ELS frame including FC header but no FCoE headers. * * Returns a non-zero error code if the frame should not be sent. @@ -471,11 +470,13 @@ static int fcoe_ctlr_encaps(struct fcoe_ctlr *fip, * The caller must check that the length is a multiple of 4. * The SKB must have enough headroom (28 bytes) and tailroom (8 bytes). */ -int fcoe_ctlr_els_send(struct fcoe_ctlr *fip, struct sk_buff *skb) +int fcoe_ctlr_els_send(struct fcoe_ctlr *fip, struct fc_lport *lport, + struct sk_buff *skb) { struct fc_frame_header *fh; u16 old_xid; u8 op; + u8 mac[ETH_ALEN]; fh = (struct fc_frame_header *)skb->data; op = *(u8 *)(fh + 1); @@ -498,6 +499,8 @@ int fcoe_ctlr_els_send(struct fcoe_ctlr *fip, struct sk_buff *skb) if (fip->state == FIP_ST_NON_FIP) return 0; + if (!fip->sel_fcf) + goto drop; switch (op) { case ELS_FLOGI: @@ -530,14 +533,15 @@ int fcoe_ctlr_els_send(struct fcoe_ctlr *fip, struct sk_buff *skb) * FLOGI. */ fip->flogi_oxid = FC_XID_UNKNOWN; - fc_fcoe_set_mac(fip->data_src_addr, fh->fh_s_id); + fc_fcoe_set_mac(mac, fh->fh_d_id); + fip->update_mac(lport, mac); return 0; default: if (fip->state != FIP_ST_ENABLED) goto drop; return 0; } - if (fcoe_ctlr_encaps(fip, op, skb)) + if (fcoe_ctlr_encaps(fip, lport, op, skb)) goto drop; fip->send(fip, skb); return -EINPROGRESS; @@ -547,9 +551,9 @@ drop: } EXPORT_SYMBOL(fcoe_ctlr_els_send); -/* - * fcoe_ctlr_age_fcfs() - Reset and free all old FCFs for a controller. - * @fip: FCoE controller. +/** + * fcoe_ctlr_age_fcfs() - Reset and free all old FCFs for a controller + * @fip: The FCoE controller to free FCFs on * * Called with lock held. * @@ -558,14 +562,28 @@ EXPORT_SYMBOL(fcoe_ctlr_els_send); * times its keep-alive period including fuzz. * * In addition, determine the time when an FCF selection can occur. + * + * Also, increment the MissDiscAdvCount when no advertisement is received + * for the corresponding FCF for 1.5 * FKA_ADV_PERIOD (FC-BB-5 LESB). */ static void fcoe_ctlr_age_fcfs(struct fcoe_ctlr *fip) { struct fcoe_fcf *fcf; struct fcoe_fcf *next; unsigned long sel_time = 0; + unsigned long mda_time = 0; list_for_each_entry_safe(fcf, next, &fip->fcfs, list) { + mda_time = fcf->fka_period + (fcf->fka_period >> 1); + if ((fip->sel_fcf == fcf) && + (time_after(jiffies, fcf->time + mda_time))) { + mod_timer(&fip->timer, jiffies + mda_time); + fc_lport_get_stats(fip->lp)->MissDiscAdvCount++; + printk(KERN_INFO "libfcoe: host%d: Missing Discovery " + "Advertisement for fab %llx count %lld\n", + fip->lp->host->host_no, fcf->fabric_name, + fc_lport_get_stats(fip->lp)->MissDiscAdvCount); + } if (time_after(jiffies, fcf->time + fcf->fka_period * 3 + msecs_to_jiffies(FIP_FCF_FUZZ * 3))) { if (fip->sel_fcf == fcf) @@ -574,6 +592,7 @@ static void fcoe_ctlr_age_fcfs(struct fcoe_ctlr *fip) WARN_ON(!fip->fcf_count); fip->fcf_count--; kfree(fcf); + fc_lport_get_stats(fip->lp)->VLinkFailureCount++; } else if (fcoe_ctlr_mtu_valid(fcf) && (!sel_time || time_before(sel_time, fcf->time))) { sel_time = fcf->time; @@ -590,14 +609,16 @@ static void fcoe_ctlr_age_fcfs(struct fcoe_ctlr *fip) } /** - * fcoe_ctlr_parse_adv() - Decode a FIP advertisement into a new FCF entry. - * @skb: received FIP advertisement frame - * @fcf: resulting FCF entry. + * fcoe_ctlr_parse_adv() - Decode a FIP advertisement into a new FCF entry + * @fip: The FCoE controller receiving the advertisement + * @skb: The received FIP advertisement frame + * @fcf: The resulting FCF entry * * Returns zero on a valid parsed advertisement, * otherwise returns non zero value. */ -static int fcoe_ctlr_parse_adv(struct sk_buff *skb, struct fcoe_fcf *fcf) +static int fcoe_ctlr_parse_adv(struct fcoe_ctlr *fip, + struct sk_buff *skb, struct fcoe_fcf *fcf) { struct fip_header *fiph; struct fip_desc *desc = NULL; @@ -636,7 +657,7 @@ static int fcoe_ctlr_parse_adv(struct sk_buff *skb, struct fcoe_fcf *fcf) ((struct fip_mac_desc *)desc)->fd_mac, ETH_ALEN); if (!is_valid_ether_addr(fcf->fcf_mac)) { - LIBFCOE_FIP_DBG("Invalid MAC address " + LIBFCOE_FIP_DBG(fip, "Invalid MAC address " "in FIP adv\n"); return -EINVAL; } @@ -659,6 +680,8 @@ static int fcoe_ctlr_parse_adv(struct sk_buff *skb, struct fcoe_fcf *fcf) if (dlen != sizeof(struct fip_fka_desc)) goto len_err; fka = (struct fip_fka_desc *)desc; + if (fka->fd_flags & FIP_FKA_ADV_D) + fcf->fd_flags = 1; t = ntohl(fka->fd_fka_period); if (t >= FCOE_CTLR_MIN_FKA) fcf->fka_period = msecs_to_jiffies(t); @@ -670,7 +693,7 @@ static int fcoe_ctlr_parse_adv(struct sk_buff *skb, struct fcoe_fcf *fcf) case FIP_DT_LOGO: case FIP_DT_ELP: default: - LIBFCOE_FIP_DBG("unexpected descriptor type %x " + LIBFCOE_FIP_DBG(fip, "unexpected descriptor type %x " "in FIP adv\n", desc->fip_dtype); /* standard says ignore unknown descriptors >= 128 */ if (desc->fip_dtype < FIP_DT_VENDOR_BASE) @@ -687,15 +710,15 @@ static int fcoe_ctlr_parse_adv(struct sk_buff *skb, struct fcoe_fcf *fcf) return 0; len_err: - LIBFCOE_FIP_DBG("FIP length error in descriptor type %x len %zu\n", + LIBFCOE_FIP_DBG(fip, "FIP length error in descriptor type %x len %zu\n", desc->fip_dtype, dlen); return -EINVAL; } /** - * fcoe_ctlr_recv_adv() - Handle an incoming advertisement. - * @fip: FCoE controller. - * @skb: Received FIP packet. + * fcoe_ctlr_recv_adv() - Handle an incoming advertisement + * @fip: The FCoE controller receiving the advertisement + * @skb: The received FIP packet */ static void fcoe_ctlr_recv_adv(struct fcoe_ctlr *fip, struct sk_buff *skb) { @@ -706,7 +729,7 @@ static void fcoe_ctlr_recv_adv(struct fcoe_ctlr *fip, struct sk_buff *skb) int first = 0; int mtu_valid; - if (fcoe_ctlr_parse_adv(skb, &new)) + if (fcoe_ctlr_parse_adv(fip, skb, &new)) return; spin_lock_bh(&fip->lock); @@ -752,7 +775,7 @@ static void fcoe_ctlr_recv_adv(struct fcoe_ctlr *fip, struct sk_buff *skb) mtu_valid = fcoe_ctlr_mtu_valid(fcf); fcf->time = jiffies; if (!found) { - LIBFCOE_FIP_DBG("New FCF for fab %llx map %x val %d\n", + LIBFCOE_FIP_DBG(fip, "New FCF for fab %llx map %x val %d\n", fcf->fabric_name, fcf->fc_map, mtu_valid); } @@ -778,7 +801,7 @@ static void fcoe_ctlr_recv_adv(struct fcoe_ctlr *fip, struct sk_buff *skb) */ if (mtu_valid && !fip->sel_time && fcoe_ctlr_fcf_usable(fcf)) { fip->sel_time = jiffies + - msecs_to_jiffies(FCOE_CTLR_START_DELAY); + msecs_to_jiffies(FCOE_CTLR_START_DELAY); if (!timer_pending(&fip->timer) || time_before(fip->sel_time, fip->timer.expires)) mod_timer(&fip->timer, fip->sel_time); @@ -788,15 +811,15 @@ out: } /** - * fcoe_ctlr_recv_els() - Handle an incoming FIP-encapsulated ELS frame. - * @fip: FCoE controller. - * @skb: Received FIP packet. + * fcoe_ctlr_recv_els() - Handle an incoming FIP encapsulated ELS frame + * @fip: The FCoE controller which received the packet + * @skb: The received FIP packet */ static void fcoe_ctlr_recv_els(struct fcoe_ctlr *fip, struct sk_buff *skb) { - struct fc_lport *lp = fip->lp; + struct fc_lport *lport = fip->lp; struct fip_header *fiph; - struct fc_frame *fp; + struct fc_frame *fp = (struct fc_frame *)skb; struct fc_frame_header *fh = NULL; struct fip_desc *desc; struct fip_encaps *els; @@ -831,10 +854,11 @@ static void fcoe_ctlr_recv_els(struct fcoe_ctlr *fip, struct sk_buff *skb) ((struct fip_mac_desc *)desc)->fd_mac, ETH_ALEN); if (!is_valid_ether_addr(granted_mac)) { - LIBFCOE_FIP_DBG("Invalid MAC address " + LIBFCOE_FIP_DBG(fip, "Invalid MAC address " "in FIP ELS\n"); goto drop; } + memcpy(fr_cb(fp)->granted_mac, granted_mac, ETH_ALEN); break; case FIP_DT_FLOGI: case FIP_DT_FDISC: @@ -850,7 +874,7 @@ static void fcoe_ctlr_recv_els(struct fcoe_ctlr *fip, struct sk_buff *skb) els_dtype = desc->fip_dtype; break; default: - LIBFCOE_FIP_DBG("unexpected descriptor type %x " + LIBFCOE_FIP_DBG(fip, "unexpected descriptor type %x " "in FIP adv\n", desc->fip_dtype); /* standard says ignore unknown descriptors >= 128 */ if (desc->fip_dtype < FIP_DT_VENDOR_BASE) @@ -867,11 +891,8 @@ static void fcoe_ctlr_recv_els(struct fcoe_ctlr *fip, struct sk_buff *skb) if (els_dtype == FIP_DT_FLOGI && sub == FIP_SC_REP && fip->flogi_oxid == ntohs(fh->fh_ox_id) && - els_op == ELS_LS_ACC && is_valid_ether_addr(granted_mac)) { + els_op == ELS_LS_ACC && is_valid_ether_addr(granted_mac)) fip->flogi_oxid = FC_XID_UNKNOWN; - fip->update_mac(fip, fip->data_src_addr, granted_mac); - memcpy(fip->data_src_addr, granted_mac, ETH_ALEN); - } /* * Convert skb into an fc_frame containing only the ELS. @@ -882,32 +903,32 @@ static void fcoe_ctlr_recv_els(struct fcoe_ctlr *fip, struct sk_buff *skb) fc_frame_init(fp); fr_sof(fp) = FC_SOF_I3; fr_eof(fp) = FC_EOF_T; - fr_dev(fp) = lp; + fr_dev(fp) = lport; - stats = fc_lport_get_stats(lp); + stats = fc_lport_get_stats(lport); stats->RxFrames++; stats->RxWords += skb->len / FIP_BPW; - fc_exch_recv(lp, fp); + fc_exch_recv(lport, fp); return; len_err: - LIBFCOE_FIP_DBG("FIP length error in descriptor type %x len %zu\n", + LIBFCOE_FIP_DBG(fip, "FIP length error in descriptor type %x len %zu\n", desc->fip_dtype, dlen); drop: kfree_skb(skb); } /** - * fcoe_ctlr_recv_els() - Handle an incoming link reset frame. - * @fip: FCoE controller. - * @fh: Received FIP header. + * fcoe_ctlr_recv_els() - Handle an incoming link reset frame + * @fip: The FCoE controller that received the frame + * @fh: The received FIP header * * There may be multiple VN_Port descriptors. * The overall length has already been checked. */ static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip, - struct fip_header *fh) + struct fip_header *fh) { struct fip_desc *desc; struct fip_mac_desc *mp; @@ -916,13 +937,13 @@ static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip, size_t rlen; size_t dlen; struct fcoe_fcf *fcf = fip->sel_fcf; - struct fc_lport *lp = fip->lp; + struct fc_lport *lport = fip->lp; u32 desc_mask; - LIBFCOE_FIP_DBG("Clear Virtual Link received\n"); + LIBFCOE_FIP_DBG(fip, "Clear Virtual Link received\n"); if (!fcf) return; - if (!fcf || !fc_host_port_id(lp->host)) + if (!fcf || !fc_host_port_id(lport->host)) return; /* @@ -958,9 +979,10 @@ static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip, if (dlen < sizeof(*vp)) return; if (compare_ether_addr(vp->fd_mac, - fip->data_src_addr) == 0 && - get_unaligned_be64(&vp->fd_wwpn) == lp->wwpn && - ntoh24(vp->fd_fc_id) == fc_host_port_id(lp->host)) + fip->get_src_addr(lport)) == 0 && + get_unaligned_be64(&vp->fd_wwpn) == lport->wwpn && + ntoh24(vp->fd_fc_id) == + fc_host_port_id(lport->host)) desc_mask &= ~BIT(FIP_DT_VN_ID); break; default: @@ -977,33 +999,39 @@ static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip, * reset only if all required descriptors were present and valid. */ if (desc_mask) { - LIBFCOE_FIP_DBG("missing descriptors mask %x\n", desc_mask); + LIBFCOE_FIP_DBG(fip, "missing descriptors mask %x\n", + desc_mask); } else { - LIBFCOE_FIP_DBG("performing Clear Virtual Link\n"); - fcoe_ctlr_reset(fip, FIP_ST_ENABLED); + LIBFCOE_FIP_DBG(fip, "performing Clear Virtual Link\n"); + + spin_lock_bh(&fip->lock); + fc_lport_get_stats(lport)->VLinkFailureCount++; + fcoe_ctlr_reset(fip); + spin_unlock_bh(&fip->lock); + + fc_lport_reset(fip->lp); + fcoe_ctlr_solicit(fip, NULL); } } /** - * fcoe_ctlr_recv() - Receive a FIP frame. - * @fip: FCoE controller. - * @skb: Received FIP packet. + * fcoe_ctlr_recv() - Receive a FIP packet + * @fip: The FCoE controller that received the packet + * @skb: The received FIP packet * - * This is called from NET_RX_SOFTIRQ. + * This may be called from either NET_RX_SOFTIRQ or IRQ. */ void fcoe_ctlr_recv(struct fcoe_ctlr *fip, struct sk_buff *skb) { - spin_lock_bh(&fip->fip_recv_list.lock); - __skb_queue_tail(&fip->fip_recv_list, skb); - spin_unlock_bh(&fip->fip_recv_list.lock); + skb_queue_tail(&fip->fip_recv_list, skb); schedule_work(&fip->recv_work); } EXPORT_SYMBOL(fcoe_ctlr_recv); /** - * fcoe_ctlr_recv_handler() - Receive a FIP frame. - * @fip: FCoE controller. - * @skb: Received FIP packet. + * fcoe_ctlr_recv_handler() - Receive a FIP frame + * @fip: The FCoE controller that received the frame + * @skb: The received FIP frame * * Returns non-zero if the frame is dropped. */ @@ -1038,7 +1066,7 @@ static int fcoe_ctlr_recv_handler(struct fcoe_ctlr *fip, struct sk_buff *skb) fip->map_dest = 0; fip->state = FIP_ST_ENABLED; state = FIP_ST_ENABLED; - LIBFCOE_FIP_DBG("Using FIP mode\n"); + LIBFCOE_FIP_DBG(fip, "Using FIP mode\n"); } spin_unlock_bh(&fip->lock); if (state != FIP_ST_ENABLED) @@ -1060,8 +1088,8 @@ drop: } /** - * fcoe_ctlr_select() - Select the best FCF, if possible. - * @fip: FCoE controller. + * fcoe_ctlr_select() - Select the best FCF (if possible) + * @fip: The FCoE controller * * If there are conflicting advertisements, no FCF can be chosen. * @@ -1073,11 +1101,11 @@ static void fcoe_ctlr_select(struct fcoe_ctlr *fip) struct fcoe_fcf *best = NULL; list_for_each_entry(fcf, &fip->fcfs, list) { - LIBFCOE_FIP_DBG("consider FCF for fab %llx VFID %d map %x " + LIBFCOE_FIP_DBG(fip, "consider FCF for fab %llx VFID %d map %x " "val %d\n", fcf->fabric_name, fcf->vfid, fcf->fc_map, fcoe_ctlr_mtu_valid(fcf)); if (!fcoe_ctlr_fcf_usable(fcf)) { - LIBFCOE_FIP_DBG("FCF for fab %llx map %x %svalid " + LIBFCOE_FIP_DBG(fip, "FCF for fab %llx map %x %svalid " "%savailable\n", fcf->fabric_name, fcf->fc_map, (fcf->flags & FIP_FL_SOL) ? "" : "in", (fcf->flags & FIP_FL_AVAIL) @@ -1091,7 +1119,7 @@ static void fcoe_ctlr_select(struct fcoe_ctlr *fip) if (fcf->fabric_name != best->fabric_name || fcf->vfid != best->vfid || fcf->fc_map != best->fc_map) { - LIBFCOE_FIP_DBG("Conflicting fabric, VFID, " + LIBFCOE_FIP_DBG(fip, "Conflicting fabric, VFID, " "or FC-MAP\n"); return; } @@ -1102,8 +1130,8 @@ static void fcoe_ctlr_select(struct fcoe_ctlr *fip) } /** - * fcoe_ctlr_timeout() - FIP timer function. - * @arg: &fcoe_ctlr pointer. + * fcoe_ctlr_timeout() - FIP timeout handler + * @arg: The FCoE controller that timed out * * Ages FCFs. Triggers FCF selection if possible. Sends keep-alives. */ @@ -1113,8 +1141,6 @@ static void fcoe_ctlr_timeout(unsigned long arg) struct fcoe_fcf *sel; struct fcoe_fcf *fcf; unsigned long next_timer = jiffies + msecs_to_jiffies(FIP_VN_KA_PERIOD); - u8 send_ctlr_ka; - u8 send_port_ka; spin_lock_bh(&fip->lock); if (fip->state == FIP_ST_DISABLED) { @@ -1140,53 +1166,47 @@ static void fcoe_ctlr_timeout(unsigned long arg) fip->lp->host->host_no, sel->fcf_mac); memcpy(fip->dest_addr, sel->fcf_mac, ETH_ALEN); fip->port_ka_time = jiffies + - msecs_to_jiffies(FIP_VN_KA_PERIOD); + msecs_to_jiffies(FIP_VN_KA_PERIOD); fip->ctlr_ka_time = jiffies + sel->fka_period; - fip->link = 1; } else { printk(KERN_NOTICE "libfcoe: host%d: " - "FIP Fibre-Channel Forwarder timed out. " + "FIP Fibre-Channel Forwarder timed out. " "Starting FCF discovery.\n", fip->lp->host->host_no); - fip->link = 0; + fip->reset_req = 1; + schedule_work(&fip->link_work); } - schedule_work(&fip->link_work); } - send_ctlr_ka = 0; - send_port_ka = 0; - if (sel) { + if (sel && !sel->fd_flags) { if (time_after_eq(jiffies, fip->ctlr_ka_time)) { fip->ctlr_ka_time = jiffies + sel->fka_period; - send_ctlr_ka = 1; + fip->send_ctlr_ka = 1; } if (time_after(next_timer, fip->ctlr_ka_time)) next_timer = fip->ctlr_ka_time; if (time_after_eq(jiffies, fip->port_ka_time)) { fip->port_ka_time += jiffies + - msecs_to_jiffies(FIP_VN_KA_PERIOD); - send_port_ka = 1; + msecs_to_jiffies(FIP_VN_KA_PERIOD); + fip->send_port_ka = 1; } if (time_after(next_timer, fip->port_ka_time)) next_timer = fip->port_ka_time; mod_timer(&fip->timer, next_timer); } else if (fip->sel_time) { next_timer = fip->sel_time + - msecs_to_jiffies(FCOE_CTLR_START_DELAY); + msecs_to_jiffies(FCOE_CTLR_START_DELAY); mod_timer(&fip->timer, next_timer); } + if (fip->send_ctlr_ka || fip->send_port_ka) + schedule_work(&fip->link_work); spin_unlock_bh(&fip->lock); - - if (send_ctlr_ka) - fcoe_ctlr_send_keep_alive(fip, 0, fip->ctl_src_addr); - if (send_port_ka) - fcoe_ctlr_send_keep_alive(fip, 1, fip->data_src_addr); } /** - * fcoe_ctlr_link_work() - worker thread function for link changes. - * @work: pointer to link_work member inside &fcoe_ctlr. + * fcoe_ctlr_link_work() - Worker thread function for link changes + * @work: Handle to a FCoE controller * * See if the link status has changed and if so, report it. * @@ -1196,27 +1216,49 @@ static void fcoe_ctlr_timeout(unsigned long arg) static void fcoe_ctlr_link_work(struct work_struct *work) { struct fcoe_ctlr *fip; + struct fc_lport *vport; + u8 *mac; int link; int last_link; + int reset; fip = container_of(work, struct fcoe_ctlr, link_work); spin_lock_bh(&fip->lock); last_link = fip->last_link; link = fip->link; fip->last_link = link; + reset = fip->reset_req; + fip->reset_req = 0; spin_unlock_bh(&fip->lock); if (last_link != link) { if (link) fc_linkup(fip->lp); else - fcoe_ctlr_reset(fip, FIP_ST_LINK_WAIT); + fc_linkdown(fip->lp); + } else if (reset && link) + fc_lport_reset(fip->lp); + + if (fip->send_ctlr_ka) { + fip->send_ctlr_ka = 0; + fcoe_ctlr_send_keep_alive(fip, NULL, 0, fip->ctl_src_addr); + } + if (fip->send_port_ka) { + fip->send_port_ka = 0; + mutex_lock(&fip->lp->lp_mutex); + mac = fip->get_src_addr(fip->lp); + fcoe_ctlr_send_keep_alive(fip, fip->lp, 1, mac); + list_for_each_entry(vport, &fip->lp->vports, list) { + mac = fip->get_src_addr(vport); + fcoe_ctlr_send_keep_alive(fip, vport, 1, mac); + } + mutex_unlock(&fip->lp->lp_mutex); } } /** - * fcoe_ctlr_recv_work() - Worker thread function for receiving FIP frames. - * @recv_work: pointer to recv_work member inside &fcoe_ctlr. + * fcoe_ctlr_recv_work() - Worker thread function for receiving FIP frames + * @recv_work: Handle to a FCoE controller */ static void fcoe_ctlr_recv_work(struct work_struct *recv_work) { @@ -1224,20 +1266,14 @@ static void fcoe_ctlr_recv_work(struct work_struct *recv_work) struct sk_buff *skb; fip = container_of(recv_work, struct fcoe_ctlr, recv_work); - spin_lock_bh(&fip->fip_recv_list.lock); - while ((skb = __skb_dequeue(&fip->fip_recv_list))) { - spin_unlock_bh(&fip->fip_recv_list.lock); + while ((skb = skb_dequeue(&fip->fip_recv_list))) fcoe_ctlr_recv_handler(fip, skb); - spin_lock_bh(&fip->fip_recv_list.lock); - } - spin_unlock_bh(&fip->fip_recv_list.lock); } /** - * fcoe_ctlr_recv_flogi() - snoop Pre-FIP receipt of FLOGI response or request. - * @fip: FCoE controller. - * @fp: FC frame. - * @sa: Ethernet source MAC address from received FCoE frame. + * fcoe_ctlr_recv_flogi() - Snoop pre-FIP receipt of FLOGI response + * @fip: The FCoE controller + * @fp: The FC frame to snoop * * Snoop potential response to FLOGI or even incoming FLOGI. * @@ -1245,15 +1281,18 @@ static void fcoe_ctlr_recv_work(struct work_struct *recv_work) * by fip->flogi_oxid != FC_XID_UNKNOWN. * * The caller is responsible for freeing the frame. + * Fill in the granted_mac address. * * Return non-zero if the frame should not be delivered to libfc. */ -int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *fip, struct fc_frame *fp, u8 *sa) +int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *fip, struct fc_lport *lport, + struct fc_frame *fp) { struct fc_frame_header *fh; u8 op; - u8 mac[ETH_ALEN]; + u8 *sa; + sa = eth_hdr(&fp->skb)->h_source; fh = fc_frame_header_get(fp); if (fh->fh_type != FC_TYPE_ELS) return 0; @@ -1268,7 +1307,8 @@ int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *fip, struct fc_frame *fp, u8 *sa) return -EINVAL; } fip->state = FIP_ST_NON_FIP; - LIBFCOE_FIP_DBG("received FLOGI LS_ACC using non-FIP mode\n"); + LIBFCOE_FIP_DBG(fip, + "received FLOGI LS_ACC using non-FIP mode\n"); /* * FLOGI accepted. @@ -1283,11 +1323,8 @@ int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *fip, struct fc_frame *fp, u8 *sa) fip->map_dest = 0; } fip->flogi_oxid = FC_XID_UNKNOWN; - memcpy(mac, fip->data_src_addr, ETH_ALEN); - fc_fcoe_set_mac(fip->data_src_addr, fh->fh_d_id); spin_unlock_bh(&fip->lock); - - fip->update_mac(fip, mac, fip->data_src_addr); + fc_fcoe_set_mac(fr_cb(fp)->granted_mac, fh->fh_d_id); } else if (op == ELS_FLOGI && fh->fh_r_ctl == FC_RCTL_ELS_REQ && sa) { /* * Save source MAC for point-to-point responses. @@ -1297,7 +1334,7 @@ int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *fip, struct fc_frame *fp, u8 *sa) memcpy(fip->dest_addr, sa, ETH_ALEN); fip->map_dest = 0; if (fip->state == FIP_ST_NON_FIP) - LIBFCOE_FIP_DBG("received FLOGI REQ, " + LIBFCOE_FIP_DBG(fip, "received FLOGI REQ, " "using non-FIP mode\n"); fip->state = FIP_ST_NON_FIP; } @@ -1308,10 +1345,10 @@ int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *fip, struct fc_frame *fp, u8 *sa) EXPORT_SYMBOL(fcoe_ctlr_recv_flogi); /** - * fcoe_wwn_from_mac() - Converts 48-bit IEEE MAC address to 64-bit FC WWN. - * @mac: mac address - * @scheme: check port - * @port: port indicator for converting + * fcoe_wwn_from_mac() - Converts a 48-bit IEEE MAC address to a 64-bit FC WWN + * @mac: The MAC address to convert + * @scheme: The scheme to use when converting + * @port: The port indicator for converting * * Returns: u64 fc world wide name */ @@ -1349,24 +1386,26 @@ u64 fcoe_wwn_from_mac(unsigned char mac[MAX_ADDR_LEN], EXPORT_SYMBOL_GPL(fcoe_wwn_from_mac); /** - * fcoe_libfc_config() - sets up libfc related properties for lport - * @lp: ptr to the fc_lport - * @tt: libfc function template + * fcoe_libfc_config() - Sets up libfc related properties for local port + * @lp: The local port to configure libfc for + * @tt: The libfc function template * * Returns : 0 for success */ -int fcoe_libfc_config(struct fc_lport *lp, struct libfc_function_template *tt) +int fcoe_libfc_config(struct fc_lport *lport, + struct libfc_function_template *tt) { /* Set the function pointers set by the LLDD */ - memcpy(&lp->tt, tt, sizeof(*tt)); - if (fc_fcp_init(lp)) + memcpy(&lport->tt, tt, sizeof(*tt)); + if (fc_fcp_init(lport)) return -ENOMEM; - fc_exch_init(lp); - fc_elsct_init(lp); - fc_lport_init(lp); - fc_rport_init(lp); - fc_disc_init(lp); + fc_exch_init(lport); + fc_elsct_init(lport); + fc_lport_init(lport); + fc_rport_init(lport); + fc_disc_init(lport); return 0; } EXPORT_SYMBOL_GPL(fcoe_libfc_config); + |