diff options
Diffstat (limited to 'drivers/scsi/fcoe/fcoe.c')
-rw-r--r-- | drivers/scsi/fcoe/fcoe.c | 187 |
1 files changed, 137 insertions, 50 deletions
diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index 155d7b9bdea..ba710e350ac 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -99,7 +99,8 @@ 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_ddp_target(struct fc_lport *, u16, struct scatterlist *, + unsigned int); static int fcoe_cpu_callback(struct notifier_block *, unsigned long, void *); static bool fcoe_match(struct net_device *netdev); @@ -143,6 +144,7 @@ static struct libfc_function_template fcoe_libfc_fcn_templ = { .frame_send = fcoe_xmit, .ddp_setup = fcoe_ddp_setup, .ddp_done = fcoe_ddp_done, + .ddp_target = fcoe_ddp_target, .elsct_send = fcoe_elsct_send, .get_lesb = fcoe_get_lesb, .lport_set_port_id = fcoe_set_port_id, @@ -429,21 +431,6 @@ void fcoe_interface_cleanup(struct fcoe_interface *fcoe) struct fcoe_ctlr *fip = &fcoe->ctlr; u8 flogi_maddr[ETH_ALEN]; const struct net_device_ops *ops; - struct fcoe_port *port = lport_priv(fcoe->ctlr.lp); - - FCOE_NETDEV_DBG(netdev, "Destroying interface\n"); - - /* Logout of the fabric */ - fc_fabric_logoff(fcoe->ctlr.lp); - - /* Cleanup the fc_lport */ - fc_lport_destroy(fcoe->ctlr.lp); - - /* Stop the transmit retry timer */ - del_timer_sync(&port->timer); - - /* Free existing transmit skbs */ - fcoe_clean_pending_queue(fcoe->ctlr.lp); /* * Don't listen for Ethernet packets anymore. @@ -466,9 +453,6 @@ void fcoe_interface_cleanup(struct fcoe_interface *fcoe) } else dev_mc_del(netdev, FIP_ALL_ENODE_MACS); - if (!is_zero_ether_addr(port->data_src_addr)) - dev_uc_del(netdev, port->data_src_addr); - /* Tell the LLD we are done w/ FCoE */ ops = netdev->netdev_ops; if (ops->ndo_fcoe_disable) { @@ -476,6 +460,8 @@ void fcoe_interface_cleanup(struct fcoe_interface *fcoe) FCOE_NETDEV_DBG(netdev, "Failed to disable FCoE" " specific feature for LLD.\n"); } + + /* Release the self-reference taken during fcoe_interface_create() */ fcoe_interface_put(fcoe); } @@ -501,6 +487,19 @@ static int fcoe_fip_recv(struct sk_buff *skb, struct net_device *netdev, } /** + * fcoe_port_send() - Send an Ethernet-encapsulated FIP/FCoE frame + * @port: The FCoE port + * @skb: The FIP/FCoE packet to be sent + */ +static void fcoe_port_send(struct fcoe_port *port, struct sk_buff *skb) +{ + if (port->fcoe_pending_queue.qlen) + fcoe_check_wait_queue(port->lport, skb); + else if (fcoe_start_io(skb)) + fcoe_check_wait_queue(port->lport, skb); +} + +/** * fcoe_fip_send() - Send an Ethernet-encapsulated FIP frame * @fip: The FCoE controller * @skb: The FIP packet to be sent @@ -508,7 +507,7 @@ static int fcoe_fip_recv(struct sk_buff *skb, struct net_device *netdev, static void fcoe_fip_send(struct fcoe_ctlr *fip, struct sk_buff *skb) { skb->dev = fcoe_from_ctlr(fip)->netdev; - dev_queue_xmit(skb); + fcoe_port_send(lport_priv(fip->lp), skb); } /** @@ -749,12 +748,27 @@ static int fcoe_shost_config(struct fc_lport *lport, struct device *dev) * The offload EM that this routine is associated with will handle any * packets that are for SCSI read requests. * + * This has been enhanced to work when FCoE stack is operating in target + * mode. + * * Returns: True for read types I/O, otherwise returns false. */ bool fcoe_oem_match(struct fc_frame *fp) { - return fc_fcp_is_read(fr_fsp(fp)) && - (fr_fsp(fp)->data_len > fcoe_ddp_min); + struct fc_frame_header *fh = fc_frame_header_get(fp); + struct fcp_cmnd *fcp; + + if (fc_fcp_is_read(fr_fsp(fp)) && + (fr_fsp(fp)->data_len > fcoe_ddp_min)) + return true; + else if (!(ntoh24(fh->fh_f_ctl) & FC_FC_EX_CTX)) { + fcp = fc_frame_payload_get(fp, sizeof(*fcp)); + if (ntohs(fh->fh_rx_id) == FC_XID_UNKNOWN && + fcp && (ntohl(fcp->fc_dl) > fcoe_ddp_min) && + (fcp->fc_flags & FCP_CFL_WRDATA)) + return true; + } + return false; } /** @@ -844,6 +858,32 @@ skip_oem: */ static void fcoe_if_destroy(struct fc_lport *lport) { + struct fcoe_port *port = lport_priv(lport); + struct fcoe_interface *fcoe = port->priv; + struct net_device *netdev = fcoe->netdev; + + FCOE_NETDEV_DBG(netdev, "Destroying interface\n"); + + /* Logout of the fabric */ + fc_fabric_logoff(lport); + + /* Cleanup the fc_lport */ + fc_lport_destroy(lport); + + /* Stop the transmit retry timer */ + del_timer_sync(&port->timer); + + /* Free existing transmit skbs */ + fcoe_clean_pending_queue(lport); + + rtnl_lock(); + if (!is_zero_ether_addr(port->data_src_addr)) + dev_uc_del(netdev, port->data_src_addr); + rtnl_unlock(); + + /* Release reference held in fcoe_if_create() */ + fcoe_interface_put(fcoe); + /* Free queued packets for the per-CPU receive threads */ fcoe_percpu_clean(lport); @@ -887,6 +927,28 @@ static int fcoe_ddp_setup(struct fc_lport *lport, u16 xid, } /** + * fcoe_ddp_target() - Call a LLD's ddp_target 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 if the DDP context was not configured + */ +static int fcoe_ddp_target(struct fc_lport *lport, u16 xid, + struct scatterlist *sgl, unsigned int sgc) +{ + struct net_device *netdev = fcoe_netdev(lport); + + if (netdev->netdev_ops->ndo_fcoe_ddp_target) + return netdev->netdev_ops->ndo_fcoe_ddp_target(netdev, xid, + sgl, sgc); + + return 0; +} + + +/** * 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 @@ -1206,6 +1268,26 @@ static int fcoe_cpu_callback(struct notifier_block *nfb, } /** + * fcoe_select_cpu() - Selects CPU to handle post-processing of incoming + * command. + * + * This routine selects next CPU based on cpumask to distribute + * incoming requests in round robin. + * + * Returns: int CPU number + */ +static inline unsigned int fcoe_select_cpu(void) +{ + static unsigned int selected_cpu; + + selected_cpu = cpumask_next(selected_cpu, cpu_online_mask); + if (selected_cpu >= nr_cpu_ids) + selected_cpu = cpumask_first(cpu_online_mask); + + return selected_cpu; +} + +/** * fcoe_rcv() - Receive packets from a net device * @skb: The received packet * @netdev: The net device that the packet was received on @@ -1271,18 +1353,25 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *netdev, fr = fcoe_dev_from_skb(skb); fr->fr_dev = lport; - fr->ptype = ptype; /* * In case the incoming frame's exchange is originated from * the initiator, then received frame's exchange id is ANDed * with fc_cpu_mask bits to get the same cpu on which exchange - * was originated, otherwise just use the current cpu. + * was originated, otherwise select cpu using rx exchange id + * or fcoe_select_cpu(). */ if (ntoh24(fh->fh_f_ctl) & FC_FC_EX_CTX) cpu = ntohs(fh->fh_ox_id) & fc_cpu_mask; - else - cpu = smp_processor_id(); + else { + if (ntohs(fh->fh_rx_id) == FC_XID_UNKNOWN) + cpu = fcoe_select_cpu(); + else + cpu = ntohs(fh->fh_rx_id) & fc_cpu_mask; + } + + if (cpu >= nr_cpu_ids) + goto err; fps = &per_cpu(fcoe_percpu, cpu); spin_lock_bh(&fps->fcoe_rx_list.lock); @@ -1482,11 +1571,7 @@ int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp) /* send down to lld */ fr_dev(fp) = lport; - if (port->fcoe_pending_queue.qlen) - fcoe_check_wait_queue(lport, skb); - else if (fcoe_start_io(skb)) - fcoe_check_wait_queue(lport, skb); - + fcoe_port_send(port, skb); return 0; } @@ -1733,7 +1818,6 @@ static int fcoe_device_notification(struct notifier_block *notifier, case NETDEV_UNREGISTER: list_del(&fcoe->list); port = lport_priv(fcoe->ctlr.lp); - fcoe_interface_cleanup(fcoe); queue_work(fcoe_wq, &port->destroy_work); goto out; break; @@ -1827,22 +1911,22 @@ static int fcoe_destroy(struct net_device *netdev) { struct fcoe_interface *fcoe; struct fc_lport *lport; + struct fcoe_port *port; int rc = 0; mutex_lock(&fcoe_config_mutex); rtnl_lock(); fcoe = fcoe_hostlist_lookup_port(netdev); if (!fcoe) { - rtnl_unlock(); rc = -ENODEV; goto out_nodev; } lport = fcoe->ctlr.lp; + port = lport_priv(lport); list_del(&fcoe->list); - fcoe_interface_cleanup(fcoe); - rtnl_unlock(); - fcoe_if_destroy(lport); + queue_work(fcoe_wq, &port->destroy_work); out_nodev: + rtnl_unlock(); mutex_unlock(&fcoe_config_mutex); return rc; } @@ -1854,10 +1938,25 @@ out_nodev: static void fcoe_destroy_work(struct work_struct *work) { struct fcoe_port *port; + struct fcoe_interface *fcoe; + int npiv = 0; port = container_of(work, struct fcoe_port, destroy_work); mutex_lock(&fcoe_config_mutex); + + /* set if this is an NPIV port */ + npiv = port->lport->vport ? 1 : 0; + + fcoe = port->priv; fcoe_if_destroy(port->lport); + + /* Do not tear down the fcoe interface for NPIV port */ + if (!npiv) { + rtnl_lock(); + fcoe_interface_cleanup(fcoe); + rtnl_unlock(); + } + mutex_unlock(&fcoe_config_mutex); } @@ -1886,7 +1985,7 @@ static bool fcoe_match(struct net_device *netdev) */ static int fcoe_create(struct net_device *netdev, enum fip_state fip_mode) { - int rc; + int rc = 0; struct fcoe_interface *fcoe; struct fc_lport *lport; @@ -1911,7 +2010,7 @@ static int fcoe_create(struct net_device *netdev, enum fip_state fip_mode) netdev->name); rc = -EIO; fcoe_interface_cleanup(fcoe); - goto out_free; + goto out_nodev; } /* Make this the "master" N_Port */ @@ -1926,17 +2025,6 @@ static int fcoe_create(struct net_device *netdev, enum fip_state fip_mode) if (!fcoe_link_ok(lport)) fcoe_ctlr_link_up(&fcoe->ctlr); - /* - * Release from init in fcoe_interface_create(), on success lport - * should be holding a reference taken in fcoe_if_create(). - */ - fcoe_interface_put(fcoe); - rtnl_unlock(); - mutex_unlock(&fcoe_config_mutex); - - return 0; -out_free: - fcoe_interface_put(fcoe); out_nodev: rtnl_unlock(); mutex_unlock(&fcoe_config_mutex); @@ -2218,7 +2306,6 @@ static void __exit fcoe_exit(void) list_for_each_entry_safe(fcoe, tmp, &fcoe_hostlist, list) { list_del(&fcoe->list); port = lport_priv(fcoe->ctlr.lp); - fcoe_interface_cleanup(fcoe); queue_work(fcoe_wq, &port->destroy_work); } rtnl_unlock(); |