From 7a7f0c7f7a0cbda062d1ff2ff1d3f99d0e41d2af Mon Sep 17 00:00:00 2001 From: Yi Zou Date: Wed, 29 Jul 2009 17:03:50 -0700 Subject: [SCSI] fcoe: Fix validation of mac address when checking for spma support Fix this bug of validating the wrong mac address while checking for SAN MAC address support from LLD as we should check ha->addr not ctlr.ctl_src_addr. Signed-off-by: Yi Zou Signed-off-by: Robert Love Signed-off-by: James Bottomley Signed-off-by: James Bottomley --- drivers/scsi/fcoe/fcoe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/scsi/fcoe/fcoe.c') diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index 0a5609bb581..b7dfc6ebed3 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -329,7 +329,7 @@ static int fcoe_netdev_config(struct fc_lport *lp, struct net_device *netdev) rcu_read_lock(); for_each_dev_addr(netdev, ha) { if ((ha->type == NETDEV_HW_ADDR_T_SAN) && - (is_valid_ether_addr(fc->ctlr.ctl_src_addr))) { + (is_valid_ether_addr(ha->addr))) { memcpy(fc->ctlr.ctl_src_addr, ha->addr, ETH_ALEN); fc->ctlr.spma = 1; break; -- cgit v1.2.3-70-g09d2 From 2f718d64ecc7010463d36e6ec4ae37778d03fc0b Mon Sep 17 00:00:00 2001 From: Yi Zou Date: Wed, 29 Jul 2009 17:04:01 -0700 Subject: [SCSI] fcoe: Call dev_ethtool_get_settings() in fcoe_link_ok No need to check phys_dev here, just call dev_ethtool_get_settings() directly will take care of this. Signed-off-by: Yi Zou Signed-off-by: Robert Love Signed-off-by: James Bottomley Signed-off-by: James Bottomley --- drivers/scsi/fcoe/fcoe.c | 38 +++++++++++++++++--------------------- 1 file changed, 17 insertions(+), 21 deletions(-) (limited to 'drivers/scsi/fcoe/fcoe.c') diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index b7dfc6ebed3..81601269f4c 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -1635,29 +1635,25 @@ int fcoe_link_ok(struct fc_lport *lp) struct fcoe_softc *fc = lport_priv(lp); struct net_device *dev = fc->real_dev; struct ethtool_cmd ecmd = { ETHTOOL_GSET }; - int rc = 0; - if ((dev->flags & IFF_UP) && netif_carrier_ok(dev)) { - dev = fc->phys_dev; - if (dev->ethtool_ops->get_settings) { - dev->ethtool_ops->get_settings(dev, &ecmd); - lp->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; - if (ecmd.supported & SUPPORTED_10000baseT_Full) - lp->link_supported_speeds |= - FC_PORTSPEED_10GBIT; - if (ecmd.speed == SPEED_1000) - lp->link_speed = FC_PORTSPEED_1GBIT; - if (ecmd.speed == SPEED_10000) - lp->link_speed = FC_PORTSPEED_10GBIT; - } - } else - rc = -1; + if ((dev->flags & IFF_UP) && netif_carrier_ok(dev) && + (!dev_ethtool_get_settings(dev, &ecmd))) { + lp->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; + if (ecmd.supported & SUPPORTED_10000baseT_Full) + lp->link_supported_speeds |= + FC_PORTSPEED_10GBIT; + if (ecmd.speed == SPEED_1000) + lp->link_speed = FC_PORTSPEED_1GBIT; + if (ecmd.speed == SPEED_10000) + lp->link_speed = FC_PORTSPEED_10GBIT; - return rc; + return 0; + } + return -1; } /** -- cgit v1.2.3-70-g09d2 From f161fb72104c7addac3d404a1ff543b2491c1426 Mon Sep 17 00:00:00 2001 From: Joe Eykholt Date: Wed, 29 Jul 2009 17:04:17 -0700 Subject: [SCSI] fcoe: stop delivery of received frames before doing lport_destroy() To be more sure that no more input arrives at the local port as it is being destroyed, clean the queues in the per-cpu receive threads. Signed-off-by: Joe Eykholt Signed-off-by: Robert Love Signed-off-by: James Bottomley Signed-off-by: James Bottomley --- drivers/scsi/fcoe/fcoe.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/scsi/fcoe/fcoe.c') diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index 81601269f4c..14a4017a153 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -466,6 +466,9 @@ static int fcoe_if_destroy(struct net_device *netdev) /* tear-down the FCoE controller */ fcoe_ctlr_destroy(&fc->ctlr); + /* Free queued packets for the per-CPU receive threads */ + fcoe_percpu_clean(lp); + /* Cleanup the fc_lport */ fc_lport_destroy(lp); fc_fcp_destroy(lp); @@ -478,9 +481,6 @@ static int fcoe_if_destroy(struct net_device *netdev) if (lp->emp) fc_exch_mgr_free(lp->emp); - /* Free the per-CPU receive threads */ - fcoe_percpu_clean(lp); - /* Free existing skbs */ fcoe_clean_pending_queue(lp); -- cgit v1.2.3-70-g09d2 From 96316099ac3cb259eac2d6891f3c75b38b29d26e Mon Sep 17 00:00:00 2001 From: Vasu Dev Date: Wed, 29 Jul 2009 17:05:00 -0700 Subject: [SCSI] fcoe, libfc: adds exchange manager(EM) anchor list per lport and related APIs Adds EM list using a anchor struct fc_exch_mgr_anchor, anchor is used to allow same EM instance sharing across more than one lport on a eth device, this implementation is per discussed design posted at http://www.open-fcoe.org/pipermail/devel/2009-June/002566.html. The shared EM is required for multiple lports on eth device when using multiple VLANs or NPIV. Adds fc_exch_mgr_add API to add a EM to the lport and fc_exch_mgr_del API to delete previously added EM. Also adds function fc_exch_mgr_destroy() to destroy allocated EM. The kref is added to the EM to keep track of EM usage count, the EM is destroyed when no longer in use upon kref reaching to zero. The caller can specify match function to fc_exch_mgr_add, this will be used in determining exchange allocation from its EM or not. Moved calling of fcoe_em_config below fcoe_libfc_config calling, so that list head lp->ema_list is initialized before configuring EM. Signed-off-by: Vasu Dev Signed-off-by: Robert Love Signed-off-by: James Bottomley Signed-off-by: James Bottomley --- drivers/scsi/fcoe/fcoe.c | 14 ++++++------- drivers/scsi/libfc/fc_exch.c | 48 +++++++++++++++++++++++++++++++++++++++++++ drivers/scsi/libfc/fc_lport.c | 1 + include/scsi/libfc.h | 24 ++++++++++++++++++++++ 4 files changed, 80 insertions(+), 7 deletions(-) (limited to 'drivers/scsi/fcoe/fcoe.c') diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index 14a4017a153..719a99d4a43 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -603,18 +603,18 @@ static int fcoe_if_create(struct net_device *netdev) goto out_netdev_cleanup; } - /* lport exch manager allocation */ - rc = fcoe_em_config(lp); + /* Initialize the library */ + rc = fcoe_libfc_config(lp, &fcoe_libfc_fcn_templ); if (rc) { - FCOE_NETDEV_DBG(netdev, "Could not configure the EM for the " + FCOE_NETDEV_DBG(netdev, "Could not configure libfc for the " "interface\n"); - goto out_netdev_cleanup; + goto out_lp_destroy; } - /* Initialize the library */ - rc = fcoe_libfc_config(lp, &fcoe_libfc_fcn_templ); + /* lport exch manager allocation */ + rc = fcoe_em_config(lp); if (rc) { - FCOE_NETDEV_DBG(netdev, "Could not configure libfc for the " + FCOE_NETDEV_DBG(netdev, "Could not configure the EM for the " "interface\n"); goto out_lp_destroy; } diff --git a/drivers/scsi/libfc/fc_exch.c b/drivers/scsi/libfc/fc_exch.c index cab54996375..f1fa2b196e9 100644 --- a/drivers/scsi/libfc/fc_exch.c +++ b/drivers/scsi/libfc/fc_exch.c @@ -55,6 +55,7 @@ static struct kmem_cache *fc_em_cachep; /* cache for exchanges */ */ struct fc_exch_mgr { enum fc_class class; /* default class for sequences */ + struct kref kref; /* exchange mgr reference count */ spinlock_t em_lock; /* exchange manager lock, must be taken before ex_lock */ u16 last_xid; /* last allocated exchange ID */ @@ -84,6 +85,12 @@ struct fc_exch_mgr { }; #define fc_seq_exch(sp) container_of(sp, struct fc_exch, seq) +struct fc_exch_mgr_anchor { + struct list_head ema_list; + struct fc_exch_mgr *mp; + bool (*match)(struct fc_frame *); +}; + static void fc_exch_rrq(struct fc_exch *); static void fc_seq_ls_acc(struct fc_seq *); static void fc_seq_ls_rjt(struct fc_seq *, enum fc_els_rjt_reason, @@ -1729,6 +1736,47 @@ reject: fc_frame_free(fp); } +struct fc_exch_mgr_anchor *fc_exch_mgr_add(struct fc_lport *lport, + struct fc_exch_mgr *mp, + bool (*match)(struct fc_frame *)) +{ + struct fc_exch_mgr_anchor *ema; + + ema = kmalloc(sizeof(*ema), GFP_ATOMIC); + if (!ema) + return ema; + + ema->mp = mp; + ema->match = match; + /* add EM anchor to EM anchors list */ + list_add_tail(&ema->ema_list, &lport->ema_list); + kref_get(&mp->kref); + return ema; +} +EXPORT_SYMBOL(fc_exch_mgr_add); + +static void fc_exch_mgr_destroy(struct kref *kref) +{ + struct fc_exch_mgr *mp = container_of(kref, struct fc_exch_mgr, kref); + + /* + * The total exch count must be zero + * before freeing exchange manager. + */ + WARN_ON(mp->total_exches != 0); + mempool_destroy(mp->ep_pool); + kfree(mp); +} + +void fc_exch_mgr_del(struct fc_exch_mgr_anchor *ema) +{ + /* remove EM anchor from EM anchors list */ + list_del(&ema->ema_list); + kref_put(&ema->mp->kref, fc_exch_mgr_destroy); + kfree(ema); +} +EXPORT_SYMBOL(fc_exch_mgr_del); + struct fc_exch_mgr *fc_exch_mgr_alloc(struct fc_lport *lp, enum fc_class class, u16 min_xid, u16 max_xid) diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c index a430335ebf5..ca8ea264b68 100644 --- a/drivers/scsi/libfc/fc_lport.c +++ b/drivers/scsi/libfc/fc_lport.c @@ -1618,6 +1618,7 @@ int fc_lport_init(struct fc_lport *lport) if (lport->link_supported_speeds & FC_PORTSPEED_10GBIT) fc_host_supported_speeds(lport->host) |= FC_PORTSPEED_10GBIT; + INIT_LIST_HEAD(&lport->ema_list); return 0; } EXPORT_SYMBOL(fc_lport_init); diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h index 04db7a9e631..b381b1ca9ae 100644 --- a/include/scsi/libfc.h +++ b/include/scsi/libfc.h @@ -348,6 +348,7 @@ static inline bool fc_fcp_is_read(const struct fc_fcp_pkt *fsp) */ struct fc_exch_mgr; +struct fc_exch_mgr_anchor; /* * Sequence. @@ -709,6 +710,7 @@ struct fc_lport { /* Associations */ struct Scsi_Host *host; struct fc_exch_mgr *emp; + struct list_head ema_list; struct fc_rport *dns_rp; struct fc_rport *ptp_rp; void *scsi_priv; @@ -963,6 +965,28 @@ int fc_elsct_init(struct fc_lport *lp); */ int fc_exch_init(struct fc_lport *lp); +/* + * Adds Exchange Manager (EM) mp to lport. + * + * Adds specified mp to lport using struct fc_exch_mgr_anchor, + * the struct fc_exch_mgr_anchor allows same EM sharing by + * more than one lport with their specified match function, + * the match function is used in allocating exchange from + * added mp. + */ +struct fc_exch_mgr_anchor *fc_exch_mgr_add(struct fc_lport *lport, + struct fc_exch_mgr *mp, + bool (*match)(struct fc_frame *)); + +/* + * Deletes Exchange Manager (EM) from lport by removing + * its anchor ema from lport. + * + * If removed anchor ema was the last user of its associated EM + * then also destroys associated EM. + */ +void fc_exch_mgr_del(struct fc_exch_mgr_anchor *ema); + /* * Allocates an Exchange Manager (EM). * -- cgit v1.2.3-70-g09d2 From 52ff878c912215210f53c0a080552dd6ba3055a2 Mon Sep 17 00:00:00 2001 From: Vasu Dev Date: Wed, 29 Jul 2009 17:05:10 -0700 Subject: [SCSI] fcoe, fnic, libfc: modifies current code paths to use EM anchor list Modifies current code to use EM anchor list in EM allocation, EM free, EM reset, exch allocation and exch lookup code paths. 1. Modifies fc_exch_mgr_alloc to accept EM match function and then have allocated EM added to the lport using fc_exch_mgr_add API while also updating EM kref for newly added EM. 2. Updates fc_exch_mgr_free API to accept only lport pointer instead EM and then have this API free all EMs of the lport from EM anchor list. 3. Removes single lport pointer link from the EM, which was used in associating lport pointer in newly allocated exchange. Instead have lport pointer passed along new exchange allocation call path and then store passed lport pointer in newly allocated exchange, this will allow a single EM instance to be used across more than one lport and used in EM reset to reset only lport specific exchanges. 4. Modifies fc_exch_mgr_reset to reset all EMs from the EM anchor list of the lport, adds additional exch lport pointer (ep->lp) check for shared EM case to reset exchange specific to a lport requested reset. 5. Updates exch allocation API fc_exch_alloc to use EM anchor list and its anchor match func pointer. The fc_exch_alloc will walk the list of EMs until it finds a match, a match will be either null match func pointer or call to match function returning true value. 6. Updates fc_exch_recv to accept incoming frame on local port using only lport pointer and frame pointer without specifying EM instance of incoming frame. Instead modified fc_exch_recv to locate EM for the incoming frame by matching xid of incoming frame against a EM xid range. This change was required to use EM list in libfc Rx path and after this change the lport fc_exch_mgr pointer emp is not needed anymore, so removed emp pointer. 7. Updates fnic for removed lport emp pointer and above modified libfc APIs fc_exch_recv, fc_exch_mgr_alloc and fc_exch_mgr_free. 8. Removes exch_get and exch_put from libfc_function_template as these are no longer needed with EM anchor list and its match function use. Also removes its default function fc_exch_get. A defect this patch introduced regarding the libfc initialization order in the fnic driver was fixed by Joe Eykholt . Signed-off-by: Vasu Dev Signed-off-by: Robert Love Signed-off-by: James Bottomley Signed-off-by: James Bottomley --- drivers/scsi/fcoe/fcoe.c | 16 ++-- drivers/scsi/fcoe/libfcoe.c | 2 +- drivers/scsi/fnic/fnic_fcs.c | 2 +- drivers/scsi/fnic/fnic_main.c | 20 ++--- drivers/scsi/libfc/fc_exch.c | 191 ++++++++++++++++++++++++++---------------- include/scsi/libfc.h | 48 ++--------- 6 files changed, 142 insertions(+), 137 deletions(-) (limited to 'drivers/scsi/fcoe/fcoe.c') diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index 719a99d4a43..ebf2e20370d 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -423,11 +423,8 @@ static int fcoe_shost_config(struct fc_lport *lp, struct Scsi_Host *shost, */ static inline int fcoe_em_config(struct fc_lport *lp) { - BUG_ON(lp->emp); - - lp->emp = fc_exch_mgr_alloc(lp, FC_CLASS_3, - FCOE_MIN_XID, FCOE_MAX_XID); - if (!lp->emp) + if (!fc_exch_mgr_alloc(lp, FC_CLASS_3, FCOE_MIN_XID, + FCOE_MAX_XID, NULL)) return -ENOMEM; return 0; @@ -478,8 +475,7 @@ static int fcoe_if_destroy(struct net_device *netdev) scsi_remove_host(lp->host); /* There are no more rports or I/O, free the EM */ - if (lp->emp) - fc_exch_mgr_free(lp->emp); + fc_exch_mgr_free(lp); /* Free existing skbs */ fcoe_clean_pending_queue(lp); @@ -634,7 +630,7 @@ static int fcoe_if_create(struct net_device *netdev) return rc; out_lp_destroy: - fc_exch_mgr_free(lp->emp); /* Free the EM */ + fc_exch_mgr_free(lp); out_netdev_cleanup: fcoe_netdev_cleanup(fc); out_host_put: @@ -1277,7 +1273,7 @@ int fcoe_percpu_receive_thread(void *arg) 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, lp->emp, fp); + fc_exch_recv(lp, fp); continue; } if (fr_flags(fp) & FCPHF_CRC_UNCHECKED) { @@ -1298,7 +1294,7 @@ int fcoe_percpu_receive_thread(void *arg) fc_frame_free(fp); continue; } - fc_exch_recv(lp, lp->emp, fp); + fc_exch_recv(lp, fp); } return 0; } diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c index 78caa6be113..4db719d6ada 100644 --- a/drivers/scsi/fcoe/libfcoe.c +++ b/drivers/scsi/fcoe/libfcoe.c @@ -885,7 +885,7 @@ static void fcoe_ctlr_recv_els(struct fcoe_ctlr *fip, struct sk_buff *skb) stats->RxFrames++; stats->RxWords += skb->len / FIP_BPW; - fc_exch_recv(lp, lp->emp, fp); + fc_exch_recv(lp, fp); return; len_err: diff --git a/drivers/scsi/fnic/fnic_fcs.c b/drivers/scsi/fnic/fnic_fcs.c index 07e6eedb83c..50db3e36a61 100644 --- a/drivers/scsi/fnic/fnic_fcs.c +++ b/drivers/scsi/fnic/fnic_fcs.c @@ -115,7 +115,7 @@ void fnic_handle_frame(struct work_struct *work) } spin_unlock_irqrestore(&fnic->fnic_lock, flags); - fc_exch_recv(lp, lp->emp, fp); + fc_exch_recv(lp, fp); } } diff --git a/drivers/scsi/fnic/fnic_main.c b/drivers/scsi/fnic/fnic_main.c index 2c266c01dc5..71c7bbe26d0 100644 --- a/drivers/scsi/fnic/fnic_main.c +++ b/drivers/scsi/fnic/fnic_main.c @@ -671,14 +671,6 @@ static int __devinit fnic_probe(struct pci_dev *pdev, lp->link_up = 0; lp->tt = fnic_transport_template; - lp->emp = fc_exch_mgr_alloc(lp, FC_CLASS_3, - FCPIO_HOST_EXCH_RANGE_START, - FCPIO_HOST_EXCH_RANGE_END); - if (!lp->emp) { - err = -ENOMEM; - goto err_out_remove_scsi_host; - } - lp->max_retry_count = fnic->config.flogi_retries; lp->max_rport_retry_count = fnic->config.plogi_retries; lp->service_params = (FCP_SPPF_INIT_FCN | FCP_SPPF_RD_XRDY_DIS | @@ -693,12 +685,18 @@ static int __devinit fnic_probe(struct pci_dev *pdev, fc_set_wwnn(lp, fnic->config.node_wwn); fc_set_wwpn(lp, fnic->config.port_wwn); - fc_exch_init(lp); fc_lport_init(lp); + fc_exch_init(lp); fc_elsct_init(lp); fc_rport_init(lp); fc_disc_init(lp); + if (!fc_exch_mgr_alloc(lp, FC_CLASS_3, FCPIO_HOST_EXCH_RANGE_START, + FCPIO_HOST_EXCH_RANGE_END, NULL)) { + err = -ENOMEM; + goto err_out_remove_scsi_host; + } + fc_lport_config(lp); if (fc_set_mfs(lp, fnic->config.maxdatafieldsize + @@ -738,7 +736,7 @@ static int __devinit fnic_probe(struct pci_dev *pdev, return 0; err_out_free_exch_mgr: - fc_exch_mgr_free(lp->emp); + fc_exch_mgr_free(lp); err_out_remove_scsi_host: fc_remove_host(fnic->lport->host); scsi_remove_host(fnic->lport->host); @@ -827,7 +825,7 @@ static void __devexit fnic_remove(struct pci_dev *pdev) fc_remove_host(fnic->lport->host); scsi_remove_host(fnic->lport->host); - fc_exch_mgr_free(fnic->lport->emp); + fc_exch_mgr_free(fnic->lport); vnic_dev_notify_unset(fnic->vdev); fnic_free_vnic_resources(fnic); fnic_free_intr(fnic); diff --git a/drivers/scsi/libfc/fc_exch.c b/drivers/scsi/libfc/fc_exch.c index 3ad7f88e7ae..324589a5cc0 100644 --- a/drivers/scsi/libfc/fc_exch.c +++ b/drivers/scsi/libfc/fc_exch.c @@ -65,7 +65,6 @@ struct fc_exch_mgr { u16 last_read; /* last xid allocated for read */ u32 total_exches; /* total allocated exchanges */ struct list_head ex_list; /* allocated exchanges list */ - struct fc_lport *lp; /* fc device instance */ mempool_t *ep_pool; /* reserve ep's */ /* @@ -275,8 +274,6 @@ static void fc_exch_release(struct fc_exch *ep) mp = ep->em; if (ep->destructor) ep->destructor(&ep->seq, ep->arg); - if (ep->lp->tt.exch_put) - ep->lp->tt.exch_put(ep->lp, mp, ep->xid); WARN_ON(!(ep->esb_stat & ESB_ST_COMPLETE)); mempool_free(ep, mp->ep_pool); } @@ -513,17 +510,20 @@ static u16 fc_em_alloc_xid(struct fc_exch_mgr *mp, const struct fc_frame *fp) return xid; } -/* - * fc_exch_alloc - allocate an exchange. - * @mp : ptr to the exchange manager - * @xid: input xid +/** + * fc_exch_em_alloc() - allocate an exchange from a specified EM. + * @lport: ptr to the local port + * @mp: ptr to the exchange manager + * @fp: ptr to the FC frame + * @xid: input xid * * if xid is supplied zero then assign next free exchange ID * from exchange manager, otherwise use supplied xid. * Returns with exch lock held. */ -struct fc_exch *fc_exch_alloc(struct fc_exch_mgr *mp, - struct fc_frame *fp, u16 xid) +static struct fc_exch *fc_exch_em_alloc(struct fc_lport *lport, + struct fc_exch_mgr *mp, + struct fc_frame *fp, u16 xid) { struct fc_exch *ep; @@ -566,7 +566,7 @@ struct fc_exch *fc_exch_alloc(struct fc_exch_mgr *mp, */ ep->oxid = ep->xid = xid; ep->em = mp; - ep->lp = mp->lp; + ep->lp = lport; ep->f_ctl = FC_FC_FIRST_SEQ; /* next seq is first seq */ ep->rxid = FC_XID_UNKNOWN; ep->class = mp->class; @@ -579,6 +579,31 @@ err: mempool_free(ep, mp->ep_pool); return NULL; } + +/** + * fc_exch_alloc() - allocate an exchange. + * @lport: ptr to the local port + * @fp: ptr to the FC frame + * + * This function walks the list of the exchange manager(EM) + * anchors to select a EM for new exchange allocation. The + * EM is selected having either a NULL match function pointer + * or call to match function returning true. + */ +struct fc_exch *fc_exch_alloc(struct fc_lport *lport, struct fc_frame *fp) +{ + struct fc_exch_mgr_anchor *ema; + struct fc_exch *ep; + + list_for_each_entry(ema, &lport->ema_list, ema_list) { + if (!ema->match || ema->match(fp)) { + ep = fc_exch_em_alloc(lport, ema->mp, fp, 0); + if (ep) + return ep; + } + } + return NULL; +} EXPORT_SYMBOL(fc_exch_alloc); /* @@ -617,12 +642,14 @@ EXPORT_SYMBOL(fc_exch_done); * Allocate a new exchange as responder. * Sets the responder ID in the frame header. */ -static struct fc_exch *fc_exch_resp(struct fc_exch_mgr *mp, struct fc_frame *fp) +static struct fc_exch *fc_exch_resp(struct fc_lport *lport, + struct fc_exch_mgr *mp, + struct fc_frame *fp) { struct fc_exch *ep; struct fc_frame_header *fh; - ep = mp->lp->tt.exch_get(mp->lp, fp); + ep = fc_exch_alloc(lport, fp); if (ep) { ep->class = fc_frame_class(fp); @@ -648,7 +675,7 @@ static struct fc_exch *fc_exch_resp(struct fc_exch_mgr *mp, struct fc_frame *fp) ep->esb_stat &= ~ESB_ST_SEQ_INIT; fc_exch_hold(ep); /* hold for caller */ - spin_unlock_bh(&ep->ex_lock); /* lock from exch_get */ + spin_unlock_bh(&ep->ex_lock); /* lock from fc_exch_alloc */ } return ep; } @@ -658,7 +685,8 @@ static struct fc_exch *fc_exch_resp(struct fc_exch_mgr *mp, struct fc_frame *fp) * If fc_pf_rjt_reason is FC_RJT_NONE then this function will have a hold * on the ep that should be released by the caller. */ -static enum fc_pf_rjt_reason fc_seq_lookup_recip(struct fc_exch_mgr *mp, +static enum fc_pf_rjt_reason fc_seq_lookup_recip(struct fc_lport *lport, + struct fc_exch_mgr *mp, struct fc_frame *fp) { struct fc_frame_header *fh = fc_frame_header_get(fp); @@ -712,7 +740,7 @@ static enum fc_pf_rjt_reason fc_seq_lookup_recip(struct fc_exch_mgr *mp, reject = FC_RJT_RX_ID; goto rel; } - ep = fc_exch_resp(mp, fp); + ep = fc_exch_resp(lport, mp, fp); if (!ep) { reject = FC_RJT_EXCH_EST; /* XXX */ goto out; @@ -1103,7 +1131,7 @@ static void fc_exch_recv_req(struct fc_lport *lp, struct fc_exch_mgr *mp, enum fc_pf_rjt_reason reject; fr_seq(fp) = NULL; - reject = fc_seq_lookup_recip(mp, fp); + reject = fc_seq_lookup_recip(lp, mp, fp); if (reject == FC_RJT_NONE) { sp = fr_seq(fp); /* sequence will be held */ ep = fc_seq_exch(sp); @@ -1467,29 +1495,34 @@ void fc_exch_mgr_reset(struct fc_lport *lp, u32 sid, u32 did) { struct fc_exch *ep; struct fc_exch *next; - struct fc_exch_mgr *mp = lp->emp; + struct fc_exch_mgr *mp; + struct fc_exch_mgr_anchor *ema; - spin_lock_bh(&mp->em_lock); + list_for_each_entry(ema, &lp->ema_list, ema_list) { + mp = ema->mp; + spin_lock_bh(&mp->em_lock); restart: - list_for_each_entry_safe(ep, next, &mp->ex_list, ex_list) { - if ((sid == 0 || sid == ep->sid) && - (did == 0 || did == ep->did)) { - fc_exch_hold(ep); - spin_unlock_bh(&mp->em_lock); - - fc_exch_reset(ep); - - fc_exch_release(ep); - spin_lock_bh(&mp->em_lock); - - /* - * must restart loop incase while lock was down - * multiple eps were released. - */ - goto restart; + list_for_each_entry_safe(ep, next, &mp->ex_list, ex_list) { + if ((lp == ep->lp) && + (sid == 0 || sid == ep->sid) && + (did == 0 || did == ep->did)) { + fc_exch_hold(ep); + spin_unlock_bh(&mp->em_lock); + + fc_exch_reset(ep); + + fc_exch_release(ep); + spin_lock_bh(&mp->em_lock); + + /* + * must restart loop incase while lock + * was down multiple eps were released. + */ + goto restart; + } } + spin_unlock_bh(&mp->em_lock); } - spin_unlock_bh(&mp->em_lock); } EXPORT_SYMBOL(fc_exch_mgr_reset); @@ -1778,7 +1811,8 @@ EXPORT_SYMBOL(fc_exch_mgr_del); struct fc_exch_mgr *fc_exch_mgr_alloc(struct fc_lport *lp, enum fc_class class, - u16 min_xid, u16 max_xid) + u16 min_xid, u16 max_xid, + bool (*match)(struct fc_frame *)) { struct fc_exch_mgr *mp; size_t len; @@ -1803,7 +1837,6 @@ struct fc_exch_mgr *fc_exch_mgr_alloc(struct fc_lport *lp, mp->class = class; mp->total_exches = 0; mp->exches = (struct fc_exch **)(mp + 1); - mp->lp = lp; /* adjust em exch xid range for offload */ mp->min_xid = min_xid; mp->max_xid = max_xid; @@ -1826,6 +1859,18 @@ struct fc_exch_mgr *fc_exch_mgr_alloc(struct fc_lport *lp, if (!mp->ep_pool) goto free_mp; + kref_init(&mp->kref); + if (!fc_exch_mgr_add(lp, mp, match)) { + mempool_destroy(mp->ep_pool); + goto free_mp; + } + + /* + * Above kref_init() sets mp->kref to 1 and then + * call to fc_exch_mgr_add incremented mp->kref again, + * so adjust that extra increment. + */ + kref_put(&mp->kref, fc_exch_mgr_destroy); return mp; free_mp: @@ -1834,27 +1879,15 @@ free_mp: } EXPORT_SYMBOL(fc_exch_mgr_alloc); -void fc_exch_mgr_free(struct fc_exch_mgr *mp) +void fc_exch_mgr_free(struct fc_lport *lport) { - WARN_ON(!mp); - /* - * The total exch count must be zero - * before freeing exchange manager. - */ - WARN_ON(mp->total_exches != 0); - mempool_destroy(mp->ep_pool); - kfree(mp); + struct fc_exch_mgr_anchor *ema, *next; + + list_for_each_entry_safe(ema, next, &lport->ema_list, ema_list) + fc_exch_mgr_del(ema); } EXPORT_SYMBOL(fc_exch_mgr_free); -struct fc_exch *fc_exch_get(struct fc_lport *lp, struct fc_frame *fp) -{ - if (!lp || !lp->emp) - return NULL; - - return fc_exch_alloc(lp->emp, fp, 0); -} -EXPORT_SYMBOL(fc_exch_get); struct fc_seq *fc_exch_seq_send(struct fc_lport *lp, struct fc_frame *fp, @@ -1869,7 +1902,7 @@ struct fc_seq *fc_exch_seq_send(struct fc_lport *lp, struct fc_frame_header *fh; int rc = 1; - ep = lp->tt.exch_get(lp, fp); + ep = fc_exch_alloc(lp, fp); if (!ep) { fc_frame_free(fp); return NULL; @@ -1914,24 +1947,44 @@ EXPORT_SYMBOL(fc_exch_seq_send); /* * Receive a frame */ -void fc_exch_recv(struct fc_lport *lp, struct fc_exch_mgr *mp, - struct fc_frame *fp) +void fc_exch_recv(struct fc_lport *lp, struct fc_frame *fp) { struct fc_frame_header *fh = fc_frame_header_get(fp); - u32 f_ctl; + struct fc_exch_mgr_anchor *ema; + u32 f_ctl, found = 0; + u16 oxid; /* lport lock ? */ - if (!lp || !mp || lp->state == LPORT_ST_DISABLED) { + if (!lp || lp->state == LPORT_ST_DISABLED) { FC_LPORT_DBG(lp, "Receiving frames for an lport that " "has not been initialized correctly\n"); fc_frame_free(fp); return; } + f_ctl = ntoh24(fh->fh_f_ctl); + oxid = ntohs(fh->fh_ox_id); + if (f_ctl & FC_FC_EX_CTX) { + list_for_each_entry(ema, &lp->ema_list, ema_list) { + if ((oxid >= ema->mp->min_xid) && + (oxid <= ema->mp->max_xid)) { + found = 1; + break; + } + } + + if (!found) { + FC_LPORT_DBG(lp, "Received response for out " + "of range oxid:%hx\n", oxid); + fc_frame_free(fp); + return; + } + } else + ema = list_entry(lp->ema_list.prev, typeof(*ema), ema_list); + /* * If frame is marked invalid, just drop it. */ - f_ctl = ntoh24(fh->fh_f_ctl); switch (fr_eof(fp)) { case FC_EOF_T: if (f_ctl & FC_FC_END_SEQ) @@ -1939,34 +1992,24 @@ void fc_exch_recv(struct fc_lport *lp, struct fc_exch_mgr *mp, /* fall through */ case FC_EOF_N: if (fh->fh_type == FC_TYPE_BLS) - fc_exch_recv_bls(mp, fp); + fc_exch_recv_bls(ema->mp, fp); else if ((f_ctl & (FC_FC_EX_CTX | FC_FC_SEQ_CTX)) == FC_FC_EX_CTX) - fc_exch_recv_seq_resp(mp, fp); + fc_exch_recv_seq_resp(ema->mp, fp); else if (f_ctl & FC_FC_SEQ_CTX) - fc_exch_recv_resp(mp, fp); + fc_exch_recv_resp(ema->mp, fp); else - fc_exch_recv_req(lp, mp, fp); + fc_exch_recv_req(lp, ema->mp, fp); break; default: FC_LPORT_DBG(lp, "dropping invalid frame (eof %x)", fr_eof(fp)); fc_frame_free(fp); - break; } } EXPORT_SYMBOL(fc_exch_recv); int fc_exch_init(struct fc_lport *lp) { - if (!lp->tt.exch_get) { - /* - * exch_put() should be NULL if - * exch_get() is NULL - */ - WARN_ON(lp->tt.exch_put); - lp->tt.exch_get = fc_exch_get; - } - if (!lp->tt.seq_start_next) lp->tt.seq_start_next = fc_seq_start_next; diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h index f1bde91f98a..c2b928cfafb 100644 --- a/include/scsi/libfc.h +++ b/include/scsi/libfc.h @@ -517,25 +517,6 @@ struct libfc_function_template { */ void (*exch_done)(struct fc_seq *sp); - /* - * Assigns a EM and a free XID for an new exchange and then - * allocates a new exchange and sequence pair. - * The fp can be used to determine free XID. - * - * STATUS: OPTIONAL - */ - struct fc_exch *(*exch_get)(struct fc_lport *lp, struct fc_frame *fp); - - /* - * Release previously assigned XID by exch_get API. - * The LLD may implement this if XID is assigned by LLD - * in exch_get(). - * - * STATUS: OPTIONAL - */ - void (*exch_put)(struct fc_lport *lp, struct fc_exch_mgr *mp, - u16 ex_id); - /* * Start a new sequence on the same exchange/sequence tuple. * @@ -703,7 +684,6 @@ struct fc_lport { /* Associations */ struct Scsi_Host *host; - struct fc_exch_mgr *emp; struct list_head ema_list; struct fc_rport *dns_rp; struct fc_rport *ptp_rp; @@ -996,27 +976,25 @@ void fc_exch_mgr_del(struct fc_exch_mgr_anchor *ema); * a new exchange. * The LLD may choose to have multiple EMs, * e.g. one EM instance per CPU receive thread in LLD. - * The LLD can use exch_get() of struct libfc_function_template - * to specify XID for a new exchange within - * a specified EM instance. * - * The em_idx to uniquely identify an EM instance. + * Specified match function is used in allocating exchanges + * from newly allocated EM. */ struct fc_exch_mgr *fc_exch_mgr_alloc(struct fc_lport *lp, enum fc_class class, u16 min_xid, - u16 max_xid); + u16 max_xid, + bool (*match)(struct fc_frame *)); /* - * Free an exchange manager. + * Free all exchange managers of a lport. */ -void fc_exch_mgr_free(struct fc_exch_mgr *mp); +void fc_exch_mgr_free(struct fc_lport *lport); /* * Receive a frame on specified local port and exchange manager. */ -void fc_exch_recv(struct fc_lport *lp, struct fc_exch_mgr *mp, - struct fc_frame *fp); +void fc_exch_recv(struct fc_lport *lp, struct fc_frame *fp); /* * This function is for exch_seq_send function pointer in @@ -1057,20 +1035,10 @@ int fc_seq_exch_abort(const struct fc_seq *req_sp, unsigned int timer_msec); */ void fc_exch_done(struct fc_seq *sp); -/* - * Assigns a EM and XID for a frame and then allocates - * a new exchange and sequence pair. - * The fp can be used to determine free XID. - */ -struct fc_exch *fc_exch_get(struct fc_lport *lp, struct fc_frame *fp); - /* * Allocate a new exchange and sequence pair. - * if ex_id is zero then next free exchange id - * from specified exchange manger mp will be assigned. */ -struct fc_exch *fc_exch_alloc(struct fc_exch_mgr *mp, - struct fc_frame *fp, u16 ex_id); +struct fc_exch *fc_exch_alloc(struct fc_lport *lport, struct fc_frame *fp); /* * Start a new sequence on the same exchange as the supplied sequence. */ -- cgit v1.2.3-70-g09d2 From e8af4d4380babc89d193c16163f070a6418f033b Mon Sep 17 00:00:00 2001 From: Vasu Dev Date: Wed, 29 Jul 2009 17:05:15 -0700 Subject: [SCSI] fcoe: modifies fcoe_hostlist_lock uses as prep work to add shared offload EM Modifies fcoe_hostlist_lock uses such that a new EM allocation in fcoe_em_config and adding new fcoe_softc using fcoe_hostlist_add are atomic, this is to ensure that a shared offload EM gets allocated only once per eth device for its all lports. Signed-off-by: Vasu Dev Signed-off-by: Robert Love Signed-off-by: James Bottomley Signed-off-by: James Bottomley --- drivers/scsi/fcoe/fcoe.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) (limited to 'drivers/scsi/fcoe/fcoe.c') diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index ebf2e20370d..86410b9a30c 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -419,6 +419,8 @@ static int fcoe_shost_config(struct fc_lport *lp, struct Scsi_Host *shost, * fcoe_em_config() - allocates em for this lport * @lp: the port that em is to allocated for * + * Called with write fcoe_hostlist_lock held. + * * Returns : 0 on success */ static inline int fcoe_em_config(struct fc_lport *lp) @@ -607,6 +609,13 @@ static int fcoe_if_create(struct net_device *netdev) goto out_lp_destroy; } + /* + * fcoe_em_alloc() and fcoe_hostlist_add() both + * need to be atomic under fcoe_hostlist_lock + * since fcoe_em_alloc() looks for an existing EM + * instance on host list updated by fcoe_hostlist_add(). + */ + write_lock(&fcoe_hostlist_lock); /* lport exch manager allocation */ rc = fcoe_em_config(lp); if (rc) { @@ -617,6 +626,7 @@ static int fcoe_if_create(struct net_device *netdev) /* add to lports list */ fcoe_hostlist_add(lp); + write_unlock(&fcoe_hostlist_lock); lp->boot_time = jiffies; @@ -1720,6 +1730,8 @@ int fcoe_reset(struct Scsi_Host *shost) * fcoe_hostlist_lookup_softc() - find the corresponding lport by a given device * @dev: this is currently ptr to net_device * + * Called with fcoe_hostlist_lock held. + * * Returns: NULL or the located fcoe_softc */ static struct fcoe_softc * @@ -1727,14 +1739,10 @@ fcoe_hostlist_lookup_softc(const struct net_device *dev) { struct fcoe_softc *fc; - read_lock(&fcoe_hostlist_lock); list_for_each_entry(fc, &fcoe_hostlist, list) { - if (fc->real_dev == dev) { - read_unlock(&fcoe_hostlist_lock); + if (fc->real_dev == dev) return fc; - } } - read_unlock(&fcoe_hostlist_lock); return NULL; } @@ -1748,7 +1756,9 @@ struct fc_lport *fcoe_hostlist_lookup(const struct net_device *netdev) { struct fcoe_softc *fc; + read_lock(&fcoe_hostlist_lock); fc = fcoe_hostlist_lookup_softc(netdev); + read_unlock(&fcoe_hostlist_lock); return (fc) ? fc->ctlr.lp : NULL; } @@ -1757,6 +1767,8 @@ 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 * + * Called with write fcoe_hostlist_lock held. + * * Returns: 0 for success */ int fcoe_hostlist_add(const struct fc_lport *lp) @@ -1766,9 +1778,7 @@ int fcoe_hostlist_add(const struct fc_lport *lp) fc = fcoe_hostlist_lookup_softc(fcoe_netdev(lp)); if (!fc) { fc = lport_priv(lp); - write_lock_bh(&fcoe_hostlist_lock); list_add_tail(&fc->list, &fcoe_hostlist); - write_unlock_bh(&fcoe_hostlist_lock); } return 0; } @@ -1783,9 +1793,9 @@ int fcoe_hostlist_remove(const struct fc_lport *lp) { struct fcoe_softc *fc; + write_lock_bh(&fcoe_hostlist_lock); fc = fcoe_hostlist_lookup_softc(fcoe_netdev(lp)); BUG_ON(!fc); - write_lock_bh(&fcoe_hostlist_lock); list_del(&fc->list); write_unlock_bh(&fcoe_hostlist_lock); -- cgit v1.2.3-70-g09d2 From d7179680d04f1e196b7a5f70e7f93bb1850407c6 Mon Sep 17 00:00:00 2001 From: Vasu Dev Date: Wed, 29 Jul 2009 17:05:21 -0700 Subject: [SCSI] fcoe, libfc: adds offload EM per eth device with only single xid range per EM Updates fcoe_em_config to allocate a single instance of sharable offload EM for supported lp->lro_xid per eth device, and then share this EM for subsequently more lports creation on same eth device (e.g when using VLAN). Adds tiny fcoe_oem_match function for offload EM to return true for read types IO to have read IO exchanges allocated from offload shared EM. Removes fc_em_alloc_xid function completely which was needed to manage two xid ranges within a EM, this is not needed any more with allocation of separate sharable offload EM per eth device. Instead this patch adds simple xid allocation logic to manage single xid range. Adds fc_exch_em_alloc with mp->next_xid as cursor to allocate new xid from single xid range of EM, uses mp->next_xid instead removed mp->last_xid which slightly increase probability of finding empty xid on exch allocation. Removes restriction of not allowing use of xid zero along with changing two xid range change to single xid range. Makes fc_fcp_ddp_setup calling conditional to only xid allocated from shared offload EM. Signed-off-by: Vasu Dev Signed-off-by: Robert Love Signed-off-by: James Bottomley Signed-off-by: James Bottomley --- drivers/scsi/fcoe/fcoe.c | 67 ++++++++++++++++++++++++++++++- drivers/scsi/fcoe/fcoe.h | 3 +- drivers/scsi/libfc/fc_exch.c | 94 +++++++++----------------------------------- 3 files changed, 85 insertions(+), 79 deletions(-) (limited to 'drivers/scsi/fcoe/fcoe.c') diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index 86410b9a30c..0306aa95eb3 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -415,6 +415,17 @@ static int fcoe_shost_config(struct fc_lport *lp, struct Scsi_Host *shost, return 0; } +/* + * fcoe_oem_match() - match for read types IO + * @fp: the fc_frame for new IO. + * + * Returns : true for read types IO, otherwise returns false. + */ +bool fcoe_oem_match(struct fc_frame *fp) +{ + return fc_fcp_is_read(fr_fsp(fp)); +} + /** * fcoe_em_config() - allocates em for this lport * @lp: the port that em is to allocated for @@ -425,9 +436,61 @@ static int fcoe_shost_config(struct fc_lport *lp, struct Scsi_Host *shost, */ static inline int fcoe_em_config(struct fc_lport *lp) { - if (!fc_exch_mgr_alloc(lp, FC_CLASS_3, FCOE_MIN_XID, - FCOE_MAX_XID, NULL)) + struct fcoe_softc *fc = lport_priv(lp); + struct fcoe_softc *oldfc = NULL; + u16 min_xid = FCOE_MIN_XID; + u16 max_xid = FCOE_MAX_XID; + + /* + * 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; + goto skip_oem; + } + + /* + * Reuse existing offload em instance in case + * it is already allocated on phys_dev. + */ + list_for_each_entry(oldfc, &fcoe_hostlist, list) { + if (oldfc->phys_dev == fc->phys_dev) { + fc->oem = oldfc->oem; + break; + } + } + + if (fc->oem) { + if (!fc_exch_mgr_add(lp, fc->oem, fcoe_oem_match)) { + printk(KERN_ERR "fcoe_em_config: failed to add " + "offload em:%p on interface:%s\n", + fc->oem, fc->real_dev->name); + return -ENOMEM; + } + } else { + fc->oem = fc_exch_mgr_alloc(lp, FC_CLASS_3, + FCOE_MIN_XID, lp->lro_xid, + fcoe_oem_match); + if (!fc->oem) { + printk(KERN_ERR "fcoe_em_config: failed to allocate " + "em for offload exches on interface:%s\n", + fc->real_dev->name); + return -ENOMEM; + } + } + + /* + * Exclude offload EM xid range from next EM xid range. + */ + min_xid += lp->lro_xid + 1; + +skip_oem: + if (!fc_exch_mgr_alloc(lp, FC_CLASS_3, min_xid, max_xid, NULL)) { + printk(KERN_ERR "fcoe_em_config: failed to " + "allocate em on interface %s\n", fc->real_dev->name); return -ENOMEM; + } return 0; } diff --git a/drivers/scsi/fcoe/fcoe.h b/drivers/scsi/fcoe/fcoe.h index 0d724fa0898..6905efc166b 100644 --- a/drivers/scsi/fcoe/fcoe.h +++ b/drivers/scsi/fcoe/fcoe.h @@ -37,7 +37,7 @@ #define FCOE_MAX_OUTSTANDING_COMMANDS 1024 -#define FCOE_MIN_XID 0x0001 /* the min xid supported by fcoe_sw */ +#define FCOE_MIN_XID 0x0000 /* the min xid supported by fcoe_sw */ #define FCOE_MAX_XID 0x07ef /* the max xid supported by fcoe_sw */ unsigned int fcoe_debug_logging; @@ -81,6 +81,7 @@ struct fcoe_softc { struct list_head list; struct net_device *real_dev; struct net_device *phys_dev; /* device with ethtool_ops */ + struct fc_exch_mgr *oem; /* offload exchange manger */ struct packet_type fcoe_packet_type; struct packet_type fip_packet_type; struct sk_buff_head fcoe_pending_queue; diff --git a/drivers/scsi/libfc/fc_exch.c b/drivers/scsi/libfc/fc_exch.c index 324589a5cc0..11ddd115efb 100644 --- a/drivers/scsi/libfc/fc_exch.c +++ b/drivers/scsi/libfc/fc_exch.c @@ -58,7 +58,7 @@ struct fc_exch_mgr { struct kref kref; /* exchange mgr reference count */ spinlock_t em_lock; /* exchange manager lock, must be taken before ex_lock */ - u16 last_xid; /* last allocated exchange ID */ + u16 next_xid; /* next possible free exchange ID */ u16 min_xid; /* min exchange ID */ u16 max_xid; /* max exchange ID */ u16 max_read; /* max exchange ID for read */ @@ -464,68 +464,21 @@ static struct fc_seq *fc_seq_alloc(struct fc_exch *ep, u8 seq_id) return sp; } -/* - * fc_em_alloc_xid - returns an xid based on request type - * @lp : ptr to associated lport - * @fp : ptr to the assocated frame - * - * check the associated fc_fsp_pkt to get scsi command type and - * command direction to decide from which range this exch id - * will be allocated from. - * - * Returns : 0 or an valid xid - */ -static u16 fc_em_alloc_xid(struct fc_exch_mgr *mp, const struct fc_frame *fp) -{ - u16 xid, min, max; - u16 *plast; - struct fc_exch *ep = NULL; - - if (mp->max_read) { - if (fc_fcp_is_read(fr_fsp(fp))) { - min = mp->min_xid; - max = mp->max_read; - plast = &mp->last_read; - } else { - min = mp->max_read + 1; - max = mp->max_xid; - plast = &mp->last_xid; - } - } else { - min = mp->min_xid; - max = mp->max_xid; - plast = &mp->last_xid; - } - xid = *plast; - do { - xid = (xid == max) ? min : xid + 1; - ep = mp->exches[xid - mp->min_xid]; - } while ((ep != NULL) && (xid != *plast)); - - if (unlikely(ep)) - xid = 0; - else - *plast = xid; - - return xid; -} - /** * fc_exch_em_alloc() - allocate an exchange from a specified EM. * @lport: ptr to the local port * @mp: ptr to the exchange manager - * @fp: ptr to the FC frame - * @xid: input xid * - * if xid is supplied zero then assign next free exchange ID - * from exchange manager, otherwise use supplied xid. - * Returns with exch lock held. + * Returns pointer to allocated fc_exch with exch lock held. */ static struct fc_exch *fc_exch_em_alloc(struct fc_lport *lport, - struct fc_exch_mgr *mp, - struct fc_frame *fp, u16 xid) + struct fc_exch_mgr *mp) { struct fc_exch *ep; + u16 min, max, xid; + + min = mp->min_xid; + max = mp->max_xid; /* allocate memory for exchange */ ep = mempool_alloc(mp->ep_pool, GFP_ATOMIC); @@ -536,15 +489,14 @@ static struct fc_exch *fc_exch_em_alloc(struct fc_lport *lport, memset(ep, 0, sizeof(*ep)); spin_lock_bh(&mp->em_lock); - /* alloc xid if input xid 0 */ - if (!xid) { - /* alloc a new xid */ - xid = fc_em_alloc_xid(mp, fp); - if (!xid) { - printk(KERN_WARNING "libfc: Failed to allocate an exhange\n"); + xid = mp->next_xid; + /* alloc a new xid */ + while (mp->exches[xid - min]) { + xid = (xid == max) ? min : xid + 1; + if (xid == mp->next_xid) goto err; - } } + mp->next_xid = (xid == max) ? min : xid + 1; fc_exch_hold(ep); /* hold for exch in mp */ spin_lock_init(&ep->ex_lock); @@ -597,7 +549,7 @@ struct fc_exch *fc_exch_alloc(struct fc_lport *lport, struct fc_frame *fp) list_for_each_entry(ema, &lport->ema_list, ema_list) { if (!ema->match || ema->match(fp)) { - ep = fc_exch_em_alloc(lport, ema->mp, fp, 0); + ep = fc_exch_em_alloc(lport, ema->mp); if (ep) return ep; } @@ -1817,7 +1769,7 @@ struct fc_exch_mgr *fc_exch_mgr_alloc(struct fc_lport *lp, struct fc_exch_mgr *mp; size_t len; - if (max_xid <= min_xid || min_xid == 0 || max_xid == FC_XID_UNKNOWN) { + if (max_xid <= min_xid || max_xid == FC_XID_UNKNOWN) { FC_LPORT_DBG(lp, "Invalid min_xid 0x:%x and max_xid 0x:%x\n", min_xid, max_xid); return NULL; @@ -1826,7 +1778,6 @@ struct fc_exch_mgr *fc_exch_mgr_alloc(struct fc_lport *lp, /* * Memory need for EM */ -#define xid_ok(i, m1, m2) (((i) >= (m1)) && ((i) <= (m2))) len = (max_xid - min_xid + 1) * (sizeof(struct fc_exch *)); len += sizeof(struct fc_exch_mgr); @@ -1840,17 +1791,7 @@ struct fc_exch_mgr *fc_exch_mgr_alloc(struct fc_lport *lp, /* adjust em exch xid range for offload */ mp->min_xid = min_xid; mp->max_xid = max_xid; - mp->last_xid = min_xid - 1; - mp->max_read = 0; - mp->last_read = 0; - if (lp->lro_enabled && xid_ok(lp->lro_xid, min_xid, max_xid)) { - mp->max_read = lp->lro_xid; - mp->last_read = min_xid - 1; - mp->last_xid = mp->max_read; - } else { - /* disable lro if no xid control over read */ - lp->lro_enabled = 0; - } + mp->next_xid = min_xid; INIT_LIST_HEAD(&mp->ex_list); spin_lock_init(&mp->em_lock); @@ -1922,7 +1863,8 @@ struct fc_seq *fc_exch_seq_send(struct fc_lport *lp, fc_exch_setup_hdr(ep, fp, ep->f_ctl); sp->cnt++; - fc_fcp_ddp_setup(fr_fsp(fp), ep->xid); + if (ep->xid <= lp->lro_xid) + fc_fcp_ddp_setup(fr_fsp(fp), ep->xid); if (unlikely(lp->tt.frame_send(lp, fp))) goto err; -- cgit v1.2.3-70-g09d2 From 15a521b4243f3d61971f8422f3e514ef009a42b8 Mon Sep 17 00:00:00 2001 From: Yi Zou Date: Wed, 29 Jul 2009 17:05:26 -0700 Subject: [SCSI] fcoe: Remove ifdef for NETIF_F_FCOE_CRC and NETIF_F_FSO Remove the extra ifdef for NETIF_F_FSO and NETIF_F_FCOE_CRC since they are already defined in the current kernel as in include/linux/netdevice.h. Signed-off-by: Yi Zou Signed-off-by: Robert Love Signed-off-by: James Bottomley Signed-off-by: James Bottomley --- drivers/scsi/fcoe/fcoe.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'drivers/scsi/fcoe/fcoe.c') diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index 0306aa95eb3..ad21e87e400 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -300,20 +300,16 @@ static int fcoe_netdev_config(struct fc_lport *lp, struct net_device *netdev) if (fc->real_dev->features & NETIF_F_SG) lp->sg_supp = 1; -#ifdef NETIF_F_FCOE_CRC if (netdev->features & NETIF_F_FCOE_CRC) { lp->crc_offload = 1; FCOE_NETDEV_DBG(netdev, "Supports FCCRC offload\n"); } -#endif -#ifdef NETIF_F_FSO if (netdev->features & NETIF_F_FSO) { lp->seq_offload = 1; lp->lso_max = netdev->gso_max_size; FCOE_NETDEV_DBG(netdev, "Supports LSO for max len 0x%x\n", lp->lso_max); } -#endif if (netdev->fcoe_ddp_xid) { lp->lro_enabled = 1; lp->lro_xid = netdev->fcoe_ddp_xid; @@ -1205,7 +1201,6 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp) FC_FCOE_ENCAPS_VER(hp, FC_FCOE_VER); hp->fcoe_sof = sof; -#ifdef NETIF_F_FSO /* fcoe lso, mss is in max_payload which is non-zero for FCP data */ if (lp->seq_offload && fr_max_payload(fp)) { skb_shinfo(skb)->gso_type = SKB_GSO_FCOE; @@ -1214,7 +1209,6 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp) skb_shinfo(skb)->gso_type = 0; skb_shinfo(skb)->gso_size = 0; } -#endif /* update tx stats: regardless if LLD fails */ stats = fc_lport_get_stats(lp); stats->TxFrames++; -- cgit v1.2.3-70-g09d2 From 1d1b88dc01e5fd2b3e2abb7aa42d0f1eca4c33ea Mon Sep 17 00:00:00 2001 From: Vasu Dev Date: Wed, 29 Jul 2009 17:05:45 -0700 Subject: [SCSI] fcoe: removes phys_dev and renames real_dev to netdev. The phys_dev was used only to locate common offload EM instance for all FCoE instances on a eth devices in function fcoe_em_config, so just updated fcoe_em_config to look for actual real eth device in locating common offload EM instance and then no need to store phys_dev in fcoe_softc, so removes phys_dev from fcoe_softc also. Renames fcoe_softc real_dev to netdev and updates all its uses to use netdev. So effectively no functional change, use of single netdev instead phys_dev and real_dev saves one pointer memory in fcoe_softc, also real_dev used here was confusing with vlan driver terminology since real_dev in vlan driver is referred to physical eth device. Signed-off-by: Vasu Dev Signed-off-by: Robert Love Signed-off-by: James Bottomley Signed-off-by: James Bottomley --- drivers/scsi/fcoe/fcoe.c | 93 +++++++++++++++++++++++++----------------------- drivers/scsi/fcoe/fcoe.h | 5 ++- 2 files changed, 51 insertions(+), 47 deletions(-) (limited to 'drivers/scsi/fcoe/fcoe.c') diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index ad21e87e400..757aa28f0f0 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -159,7 +159,7 @@ static int fcoe_fip_recv(struct sk_buff *skb, struct net_device *dev, */ static void fcoe_fip_send(struct fcoe_ctlr *fip, struct sk_buff *skb) { - skb->dev = fcoe_from_ctlr(fip)->real_dev; + skb->dev = fcoe_from_ctlr(fip)->netdev; dev_queue_xmit(skb); } @@ -179,8 +179,8 @@ static void fcoe_update_src_mac(struct fcoe_ctlr *fip, u8 *old, u8 *new) fc = fcoe_from_ctlr(fip); rtnl_lock(); if (!is_zero_ether_addr(old)) - dev_unicast_delete(fc->real_dev, old); - dev_unicast_add(fc->real_dev, new); + dev_unicast_delete(fc->netdev, old); + dev_unicast_add(fc->netdev, new); rtnl_unlock(); } @@ -231,12 +231,12 @@ void fcoe_netdev_cleanup(struct fcoe_softc *fc) /* Delete secondary MAC addresses */ rtnl_lock(); memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN); - dev_unicast_delete(fc->real_dev, flogi_maddr); + dev_unicast_delete(fc->netdev, flogi_maddr); if (!is_zero_ether_addr(fc->ctlr.data_src_addr)) - dev_unicast_delete(fc->real_dev, fc->ctlr.data_src_addr); + dev_unicast_delete(fc->netdev, fc->ctlr.data_src_addr); if (fc->ctlr.spma) - dev_unicast_delete(fc->real_dev, fc->ctlr.ctl_src_addr); - dev_mc_delete(fc->real_dev, FIP_ALL_ENODE_MACS, ETH_ALEN, 0); + dev_unicast_delete(fc->netdev, fc->ctlr.ctl_src_addr); + dev_mc_delete(fc->netdev, FIP_ALL_ENODE_MACS, ETH_ALEN, 0); rtnl_unlock(); } @@ -272,17 +272,12 @@ static int fcoe_netdev_config(struct fc_lport *lp, struct net_device *netdev) /* Setup lport private data to point to fcoe softc */ fc = lport_priv(lp); fc->ctlr.lp = lp; - fc->real_dev = netdev; - fc->phys_dev = netdev; - - /* Require support for get_pauseparam ethtool op. */ - if (netdev->priv_flags & IFF_802_1Q_VLAN) - fc->phys_dev = vlan_dev_real_dev(netdev); + fc->netdev = netdev; /* Do not support for bonding device */ - if ((fc->real_dev->priv_flags & IFF_MASTER_ALB) || - (fc->real_dev->priv_flags & IFF_SLAVE_INACTIVE) || - (fc->real_dev->priv_flags & IFF_MASTER_8023AD)) { + if ((netdev->priv_flags & IFF_MASTER_ALB) || + (netdev->priv_flags & IFF_SLAVE_INACTIVE) || + (netdev->priv_flags & IFF_MASTER_8023AD)) { return -EOPNOTSUPP; } @@ -291,13 +286,13 @@ 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 = fc->real_dev->mtu - (sizeof(struct fcoe_hdr) + - sizeof(struct fcoe_crc_eof)); + mfs = netdev->mtu - (sizeof(struct fcoe_hdr) + + sizeof(struct fcoe_crc_eof)); if (fc_set_mfs(lp, mfs)) return -EINVAL; /* offload features support */ - if (fc->real_dev->features & NETIF_F_SG) + if (netdev->features & NETIF_F_SG) lp->sg_supp = 1; if (netdev->features & NETIF_F_FCOE_CRC) { @@ -335,13 +330,13 @@ static int fcoe_netdev_config(struct fc_lport *lp, struct net_device *netdev) /* setup Source Mac Address */ if (!fc->ctlr.spma) - memcpy(fc->ctlr.ctl_src_addr, fc->real_dev->dev_addr, - fc->real_dev->addr_len); + memcpy(fc->ctlr.ctl_src_addr, netdev->dev_addr, + fc->netdev->addr_len); - wwnn = fcoe_wwn_from_mac(fc->real_dev->dev_addr, 1, 0); + 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(fc->real_dev->dev_addr, 2, 0); + wwpn = fcoe_wwn_from_mac(netdev->dev_addr, 2, 0); fc_set_wwpn(lp, wwpn); /* @@ -351,10 +346,10 @@ static int fcoe_netdev_config(struct fc_lport *lp, struct net_device *netdev) */ rtnl_lock(); memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN); - dev_unicast_add(fc->real_dev, flogi_maddr); + dev_unicast_add(netdev, flogi_maddr); if (fc->ctlr.spma) - dev_unicast_add(fc->real_dev, fc->ctlr.ctl_src_addr); - dev_mc_add(fc->real_dev, FIP_ALL_ENODE_MACS, ETH_ALEN, 0); + dev_unicast_add(netdev, fc->ctlr.ctl_src_addr); + dev_mc_add(netdev, FIP_ALL_ENODE_MACS, ETH_ALEN, 0); rtnl_unlock(); /* @@ -363,12 +358,12 @@ static int fcoe_netdev_config(struct fc_lport *lp, struct net_device *netdev) */ fc->fcoe_packet_type.func = fcoe_rcv; fc->fcoe_packet_type.type = __constant_htons(ETH_P_FCOE); - fc->fcoe_packet_type.dev = fc->real_dev; + fc->fcoe_packet_type.dev = netdev; dev_add_pack(&fc->fcoe_packet_type); fc->fip_packet_type.func = fcoe_fip_recv; fc->fip_packet_type.type = htons(ETH_P_FIP); - fc->fip_packet_type.dev = fc->real_dev; + fc->fip_packet_type.dev = netdev; dev_add_pack(&fc->fip_packet_type); return 0; @@ -434,6 +429,7 @@ static inline int fcoe_em_config(struct fc_lport *lp) { struct fcoe_softc *fc = lport_priv(lp); struct fcoe_softc *oldfc = NULL; + struct net_device *old_real_dev, *cur_real_dev; u16 min_xid = FCOE_MIN_XID; u16 max_xid = FCOE_MAX_XID; @@ -448,10 +444,20 @@ static inline int fcoe_em_config(struct fc_lport *lp) /* * Reuse existing offload em instance in case - * it is already allocated on phys_dev. + * it is already allocated on real eth device */ + if (fc->netdev->priv_flags & IFF_802_1Q_VLAN) + cur_real_dev = vlan_dev_real_dev(fc->netdev); + else + cur_real_dev = fc->netdev; + list_for_each_entry(oldfc, &fcoe_hostlist, list) { - if (oldfc->phys_dev == fc->phys_dev) { + if (oldfc->netdev->priv_flags & IFF_802_1Q_VLAN) + old_real_dev = vlan_dev_real_dev(oldfc->netdev); + else + old_real_dev = oldfc->netdev; + + if (cur_real_dev == old_real_dev) { fc->oem = oldfc->oem; break; } @@ -461,7 +467,7 @@ static inline int fcoe_em_config(struct fc_lport *lp) if (!fc_exch_mgr_add(lp, fc->oem, fcoe_oem_match)) { printk(KERN_ERR "fcoe_em_config: failed to add " "offload em:%p on interface:%s\n", - fc->oem, fc->real_dev->name); + fc->oem, fc->netdev->name); return -ENOMEM; } } else { @@ -471,7 +477,7 @@ static inline int fcoe_em_config(struct fc_lport *lp) if (!fc->oem) { printk(KERN_ERR "fcoe_em_config: failed to allocate " "em for offload exches on interface:%s\n", - fc->real_dev->name); + fc->netdev->name); return -ENOMEM; } } @@ -484,7 +490,7 @@ static inline int fcoe_em_config(struct fc_lport *lp) skip_oem: if (!fc_exch_mgr_alloc(lp, FC_CLASS_3, min_xid, max_xid, NULL)) { printk(KERN_ERR "fcoe_em_config: failed to " - "allocate em on interface %s\n", fc->real_dev->name); + "allocate em on interface %s\n", fc->netdev->name); return -ENOMEM; } @@ -548,7 +554,7 @@ static int fcoe_if_destroy(struct net_device *netdev) fc_lport_free_stats(lp); /* Release the net_device and Scsi_Host */ - dev_put(fc->real_dev); + dev_put(netdev); scsi_host_put(lp->host); return 0; @@ -1179,7 +1185,7 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp) skb_reset_network_header(skb); skb->mac_len = elen; skb->protocol = htons(ETH_P_FCOE); - skb->dev = fc->real_dev; + skb->dev = fc->netdev; /* fill up mac and fcoe headers */ eh = eth_hdr(skb); @@ -1454,7 +1460,7 @@ static int fcoe_device_notification(struct notifier_block *notifier, ulong event, void *ptr) { struct fc_lport *lp = NULL; - struct net_device *real_dev = ptr; + struct net_device *netdev = ptr; struct fcoe_softc *fc; struct fcoe_dev_stats *stats; u32 link_possible = 1; @@ -1463,7 +1469,7 @@ static int fcoe_device_notification(struct notifier_block *notifier, read_lock(&fcoe_hostlist_lock); list_for_each_entry(fc, &fcoe_hostlist, list) { - if (fc->real_dev == real_dev) { + if (fc->netdev == netdev) { lp = fc->ctlr.lp; break; } @@ -1483,16 +1489,15 @@ static int fcoe_device_notification(struct notifier_block *notifier, case NETDEV_CHANGE: break; case NETDEV_CHANGEMTU: - mfs = fc->real_dev->mtu - - (sizeof(struct fcoe_hdr) + - sizeof(struct fcoe_crc_eof)); + mfs = netdev->mtu - (sizeof(struct fcoe_hdr) + + sizeof(struct fcoe_crc_eof)); if (mfs >= FC_MIN_MAX_FRAME) fc_set_mfs(lp, mfs); break; case NETDEV_REGISTER: break; default: - FCOE_NETDEV_DBG(real_dev, "Unknown event %ld " + FCOE_NETDEV_DBG(netdev, "Unknown event %ld " "from netdev netlink\n", event); } if (link_possible && !fcoe_link_ok(lp)) @@ -1696,7 +1701,7 @@ MODULE_PARM_DESC(destroy, "Destroy fcoe port"); int fcoe_link_ok(struct fc_lport *lp) { struct fcoe_softc *fc = lport_priv(lp); - struct net_device *dev = fc->real_dev; + struct net_device *dev = fc->netdev; struct ethtool_cmd ecmd = { ETHTOOL_GSET }; if ((dev->flags & IFF_UP) && netif_carrier_ok(dev) && @@ -1797,7 +1802,7 @@ fcoe_hostlist_lookup_softc(const struct net_device *dev) struct fcoe_softc *fc; list_for_each_entry(fc, &fcoe_hostlist, list) { - if (fc->real_dev == dev) + if (fc->netdev == dev) return fc; } return NULL; @@ -1916,7 +1921,7 @@ static void __exit fcoe_exit(void) /* releases the associated fcoe hosts */ list_for_each_entry_safe(fc, tmp, &fcoe_hostlist, list) - fcoe_if_destroy(fc->real_dev); + fcoe_if_destroy(fc->netdev); unregister_hotcpu_notifier(&fcoe_cpu_notifier); diff --git a/drivers/scsi/fcoe/fcoe.h b/drivers/scsi/fcoe/fcoe.h index 6905efc166b..5ae8ca71afc 100644 --- a/drivers/scsi/fcoe/fcoe.h +++ b/drivers/scsi/fcoe/fcoe.h @@ -79,8 +79,7 @@ struct fcoe_percpu_s { */ struct fcoe_softc { struct list_head list; - struct net_device *real_dev; - struct net_device *phys_dev; /* device with ethtool_ops */ + struct net_device *netdev; struct fc_exch_mgr *oem; /* offload exchange manger */ struct packet_type fcoe_packet_type; struct packet_type fip_packet_type; @@ -95,7 +94,7 @@ struct fcoe_softc { static inline struct net_device *fcoe_netdev( const struct fc_lport *lp) { - return ((struct fcoe_softc *)lport_priv(lp))->real_dev; + return ((struct fcoe_softc *)lport_priv(lp))->netdev; } #endif /* _FCOE_H_ */ -- cgit v1.2.3-70-g09d2 From b2f0091fbf8b475fa09b5e1712e0ab84cb3e1ca4 Mon Sep 17 00:00:00 2001 From: Vasu Dev Date: Tue, 25 Aug 2009 13:58:53 -0700 Subject: [SCSI] fcoe, libfc: fully makes use of per cpu exch pool and then removes em_lock 1. Updates fcoe_rcv() to queue incoming frames to the fcoe per cpu thread on which this frame's exch was originated and simply use current cpu for request exch not originated by initiator. It is redundant to add this code under CONFIG_SMP, so removes CONFIG_SMP uses around this code. 2. Updates fc_exch_em_alloc, fc_exch_delete, fc_exch_find to use per cpu exch pools, here fc_exch_delete is rename of older fc_exch_mgr_delete_ep since ep/exch are now deleted in pools of EM and so brief new name is sufficient and better name. Updates these functions to map exch id to their index into exch pool using fc_cpu_mask, fc_cpu_order and EM min_xid. This mapping is as per detailed explanation about this in last patch and basically this is just as lower fc_cpu_mask bits of exch id as cpu number and upper bit sum of EM min_xid and exch index in pool. Uses pool next_index to keep track of exch allocation from pool along with pool_max_index as upper bound of exches array in pool. 3. Adds exch pool ptr to fc_exch to free exch to its pool in fc_exch_delete. 4. Updates fc_exch_mgr_reset to reset all exch pools of an EM, this required adding fc_exch_pool_reset func to reset exches in pool and then have fc_exch_mgr_reset call fc_exch_pool_reset for each pool within each EM for a lport. 5. Removes no longer needed exches array, em_lock, next_xid, and total_exches from struct fc_exch_mgr, these are not needed after use of per cpu exch pool, also removes not used max_read, last_read from struct fc_exch_mgr. 6. Updates locking notes for exch pool lock with fc_exch lock and uses pool lock in exch allocation, lookup and reset. Signed-off-by: Vasu Dev Signed-off-by: Robert Love Signed-off-by: James Bottomley --- drivers/scsi/fcoe/fcoe.c | 19 +++-- drivers/scsi/libfc/fc_exch.c | 189 +++++++++++++++++++++++-------------------- include/scsi/libfc.h | 9 ++- 3 files changed, 115 insertions(+), 102 deletions(-) (limited to 'drivers/scsi/fcoe/fcoe.c') diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index 757aa28f0f0..e32a0ed266a 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -912,8 +912,7 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *dev, struct fcoe_softc *fc; struct fc_frame_header *fh; struct fcoe_percpu_s *fps; - unsigned short oxid; - unsigned int cpu = 0; + unsigned int cpu; fc = container_of(ptype, struct fcoe_softc, fcoe_packet_type); lp = fc->ctlr.lp; @@ -947,20 +946,20 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *dev, skb_set_transport_header(skb, sizeof(struct fcoe_hdr)); fh = (struct fc_frame_header *) skb_transport_header(skb); - oxid = ntohs(fh->fh_ox_id); - fr = fcoe_dev_from_skb(skb); fr->fr_dev = lp; fr->ptype = ptype; -#ifdef CONFIG_SMP /* - * The incoming frame exchange id(oxid) is ANDed with num of online - * cpu bits to get cpu and then this cpu is used for selecting - * a per cpu kernel thread from fcoe_percpu. + * 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. */ - cpu = oxid & (num_online_cpus() - 1); -#endif + if (ntoh24(fh->fh_f_ctl) & FC_FC_EX_CTX) + cpu = ntohs(fh->fh_ox_id) & fc_cpu_mask; + else + cpu = smp_processor_id(); fps = &per_cpu(fcoe_percpu, cpu); spin_lock_bh(&fps->fcoe_rx_list.lock); diff --git a/drivers/scsi/libfc/fc_exch.c b/drivers/scsi/libfc/fc_exch.c index 9cbe8d66eb2..b51db15a387 100644 --- a/drivers/scsi/libfc/fc_exch.c +++ b/drivers/scsi/libfc/fc_exch.c @@ -73,14 +73,8 @@ struct fc_exch_pool { struct fc_exch_mgr { enum fc_class class; /* default class for sequences */ struct kref kref; /* exchange mgr reference count */ - spinlock_t em_lock; /* exchange manager lock, - must be taken before ex_lock */ - u16 next_xid; /* next possible free exchange ID */ u16 min_xid; /* min exchange ID */ u16 max_xid; /* max exchange ID */ - u16 max_read; /* max exchange ID for read */ - u16 last_read; /* last xid allocated for read */ - u32 total_exches; /* total allocated exchanges */ struct list_head ex_list; /* allocated exchanges list */ mempool_t *ep_pool; /* reserve ep's */ u16 pool_max_index; /* max exch array index in exch pool */ @@ -99,7 +93,6 @@ struct fc_exch_mgr { atomic_t seq_not_found; atomic_t non_bls_resp; } stats; - struct fc_exch **exches; /* for exch pointers indexed by xid */ }; #define fc_seq_exch(sp) container_of(sp, struct fc_exch, seq) @@ -192,8 +185,8 @@ static struct fc_seq *fc_seq_start_next_locked(struct fc_seq *sp); * sequence allocation and deallocation must be locked. * - exchange refcnt can be done atomicly without locks. * - sequence allocation must be locked by exch lock. - * - If the em_lock and ex_lock must be taken at the same time, then the - * em_lock must be taken before the ex_lock. + * - If the EM pool lock and ex_lock must be taken at the same time, then the + * EM pool lock must be taken before the ex_lock. */ /* @@ -335,17 +328,18 @@ static inline void fc_exch_ptr_set(struct fc_exch_pool *pool, u16 index, ((struct fc_exch **)(pool + 1))[index] = ep; } -static void fc_exch_mgr_delete_ep(struct fc_exch *ep) +static void fc_exch_delete(struct fc_exch *ep) { - struct fc_exch_mgr *mp; + struct fc_exch_pool *pool; - mp = ep->em; - spin_lock_bh(&mp->em_lock); - WARN_ON(mp->total_exches <= 0); - mp->total_exches--; - mp->exches[ep->xid - mp->min_xid] = NULL; + pool = ep->pool; + spin_lock_bh(&pool->lock); + WARN_ON(pool->total_exches <= 0); + pool->total_exches--; + fc_exch_ptr_set(pool, (ep->xid - ep->em->min_xid) >> fc_cpu_order, + NULL); list_del(&ep->ex_list); - spin_unlock_bh(&mp->em_lock); + spin_unlock_bh(&pool->lock); fc_exch_release(ep); /* drop hold for exch in mp */ } @@ -465,7 +459,7 @@ static void fc_exch_timeout(struct work_struct *work) rc = fc_exch_done_locked(ep); spin_unlock_bh(&ep->ex_lock); if (!rc) - fc_exch_mgr_delete_ep(ep); + fc_exch_delete(ep); if (resp) resp(sp, ERR_PTR(-FC_EX_TIMEOUT), arg); fc_seq_exch_abort(sp, 2 * ep->r_a_tov); @@ -509,10 +503,9 @@ static struct fc_exch *fc_exch_em_alloc(struct fc_lport *lport, struct fc_exch_mgr *mp) { struct fc_exch *ep; - u16 min, max, xid; - - min = mp->min_xid; - max = mp->max_xid; + unsigned int cpu; + u16 index; + struct fc_exch_pool *pool; /* allocate memory for exchange */ ep = mempool_alloc(mp->ep_pool, GFP_ATOMIC); @@ -522,15 +515,17 @@ static struct fc_exch *fc_exch_em_alloc(struct fc_lport *lport, } memset(ep, 0, sizeof(*ep)); - spin_lock_bh(&mp->em_lock); - xid = mp->next_xid; - /* alloc a new xid */ - while (mp->exches[xid - min]) { - xid = (xid == max) ? min : xid + 1; - if (xid == mp->next_xid) + cpu = smp_processor_id(); + pool = per_cpu_ptr(mp->pool, cpu); + spin_lock_bh(&pool->lock); + index = pool->next_index; + /* allocate new exch from pool */ + while (fc_exch_ptr_get(pool, index)) { + index = index == mp->pool_max_index ? 0 : index + 1; + if (index == pool->next_index) goto err; } - mp->next_xid = (xid == max) ? min : xid + 1; + pool->next_index = index == mp->pool_max_index ? 0 : index + 1; fc_exch_hold(ep); /* hold for exch in mp */ spin_lock_init(&ep->ex_lock); @@ -541,17 +536,18 @@ static struct fc_exch *fc_exch_em_alloc(struct fc_lport *lport, */ spin_lock_bh(&ep->ex_lock); - mp->exches[xid - mp->min_xid] = ep; - list_add_tail(&ep->ex_list, &mp->ex_list); + fc_exch_ptr_set(pool, index, ep); + list_add_tail(&ep->ex_list, &pool->ex_list); fc_seq_alloc(ep, ep->seq_id++); - mp->total_exches++; - spin_unlock_bh(&mp->em_lock); + pool->total_exches++; + spin_unlock_bh(&pool->lock); /* * update exchange */ - ep->oxid = ep->xid = xid; + ep->oxid = ep->xid = (index << fc_cpu_order | cpu) + mp->min_xid; ep->em = mp; + ep->pool = pool; ep->lp = lport; ep->f_ctl = FC_FC_FIRST_SEQ; /* next seq is first seq */ ep->rxid = FC_XID_UNKNOWN; @@ -560,7 +556,7 @@ static struct fc_exch *fc_exch_em_alloc(struct fc_lport *lport, out: return ep; err: - spin_unlock_bh(&mp->em_lock); + spin_unlock_bh(&pool->lock); atomic_inc(&mp->stats.no_free_exch_xid); mempool_free(ep, mp->ep_pool); return NULL; @@ -597,16 +593,18 @@ EXPORT_SYMBOL(fc_exch_alloc); */ static struct fc_exch *fc_exch_find(struct fc_exch_mgr *mp, u16 xid) { + struct fc_exch_pool *pool; struct fc_exch *ep = NULL; if ((xid >= mp->min_xid) && (xid <= mp->max_xid)) { - spin_lock_bh(&mp->em_lock); - ep = mp->exches[xid - mp->min_xid]; + pool = per_cpu_ptr(mp->pool, xid & fc_cpu_mask); + spin_lock_bh(&pool->lock); + ep = fc_exch_ptr_get(pool, (xid - mp->min_xid) >> fc_cpu_order); if (ep) { fc_exch_hold(ep); WARN_ON(ep->xid != xid); } - spin_unlock_bh(&mp->em_lock); + spin_unlock_bh(&pool->lock); } return ep; } @@ -620,7 +618,7 @@ void fc_exch_done(struct fc_seq *sp) rc = fc_exch_done_locked(ep); spin_unlock_bh(&ep->ex_lock); if (!rc) - fc_exch_mgr_delete_ep(ep); + fc_exch_delete(ep); } EXPORT_SYMBOL(fc_exch_done); @@ -1213,7 +1211,7 @@ static void fc_exch_recv_seq_resp(struct fc_exch_mgr *mp, struct fc_frame *fp) WARN_ON(fc_seq_exch(sp) != ep); spin_unlock_bh(&ep->ex_lock); if (!rc) - fc_exch_mgr_delete_ep(ep); + fc_exch_delete(ep); } /* @@ -1323,7 +1321,7 @@ static void fc_exch_abts_resp(struct fc_exch *ep, struct fc_frame *fp) rc = fc_exch_done_locked(ep); spin_unlock_bh(&ep->ex_lock); if (!rc) - fc_exch_mgr_delete_ep(ep); + fc_exch_delete(ep); if (resp) resp(sp, fp, ex_resp_arg); @@ -1466,48 +1464,76 @@ static void fc_exch_reset(struct fc_exch *ep) rc = fc_exch_done_locked(ep); spin_unlock_bh(&ep->ex_lock); if (!rc) - fc_exch_mgr_delete_ep(ep); + fc_exch_delete(ep); if (resp) resp(sp, ERR_PTR(-FC_EX_CLOSED), arg); } -/* - * Reset an exchange manager, releasing all sequences and exchanges. - * If sid is non-zero, reset only exchanges we source from that FID. - * If did is non-zero, reset only exchanges destined to that FID. +/** + * fc_exch_pool_reset() - Resets an per cpu exches pool. + * @lport: ptr to the local port + * @pool: ptr to the per cpu exches pool + * @sid: source FC ID + * @did: destination FC ID + * + * Resets an per cpu exches pool, releasing its all sequences + * and exchanges. If sid is non-zero, then reset only exchanges + * we sourced from that FID. If did is non-zero, reset only + * exchanges destined to that FID. */ -void fc_exch_mgr_reset(struct fc_lport *lp, u32 sid, u32 did) +static void fc_exch_pool_reset(struct fc_lport *lport, + struct fc_exch_pool *pool, + u32 sid, u32 did) { struct fc_exch *ep; struct fc_exch *next; - struct fc_exch_mgr *mp; - struct fc_exch_mgr_anchor *ema; - list_for_each_entry(ema, &lp->ema_list, ema_list) { - mp = ema->mp; - spin_lock_bh(&mp->em_lock); + spin_lock_bh(&pool->lock); restart: - list_for_each_entry_safe(ep, next, &mp->ex_list, ex_list) { - if ((lp == ep->lp) && - (sid == 0 || sid == ep->sid) && - (did == 0 || did == ep->did)) { - fc_exch_hold(ep); - spin_unlock_bh(&mp->em_lock); - - fc_exch_reset(ep); - - fc_exch_release(ep); - spin_lock_bh(&mp->em_lock); - - /* - * must restart loop incase while lock - * was down multiple eps were released. - */ - goto restart; - } + list_for_each_entry_safe(ep, next, &pool->ex_list, ex_list) { + if ((lport == ep->lp) && + (sid == 0 || sid == ep->sid) && + (did == 0 || did == ep->did)) { + fc_exch_hold(ep); + spin_unlock_bh(&pool->lock); + + fc_exch_reset(ep); + + fc_exch_release(ep); + spin_lock_bh(&pool->lock); + + /* + * must restart loop incase while lock + * was down multiple eps were released. + */ + goto restart; } - spin_unlock_bh(&mp->em_lock); + } + spin_unlock_bh(&pool->lock); +} + +/** + * fc_exch_mgr_reset() - Resets all EMs of a lport + * @lport: ptr to the local port + * @sid: source FC ID + * @did: destination FC ID + * + * Reset all EMs of a lport, releasing its all sequences and + * exchanges. If sid is non-zero, then reset only exchanges + * we sourced from that FID. If did is non-zero, reset only + * exchanges destined to that FID. + */ +void fc_exch_mgr_reset(struct fc_lport *lport, u32 sid, u32 did) +{ + struct fc_exch_mgr_anchor *ema; + unsigned int cpu; + + list_for_each_entry(ema, &lport->ema_list, ema_list) { + for_each_possible_cpu(cpu) + fc_exch_pool_reset(lport, + per_cpu_ptr(ema->mp->pool, cpu), + sid, did); } } EXPORT_SYMBOL(fc_exch_mgr_reset); @@ -1777,11 +1803,6 @@ static void fc_exch_mgr_destroy(struct kref *kref) { struct fc_exch_mgr *mp = container_of(kref, struct fc_exch_mgr, kref); - /* - * The total exch count must be zero - * before freeing exchange manager. - */ - WARN_ON(mp->total_exches != 0); mempool_destroy(mp->ep_pool); free_percpu(mp->pool); kfree(mp); @@ -1802,7 +1823,6 @@ struct fc_exch_mgr *fc_exch_mgr_alloc(struct fc_lport *lp, bool (*match)(struct fc_frame *)) { struct fc_exch_mgr *mp; - size_t len; u16 pool_exch_range; size_t pool_size; unsigned int cpu; @@ -1816,25 +1836,16 @@ struct fc_exch_mgr *fc_exch_mgr_alloc(struct fc_lport *lp, } /* - * Memory need for EM + * allocate memory for EM */ - len = (max_xid - min_xid + 1) * (sizeof(struct fc_exch *)); - len += sizeof(struct fc_exch_mgr); - - mp = kzalloc(len, GFP_ATOMIC); + mp = kzalloc(sizeof(struct fc_exch_mgr), GFP_ATOMIC); if (!mp) return NULL; mp->class = class; - mp->total_exches = 0; - mp->exches = (struct fc_exch **)(mp + 1); /* adjust em exch xid range for offload */ mp->min_xid = min_xid; mp->max_xid = max_xid; - mp->next_xid = min_xid; - - INIT_LIST_HEAD(&mp->ex_list); - spin_lock_init(&mp->em_lock); mp->ep_pool = mempool_create_slab_pool(2, fc_em_cachep); if (!mp->ep_pool) @@ -1944,7 +1955,7 @@ err: rc = fc_exch_done_locked(ep); spin_unlock_bh(&ep->ex_lock); if (!rc) - fc_exch_mgr_delete_ep(ep); + fc_exch_delete(ep); return NULL; } EXPORT_SYMBOL(fc_exch_seq_send); diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h index 32063389c4b..53b38814d38 100644 --- a/include/scsi/libfc.h +++ b/include/scsi/libfc.h @@ -368,6 +368,7 @@ struct fc_seq { */ struct fc_exch { struct fc_exch_mgr *em; /* exchange manager */ + struct fc_exch_pool *pool; /* per cpu exches pool */ u32 state; /* internal driver state */ u16 xid; /* our exchange ID */ struct list_head ex_list; /* free or busy list linkage */ @@ -1045,10 +1046,12 @@ struct fc_exch *fc_exch_alloc(struct fc_lport *lport, struct fc_frame *fp); */ struct fc_seq *fc_seq_start_next(struct fc_seq *sp); + /* - * Reset an exchange manager, completing all sequences and exchanges. - * If s_id is non-zero, reset only exchanges originating from that FID. - * If d_id is non-zero, reset only exchanges sending to that FID. + * Reset all EMs of a lport, releasing its all sequences and + * exchanges. If sid is non-zero, then reset only exchanges + * we sourced from that FID. If did is non-zero, reset only + * exchanges destined to that FID. */ void fc_exch_mgr_reset(struct fc_lport *, u32 s_id, u32 d_id); -- cgit v1.2.3-70-g09d2 From 05cc7390735c49357b9ae67bf97f1c1579547f5b Mon Sep 17 00:00:00 2001 From: Yi Zou Date: Tue, 25 Aug 2009 13:59:03 -0700 Subject: [SCSI] fcoe: Add sysfs parameter to fcoe for minimum DDP read I/O size This adds fcoe_ddp_min as a module parameter for fcoe module to: /sys/module/fcoe/parameters/ddp_min It is observed that for some hardware, particularly Intel 82599, there is too much overhead in setting up context for direct data placement (DDP) read when the requested read I/O size is small. This is added as a module parameter for performance tuning and is set as 0 by default and user can change this based on their own hardware. Signed-off-by: Yi Zou Signed-off-by: Robert Love Signed-off-by: James Bottomley --- drivers/scsi/fcoe/fcoe.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'drivers/scsi/fcoe/fcoe.c') diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index e32a0ed266a..a39d370bccf 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -49,6 +49,12 @@ MODULE_AUTHOR("Open-FCoE.org"); MODULE_DESCRIPTION("FCoE"); MODULE_LICENSE("GPL v2"); +/* Performance tuning parameters for fcoe */ +static unsigned int fcoe_ddp_min; +module_param_named(ddp_min, fcoe_ddp_min, uint, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(ddp_min, "Minimum I/O size in bytes for " \ + "Direct Data Placement (DDP)."); + /* fcoe host list */ LIST_HEAD(fcoe_hostlist); DEFINE_RWLOCK(fcoe_hostlist_lock); @@ -414,7 +420,8 @@ static int fcoe_shost_config(struct fc_lport *lp, struct Scsi_Host *shost, */ bool fcoe_oem_match(struct fc_frame *fp) { - return fc_fcp_is_read(fr_fsp(fp)); + return fc_fcp_is_read(fr_fsp(fp)) && + (fr_fsp(fp)->data_len > fcoe_ddp_min); } /** -- cgit v1.2.3-70-g09d2 From 5892c32f8a2d5a37d4c2ff1df62f6e8a2664abdb Mon Sep 17 00:00:00 2001 From: Chris Leech Date: Tue, 25 Aug 2009 13:59:14 -0700 Subject: [SCSI] fcoe: fix missing error check in call to fcoe_if_init fcoe_if_init() can fail, but it's return value wasn't checked Signed-off-by: Chris Leech Signed-off-by: Robert Love Signed-off-by: James Bottomley --- drivers/scsi/fcoe/fcoe.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/scsi/fcoe/fcoe.c') diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index a39d370bccf..d205ac08d07 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -1900,7 +1900,9 @@ static int __init fcoe_init(void) /* Setup link change notification */ fcoe_dev_setup(); - fcoe_if_init(); + rc = fcoe_if_init(); + if (rc) + goto out_free; return 0; -- cgit v1.2.3-70-g09d2 From 36eb7fc872bdd7f7443ce24a718784bfa567f6c7 Mon Sep 17 00:00:00 2001 From: Joe Eykholt Date: Tue, 25 Aug 2009 13:59:19 -0700 Subject: [SCSI] fcoe: remove unnecessary list and lock initializations. The hostlist and the hostlist_lock were initialized both in the delcaration and in fcoe_init(). Remove the unneeded code. Signed-off-by: Joe Eykholt Signed-off-by: Chris Leech Signed-off-by: Robert Love Signed-off-by: James Bottomley --- drivers/scsi/fcoe/fcoe.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/scsi/fcoe/fcoe.c') diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index d205ac08d07..e481882a3e8 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -1881,9 +1881,6 @@ static int __init fcoe_init(void) int rc = 0; struct fcoe_percpu_s *p; - INIT_LIST_HEAD(&fcoe_hostlist); - rwlock_init(&fcoe_hostlist_lock); - for_each_possible_cpu(cpu) { p = &per_cpu(fcoe_percpu, cpu); skb_queue_head_init(&p->fcoe_rx_list); -- cgit v1.2.3-70-g09d2 From af7f85d95a34b04f5cf8c3b8c2d4cdd179dc1e19 Mon Sep 17 00:00:00 2001 From: Chris Leech Date: Tue, 25 Aug 2009 13:59:24 -0700 Subject: [SCSI] fcoe: interface changes to fcoe_if_create and fcoe_if_destroy By passing in the parent device instead of assuming the netdev is what should be used, fcoe_if_create becomes usable for NPIV vports as well. You still need a netdev, because that's how FCoE works. Also removed some duplicate checks from fcoe_if_create that are already in fcoe_create. fcoe_if_destroy needs to take an lport as it's only argument, not a netdev. That removes the 1:1 netdev:lport assumption from the destroy path. Signed-off-by: Chris Leech Signed-off-by: Robert Love Signed-off-by: James Bottomley --- drivers/scsi/fcoe/fcoe.c | 113 ++++++++++++++++++++--------------------------- 1 file changed, 48 insertions(+), 65 deletions(-) (limited to 'drivers/scsi/fcoe/fcoe.c') diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index e481882a3e8..74927969813 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -1,5 +1,5 @@ /* - * Copyright(c) 2007 - 2008 Intel Corporation. All rights reserved. + * Copyright(c) 2007 - 2009 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -506,30 +506,20 @@ skip_oem: /** * fcoe_if_destroy() - FCoE software HBA tear-down function - * @netdev: ptr to the associated net_device - * - * Returns: 0 if link is OK for use by FCoE. + * @lport: fc_lport to destroy */ -static int fcoe_if_destroy(struct net_device *netdev) +static void fcoe_if_destroy(struct fc_lport *lport) { - struct fc_lport *lp = NULL; - struct fcoe_softc *fc; - - BUG_ON(!netdev); + struct fcoe_softc *fc = lport_priv(lport); + struct net_device *netdev = fc->netdev; FCOE_NETDEV_DBG(netdev, "Destroying interface\n"); - lp = fcoe_hostlist_lookup(netdev); - if (!lp) - return -ENODEV; - - fc = lport_priv(lp); - /* Logout of the fabric */ - fc_fabric_logoff(lp); + fc_fabric_logoff(lport); /* Remove the instance from fcoe's list */ - fcoe_hostlist_remove(lp); + fcoe_hostlist_remove(lport); /* clean up netdev configurations */ fcoe_netdev_cleanup(fc); @@ -538,33 +528,31 @@ static int fcoe_if_destroy(struct net_device *netdev) fcoe_ctlr_destroy(&fc->ctlr); /* Free queued packets for the per-CPU receive threads */ - fcoe_percpu_clean(lp); + fcoe_percpu_clean(lport); /* Cleanup the fc_lport */ - fc_lport_destroy(lp); - fc_fcp_destroy(lp); + fc_lport_destroy(lport); + fc_fcp_destroy(lport); /* Detach from the scsi-ml */ - fc_remove_host(lp->host); - scsi_remove_host(lp->host); + fc_remove_host(lport->host); + scsi_remove_host(lport->host); /* There are no more rports or I/O, free the EM */ - fc_exch_mgr_free(lp); + fc_exch_mgr_free(lport); /* Free existing skbs */ - fcoe_clean_pending_queue(lp); + fcoe_clean_pending_queue(lport); /* Stop the timer */ del_timer_sync(&fc->timer); /* Free memory used by statistical counters */ - fc_lport_free_stats(lp); + fc_lport_free_stats(lport); /* Release the net_device and Scsi_Host */ dev_put(netdev); - scsi_host_put(lp->host); - - return 0; + scsi_host_put(lport->host); } /* @@ -612,38 +600,35 @@ static struct libfc_function_template fcoe_libfc_fcn_templ = { /** * fcoe_if_create() - this function creates the fcoe interface * @netdev: pointer the associated netdevice + * @parent: device pointer to be the parent in sysfs for the SCSI host * * Creates fc_lport struct and scsi_host for lport, configures lport * and starts fabric login. * - * Returns : 0 on success + * Returns : The allocated fc_lport or an error pointer */ -static int fcoe_if_create(struct net_device *netdev) +static struct fc_lport *fcoe_if_create(struct net_device *netdev, + struct device *parent) { int rc; - struct fc_lport *lp = NULL; + struct fc_lport *lport = NULL; struct fcoe_softc *fc; struct Scsi_Host *shost; - BUG_ON(!netdev); - FCOE_NETDEV_DBG(netdev, "Create Interface\n"); - lp = fcoe_hostlist_lookup(netdev); - if (lp) - return -EEXIST; - shost = libfc_host_alloc(&fcoe_shost_template, sizeof(struct fcoe_softc)); if (!shost) { FCOE_NETDEV_DBG(netdev, "Could not allocate host structure\n"); - return -ENOMEM; + rc = -ENOMEM; + goto out; } - lp = shost_priv(shost); - fc = lport_priv(lp); + lport = shost_priv(shost); + fc = lport_priv(lport); /* configure fc_lport, e.g., em */ - rc = fcoe_lport_config(lp); + rc = fcoe_lport_config(lport); if (rc) { FCOE_NETDEV_DBG(netdev, "Could not configure lport for the " "interface\n"); @@ -658,7 +643,7 @@ static int fcoe_if_create(struct net_device *netdev) fc->ctlr.update_mac = fcoe_update_src_mac; /* configure lport network properties */ - rc = fcoe_netdev_config(lp, netdev); + rc = fcoe_netdev_config(lport, netdev); if (rc) { FCOE_NETDEV_DBG(netdev, "Could not configure netdev for the " "interface\n"); @@ -666,7 +651,7 @@ static int fcoe_if_create(struct net_device *netdev) } /* configure lport scsi host properties */ - rc = fcoe_shost_config(lp, shost, &netdev->dev); + rc = fcoe_shost_config(lport, shost, parent); if (rc) { FCOE_NETDEV_DBG(netdev, "Could not configure shost for the " "interface\n"); @@ -674,7 +659,7 @@ static int fcoe_if_create(struct net_device *netdev) } /* Initialize the library */ - rc = fcoe_libfc_config(lp, &fcoe_libfc_fcn_templ); + rc = fcoe_libfc_config(lport, &fcoe_libfc_fcn_templ); if (rc) { FCOE_NETDEV_DBG(netdev, "Could not configure libfc for the " "interface\n"); @@ -689,7 +674,7 @@ static int fcoe_if_create(struct net_device *netdev) */ write_lock(&fcoe_hostlist_lock); /* lport exch manager allocation */ - rc = fcoe_em_config(lp); + rc = fcoe_em_config(lport); if (rc) { FCOE_NETDEV_DBG(netdev, "Could not configure the EM for the " "interface\n"); @@ -697,27 +682,28 @@ static int fcoe_if_create(struct net_device *netdev) } /* add to lports list */ - fcoe_hostlist_add(lp); + fcoe_hostlist_add(lport); write_unlock(&fcoe_hostlist_lock); - lp->boot_time = jiffies; + lport->boot_time = jiffies; - fc_fabric_login(lp); + fc_fabric_login(lport); - if (!fcoe_link_ok(lp)) + if (!fcoe_link_ok(lport)) fcoe_ctlr_link_up(&fc->ctlr); dev_hold(netdev); - return rc; + return lport; out_lp_destroy: - fc_exch_mgr_free(lp); + fc_exch_mgr_free(lport); out_netdev_cleanup: fcoe_netdev_cleanup(fc); out_host_put: - scsi_host_put(lp->host); - return rc; + scsi_host_put(lport->host); +out: + return ERR_PTR(rc); } /** @@ -1616,8 +1602,9 @@ static int fcoe_ethdrv_put(const struct net_device *netdev) */ static int fcoe_destroy(const char *buffer, struct kernel_param *kp) { - int rc; struct net_device *netdev; + struct fc_lport *lport; + int rc; netdev = fcoe_if_to_netdev(buffer); if (!netdev) { @@ -1625,17 +1612,12 @@ static int fcoe_destroy(const char *buffer, struct kernel_param *kp) goto out_nodev; } /* look for existing lport */ - if (!fcoe_hostlist_lookup(netdev)) { + lport = fcoe_hostlist_lookup(netdev); + if (!lport) { rc = -ENODEV; goto out_putdev; } - rc = fcoe_if_destroy(netdev); - if (rc) { - printk(KERN_ERR "fcoe: Failed to destroy interface (%s)\n", - netdev->name); - rc = -EIO; - goto out_putdev; - } + fcoe_if_destroy(lport); fcoe_ethdrv_put(netdev); rc = 0; out_putdev: @@ -1654,6 +1636,7 @@ out_nodev: static int fcoe_create(const char *buffer, struct kernel_param *kp) { int rc; + struct fc_lport *lport; struct net_device *netdev; netdev = fcoe_if_to_netdev(buffer); @@ -1668,8 +1651,8 @@ static int fcoe_create(const char *buffer, struct kernel_param *kp) } fcoe_ethdrv_get(netdev); - rc = fcoe_if_create(netdev); - if (rc) { + lport = fcoe_if_create(netdev, &netdev->dev); + if (IS_ERR(lport)) { printk(KERN_ERR "fcoe: Failed to create interface (%s)\n", netdev->name); fcoe_ethdrv_put(netdev); @@ -1926,7 +1909,7 @@ static void __exit fcoe_exit(void) /* releases the associated fcoe hosts */ list_for_each_entry_safe(fc, tmp, &fcoe_hostlist, list) - fcoe_if_destroy(fc->netdev); + fcoe_if_destroy(fc->ctlr.lp); unregister_hotcpu_notifier(&fcoe_cpu_notifier); -- cgit v1.2.3-70-g09d2 From 014f5c3f560a336cb8ad5b9f828c85de0398e7bb Mon Sep 17 00:00:00 2001 From: Chris Leech Date: Tue, 25 Aug 2009 13:59:30 -0700 Subject: [SCSI] fcoe: Introduce and allocate fcoe_interface structure, 1:1 with net_device In preparation for NPIV support, I'm splitting the fcoe instance structure into two to remove the assumptions about it being 1:1 with the net_device. There will now be two structures, one which is 1:1 with the underlying net_device and one which is allocated per virtual SCSI/FC host. fcoe_softc is renamed to fcoe_port for the per Scsi_Host FCoE private data. Later patches with start moving shared stuff from fcoe_port to fcoe_interface Signed-off-by: Chris Leech Signed-off-by: Robert Love Signed-off-by: James Bottomley --- drivers/scsi/fcoe/fcoe.c | 326 +++++++++++++++++++++++++---------------------- drivers/scsi/fcoe/fcoe.h | 19 ++- 2 files changed, 188 insertions(+), 157 deletions(-) (limited to 'drivers/scsi/fcoe/fcoe.c') diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index 74927969813..0ae54b2bce3 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -151,10 +151,10 @@ static int fcoe_fip_recv(struct sk_buff *skb, struct net_device *dev, struct packet_type *ptype, struct net_device *orig_dev) { - struct fcoe_softc *fc; + struct fcoe_port *port; - fc = container_of(ptype, struct fcoe_softc, fip_packet_type); - fcoe_ctlr_recv(&fc->ctlr, skb); + port = container_of(ptype, struct fcoe_port, fip_packet_type); + fcoe_ctlr_recv(&port->ctlr, skb); return 0; } @@ -180,13 +180,13 @@ 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) { - struct fcoe_softc *fc; + struct fcoe_port *port; - fc = fcoe_from_ctlr(fip); + port = fcoe_from_ctlr(fip); rtnl_lock(); if (!is_zero_ether_addr(old)) - dev_unicast_delete(fc->netdev, old); - dev_unicast_add(fc->netdev, new); + dev_unicast_delete(port->netdev, old); + dev_unicast_add(port->netdev, new); rtnl_unlock(); } @@ -224,25 +224,25 @@ static int fcoe_lport_config(struct fc_lport *lp) /** * fcoe_netdev_cleanup() - clean up netdev configurations - * @fc: ptr to the fcoe_softc + * @port: ptr to the fcoe_port */ -void fcoe_netdev_cleanup(struct fcoe_softc *fc) +void fcoe_netdev_cleanup(struct fcoe_port *port) { u8 flogi_maddr[ETH_ALEN]; /* Don't listen for Ethernet packets anymore */ - dev_remove_pack(&fc->fcoe_packet_type); - dev_remove_pack(&fc->fip_packet_type); + dev_remove_pack(&port->fcoe_packet_type); + dev_remove_pack(&port->fip_packet_type); /* Delete secondary MAC addresses */ rtnl_lock(); memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN); - dev_unicast_delete(fc->netdev, flogi_maddr); - if (!is_zero_ether_addr(fc->ctlr.data_src_addr)) - dev_unicast_delete(fc->netdev, fc->ctlr.data_src_addr); - if (fc->ctlr.spma) - dev_unicast_delete(fc->netdev, fc->ctlr.ctl_src_addr); - dev_mc_delete(fc->netdev, FIP_ALL_ENODE_MACS, ETH_ALEN, 0); + dev_unicast_delete(port->netdev, flogi_maddr); + if (!is_zero_ether_addr(port->ctlr.data_src_addr)) + dev_unicast_delete(port->netdev, port->ctlr.data_src_addr); + if (port->ctlr.spma) + dev_unicast_delete(port->netdev, port->ctlr.ctl_src_addr); + dev_mc_delete(port->netdev, FIP_ALL_ENODE_MACS, ETH_ALEN, 0); rtnl_unlock(); } @@ -271,14 +271,14 @@ static int fcoe_netdev_config(struct fc_lport *lp, struct net_device *netdev) { u32 mfs; u64 wwnn, wwpn; - struct fcoe_softc *fc; + struct fcoe_port *port; u8 flogi_maddr[ETH_ALEN]; struct netdev_hw_addr *ha; /* Setup lport private data to point to fcoe softc */ - fc = lport_priv(lp); - fc->ctlr.lp = lp; - fc->netdev = netdev; + port = lport_priv(lp); + port->ctlr.lp = lp; + port->netdev = netdev; /* Do not support for bonding device */ if ((netdev->priv_flags & IFF_MASTER_ALB) || @@ -317,27 +317,27 @@ static int fcoe_netdev_config(struct fc_lport *lp, struct net_device *netdev) FCOE_NETDEV_DBG(netdev, "Supports LRO for max xid 0x%x\n", lp->lro_xid); } - skb_queue_head_init(&fc->fcoe_pending_queue); - fc->fcoe_pending_queue_active = 0; - setup_timer(&fc->timer, fcoe_queue_timer, (unsigned long)lp); + skb_queue_head_init(&port->fcoe_pending_queue); + port->fcoe_pending_queue_active = 0; + setup_timer(&port->timer, fcoe_queue_timer, (unsigned long)lp); /* look for SAN MAC address, if multiple SAN MACs exist, only * use the first one for SPMA */ rcu_read_lock(); for_each_dev_addr(netdev, ha) { if ((ha->type == NETDEV_HW_ADDR_T_SAN) && - (is_valid_ether_addr(ha->addr))) { - memcpy(fc->ctlr.ctl_src_addr, ha->addr, ETH_ALEN); - fc->ctlr.spma = 1; + (is_valid_ether_addr(port->ctlr.ctl_src_addr))) { + memcpy(port->ctlr.ctl_src_addr, ha->addr, ETH_ALEN); + port->ctlr.spma = 1; break; } } rcu_read_unlock(); /* setup Source Mac Address */ - if (!fc->ctlr.spma) - memcpy(fc->ctlr.ctl_src_addr, netdev->dev_addr, - fc->netdev->addr_len); + if (!port->ctlr.spma) + memcpy(port->ctlr.ctl_src_addr, netdev->dev_addr, + netdev->addr_len); wwnn = fcoe_wwn_from_mac(netdev->dev_addr, 1, 0); fc_set_wwnn(lp, wwnn); @@ -353,8 +353,8 @@ static int fcoe_netdev_config(struct fc_lport *lp, struct net_device *netdev) rtnl_lock(); memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN); dev_unicast_add(netdev, flogi_maddr); - if (fc->ctlr.spma) - dev_unicast_add(netdev, fc->ctlr.ctl_src_addr); + if (port->ctlr.spma) + dev_unicast_add(netdev, port->ctlr.ctl_src_addr); dev_mc_add(netdev, FIP_ALL_ENODE_MACS, ETH_ALEN, 0); rtnl_unlock(); @@ -362,15 +362,15 @@ static int fcoe_netdev_config(struct fc_lport *lp, struct net_device *netdev) * setup the receive function from ethernet driver * on the ethertype for the given device */ - fc->fcoe_packet_type.func = fcoe_rcv; - fc->fcoe_packet_type.type = __constant_htons(ETH_P_FCOE); - fc->fcoe_packet_type.dev = netdev; - dev_add_pack(&fc->fcoe_packet_type); + port->fcoe_packet_type.func = fcoe_rcv; + port->fcoe_packet_type.type = __constant_htons(ETH_P_FCOE); + port->fcoe_packet_type.dev = netdev; + dev_add_pack(&port->fcoe_packet_type); - fc->fip_packet_type.func = fcoe_fip_recv; - fc->fip_packet_type.type = htons(ETH_P_FIP); - fc->fip_packet_type.dev = netdev; - dev_add_pack(&fc->fip_packet_type); + port->fip_packet_type.func = fcoe_fip_recv; + port->fip_packet_type.type = htons(ETH_P_FIP); + port->fip_packet_type.dev = netdev; + dev_add_pack(&port->fip_packet_type); return 0; } @@ -426,7 +426,7 @@ bool fcoe_oem_match(struct fc_frame *fp) /** * fcoe_em_config() - allocates em for this lport - * @lp: the port that em is to allocated for + * @lp: the fcoe that em is to allocated for * * Called with write fcoe_hostlist_lock held. * @@ -434,8 +434,9 @@ bool fcoe_oem_match(struct fc_frame *fp) */ static inline int fcoe_em_config(struct fc_lport *lp) { - struct fcoe_softc *fc = lport_priv(lp); - struct fcoe_softc *oldfc = NULL; + struct fcoe_interface *fcoe; + struct fcoe_port *port = lport_priv(lp); + struct fcoe_port *oldfc = NULL; struct net_device *old_real_dev, *cur_real_dev; u16 min_xid = FCOE_MIN_XID; u16 max_xid = FCOE_MAX_XID; @@ -453,38 +454,39 @@ static inline int fcoe_em_config(struct fc_lport *lp) * Reuse existing offload em instance in case * it is already allocated on real eth device */ - if (fc->netdev->priv_flags & IFF_802_1Q_VLAN) - cur_real_dev = vlan_dev_real_dev(fc->netdev); + if (port->netdev->priv_flags & IFF_802_1Q_VLAN) + cur_real_dev = vlan_dev_real_dev(port->netdev); else - cur_real_dev = fc->netdev; + cur_real_dev = port->netdev; - list_for_each_entry(oldfc, &fcoe_hostlist, list) { + list_for_each_entry(fcoe, &fcoe_hostlist, list) { + oldfc = fcoe->priv; if (oldfc->netdev->priv_flags & IFF_802_1Q_VLAN) old_real_dev = vlan_dev_real_dev(oldfc->netdev); else old_real_dev = oldfc->netdev; if (cur_real_dev == old_real_dev) { - fc->oem = oldfc->oem; + port->oem = oldfc->oem; break; } } - if (fc->oem) { - if (!fc_exch_mgr_add(lp, fc->oem, fcoe_oem_match)) { + if (port->oem) { + if (!fc_exch_mgr_add(lp, port->oem, fcoe_oem_match)) { printk(KERN_ERR "fcoe_em_config: failed to add " "offload em:%p on interface:%s\n", - fc->oem, fc->netdev->name); + port->oem, port->netdev->name); return -ENOMEM; } } else { - fc->oem = fc_exch_mgr_alloc(lp, FC_CLASS_3, + port->oem = fc_exch_mgr_alloc(lp, FC_CLASS_3, FCOE_MIN_XID, lp->lro_xid, fcoe_oem_match); - if (!fc->oem) { + if (!port->oem) { printk(KERN_ERR "fcoe_em_config: failed to allocate " "em for offload exches on interface:%s\n", - fc->netdev->name); + port->netdev->name); return -ENOMEM; } } @@ -497,7 +499,7 @@ static inline int fcoe_em_config(struct fc_lport *lp) skip_oem: if (!fc_exch_mgr_alloc(lp, FC_CLASS_3, min_xid, max_xid, NULL)) { printk(KERN_ERR "fcoe_em_config: failed to " - "allocate em on interface %s\n", fc->netdev->name); + "allocate em on interface %s\n", port->netdev->name); return -ENOMEM; } @@ -510,8 +512,9 @@ skip_oem: */ static void fcoe_if_destroy(struct fc_lport *lport) { - struct fcoe_softc *fc = lport_priv(lport); - struct net_device *netdev = fc->netdev; + struct fcoe_port *port = lport_priv(lport); + struct fcoe_interface *fcoe = port->fcoe; + struct net_device *netdev = port->netdev; FCOE_NETDEV_DBG(netdev, "Destroying interface\n"); @@ -522,10 +525,10 @@ static void fcoe_if_destroy(struct fc_lport *lport) fcoe_hostlist_remove(lport); /* clean up netdev configurations */ - fcoe_netdev_cleanup(fc); + fcoe_netdev_cleanup(port); /* tear-down the FCoE controller */ - fcoe_ctlr_destroy(&fc->ctlr); + fcoe_ctlr_destroy(&port->ctlr); /* Free queued packets for the per-CPU receive threads */ fcoe_percpu_clean(lport); @@ -545,7 +548,7 @@ static void fcoe_if_destroy(struct fc_lport *lport) fcoe_clean_pending_queue(lport); /* Stop the timer */ - del_timer_sync(&fc->timer); + del_timer_sync(&port->timer); /* Free memory used by statistical counters */ fc_lport_free_stats(lport); @@ -553,6 +556,7 @@ static void fcoe_if_destroy(struct fc_lport *lport) /* Release the net_device and Scsi_Host */ dev_put(netdev); scsi_host_put(lport->host); + kfree(fcoe); /* TODO, should be refcounted */ } /* @@ -612,20 +616,31 @@ static struct fc_lport *fcoe_if_create(struct net_device *netdev, { int rc; struct fc_lport *lport = NULL; - struct fcoe_softc *fc; + struct fcoe_port *port; + struct fcoe_interface *fcoe; struct Scsi_Host *shost; FCOE_NETDEV_DBG(netdev, "Create Interface\n"); + fcoe = kzalloc(sizeof(*fcoe), GFP_KERNEL); + if (!fcoe) { + FCOE_NETDEV_DBG(netdev, "Could not allocate fcoe structure\n"); + rc = -ENOMEM; + goto out; + } + shost = libfc_host_alloc(&fcoe_shost_template, - sizeof(struct fcoe_softc)); + sizeof(struct fcoe_port)); if (!shost) { FCOE_NETDEV_DBG(netdev, "Could not allocate host structure\n"); rc = -ENOMEM; - goto out; + goto out_kfree_port; } lport = shost_priv(shost); - fc = lport_priv(lport); + port = lport_priv(lport); + + port->fcoe = fcoe; + fcoe->priv = port; /* configure fc_lport, e.g., em */ rc = fcoe_lport_config(lport); @@ -638,9 +653,9 @@ static struct fc_lport *fcoe_if_create(struct net_device *netdev, /* * Initialize FIP. */ - fcoe_ctlr_init(&fc->ctlr); - fc->ctlr.send = fcoe_fip_send; - fc->ctlr.update_mac = fcoe_update_src_mac; + fcoe_ctlr_init(&port->ctlr); + port->ctlr.send = fcoe_fip_send; + port->ctlr.update_mac = fcoe_update_src_mac; /* configure lport network properties */ rc = fcoe_netdev_config(lport, netdev); @@ -690,7 +705,7 @@ static struct fc_lport *fcoe_if_create(struct net_device *netdev, fc_fabric_login(lport); if (!fcoe_link_ok(lport)) - fcoe_ctlr_link_up(&fc->ctlr); + fcoe_ctlr_link_up(&port->ctlr); dev_hold(netdev); @@ -699,9 +714,11 @@ static struct fc_lport *fcoe_if_create(struct net_device *netdev, out_lp_destroy: fc_exch_mgr_free(lport); out_netdev_cleanup: - fcoe_netdev_cleanup(fc); + fcoe_netdev_cleanup(port); out_host_put: scsi_host_put(lport->host); +out_kfree_port: + kfree(fcoe); out: return ERR_PTR(rc); } @@ -902,13 +919,13 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *dev, { struct fc_lport *lp; struct fcoe_rcv_info *fr; - struct fcoe_softc *fc; + struct fcoe_port *port; struct fc_frame_header *fh; struct fcoe_percpu_s *fps; unsigned int cpu; - fc = container_of(ptype, struct fcoe_softc, fcoe_packet_type); - lp = fc->ctlr.lp; + port = container_of(ptype, struct fcoe_port, fcoe_packet_type); + lp = port->ctlr.lp; if (unlikely(lp == NULL)) { FCOE_NETDEV_DBG(dev, "Cannot find hba structure"); goto err2; @@ -1059,7 +1076,7 @@ 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 * - * This uses crc32() to calculate the crc for fc frame + * This uses crc32() to calculate the crc for port frame * Return : 32 bit crc */ u32 fcoe_fc_crc(struct fc_frame *fp) @@ -1092,7 +1109,7 @@ u32 fcoe_fc_crc(struct fc_frame *fp) /** * fcoe_xmit() - FCoE frame transmit function - * @lp: the associated local port + * @lp: the associated local fcoe * @fp: the fc_frame to be transmitted * * Return : 0 for success @@ -1109,13 +1126,13 @@ 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_softc *fc; + struct fcoe_port *port; u8 sof, eof; struct fcoe_hdr *hp; WARN_ON((fr_len(fp) % sizeof(u32)) != 0); - fc = lport_priv(lp); + port = lport_priv(lp); fh = fc_frame_header_get(fp); skb = fp_skb(fp); wlen = skb->len / FCOE_WORD_TO_BYTE; @@ -1126,7 +1143,7 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp) } if (unlikely(fh->fh_r_ctl == FC_RCTL_ELS_REQ) && - fcoe_ctlr_els_send(&fc->ctlr, skb)) + fcoe_ctlr_els_send(&port->ctlr, skb)) return 0; sof = fr_sof(fp); @@ -1148,7 +1165,7 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp) crc = fcoe_fc_crc(fp); } - /* copy fc crc and eof to the skb buff */ + /* copy port crc and eof to the skb buff */ if (skb_is_nonlinear(skb)) { skb_frag_t *frag; if (fcoe_get_paged_crc_eof(skb, tlen)) { @@ -1171,27 +1188,27 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp) cp = NULL; } - /* adjust skb network/transport offsets to match mac/fcoe/fc */ + /* adjust skb network/transport offsets to match mac/fcoe/port */ skb_push(skb, elen + hlen); skb_reset_mac_header(skb); skb_reset_network_header(skb); skb->mac_len = elen; skb->protocol = htons(ETH_P_FCOE); - skb->dev = fc->netdev; + skb->dev = port->netdev; /* fill up mac and fcoe headers */ eh = eth_hdr(skb); eh->h_proto = htons(ETH_P_FCOE); - if (fc->ctlr.map_dest) + if (port->ctlr.map_dest) fc_fcoe_set_mac(eh->h_dest, fh->fh_d_id); else /* insert GW address */ - memcpy(eh->h_dest, fc->ctlr.dest_addr, ETH_ALEN); + memcpy(eh->h_dest, port->ctlr.dest_addr, ETH_ALEN); - if (unlikely(fc->ctlr.flogi_oxid != FC_XID_UNKNOWN)) - memcpy(eh->h_source, fc->ctlr.ctl_src_addr, ETH_ALEN); + if (unlikely(port->ctlr.flogi_oxid != FC_XID_UNKNOWN)) + memcpy(eh->h_source, port->ctlr.ctl_src_addr, ETH_ALEN); else - memcpy(eh->h_source, fc->ctlr.data_src_addr, ETH_ALEN); + memcpy(eh->h_source, port->ctlr.data_src_addr, ETH_ALEN); hp = (struct fcoe_hdr *)(eh + 1); memset(hp, 0, sizeof(*hp)); @@ -1214,7 +1231,7 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp) /* send down to lld */ fr_dev(fp) = lp; - if (fc->fcoe_pending_queue.qlen) + if (port->fcoe_pending_queue.qlen) fcoe_check_wait_queue(lp, skb); else if (fcoe_start_io(skb)) fcoe_check_wait_queue(lp, skb); @@ -1240,7 +1257,7 @@ int fcoe_percpu_receive_thread(void *arg) struct fcoe_crc_eof crc_eof; struct fc_frame *fp; u8 *mac = NULL; - struct fcoe_softc *fc; + struct fcoe_port *port; struct fcoe_hdr *hp; set_user_nice(current, -20); @@ -1276,7 +1293,7 @@ int fcoe_percpu_receive_thread(void *arg) /* * Save source MAC address before discarding header. */ - fc = lport_priv(lp); + port = lport_priv(lp); if (skb_is_nonlinear(skb)) skb_linearize(skb); /* not ideal */ mac = eth_hdr(skb)->h_source; @@ -1354,8 +1371,8 @@ int fcoe_percpu_receive_thread(void *arg) } fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED; } - if (unlikely(fc->ctlr.flogi_oxid != FC_XID_UNKNOWN) && - fcoe_ctlr_recv_flogi(&fc->ctlr, fp, mac)) { + if (unlikely(port->ctlr.flogi_oxid != FC_XID_UNKNOWN) && + fcoe_ctlr_recv_flogi(&port->ctlr, fp, mac)) { fc_frame_free(fp); continue; } @@ -1379,46 +1396,46 @@ int fcoe_percpu_receive_thread(void *arg) */ static void fcoe_check_wait_queue(struct fc_lport *lp, struct sk_buff *skb) { - struct fcoe_softc *fc = lport_priv(lp); + struct fcoe_port *port = lport_priv(lp); int rc; - spin_lock_bh(&fc->fcoe_pending_queue.lock); + spin_lock_bh(&port->fcoe_pending_queue.lock); if (skb) - __skb_queue_tail(&fc->fcoe_pending_queue, skb); + __skb_queue_tail(&port->fcoe_pending_queue, skb); - if (fc->fcoe_pending_queue_active) + if (port->fcoe_pending_queue_active) goto out; - fc->fcoe_pending_queue_active = 1; + port->fcoe_pending_queue_active = 1; - while (fc->fcoe_pending_queue.qlen) { + while (port->fcoe_pending_queue.qlen) { /* keep qlen > 0 until fcoe_start_io succeeds */ - fc->fcoe_pending_queue.qlen++; - skb = __skb_dequeue(&fc->fcoe_pending_queue); + port->fcoe_pending_queue.qlen++; + skb = __skb_dequeue(&port->fcoe_pending_queue); - spin_unlock_bh(&fc->fcoe_pending_queue.lock); + spin_unlock_bh(&port->fcoe_pending_queue.lock); rc = fcoe_start_io(skb); - spin_lock_bh(&fc->fcoe_pending_queue.lock); + spin_lock_bh(&port->fcoe_pending_queue.lock); if (rc) { - __skb_queue_head(&fc->fcoe_pending_queue, skb); + __skb_queue_head(&port->fcoe_pending_queue, skb); /* undo temporary increment above */ - fc->fcoe_pending_queue.qlen--; + port->fcoe_pending_queue.qlen--; break; } /* undo temporary increment above */ - fc->fcoe_pending_queue.qlen--; + port->fcoe_pending_queue.qlen--; } - if (fc->fcoe_pending_queue.qlen < FCOE_LOW_QUEUE_DEPTH) + if (port->fcoe_pending_queue.qlen < FCOE_LOW_QUEUE_DEPTH) lp->qfull = 0; - if (fc->fcoe_pending_queue.qlen && !timer_pending(&fc->timer)) - mod_timer(&fc->timer, jiffies + 2); - fc->fcoe_pending_queue_active = 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 (fc->fcoe_pending_queue.qlen > FCOE_MAX_QUEUE_DEPTH) + if (port->fcoe_pending_queue.qlen > FCOE_MAX_QUEUE_DEPTH) lp->qfull = 1; - spin_unlock_bh(&fc->fcoe_pending_queue.lock); + spin_unlock_bh(&port->fcoe_pending_queue.lock); return; } @@ -1453,16 +1470,18 @@ static int fcoe_device_notification(struct notifier_block *notifier, { struct fc_lport *lp = NULL; struct net_device *netdev = ptr; - struct fcoe_softc *fc; + struct fcoe_interface *fcoe; + struct fcoe_port *port = NULL; struct fcoe_dev_stats *stats; u32 link_possible = 1; u32 mfs; int rc = NOTIFY_OK; read_lock(&fcoe_hostlist_lock); - list_for_each_entry(fc, &fcoe_hostlist, list) { - if (fc->netdev == netdev) { - lp = fc->ctlr.lp; + list_for_each_entry(fcoe, &fcoe_hostlist, list) { + port = fcoe->priv; + if (port->netdev == netdev) { + lp = port->ctlr.lp; break; } } @@ -1493,8 +1512,8 @@ static int fcoe_device_notification(struct notifier_block *notifier, "from netdev netlink\n", event); } if (link_possible && !fcoe_link_ok(lp)) - fcoe_ctlr_link_up(&fc->ctlr); - else if (fcoe_ctlr_link_down(&fc->ctlr)) { + fcoe_ctlr_link_up(&port->ctlr); + else if (fcoe_ctlr_link_down(&port->ctlr)) { stats = fc_lport_get_stats(lp); stats->LinkFailureCount++; fcoe_clean_pending_queue(lp); @@ -1668,10 +1687,10 @@ out_nodev: module_param_call(create, fcoe_create, NULL, NULL, S_IWUSR); __MODULE_PARM_TYPE(create, "string"); -MODULE_PARM_DESC(create, "Create fcoe port using net device passed in."); +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 port"); +MODULE_PARM_DESC(destroy, "Destroy fcoe fcoe"); /** * fcoe_link_ok() - Check if link is ok for the fc_lport @@ -1689,8 +1708,8 @@ MODULE_PARM_DESC(destroy, "Destroy fcoe port"); */ int fcoe_link_ok(struct fc_lport *lp) { - struct fcoe_softc *fc = lport_priv(lp); - struct net_device *dev = fc->netdev; + struct fcoe_port *port = lport_priv(lp); + struct net_device *dev = port->netdev; struct ethtool_cmd ecmd = { ETHTOOL_GSET }; if ((dev->flags & IFF_UP) && netif_carrier_ok(dev) && @@ -1752,16 +1771,16 @@ void fcoe_percpu_clean(struct fc_lport *lp) */ void fcoe_clean_pending_queue(struct fc_lport *lp) { - struct fcoe_softc *fc = lport_priv(lp); + struct fcoe_port *port = lport_priv(lp); struct sk_buff *skb; - spin_lock_bh(&fc->fcoe_pending_queue.lock); - while ((skb = __skb_dequeue(&fc->fcoe_pending_queue)) != NULL) { - spin_unlock_bh(&fc->fcoe_pending_queue.lock); + spin_lock_bh(&port->fcoe_pending_queue.lock); + while ((skb = __skb_dequeue(&port->fcoe_pending_queue)) != NULL) { + spin_unlock_bh(&port->fcoe_pending_queue.lock); kfree_skb(skb); - spin_lock_bh(&fc->fcoe_pending_queue.lock); + spin_lock_bh(&port->fcoe_pending_queue.lock); } - spin_unlock_bh(&fc->fcoe_pending_queue.lock); + spin_unlock_bh(&port->fcoe_pending_queue.lock); } /** @@ -1778,21 +1797,21 @@ int fcoe_reset(struct Scsi_Host *shost) } /** - * fcoe_hostlist_lookup_softc() - find the corresponding lport by a given device + * fcoe_hostlist_lookup_port() - find the corresponding lport by a given device * @dev: this is currently ptr to net_device * * Called with fcoe_hostlist_lock held. * - * Returns: NULL or the located fcoe_softc + * Returns: NULL or the located fcoe_port */ -static struct fcoe_softc * -fcoe_hostlist_lookup_softc(const struct net_device *dev) +static struct fcoe_interface * +fcoe_hostlist_lookup_port(const struct net_device *dev) { - struct fcoe_softc *fc; + struct fcoe_interface *fcoe; - list_for_each_entry(fc, &fcoe_hostlist, list) { - if (fc->netdev == dev) - return fc; + list_for_each_entry(fcoe, &fcoe_hostlist, list) { + if (fcoe->priv->netdev == dev) + return fcoe; } return NULL; } @@ -1805,13 +1824,13 @@ fcoe_hostlist_lookup_softc(const struct net_device *dev) */ struct fc_lport *fcoe_hostlist_lookup(const struct net_device *netdev) { - struct fcoe_softc *fc; + struct fcoe_interface *fcoe; read_lock(&fcoe_hostlist_lock); - fc = fcoe_hostlist_lookup_softc(netdev); + fcoe = fcoe_hostlist_lookup_port(netdev); read_unlock(&fcoe_hostlist_lock); - return (fc) ? fc->ctlr.lp : NULL; + return (fcoe) ? fcoe->priv->ctlr.lp : NULL; } /** @@ -1822,14 +1841,16 @@ struct fc_lport *fcoe_hostlist_lookup(const struct net_device *netdev) * * Returns: 0 for success */ -int fcoe_hostlist_add(const struct fc_lport *lp) +int fcoe_hostlist_add(const struct fc_lport *lport) { - struct fcoe_softc *fc; - - fc = fcoe_hostlist_lookup_softc(fcoe_netdev(lp)); - if (!fc) { - fc = lport_priv(lp); - list_add_tail(&fc->list, &fcoe_hostlist); + struct fcoe_interface *fcoe; + struct fcoe_port *port; + + fcoe = fcoe_hostlist_lookup_port(fcoe_netdev(lport)); + if (!fcoe) { + port = lport_priv(lport); + fcoe = port->fcoe; + list_add_tail(&fcoe->list, &fcoe_hostlist); } return 0; } @@ -1840,14 +1861,14 @@ int fcoe_hostlist_add(const struct fc_lport *lp) * * Returns: 0 for success */ -int fcoe_hostlist_remove(const struct fc_lport *lp) +int fcoe_hostlist_remove(const struct fc_lport *lport) { - struct fcoe_softc *fc; + struct fcoe_interface *fcoe; write_lock_bh(&fcoe_hostlist_lock); - fc = fcoe_hostlist_lookup_softc(fcoe_netdev(lp)); - BUG_ON(!fc); - list_del(&fc->list); + fcoe = fcoe_hostlist_lookup_port(fcoe_netdev(lport)); + BUG_ON(!fcoe); + list_del(&fcoe->list); write_unlock_bh(&fcoe_hostlist_lock); return 0; @@ -1903,19 +1924,18 @@ module_init(fcoe_init); static void __exit fcoe_exit(void) { unsigned int cpu; - struct fcoe_softc *fc, *tmp; + struct fcoe_interface *fcoe, *tmp; fcoe_dev_cleanup(); /* releases the associated fcoe hosts */ - list_for_each_entry_safe(fc, tmp, &fcoe_hostlist, list) - fcoe_if_destroy(fc->ctlr.lp); + list_for_each_entry_safe(fcoe, tmp, &fcoe_hostlist, list) + fcoe_if_destroy(fcoe->priv->ctlr.lp); unregister_hotcpu_notifier(&fcoe_cpu_notifier); - for_each_online_cpu(cpu) { + for_each_online_cpu(cpu) fcoe_percpu_thread_destroy(cpu); - } /* detach from scsi transport */ fcoe_if_exit(); diff --git a/drivers/scsi/fcoe/fcoe.h b/drivers/scsi/fcoe/fcoe.h index 550d1e49d1a..060a6dce658 100644 --- a/drivers/scsi/fcoe/fcoe.h +++ b/drivers/scsi/fcoe/fcoe.h @@ -75,10 +75,21 @@ struct fcoe_percpu_s { }; /* - * the fcoe sw transport private data + * an FCoE interface, 1:1 with netdev */ -struct fcoe_softc { +struct fcoe_interface { struct list_head list; + /* This will be removed once all the shared values are + * moved out of fcoe_port */ + struct fcoe_port *priv; +}; + +/* + * the FCoE private structure that's allocated along with the + * Scsi_Host and libfc fc_lport structures + */ +struct fcoe_port { + struct fcoe_interface *fcoe; struct net_device *netdev; struct fc_exch_mgr *oem; /* offload exchange manger */ struct packet_type fcoe_packet_type; @@ -89,12 +100,12 @@ struct fcoe_softc { struct fcoe_ctlr ctlr; }; -#define fcoe_from_ctlr(fc) container_of(fc, struct fcoe_softc, ctlr) +#define fcoe_from_ctlr(port) container_of(port, struct fcoe_port, ctlr) static inline struct net_device *fcoe_netdev( const struct fc_lport *lp) { - return ((struct fcoe_softc *)lport_priv(lp))->netdev; + return ((struct fcoe_port *)lport_priv(lp))->netdev; } #endif /* _FCOE_H_ */ -- cgit v1.2.3-70-g09d2 From 250249898a92a1228050f40fbe3c05deb1392da8 Mon Sep 17 00:00:00 2001 From: Chris Leech Date: Tue, 25 Aug 2009 13:59:35 -0700 Subject: [SCSI] fcoe: move netdev to fcoe_interface The network interface needs to be shared between all NPIV VN_Ports, therefor it should be tracked in the fcoe_interface and not for each SCSI host in fcoe_port. Signed-off-by: Chris Leech Signed-off-by: Robert Love Signed-off-by: James Bottomley --- drivers/scsi/fcoe/fcoe.c | 61 +++++++++++++++++++++++++++--------------------- drivers/scsi/fcoe/fcoe.h | 9 ++++--- 2 files changed, 38 insertions(+), 32 deletions(-) (limited to 'drivers/scsi/fcoe/fcoe.c') diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index 0ae54b2bce3..44c963c8bc7 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -165,7 +165,7 @@ static int fcoe_fip_recv(struct sk_buff *skb, struct net_device *dev, */ static void fcoe_fip_send(struct fcoe_ctlr *fip, struct sk_buff *skb) { - skb->dev = fcoe_from_ctlr(fip)->netdev; + skb->dev = fcoe_from_ctlr(fip)->fcoe->netdev; dev_queue_xmit(skb); } @@ -180,13 +180,16 @@ 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) { + struct fcoe_interface *fcoe; struct fcoe_port *port; port = fcoe_from_ctlr(fip); + fcoe = port->fcoe; + rtnl_lock(); if (!is_zero_ether_addr(old)) - dev_unicast_delete(port->netdev, old); - dev_unicast_add(port->netdev, new); + dev_unicast_delete(fcoe->netdev, old); + dev_unicast_add(fcoe->netdev, new); rtnl_unlock(); } @@ -229,6 +232,7 @@ static int fcoe_lport_config(struct fc_lport *lp) void fcoe_netdev_cleanup(struct fcoe_port *port) { u8 flogi_maddr[ETH_ALEN]; + struct fcoe_interface *fcoe = port->fcoe; /* Don't listen for Ethernet packets anymore */ dev_remove_pack(&port->fcoe_packet_type); @@ -237,12 +241,12 @@ void fcoe_netdev_cleanup(struct fcoe_port *port) /* Delete secondary MAC addresses */ rtnl_lock(); memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN); - dev_unicast_delete(port->netdev, flogi_maddr); + dev_unicast_delete(fcoe->netdev, flogi_maddr); if (!is_zero_ether_addr(port->ctlr.data_src_addr)) - dev_unicast_delete(port->netdev, port->ctlr.data_src_addr); + dev_unicast_delete(fcoe->netdev, port->ctlr.data_src_addr); if (port->ctlr.spma) - dev_unicast_delete(port->netdev, port->ctlr.ctl_src_addr); - dev_mc_delete(port->netdev, FIP_ALL_ENODE_MACS, ETH_ALEN, 0); + dev_unicast_delete(fcoe->netdev, port->ctlr.ctl_src_addr); + dev_mc_delete(fcoe->netdev, FIP_ALL_ENODE_MACS, ETH_ALEN, 0); rtnl_unlock(); } @@ -271,14 +275,16 @@ static int fcoe_netdev_config(struct fc_lport *lp, struct net_device *netdev) { u32 mfs; u64 wwnn, wwpn; + struct fcoe_interface *fcoe; struct fcoe_port *port; u8 flogi_maddr[ETH_ALEN]; struct netdev_hw_addr *ha; /* Setup lport private data to point to fcoe softc */ port = lport_priv(lp); + fcoe = port->fcoe; port->ctlr.lp = lp; - port->netdev = netdev; + fcoe->netdev = netdev; /* Do not support for bonding device */ if ((netdev->priv_flags & IFF_MASTER_ALB) || @@ -434,9 +440,10 @@ bool fcoe_oem_match(struct fc_frame *fp) */ static inline int fcoe_em_config(struct fc_lport *lp) { - struct fcoe_interface *fcoe; struct fcoe_port *port = lport_priv(lp); - struct fcoe_port *oldfc = NULL; + struct fcoe_port *oldport = NULL; + struct fcoe_interface *fcoe = port->fcoe; + struct fcoe_interface *oldfcoe = NULL; struct net_device *old_real_dev, *cur_real_dev; u16 min_xid = FCOE_MIN_XID; u16 max_xid = FCOE_MAX_XID; @@ -454,20 +461,20 @@ static inline int fcoe_em_config(struct fc_lport *lp) * Reuse existing offload em instance in case * it is already allocated on real eth device */ - if (port->netdev->priv_flags & IFF_802_1Q_VLAN) - cur_real_dev = vlan_dev_real_dev(port->netdev); + if (fcoe->netdev->priv_flags & IFF_802_1Q_VLAN) + cur_real_dev = vlan_dev_real_dev(fcoe->netdev); else - cur_real_dev = port->netdev; + cur_real_dev = fcoe->netdev; - list_for_each_entry(fcoe, &fcoe_hostlist, list) { - oldfc = fcoe->priv; - if (oldfc->netdev->priv_flags & IFF_802_1Q_VLAN) - old_real_dev = vlan_dev_real_dev(oldfc->netdev); + list_for_each_entry(oldfcoe, &fcoe_hostlist, list) { + oldport = oldfcoe->priv; + if (oldfcoe->netdev->priv_flags & IFF_802_1Q_VLAN) + old_real_dev = vlan_dev_real_dev(oldfcoe->netdev); else - old_real_dev = oldfc->netdev; + old_real_dev = oldfcoe->netdev; if (cur_real_dev == old_real_dev) { - port->oem = oldfc->oem; + port->oem = oldport->oem; break; } } @@ -476,7 +483,7 @@ static inline int fcoe_em_config(struct fc_lport *lp) if (!fc_exch_mgr_add(lp, port->oem, fcoe_oem_match)) { printk(KERN_ERR "fcoe_em_config: failed to add " "offload em:%p on interface:%s\n", - port->oem, port->netdev->name); + port->oem, fcoe->netdev->name); return -ENOMEM; } } else { @@ -486,7 +493,7 @@ static inline int fcoe_em_config(struct fc_lport *lp) if (!port->oem) { printk(KERN_ERR "fcoe_em_config: failed to allocate " "em for offload exches on interface:%s\n", - port->netdev->name); + fcoe->netdev->name); return -ENOMEM; } } @@ -499,7 +506,7 @@ static inline int fcoe_em_config(struct fc_lport *lp) skip_oem: if (!fc_exch_mgr_alloc(lp, FC_CLASS_3, min_xid, max_xid, NULL)) { printk(KERN_ERR "fcoe_em_config: failed to " - "allocate em on interface %s\n", port->netdev->name); + "allocate em on interface %s\n", fcoe->netdev->name); return -ENOMEM; } @@ -514,7 +521,7 @@ static void fcoe_if_destroy(struct fc_lport *lport) { struct fcoe_port *port = lport_priv(lport); struct fcoe_interface *fcoe = port->fcoe; - struct net_device *netdev = port->netdev; + struct net_device *netdev = fcoe->netdev; FCOE_NETDEV_DBG(netdev, "Destroying interface\n"); @@ -1194,7 +1201,7 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp) skb_reset_network_header(skb); skb->mac_len = elen; skb->protocol = htons(ETH_P_FCOE); - skb->dev = port->netdev; + skb->dev = port->fcoe->netdev; /* fill up mac and fcoe headers */ eh = eth_hdr(skb); @@ -1480,7 +1487,7 @@ static int fcoe_device_notification(struct notifier_block *notifier, read_lock(&fcoe_hostlist_lock); list_for_each_entry(fcoe, &fcoe_hostlist, list) { port = fcoe->priv; - if (port->netdev == netdev) { + if (fcoe->netdev == netdev) { lp = port->ctlr.lp; break; } @@ -1709,7 +1716,7 @@ MODULE_PARM_DESC(destroy, "Destroy fcoe fcoe"); int fcoe_link_ok(struct fc_lport *lp) { struct fcoe_port *port = lport_priv(lp); - struct net_device *dev = port->netdev; + struct net_device *dev = port->fcoe->netdev; struct ethtool_cmd ecmd = { ETHTOOL_GSET }; if ((dev->flags & IFF_UP) && netif_carrier_ok(dev) && @@ -1810,7 +1817,7 @@ fcoe_hostlist_lookup_port(const struct net_device *dev) struct fcoe_interface *fcoe; list_for_each_entry(fcoe, &fcoe_hostlist, list) { - if (fcoe->priv->netdev == dev) + if (fcoe->netdev == dev) return fcoe; } return NULL; diff --git a/drivers/scsi/fcoe/fcoe.h b/drivers/scsi/fcoe/fcoe.h index 060a6dce658..3b3886e99b4 100644 --- a/drivers/scsi/fcoe/fcoe.h +++ b/drivers/scsi/fcoe/fcoe.h @@ -82,6 +82,7 @@ struct fcoe_interface { /* This will be removed once all the shared values are * moved out of fcoe_port */ struct fcoe_port *priv; + struct net_device *netdev; }; /* @@ -90,7 +91,6 @@ struct fcoe_interface { */ struct fcoe_port { struct fcoe_interface *fcoe; - struct net_device *netdev; struct fc_exch_mgr *oem; /* offload exchange manger */ struct packet_type fcoe_packet_type; struct packet_type fip_packet_type; @@ -100,12 +100,11 @@ struct fcoe_port { struct fcoe_ctlr ctlr; }; -#define fcoe_from_ctlr(port) container_of(port, struct fcoe_port, ctlr) +#define fcoe_from_ctlr(fip) container_of(fip, struct fcoe_port, ctlr) -static inline struct net_device *fcoe_netdev( - const struct fc_lport *lp) +static inline struct net_device *fcoe_netdev(const struct fc_lport *lp) { - return ((struct fcoe_port *)lport_priv(lp))->netdev; + return ((struct fcoe_port *)lport_priv(lp))->fcoe->netdev; } #endif /* _FCOE_H_ */ -- cgit v1.2.3-70-g09d2 From 259ad85d8dbbcd508e3dad29a36e3e76365853b7 Mon Sep 17 00:00:00 2001 From: Chris Leech Date: Tue, 25 Aug 2009 13:59:41 -0700 Subject: [SCSI] fcoe: move packet handlers from fcoe_port to fcoe_interface The packet handlers need to be tracked in fcoe_interface so there is only one set per net_device. When NPIV is enabled there will be multiple SCSI hosts and multiple fcoe_port structures on a single net_device. The packet handlers match by ethertype and netdev. If the same handler gets registered on a single netdev multiple times, the receive function will be called multiple times for each frame. Signed-off-by: Chris Leech Signed-off-by: Robert Love Signed-off-by: James Bottomley --- drivers/scsi/fcoe/fcoe.c | 28 ++++++++++++++++------------ drivers/scsi/fcoe/fcoe.h | 4 ++-- 2 files changed, 18 insertions(+), 14 deletions(-) (limited to 'drivers/scsi/fcoe/fcoe.c') diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index 44c963c8bc7..c215235ee39 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -151,9 +151,11 @@ static int fcoe_fip_recv(struct sk_buff *skb, struct net_device *dev, struct packet_type *ptype, struct net_device *orig_dev) { + struct fcoe_interface *fcoe; struct fcoe_port *port; - port = container_of(ptype, struct fcoe_port, fip_packet_type); + fcoe = container_of(ptype, struct fcoe_interface, fip_packet_type); + port = fcoe->priv; fcoe_ctlr_recv(&port->ctlr, skb); return 0; } @@ -235,8 +237,8 @@ void fcoe_netdev_cleanup(struct fcoe_port *port) struct fcoe_interface *fcoe = port->fcoe; /* Don't listen for Ethernet packets anymore */ - dev_remove_pack(&port->fcoe_packet_type); - dev_remove_pack(&port->fip_packet_type); + dev_remove_pack(&fcoe->fcoe_packet_type); + dev_remove_pack(&fcoe->fip_packet_type); /* Delete secondary MAC addresses */ rtnl_lock(); @@ -368,15 +370,15 @@ static int fcoe_netdev_config(struct fc_lport *lp, struct net_device *netdev) * setup the receive function from ethernet driver * on the ethertype for the given device */ - port->fcoe_packet_type.func = fcoe_rcv; - port->fcoe_packet_type.type = __constant_htons(ETH_P_FCOE); - port->fcoe_packet_type.dev = netdev; - dev_add_pack(&port->fcoe_packet_type); + fcoe->fcoe_packet_type.func = fcoe_rcv; + fcoe->fcoe_packet_type.type = __constant_htons(ETH_P_FCOE); + fcoe->fcoe_packet_type.dev = netdev; + dev_add_pack(&fcoe->fcoe_packet_type); - port->fip_packet_type.func = fcoe_fip_recv; - port->fip_packet_type.type = htons(ETH_P_FIP); - port->fip_packet_type.dev = netdev; - dev_add_pack(&port->fip_packet_type); + fcoe->fip_packet_type.func = fcoe_fip_recv; + fcoe->fip_packet_type.type = htons(ETH_P_FIP); + fcoe->fip_packet_type.dev = netdev; + dev_add_pack(&fcoe->fip_packet_type); return 0; } @@ -926,12 +928,14 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *dev, { struct fc_lport *lp; struct fcoe_rcv_info *fr; + struct fcoe_interface *fcoe; struct fcoe_port *port; struct fc_frame_header *fh; struct fcoe_percpu_s *fps; unsigned int cpu; - port = container_of(ptype, struct fcoe_port, fcoe_packet_type); + fcoe = container_of(ptype, struct fcoe_interface, fcoe_packet_type); + port = fcoe->priv; lp = port->ctlr.lp; if (unlikely(lp == NULL)) { FCOE_NETDEV_DBG(dev, "Cannot find hba structure"); diff --git a/drivers/scsi/fcoe/fcoe.h b/drivers/scsi/fcoe/fcoe.h index 3b3886e99b4..685aa9d0222 100644 --- a/drivers/scsi/fcoe/fcoe.h +++ b/drivers/scsi/fcoe/fcoe.h @@ -83,6 +83,8 @@ struct fcoe_interface { * moved out of fcoe_port */ struct fcoe_port *priv; struct net_device *netdev; + struct packet_type fcoe_packet_type; + struct packet_type fip_packet_type; }; /* @@ -92,8 +94,6 @@ struct fcoe_interface { struct fcoe_port { struct fcoe_interface *fcoe; struct fc_exch_mgr *oem; /* offload exchange manger */ - struct packet_type fcoe_packet_type; - struct packet_type fip_packet_type; struct sk_buff_head fcoe_pending_queue; u8 fcoe_pending_queue_active; struct timer_list timer; /* queue timer */ -- cgit v1.2.3-70-g09d2 From 3fe9a0badae7fa2eb35eff4f07e851fbd25e3d4f Mon Sep 17 00:00:00 2001 From: Chris Leech Date: Tue, 25 Aug 2009 13:59:46 -0700 Subject: [SCSI] fcoe: move FIP controller from fcoe_port to fcoe_interface There is only one FIP state per net_device, so the FIP controller needs to be moved from the per-SCSI-host fcoe_port to the per-net_device fcoe_interface structure. Signed-off-by: Chris Leech Signed-off-by: Robert Love Signed-off-by: James Bottomley --- drivers/scsi/fcoe/fcoe.c | 83 +++++++++++++++++++++--------------------------- drivers/scsi/fcoe/fcoe.h | 4 +-- 2 files changed, 39 insertions(+), 48 deletions(-) (limited to 'drivers/scsi/fcoe/fcoe.c') diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index c215235ee39..01519c722ed 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -152,11 +152,9 @@ static int fcoe_fip_recv(struct sk_buff *skb, struct net_device *dev, struct net_device *orig_dev) { struct fcoe_interface *fcoe; - struct fcoe_port *port; fcoe = container_of(ptype, struct fcoe_interface, fip_packet_type); - port = fcoe->priv; - fcoe_ctlr_recv(&port->ctlr, skb); + fcoe_ctlr_recv(&fcoe->ctlr, skb); return 0; } @@ -167,7 +165,7 @@ static int fcoe_fip_recv(struct sk_buff *skb, struct net_device *dev, */ static void fcoe_fip_send(struct fcoe_ctlr *fip, struct sk_buff *skb) { - skb->dev = fcoe_from_ctlr(fip)->fcoe->netdev; + skb->dev = fcoe_from_ctlr(fip)->netdev; dev_queue_xmit(skb); } @@ -183,11 +181,8 @@ 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) { struct fcoe_interface *fcoe; - struct fcoe_port *port; - - port = fcoe_from_ctlr(fip); - fcoe = port->fcoe; + fcoe = fcoe_from_ctlr(fip); rtnl_lock(); if (!is_zero_ether_addr(old)) dev_unicast_delete(fcoe->netdev, old); @@ -244,10 +239,10 @@ void fcoe_netdev_cleanup(struct fcoe_port *port) rtnl_lock(); memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN); dev_unicast_delete(fcoe->netdev, flogi_maddr); - if (!is_zero_ether_addr(port->ctlr.data_src_addr)) - dev_unicast_delete(fcoe->netdev, port->ctlr.data_src_addr); - if (port->ctlr.spma) - dev_unicast_delete(fcoe->netdev, port->ctlr.ctl_src_addr); + if (!is_zero_ether_addr(fcoe->ctlr.data_src_addr)) + dev_unicast_delete(fcoe->netdev, fcoe->ctlr.data_src_addr); + if (fcoe->ctlr.spma) + dev_unicast_delete(fcoe->netdev, fcoe->ctlr.ctl_src_addr); dev_mc_delete(fcoe->netdev, FIP_ALL_ENODE_MACS, ETH_ALEN, 0); rtnl_unlock(); } @@ -285,7 +280,7 @@ static int fcoe_netdev_config(struct fc_lport *lp, struct net_device *netdev) /* Setup lport private data to point to fcoe softc */ port = lport_priv(lp); fcoe = port->fcoe; - port->ctlr.lp = lp; + fcoe->ctlr.lp = lp; fcoe->netdev = netdev; /* Do not support for bonding device */ @@ -334,17 +329,17 @@ static int fcoe_netdev_config(struct fc_lport *lp, struct net_device *netdev) rcu_read_lock(); for_each_dev_addr(netdev, ha) { if ((ha->type == NETDEV_HW_ADDR_T_SAN) && - (is_valid_ether_addr(port->ctlr.ctl_src_addr))) { - memcpy(port->ctlr.ctl_src_addr, ha->addr, ETH_ALEN); - port->ctlr.spma = 1; + (is_valid_ether_addr(fcoe->ctlr.ctl_src_addr))) { + memcpy(fcoe->ctlr.ctl_src_addr, ha->addr, ETH_ALEN); + fcoe->ctlr.spma = 1; break; } } rcu_read_unlock(); /* setup Source Mac Address */ - if (!port->ctlr.spma) - memcpy(port->ctlr.ctl_src_addr, netdev->dev_addr, + if (!fcoe->ctlr.spma) + memcpy(fcoe->ctlr.ctl_src_addr, netdev->dev_addr, netdev->addr_len); wwnn = fcoe_wwn_from_mac(netdev->dev_addr, 1, 0); @@ -361,8 +356,8 @@ static int fcoe_netdev_config(struct fc_lport *lp, struct net_device *netdev) rtnl_lock(); memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN); dev_unicast_add(netdev, flogi_maddr); - if (port->ctlr.spma) - dev_unicast_add(netdev, port->ctlr.ctl_src_addr); + if (fcoe->ctlr.spma) + dev_unicast_add(netdev, fcoe->ctlr.ctl_src_addr); dev_mc_add(netdev, FIP_ALL_ENODE_MACS, ETH_ALEN, 0); rtnl_unlock(); @@ -537,7 +532,7 @@ static void fcoe_if_destroy(struct fc_lport *lport) fcoe_netdev_cleanup(port); /* tear-down the FCoE controller */ - fcoe_ctlr_destroy(&port->ctlr); + fcoe_ctlr_destroy(&fcoe->ctlr); /* Free queued packets for the per-CPU receive threads */ fcoe_percpu_clean(lport); @@ -662,9 +657,9 @@ static struct fc_lport *fcoe_if_create(struct net_device *netdev, /* * Initialize FIP. */ - fcoe_ctlr_init(&port->ctlr); - port->ctlr.send = fcoe_fip_send; - port->ctlr.update_mac = fcoe_update_src_mac; + fcoe_ctlr_init(&fcoe->ctlr); + fcoe->ctlr.send = fcoe_fip_send; + fcoe->ctlr.update_mac = fcoe_update_src_mac; /* configure lport network properties */ rc = fcoe_netdev_config(lport, netdev); @@ -714,7 +709,7 @@ static struct fc_lport *fcoe_if_create(struct net_device *netdev, fc_fabric_login(lport); if (!fcoe_link_ok(lport)) - fcoe_ctlr_link_up(&port->ctlr); + fcoe_ctlr_link_up(&fcoe->ctlr); dev_hold(netdev); @@ -929,14 +924,12 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *dev, struct fc_lport *lp; struct fcoe_rcv_info *fr; struct fcoe_interface *fcoe; - struct fcoe_port *port; struct fc_frame_header *fh; struct fcoe_percpu_s *fps; unsigned int cpu; fcoe = container_of(ptype, struct fcoe_interface, fcoe_packet_type); - port = fcoe->priv; - lp = port->ctlr.lp; + lp = fcoe->ctlr.lp; if (unlikely(lp == NULL)) { FCOE_NETDEV_DBG(dev, "Cannot find hba structure"); goto err2; @@ -1137,13 +1130,13 @@ 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; + struct fcoe_port *port = lport_priv(lp); + struct fcoe_interface *fcoe = port->fcoe; u8 sof, eof; struct fcoe_hdr *hp; WARN_ON((fr_len(fp) % sizeof(u32)) != 0); - port = lport_priv(lp); fh = fc_frame_header_get(fp); skb = fp_skb(fp); wlen = skb->len / FCOE_WORD_TO_BYTE; @@ -1154,7 +1147,7 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp) } if (unlikely(fh->fh_r_ctl == FC_RCTL_ELS_REQ) && - fcoe_ctlr_els_send(&port->ctlr, skb)) + fcoe_ctlr_els_send(&fcoe->ctlr, skb)) return 0; sof = fr_sof(fp); @@ -1205,21 +1198,21 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp) skb_reset_network_header(skb); skb->mac_len = elen; skb->protocol = htons(ETH_P_FCOE); - skb->dev = port->fcoe->netdev; + skb->dev = fcoe->netdev; /* fill up mac and fcoe headers */ eh = eth_hdr(skb); eh->h_proto = htons(ETH_P_FCOE); - if (port->ctlr.map_dest) + if (fcoe->ctlr.map_dest) fc_fcoe_set_mac(eh->h_dest, fh->fh_d_id); else /* insert GW address */ - memcpy(eh->h_dest, port->ctlr.dest_addr, ETH_ALEN); + memcpy(eh->h_dest, fcoe->ctlr.dest_addr, ETH_ALEN); - if (unlikely(port->ctlr.flogi_oxid != FC_XID_UNKNOWN)) - memcpy(eh->h_source, port->ctlr.ctl_src_addr, ETH_ALEN); + 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, port->ctlr.data_src_addr, ETH_ALEN); + memcpy(eh->h_source, fcoe->ctlr.data_src_addr, ETH_ALEN); hp = (struct fcoe_hdr *)(eh + 1); memset(hp, 0, sizeof(*hp)); @@ -1382,8 +1375,8 @@ int fcoe_percpu_receive_thread(void *arg) } fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED; } - if (unlikely(port->ctlr.flogi_oxid != FC_XID_UNKNOWN) && - fcoe_ctlr_recv_flogi(&port->ctlr, fp, mac)) { + if (unlikely(port->fcoe->ctlr.flogi_oxid != FC_XID_UNKNOWN) && + fcoe_ctlr_recv_flogi(&port->fcoe->ctlr, fp, mac)) { fc_frame_free(fp); continue; } @@ -1482,7 +1475,6 @@ static int fcoe_device_notification(struct notifier_block *notifier, struct fc_lport *lp = NULL; struct net_device *netdev = ptr; struct fcoe_interface *fcoe; - struct fcoe_port *port = NULL; struct fcoe_dev_stats *stats; u32 link_possible = 1; u32 mfs; @@ -1490,9 +1482,8 @@ static int fcoe_device_notification(struct notifier_block *notifier, read_lock(&fcoe_hostlist_lock); list_for_each_entry(fcoe, &fcoe_hostlist, list) { - port = fcoe->priv; if (fcoe->netdev == netdev) { - lp = port->ctlr.lp; + lp = fcoe->ctlr.lp; break; } } @@ -1523,8 +1514,8 @@ static int fcoe_device_notification(struct notifier_block *notifier, "from netdev netlink\n", event); } if (link_possible && !fcoe_link_ok(lp)) - fcoe_ctlr_link_up(&port->ctlr); - else if (fcoe_ctlr_link_down(&port->ctlr)) { + fcoe_ctlr_link_up(&fcoe->ctlr); + else if (fcoe_ctlr_link_down(&fcoe->ctlr)) { stats = fc_lport_get_stats(lp); stats->LinkFailureCount++; fcoe_clean_pending_queue(lp); @@ -1841,7 +1832,7 @@ struct fc_lport *fcoe_hostlist_lookup(const struct net_device *netdev) fcoe = fcoe_hostlist_lookup_port(netdev); read_unlock(&fcoe_hostlist_lock); - return (fcoe) ? fcoe->priv->ctlr.lp : NULL; + return (fcoe) ? fcoe->ctlr.lp : NULL; } /** @@ -1941,7 +1932,7 @@ static void __exit fcoe_exit(void) /* releases the associated fcoe hosts */ list_for_each_entry_safe(fcoe, tmp, &fcoe_hostlist, list) - fcoe_if_destroy(fcoe->priv->ctlr.lp); + fcoe_if_destroy(fcoe->ctlr.lp); unregister_hotcpu_notifier(&fcoe_cpu_notifier); diff --git a/drivers/scsi/fcoe/fcoe.h b/drivers/scsi/fcoe/fcoe.h index 685aa9d0222..5b190b5fea3 100644 --- a/drivers/scsi/fcoe/fcoe.h +++ b/drivers/scsi/fcoe/fcoe.h @@ -85,6 +85,7 @@ struct fcoe_interface { struct net_device *netdev; struct packet_type fcoe_packet_type; struct packet_type fip_packet_type; + struct fcoe_ctlr ctlr; }; /* @@ -97,10 +98,9 @@ struct fcoe_port { struct sk_buff_head fcoe_pending_queue; u8 fcoe_pending_queue_active; struct timer_list timer; /* queue timer */ - struct fcoe_ctlr ctlr; }; -#define fcoe_from_ctlr(fip) container_of(fip, struct fcoe_port, ctlr) +#define fcoe_from_ctlr(fip) container_of(fip, struct fcoe_interface, ctlr) static inline struct net_device *fcoe_netdev(const struct fc_lport *lp) { -- cgit v1.2.3-70-g09d2 From 991cbb6082db3025bd82908eb9ee2d2920be2114 Mon Sep 17 00:00:00 2001 From: Chris Leech Date: Tue, 25 Aug 2009 13:59:51 -0700 Subject: [SCSI] fcoe: move offload exchange manager pointer from fcoe_port to fcoe_interface The offload EM pointer is only used when setting up a new libfc instance, but as it's designed to be shared among NPIV VN_Ports it should be tracked in fcoe_interface. With the host-list changed to track fcoe_interfaces as well, this is needed before we can remove the priv pointer from that structure (which is only there to help in the transition, and stops making sense once NPIV is enabled). Signed-off-by: Chris Leech Signed-off-by: Robert Love Signed-off-by: James Bottomley --- drivers/scsi/fcoe/fcoe.c | 14 ++++++-------- drivers/scsi/fcoe/fcoe.h | 2 +- 2 files changed, 7 insertions(+), 9 deletions(-) (limited to 'drivers/scsi/fcoe/fcoe.c') diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index 01519c722ed..bb59a7a04f2 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -438,7 +438,6 @@ bool fcoe_oem_match(struct fc_frame *fp) static inline int fcoe_em_config(struct fc_lport *lp) { struct fcoe_port *port = lport_priv(lp); - struct fcoe_port *oldport = NULL; struct fcoe_interface *fcoe = port->fcoe; struct fcoe_interface *oldfcoe = NULL; struct net_device *old_real_dev, *cur_real_dev; @@ -464,30 +463,29 @@ static inline int fcoe_em_config(struct fc_lport *lp) cur_real_dev = fcoe->netdev; list_for_each_entry(oldfcoe, &fcoe_hostlist, list) { - oldport = oldfcoe->priv; if (oldfcoe->netdev->priv_flags & IFF_802_1Q_VLAN) old_real_dev = vlan_dev_real_dev(oldfcoe->netdev); else old_real_dev = oldfcoe->netdev; if (cur_real_dev == old_real_dev) { - port->oem = oldport->oem; + fcoe->oem = oldfcoe->oem; break; } } - if (port->oem) { - if (!fc_exch_mgr_add(lp, port->oem, fcoe_oem_match)) { + if (fcoe->oem) { + if (!fc_exch_mgr_add(lp, fcoe->oem, fcoe_oem_match)) { printk(KERN_ERR "fcoe_em_config: failed to add " "offload em:%p on interface:%s\n", - port->oem, fcoe->netdev->name); + fcoe->oem, fcoe->netdev->name); return -ENOMEM; } } else { - port->oem = fc_exch_mgr_alloc(lp, FC_CLASS_3, + fcoe->oem = fc_exch_mgr_alloc(lp, FC_CLASS_3, FCOE_MIN_XID, lp->lro_xid, fcoe_oem_match); - if (!port->oem) { + if (!fcoe->oem) { printk(KERN_ERR "fcoe_em_config: failed to allocate " "em for offload exches on interface:%s\n", fcoe->netdev->name); diff --git a/drivers/scsi/fcoe/fcoe.h b/drivers/scsi/fcoe/fcoe.h index 5b190b5fea3..26e85957460 100644 --- a/drivers/scsi/fcoe/fcoe.h +++ b/drivers/scsi/fcoe/fcoe.h @@ -86,6 +86,7 @@ struct fcoe_interface { struct packet_type fcoe_packet_type; struct packet_type fip_packet_type; struct fcoe_ctlr ctlr; + struct fc_exch_mgr *oem; /* offload exchange manager */ }; /* @@ -94,7 +95,6 @@ struct fcoe_interface { */ struct fcoe_port { struct fcoe_interface *fcoe; - struct fc_exch_mgr *oem; /* offload exchange manger */ struct sk_buff_head fcoe_pending_queue; u8 fcoe_pending_queue_active; struct timer_list timer; /* queue timer */ -- cgit v1.2.3-70-g09d2 From cb0a6ca81439a9f113d3b46de0953da168a06f6a Mon Sep 17 00:00:00 2001 From: Chris Leech Date: Tue, 25 Aug 2009 13:59:56 -0700 Subject: [SCSI] fcoe: remove fcoe_interface->priv pointer The priv pointer is no longer needed, and once NPIV is enabled fcoe_interface:fc_lport becomes a one-to-many relationship. Remove the single pointer. Signed-off-by: Chris Leech Signed-off-by: Robert Love Signed-off-by: James Bottomley --- drivers/scsi/fcoe/fcoe.c | 2 -- drivers/scsi/fcoe/fcoe.h | 3 --- 2 files changed, 5 deletions(-) (limited to 'drivers/scsi/fcoe/fcoe.c') diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index bb59a7a04f2..7f14c633da6 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -640,9 +640,7 @@ static struct fc_lport *fcoe_if_create(struct net_device *netdev, } lport = shost_priv(shost); port = lport_priv(lport); - port->fcoe = fcoe; - fcoe->priv = port; /* configure fc_lport, e.g., em */ rc = fcoe_lport_config(lport); diff --git a/drivers/scsi/fcoe/fcoe.h b/drivers/scsi/fcoe/fcoe.h index 26e85957460..673e70e1f8b 100644 --- a/drivers/scsi/fcoe/fcoe.h +++ b/drivers/scsi/fcoe/fcoe.h @@ -79,9 +79,6 @@ struct fcoe_percpu_s { */ struct fcoe_interface { struct list_head list; - /* This will be removed once all the shared values are - * moved out of fcoe_port */ - struct fcoe_port *priv; struct net_device *netdev; struct packet_type fcoe_packet_type; struct packet_type fip_packet_type; -- cgit v1.2.3-70-g09d2 From 030f4e001f13e0ee80bac1e756013341b1674d10 Mon Sep 17 00:00:00 2001 From: Chris Leech Date: Tue, 25 Aug 2009 14:00:02 -0700 Subject: [SCSI] fcoe: fcoe_interface create, destroy and refcounting Up to this point the fcoe_instance structure was simply kzalloc/kfreed. This patch introduces create and destroy functions as well as kref based reference counting. The create function will grow as the initialization code is moved there. Signed-off-by: Chris Leech Signed-off-by: Robert Love Signed-off-by: James Bottomley --- drivers/scsi/fcoe/fcoe.c | 97 ++++++++++++++++++++++++++++++++++++++---------- drivers/scsi/fcoe/fcoe.h | 1 + 2 files changed, 79 insertions(+), 19 deletions(-) (limited to 'drivers/scsi/fcoe/fcoe.c') diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index 7f14c633da6..d1d6b3b8bf5 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -138,6 +138,58 @@ static struct scsi_host_template fcoe_shost_template = { .max_sectors = 0xffff, }; +/** + * fcoe_interface_create() + * @netdev: network interface + * + * 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; + + fcoe = kzalloc(sizeof(*fcoe), GFP_KERNEL); + if (!fcoe) { + FCOE_NETDEV_DBG(netdev, "Could not allocate fcoe structure\n"); + return NULL; + } + + kref_init(&fcoe->kref); + fcoe->netdev = netdev; + + return fcoe; +} + +/** + * fcoe_interface_release() - fcoe_port kref release function + * @kref: embedded reference count in an fcoe_interface struct + */ +static void fcoe_interface_release(struct kref *kref) +{ + struct fcoe_interface *fcoe; + + fcoe = container_of(kref, struct fcoe_interface, kref); + kfree(fcoe); +} + +/** + * fcoe_interface_get() + * @fcoe: + */ +static inline void fcoe_interface_get(struct fcoe_interface *fcoe) +{ + kref_get(&fcoe->kref); +} + +/** + * fcoe_interface_put() + * @fcoe: + */ +static inline void fcoe_interface_put(struct fcoe_interface *fcoe) +{ + kref_put(&fcoe->kref, fcoe_interface_release); +} + /** * fcoe_fip_recv - handle a received FIP frame. * @skb: the receive skb @@ -558,7 +610,7 @@ static void fcoe_if_destroy(struct fc_lport *lport) /* Release the net_device and Scsi_Host */ dev_put(netdev); scsi_host_put(lport->host); - kfree(fcoe); /* TODO, should be refcounted */ + fcoe_interface_put(fcoe); } /* @@ -604,8 +656,8 @@ static struct libfc_function_template fcoe_libfc_fcn_templ = { }; /** - * fcoe_if_create() - this function creates the fcoe interface - * @netdev: pointer the associated netdevice + * 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 * * Creates fc_lport struct and scsi_host for lport, configures lport @@ -613,30 +665,23 @@ static struct libfc_function_template fcoe_libfc_fcn_templ = { * * Returns : The allocated fc_lport or an error pointer */ -static struct fc_lport *fcoe_if_create(struct net_device *netdev, +static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe, struct device *parent) { int rc; struct fc_lport *lport = NULL; struct fcoe_port *port; - struct fcoe_interface *fcoe; struct Scsi_Host *shost; + struct net_device *netdev = fcoe->netdev; FCOE_NETDEV_DBG(netdev, "Create Interface\n"); - fcoe = kzalloc(sizeof(*fcoe), GFP_KERNEL); - if (!fcoe) { - FCOE_NETDEV_DBG(netdev, "Could not allocate fcoe structure\n"); - rc = -ENOMEM; - goto out; - } - shost = libfc_host_alloc(&fcoe_shost_template, sizeof(struct fcoe_port)); if (!shost) { FCOE_NETDEV_DBG(netdev, "Could not allocate host structure\n"); rc = -ENOMEM; - goto out_kfree_port; + goto out; } lport = shost_priv(shost); port = lport_priv(lport); @@ -708,7 +753,7 @@ static struct fc_lport *fcoe_if_create(struct net_device *netdev, fcoe_ctlr_link_up(&fcoe->ctlr); dev_hold(netdev); - + fcoe_interface_get(fcoe); return lport; out_lp_destroy: @@ -717,8 +762,6 @@ out_netdev_cleanup: fcoe_netdev_cleanup(port); out_host_put: scsi_host_put(lport->host); -out_kfree_port: - kfree(fcoe); out: return ERR_PTR(rc); } @@ -1620,6 +1663,8 @@ static int fcoe_ethdrv_put(const struct net_device *netdev) static int fcoe_destroy(const char *buffer, struct kernel_param *kp) { struct net_device *netdev; + struct fcoe_interface *fcoe; + struct fcoe_port *port; struct fc_lport *lport; int rc; @@ -1634,6 +1679,8 @@ static int fcoe_destroy(const char *buffer, struct kernel_param *kp) rc = -ENODEV; goto out_putdev; } + port = lport_priv(lport); + fcoe = port->fcoe; fcoe_if_destroy(lport); fcoe_ethdrv_put(netdev); rc = 0; @@ -1653,6 +1700,7 @@ out_nodev: static int fcoe_create(const char *buffer, struct kernel_param *kp) { int rc; + struct fcoe_interface *fcoe; struct fc_lport *lport; struct net_device *netdev; @@ -1668,15 +1716,26 @@ static int fcoe_create(const char *buffer, struct kernel_param *kp) } fcoe_ethdrv_get(netdev); - lport = fcoe_if_create(netdev, &netdev->dev); + fcoe = fcoe_interface_create(netdev); + if (!fcoe) { + rc = -ENOMEM; + goto out_putdev; + } + + lport = fcoe_if_create(fcoe, &netdev->dev); if (IS_ERR(lport)) { printk(KERN_ERR "fcoe: Failed to create interface (%s)\n", netdev->name); fcoe_ethdrv_put(netdev); rc = -EIO; - goto out_putdev; + goto out_free; } - rc = 0; + + dev_put(netdev); + return 0; + +out_free: + fcoe_interface_put(fcoe); out_putdev: dev_put(netdev); out_nodev: diff --git a/drivers/scsi/fcoe/fcoe.h b/drivers/scsi/fcoe/fcoe.h index 673e70e1f8b..ff229288b7f 100644 --- a/drivers/scsi/fcoe/fcoe.h +++ b/drivers/scsi/fcoe/fcoe.h @@ -84,6 +84,7 @@ struct fcoe_interface { struct packet_type fip_packet_type; struct fcoe_ctlr ctlr; struct fc_exch_mgr *oem; /* offload exchange manager */ + struct kref kref; }; /* -- cgit v1.2.3-70-g09d2 From 54b649f88eb17a29687bece4b8ad7d72d99e2d95 Mon Sep 17 00:00:00 2001 From: Chris Leech Date: Tue, 25 Aug 2009 14:00:07 -0700 Subject: [SCSI] fcoe: split out per interface setup fcoe_netdev_config() is called during initialization of a libfc instance. Much of what was there only needs to be done once for each net_device. The same goes for the corresponding cleanup. The FIP controller initialization is moved to interface creation time. Otherwise it will keep getting re-initialized for every VN_Port once NPIV is enabled. fcoe_if_destroy() has some reordering to deal with the changes. Receives are not stopped until after fcoe_interface_put() is called, but transmits must be stopped before. So there is some care to stop libfc transmits and the transmit backlog timer, then call fcoe_interface_put which will stop receives and cleanup the FIP controller, then the receive queues can be cleaned and the port freed. Signed-off-by: Chris Leech Signed-off-by: Robert Love Signed-off-by: James Bottomley --- drivers/scsi/fcoe/fcoe.c | 267 ++++++++++++++++++++++++++--------------------- 1 file changed, 148 insertions(+), 119 deletions(-) (limited to 'drivers/scsi/fcoe/fcoe.c') diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index d1d6b3b8bf5..63aeeca384b 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -138,6 +138,82 @@ static struct scsi_host_template fcoe_shost_template = { .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 + * + * Returns : 0 for success + */ +static int fcoe_interface_setup(struct fcoe_interface *fcoe, + struct net_device *netdev) +{ + struct fcoe_ctlr *fip = &fcoe->ctlr; + struct netdev_hw_addr *ha; + u8 flogi_maddr[ETH_ALEN]; + + fcoe->netdev = netdev; + + /* 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)) { + return -EOPNOTSUPP; + } + + /* look for SAN MAC address, if multiple SAN MACs exist, only + * use the first one for SPMA */ + rcu_read_lock(); + for_each_dev_addr(netdev, ha) { + if ((ha->type == NETDEV_HW_ADDR_T_SAN) && + (is_valid_ether_addr(fip->ctl_src_addr))) { + memcpy(fip->ctl_src_addr, ha->addr, ETH_ALEN); + fip->spma = 1; + break; + } + } + rcu_read_unlock(); + + /* setup Source Mac Address */ + if (!fip->spma) + memcpy(fip->ctl_src_addr, netdev->dev_addr, netdev->addr_len); + + /* + * Add FCoE MAC address as second unicast MAC address + * or enter promiscuous mode if not capable of listening + * for multiple unicast MACs. + */ + rtnl_lock(); + memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN); + dev_unicast_add(netdev, flogi_maddr); + if (fip->spma) + dev_unicast_add(netdev, fip->ctl_src_addr); + dev_mc_add(netdev, FIP_ALL_ENODE_MACS, ETH_ALEN, 0); + rtnl_unlock(); + + /* + * setup the receive function from ethernet driver + * on the ethertype for the given device + */ + fcoe->fcoe_packet_type.func = fcoe_rcv; + fcoe->fcoe_packet_type.type = __constant_htons(ETH_P_FCOE); + fcoe->fcoe_packet_type.dev = netdev; + dev_add_pack(&fcoe->fcoe_packet_type); + + fcoe->fip_packet_type.func = fcoe_fip_recv; + fcoe->fip_packet_type.type = htons(ETH_P_FIP); + fcoe->fip_packet_type.dev = netdev; + dev_add_pack(&fcoe->fip_packet_type); + + 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); + /** * fcoe_interface_create() * @netdev: network interface @@ -155,11 +231,54 @@ static struct fcoe_interface *fcoe_interface_create(struct net_device *netdev) } kref_init(&fcoe->kref); - fcoe->netdev = netdev; + + /* + * Initialize FIP. + */ + fcoe_ctlr_init(&fcoe->ctlr); + fcoe->ctlr.send = fcoe_fip_send; + fcoe->ctlr.update_mac = fcoe_update_src_mac; + + fcoe_interface_setup(fcoe, netdev); return fcoe; } +/** + * fcoe_interface_cleanup() - clean up netdev configurations + * @fcoe: + */ +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]; + + /* + * Don't listen for Ethernet packets anymore. + * synchronize_net() ensures that the packet handlers are not running + * on another CPU. dev_remove_pack() would do that, this calls the + * unsyncronized version __dev_remove_pack() to avoid multiple delays. + */ + __dev_remove_pack(&fcoe->fcoe_packet_type); + __dev_remove_pack(&fcoe->fip_packet_type); + synchronize_net(); + + /* tear-down the FCoE controller */ + fcoe_ctlr_destroy(&fcoe->ctlr); + + /* Delete secondary MAC addresses */ + rtnl_lock(); + 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); + rtnl_unlock(); +} + /** * fcoe_interface_release() - fcoe_port kref release function * @kref: embedded reference count in an fcoe_interface struct @@ -169,6 +288,7 @@ static void fcoe_interface_release(struct kref *kref) struct fcoe_interface *fcoe; fcoe = container_of(kref, struct fcoe_interface, kref); + fcoe_interface_cleanup(fcoe); kfree(fcoe); } @@ -274,31 +394,6 @@ static int fcoe_lport_config(struct fc_lport *lp) return 0; } -/** - * fcoe_netdev_cleanup() - clean up netdev configurations - * @port: ptr to the fcoe_port - */ -void fcoe_netdev_cleanup(struct fcoe_port *port) -{ - u8 flogi_maddr[ETH_ALEN]; - struct fcoe_interface *fcoe = port->fcoe; - - /* Don't listen for Ethernet packets anymore */ - dev_remove_pack(&fcoe->fcoe_packet_type); - dev_remove_pack(&fcoe->fip_packet_type); - - /* Delete secondary MAC addresses */ - rtnl_lock(); - memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN); - dev_unicast_delete(fcoe->netdev, flogi_maddr); - if (!is_zero_ether_addr(fcoe->ctlr.data_src_addr)) - dev_unicast_delete(fcoe->netdev, fcoe->ctlr.data_src_addr); - if (fcoe->ctlr.spma) - dev_unicast_delete(fcoe->netdev, fcoe->ctlr.ctl_src_addr); - dev_mc_delete(fcoe->netdev, FIP_ALL_ENODE_MACS, ETH_ALEN, 0); - rtnl_unlock(); -} - /** * fcoe_queue_timer() - fcoe queue timer * @lp: the fc_lport pointer @@ -326,21 +421,10 @@ static int fcoe_netdev_config(struct fc_lport *lp, struct net_device *netdev) u64 wwnn, wwpn; struct fcoe_interface *fcoe; struct fcoe_port *port; - u8 flogi_maddr[ETH_ALEN]; - struct netdev_hw_addr *ha; /* Setup lport private data to point to fcoe softc */ port = lport_priv(lp); fcoe = port->fcoe; - fcoe->ctlr.lp = lp; - fcoe->netdev = netdev; - - /* 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)) { - return -EOPNOTSUPP; - } /* * Determine max frame size based on underlying device and optional @@ -376,57 +460,12 @@ static int fcoe_netdev_config(struct fc_lport *lp, struct net_device *netdev) port->fcoe_pending_queue_active = 0; setup_timer(&port->timer, fcoe_queue_timer, (unsigned long)lp); - /* look for SAN MAC address, if multiple SAN MACs exist, only - * use the first one for SPMA */ - rcu_read_lock(); - for_each_dev_addr(netdev, ha) { - if ((ha->type == NETDEV_HW_ADDR_T_SAN) && - (is_valid_ether_addr(fcoe->ctlr.ctl_src_addr))) { - memcpy(fcoe->ctlr.ctl_src_addr, ha->addr, ETH_ALEN); - fcoe->ctlr.spma = 1; - break; - } - } - rcu_read_unlock(); - - /* setup Source Mac Address */ - if (!fcoe->ctlr.spma) - memcpy(fcoe->ctlr.ctl_src_addr, netdev->dev_addr, - netdev->addr_len); - 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); - /* - * Add FCoE MAC address as second unicast MAC address - * or enter promiscuous mode if not capable of listening - * for multiple unicast MACs. - */ - rtnl_lock(); - memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN); - dev_unicast_add(netdev, flogi_maddr); - if (fcoe->ctlr.spma) - dev_unicast_add(netdev, fcoe->ctlr.ctl_src_addr); - dev_mc_add(netdev, FIP_ALL_ENODE_MACS, ETH_ALEN, 0); - rtnl_unlock(); - - /* - * setup the receive function from ethernet driver - * on the ethertype for the given device - */ - fcoe->fcoe_packet_type.func = fcoe_rcv; - fcoe->fcoe_packet_type.type = __constant_htons(ETH_P_FCOE); - fcoe->fcoe_packet_type.dev = netdev; - dev_add_pack(&fcoe->fcoe_packet_type); - - fcoe->fip_packet_type.func = fcoe_fip_recv; - fcoe->fip_packet_type.type = htons(ETH_P_FIP); - fcoe->fip_packet_type.dev = netdev; - dev_add_pack(&fcoe->fip_packet_type); - return 0; } @@ -578,19 +617,22 @@ static void fcoe_if_destroy(struct fc_lport *lport) /* Remove the instance from fcoe's list */ fcoe_hostlist_remove(lport); - /* clean up netdev configurations */ - fcoe_netdev_cleanup(port); + /* Cleanup the fc_lport */ + fc_lport_destroy(lport); + fc_fcp_destroy(lport); + + /* Stop the transmit retry timer */ + del_timer_sync(&port->timer); - /* tear-down the FCoE controller */ - fcoe_ctlr_destroy(&fcoe->ctlr); + /* Free existing transmit skbs */ + fcoe_clean_pending_queue(lport); + + /* receives may not be stopped until after this */ + fcoe_interface_put(fcoe); /* Free queued packets for the per-CPU receive threads */ fcoe_percpu_clean(lport); - /* Cleanup the fc_lport */ - fc_lport_destroy(lport); - fc_fcp_destroy(lport); - /* Detach from the scsi-ml */ fc_remove_host(lport->host); scsi_remove_host(lport->host); @@ -598,19 +640,12 @@ static void fcoe_if_destroy(struct fc_lport *lport) /* There are no more rports or I/O, free the EM */ fc_exch_mgr_free(lport); - /* Free existing skbs */ - fcoe_clean_pending_queue(lport); - - /* Stop the timer */ - del_timer_sync(&port->timer); - /* Free memory used by statistical counters */ fc_lport_free_stats(lport); /* Release the net_device and Scsi_Host */ dev_put(netdev); scsi_host_put(lport->host); - fcoe_interface_put(fcoe); } /* @@ -660,8 +695,7 @@ static struct libfc_function_template fcoe_libfc_fcn_templ = { * @fcoe: fcoe_interface structure to create an fc_lport instance on * @parent: device pointer to be the parent in sysfs for the SCSI host * - * Creates fc_lport struct and scsi_host for lport, configures lport - * and starts fabric login. + * Creates fc_lport struct and scsi_host for lport, configures lport. * * Returns : The allocated fc_lport or an error pointer */ @@ -695,19 +729,12 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe, goto out_host_put; } - /* - * Initialize FIP. - */ - fcoe_ctlr_init(&fcoe->ctlr); - fcoe->ctlr.send = fcoe_fip_send; - fcoe->ctlr.update_mac = fcoe_update_src_mac; - /* configure lport network properties */ rc = fcoe_netdev_config(lport, netdev); if (rc) { FCOE_NETDEV_DBG(netdev, "Could not configure netdev for the " "interface\n"); - goto out_netdev_cleanup; + goto out_lp_destroy; } /* configure lport scsi host properties */ @@ -715,7 +742,7 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe, if (rc) { FCOE_NETDEV_DBG(netdev, "Could not configure shost for the " "interface\n"); - goto out_netdev_cleanup; + goto out_lp_destroy; } /* Initialize the library */ @@ -745,21 +772,12 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe, fcoe_hostlist_add(lport); write_unlock(&fcoe_hostlist_lock); - lport->boot_time = jiffies; - - fc_fabric_login(lport); - - if (!fcoe_link_ok(lport)) - fcoe_ctlr_link_up(&fcoe->ctlr); - dev_hold(netdev); fcoe_interface_get(fcoe); return lport; out_lp_destroy: fc_exch_mgr_free(lport); -out_netdev_cleanup: - fcoe_netdev_cleanup(port); out_host_put: scsi_host_put(lport->host); out: @@ -1731,10 +1749,21 @@ static int fcoe_create(const char *buffer, struct kernel_param *kp) goto out_free; } - dev_put(netdev); - return 0; + /* Make this the "master" N_Port */ + fcoe->ctlr.lp = lport; + + /* start FIP Discovery and FLOGI */ + lport->boot_time = jiffies; + fc_fabric_login(lport); + if (!fcoe_link_ok(lport)) + fcoe_ctlr_link_up(&fcoe->ctlr); + rc = 0; out_free: + /* + * Release from init in fcoe_interface_create(), on success lport + * should be holding a reference taken in fcoe_if_create(). + */ fcoe_interface_put(fcoe); out_putdev: dev_put(netdev); -- cgit v1.2.3-70-g09d2 From dfc1d0fe3a8b2139295600ab519f24059493e6f6 Mon Sep 17 00:00:00 2001 From: Chris Leech Date: Tue, 25 Aug 2009 14:00:13 -0700 Subject: [SCSI] fcoe: add mutex to protect create and destroy Rather than rely on the hostlist_lock to be held while creating exchange managers, serialize fcoe instance creation and destruction with a mutex. This will allow the hostlist addition to be moved out of fcoe_if_create(), which will simplify NPIV support. Signed-off-by: Chris Leech Signed-off-by: Robert Love Signed-off-by: James Bottomley --- drivers/scsi/fcoe/fcoe.c | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) (limited to 'drivers/scsi/fcoe/fcoe.c') diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index 63aeeca384b..43added0a17 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -55,6 +55,8 @@ module_param_named(ddp_min, fcoe_ddp_min, uint, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(ddp_min, "Minimum I/O size in bytes for " \ "Direct Data Placement (DDP)."); +DEFINE_MUTEX(fcoe_config_mutex); + /* fcoe host list */ LIST_HEAD(fcoe_hostlist); DEFINE_RWLOCK(fcoe_hostlist_lock); @@ -811,6 +813,7 @@ static int __init fcoe_if_init(void) int __exit fcoe_if_exit(void) { fc_release_transport(scsi_transport_fcoe_sw); + scsi_transport_fcoe_sw = NULL; return 0; } @@ -1686,6 +1689,19 @@ static int fcoe_destroy(const char *buffer, struct kernel_param *kp) struct fc_lport *lport; int rc; + mutex_lock(&fcoe_config_mutex); +#ifdef CONFIG_FCOE_MODULE + /* + * Make sure the module has been initialized, and is not about to be + * removed. Module paramter sysfs files are writable before the + * module_init function is called and after module_exit. + */ + if (THIS_MODULE->state != MODULE_STATE_LIVE) { + rc = -ENODEV; + goto out_nodev; + } +#endif + netdev = fcoe_if_to_netdev(buffer); if (!netdev) { rc = -ENODEV; @@ -1705,6 +1721,7 @@ static int fcoe_destroy(const char *buffer, struct kernel_param *kp) out_putdev: dev_put(netdev); out_nodev: + mutex_unlock(&fcoe_config_mutex); return rc; } @@ -1722,6 +1739,19 @@ static int fcoe_create(const char *buffer, struct kernel_param *kp) struct fc_lport *lport; struct net_device *netdev; + mutex_lock(&fcoe_config_mutex); +#ifdef CONFIG_FCOE_MODULE + /* + * Make sure the module has been initialized, and is not about to be + * removed. Module paramter sysfs files are writable before the + * module_init function is called and after module_exit. + */ + if (THIS_MODULE->state != MODULE_STATE_LIVE) { + rc = -ENODEV; + goto out_nodev; + } +#endif + netdev = fcoe_if_to_netdev(buffer); if (!netdev) { rc = -ENODEV; @@ -1768,6 +1798,7 @@ out_free: out_putdev: dev_put(netdev); out_nodev: + mutex_unlock(&fcoe_config_mutex); return rc; } @@ -1971,6 +2002,8 @@ static int __init fcoe_init(void) int rc = 0; struct fcoe_percpu_s *p; + mutex_lock(&fcoe_config_mutex); + for_each_possible_cpu(cpu) { p = &per_cpu(fcoe_percpu, cpu); skb_queue_head_init(&p->fcoe_rx_list); @@ -1991,13 +2024,14 @@ static int __init fcoe_init(void) if (rc) goto out_free; + mutex_unlock(&fcoe_config_mutex); return 0; out_free: for_each_online_cpu(cpu) { fcoe_percpu_thread_destroy(cpu); } - + mutex_unlock(&fcoe_config_mutex); return rc; } module_init(fcoe_init); @@ -2012,6 +2046,8 @@ static void __exit fcoe_exit(void) unsigned int cpu; struct fcoe_interface *fcoe, *tmp; + mutex_lock(&fcoe_config_mutex); + fcoe_dev_cleanup(); /* releases the associated fcoe hosts */ @@ -2025,5 +2061,7 @@ static void __exit fcoe_exit(void) /* detach from scsi transport */ fcoe_if_exit(); + + mutex_unlock(&fcoe_config_mutex); } module_exit(fcoe_exit); -- cgit v1.2.3-70-g09d2 From c863df33bb784eecfb24090d2258fa0d3f653750 Mon Sep 17 00:00:00 2001 From: Chris Leech Date: Tue, 25 Aug 2009 14:00:18 -0700 Subject: [SCSI] fcoe: move the host-list add/remove to keep out VN_Ports We only want the FCoE create and destroy routines to deal with top level N_Ports, the VN_Ports are tracked on the vport list (see scsi_transport_fc). Signed-off-by: Chris Leech Signed-off-by: Robert Love Signed-off-by: James Bottomley --- drivers/scsi/fcoe/fcoe.c | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) (limited to 'drivers/scsi/fcoe/fcoe.c') diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index 43added0a17..c9a0346e493 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -616,9 +616,6 @@ static void fcoe_if_destroy(struct fc_lport *lport) /* Logout of the fabric */ fc_fabric_logoff(lport); - /* Remove the instance from fcoe's list */ - fcoe_hostlist_remove(lport); - /* Cleanup the fc_lport */ fc_lport_destroy(lport); fc_fcp_destroy(lport); @@ -757,11 +754,13 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe, /* * fcoe_em_alloc() and fcoe_hostlist_add() both - * need to be atomic under fcoe_hostlist_lock + * 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. */ - write_lock(&fcoe_hostlist_lock); + /* lport exch manager allocation */ rc = fcoe_em_config(lport); if (rc) { @@ -770,10 +769,6 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe, goto out_lp_destroy; } - /* add to lports list */ - fcoe_hostlist_add(lport); - write_unlock(&fcoe_hostlist_lock); - dev_hold(netdev); fcoe_interface_get(fcoe); return lport; @@ -1713,6 +1708,8 @@ static int fcoe_destroy(const char *buffer, struct kernel_param *kp) rc = -ENODEV; goto out_putdev; } + /* Remove the instance from fcoe's list */ + fcoe_hostlist_remove(lport); port = lport_priv(lport); fcoe = port->fcoe; fcoe_if_destroy(lport); @@ -1782,6 +1779,9 @@ static int fcoe_create(const char *buffer, struct kernel_param *kp) /* Make this the "master" N_Port */ fcoe->ctlr.lp = lport; + /* add to lports list */ + fcoe_hostlist_add(lport); + /* start FIP Discovery and FLOGI */ lport->boot_time = jiffies; fc_fabric_login(lport); @@ -1954,8 +1954,6 @@ 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 * - * Called with write fcoe_hostlist_lock held. - * * Returns: 0 for success */ int fcoe_hostlist_add(const struct fc_lport *lport) @@ -1963,12 +1961,14 @@ int fcoe_hostlist_add(const struct fc_lport *lport) struct fcoe_interface *fcoe; struct fcoe_port *port; + write_lock_bh(&fcoe_hostlist_lock); fcoe = fcoe_hostlist_lookup_port(fcoe_netdev(lport)); if (!fcoe) { port = lport_priv(lport); fcoe = port->fcoe; list_add_tail(&fcoe->list, &fcoe_hostlist); } + write_unlock_bh(&fcoe_hostlist_lock); return 0; } @@ -2045,14 +2045,21 @@ static void __exit fcoe_exit(void) { unsigned int cpu; struct fcoe_interface *fcoe, *tmp; + LIST_HEAD(local_list); mutex_lock(&fcoe_config_mutex); fcoe_dev_cleanup(); /* releases the associated fcoe hosts */ - list_for_each_entry_safe(fcoe, tmp, &fcoe_hostlist, list) + write_lock_bh(&fcoe_hostlist_lock); + list_splice_init(&fcoe_hostlist, &local_list); + write_unlock_bh(&fcoe_hostlist_lock); + + list_for_each_entry_safe(fcoe, tmp, &local_list, list) { + list_del(&fcoe->list); fcoe_if_destroy(fcoe->ctlr.lp); + } unregister_hotcpu_notifier(&fcoe_cpu_notifier); -- cgit v1.2.3-70-g09d2 From 2e70e2415193b84c1b79ec373af15c3f280ad7c4 Mon Sep 17 00:00:00 2001 From: Chris Leech Date: Tue, 25 Aug 2009 14:00:23 -0700 Subject: [SCSI] fcoe: Fix module ref count bug by adding NETDEV UNREGISTER handling Fixes reference counting on fcoe_instance and net_device, and adds NETDEV_UNREGISTER notifier handling so that you can unload network drivers. FCoE no longer increments the module use count for the network driver. On an NETDEV_UNREGISTER event, destroying the FCoE instance is deferred to a workqueue context to avoid RTNL deadlocks. Based in part by an earlier patch from John Fastabend John's patch description: Currently, the netdev module ref count is not decremented with module_put() when the module is unloaded while fcoe instances are present. To fix this removed reference count on netdev module completely and added functionality to netdev event handling for NETDEV_UNREGISTER events. This allows fcoe to remove devices cleanly when the netdev module is unloaded so we no longer need to hold a reference count for the netdev module. Signed-off-by: Chris Leech Signed-off-by: Robert Love Signed-off-by: James Bottomley --- drivers/scsi/fcoe/fcoe.c | 186 +++++++++++++++++------------------------------ drivers/scsi/fcoe/fcoe.h | 2 + 2 files changed, 67 insertions(+), 121 deletions(-) (limited to 'drivers/scsi/fcoe/fcoe.c') diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index c9a0346e493..c0264a98439 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -74,12 +74,13 @@ static int fcoe_link_ok(struct fc_lport *lp); static struct fc_lport *fcoe_hostlist_lookup(const struct net_device *); static int fcoe_hostlist_add(const struct fc_lport *); -static int fcoe_hostlist_remove(const struct fc_lport *); 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); /* notification function from net device */ static struct notifier_block fcoe_notifier = { @@ -149,6 +150,7 @@ static int fcoe_fip_recv(struct sk_buff *skb, struct net_device *dev, * @netdev : ptr to the associated netdevice struct * * Returns : 0 for success + * Locking: must be called with the RTNL mutex held */ static int fcoe_interface_setup(struct fcoe_interface *fcoe, struct net_device *netdev) @@ -188,13 +190,11 @@ static int fcoe_interface_setup(struct fcoe_interface *fcoe, * or enter promiscuous mode if not capable of listening * for multiple unicast MACs. */ - rtnl_lock(); memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN); dev_unicast_add(netdev, flogi_maddr); if (fip->spma) dev_unicast_add(netdev, fip->ctl_src_addr); dev_mc_add(netdev, FIP_ALL_ENODE_MACS, ETH_ALEN, 0); - rtnl_unlock(); /* * setup the receive function from ethernet driver @@ -215,6 +215,7 @@ static int fcoe_interface_setup(struct fcoe_interface *fcoe, 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() @@ -232,6 +233,7 @@ static struct fcoe_interface *fcoe_interface_create(struct net_device *netdev) return NULL; } + dev_hold(netdev); kref_init(&fcoe->kref); /* @@ -249,6 +251,8 @@ static struct fcoe_interface *fcoe_interface_create(struct net_device *netdev) /** * fcoe_interface_cleanup() - clean up netdev configurations * @fcoe: + * + * Caller must be holding the RTNL mutex */ void fcoe_interface_cleanup(struct fcoe_interface *fcoe) { @@ -266,11 +270,7 @@ void fcoe_interface_cleanup(struct fcoe_interface *fcoe) __dev_remove_pack(&fcoe->fip_packet_type); synchronize_net(); - /* tear-down the FCoE controller */ - fcoe_ctlr_destroy(&fcoe->ctlr); - /* Delete secondary MAC addresses */ - rtnl_lock(); 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)) @@ -278,7 +278,6 @@ void fcoe_interface_cleanup(struct fcoe_interface *fcoe) if (fip->spma) dev_unicast_delete(netdev, fip->ctl_src_addr); dev_mc_delete(netdev, FIP_ALL_ENODE_MACS, ETH_ALEN, 0); - rtnl_unlock(); } /** @@ -288,10 +287,14 @@ void fcoe_interface_cleanup(struct fcoe_interface *fcoe) static void fcoe_interface_release(struct kref *kref) { struct fcoe_interface *fcoe; + struct net_device *netdev; fcoe = container_of(kref, struct fcoe_interface, kref); - fcoe_interface_cleanup(fcoe); + netdev = fcoe->netdev; + /* tear-down the FCoE controller */ + fcoe_ctlr_destroy(&fcoe->ctlr); kfree(fcoe); + dev_put(netdev); } /** @@ -642,8 +645,7 @@ static void fcoe_if_destroy(struct fc_lport *lport) /* Free memory used by statistical counters */ fc_lport_free_stats(lport); - /* Release the net_device and Scsi_Host */ - dev_put(netdev); + /* Release the Scsi_Host */ scsi_host_put(lport->host); } @@ -718,7 +720,9 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe, } lport = shost_priv(shost); port = lport_priv(lport); + port->lport = lport; port->fcoe = fcoe; + INIT_WORK(&port->destroy_work, fcoe_destroy_work); /* configure fc_lport, e.g., em */ rc = fcoe_lport_config(lport); @@ -769,7 +773,6 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe, goto out_lp_destroy; } - dev_hold(netdev); fcoe_interface_get(fcoe); return lport; @@ -1530,19 +1533,19 @@ static int fcoe_device_notification(struct notifier_block *notifier, struct fc_lport *lp = NULL; struct net_device *netdev = ptr; struct fcoe_interface *fcoe; + struct fcoe_port *port; struct fcoe_dev_stats *stats; u32 link_possible = 1; u32 mfs; int rc = NOTIFY_OK; - read_lock(&fcoe_hostlist_lock); + write_lock(&fcoe_hostlist_lock); list_for_each_entry(fcoe, &fcoe_hostlist, list) { if (fcoe->netdev == netdev) { lp = fcoe->ctlr.lp; break; } } - read_unlock(&fcoe_hostlist_lock); if (lp == NULL) { rc = NOTIFY_DONE; goto out; @@ -1564,6 +1567,13 @@ static int fcoe_device_notification(struct notifier_block *notifier, break; case NETDEV_REGISTER: break; + case NETDEV_UNREGISTER: + list_del(&fcoe->list); + port = lport_priv(fcoe->ctlr.lp); + fcoe_interface_cleanup(fcoe); + schedule_work(&port->destroy_work); + goto out; + break; default: FCOE_NETDEV_DBG(netdev, "Unknown event %ld " "from netdev netlink\n", event); @@ -1576,6 +1586,7 @@ static int fcoe_device_notification(struct notifier_block *notifier, fcoe_clean_pending_queue(lp); } out: + write_unlock(&fcoe_hostlist_lock); return rc; } @@ -1600,75 +1611,6 @@ static struct net_device *fcoe_if_to_netdev(const char *buffer) return NULL; } -/** - * fcoe_netdev_to_module_owner() - finds out the driver module of the netdev - * @netdev: the target netdev - * - * Returns: ptr to the struct module, NULL for failure - */ -static struct module * -fcoe_netdev_to_module_owner(const struct net_device *netdev) -{ - struct device *dev; - - if (!netdev) - return NULL; - - dev = netdev->dev.parent; - if (!dev) - return NULL; - - if (!dev->driver) - return NULL; - - return dev->driver->owner; -} - -/** - * fcoe_ethdrv_get() - Hold the Ethernet driver - * @netdev: the target netdev - * - * Holds the Ethernet driver module by try_module_get() for - * the corresponding netdev. - * - * Returns: 0 for success - */ -static int fcoe_ethdrv_get(const struct net_device *netdev) -{ - struct module *owner; - - owner = fcoe_netdev_to_module_owner(netdev); - if (owner) { - FCOE_NETDEV_DBG(netdev, "Hold driver module %s\n", - module_name(owner)); - return try_module_get(owner); - } - return -ENODEV; -} - -/** - * fcoe_ethdrv_put() - Release the Ethernet driver - * @netdev: the target netdev - * - * Releases the Ethernet driver module by module_put for - * the corresponding netdev. - * - * Returns: 0 for success - */ -static int fcoe_ethdrv_put(const struct net_device *netdev) -{ - struct module *owner; - - owner = fcoe_netdev_to_module_owner(netdev); - if (owner) { - FCOE_NETDEV_DBG(netdev, "Release driver module %s\n", - module_name(owner)); - module_put(owner); - return 0; - } - return -ENODEV; -} - /** * fcoe_destroy() - handles the destroy from sysfs * @buffer: expected to be an eth if name @@ -1678,10 +1620,8 @@ static int fcoe_ethdrv_put(const struct net_device *netdev) */ static int fcoe_destroy(const char *buffer, struct kernel_param *kp) { - struct net_device *netdev; struct fcoe_interface *fcoe; - struct fcoe_port *port; - struct fc_lport *lport; + struct net_device *netdev; int rc; mutex_lock(&fcoe_config_mutex); @@ -1702,19 +1642,20 @@ static int fcoe_destroy(const char *buffer, struct kernel_param *kp) rc = -ENODEV; goto out_nodev; } - /* look for existing lport */ - lport = fcoe_hostlist_lookup(netdev); - if (!lport) { + + write_lock(&fcoe_hostlist_lock); + fcoe = fcoe_hostlist_lookup_port(netdev); + if (!fcoe) { + write_unlock(&fcoe_hostlist_lock); rc = -ENODEV; goto out_putdev; } - /* Remove the instance from fcoe's list */ - fcoe_hostlist_remove(lport); - port = lport_priv(lport); - fcoe = port->fcoe; - fcoe_if_destroy(lport); - fcoe_ethdrv_put(netdev); - rc = 0; + list_del(&fcoe->list); + write_unlock(&fcoe_hostlist_lock); + rtnl_lock(); + fcoe_interface_cleanup(fcoe); + rtnl_unlock(); + fcoe_if_destroy(fcoe->ctlr.lp); out_putdev: dev_put(netdev); out_nodev: @@ -1722,6 +1663,16 @@ out_nodev: return rc; } +static void fcoe_destroy_work(struct work_struct *work) +{ + struct fcoe_port *port; + + port = container_of(work, struct fcoe_port, destroy_work); + mutex_lock(&fcoe_config_mutex); + fcoe_if_destroy(port->lport); + mutex_unlock(&fcoe_config_mutex); +} + /** * fcoe_create() - Handles the create call from sysfs * @buffer: expected to be an eth if name @@ -1749,17 +1700,18 @@ static int fcoe_create(const char *buffer, struct kernel_param *kp) } #endif + rtnl_lock(); netdev = fcoe_if_to_netdev(buffer); if (!netdev) { rc = -ENODEV; goto out_nodev; } + /* look for existing lport */ if (fcoe_hostlist_lookup(netdev)) { rc = -EEXIST; goto out_putdev; } - fcoe_ethdrv_get(netdev); fcoe = fcoe_interface_create(netdev); if (!fcoe) { @@ -1771,8 +1723,8 @@ static int fcoe_create(const char *buffer, struct kernel_param *kp) if (IS_ERR(lport)) { printk(KERN_ERR "fcoe: Failed to create interface (%s)\n", netdev->name); - fcoe_ethdrv_put(netdev); rc = -EIO; + fcoe_interface_cleanup(fcoe); goto out_free; } @@ -1798,6 +1750,7 @@ out_free: out_putdev: dev_put(netdev); out_nodev: + rtnl_unlock(); mutex_unlock(&fcoe_config_mutex); return rc; } @@ -1972,25 +1925,6 @@ int fcoe_hostlist_add(const struct fc_lport *lport) return 0; } -/** - * fcoe_hostlist_remove() - remove a lport from lports list - * @lp: ptr to the fc_lport to be removed - * - * Returns: 0 for success - */ -int fcoe_hostlist_remove(const struct fc_lport *lport) -{ - struct fcoe_interface *fcoe; - - write_lock_bh(&fcoe_hostlist_lock); - fcoe = fcoe_hostlist_lookup_port(fcoe_netdev(lport)); - BUG_ON(!fcoe); - list_del(&fcoe->list); - write_unlock_bh(&fcoe_hostlist_lock); - - return 0; -} - /** * fcoe_init() - fcoe module loading initialization * @@ -2046,6 +1980,7 @@ static void __exit fcoe_exit(void) unsigned int cpu; struct fcoe_interface *fcoe, *tmp; LIST_HEAD(local_list); + struct fcoe_port *port; mutex_lock(&fcoe_config_mutex); @@ -2058,7 +1993,11 @@ static void __exit fcoe_exit(void) list_for_each_entry_safe(fcoe, tmp, &local_list, list) { list_del(&fcoe->list); - fcoe_if_destroy(fcoe->ctlr.lp); + port = lport_priv(fcoe->ctlr.lp); + rtnl_lock(); + fcoe_interface_cleanup(fcoe); + rtnl_unlock(); + schedule_work(&port->destroy_work); } unregister_hotcpu_notifier(&fcoe_cpu_notifier); @@ -2066,9 +2005,14 @@ static void __exit fcoe_exit(void) for_each_online_cpu(cpu) fcoe_percpu_thread_destroy(cpu); - /* detach from scsi transport */ - fcoe_if_exit(); - mutex_unlock(&fcoe_config_mutex); + + /* flush any asyncronous interface destroys, + * this should happen after the netdev notifier is unregistered */ + 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); diff --git a/drivers/scsi/fcoe/fcoe.h b/drivers/scsi/fcoe/fcoe.h index ff229288b7f..ce7f60fb1bc 100644 --- a/drivers/scsi/fcoe/fcoe.h +++ b/drivers/scsi/fcoe/fcoe.h @@ -93,9 +93,11 @@ struct fcoe_interface { */ 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 */ }; #define fcoe_from_ctlr(fip) container_of(fip, struct fcoe_interface, ctlr) -- cgit v1.2.3-70-g09d2 From 090eb6c41aa74273d3f0721637cff738cfd80669 Mon Sep 17 00:00:00 2001 From: Chris Leech Date: Tue, 25 Aug 2009 14:00:28 -0700 Subject: [SCSI] fcoe: use rtnl mutex in place of hostlist lock This just cuts down on the number of locks we're dealing with, and eliminates the need to take another lock in the netdev notifier. Signed-off-by: Chris Leech Signed-off-by: Robert Love Signed-off-by: James Bottomley --- drivers/scsi/fcoe/fcoe.c | 37 +++++++++++-------------------------- 1 file changed, 11 insertions(+), 26 deletions(-) (limited to 'drivers/scsi/fcoe/fcoe.c') diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index c0264a98439..ac481ad112a 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -58,8 +58,8 @@ MODULE_PARM_DESC(ddp_min, "Minimum I/O size in bytes for " \ DEFINE_MUTEX(fcoe_config_mutex); /* fcoe host list */ +/* must only by accessed under the RTNL mutex */ LIST_HEAD(fcoe_hostlist); -DEFINE_RWLOCK(fcoe_hostlist_lock); DEFINE_PER_CPU(struct fcoe_percpu_s, fcoe_percpu); /* Function Prototypes */ @@ -527,8 +527,6 @@ 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 * - * Called with write fcoe_hostlist_lock held. - * * Returns : 0 on success */ static inline int fcoe_em_config(struct fc_lport *lp) @@ -1539,7 +1537,6 @@ static int fcoe_device_notification(struct notifier_block *notifier, u32 mfs; int rc = NOTIFY_OK; - write_lock(&fcoe_hostlist_lock); list_for_each_entry(fcoe, &fcoe_hostlist, list) { if (fcoe->netdev == netdev) { lp = fcoe->ctlr.lp; @@ -1586,7 +1583,6 @@ static int fcoe_device_notification(struct notifier_block *notifier, fcoe_clean_pending_queue(lp); } out: - write_unlock(&fcoe_hostlist_lock); return rc; } @@ -1643,16 +1639,14 @@ static int fcoe_destroy(const char *buffer, struct kernel_param *kp) goto out_nodev; } - write_lock(&fcoe_hostlist_lock); + rtnl_lock(); fcoe = fcoe_hostlist_lookup_port(netdev); if (!fcoe) { - write_unlock(&fcoe_hostlist_lock); + rtnl_unlock(); rc = -ENODEV; goto out_putdev; } list_del(&fcoe->list); - write_unlock(&fcoe_hostlist_lock); - rtnl_lock(); fcoe_interface_cleanup(fcoe); rtnl_unlock(); fcoe_if_destroy(fcoe->ctlr.lp); @@ -1870,9 +1864,8 @@ 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 * - * Called with fcoe_hostlist_lock held. - * * Returns: NULL or the located fcoe_port + * Locking: must be called with the RNL mutex held */ static struct fcoe_interface * fcoe_hostlist_lookup_port(const struct net_device *dev) @@ -1891,15 +1884,13 @@ fcoe_hostlist_lookup_port(const struct net_device *dev) * @netdev: ptr to net_device * * Returns: 0 for success + * Locking: must be called with the RTNL mutex held */ -struct fc_lport *fcoe_hostlist_lookup(const struct net_device *netdev) +static struct fc_lport *fcoe_hostlist_lookup(const struct net_device *netdev) { struct fcoe_interface *fcoe; - read_lock(&fcoe_hostlist_lock); fcoe = fcoe_hostlist_lookup_port(netdev); - read_unlock(&fcoe_hostlist_lock); - return (fcoe) ? fcoe->ctlr.lp : NULL; } @@ -1908,20 +1899,19 @@ struct fc_lport *fcoe_hostlist_lookup(const struct net_device *netdev) * @lp: ptr to the fc_lport to be added * * Returns: 0 for success + * Locking: must be called with the RTNL mutex held */ -int fcoe_hostlist_add(const struct fc_lport *lport) +static int fcoe_hostlist_add(const struct fc_lport *lport) { struct fcoe_interface *fcoe; struct fcoe_port *port; - write_lock_bh(&fcoe_hostlist_lock); fcoe = fcoe_hostlist_lookup_port(fcoe_netdev(lport)); if (!fcoe) { port = lport_priv(lport); fcoe = port->fcoe; list_add_tail(&fcoe->list, &fcoe_hostlist); } - write_unlock_bh(&fcoe_hostlist_lock); return 0; } @@ -1979,7 +1969,6 @@ static void __exit fcoe_exit(void) { unsigned int cpu; struct fcoe_interface *fcoe, *tmp; - LIST_HEAD(local_list); struct fcoe_port *port; mutex_lock(&fcoe_config_mutex); @@ -1987,18 +1976,14 @@ static void __exit fcoe_exit(void) fcoe_dev_cleanup(); /* releases the associated fcoe hosts */ - write_lock_bh(&fcoe_hostlist_lock); - list_splice_init(&fcoe_hostlist, &local_list); - write_unlock_bh(&fcoe_hostlist_lock); - - list_for_each_entry_safe(fcoe, tmp, &local_list, list) { + rtnl_lock(); + list_for_each_entry_safe(fcoe, tmp, &fcoe_hostlist, list) { list_del(&fcoe->list); port = lport_priv(fcoe->ctlr.lp); - rtnl_lock(); fcoe_interface_cleanup(fcoe); - rtnl_unlock(); schedule_work(&port->destroy_work); } + rtnl_unlock(); unregister_hotcpu_notifier(&fcoe_cpu_notifier); -- cgit v1.2.3-70-g09d2 From e7a51997dad4e17395be1209970e18d2e9305b24 Mon Sep 17 00:00:00 2001 From: Joe Eykholt Date: Tue, 25 Aug 2009 14:04:08 -0700 Subject: [SCSI] fcoe: flush per-cpu thread work when destroying interface This fixes one cause of an occational problem when unloading libfc where the exchange manager pool doesn't have all items freed. The existing WARN_ON(mp->total_exches <= 0) isn't hit. However, note that total_exches is decremented when the exchange is completed, and it can be held with a refcnt for a while after that. I'm not sure what the offending exchange is, but I suspect it is an incoming request, because outgoing state machines should be all stopped at this point. Note that although receive is stopped before the exchange manager is freed, there could still be active threads handling received frames. This patch flushes the queues by allocating a new skb and sending it through, and have the thread handle this new skb specially. This is similar to the way the work queues are flushed now by putting work items in them and waiting until they make it through the queue. An skb->destructor function is used to inform us of the completion of the flush, and the fr_dev() is left NULL to indicate to fcoe_percpu_receive_thread() that the skb should be just freed. There's already a check for the lp being NULL which prints a message. We skip printing the message if the destructor is for flushing. Signed-off-by: Joe Eykholt Signed-off-by: Robert Love Signed-off-by: James Bottomley --- drivers/scsi/fcoe/fcoe.c | 42 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) (limited to 'drivers/scsi/fcoe/fcoe.c') diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index ac481ad112a..704b8e03494 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -57,6 +57,9 @@ MODULE_PARM_DESC(ddp_min, "Minimum I/O size in bytes for " \ DEFINE_MUTEX(fcoe_config_mutex); +/* fcoe_percpu_clean completion. Waiter protected by fcoe_create_mutex */ +static DECLARE_COMPLETION(fcoe_flush_completion); + /* fcoe host list */ /* must only by accessed under the RTNL mutex */ LIST_HEAD(fcoe_hostlist); @@ -827,7 +830,7 @@ static void fcoe_percpu_thread_create(unsigned int cpu) thread = kthread_create(fcoe_percpu_receive_thread, (void *)p, "fcoethread/%d", cpu); - if (likely(!IS_ERR(p->thread))) { + if (likely(!IS_ERR(thread))) { kthread_bind(thread, cpu); wake_up_process(thread); @@ -1299,6 +1302,15 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp) return 0; } +/** + * fcoe_percpu_flush_done() - Indicate percpu queue flush completion. + * @skb: the skb being completed. + */ +static void fcoe_percpu_flush_done(struct sk_buff *skb) +{ + complete(&fcoe_flush_completion); +} + /** * fcoe_percpu_receive_thread() - recv thread per cpu * @arg: ptr to the fcoe per cpu struct @@ -1338,7 +1350,8 @@ int fcoe_percpu_receive_thread(void *arg) fr = fcoe_dev_from_skb(skb); lp = fr->fr_dev; if (unlikely(lp == NULL)) { - FCOE_NETDEV_DBG(skb->dev, "Invalid HBA Structure"); + if (skb->destructor != fcoe_percpu_flush_done) + FCOE_NETDEV_DBG(skb->dev, "NULL lport in skb"); kfree_skb(skb); continue; } @@ -1799,6 +1812,13 @@ int fcoe_link_ok(struct fc_lport *lp) /** * fcoe_percpu_clean() - Clear the pending skbs for an lport * @lp: the fc_lport + * + * Must be called with fcoe_create_mutex held to single-thread completion. + * + * This flushes the pending skbs by adding a new skb to each queue and + * waiting until they are all freed. This assures us that not only are + * 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) { @@ -1823,7 +1843,25 @@ void fcoe_percpu_clean(struct fc_lport *lp) kfree_skb(skb); } } + + if (!pp->thread || !cpu_online(cpu)) { + spin_unlock_bh(&pp->fcoe_rx_list.lock); + continue; + } + + skb = dev_alloc_skb(0); + if (!skb) { + spin_unlock_bh(&pp->fcoe_rx_list.lock); + continue; + } + skb->destructor = fcoe_percpu_flush_done; + + __skb_queue_tail(&pp->fcoe_rx_list, skb); + if (pp->fcoe_rx_list.qlen == 1) + wake_up_process(pp->thread); spin_unlock_bh(&pp->fcoe_rx_list.lock); + + wait_for_completion(&fcoe_flush_completion); } } -- cgit v1.2.3-70-g09d2