linux-scsi.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 00/28] libfc, libfcoe and fcoe updates for scsi-misc
@ 2010-07-20 22:19 Robert Love
  2010-07-20 22:19 ` [PATCH 01/28] libfc: fix slowpath error from WARN_ON in fc_fcp_send_data Robert Love
                   ` (27 more replies)
  0 siblings, 28 replies; 34+ messages in thread
From: Robert Love @ 2010-07-20 22:19 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi

The following series implements multiple bug fixes, FCoE VN2VN
mode and a VLAN ID patch revert.

VN2VN is a point-to-multipoint FCoE mode. It allows direct
connections from the initiator to VN2VN capable FCoE targets.

The VLAN ID revert removes the VLAN ID from the generated WWPN.
Adding the VLAN ID to the WWPN is not typical and can cause
zoning issues in a variety of scenarios. Note that this change
will cause a one time rezone for the new WWPN.

Please apply these patches to scsi-misc.

---

Bart Van Assche (1):
      fcoe: make it possible to verify fcoe with sparse

Bhanu Prakash Gollapudi (1):
      libfc: Add retry logic to lport state machine when receiving LS_RJT

Joe Eykholt (19):
      libfc: convert rport lookup to be RCU safe
      libfc: provide space for LLD after remote port structure
      libfcoe: convert FIP to lock with mutex instead of spin lock
      libfc: add discovery-private pointer for LLD
      libfcoe: fcoe: fnic: change fcoe_ctlr_init interface to specify mode
      libfc: Add local port point-to-multipoint flag
      libfc: add FLOGI state to rport for VN2VN
      libfc: track FIP exchanges
      libfcoe: add protocol description of FIP VN2VN mode
      libfcoe: add state change debugging
      libfcoe: fcoe: fnic: add FIP VN2VN point-to-multipoint support
      libfcoe: Fix FIP ELS encapsulation details for FLOGI responses
      fcoe libfcoe: use correct FC-MAP for VN2VN mode
      fcoe: config via separate create_vn2vn module parameter
      libfc: eliminate rport LOGO state
      libfc: add fc_frame_sid() and fc_frame_did() functions
      libfc: add fc_fill_reply_hdr() and fc_fill_hdr()
      libfc: add interface to allocate a sequence for incoming requests
      libfc: don't require a local exchange for incoming requests

Vasu Dev (5):
      libfc: IO errors on link down due to cable unplug
      fcoe: cleans up fcoe_disable and fcoe_enable
      fcoe: adds src and dest mac address checking for fcoe frames
      Revert "[SCSI] fcoe: Fix using VLAN ID in creating lport's WWWN/WWPN"
      fcoe: remove check for zero fabric name

Yi Zou (2):
      libfc: fix slowpath error from WARN_ON in fc_fcp_send_data
      fcoe: fix offload feature flag change from netdev


 drivers/scsi/fcoe/fcoe.c      |  147 +++--
 drivers/scsi/fcoe/libfcoe.c   | 1306 ++++++++++++++++++++++++++++++++++++-----
 drivers/scsi/fnic/fnic_main.c |   11 
 drivers/scsi/libfc/fc_disc.c  |   39 +
 drivers/scsi/libfc/fc_elsct.c |    2 
 drivers/scsi/libfc/fc_exch.c  |  215 ++++---
 drivers/scsi/libfc/fc_fcp.c   |   13 
 drivers/scsi/libfc/fc_libfc.c |   78 ++
 drivers/scsi/libfc/fc_libfc.h |    2 
 drivers/scsi/libfc/fc_lport.c |  210 +++----
 drivers/scsi/libfc/fc_rport.c |  564 +++++++++++-------
 include/scsi/fc/fc_els.h      |    2 
 include/scsi/fc/fc_fip.h      |   46 +
 include/scsi/fc/fc_ns.h       |    7 
 include/scsi/fc_encode.h      |    7 
 include/scsi/fc_frame.h       |   52 ++
 include/scsi/libfc.h          |   68 +-
 include/scsi/libfcoe.h        |   72 ++
 18 files changed, 2157 insertions(+), 684 deletions(-)

-- 
Thanks, //Rob

^ permalink raw reply	[flat|nested] 34+ messages in thread

* [PATCH 01/28] libfc: fix slowpath error from WARN_ON in fc_fcp_send_data
  2010-07-20 22:19 [PATCH 00/28] libfc, libfcoe and fcoe updates for scsi-misc Robert Love
@ 2010-07-20 22:19 ` Robert Love
  2010-07-20 22:19 ` [PATCH 02/28] fcoe: make it possible to verify fcoe with sparse Robert Love
                   ` (26 subsequent siblings)
  27 siblings, 0 replies; 34+ messages in thread
From: Robert Love @ 2010-07-20 22:19 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi; +Cc: Yi Zou

From: Yi Zou <yi.zou@intel.com>

This is exposed by a mpio test using EMC CLARiiON targets when LUN
tresspassing happens, the burst length from the XFER_READY for the
MODE SELECT(10) is 19 bytes, much smaller than FC_MIN_MAX_PAYLOAD as
256 bytes. This patch removes the related two WARN_ON()s.

Signed-off-by: Yi Zou <yi.zou@intel.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---
 drivers/scsi/libfc/fc_fcp.c |    2 --
 1 files changed, 0 insertions(+), 2 deletions(-)

diff --git a/drivers/scsi/libfc/fc_fcp.c b/drivers/scsi/libfc/fc_fcp.c
index 8914f1e..a0a3ae7 100644
--- a/drivers/scsi/libfc/fc_fcp.c
+++ b/drivers/scsi/libfc/fc_fcp.c
@@ -580,10 +580,8 @@ static int fc_fcp_send_data(struct fc_fcp_pkt *fsp, struct fc_seq *seq,
 			   fsp, seq_blen, lport->lso_max, t_blen);
 	}
 
-	WARN_ON(t_blen < FC_MIN_MAX_PAYLOAD);
 	if (t_blen > 512)
 		t_blen &= ~(512 - 1);	/* round down to block size */
-	WARN_ON(t_blen < FC_MIN_MAX_PAYLOAD);	/* won't go below 256 */
 	sc = fsp->cmd;
 
 	remaining = seq_blen;


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 02/28] fcoe: make it possible to verify fcoe with sparse
  2010-07-20 22:19 [PATCH 00/28] libfc, libfcoe and fcoe updates for scsi-misc Robert Love
  2010-07-20 22:19 ` [PATCH 01/28] libfc: fix slowpath error from WARN_ON in fc_fcp_send_data Robert Love
@ 2010-07-20 22:19 ` Robert Love
  2010-07-20 22:19 ` [PATCH 03/28] libfc: IO errors on link down due to cable unplug Robert Love
                   ` (25 subsequent siblings)
  27 siblings, 0 replies; 34+ messages in thread
From: Robert Love @ 2010-07-20 22:19 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi; +Cc: Bart Van Assche, jeykholt

From: Bart Van Assche <bvanassche@acm.org>

Analyzing fcoe with sparse currently fails. This is because struct
fcoe_rcv_info contains two enum members that have been declared with
__attribute__((packed)). Apparently gcc honors this attribute while sparse
ignores it. The result is that sizeof(struct fcoe_rcv_info)
== sizeof(struct sk_buff::cb) == 48 on a 64-bit system according to gcc, but
not according to sparse. The patch below modifies the definition of
struct fcoe_rcv_info such that gcc and sparse interpret this structure
definition in the same way. The current sparse output is as follows:

$ cd linux-2.6.34
$ make C=2 M=drivers/scsi/fcoe modules
 CHECK   drivers/scsi/fcoe/fcoe.c

include/scsi/fc_frame.h:81:9: error: invalid bitfield width, -1.
 CC [M]  drivers/scsi/fcoe/fcoe.o
 CHECK   drivers/scsi/fcoe/libfcoe.c

include/scsi/fc_frame.h:81:9: error: invalid bitfield width, -1.
drivers/scsi/fcoe/libfcoe.c:56:37: error: invalid initializer

Signed-off-by: Bart Van Assche <bart.vanassche@gmail.com>
Cc: jeykholt@cisco.com
Signed-off-by: Robert Love <robert.w.love@intel.com>
---
 include/scsi/fc_frame.h |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/include/scsi/fc_frame.h b/include/scsi/fc_frame.h
index 4d3e9c7..15427fa 100644
--- a/include/scsi/fc_frame.h
+++ b/include/scsi/fc_frame.h
@@ -66,8 +66,8 @@ struct fcoe_rcv_info {
 	struct fc_fcp_pkt *fr_fsp;	/* for the corresponding fcp I/O */
 	u32		fr_crc;
 	u16		fr_max_payload;	/* max FC payload */
-	enum fc_sof	fr_sof;		/* start of frame delimiter */
-	enum fc_eof	fr_eof;		/* end of frame delimiter */
+	u8		fr_sof;		/* start of frame delimiter */
+	u8		fr_eof;		/* end of frame delimiter */
 	u8		fr_flags;	/* flags - see below */
 	u8		granted_mac[ETH_ALEN]; /* FCoE MAC address */
 };


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 03/28] libfc: IO errors on link down due to cable unplug
  2010-07-20 22:19 [PATCH 00/28] libfc, libfcoe and fcoe updates for scsi-misc Robert Love
  2010-07-20 22:19 ` [PATCH 01/28] libfc: fix slowpath error from WARN_ON in fc_fcp_send_data Robert Love
  2010-07-20 22:19 ` [PATCH 02/28] fcoe: make it possible to verify fcoe with sparse Robert Love
@ 2010-07-20 22:19 ` Robert Love
  2010-07-21  3:29   ` Mike Christie
  2010-07-20 22:19 ` [PATCH 04/28] fcoe: cleans up fcoe_disable and fcoe_enable Robert Love
                   ` (24 subsequent siblings)
  27 siblings, 1 reply; 34+ messages in thread
From: Robert Love @ 2010-07-20 22:19 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi; +Cc: Vasu Dev

From: Vasu Dev <vasu.dev@intel.com>

In this case, sync IO fails with EIO(5) errors as:-

"Thread:1 System call error:5 - Input/output error (::pwrite() failed)".

This is due to IO time out while libfc doing link down processing
to block all rports and if timed out IO was at last retry
attempt then it fails to user with EIO error followed by
these log messages.

[77848.612169] host2: rport bf0015: Delete port
[77848.612221] host2: rport e10aef: work delete
[77848.612232] host2: rport e10002: work event 3
[77848.612422] sd 2:0:1:1: [sdi] Unhandled error code
[77848.612426] sd 2:0:1:1: [sdi] Result: hostbyte=DID_ERROR
driverbyte=DRIVER_OK
[77848.612431] sd 2:0:1:1: [sdi] CDB: Write(10): 2a 00 00 00 11 20 00 00 20 00
[77848.612445] end_request: I/O error, dev sdi, sector 4384
[77848.612553] sd 2:0:1:2: [sdj] Unhandled error code

To fix these EIO errors, such timed out incomplete IOs needs
to be re-queued without counting retry attempt and this patch
does that using DID_REQUEUE scsi code.

Signed-off-by: Vasu Dev <vasu.dev@intel.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---
 drivers/scsi/libfc/fc_fcp.c |    5 +++++
 1 files changed, 5 insertions(+), 0 deletions(-)

diff --git a/drivers/scsi/libfc/fc_fcp.c b/drivers/scsi/libfc/fc_fcp.c
index a0a3ae7..61a1297 100644
--- a/drivers/scsi/libfc/fc_fcp.c
+++ b/drivers/scsi/libfc/fc_fcp.c
@@ -1971,6 +1971,11 @@ static void fc_io_compl(struct fc_fcp_pkt *fsp)
 		break;
 	}
 
+	if (lport->state != LPORT_ST_READY && fsp->status_code != FC_COMPLETE) {
+		sc_cmd->result = (DID_REQUEUE << 16);
+		FC_FCP_DBG(fsp, "Returning DID_REQUEUE to scsi-ml\n");
+	}
+
 	spin_lock_irqsave(&si->scsi_queue_lock, flags);
 	list_del(&fsp->list);
 	spin_unlock_irqrestore(&si->scsi_queue_lock, flags);


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 04/28] fcoe: cleans up fcoe_disable and fcoe_enable
  2010-07-20 22:19 [PATCH 00/28] libfc, libfcoe and fcoe updates for scsi-misc Robert Love
                   ` (2 preceding siblings ...)
  2010-07-20 22:19 ` [PATCH 03/28] libfc: IO errors on link down due to cable unplug Robert Love
@ 2010-07-20 22:19 ` Robert Love
  2010-07-20 22:19 ` [PATCH 05/28] fcoe: adds src and dest mac address checking for fcoe frames Robert Love
                   ` (23 subsequent siblings)
  27 siblings, 0 replies; 34+ messages in thread
From: Robert Love @ 2010-07-20 22:19 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi; +Cc: Vasu Dev

From: Vasu Dev <vasu.dev@intel.com>

The fc_fabric_logoff and fc_fabric_login are redundant
here after recently added fcoe_ctlr_link_down/up to
these functions, therefore this patch removes logoff
and login to only use link down and up here. This works
best for their current usages with fcoe DCB link down or up.

This also works well to avoid EIO errors when fcoe DCB link
goes down as lport state moves out of ready quickly from
fcoe_ctlr_link_down and that allows re-queuing timed out IOs
for this case also.

Signed-off-by: Vasu Dev <vasu.dev@intel.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---
 drivers/scsi/fcoe/fcoe.c |   10 ++++------
 1 files changed, 4 insertions(+), 6 deletions(-)

diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c
index 44a0759..d340cf2 100644
--- a/drivers/scsi/fcoe/fcoe.c
+++ b/drivers/scsi/fcoe/fcoe.c
@@ -1918,8 +1918,8 @@ static int fcoe_disable(const char *buffer, struct kernel_param *kp)
 	rtnl_unlock();
 
 	if (fcoe) {
-		fc_fabric_logoff(fcoe->ctlr.lp);
 		fcoe_ctlr_link_down(&fcoe->ctlr);
+		fcoe_clean_pending_queue(fcoe->ctlr.lp);
 	} else
 		rc = -ENODEV;
 
@@ -1972,12 +1972,10 @@ static int fcoe_enable(const char *buffer, struct kernel_param *kp)
 	fcoe = fcoe_hostlist_lookup_port(netdev);
 	rtnl_unlock();
 
-	if (fcoe) {
-		if (!fcoe_link_ok(fcoe->ctlr.lp))
-			fcoe_ctlr_link_up(&fcoe->ctlr);
-		rc = fc_fabric_login(fcoe->ctlr.lp);
-	} else
+	if (!fcoe)
 		rc = -ENODEV;
+	else if (!fcoe_link_ok(fcoe->ctlr.lp))
+		fcoe_ctlr_link_up(&fcoe->ctlr);
 
 	dev_put(netdev);
 out_nodev:


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 05/28] fcoe: adds src and dest mac address checking for fcoe frames
  2010-07-20 22:19 [PATCH 00/28] libfc, libfcoe and fcoe updates for scsi-misc Robert Love
                   ` (3 preceding siblings ...)
  2010-07-20 22:19 ` [PATCH 04/28] fcoe: cleans up fcoe_disable and fcoe_enable Robert Love
@ 2010-07-20 22:19 ` Robert Love
  2010-07-20 22:19 ` [PATCH 06/28] libfc: convert rport lookup to be RCU safe Robert Love
                   ` (22 subsequent siblings)
  27 siblings, 0 replies; 34+ messages in thread
From: Robert Love @ 2010-07-20 22:19 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi; +Cc: Vasu Dev

From: Vasu Dev <vasu.dev@intel.com>

This is  per FC-BB-5 Annex-D recommendation and per that
if address checking fails then drop the frame.

FIP code paths are already doing this so only needed for fcoe
frames.

The src address checking is limited to only fip mode since
this might break non-fip mode used in p2p due to used OUI
based addressing in some p2p code paths, going forward FIP
will be the only mode, therefore limited this to only FIP
mode so that it won't break non-fip p2p mode for now.

-v2
Removes FCOE packet type checking since fcoe_rcv is
registered to receive only FCoE type packets from netdev
and it is already checked by netdev.

Signed-off-by: Vasu Dev <vasu.dev@intel.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---
 drivers/scsi/fcoe/fcoe.c |   20 +++++++++++++++++---
 include/scsi/libfcoe.h   |   10 ++++++++++
 2 files changed, 27 insertions(+), 3 deletions(-)

diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c
index d340cf2..a120962 100644
--- a/drivers/scsi/fcoe/fcoe.c
+++ b/drivers/scsi/fcoe/fcoe.c
@@ -1210,6 +1210,8 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *netdev,
 	struct fcoe_interface *fcoe;
 	struct fc_frame_header *fh;
 	struct fcoe_percpu_s *fps;
+	struct fcoe_port *port;
+	struct ethhdr *eh;
 	unsigned int cpu;
 
 	fcoe = container_of(ptype, struct fcoe_interface, fcoe_packet_type);
@@ -1227,9 +1229,21 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *netdev,
 			skb_tail_pointer(skb), skb_end_pointer(skb),
 			skb->csum, skb->dev ? skb->dev->name : "<NULL>");
 
-	/* check for FCOE packet type */
-	if (unlikely(eth_hdr(skb)->h_proto != htons(ETH_P_FCOE))) {
-		FCOE_NETDEV_DBG(netdev, "Wrong FC type frame");
+	/* check for mac addresses */
+	eh = eth_hdr(skb);
+	port = lport_priv(lport);
+	if (compare_ether_addr(eh->h_dest, port->data_src_addr) &&
+	    compare_ether_addr(eh->h_dest, fcoe->ctlr.ctl_src_addr) &&
+	    compare_ether_addr(eh->h_dest, (u8[6])FC_FCOE_FLOGI_MAC)) {
+		FCOE_NETDEV_DBG(netdev, "wrong destination mac address:%pM\n",
+				eh->h_dest);
+		goto err;
+	}
+
+	if (is_fip_mode(&fcoe->ctlr) &&
+	    compare_ether_addr(eh->h_source, fcoe->ctlr.dest_addr)) {
+		FCOE_NETDEV_DBG(netdev, "wrong source mac address:%pM\n",
+				eh->h_source);
 		goto err;
 	}
 
diff --git a/include/scsi/libfcoe.h b/include/scsi/libfcoe.h
index ec13f51..81aee1c 100644
--- a/include/scsi/libfcoe.h
+++ b/include/scsi/libfcoe.h
@@ -170,4 +170,14 @@ int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *, struct fc_lport *,
 u64 fcoe_wwn_from_mac(unsigned char mac[], unsigned int, unsigned int);
 int fcoe_libfc_config(struct fc_lport *, struct libfc_function_template *);
 
+/**
+ * is_fip_mode() - returns true if FIP mode selected.
+ * @fip:	FCoE controller.
+ */
+static inline bool is_fip_mode(struct fcoe_ctlr *fip)
+{
+	return fip->state == FIP_ST_ENABLED;
+}
+
+
 #endif /* _LIBFCOE_H */


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 06/28] libfc: convert rport lookup to be RCU safe
  2010-07-20 22:19 [PATCH 00/28] libfc, libfcoe and fcoe updates for scsi-misc Robert Love
                   ` (4 preceding siblings ...)
  2010-07-20 22:19 ` [PATCH 05/28] fcoe: adds src and dest mac address checking for fcoe frames Robert Love
@ 2010-07-20 22:19 ` Robert Love
  2010-07-20 22:19 ` [PATCH 07/28] libfc: provide space for LLD after remote port structure Robert Love
                   ` (21 subsequent siblings)
  27 siblings, 0 replies; 34+ messages in thread
From: Robert Love @ 2010-07-20 22:19 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi; +Cc: Joe Eykholt

From: Joe Eykholt <jeykholt@cisco.com>

To allow LLD to do lookups on rports without grabbing a mutex,
make them RCU-safe.  The caller of lport->tt.rport_lookup will
have the choice of holding disc_mutex or the rcu_read_lock().

Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---
 drivers/scsi/libfc/fc_disc.c  |    6 +++---
 drivers/scsi/libfc/fc_rport.c |   22 ++++++++++++++++++----
 include/scsi/libfc.h          |    2 ++
 3 files changed, 23 insertions(+), 7 deletions(-)

diff --git a/drivers/scsi/libfc/fc_disc.c b/drivers/scsi/libfc/fc_disc.c
index c7985da..d0fa9a0 100644
--- a/drivers/scsi/libfc/fc_disc.c
+++ b/drivers/scsi/libfc/fc_disc.c
@@ -63,12 +63,12 @@ static void fc_disc_restart(struct fc_disc *);
 void fc_disc_stop_rports(struct fc_disc *disc)
 {
 	struct fc_lport *lport;
-	struct fc_rport_priv *rdata, *next;
+	struct fc_rport_priv *rdata;
 
 	lport = disc->lport;
 
 	mutex_lock(&disc->disc_mutex);
-	list_for_each_entry_safe(rdata, next, &disc->rports, peers)
+	list_for_each_entry_rcu(rdata, &disc->rports, peers)
 		lport->tt.rport_logoff(rdata);
 	mutex_unlock(&disc->disc_mutex);
 }
@@ -292,7 +292,7 @@ static void fc_disc_done(struct fc_disc *disc, enum fc_disc_event event)
 	 * Skip ports which were never discovered.  These are the dNS port
 	 * and ports which were created by PLOGI.
 	 */
-	list_for_each_entry(rdata, &disc->rports, peers) {
+	list_for_each_entry_rcu(rdata, &disc->rports, peers) {
 		if (!rdata->disc_id)
 			continue;
 		if (rdata->disc_id == disc->disc_id)
diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c
index 363cde3..6b56973 100644
--- a/drivers/scsi/libfc/fc_rport.c
+++ b/drivers/scsi/libfc/fc_rport.c
@@ -95,13 +95,15 @@ static const char *fc_rport_state_names[] = {
  * fc_rport_lookup() - Lookup a remote port by port_id
  * @lport:   The local port to lookup the remote port on
  * @port_id: The remote port ID to look up
+ *
+ * The caller must hold either disc_mutex or rcu_read_lock().
  */
 static struct fc_rport_priv *fc_rport_lookup(const struct fc_lport *lport,
 					     u32 port_id)
 {
 	struct fc_rport_priv *rdata;
 
-	list_for_each_entry(rdata, &lport->disc.rports, peers)
+	list_for_each_entry_rcu(rdata, &lport->disc.rports, peers)
 		if (rdata->ids.port_id == port_id)
 			return rdata;
 	return NULL;
@@ -146,11 +148,23 @@ static struct fc_rport_priv *fc_rport_create(struct fc_lport *lport,
 	INIT_DELAYED_WORK(&rdata->retry_work, fc_rport_timeout);
 	INIT_WORK(&rdata->event_work, fc_rport_work);
 	if (port_id != FC_FID_DIR_SERV)
-		list_add(&rdata->peers, &lport->disc.rports);
+		list_add_rcu(&rdata->peers, &lport->disc.rports);
 	return rdata;
 }
 
 /**
+ * fc_rport_free_rcu() - Free a remote port
+ * @rcu: The rcu_head structure inside the remote port
+ */
+static void fc_rport_free_rcu(struct rcu_head *rcu)
+{
+	struct fc_rport_priv *rdata;
+
+	rdata = container_of(rcu, struct fc_rport_priv, rcu);
+	kfree(rdata);
+}
+
+/**
  * fc_rport_destroy() - Free a remote port after last reference is released
  * @kref: The remote port's kref
  */
@@ -159,7 +173,7 @@ static void fc_rport_destroy(struct kref *kref)
 	struct fc_rport_priv *rdata;
 
 	rdata = container_of(kref, struct fc_rport_priv, kref);
-	kfree(rdata);
+	call_rcu(&rdata->rcu, fc_rport_free_rcu);
 }
 
 /**
@@ -334,7 +348,7 @@ static void fc_rport_work(struct work_struct *work)
 				mutex_unlock(&rdata->rp_mutex);
 			} else {
 				FC_RPORT_DBG(rdata, "work delete\n");
-				list_del(&rdata->peers);
+				list_del_rcu(&rdata->peers);
 				mutex_unlock(&rdata->rp_mutex);
 				kref_put(&rdata->kref, lport->tt.rport_destroy);
 			}
diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h
index 6d78df7..b0310b9 100644
--- a/include/scsi/libfc.h
+++ b/include/scsi/libfc.h
@@ -195,6 +195,7 @@ struct fc_rport_libfc_priv {
  * @rp_mutex:       The mutex that protects the remote port
  * @retry_work:     Handle for retries
  * @event_callback: Callback when READY, FAILED or LOGO states complete
+ * @rcu:	    Structure used for freeing in an RCU-safe manner
  */
 struct fc_rport_priv {
 	struct fc_lport		    *local_port;
@@ -217,6 +218,7 @@ struct fc_rport_priv {
 	struct list_head            peers;
 	struct work_struct          event_work;
 	u32			    supported_classes;
+	struct rcu_head		    rcu;
 };
 
 /**


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 07/28] libfc: provide space for LLD after remote port structure
  2010-07-20 22:19 [PATCH 00/28] libfc, libfcoe and fcoe updates for scsi-misc Robert Love
                   ` (5 preceding siblings ...)
  2010-07-20 22:19 ` [PATCH 06/28] libfc: convert rport lookup to be RCU safe Robert Love
@ 2010-07-20 22:19 ` Robert Love
  2010-07-20 22:19 ` [PATCH 08/28] libfcoe: convert FIP to lock with mutex instead of spin lock Robert Love
                   ` (20 subsequent siblings)
  27 siblings, 0 replies; 34+ messages in thread
From: Robert Love @ 2010-07-20 22:19 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi; +Cc: Joe Eykholt

From: Joe Eykholt <jeykholt@cisco.com>

Add pre-zeroed space after the allocation for fc_rport_priv
for use by the lower-level driver.

This is primarily for VN2VN FIP mode, but could be used in
other ways someday.

The space required is specified in lport->rport_priv_size.

Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---
 drivers/scsi/libfc/fc_rport.c |    2 +-
 include/scsi/libfc.h          |    2 ++
 2 files changed, 3 insertions(+), 1 deletions(-)

diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c
index 6b56973..6d68482 100644
--- a/drivers/scsi/libfc/fc_rport.c
+++ b/drivers/scsi/libfc/fc_rport.c
@@ -127,7 +127,7 @@ static struct fc_rport_priv *fc_rport_create(struct fc_lport *lport,
 	if (rdata)
 		return rdata;
 
-	rdata = kzalloc(sizeof(*rdata), GFP_KERNEL);
+	rdata = kzalloc(sizeof(*rdata) + lport->rport_priv_size, GFP_KERNEL);
 	if (!rdata)
 		return NULL;
 
diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h
index b0310b9..fcbee8c 100644
--- a/include/scsi/libfc.h
+++ b/include/scsi/libfc.h
@@ -799,6 +799,7 @@ struct fc_disc {
  * @mfs:                   The maximum Fibre Channel payload size
  * @max_retry_count:       The maximum retry attempts
  * @max_rport_retry_count: The maximum remote port retry attempts
+ * @rport_priv_size:       Size needed by driver after struct fc_rport_priv
  * @lro_xid:               The maximum XID for LRO
  * @lso_max:               The maximum large offload send size
  * @fcts:                  FC-4 type mask
@@ -848,6 +849,7 @@ struct fc_lport {
 	u32			       mfs;
 	u8			       max_retry_count;
 	u8			       max_rport_retry_count;
+	u16			       rport_priv_size;
 	u16			       link_speed;
 	u16			       link_supported_speeds;
 	u16			       lro_xid;


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 08/28] libfcoe: convert FIP to lock with mutex instead of spin lock
  2010-07-20 22:19 [PATCH 00/28] libfc, libfcoe and fcoe updates for scsi-misc Robert Love
                   ` (6 preceding siblings ...)
  2010-07-20 22:19 ` [PATCH 07/28] libfc: provide space for LLD after remote port structure Robert Love
@ 2010-07-20 22:19 ` Robert Love
  2010-07-20 22:19 ` [PATCH 09/28] libfc: add discovery-private pointer for LLD Robert Love
                   ` (19 subsequent siblings)
  27 siblings, 0 replies; 34+ messages in thread
From: Robert Love @ 2010-07-20 22:19 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi; +Cc: Joe Eykholt

From: Joe Eykholt <jeykholt@cisco.com>

It turns out most of the FIP work is now done from worker threads
or process context now, so there's no need to use a spin lock.

Change to use mutex instead of spin lock and delayed_work instead
of a timer.

This will make it nicer for the VN_port to VN_port feature that
will interact more with the libfc layers requiring that
spinlocks not be held.

Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---
 drivers/scsi/fcoe/libfcoe.c |  116 ++++++++++++++++++++-----------------------
 include/scsi/libfcoe.h      |    9 +--
 2 files changed, 57 insertions(+), 68 deletions(-)

diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c
index f009191..e510888 100644
--- a/drivers/scsi/fcoe/libfcoe.c
+++ b/drivers/scsi/fcoe/libfcoe.c
@@ -113,7 +113,7 @@ void fcoe_ctlr_init(struct fcoe_ctlr *fip)
 	fip->state = FIP_ST_LINK_WAIT;
 	fip->mode = FIP_ST_AUTO;
 	INIT_LIST_HEAD(&fip->fcfs);
-	spin_lock_init(&fip->lock);
+	mutex_init(&fip->ctlr_mutex);
 	fip->flogi_oxid = FC_XID_UNKNOWN;
 	setup_timer(&fip->timer, fcoe_ctlr_timeout, (unsigned long)fip);
 	INIT_WORK(&fip->timer_work, fcoe_ctlr_timer_work);
@@ -159,10 +159,10 @@ void fcoe_ctlr_destroy(struct fcoe_ctlr *fip)
 	cancel_work_sync(&fip->recv_work);
 	skb_queue_purge(&fip->fip_recv_list);
 
-	spin_lock_bh(&fip->lock);
+	mutex_lock(&fip->ctlr_mutex);
 	fip->state = FIP_ST_DISABLED;
 	fcoe_ctlr_reset_fcfs(fip);
-	spin_unlock_bh(&fip->lock);
+	mutex_unlock(&fip->ctlr_mutex);
 	del_timer_sync(&fip->timer);
 	cancel_work_sync(&fip->timer_work);
 }
@@ -255,19 +255,19 @@ static void fcoe_ctlr_solicit(struct fcoe_ctlr *fip, struct fcoe_fcf *fcf)
  */
 void fcoe_ctlr_link_up(struct fcoe_ctlr *fip)
 {
-	spin_lock_bh(&fip->lock);
+	mutex_lock(&fip->ctlr_mutex);
 	if (fip->state == FIP_ST_NON_FIP || fip->state == FIP_ST_AUTO) {
-		spin_unlock_bh(&fip->lock);
+		mutex_unlock(&fip->ctlr_mutex);
 		fc_linkup(fip->lp);
 	} else if (fip->state == FIP_ST_LINK_WAIT) {
 		fip->state = fip->mode;
-		spin_unlock_bh(&fip->lock);
+		mutex_unlock(&fip->ctlr_mutex);
 		if (fip->state == FIP_ST_AUTO)
 			LIBFCOE_FIP_DBG(fip, "%s", "setting AUTO mode.\n");
 		fc_linkup(fip->lp);
 		fcoe_ctlr_solicit(fip, NULL);
 	} else
-		spin_unlock_bh(&fip->lock);
+		mutex_unlock(&fip->ctlr_mutex);
 }
 EXPORT_SYMBOL(fcoe_ctlr_link_up);
 
@@ -300,11 +300,11 @@ int fcoe_ctlr_link_down(struct fcoe_ctlr *fip)
 	int link_dropped;
 
 	LIBFCOE_FIP_DBG(fip, "link down.\n");
-	spin_lock_bh(&fip->lock);
+	mutex_lock(&fip->ctlr_mutex);
 	fcoe_ctlr_reset(fip);
 	link_dropped = fip->state != FIP_ST_LINK_WAIT;
 	fip->state = FIP_ST_LINK_WAIT;
-	spin_unlock_bh(&fip->lock);
+	mutex_unlock(&fip->ctlr_mutex);
 
 	if (link_dropped)
 		fc_linkdown(fip->lp);
@@ -577,12 +577,12 @@ static unsigned long fcoe_ctlr_age_fcfs(struct fcoe_ctlr *fip)
 	unsigned long sel_time = 0;
 	struct fcoe_dev_stats *stats;
 
+	stats = per_cpu_ptr(fip->lp->dev_stats, get_cpu());
+
 	list_for_each_entry_safe(fcf, next, &fip->fcfs, list) {
 		deadline = fcf->time + fcf->fka_period + fcf->fka_period / 2;
 		if (fip->sel_fcf == fcf) {
 			if (time_after(jiffies, deadline)) {
-				stats = per_cpu_ptr(fip->lp->dev_stats,
-						    smp_processor_id());
 				stats->MissDiscAdvCount++;
 				printk(KERN_INFO "libfcoe: host%d: "
 				       "Missing Discovery Advertisement "
@@ -601,8 +601,6 @@ static unsigned long fcoe_ctlr_age_fcfs(struct fcoe_ctlr *fip)
 			WARN_ON(!fip->fcf_count);
 			fip->fcf_count--;
 			kfree(fcf);
-			stats = per_cpu_ptr(fip->lp->dev_stats,
-					    smp_processor_id());
 			stats->VLinkFailureCount++;
 		} else {
 			if (time_after(next_timer, deadline))
@@ -612,6 +610,7 @@ static unsigned long fcoe_ctlr_age_fcfs(struct fcoe_ctlr *fip)
 				sel_time = fcf->time;
 		}
 	}
+	put_cpu();
 	if (sel_time && !fip->sel_fcf && !fip->sel_time) {
 		sel_time += msecs_to_jiffies(FCOE_CTLR_START_DELAY);
 		fip->sel_time = sel_time;
@@ -768,7 +767,7 @@ static void fcoe_ctlr_recv_adv(struct fcoe_ctlr *fip, struct sk_buff *skb)
 	if (fcoe_ctlr_parse_adv(fip, skb, &new))
 		return;
 
-	spin_lock_bh(&fip->lock);
+	mutex_lock(&fip->ctlr_mutex);
 	first = list_empty(&fip->fcfs);
 	found = NULL;
 	list_for_each_entry(fcf, &fip->fcfs, list) {
@@ -847,7 +846,7 @@ static void fcoe_ctlr_recv_adv(struct fcoe_ctlr *fip, struct sk_buff *skb)
 			mod_timer(&fip->timer, fip->sel_time);
 	}
 out:
-	spin_unlock_bh(&fip->lock);
+	mutex_unlock(&fip->ctlr_mutex);
 }
 
 /**
@@ -1108,11 +1107,12 @@ static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip,
 		if (is_vn_port)
 			fc_lport_reset(vn_port);
 		else {
-			spin_lock_bh(&fip->lock);
+			mutex_lock(&fip->ctlr_mutex);
 			per_cpu_ptr(lport->dev_stats,
-				    smp_processor_id())->VLinkFailureCount++;
+				    get_cpu())->VLinkFailureCount++;
+			put_cpu();
 			fcoe_ctlr_reset(fip);
-			spin_unlock_bh(&fip->lock);
+			mutex_unlock(&fip->ctlr_mutex);
 
 			fc_lport_reset(fip->lp);
 			fcoe_ctlr_solicit(fip, NULL);
@@ -1166,7 +1166,7 @@ static int fcoe_ctlr_recv_handler(struct fcoe_ctlr *fip, struct sk_buff *skb)
 	if (ntohs(fiph->fip_dl_len) * FIP_BPW + sizeof(*fiph) > skb->len)
 		goto drop;
 
-	spin_lock_bh(&fip->lock);
+	mutex_lock(&fip->ctlr_mutex);
 	state = fip->state;
 	if (state == FIP_ST_AUTO) {
 		fip->map_dest = 0;
@@ -1174,7 +1174,7 @@ static int fcoe_ctlr_recv_handler(struct fcoe_ctlr *fip, struct sk_buff *skb)
 		state = FIP_ST_ENABLED;
 		LIBFCOE_FIP_DBG(fip, "Using FIP mode\n");
 	}
-	spin_unlock_bh(&fip->lock);
+	mutex_unlock(&fip->ctlr_mutex);
 	if (state != FIP_ST_ENABLED)
 		goto drop;
 
@@ -1240,19 +1240,38 @@ static void fcoe_ctlr_select(struct fcoe_ctlr *fip)
 /**
  * fcoe_ctlr_timeout() - FIP timeout handler
  * @arg: The FCoE controller that timed out
- *
- * Ages FCFs.  Triggers FCF selection if possible.  Sends keep-alives.
  */
 static void fcoe_ctlr_timeout(unsigned long arg)
 {
 	struct fcoe_ctlr *fip = (struct fcoe_ctlr *)arg;
+
+	schedule_work(&fip->timer_work);
+}
+
+/**
+ * fcoe_ctlr_timer_work() - Worker thread function for timer work
+ * @work: Handle to a FCoE controller
+ *
+ * Ages FCFs.  Triggers FCF selection if possible.
+ * Sends keep-alives and resets.
+ */
+static void fcoe_ctlr_timer_work(struct work_struct *work)
+{
+	struct fcoe_ctlr *fip;
+	struct fc_lport *vport;
+	u8 *mac;
+	u8 reset = 0;
+	u8 send_ctlr_ka = 0;
+	u8 send_port_ka = 0;
 	struct fcoe_fcf *sel;
 	struct fcoe_fcf *fcf;
 	unsigned long next_timer;
 
-	spin_lock_bh(&fip->lock);
+	fip = container_of(work, struct fcoe_ctlr, timer_work);
+
+	mutex_lock(&fip->ctlr_mutex);
 	if (fip->state == FIP_ST_DISABLED) {
-		spin_unlock_bh(&fip->lock);
+		mutex_unlock(&fip->ctlr_mutex);
 		return;
 	}
 
@@ -1286,7 +1305,7 @@ static void fcoe_ctlr_timeout(unsigned long arg)
 			       "FIP Fibre-Channel Forwarder timed out.	"
 			       "Starting FCF discovery.\n",
 			       fip->lp->host->host_no);
-			fip->reset_req = 1;
+			reset = 1;
 			schedule_work(&fip->timer_work);
 		}
 	}
@@ -1294,7 +1313,7 @@ static void fcoe_ctlr_timeout(unsigned long arg)
 	if (sel && !sel->fd_flags) {
 		if (time_after_eq(jiffies, fip->ctlr_ka_time)) {
 			fip->ctlr_ka_time = jiffies + sel->fka_period;
-			fip->send_ctlr_ka = 1;
+			send_ctlr_ka = 1;
 		}
 		if (time_after(next_timer, fip->ctlr_ka_time))
 			next_timer = fip->ctlr_ka_time;
@@ -1302,37 +1321,14 @@ static void fcoe_ctlr_timeout(unsigned long arg)
 		if (time_after_eq(jiffies, fip->port_ka_time)) {
 			fip->port_ka_time = jiffies +
 				msecs_to_jiffies(FIP_VN_KA_PERIOD);
-			fip->send_port_ka = 1;
+			send_port_ka = 1;
 		}
 		if (time_after(next_timer, fip->port_ka_time))
 			next_timer = fip->port_ka_time;
 	}
 	if (!list_empty(&fip->fcfs))
 		mod_timer(&fip->timer, next_timer);
-	if (fip->send_ctlr_ka || fip->send_port_ka)
-		schedule_work(&fip->timer_work);
-	spin_unlock_bh(&fip->lock);
-}
-
-/**
- * fcoe_ctlr_timer_work() - Worker thread function for timer work
- * @work: Handle to a FCoE controller
- *
- * Sends keep-alives and resets which must not
- * be called from the timer directly, since they use a mutex.
- */
-static void fcoe_ctlr_timer_work(struct work_struct *work)
-{
-	struct fcoe_ctlr *fip;
-	struct fc_lport *vport;
-	u8 *mac;
-	int reset;
-
-	fip = container_of(work, struct fcoe_ctlr, timer_work);
-	spin_lock_bh(&fip->lock);
-	reset = fip->reset_req;
-	fip->reset_req = 0;
-	spin_unlock_bh(&fip->lock);
+	mutex_unlock(&fip->ctlr_mutex);
 
 	if (reset) {
 		fc_lport_reset(fip->lp);
@@ -1340,12 +1336,10 @@ static void fcoe_ctlr_timer_work(struct work_struct *work)
 		fcoe_ctlr_solicit(fip, NULL);
 	}
 
-	if (fip->send_ctlr_ka) {
-		fip->send_ctlr_ka = 0;
+	if (send_ctlr_ka)
 		fcoe_ctlr_send_keep_alive(fip, NULL, 0, fip->ctl_src_addr);
-	}
-	if (fip->send_port_ka) {
-		fip->send_port_ka = 0;
+
+	if (send_port_ka) {
 		mutex_lock(&fip->lp->lp_mutex);
 		mac = fip->get_src_addr(fip->lp);
 		fcoe_ctlr_send_keep_alive(fip, fip->lp, 1, mac);
@@ -1402,9 +1396,9 @@ int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *fip, struct fc_lport *lport,
 	if (op == ELS_LS_ACC && fh->fh_r_ctl == FC_RCTL_ELS_REP &&
 	    fip->flogi_oxid == ntohs(fh->fh_ox_id)) {
 
-		spin_lock_bh(&fip->lock);
+		mutex_lock(&fip->ctlr_mutex);
 		if (fip->state != FIP_ST_AUTO && fip->state != FIP_ST_NON_FIP) {
-			spin_unlock_bh(&fip->lock);
+			mutex_unlock(&fip->ctlr_mutex);
 			return -EINVAL;
 		}
 		fip->state = FIP_ST_NON_FIP;
@@ -1424,13 +1418,13 @@ int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *fip, struct fc_lport *lport,
 			fip->map_dest = 0;
 		}
 		fip->flogi_oxid = FC_XID_UNKNOWN;
-		spin_unlock_bh(&fip->lock);
+		mutex_unlock(&fip->ctlr_mutex);
 		fc_fcoe_set_mac(fr_cb(fp)->granted_mac, fh->fh_d_id);
 	} else if (op == ELS_FLOGI && fh->fh_r_ctl == FC_RCTL_ELS_REQ && sa) {
 		/*
 		 * Save source MAC for point-to-point responses.
 		 */
-		spin_lock_bh(&fip->lock);
+		mutex_lock(&fip->ctlr_mutex);
 		if (fip->state == FIP_ST_AUTO || fip->state == FIP_ST_NON_FIP) {
 			memcpy(fip->dest_addr, sa, ETH_ALEN);
 			fip->map_dest = 0;
@@ -1439,7 +1433,7 @@ int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *fip, struct fc_lport *lport,
 						"Setting non-FIP mode\n");
 			fip->state = FIP_ST_NON_FIP;
 		}
-		spin_unlock_bh(&fip->lock);
+		mutex_unlock(&fip->ctlr_mutex);
 	}
 	return 0;
 }
diff --git a/include/scsi/libfcoe.h b/include/scsi/libfcoe.h
index 81aee1c..7d18b50 100644
--- a/include/scsi/libfcoe.h
+++ b/include/scsi/libfcoe.h
@@ -75,14 +75,12 @@ enum fip_state {
  * @flogi_count:   number of FLOGI attempts in AUTO mode.
  * @map_dest:	   use the FC_MAP mode for destination MAC addresses.
  * @spma:	   supports SPMA server-provided MACs mode
- * @send_ctlr_ka:  need to send controller keep alive
- * @send_port_ka:  need to send port keep alives
  * @dest_addr:	   MAC address of the selected FC forwarder.
  * @ctl_src_addr:  the native MAC address of our local port.
  * @send:	   LLD-supplied function to handle sending FIP Ethernet frames
  * @update_mac:    LLD-supplied function to handle changes to MAC addresses.
  * @get_src_addr:  LLD-supplied function to supply a source MAC address.
- * @lock:	   lock protecting this structure.
+ * @ctlr_mutex:	   lock protecting this structure.
  *
  * This structure is used by all FCoE drivers.  It contains information
  * needed by all FCoE low-level drivers (LLDs) as well as internal state
@@ -106,18 +104,15 @@ struct fcoe_ctlr {
 	u16 user_mfs;
 	u16 flogi_oxid;
 	u8 flogi_count;
-	u8 reset_req;
 	u8 map_dest;
 	u8 spma;
-	u8 send_ctlr_ka;
-	u8 send_port_ka;
 	u8 dest_addr[ETH_ALEN];
 	u8 ctl_src_addr[ETH_ALEN];
 
 	void (*send)(struct fcoe_ctlr *, struct sk_buff *);
 	void (*update_mac)(struct fc_lport *, u8 *addr);
 	u8 * (*get_src_addr)(struct fc_lport *);
-	spinlock_t lock;
+	struct mutex ctlr_mutex;
 };
 
 /**


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 09/28] libfc: add discovery-private pointer for LLD
  2010-07-20 22:19 [PATCH 00/28] libfc, libfcoe and fcoe updates for scsi-misc Robert Love
                   ` (7 preceding siblings ...)
  2010-07-20 22:19 ` [PATCH 08/28] libfcoe: convert FIP to lock with mutex instead of spin lock Robert Love
@ 2010-07-20 22:19 ` Robert Love
  2010-07-20 22:19 ` [PATCH 10/28] libfcoe: fcoe: fnic: change fcoe_ctlr_init interface to specify mode Robert Love
                   ` (18 subsequent siblings)
  27 siblings, 0 replies; 34+ messages in thread
From: Robert Love @ 2010-07-20 22:19 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi; +Cc: Joe Eykholt

From: Joe Eykholt <jeykholt@cisco.com>

For VN_port to VN_port mode, FIP will do discovery and needs a
way to find its state from the local port or discovery structure.
It seems that any other LLD that implements its own discovery
would also need something like this.

Replace disc->lport with disc->priv, and use container_of to
find the lport.  We could use disc->priv for that, but
container_of is smaller and faster.

Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---
 drivers/scsi/libfc/fc_disc.c  |   14 +++++++-------
 drivers/scsi/libfc/fc_libfc.h |    2 +-
 include/scsi/libfc.h          |    9 +++++++--
 3 files changed, 15 insertions(+), 10 deletions(-)

diff --git a/drivers/scsi/libfc/fc_disc.c b/drivers/scsi/libfc/fc_disc.c
index d0fa9a0..0447455 100644
--- a/drivers/scsi/libfc/fc_disc.c
+++ b/drivers/scsi/libfc/fc_disc.c
@@ -65,7 +65,7 @@ void fc_disc_stop_rports(struct fc_disc *disc)
 	struct fc_lport *lport;
 	struct fc_rport_priv *rdata;
 
-	lport = disc->lport;
+	lport = fc_disc_lport(disc);
 
 	mutex_lock(&disc->disc_mutex);
 	list_for_each_entry_rcu(rdata, &disc->rports, peers)
@@ -96,7 +96,7 @@ static void fc_disc_recv_rscn_req(struct fc_seq *sp, struct fc_frame *fp,
 	LIST_HEAD(disc_ports);
 	struct fc_disc_port *dp, *next;
 
-	lport = disc->lport;
+	lport = fc_disc_lport(disc);
 
 	FC_DISC_DBG(disc, "Received an RSCN event\n");
 
@@ -275,7 +275,7 @@ static void fc_disc_start(void (*disc_callback)(struct fc_lport *,
  */
 static void fc_disc_done(struct fc_disc *disc, enum fc_disc_event event)
 {
-	struct fc_lport *lport = disc->lport;
+	struct fc_lport *lport = fc_disc_lport(disc);
 	struct fc_rport_priv *rdata;
 
 	FC_DISC_DBG(disc, "Discovery complete\n");
@@ -313,7 +313,7 @@ static void fc_disc_done(struct fc_disc *disc, enum fc_disc_event event)
  */
 static void fc_disc_error(struct fc_disc *disc, struct fc_frame *fp)
 {
-	struct fc_lport *lport = disc->lport;
+	struct fc_lport *lport = fc_disc_lport(disc);
 	unsigned long delay = 0;
 
 	FC_DISC_DBG(disc, "Error %ld, retries %d/%d\n",
@@ -353,7 +353,7 @@ static void fc_disc_error(struct fc_disc *disc, struct fc_frame *fp)
 static void fc_disc_gpn_ft_req(struct fc_disc *disc)
 {
 	struct fc_frame *fp;
-	struct fc_lport *lport = disc->lport;
+	struct fc_lport *lport = fc_disc_lport(disc);
 
 	WARN_ON(!fc_lport_test_ready(lport));
 
@@ -396,7 +396,7 @@ static int fc_disc_gpn_ft_parse(struct fc_disc *disc, void *buf, size_t len)
 	struct fc_rport_identifiers ids;
 	struct fc_rport_priv *rdata;
 
-	lport = disc->lport;
+	lport = fc_disc_lport(disc);
 	disc->seq_count++;
 
 	/*
@@ -733,7 +733,7 @@ int fc_disc_init(struct fc_lport *lport)
 	mutex_init(&disc->disc_mutex);
 	INIT_LIST_HEAD(&disc->rports);
 
-	disc->lport = lport;
+	disc->priv = lport;
 
 	return 0;
 }
diff --git a/drivers/scsi/libfc/fc_libfc.h b/drivers/scsi/libfc/fc_libfc.h
index f5c0ca4..16d2162 100644
--- a/drivers/scsi/libfc/fc_libfc.h
+++ b/drivers/scsi/libfc/fc_libfc.h
@@ -52,7 +52,7 @@ extern unsigned int fc_debug_logging;
 #define FC_DISC_DBG(disc, fmt, args...)				\
 	FC_CHECK_LOGGING(FC_DISC_LOGGING,			\
 			 printk(KERN_INFO "host%u: disc: " fmt,	\
-				(disc)->lport->host->host_no,	\
+				fc_disc_lport(disc)->host->host_no,	\
 				##args))
 
 #define FC_RPORT_ID_DBG(lport, port_id, fmt, args...)			\
diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h
index fcbee8c..5f64e59 100644
--- a/include/scsi/libfc.h
+++ b/include/scsi/libfc.h
@@ -739,7 +739,7 @@ struct libfc_function_template {
  * @buf_len:       Length of the discovery buffer
  * @disc_id:       Discovery ID
  * @rports:        List of discovered remote ports
- * @lport:         The local port that discovery is for
+ * @priv:          Private pointer for use by discovery code
  * @disc_mutex:    Mutex that protects the discovery context
  * @partial_buf:   Partial name buffer (if names are returned
  *                 in multiple frames)
@@ -755,7 +755,7 @@ struct fc_disc {
 	u16                   disc_id;
 
 	struct list_head      rports;
-	struct fc_lport	      *lport;
+	void		      *priv;
 	struct mutex	      disc_mutex;
 	struct fc_gpn_ft_resp partial_buf;
 	struct delayed_work   disc_work;
@@ -1003,6 +1003,11 @@ void fc_rport_terminate_io(struct fc_rport *);
  *****************************/
 int fc_disc_init(struct fc_lport *);
 
+static inline struct fc_lport *fc_disc_lport(struct fc_disc *disc)
+{
+	return container_of(disc, struct fc_lport, disc);
+}
+
 /*
  * FCP LAYER
  *****************************/


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 10/28] libfcoe: fcoe: fnic: change fcoe_ctlr_init interface to specify mode
  2010-07-20 22:19 [PATCH 00/28] libfc, libfcoe and fcoe updates for scsi-misc Robert Love
                   ` (8 preceding siblings ...)
  2010-07-20 22:19 ` [PATCH 09/28] libfc: add discovery-private pointer for LLD Robert Love
@ 2010-07-20 22:19 ` Robert Love
  2010-07-20 22:20 ` [PATCH 11/28] libfc: Add local port point-to-multipoint flag Robert Love
                   ` (17 subsequent siblings)
  27 siblings, 0 replies; 34+ messages in thread
From: Robert Love @ 2010-07-20 22:19 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi; +Cc: Joe Eykholt

From: Joe Eykholt <jeykholt@cisco.com>

There are three modes that libfcoe currently supports, and a new one
is coming.  Change the fcoe_ctlr_init() interface to add the mode
desired.  This should not change any functionality.

Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---
 drivers/scsi/fcoe/fcoe.c      |    2 +-
 drivers/scsi/fcoe/libfcoe.c   |    4 ++--
 drivers/scsi/fnic/fnic_main.c |    4 ++--
 include/scsi/libfcoe.h        |   11 ++++++++++-
 4 files changed, 15 insertions(+), 6 deletions(-)

diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c
index a120962..9d64e08 100644
--- a/drivers/scsi/fcoe/fcoe.c
+++ b/drivers/scsi/fcoe/fcoe.c
@@ -357,7 +357,7 @@ static struct fcoe_interface *fcoe_interface_create(struct net_device *netdev)
 	/*
 	 * Initialize FIP.
 	 */
-	fcoe_ctlr_init(&fcoe->ctlr);
+	fcoe_ctlr_init(&fcoe->ctlr, FIP_MODE_AUTO);
 	fcoe->ctlr.send = fcoe_fip_send;
 	fcoe->ctlr.update_mac = fcoe_update_src_mac;
 	fcoe->ctlr.get_src_addr = fcoe_get_src_mac;
diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c
index e510888..76056e4 100644
--- a/drivers/scsi/fcoe/libfcoe.c
+++ b/drivers/scsi/fcoe/libfcoe.c
@@ -108,10 +108,10 @@ static inline int fcoe_ctlr_fcf_usable(struct fcoe_fcf *fcf)
  * fcoe_ctlr_init() - Initialize the FCoE Controller instance
  * @fip: The FCoE controller to initialize
  */
-void fcoe_ctlr_init(struct fcoe_ctlr *fip)
+void fcoe_ctlr_init(struct fcoe_ctlr *fip, enum fip_state mode)
 {
 	fip->state = FIP_ST_LINK_WAIT;
-	fip->mode = FIP_ST_AUTO;
+	fip->mode = mode;
 	INIT_LIST_HEAD(&fip->fcfs);
 	mutex_init(&fip->ctlr_mutex);
 	fip->flogi_oxid = FC_XID_UNKNOWN;
diff --git a/drivers/scsi/fnic/fnic_main.c b/drivers/scsi/fnic/fnic_main.c
index 265e73d..d0fe1c3 100644
--- a/drivers/scsi/fnic/fnic_main.c
+++ b/drivers/scsi/fnic/fnic_main.c
@@ -617,7 +617,6 @@ static int __devinit fnic_probe(struct pci_dev *pdev,
 	fnic->ctlr.send = fnic_eth_send;
 	fnic->ctlr.update_mac = fnic_update_mac;
 	fnic->ctlr.get_src_addr = fnic_get_mac;
-	fcoe_ctlr_init(&fnic->ctlr);
 	if (fnic->config.flags & VFCF_FIP_CAPABLE) {
 		shost_printk(KERN_INFO, fnic->lport->host,
 			     "firmware supports FIP\n");
@@ -625,10 +624,11 @@ static int __devinit fnic_probe(struct pci_dev *pdev,
 		vnic_dev_packet_filter(fnic->vdev, 1, 1, 0, 0, 0);
 		vnic_dev_add_addr(fnic->vdev, FIP_ALL_ENODE_MACS);
 		vnic_dev_add_addr(fnic->vdev, fnic->ctlr.ctl_src_addr);
+		fcoe_ctlr_init(&fnic->ctlr, FIP_MODE_AUTO);
 	} else {
 		shost_printk(KERN_INFO, fnic->lport->host,
 			     "firmware uses non-FIP mode\n");
-		fnic->ctlr.mode = FIP_ST_NON_FIP;
+		fcoe_ctlr_init(&fnic->ctlr, FIP_MODE_NON_FIP);
 	}
 	fnic->state = FNIC_IN_FC_MODE;
 
diff --git a/include/scsi/libfcoe.h b/include/scsi/libfcoe.h
index 7d18b50..1a84a31 100644
--- a/include/scsi/libfcoe.h
+++ b/include/scsi/libfcoe.h
@@ -54,6 +54,15 @@ enum fip_state {
 	FIP_ST_ENABLED,
 };
 
+/*
+ * Modes:
+ * The mode is the state that is to be entered after link up.
+ * It must not change after fcoe_ctlr_init() sets it.
+ */
+#define FIP_MODE_AUTO		FIP_ST_AUTO
+#define FIP_MODE_NON_FIP	FIP_ST_NON_FIP
+#define FIP_MODE_FABRIC		FIP_ST_ENABLED
+
 /**
  * struct fcoe_ctlr - FCoE Controller and FIP state
  * @state:	   internal FIP state for network link and FIP or non-FIP mode.
@@ -152,7 +161,7 @@ struct fcoe_fcf {
 };
 
 /* FIP API functions */
-void fcoe_ctlr_init(struct fcoe_ctlr *);
+void fcoe_ctlr_init(struct fcoe_ctlr *, enum fip_state);
 void fcoe_ctlr_destroy(struct fcoe_ctlr *);
 void fcoe_ctlr_link_up(struct fcoe_ctlr *);
 int fcoe_ctlr_link_down(struct fcoe_ctlr *);


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 11/28] libfc: Add local port point-to-multipoint flag
  2010-07-20 22:19 [PATCH 00/28] libfc, libfcoe and fcoe updates for scsi-misc Robert Love
                   ` (9 preceding siblings ...)
  2010-07-20 22:19 ` [PATCH 10/28] libfcoe: fcoe: fnic: change fcoe_ctlr_init interface to specify mode Robert Love
@ 2010-07-20 22:20 ` Robert Love
  2010-07-20 22:20 ` [PATCH 12/28] libfc: add FLOGI state to rport for VN2VN Robert Love
                   ` (16 subsequent siblings)
  27 siblings, 0 replies; 34+ messages in thread
From: Robert Love @ 2010-07-20 22:20 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi; +Cc: Joe Eykholt

From: Joe Eykholt <jeykholt@cisco.com>

For VN_port to VN_port mode, the transport sets the port_id and
there's no lport FLOGI.  This is similar to FC loop mode.

Add a point_to_multipoint flag that indicates the local port is in
point-to-multipoint mode.  This skips FLOGI and discovery.
It also skips resetting the port_id on resets other than link down.

Add function fc_lport_set_local_id() that sets the local port_id.
This is called by libfcoe on behalf of the low-level driver
to set the port_id when the link comes up.

Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---
 drivers/scsi/libfc/fc_lport.c |   36 +++++++++++++++++++++++++++++++++++-
 include/scsi/libfc.h          |    2 ++
 2 files changed, 37 insertions(+), 1 deletions(-)

diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c
index 79c9e3c..f7bff2c 100644
--- a/drivers/scsi/libfc/fc_lport.c
+++ b/drivers/scsi/libfc/fc_lport.c
@@ -755,6 +755,34 @@ static void fc_lport_set_port_id(struct fc_lport *lport, u32 port_id,
 }
 
 /**
+ * fc_lport_set_port_id() - set the local port Port ID for point-to-multipoint
+ * @lport: The local port which will have its Port ID set.
+ * @port_id: The new port ID.
+ *
+ * Called by the lower-level driver when transport sets the local port_id.
+ * This is used in VN_port to VN_port mode for FCoE, and causes FLOGI and
+ * discovery to be skipped.
+ */
+void fc_lport_set_local_id(struct fc_lport *lport, u32 port_id)
+{
+	mutex_lock(&lport->lp_mutex);
+
+	fc_lport_set_port_id(lport, port_id, NULL);
+
+	switch (lport->state) {
+	case LPORT_ST_RESET:
+	case LPORT_ST_FLOGI:
+		if (port_id)
+			fc_lport_enter_ready(lport);
+		break;
+	default:
+		break;
+	}
+	mutex_unlock(&lport->lp_mutex);
+}
+EXPORT_SYMBOL(fc_lport_set_local_id);
+
+/**
  * fc_lport_recv_flogi_req() - Receive a FLOGI request
  * @sp_in: The sequence the FLOGI is on
  * @rx_fp: The FLOGI frame
@@ -954,7 +982,7 @@ static void fc_lport_reset_locked(struct fc_lport *lport)
 	lport->tt.exch_mgr_reset(lport, 0, 0);
 	fc_host_fabric_name(lport->host) = 0;
 
-	if (lport->port_id)
+	if (lport->port_id && (!lport->point_to_multipoint || !lport->link_up))
 		fc_lport_set_port_id(lport, 0, NULL);
 }
 
@@ -1536,6 +1564,12 @@ void fc_lport_enter_flogi(struct fc_lport *lport)
 
 	fc_lport_state_enter(lport, LPORT_ST_FLOGI);
 
+	if (lport->point_to_multipoint) {
+		if (lport->port_id)
+			fc_lport_enter_ready(lport);
+		return;
+	}
+
 	fp = fc_frame_alloc(lport, sizeof(struct fc_els_flogi));
 	if (!fp)
 		return fc_lport_error(lport, fp);
diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h
index 5f64e59..bd05605 100644
--- a/include/scsi/libfc.h
+++ b/include/scsi/libfc.h
@@ -846,6 +846,7 @@ struct fc_lport {
 	u32			       lro_enabled:1;
 	u32			       does_npiv:1;
 	u32			       npiv_enabled:1;
+	u32			       point_to_multipoint:1;
 	u32			       mfs;
 	u8			       max_retry_count;
 	u8			       max_rport_retry_count;
@@ -991,6 +992,7 @@ int fc_set_mfs(struct fc_lport *, u32 mfs);
 struct fc_lport *libfc_vport_create(struct fc_vport *, int privsize);
 struct fc_lport *fc_vport_id_lookup(struct fc_lport *, u32 port_id);
 int fc_lport_bsg_request(struct fc_bsg_job *);
+void fc_lport_set_local_id(struct fc_lport *, u32 port_id);
 
 /*
  * REMOTE PORT LAYER


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 12/28] libfc: add FLOGI state to rport for VN2VN
  2010-07-20 22:19 [PATCH 00/28] libfc, libfcoe and fcoe updates for scsi-misc Robert Love
                   ` (10 preceding siblings ...)
  2010-07-20 22:20 ` [PATCH 11/28] libfc: Add local port point-to-multipoint flag Robert Love
@ 2010-07-20 22:20 ` Robert Love
  2010-07-20 22:20 ` [PATCH 13/28] libfc: track FIP exchanges Robert Love
                   ` (15 subsequent siblings)
  27 siblings, 0 replies; 34+ messages in thread
From: Robert Love @ 2010-07-20 22:20 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi; +Cc: Joe Eykholt

From: Joe Eykholt <jeykholt@cisco.com>

The FIP proposal for VN_port to VN_port point-to-multipoint
operation requires a FLOGI be sent to each remote port.
The FLOGI is sent with the assigned S_ID and D_IDs of the
local and remote ports.  This and the response get
FIP-encapsulated for Ethernet.

Add FLOGI state to the remote port state machine.
This will be skipped if not in point-to-multipoint mode.

To reduce a little duplication between PLOGI and FLOGI
response handling, added fc_rport_login_complete(), which
handles the parameters for the rdata struct.

Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---
 drivers/scsi/libfc/fc_lport.c |    4 -
 drivers/scsi/libfc/fc_rport.c |  290 +++++++++++++++++++++++++++++++++++++++--
 include/scsi/fc/fc_els.h      |    2 
 include/scsi/libfc.h          |    4 +
 4 files changed, 286 insertions(+), 14 deletions(-)

diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c
index f7bff2c..ec9850c 100644
--- a/drivers/scsi/libfc/fc_lport.c
+++ b/drivers/scsi/libfc/fc_lport.c
@@ -906,10 +906,10 @@ static void fc_lport_recv_req(struct fc_lport *lport, struct fc_seq *sp,
 		recv = lport->tt.rport_recv_req;
 		switch (fc_frame_payload_op(fp)) {
 		case ELS_FLOGI:
-			recv = fc_lport_recv_flogi_req;
+			if (!lport->point_to_multipoint)
+				recv = fc_lport_recv_flogi_req;
 			break;
 		case ELS_LOGO:
-			fh = fc_frame_header_get(fp);
 			if (ntoh24(fh->fh_s_id) == FC_FID_FLOGI)
 				recv = fc_lport_recv_logo_req;
 			break;
diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c
index 6d68482..4d6adf2 100644
--- a/drivers/scsi/libfc/fc_rport.c
+++ b/drivers/scsi/libfc/fc_rport.c
@@ -60,6 +60,7 @@
 
 struct workqueue_struct *rport_event_queue;
 
+static void fc_rport_enter_flogi(struct fc_rport_priv *);
 static void fc_rport_enter_plogi(struct fc_rport_priv *);
 static void fc_rport_enter_prli(struct fc_rport_priv *);
 static void fc_rport_enter_rtv(struct fc_rport_priv *);
@@ -82,6 +83,8 @@ static void fc_rport_work(struct work_struct *);
 
 static const char *fc_rport_state_names[] = {
 	[RPORT_ST_INIT] = "Init",
+	[RPORT_ST_FLOGI] = "FLOGI",
+	[RPORT_ST_PLOGI_WAIT] = "PLOGI_WAIT",
 	[RPORT_ST_PLOGI] = "PLOGI",
 	[RPORT_ST_PRLI] = "PRLI",
 	[RPORT_ST_RTV] = "RTV",
@@ -207,7 +210,7 @@ EXPORT_SYMBOL(fc_set_rport_loss_tmo);
 /**
  * fc_plogi_get_maxframe() - Get the maximum payload from the common service
  *			     parameters in a FLOGI frame
- * @flp:    The FLOGI payload
+ * @flp:    The FLOGI or PLOGI payload
  * @maxval: The maximum frame size upper limit; this may be less than what
  *	    is in the service parameters
  */
@@ -344,7 +347,7 @@ static void fc_rport_work(struct work_struct *work)
 				rdata->major_retries++;
 				rdata->event = RPORT_EV_NONE;
 				FC_RPORT_DBG(rdata, "work restart\n");
-				fc_rport_enter_plogi(rdata);
+				fc_rport_enter_flogi(rdata);
 				mutex_unlock(&rdata->rp_mutex);
 			} else {
 				FC_RPORT_DBG(rdata, "work delete\n");
@@ -397,7 +400,7 @@ int fc_rport_login(struct fc_rport_priv *rdata)
 		break;
 	default:
 		FC_RPORT_DBG(rdata, "Login to port\n");
-		fc_rport_enter_plogi(rdata);
+		fc_rport_enter_flogi(rdata);
 		break;
 	}
 	mutex_unlock(&rdata->rp_mutex);
@@ -499,6 +502,9 @@ static void fc_rport_timeout(struct work_struct *work)
 	mutex_lock(&rdata->rp_mutex);
 
 	switch (rdata->rp_state) {
+	case RPORT_ST_FLOGI:
+		fc_rport_enter_flogi(rdata);
+		break;
 	case RPORT_ST_PLOGI:
 		fc_rport_enter_plogi(rdata);
 		break;
@@ -514,6 +520,7 @@ static void fc_rport_timeout(struct work_struct *work)
 	case RPORT_ST_ADISC:
 		fc_rport_enter_adisc(rdata);
 		break;
+	case RPORT_ST_PLOGI_WAIT:
 	case RPORT_ST_READY:
 	case RPORT_ST_INIT:
 	case RPORT_ST_DELETE:
@@ -538,6 +545,7 @@ static void fc_rport_error(struct fc_rport_priv *rdata, struct fc_frame *fp)
 		     fc_rport_state(rdata), rdata->retries);
 
 	switch (rdata->rp_state) {
+	case RPORT_ST_FLOGI:
 	case RPORT_ST_PLOGI:
 	case RPORT_ST_LOGO:
 		rdata->flags &= ~FC_RP_STARTED;
@@ -550,6 +558,7 @@ static void fc_rport_error(struct fc_rport_priv *rdata, struct fc_frame *fp)
 	case RPORT_ST_ADISC:
 		fc_rport_enter_logo(rdata);
 		break;
+	case RPORT_ST_PLOGI_WAIT:
 	case RPORT_ST_DELETE:
 	case RPORT_ST_READY:
 	case RPORT_ST_INIT:
@@ -592,7 +601,260 @@ static void fc_rport_error_retry(struct fc_rport_priv *rdata,
 }
 
 /**
- * fc_rport_plogi_recv_resp() - Handler for ELS PLOGI responses
+ * fc_rport_login_complete() - Handle parameters and completion of p-mp login.
+ * @rdata:  The remote port which we logged into or which logged into us.
+ * @fp:     The FLOGI or PLOGI request or response frame
+ *
+ * Returns non-zero error if a problem is detected with the frame.
+ * Does not free the frame.
+ *
+ * This is only used in point-to-multipoint mode for FIP currently.
+ */
+static int fc_rport_login_complete(struct fc_rport_priv *rdata,
+				   struct fc_frame *fp)
+{
+	struct fc_lport *lport = rdata->local_port;
+	struct fc_els_flogi *flogi;
+	unsigned int e_d_tov;
+	u16 csp_flags;
+
+	flogi = fc_frame_payload_get(fp, sizeof(*flogi));
+	if (!flogi)
+		return -EINVAL;
+
+	csp_flags = ntohs(flogi->fl_csp.sp_features);
+
+	if (fc_frame_payload_op(fp) == ELS_FLOGI) {
+		if (csp_flags & FC_SP_FT_FPORT) {
+			FC_RPORT_DBG(rdata, "Fabric bit set in FLOGI\n");
+			return -EINVAL;
+		}
+	} else {
+
+		/*
+		 * E_D_TOV is not valid on an incoming FLOGI request.
+		 */
+		e_d_tov = ntohl(flogi->fl_csp.sp_e_d_tov);
+		if (csp_flags & FC_SP_FT_EDTR)
+			e_d_tov /= 1000000;
+		if (e_d_tov > rdata->e_d_tov)
+			rdata->e_d_tov = e_d_tov;
+	}
+	rdata->maxframe_size = fc_plogi_get_maxframe(flogi, lport->mfs);
+	return 0;
+}
+
+/**
+ * fc_rport_flogi_resp() - Handle response to FLOGI request for p-mp mode
+ * @sp:	    The sequence that the FLOGI was on
+ * @fp:	    The FLOGI response frame
+ * @rp_arg: The remote port that received the FLOGI response
+ */
+void fc_rport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp,
+			 void *rp_arg)
+{
+	struct fc_rport_priv *rdata = rp_arg;
+	struct fc_lport *lport = rdata->local_port;
+	struct fc_els_flogi *flogi;
+	unsigned int r_a_tov;
+
+	FC_RPORT_DBG(rdata, "Received a FLOGI %s\n", fc_els_resp_type(fp));
+
+	if (fp == ERR_PTR(-FC_EX_CLOSED))
+		return;
+
+	mutex_lock(&rdata->rp_mutex);
+
+	if (rdata->rp_state != RPORT_ST_FLOGI) {
+		FC_RPORT_DBG(rdata, "Received a FLOGI response, but in state "
+			     "%s\n", fc_rport_state(rdata));
+		if (IS_ERR(fp))
+			goto err;
+		goto out;
+	}
+
+	if (IS_ERR(fp)) {
+		fc_rport_error(rdata, fp);
+		goto err;
+	}
+
+	if (fc_frame_payload_op(fp) != ELS_LS_ACC)
+		goto bad;
+	if (fc_rport_login_complete(rdata, fp))
+		goto bad;
+
+	flogi = fc_frame_payload_get(fp, sizeof(*flogi));
+	if (!flogi)
+		goto bad;
+	r_a_tov = ntohl(flogi->fl_csp.sp_r_a_tov);
+	if (r_a_tov > rdata->r_a_tov)
+		rdata->r_a_tov = r_a_tov;
+
+	if (rdata->ids.port_name < lport->wwpn)
+		fc_rport_enter_plogi(rdata);
+	else
+		fc_rport_state_enter(rdata, RPORT_ST_PLOGI_WAIT);
+out:
+	fc_frame_free(fp);
+err:
+	mutex_unlock(&rdata->rp_mutex);
+	kref_put(&rdata->kref, rdata->local_port->tt.rport_destroy);
+	return;
+bad:
+	FC_RPORT_DBG(rdata, "Bad FLOGI response\n");
+	fc_rport_error_retry(rdata, fp);
+	goto out;
+}
+
+/**
+ * fc_rport_enter_flogi() - Send a FLOGI request to the remote port for p-mp
+ * @rdata: The remote port to send a FLOGI to
+ *
+ * Locking Note: The rport lock is expected to be held before calling
+ * this routine.
+ */
+static void fc_rport_enter_flogi(struct fc_rport_priv *rdata)
+{
+	struct fc_lport *lport = rdata->local_port;
+	struct fc_frame *fp;
+
+	if (!lport->point_to_multipoint)
+		return fc_rport_enter_plogi(rdata);
+
+	FC_RPORT_DBG(rdata, "Entered FLOGI state from %s state\n",
+		     fc_rport_state(rdata));
+
+	fc_rport_state_enter(rdata, RPORT_ST_FLOGI);
+
+	fp = fc_frame_alloc(lport, sizeof(struct fc_els_flogi));
+	if (!fp)
+		return fc_rport_error_retry(rdata, fp);
+
+	if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_FLOGI,
+				  fc_rport_flogi_resp, rdata,
+				  2 * lport->r_a_tov))
+		fc_rport_error_retry(rdata, NULL);
+	else
+		kref_get(&rdata->kref);
+}
+
+/**
+ * fc_rport_recv_flogi_req() - Handle Fabric Login (FLOGI) request in p-mp mode
+ * @lport: The local port that received the PLOGI request
+ * @sp:	   The sequence that the PLOGI request was on
+ * @rx_fp: The PLOGI request frame
+ */
+static void fc_rport_recv_flogi_req(struct fc_lport *lport,
+				    struct fc_seq *sp, struct fc_frame *rx_fp)
+{
+	struct fc_disc *disc;
+	struct fc_els_flogi *flp;
+	struct fc_rport_priv *rdata;
+	struct fc_frame *fp = rx_fp;
+	struct fc_exch *ep;
+	struct fc_frame_header *fh;
+	struct fc_seq_els_data rjt_data;
+	u32 sid, f_ctl;
+
+	rjt_data.fp = NULL;
+	fh = fc_frame_header_get(fp);
+	sid = ntoh24(fh->fh_s_id);
+
+	FC_RPORT_ID_DBG(lport, sid, "Received FLOGI request\n");
+
+	disc = &lport->disc;
+	mutex_lock(&disc->disc_mutex);
+
+	if (!lport->point_to_multipoint) {
+		rjt_data.reason = ELS_RJT_UNSUP;
+		rjt_data.explan = ELS_EXPL_NONE;
+		goto reject;
+	}
+
+	flp = fc_frame_payload_get(fp, sizeof(*flp));
+	if (!flp) {
+		rjt_data.reason = ELS_RJT_LOGIC;
+		rjt_data.explan = ELS_EXPL_INV_LEN;
+		goto reject;
+	}
+
+	rdata = lport->tt.rport_lookup(lport, sid);
+	if (!rdata) {
+		rjt_data.reason = ELS_RJT_FIP;
+		rjt_data.explan = ELS_EXPL_NOT_NEIGHBOR;
+		goto reject;
+	}
+	mutex_lock(&rdata->rp_mutex);
+
+	FC_RPORT_DBG(rdata, "Received FLOGI in %s state\n",
+		     fc_rport_state(rdata));
+
+	switch (rdata->rp_state) {
+	case RPORT_ST_INIT:
+	case RPORT_ST_LOGO:
+	case RPORT_ST_DELETE:
+		mutex_unlock(&rdata->rp_mutex);
+		rjt_data.reason = ELS_RJT_FIP;
+		rjt_data.explan = ELS_EXPL_NOT_NEIGHBOR;
+		goto reject;
+	case RPORT_ST_FLOGI:
+	case RPORT_ST_PLOGI_WAIT:
+	case RPORT_ST_PLOGI:
+		break;
+	case RPORT_ST_PRLI:
+	case RPORT_ST_RTV:
+	case RPORT_ST_READY:
+	case RPORT_ST_ADISC:
+		/*
+		 * Set the remote port to be deleted and to then restart.
+		 * This queues work to be sure exchanges are reset.
+		 */
+		fc_rport_enter_delete(rdata, RPORT_EV_LOGO);
+		mutex_unlock(&rdata->rp_mutex);
+		rjt_data.reason = ELS_RJT_BUSY;
+		rjt_data.explan = ELS_EXPL_NONE;
+		goto reject;
+	}
+	if (fc_rport_login_complete(rdata, fp)) {
+		mutex_unlock(&rdata->rp_mutex);
+		rjt_data.reason = ELS_RJT_LOGIC;
+		rjt_data.explan = ELS_EXPL_NONE;
+		goto reject;
+	}
+	fc_frame_free(rx_fp);
+
+	fp = fc_frame_alloc(lport, sizeof(*flp));
+	if (!fp)
+		goto out;
+
+	sp = lport->tt.seq_start_next(sp);
+	fc_flogi_fill(lport, fp);
+	flp = fc_frame_payload_get(fp, sizeof(*flp));
+	flp->fl_cmd = ELS_LS_ACC;
+
+	f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT;
+	ep = fc_seq_exch(sp);
+	fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid,
+		       FC_TYPE_ELS, f_ctl, 0);
+	lport->tt.seq_send(lport, sp, fp);
+
+	if (rdata->ids.port_name < lport->wwpn)
+		fc_rport_enter_plogi(rdata);
+	else
+		fc_rport_state_enter(rdata, RPORT_ST_PLOGI_WAIT);
+out:
+	mutex_unlock(&rdata->rp_mutex);
+	mutex_unlock(&disc->disc_mutex);
+	return;
+
+reject:
+	mutex_unlock(&disc->disc_mutex);
+	lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data);
+	fc_frame_free(fp);
+}
+
+/**
+ * fc_rport_plogi_resp() - Handler for ELS PLOGI responses
  * @sp:	       The sequence the PLOGI is on
  * @fp:	       The PLOGI response frame
  * @rdata_arg: The remote port that sent the PLOGI response
@@ -607,7 +869,6 @@ static void fc_rport_plogi_resp(struct fc_seq *sp, struct fc_frame *fp,
 	struct fc_rport_priv *rdata = rdata_arg;
 	struct fc_lport *lport = rdata->local_port;
 	struct fc_els_flogi *plp = NULL;
-	unsigned int tov;
 	u16 csp_seq;
 	u16 cssp_seq;
 	u8 op;
@@ -635,11 +896,8 @@ static void fc_rport_plogi_resp(struct fc_seq *sp, struct fc_frame *fp,
 		rdata->ids.port_name = get_unaligned_be64(&plp->fl_wwpn);
 		rdata->ids.node_name = get_unaligned_be64(&plp->fl_wwnn);
 
-		tov = ntohl(plp->fl_csp.sp_e_d_tov);
-		if (ntohs(plp->fl_csp.sp_features) & FC_SP_FT_EDTR)
-			tov /= 1000000;
-		if (tov > rdata->e_d_tov)
-			rdata->e_d_tov = tov;
+		if (lport->point_to_multipoint)
+			fc_rport_login_complete(rdata, fp);
 		csp_seq = ntohs(plp->fl_csp.sp_tot_seq);
 		cssp_seq = ntohs(plp->fl_cssp[3 - 1].cp_con_seq);
 		if (cssp_seq < csp_seq)
@@ -677,6 +935,7 @@ static void fc_rport_enter_plogi(struct fc_rport_priv *rdata)
 	rdata->maxframe_size = FC_MIN_MAX_PAYLOAD;
 	fp = fc_frame_alloc(lport, sizeof(struct fc_els_flogi));
 	if (!fp) {
+		FC_RPORT_DBG(rdata, "%s frame alloc failed\n", __func__);
 		fc_rport_error_retry(rdata, fp);
 		return;
 	}
@@ -1041,7 +1300,7 @@ static void fc_rport_adisc_resp(struct fc_seq *sp, struct fc_frame *fp,
 	    get_unaligned_be64(&adisc->adisc_wwpn) != rdata->ids.port_name ||
 	    get_unaligned_be64(&adisc->adisc_wwnn) != rdata->ids.node_name) {
 		FC_RPORT_DBG(rdata, "ADISC error or mismatch\n");
-		fc_rport_enter_plogi(rdata);
+		fc_rport_enter_flogi(rdata);
 	} else {
 		FC_RPORT_DBG(rdata, "ADISC OK\n");
 		fc_rport_enter_ready(rdata);
@@ -1291,12 +1550,15 @@ void fc_rport_recv_req(struct fc_seq *sp, struct fc_frame *fp,
 	struct fc_seq_els_data els_data;
 
 	/*
-	 * Handle PLOGI and LOGO requests separately, since they
+	 * Handle FLOGI, PLOGI and LOGO requests separately, since they
 	 * don't require prior login.
 	 * Check for unsupported opcodes first and reject them.
 	 * For some ops, it would be incorrect to reject with "PLOGI required".
 	 */
 	switch (fc_frame_payload_op(fp)) {
+	case ELS_FLOGI:
+		fc_rport_recv_flogi_req(lport, sp, fp);
+		break;
 	case ELS_PLOGI:
 		fc_rport_recv_plogi_req(lport, sp, fp);
 		break;
@@ -1386,6 +1648,9 @@ static void fc_rport_recv_plogi_req(struct fc_lport *lport,
 	case RPORT_ST_INIT:
 		FC_RPORT_DBG(rdata, "Received PLOGI in INIT state\n");
 		break;
+	case RPORT_ST_PLOGI_WAIT:
+		FC_RPORT_DBG(rdata, "Received PLOGI in PLOGI_WAIT state\n");
+		break;
 	case RPORT_ST_PLOGI:
 		FC_RPORT_DBG(rdata, "Received PLOGI in PLOGI state\n");
 		if (rdata->ids.port_name < lport->wwpn) {
@@ -1403,6 +1668,7 @@ static void fc_rport_recv_plogi_req(struct fc_lport *lport,
 			     "- ignored for now\n", rdata->rp_state);
 		/* XXX TBD - should reset */
 		break;
+	case RPORT_ST_FLOGI:
 	case RPORT_ST_DELETE:
 	case RPORT_ST_LOGO:
 		FC_RPORT_DBG(rdata, "Received PLOGI in state %s - send busy\n",
diff --git a/include/scsi/fc/fc_els.h b/include/scsi/fc/fc_els.h
index 70a7e92..481abbd 100644
--- a/include/scsi/fc/fc_els.h
+++ b/include/scsi/fc/fc_els.h
@@ -191,6 +191,7 @@ enum fc_els_rjt_reason {
 	ELS_RJT_UNAB =		0x09,	/* unable to perform command request */
 	ELS_RJT_UNSUP =		0x0b,	/* command not supported */
 	ELS_RJT_INPROG =	0x0e,	/* command already in progress */
+	ELS_RJT_FIP =		0x20,	/* FIP error */
 	ELS_RJT_VENDOR =	0xff,	/* vendor specific error */
 };
 
@@ -212,6 +213,7 @@ enum fc_els_rjt_explan {
 	ELS_EXPL_UNAB_DATA =	0x2a,	/* unable to supply requested data */
 	ELS_EXPL_UNSUPR =	0x2c,	/* Request not supported */
 	ELS_EXPL_INV_LEN =	0x2d,	/* Invalid payload length */
+	ELS_EXPL_NOT_NEIGHBOR = 0x62,	/* VN2VN_Port not in neighbor set */
 	/* TBD - above definitions incomplete */
 };
 
diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h
index bd05605..24b91c9 100644
--- a/include/scsi/libfc.h
+++ b/include/scsi/libfc.h
@@ -97,6 +97,8 @@ enum fc_disc_event {
 /**
  * enum fc_rport_state - Remote port states
  * @RPORT_ST_INIT:    Initialized
+ * @RPORT_ST_FLOGI:   Waiting for FLOGI completion for point-to-multipoint
+ * @RPORT_ST_PLOGI_WAIT:   Waiting for peer to login for point-to-multipoint
  * @RPORT_ST_PLOGI:   Waiting for PLOGI completion
  * @RPORT_ST_PRLI:    Waiting for PRLI completion
  * @RPORT_ST_RTV:     Waiting for RTV completion
@@ -107,6 +109,8 @@ enum fc_disc_event {
 */
 enum fc_rport_state {
 	RPORT_ST_INIT,
+	RPORT_ST_FLOGI,
+	RPORT_ST_PLOGI_WAIT,
 	RPORT_ST_PLOGI,
 	RPORT_ST_PRLI,
 	RPORT_ST_RTV,


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 13/28] libfc: track FIP exchanges
  2010-07-20 22:19 [PATCH 00/28] libfc, libfcoe and fcoe updates for scsi-misc Robert Love
                   ` (11 preceding siblings ...)
  2010-07-20 22:20 ` [PATCH 12/28] libfc: add FLOGI state to rport for VN2VN Robert Love
@ 2010-07-20 22:20 ` Robert Love
  2010-07-20 22:20 ` [PATCH 14/28] libfcoe: add protocol description of FIP VN2VN mode Robert Love
                   ` (14 subsequent siblings)
  27 siblings, 0 replies; 34+ messages in thread
From: Robert Love @ 2010-07-20 22:20 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi; +Cc: Joe Eykholt

From: Joe Eykholt <jeykholt@cisco.com>

When an exchange is received with a FIP encapsulation, we need
to know that the response must be sent via FIP and what the original
ELS opcode was.  This becomes important for VN2VN mode, where we may
receive FLOGI or LOGO from several peer VN_ports, and the LS_ACC or
LS_RJT must be sent FIP-encapsulated with the correct sub-type.

Add a field to the struct fc_frame, fr_encaps, to indicate the
encapsulation values.  That term is chosen to be neutral and
LLD-agnostic in case non-FCoE/FIP LLDs might find it useful.

The frame fr_encaps is transferred from the ingress frame to the
exchange by fc_exch_recv_req(), and back to the outgoing frame
by fc_seq_send().

This is taking the last byte in the skb->cb array.  If needed,
we could combine the info in sof, eof, flags, and encaps
together into one field, but it'd be better to do that if
and when its needed.

Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---
 drivers/scsi/libfc/fc_exch.c |    2 ++
 include/scsi/fc_frame.h      |    3 +++
 include/scsi/libfc.h         |    2 ++
 3 files changed, 7 insertions(+), 0 deletions(-)

diff --git a/drivers/scsi/libfc/fc_exch.c b/drivers/scsi/libfc/fc_exch.c
index 104e0fb..61eabd3 100644
--- a/drivers/scsi/libfc/fc_exch.c
+++ b/drivers/scsi/libfc/fc_exch.c
@@ -464,6 +464,7 @@ static int fc_seq_send(struct fc_lport *lport, struct fc_seq *sp,
 
 	f_ctl = ntoh24(fh->fh_f_ctl);
 	fc_exch_setup_hdr(ep, fp, f_ctl);
+	fr_encaps(fp) = ep->encaps;
 
 	/*
 	 * update sequence count if this frame is carrying
@@ -1259,6 +1260,7 @@ static void fc_exch_recv_req(struct fc_lport *lport, struct fc_exch_mgr *mp,
 		sp = fr_seq(fp);	/* sequence will be held */
 		ep = fc_seq_exch(sp);
 		fc_seq_send_ack(sp, fp);
+		ep->encaps = fr_encaps(fp);
 
 		/*
 		 * Call the receive function.
diff --git a/include/scsi/fc_frame.h b/include/scsi/fc_frame.h
index 15427fa..29dd97d 100644
--- a/include/scsi/fc_frame.h
+++ b/include/scsi/fc_frame.h
@@ -51,6 +51,7 @@
 #define fr_sof(fp)	(fr_cb(fp)->fr_sof)
 #define fr_eof(fp)	(fr_cb(fp)->fr_eof)
 #define fr_flags(fp)	(fr_cb(fp)->fr_flags)
+#define fr_encaps(fp)	(fr_cb(fp)->fr_encaps)
 #define fr_max_payload(fp)	(fr_cb(fp)->fr_max_payload)
 #define fr_fsp(fp)	(fr_cb(fp)->fr_fsp)
 #define fr_crc(fp)	(fr_cb(fp)->fr_crc)
@@ -69,6 +70,7 @@ struct fcoe_rcv_info {
 	u8		fr_sof;		/* start of frame delimiter */
 	u8		fr_eof;		/* end of frame delimiter */
 	u8		fr_flags;	/* flags - see below */
+	u8		fr_encaps;	/* LLD encapsulation info (e.g. FIP) */
 	u8		granted_mac[ETH_ALEN]; /* FCoE MAC address */
 };
 
@@ -97,6 +99,7 @@ static inline void fc_frame_init(struct fc_frame *fp)
 	fr_dev(fp) = NULL;
 	fr_seq(fp) = NULL;
 	fr_flags(fp) = 0;
+	fr_encaps(fp) = 0;
 }
 
 struct fc_frame *fc_frame_alloc_fill(struct fc_lport *, size_t payload_len);
diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h
index 24b91c9..8d297f9 100644
--- a/include/scsi/libfc.h
+++ b/include/scsi/libfc.h
@@ -412,6 +412,7 @@ struct fc_seq {
  * @esb_stat:     ESB exchange status
  * @r_a_tov:      Resouce allocation time out value (in msecs)
  * @seq_id:       The next sequence ID to use
+ * @encaps:       encapsulation information for lower-level driver
  * @f_ctl:        F_CTL flags for the sequence
  * @fh_type:      The frame type
  * @class:        The class of service
@@ -443,6 +444,7 @@ struct fc_exch {
 	u32		    esb_stat;
 	u32		    r_a_tov;
 	u8		    seq_id;
+	u8		    encaps;
 	u32		    f_ctl;
 	u8		    fh_type;
 	enum fc_class	    class;


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 14/28] libfcoe: add protocol description of FIP VN2VN mode
  2010-07-20 22:19 [PATCH 00/28] libfc, libfcoe and fcoe updates for scsi-misc Robert Love
                   ` (12 preceding siblings ...)
  2010-07-20 22:20 ` [PATCH 13/28] libfc: track FIP exchanges Robert Love
@ 2010-07-20 22:20 ` Robert Love
  2010-07-20 22:20 ` [PATCH 15/28] libfcoe: add state change debugging Robert Love
                   ` (13 subsequent siblings)
  27 siblings, 0 replies; 34+ messages in thread
From: Robert Love @ 2010-07-20 22:20 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi; +Cc: Joe Eykholt

From: Joe Eykholt <jeykholt@cisco.com>

The FC-BB-6 committee is proposing a new FIP usage model called
VN_port to VN_port mode.  It allows VN_ports to discover each other
over a loss-free L2 Ethernet without any FCF or Fibre-channel fabric
services.  This is point-to-multipoint.  There is also a variant
of this called point-to-point which provides for making sure there
is just one pair of ports operating over the Ethernet fabric.

This patch defines the new message type and subtypes as well as
one new descriptor type used by VN2VN mode.

These are all still at the proposed stage and subject to change.

Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---
 include/scsi/fc/fc_fip.h |   46 +++++++++++++++++++++++++++++++++++++++++++---
 include/scsi/fc/fc_ns.h  |    7 +++++++
 2 files changed, 50 insertions(+), 3 deletions(-)

diff --git a/include/scsi/fc/fc_fip.h b/include/scsi/fc/fc_fip.h
index 17baa19..ae25d4a 100644
--- a/include/scsi/fc/fc_fip.h
+++ b/include/scsi/fc/fc_fip.h
@@ -17,9 +17,12 @@
 #ifndef _FC_FIP_H_
 #define _FC_FIP_H_
 
+#include <scsi/fc/fc_ns.h>
+
 /*
  * This version is based on:
  * http://www.t11.org/ftp/t11/pub/fc/bb-5/08-543v1.pdf
+ * and T11 FC-BB-6 10-019v4.pdf (June 2010 VN2VN proposal)
  */
 
 #define FIP_DEF_PRI	128	/* default selection priority */
@@ -29,11 +32,24 @@
 #define FIP_FCF_FUZZ	100	/* random time added by FCF (mS) */
 
 /*
+ * VN2VN proposed-standard values.
+ */
+#define FIP_VN_FC_MAP	0x0efd00 /* MAC OUI for VN2VN use */
+#define FIP_VN_PROBE_WAIT 100	/* interval between VN2VN probes (ms) */
+#define FIP_VN_ANN_WAIT 400	/* interval between VN2VN announcements (ms) */
+#define FIP_VN_RLIM_INT 10000	/* interval between probes when rate limited */
+#define FIP_VN_RLIM_COUNT 10	/* number of probes before rate limiting */
+#define FIP_VN_BEACON_INT 8000	/* interval between VN2VN beacons */
+#define FIP_VN_BEACON_FUZZ 100	/* random time to add to beacon period (ms) */
+
+/*
  * Multicast MAC addresses.  T11-adopted.
  */
-#define FIP_ALL_FCOE_MACS	((u8[6]) { 1, 0x10, 0x18, 1, 0, 0 })
-#define FIP_ALL_ENODE_MACS	((u8[6]) { 1, 0x10, 0x18, 1, 0, 1 })
-#define FIP_ALL_FCF_MACS	((u8[6]) { 1, 0x10, 0x18, 1, 0, 2 })
+#define FIP_ALL_FCOE_MACS	((__u8[6]) { 1, 0x10, 0x18, 1, 0, 0 })
+#define FIP_ALL_ENODE_MACS	((__u8[6]) { 1, 0x10, 0x18, 1, 0, 1 })
+#define FIP_ALL_FCF_MACS	((__u8[6]) { 1, 0x10, 0x18, 1, 0, 2 })
+#define FIP_ALL_VN2VN_MACS	((__u8[6]) { 1, 0x10, 0x18, 1, 0, 4 })
+#define FIP_ALL_P2P_MACS	((__u8[6]) { 1, 0x10, 0x18, 1, 0, 5 })
 
 #define FIP_VER		1		/* version for fip_header */
 
@@ -60,6 +76,7 @@ enum fip_opcode {
 	FIP_OP_LS =	2,		/* Link Service request or reply */
 	FIP_OP_CTRL =	3,		/* Keep Alive / Link Reset */
 	FIP_OP_VLAN =	4,		/* VLAN discovery */
+	FIP_OP_VN2VN =	5,		/* VN2VN operation */
 	FIP_OP_VENDOR_MIN = 0xfff8,	/* min vendor-specific opcode */
 	FIP_OP_VENDOR_MAX = 0xfffe,	/* max vendor-specific opcode */
 };
@@ -97,11 +114,23 @@ enum fip_vlan_subcode {
 };
 
 /*
+ * Subcodes for FIP_OP_VN2VN.
+ */
+enum fip_vn2vn_subcode {
+	FIP_SC_VN_PROBE_REQ = 1,	/* probe request */
+	FIP_SC_VN_PROBE_REP = 2,	/* probe reply */
+	FIP_SC_VN_CLAIM_NOTIFY = 3,	/* claim notification */
+	FIP_SC_VN_CLAIM_REP = 4,	/* claim response */
+	FIP_SC_VN_BEACON = 5,		/* beacon */
+};
+
+/*
  * flags in header fip_flags.
  */
 enum fip_flag {
 	FIP_FL_FPMA =	0x8000,		/* supports FPMA fabric-provided MACs */
 	FIP_FL_SPMA =	0x4000,		/* supports SPMA server-provided MACs */
+	FIP_FL_REC_OR_P2P = 0x0008,	/* configured addr or point-to-point */
 	FIP_FL_AVAIL =	0x0004,		/* available for FLOGI/ELP */
 	FIP_FL_SOL =	0x0002,		/* this is a solicited message */
 	FIP_FL_FPORT =	0x0001,		/* sent from an F port */
@@ -130,6 +159,7 @@ enum fip_desc_type {
 	FIP_DT_FKA =	12,		/* advertisement keep-alive period */
 	FIP_DT_VENDOR =	13,		/* vendor ID */
 	FIP_DT_VLAN =	14,		/* vlan number */
+	FIP_DT_FC4F =	15,		/* FC-4 features */
 	FIP_DT_LIMIT,			/* max defined desc_type + 1 */
 	FIP_DT_VENDOR_BASE = 128,	/* first vendor-specific desc_type */
 };
@@ -229,6 +259,16 @@ enum fip_fka_flags {
 /* FIP_DT_FKA flags */
 
 /*
+ * FIP_DT_FC4F - FC-4 features.
+ */
+struct fip_fc4_feat {
+	struct fip_desc fd_desc;
+	__u8		fd_resvd[2];
+	struct fc_ns_fts fd_fts;
+	struct fc_ns_ff	fd_ff;
+} __attribute__((packed));
+
+/*
  * FIP_DT_VENDOR descriptor.
  */
 struct fip_vendor_desc {
diff --git a/include/scsi/fc/fc_ns.h b/include/scsi/fc/fc_ns.h
index e7d3ac4..185015d 100644
--- a/include/scsi/fc/fc_ns.h
+++ b/include/scsi/fc/fc_ns.h
@@ -100,6 +100,13 @@ struct fc_ns_fts {
 };
 
 /*
+ * FC4-features object.
+ */
+struct fc_ns_ff	{
+	__be32	fd_feat[FC_NS_TYPES * 4 / FC_NS_BPW]; /* 4-bits per FC-type */
+};
+
+/*
  * GID_PT request.
  */
 struct fc_ns_gid_pt {


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 15/28] libfcoe: add state change debugging
  2010-07-20 22:19 [PATCH 00/28] libfc, libfcoe and fcoe updates for scsi-misc Robert Love
                   ` (13 preceding siblings ...)
  2010-07-20 22:20 ` [PATCH 14/28] libfcoe: add protocol description of FIP VN2VN mode Robert Love
@ 2010-07-20 22:20 ` Robert Love
  2010-07-20 22:20 ` [PATCH 16/28] libfcoe: fcoe: fnic: add FIP VN2VN point-to-multipoint support Robert Love
                   ` (12 subsequent siblings)
  27 siblings, 0 replies; 34+ messages in thread
From: Robert Love @ 2010-07-20 22:20 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi; +Cc: Joe Eykholt

From: Joe Eykholt <jeykholt@cisco.com>

Enhancement: add debug messages at all state transitions.

Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---
 drivers/scsi/fcoe/libfcoe.c |   48 +++++++++++++++++++++++++++++++++++++------
 1 files changed, 41 insertions(+), 7 deletions(-)

diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c
index 76056e4..11f3db5 100644
--- a/drivers/scsi/fcoe/libfcoe.c
+++ b/drivers/scsi/fcoe/libfcoe.c
@@ -80,6 +80,40 @@ do {							\
 			      printk(KERN_INFO "host%d: fip: " fmt, 	\
 				     (fip)->lp->host->host_no, ##args);)
 
+static const char *fcoe_ctlr_states[] = {
+	[FIP_ST_DISABLED] =	"DISABLED",
+	[FIP_ST_LINK_WAIT] =	"LINK_WAIT",
+	[FIP_ST_AUTO] =		"AUTO",
+	[FIP_ST_NON_FIP] =	"NON_FIP",
+	[FIP_ST_ENABLED] =	"ENABLED",
+};
+
+static const char *fcoe_ctlr_state(enum fip_state state)
+{
+	const char *cp = "unknown";
+
+	if (state < ARRAY_SIZE(fcoe_ctlr_states))
+		cp = fcoe_ctlr_states[state];
+	if (!cp)
+		cp = "unknown";
+	return cp;
+}
+
+/**
+ * fcoe_ctlr_set_state() - Set and do debug printing for the new FIP state.
+ * @fip: The FCoE controller
+ * @state: The new state
+ */
+static void fcoe_ctlr_set_state(struct fcoe_ctlr *fip, enum fip_state state)
+{
+	if (state == fip->state)
+		return;
+	if (fip->lp)
+		LIBFCOE_FIP_DBG(fip, "state %s -> %s\n",
+			fcoe_ctlr_state(fip->state), fcoe_ctlr_state(state));
+	fip->state = state;
+}
+
 /**
  * fcoe_ctlr_mtu_valid() - Check if a FCF's MTU is valid
  * @fcf: The FCF to check
@@ -110,7 +144,7 @@ static inline int fcoe_ctlr_fcf_usable(struct fcoe_fcf *fcf)
  */
 void fcoe_ctlr_init(struct fcoe_ctlr *fip, enum fip_state mode)
 {
-	fip->state = FIP_ST_LINK_WAIT;
+	fcoe_ctlr_set_state(fip, FIP_ST_LINK_WAIT);
 	fip->mode = mode;
 	INIT_LIST_HEAD(&fip->fcfs);
 	mutex_init(&fip->ctlr_mutex);
@@ -160,7 +194,7 @@ void fcoe_ctlr_destroy(struct fcoe_ctlr *fip)
 	skb_queue_purge(&fip->fip_recv_list);
 
 	mutex_lock(&fip->ctlr_mutex);
-	fip->state = FIP_ST_DISABLED;
+	fcoe_ctlr_set_state(fip, FIP_ST_DISABLED);
 	fcoe_ctlr_reset_fcfs(fip);
 	mutex_unlock(&fip->ctlr_mutex);
 	del_timer_sync(&fip->timer);
@@ -260,7 +294,7 @@ void fcoe_ctlr_link_up(struct fcoe_ctlr *fip)
 		mutex_unlock(&fip->ctlr_mutex);
 		fc_linkup(fip->lp);
 	} else if (fip->state == FIP_ST_LINK_WAIT) {
-		fip->state = fip->mode;
+		fcoe_ctlr_set_state(fip, fip->mode);
 		mutex_unlock(&fip->ctlr_mutex);
 		if (fip->state == FIP_ST_AUTO)
 			LIBFCOE_FIP_DBG(fip, "%s", "setting AUTO mode.\n");
@@ -303,7 +337,7 @@ int fcoe_ctlr_link_down(struct fcoe_ctlr *fip)
 	mutex_lock(&fip->ctlr_mutex);
 	fcoe_ctlr_reset(fip);
 	link_dropped = fip->state != FIP_ST_LINK_WAIT;
-	fip->state = FIP_ST_LINK_WAIT;
+	fcoe_ctlr_set_state(fip, FIP_ST_LINK_WAIT);
 	mutex_unlock(&fip->ctlr_mutex);
 
 	if (link_dropped)
@@ -1170,7 +1204,7 @@ static int fcoe_ctlr_recv_handler(struct fcoe_ctlr *fip, struct sk_buff *skb)
 	state = fip->state;
 	if (state == FIP_ST_AUTO) {
 		fip->map_dest = 0;
-		fip->state = FIP_ST_ENABLED;
+		fcoe_ctlr_set_state(fip, FIP_ST_ENABLED);
 		state = FIP_ST_ENABLED;
 		LIBFCOE_FIP_DBG(fip, "Using FIP mode\n");
 	}
@@ -1401,7 +1435,7 @@ int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *fip, struct fc_lport *lport,
 			mutex_unlock(&fip->ctlr_mutex);
 			return -EINVAL;
 		}
-		fip->state = FIP_ST_NON_FIP;
+		fcoe_ctlr_set_state(fip, FIP_ST_NON_FIP);
 		LIBFCOE_FIP_DBG(fip,
 				"received FLOGI LS_ACC using non-FIP mode\n");
 
@@ -1431,7 +1465,7 @@ int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *fip, struct fc_lport *lport,
 			if (fip->state == FIP_ST_AUTO)
 				LIBFCOE_FIP_DBG(fip, "received non-FIP FLOGI. "
 						"Setting non-FIP mode\n");
-			fip->state = FIP_ST_NON_FIP;
+			fcoe_ctlr_set_state(fip, FIP_ST_NON_FIP);
 		}
 		mutex_unlock(&fip->ctlr_mutex);
 	}


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 16/28] libfcoe: fcoe: fnic: add FIP VN2VN point-to-multipoint support
  2010-07-20 22:19 [PATCH 00/28] libfc, libfcoe and fcoe updates for scsi-misc Robert Love
                   ` (14 preceding siblings ...)
  2010-07-20 22:20 ` [PATCH 15/28] libfcoe: add state change debugging Robert Love
@ 2010-07-20 22:20 ` Robert Love
  2010-07-20 22:20 ` [PATCH 17/28] libfcoe: Fix FIP ELS encapsulation details for FLOGI responses Robert Love
                   ` (11 subsequent siblings)
  27 siblings, 0 replies; 34+ messages in thread
From: Robert Love @ 2010-07-20 22:20 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi; +Cc: Joe Eykholt

From: Joe Eykholt <jeykholt@cisco.com>

The FC-BB-6 committee is proposing a new FIP usage model called
VN_port to VN_port mode.  It allows VN_ports to discover each other
over a loss-free L2 Ethernet without any FCF or Fibre-channel fabric
services.  This is point-to-multipoint.  There is also a variant
of this called point-to-point which provides for making sure there
is just one pair of ports operating over the Ethernet fabric.

We add these new states:  VNMP_START, _PROBE1, _PROBE2, _CLAIM, and _UP.
These usually go quickly in that sequence.  After waiting a random
amount of time up to 100 ms in START, we select a pseudo-random
proposed locally-unique port ID and send out probes in states PROBE1
and PROBE2, 100 ms apart.  If no probe responses are heard, we
proceed to CLAIM state 400 ms later and send a claim notification.
We wait another 400 ms to receive claim responses, which give us
a list of the other nodes on the network, including their FC-4
capabilities.  After another 400 ms we go to VNMP_UP state and
should start interoperating with any of the nodes for whic we
receivec claim responses.  More details are in the spec.j

Add the new mode as FIP_MODE_VN2VN.  The driver must specify
explicitly that it wants to operate in this mode.  There is
no automatic detection between point-to-multipoint and fabric
mode, and the local port initialization is affected, so it isn't
anticipated that there will ever be any such automatic switchover.

It may eventually be possible to have both fabric and VN2VN
modes on the same L2 network, which may be done by two separate
local VN_ports (lports).

When in VN2VN mode, FIP replaces libfc's fabric-oriented discovery
module with its own simple code that adds remote ports as they
are discovered from incoming claim notifications and responses.
These hooks are placed by fcoe_disc_init().

A linear list of discovered vn_ports is maintained under the
fcoe_ctlr struct.  It is expected to be short for now, and
accessed infrequently.  It is kept under RCU for lock-ordering
reasons.  The lport and/or rport mutexes may be held when we
need to lookup a fcoe_vnport during an ELS send.

Change fcoe_ctlr_encaps() to lookup the destination vn_port in
the list of peers for the destination MAC address of the
FIP-encapsulated frame.

Add a new function fcoe_disc_init() to initialize just the
discovery portion of libfcoe for VN2VN mode.

Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---
 drivers/scsi/fcoe/fcoe.c      |   16 -
 drivers/scsi/fcoe/libfcoe.c   | 1075 +++++++++++++++++++++++++++++++++++++++--
 drivers/scsi/fnic/fnic_main.c |    7 
 include/scsi/libfcoe.h        |   42 ++
 4 files changed, 1071 insertions(+), 69 deletions(-)

diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c
index 9d64e08..216aba3 100644
--- a/drivers/scsi/fcoe/fcoe.c
+++ b/drivers/scsi/fcoe/fcoe.c
@@ -315,7 +315,11 @@ static int fcoe_interface_setup(struct fcoe_interface *fcoe,
 	dev_uc_add(netdev, flogi_maddr);
 	if (fip->spma)
 		dev_uc_add(netdev, fip->ctl_src_addr);
-	dev_mc_add(netdev, FIP_ALL_ENODE_MACS);
+	if (fip->mode == FIP_MODE_VN2VN) {
+		dev_mc_add(netdev, FIP_ALL_VN2VN_MACS);
+		dev_mc_add(netdev, FIP_ALL_P2P_MACS);
+	} else
+		dev_mc_add(netdev, FIP_ALL_ENODE_MACS);
 
 	/*
 	 * setup the receive function from ethernet driver
@@ -401,7 +405,11 @@ void fcoe_interface_cleanup(struct fcoe_interface *fcoe)
 	dev_uc_del(netdev, flogi_maddr);
 	if (fip->spma)
 		dev_uc_del(netdev, fip->ctl_src_addr);
-	dev_mc_del(netdev, FIP_ALL_ENODE_MACS);
+	if (fip->mode == FIP_MODE_VN2VN) {
+		dev_mc_del(netdev, FIP_ALL_VN2VN_MACS);
+		dev_mc_del(netdev, FIP_ALL_P2P_MACS);
+	} else
+		dev_mc_del(netdev, FIP_ALL_ENODE_MACS);
 
 	/* Tell the LLD we are done w/ FCoE */
 	ops = netdev->netdev_ops;
@@ -967,7 +975,7 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe,
 	}
 
 	/* Initialize the library */
-	rc = fcoe_libfc_config(lport, &fcoe_libfc_fcn_templ);
+	rc = fcoe_libfc_config(lport, &fcoe->ctlr, &fcoe_libfc_fcn_templ, 1);
 	if (rc) {
 		FCOE_NETDEV_DBG(netdev, "Could not configure libfc for the "
 				"interface\n");
@@ -2533,6 +2541,8 @@ static struct fc_seq *fcoe_elsct_send(struct fc_lport *lport, u32 did,
 	switch (op) {
 	case ELS_FLOGI:
 	case ELS_FDISC:
+		if (lport->point_to_multipoint)
+			break;
 		return fc_elsct_send(lport, did, fp, op, fcoe_flogi_resp,
 				     fip, timeout);
 	case ELS_LOGO:
diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c
index 11f3db5..79df78f 100644
--- a/drivers/scsi/fcoe/libfcoe.c
+++ b/drivers/scsi/fcoe/libfcoe.c
@@ -39,6 +39,7 @@
 #include <scsi/fc/fc_fip.h>
 #include <scsi/fc/fc_encaps.h>
 #include <scsi/fc/fc_fcoe.h>
+#include <scsi/fc/fc_fcp.h>
 
 #include <scsi/libfc.h>
 #include <scsi/libfcoe.h>
@@ -54,7 +55,15 @@ static void fcoe_ctlr_timeout(unsigned long);
 static void fcoe_ctlr_timer_work(struct work_struct *);
 static void fcoe_ctlr_recv_work(struct work_struct *);
 
+static void fcoe_ctlr_vn_start(struct fcoe_ctlr *);
+static int fcoe_ctlr_vn_recv(struct fcoe_ctlr *, struct sk_buff *);
+static void fcoe_ctlr_vn_timeout(struct fcoe_ctlr *);
+static int fcoe_ctlr_vn_lookup(struct fcoe_ctlr *, u32, u8 *);
+
 static u8 fcoe_all_fcfs[ETH_ALEN] = FIP_ALL_FCF_MACS;
+static u8 fcoe_all_enode[ETH_ALEN] = FIP_ALL_ENODE_MACS;
+static u8 fcoe_all_vn2vn[ETH_ALEN] = FIP_ALL_VN2VN_MACS;
+static u8 fcoe_all_p2p[ETH_ALEN] = FIP_ALL_P2P_MACS;
 
 unsigned int libfcoe_debug_logging;
 module_param_named(debug_logging, libfcoe_debug_logging, int, S_IRUGO|S_IWUSR);
@@ -86,6 +95,11 @@ static const char *fcoe_ctlr_states[] = {
 	[FIP_ST_AUTO] =		"AUTO",
 	[FIP_ST_NON_FIP] =	"NON_FIP",
 	[FIP_ST_ENABLED] =	"ENABLED",
+	[FIP_ST_VNMP_START] =	"VNMP_START",
+	[FIP_ST_VNMP_PROBE1] =	"VNMP_PROBE1",
+	[FIP_ST_VNMP_PROBE2] =	"VNMP_PROBE2",
+	[FIP_ST_VNMP_CLAIM] =	"VNMP_CLAIM",
+	[FIP_ST_VNMP_UP] =	"VNMP_UP",
 };
 
 static const char *fcoe_ctlr_state(enum fip_state state)
@@ -295,11 +309,25 @@ void fcoe_ctlr_link_up(struct fcoe_ctlr *fip)
 		fc_linkup(fip->lp);
 	} else if (fip->state == FIP_ST_LINK_WAIT) {
 		fcoe_ctlr_set_state(fip, fip->mode);
-		mutex_unlock(&fip->ctlr_mutex);
-		if (fip->state == FIP_ST_AUTO)
+		switch (fip->mode) {
+		default:
+			LIBFCOE_FIP_DBG(fip, "invalid mode %d\n", fip->mode);
+			/* fall-through */
+		case FIP_MODE_AUTO:
 			LIBFCOE_FIP_DBG(fip, "%s", "setting AUTO mode.\n");
-		fc_linkup(fip->lp);
-		fcoe_ctlr_solicit(fip, NULL);
+			/* fall-through */
+		case FIP_MODE_FABRIC:
+		case FIP_MODE_NON_FIP:
+			mutex_unlock(&fip->ctlr_mutex);
+			fc_linkup(fip->lp);
+			fcoe_ctlr_solicit(fip, NULL);
+			break;
+		case FIP_MODE_VN2VN:
+			fcoe_ctlr_vn_start(fip);
+			mutex_unlock(&fip->ctlr_mutex);
+			fc_linkup(fip->lp);
+			break;
+		}
 	} else
 		mutex_unlock(&fip->ctlr_mutex);
 }
@@ -423,6 +451,7 @@ static void fcoe_ctlr_send_keep_alive(struct fcoe_ctlr *fip,
  * @fip:   The FCoE controller for the ELS frame
  * @dtype: The FIP descriptor type for the frame
  * @skb:   The FCoE ELS frame including FC header but no FCoE headers
+ * @d_id:  The destination port ID.
  *
  * Returns non-zero error code on failure.
  *
@@ -433,7 +462,7 @@ static void fcoe_ctlr_send_keep_alive(struct fcoe_ctlr *fip,
  * Ethernet header.  The tailroom is for the FIP MAC descriptor.
  */
 static int fcoe_ctlr_encaps(struct fcoe_ctlr *fip, struct fc_lport *lport,
-			    u8 dtype, struct sk_buff *skb)
+			    u8 dtype, struct sk_buff *skb, u32 d_id)
 {
 	struct fip_encaps_head {
 		struct ethhdr eth;
@@ -445,21 +474,24 @@ static int fcoe_ctlr_encaps(struct fcoe_ctlr *fip, struct fc_lport *lport,
 	size_t dlen;
 	u16 fip_flags;
 
-	fcf = fip->sel_fcf;
-	if (!fcf)
-		return -ENODEV;
-
-	/* set flags according to both FCF and lport's capability on SPMA */
-	fip_flags = fcf->flags;
-	fip_flags &= fip->spma ? FIP_FL_SPMA | FIP_FL_FPMA : FIP_FL_FPMA;
-	if (!fip_flags)
-		return -ENODEV;
-
 	dlen = sizeof(struct fip_encaps) + skb->len;	/* len before push */
 	cap = (struct fip_encaps_head *)skb_push(skb, sizeof(*cap));
-
 	memset(cap, 0, sizeof(*cap));
-	memcpy(cap->eth.h_dest, fcf->fcf_mac, ETH_ALEN);
+
+	if (lport->point_to_multipoint) {
+		if (fcoe_ctlr_vn_lookup(fip, d_id, cap->eth.h_dest))
+			return -ENODEV;
+	} else {
+		fcf = fip->sel_fcf;
+		if (!fcf)
+			return -ENODEV;
+		fip_flags = fcf->flags;
+		fip_flags &= fip->spma ? FIP_FL_SPMA | FIP_FL_FPMA :
+					 FIP_FL_FPMA;
+		if (!fip_flags)
+			return -ENODEV;
+		memcpy(cap->eth.h_dest, fcf->fcf_mac, ETH_ALEN);
+	}
 	memcpy(cap->eth.h_source, fip->ctl_src_addr, ETH_ALEN);
 	cap->eth.h_proto = htons(ETH_P_FIP);
 
@@ -503,19 +535,22 @@ static int fcoe_ctlr_encaps(struct fcoe_ctlr *fip, struct fc_lport *lport,
  *
  * The caller must check that the length is a multiple of 4.
  * The SKB must have enough headroom (28 bytes) and tailroom (8 bytes).
+ * The the skb must also be an fc_frame.
  */
 int fcoe_ctlr_els_send(struct fcoe_ctlr *fip, struct fc_lport *lport,
 		       struct sk_buff *skb)
 {
+	struct fc_frame *fp;
 	struct fc_frame_header *fh;
 	u16 old_xid;
 	u8 op;
 	u8 mac[ETH_ALEN];
 
+	fp = container_of(skb, struct fc_frame, skb);
 	fh = (struct fc_frame_header *)skb->data;
 	op = *(u8 *)(fh + 1);
 
-	if (op == ELS_FLOGI) {
+	if (op == ELS_FLOGI && fip->mode != FIP_MODE_VN2VN) {
 		old_xid = fip->flogi_oxid;
 		fip->flogi_oxid = ntohs(fh->fh_ox_id);
 		if (fip->state == FIP_ST_AUTO) {
@@ -533,9 +568,8 @@ int fcoe_ctlr_els_send(struct fcoe_ctlr *fip, struct fc_lport *lport,
 
 	if (fip->state == FIP_ST_NON_FIP)
 		return 0;
-	if (!fip->sel_fcf)
+	if (!fip->sel_fcf && fip->mode != FIP_MODE_VN2VN)
 		goto drop;
-
 	switch (op) {
 	case ELS_FLOGI:
 		op = FIP_DT_FLOGI;
@@ -546,36 +580,49 @@ int fcoe_ctlr_els_send(struct fcoe_ctlr *fip, struct fc_lport *lport,
 		op = FIP_DT_FDISC;
 		break;
 	case ELS_LOGO:
-		if (fip->state != FIP_ST_ENABLED)
-			return 0;
-		if (ntoh24(fh->fh_d_id) != FC_FID_FLOGI)
-			return 0;
+		if (fip->mode == FIP_MODE_VN2VN) {
+			if (fip->state != FIP_ST_VNMP_UP)
+				return -EINVAL;
+			if (ntoh24(fh->fh_d_id) == FC_FID_FLOGI)
+				return -EINVAL;
+		} else {
+			if (fip->state != FIP_ST_ENABLED)
+				return 0;
+			if (ntoh24(fh->fh_d_id) != FC_FID_FLOGI)
+				return 0;
+		}
 		op = FIP_DT_LOGO;
 		break;
 	case ELS_LS_ACC:
-		if (fip->flogi_oxid == FC_XID_UNKNOWN)
-			return 0;
-		if (!ntoh24(fh->fh_s_id))
-			return 0;
-		if (fip->state == FIP_ST_AUTO)
-			return 0;
 		/*
-		 * Here we must've gotten an SID by accepting an FLOGI
+		 * If non-FIP, we may have gotten an SID by accepting an FLOGI
 		 * from a point-to-point connection.  Switch to using
 		 * the source mac based on the SID.  The destination
 		 * MAC in this case would have been set by receving the
 		 * FLOGI.
 		 */
-		fip->flogi_oxid = FC_XID_UNKNOWN;
-		fc_fcoe_set_mac(mac, fh->fh_d_id);
-		fip->update_mac(lport, mac);
+		if (fip->state == FIP_ST_NON_FIP) {
+			if (fip->flogi_oxid == FC_XID_UNKNOWN)
+				return 0;
+			fip->flogi_oxid = FC_XID_UNKNOWN;
+			fc_fcoe_set_mac(mac, fh->fh_d_id);
+			fip->update_mac(lport, mac);
+		}
+		/* fall through */
+	case ELS_LS_RJT:
+		op = fr_encaps(fp);
+		if (op)
+			break;
 		return 0;
 	default:
-		if (fip->state != FIP_ST_ENABLED)
+		if (fip->state != FIP_ST_ENABLED &&
+		    fip->state != FIP_ST_VNMP_UP)
 			goto drop;
 		return 0;
 	}
-	if (fcoe_ctlr_encaps(fip, lport, op, skb))
+	LIBFCOE_FIP_DBG(fip, "els_send op %u d_id %x\n",
+			op, ntoh24(fh->fh_d_id));
+	if (fcoe_ctlr_encaps(fip, lport, op, skb, ntoh24(fh->fh_d_id)))
 		goto drop;
 	fip->send(fip, skb);
 	return -EINPROGRESS;
@@ -717,8 +764,9 @@ static int fcoe_ctlr_parse_adv(struct fcoe_ctlr *fip,
 			       ((struct fip_mac_desc *)desc)->fd_mac,
 			       ETH_ALEN);
 			if (!is_valid_ether_addr(fcf->fcf_mac)) {
-				LIBFCOE_FIP_DBG(fip, "Invalid MAC address "
-						"in FIP adv\n");
+				LIBFCOE_FIP_DBG(fip,
+					"Invalid MAC addr %pM in FIP adv\n",
+					fcf->fcf_mac);
 				return -EINVAL;
 			}
 			desc_mask &= ~BIT(FIP_DT_MAC);
@@ -944,12 +992,6 @@ static void fcoe_ctlr_recv_els(struct fcoe_ctlr *fip, struct sk_buff *skb)
 			memcpy(granted_mac,
 			       ((struct fip_mac_desc *)desc)->fd_mac,
 			       ETH_ALEN);
-			if (!is_valid_ether_addr(granted_mac)) {
-				LIBFCOE_FIP_DBG(fip, "Invalid MAC address "
-						"in FIP ELS\n");
-				goto drop;
-			}
-			memcpy(fr_cb(fp)->granted_mac, granted_mac, ETH_ALEN);
 			break;
 		case FIP_DT_FLOGI:
 		case FIP_DT_FDISC:
@@ -990,10 +1032,20 @@ static void fcoe_ctlr_recv_els(struct fcoe_ctlr *fip, struct sk_buff *skb)
 		goto drop;
 	els_op = *(u8 *)(fh + 1);
 
-	if (els_dtype == FIP_DT_FLOGI && sub == FIP_SC_REP &&
-	    fip->flogi_oxid == ntohs(fh->fh_ox_id) &&
-	    els_op == ELS_LS_ACC && is_valid_ether_addr(granted_mac))
-		fip->flogi_oxid = FC_XID_UNKNOWN;
+	if ((els_dtype == FIP_DT_FLOGI || els_dtype == FIP_DT_FDISC) &&
+	    sub == FIP_SC_REP && els_op == ELS_LS_ACC &&
+	    fip->mode != FIP_MODE_VN2VN) {
+		if (!is_valid_ether_addr(granted_mac)) {
+			LIBFCOE_FIP_DBG(fip,
+				"Invalid MAC address %pM in FIP ELS\n",
+				granted_mac);
+			goto drop;
+		}
+		memcpy(fr_cb(fp)->granted_mac, granted_mac, ETH_ALEN);
+
+		if (fip->flogi_oxid == ntohs(fh->fh_ox_id))
+			fip->flogi_oxid = FC_XID_UNKNOWN;
+	}
 
 	if ((desc_cnt == 0) || ((els_op != ELS_LS_RJT) &&
 	    (!(1U << FIP_DT_MAC & desc_mask)))) {
@@ -1012,6 +1064,7 @@ static void fcoe_ctlr_recv_els(struct fcoe_ctlr *fip, struct sk_buff *skb)
 	fr_sof(fp) = FC_SOF_I3;
 	fr_eof(fp) = FC_EOF_T;
 	fr_dev(fp) = lport;
+	fr_encaps(fp) = els_dtype;
 
 	stats = per_cpu_ptr(lport->dev_stats, get_cpu());
 	stats->RxFrames++;
@@ -1188,8 +1241,13 @@ static int fcoe_ctlr_recv_handler(struct fcoe_ctlr *fip, struct sk_buff *skb)
 	if (skb->len < sizeof(*fiph))
 		goto drop;
 	eh = eth_hdr(skb);
-	if (compare_ether_addr(eh->h_dest, fip->ctl_src_addr) &&
-	    compare_ether_addr(eh->h_dest, FIP_ALL_ENODE_MACS))
+	if (fip->mode == FIP_MODE_VN2VN) {
+		if (compare_ether_addr(eh->h_dest, fip->ctl_src_addr) &&
+		    compare_ether_addr(eh->h_dest, fcoe_all_vn2vn) &&
+		    compare_ether_addr(eh->h_dest, fcoe_all_p2p))
+			goto drop;
+	} else if (compare_ether_addr(eh->h_dest, fip->ctl_src_addr) &&
+		   compare_ether_addr(eh->h_dest, fcoe_all_enode))
 		goto drop;
 	fiph = (struct fip_header *)skb->data;
 	op = ntohs(fiph->fip_op);
@@ -1209,13 +1267,22 @@ static int fcoe_ctlr_recv_handler(struct fcoe_ctlr *fip, struct sk_buff *skb)
 		LIBFCOE_FIP_DBG(fip, "Using FIP mode\n");
 	}
 	mutex_unlock(&fip->ctlr_mutex);
-	if (state != FIP_ST_ENABLED)
+
+	if (fip->mode == FIP_MODE_VN2VN && op == FIP_OP_VN2VN)
+		return fcoe_ctlr_vn_recv(fip, skb);
+
+	if (state != FIP_ST_ENABLED && state != FIP_ST_VNMP_UP &&
+	    state != FIP_ST_VNMP_CLAIM)
 		goto drop;
 
 	if (op == FIP_OP_LS) {
 		fcoe_ctlr_recv_els(fip, skb);	/* consumes skb */
 		return 0;
 	}
+
+	if (state != FIP_ST_ENABLED)
+		goto drop;
+
 	if (op == FIP_OP_DISC && sub == FIP_SC_ADV)
 		fcoe_ctlr_recv_adv(fip, skb);
 	else if (op == FIP_OP_CTRL && sub == FIP_SC_CLR_VLINK)
@@ -1302,7 +1369,8 @@ static void fcoe_ctlr_timer_work(struct work_struct *work)
 	unsigned long next_timer;
 
 	fip = container_of(work, struct fcoe_ctlr, timer_work);
-
+	if (fip->mode == FIP_MODE_VN2VN)
+		return fcoe_ctlr_vn_timeout(fip);
 	mutex_lock(&fip->ctlr_mutex);
 	if (fip->state == FIP_ST_DISABLED) {
 		mutex_unlock(&fip->ctlr_mutex);
@@ -1340,7 +1408,6 @@ static void fcoe_ctlr_timer_work(struct work_struct *work)
 			       "Starting FCF discovery.\n",
 			       fip->lp->host->host_no);
 			reset = 1;
-			schedule_work(&fip->timer_work);
 		}
 	}
 
@@ -1515,26 +1582,916 @@ u64 fcoe_wwn_from_mac(unsigned char mac[MAX_ADDR_LEN],
 EXPORT_SYMBOL_GPL(fcoe_wwn_from_mac);
 
 /**
+ * fcoe_ctlr_rport() - return the fcoe_rport for a given fc_rport_priv
+ * @rdata: libfc remote port
+ */
+static inline struct fcoe_rport *fcoe_ctlr_rport(struct fc_rport_priv *rdata)
+{
+	return (struct fcoe_rport *)(rdata + 1);
+}
+
+/**
+ * fcoe_ctlr_vn_send() - Send a FIP VN2VN Probe Request or Reply.
+ * @fip: The FCoE controller
+ * @sub: sub-opcode for probe request, reply, or advertisement.
+ * @dest: The destination Ethernet MAC address
+ * @min_len: minimum size of the Ethernet payload to be sent
+ */
+static void fcoe_ctlr_vn_send(struct fcoe_ctlr *fip,
+			      enum fip_vn2vn_subcode sub,
+			      const u8 *dest, size_t min_len)
+{
+	struct sk_buff *skb;
+	struct fip_frame {
+		struct ethhdr eth;
+		struct fip_header fip;
+		struct fip_mac_desc mac;
+		struct fip_wwn_desc wwnn;
+		struct fip_vn_desc vn;
+	} __attribute__((packed)) *frame;
+	struct fip_fc4_feat *ff;
+	struct fip_size_desc *size;
+	u32 fcp_feat;
+	size_t len;
+	size_t dlen;
+
+	len = sizeof(*frame);
+	dlen = 0;
+	if (sub == FIP_SC_VN_CLAIM_NOTIFY || sub == FIP_SC_VN_CLAIM_REP) {
+		dlen = sizeof(struct fip_fc4_feat) +
+		       sizeof(struct fip_size_desc);
+		len += dlen;
+	}
+	dlen += sizeof(frame->mac) + sizeof(frame->wwnn) + sizeof(frame->vn);
+	len = max(len, min_len + sizeof(struct ethhdr));
+
+	skb = dev_alloc_skb(len);
+	if (!skb)
+		return;
+
+	frame = (struct fip_frame *)skb->data;
+	memset(frame, 0, len);
+	memcpy(frame->eth.h_dest, dest, ETH_ALEN);
+	memcpy(frame->eth.h_source, fip->ctl_src_addr, ETH_ALEN);
+	frame->eth.h_proto = htons(ETH_P_FIP);
+
+	frame->fip.fip_ver = FIP_VER_ENCAPS(FIP_VER);
+	frame->fip.fip_op = htons(FIP_OP_VN2VN);
+	frame->fip.fip_subcode = sub;
+	frame->fip.fip_dl_len = htons(dlen / FIP_BPW);
+
+	frame->mac.fd_desc.fip_dtype = FIP_DT_MAC;
+	frame->mac.fd_desc.fip_dlen = sizeof(frame->mac) / FIP_BPW;
+	memcpy(frame->mac.fd_mac, fip->ctl_src_addr, ETH_ALEN);
+
+	frame->wwnn.fd_desc.fip_dtype = FIP_DT_NAME;
+	frame->wwnn.fd_desc.fip_dlen = sizeof(frame->wwnn) / FIP_BPW;
+	put_unaligned_be64(fip->lp->wwnn, &frame->wwnn.fd_wwn);
+
+	frame->vn.fd_desc.fip_dtype = FIP_DT_VN_ID;
+	frame->vn.fd_desc.fip_dlen = sizeof(frame->vn) / FIP_BPW;
+	hton24(frame->vn.fd_mac, FIP_VN_FC_MAP);
+	hton24(frame->vn.fd_mac + 3, fip->port_id);
+	hton24(frame->vn.fd_fc_id, fip->port_id);
+	put_unaligned_be64(fip->lp->wwpn, &frame->vn.fd_wwpn);
+
+	/*
+	 * For claims, add FC-4 features.
+	 * TBD: Add interface to get fc-4 types and features from libfc.
+	 */
+	if (sub == FIP_SC_VN_CLAIM_NOTIFY || sub == FIP_SC_VN_CLAIM_REP) {
+		ff = (struct fip_fc4_feat *)(frame + 1);
+		ff->fd_desc.fip_dtype = FIP_DT_FC4F;
+		ff->fd_desc.fip_dlen = sizeof(*ff) / FIP_BPW;
+		ff->fd_fts = fip->lp->fcts;
+
+		fcp_feat = 0;
+		if (fip->lp->service_params & FCP_SPPF_INIT_FCN)
+			fcp_feat |= FCP_FEAT_INIT;
+		if (fip->lp->service_params & FCP_SPPF_TARG_FCN)
+			fcp_feat |= FCP_FEAT_TARG;
+		fcp_feat <<= (FC_TYPE_FCP * 4) % 32;
+		ff->fd_ff.fd_feat[FC_TYPE_FCP * 4 / 32] = htonl(fcp_feat);
+
+		size = (struct fip_size_desc *)(ff + 1);
+		size->fd_desc.fip_dtype = FIP_DT_FCOE_SIZE;
+		size->fd_desc.fip_dlen = sizeof(*size) / FIP_BPW;
+		size->fd_size = htons(fcoe_ctlr_fcoe_size(fip));
+	}
+
+	skb_put(skb, len);
+	skb->protocol = htons(ETH_P_FIP);
+	skb_reset_mac_header(skb);
+	skb_reset_network_header(skb);
+
+	fip->send(fip, skb);
+}
+
+/**
+ * fcoe_ctlr_vn_rport_callback - Event handler for rport events.
+ * @lport: The lport which is receiving the event
+ * @rdata: remote port private data
+ * @event: The event that occured
+ *
+ * Locking Note:  The rport lock must not be held when calling this function.
+ */
+static void fcoe_ctlr_vn_rport_callback(struct fc_lport *lport,
+					struct fc_rport_priv *rdata,
+					enum fc_rport_event event)
+{
+	struct fcoe_ctlr *fip = lport->disc.priv;
+	struct fcoe_rport *frport = fcoe_ctlr_rport(rdata);
+
+	LIBFCOE_FIP_DBG(fip, "vn_rport_callback %x event %d\n",
+			rdata->ids.port_id, event);
+
+	mutex_lock(&fip->ctlr_mutex);
+	switch (event) {
+	case RPORT_EV_READY:
+		frport->login_count = 0;
+		break;
+	case RPORT_EV_LOGO:
+	case RPORT_EV_FAILED:
+	case RPORT_EV_STOP:
+		frport->login_count++;
+		if (frport->login_count > FCOE_CTLR_VN2VN_LOGIN_LIMIT) {
+			LIBFCOE_FIP_DBG(fip,
+					"rport FLOGI limited port_id %6.6x\n",
+					rdata->ids.port_id);
+			lport->tt.rport_logoff(rdata);
+		}
+		break;
+	default:
+		break;
+	}
+	mutex_unlock(&fip->ctlr_mutex);
+}
+
+static struct fc_rport_operations fcoe_ctlr_vn_rport_ops = {
+	.event_callback = fcoe_ctlr_vn_rport_callback,
+};
+
+/**
+ * fcoe_ctlr_disc_stop_locked() - stop discovery in VN2VN mode
+ * @fip: The FCoE controller
+ *
+ * Called with ctlr_mutex held.
+ */
+static void fcoe_ctlr_disc_stop_locked(struct fc_lport *lport)
+{
+	mutex_lock(&lport->disc.disc_mutex);
+	lport->disc.disc_callback = NULL;
+	mutex_unlock(&lport->disc.disc_mutex);
+}
+
+/**
+ * fcoe_ctlr_disc_stop() - stop discovery in VN2VN mode
+ * @fip: The FCoE controller
+ *
+ * Called through the local port template for discovery.
+ * Called without the ctlr_mutex held.
+ */
+static void fcoe_ctlr_disc_stop(struct fc_lport *lport)
+{
+	struct fcoe_ctlr *fip = lport->disc.priv;
+
+	mutex_lock(&fip->ctlr_mutex);
+	fcoe_ctlr_disc_stop_locked(lport);
+	mutex_unlock(&fip->ctlr_mutex);
+}
+
+/**
+ * fcoe_ctlr_disc_stop_final() - stop discovery for shutdown in VN2VN mode
+ * @fip: The FCoE controller
+ *
+ * Called through the local port template for discovery.
+ * Called without the ctlr_mutex held.
+ */
+static void fcoe_ctlr_disc_stop_final(struct fc_lport *lport)
+{
+	fcoe_ctlr_disc_stop(lport);
+	lport->tt.rport_flush_queue();
+	synchronize_rcu();
+}
+
+/**
+ * fcoe_ctlr_vn_restart() - VN2VN probe restart with new port_id
+ * @fip: The FCoE controller
+ *
+ * Called with fcoe_ctlr lock held.
+ */
+static void fcoe_ctlr_vn_restart(struct fcoe_ctlr *fip)
+{
+	unsigned long wait;
+	u32 port_id;
+
+	fcoe_ctlr_disc_stop_locked(fip->lp);
+
+	/*
+	 * Get proposed port ID.
+	 * If this is the first try after link up, use any previous port_id.
+	 * If there was none, use the low bits of the port_name.
+	 * On subsequent tries, get the next random one.
+	 * Don't use reserved IDs, use another non-zero value, just as random.
+	 */
+	port_id = fip->port_id;
+	if (fip->probe_tries)
+		port_id = prandom32(&fip->rnd_state) & 0xffff;
+	else if (!port_id)
+		port_id = fip->lp->wwpn & 0xffff;
+	if (!port_id || port_id == 0xffff)
+		port_id = 1;
+	fip->port_id = port_id;
+
+	if (fip->probe_tries < FIP_VN_RLIM_COUNT) {
+		fip->probe_tries++;
+		wait = random32() % FIP_VN_PROBE_WAIT;
+	} else
+		wait = FIP_VN_RLIM_INT;
+	mod_timer(&fip->timer, jiffies + msecs_to_jiffies(wait));
+	fcoe_ctlr_set_state(fip, FIP_ST_VNMP_START);
+}
+
+/**
+ * fcoe_ctlr_vn_start() - Start in VN2VN mode
+ * @fip: The FCoE controller
+ *
+ * Called with fcoe_ctlr lock held.
+ */
+static void fcoe_ctlr_vn_start(struct fcoe_ctlr *fip)
+{
+	fip->probe_tries = 0;
+	prandom32_seed(&fip->rnd_state, fip->lp->wwpn);
+	fcoe_ctlr_vn_restart(fip);
+}
+
+/**
+ * fcoe_ctlr_vn_parse - parse probe request or response
+ * @fip: The FCoE controller
+ * @skb: incoming packet
+ * @rdata: buffer for resulting parsed VN entry plus fcoe_rport
+ *
+ * Returns non-zero error number on error.
+ * Does not consume the packet.
+ */
+static int fcoe_ctlr_vn_parse(struct fcoe_ctlr *fip,
+			      struct sk_buff *skb,
+			      struct fc_rport_priv *rdata)
+{
+	struct fip_header *fiph;
+	struct fip_desc *desc = NULL;
+	struct fip_mac_desc *macd = NULL;
+	struct fip_wwn_desc *wwn = NULL;
+	struct fip_vn_desc *vn = NULL;
+	struct fip_size_desc *size = NULL;
+	struct fcoe_rport *frport;
+	size_t rlen;
+	size_t dlen;
+	u32 desc_mask = 0;
+	u32 dtype;
+	u8 sub;
+
+	memset(rdata, 0, sizeof(*rdata) + sizeof(*frport));
+	frport = fcoe_ctlr_rport(rdata);
+
+	fiph = (struct fip_header *)skb->data;
+	frport->flags = ntohs(fiph->fip_flags);
+
+	sub = fiph->fip_subcode;
+	switch (sub) {
+	case FIP_SC_VN_PROBE_REQ:
+	case FIP_SC_VN_PROBE_REP:
+	case FIP_SC_VN_BEACON:
+		desc_mask = BIT(FIP_DT_MAC) | BIT(FIP_DT_NAME) |
+			    BIT(FIP_DT_VN_ID);
+		break;
+	case FIP_SC_VN_CLAIM_NOTIFY:
+	case FIP_SC_VN_CLAIM_REP:
+		desc_mask = BIT(FIP_DT_MAC) | BIT(FIP_DT_NAME) |
+			    BIT(FIP_DT_VN_ID) | BIT(FIP_DT_FC4F) |
+			    BIT(FIP_DT_FCOE_SIZE);
+		break;
+	default:
+		LIBFCOE_FIP_DBG(fip, "vn_parse unknown subcode %u\n", sub);
+		return -EINVAL;
+	}
+
+	rlen = ntohs(fiph->fip_dl_len) * 4;
+	if (rlen + sizeof(*fiph) > skb->len)
+		return -EINVAL;
+
+	desc = (struct fip_desc *)(fiph + 1);
+	while (rlen > 0) {
+		dlen = desc->fip_dlen * FIP_BPW;
+		if (dlen < sizeof(*desc) || dlen > rlen)
+			return -EINVAL;
+
+		dtype = desc->fip_dtype;
+		if (dtype < 32) {
+			if (!(desc_mask & BIT(dtype))) {
+				LIBFCOE_FIP_DBG(fip,
+						"unexpected or duplicated desc "
+						"desc type %u in "
+						"FIP VN2VN subtype %u\n",
+						dtype, sub);
+				return -EINVAL;
+			}
+			desc_mask &= ~BIT(dtype);
+		}
+
+		switch (dtype) {
+		case FIP_DT_MAC:
+			if (dlen != sizeof(struct fip_mac_desc))
+				goto len_err;
+			macd = (struct fip_mac_desc *)desc;
+			if (!is_valid_ether_addr(macd->fd_mac)) {
+				LIBFCOE_FIP_DBG(fip,
+					"Invalid MAC addr %pM in FIP VN2VN\n",
+					 macd->fd_mac);
+				return -EINVAL;
+			}
+			memcpy(frport->enode_mac, macd->fd_mac, ETH_ALEN);
+			break;
+		case FIP_DT_NAME:
+			if (dlen != sizeof(struct fip_wwn_desc))
+				goto len_err;
+			wwn = (struct fip_wwn_desc *)desc;
+			rdata->ids.node_name = get_unaligned_be64(&wwn->fd_wwn);
+			break;
+		case FIP_DT_VN_ID:
+			if (dlen != sizeof(struct fip_vn_desc))
+				goto len_err;
+			vn = (struct fip_vn_desc *)desc;
+			memcpy(frport->vn_mac, vn->fd_mac, ETH_ALEN);
+			rdata->ids.port_id = ntoh24(vn->fd_fc_id);
+			rdata->ids.port_name = get_unaligned_be64(&vn->fd_wwpn);
+			break;
+		case FIP_DT_FC4F:
+			if (dlen != sizeof(struct fip_fc4_feat))
+				goto len_err;
+			break;
+		case FIP_DT_FCOE_SIZE:
+			if (dlen != sizeof(struct fip_size_desc))
+				goto len_err;
+			size = (struct fip_size_desc *)desc;
+			frport->fcoe_len = ntohs(size->fd_size);
+			break;
+		default:
+			LIBFCOE_FIP_DBG(fip, "unexpected descriptor type %x "
+					"in FIP probe\n", dtype);
+			/* standard says ignore unknown descriptors >= 128 */
+			if (dtype < FIP_DT_VENDOR_BASE)
+				return -EINVAL;
+			break;
+		}
+		desc = (struct fip_desc *)((char *)desc + dlen);
+		rlen -= dlen;
+	}
+	return 0;
+
+len_err:
+	LIBFCOE_FIP_DBG(fip, "FIP length error in descriptor type %x len %zu\n",
+			dtype, dlen);
+	return -EINVAL;
+}
+
+/**
+ * fcoe_ctlr_vn_send_claim() - send multicast FIP VN2VN Claim Notification.
+ * @fip: The FCoE controller
+ *
+ * Called with ctlr_mutex held.
+ */
+static void fcoe_ctlr_vn_send_claim(struct fcoe_ctlr *fip)
+{
+	fcoe_ctlr_vn_send(fip, FIP_SC_VN_CLAIM_NOTIFY, fcoe_all_vn2vn, 0);
+	fip->sol_time = jiffies;
+}
+
+/**
+ * fcoe_ctlr_vn_probe_req() - handle incoming VN2VN probe request.
+ * @fip: The FCoE controller
+ * @rdata: parsed remote port with frport from the probe request
+ *
+ * Called with ctlr_mutex held.
+ */
+static void fcoe_ctlr_vn_probe_req(struct fcoe_ctlr *fip,
+				   struct fc_rport_priv *rdata)
+{
+	struct fcoe_rport *frport = fcoe_ctlr_rport(rdata);
+
+	if (rdata->ids.port_id != fip->port_id)
+		return;
+
+	switch (fip->state) {
+	case FIP_ST_VNMP_CLAIM:
+	case FIP_ST_VNMP_UP:
+		fcoe_ctlr_vn_send(fip, FIP_SC_VN_PROBE_REP,
+				  frport->enode_mac, 0);
+		break;
+	case FIP_ST_VNMP_PROBE1:
+	case FIP_ST_VNMP_PROBE2:
+		/*
+		 * Decide whether to reply to the Probe.
+		 * Our selected address is never a "recorded" one, so
+		 * only reply if our WWPN is greater and the
+		 * Probe's REC bit is not set.
+		 * If we don't reply, we will change our address.
+		 */
+		if (fip->lp->wwpn > rdata->ids.port_name &&
+		    !(frport->flags & FIP_FL_REC_OR_P2P)) {
+			fcoe_ctlr_vn_send(fip, FIP_SC_VN_PROBE_REP,
+					  frport->enode_mac, 0);
+			break;
+		}
+		/* fall through */
+	case FIP_ST_VNMP_START:
+		fcoe_ctlr_vn_restart(fip);
+		break;
+	default:
+		break;
+	}
+}
+
+/**
+ * fcoe_ctlr_vn_probe_reply() - handle incoming VN2VN probe reply.
+ * @fip: The FCoE controller
+ * @rdata: parsed remote port with frport from the probe request
+ *
+ * Called with ctlr_mutex held.
+ */
+static void fcoe_ctlr_vn_probe_reply(struct fcoe_ctlr *fip,
+				   struct fc_rport_priv *rdata)
+{
+	if (rdata->ids.port_id != fip->port_id)
+		return;
+	switch (fip->state) {
+	case FIP_ST_VNMP_START:
+	case FIP_ST_VNMP_PROBE1:
+	case FIP_ST_VNMP_PROBE2:
+	case FIP_ST_VNMP_CLAIM:
+		fcoe_ctlr_vn_restart(fip);
+		break;
+	case FIP_ST_VNMP_UP:
+		fcoe_ctlr_vn_send_claim(fip);
+		break;
+	default:
+		break;
+	}
+}
+
+/**
+ * fcoe_ctlr_vn_add() - Add a VN2VN entry to the list, based on a claim reply.
+ * @fip: The FCoE controller
+ * @new: newly-parsed remote port with frport as a template for new rdata
+ *
+ * Called with ctlr_mutex held.
+ */
+static void fcoe_ctlr_vn_add(struct fcoe_ctlr *fip, struct fc_rport_priv *new)
+{
+	struct fc_lport *lport = fip->lp;
+	struct fc_rport_priv *rdata;
+	struct fc_rport_identifiers *ids;
+	struct fcoe_rport *frport;
+	u32 port_id;
+
+	port_id = new->ids.port_id;
+	if (port_id == fip->port_id)
+		return;
+
+	mutex_lock(&lport->disc.disc_mutex);
+	rdata = lport->tt.rport_create(lport, port_id);
+	if (!rdata) {
+		mutex_unlock(&lport->disc.disc_mutex);
+		return;
+	}
+
+	rdata->ops = &fcoe_ctlr_vn_rport_ops;
+	rdata->disc_id = lport->disc.disc_id;
+
+	ids = &rdata->ids;
+	if ((ids->port_name != -1 && ids->port_name != new->ids.port_name) ||
+	    (ids->node_name != -1 && ids->node_name != new->ids.node_name))
+		lport->tt.rport_logoff(rdata);
+	ids->port_name = new->ids.port_name;
+	ids->node_name = new->ids.node_name;
+	mutex_unlock(&lport->disc.disc_mutex);
+
+	frport = fcoe_ctlr_rport(rdata);
+	LIBFCOE_FIP_DBG(fip, "vn_add rport %6.6x %s\n",
+			port_id, frport->fcoe_len ? "old" : "new");
+	*frport = *fcoe_ctlr_rport(new);
+	frport->time = 0;
+}
+
+/**
+ * fcoe_ctlr_vn_lookup() - Find VN remote port's MAC address
+ * @fip: The FCoE controller
+ * @port_id:  The port_id of the remote VN_node
+ * @mac: buffer which will hold the VN_NODE destination MAC address, if found.
+ *
+ * Returns non-zero error if no remote port found.
+ */
+static int fcoe_ctlr_vn_lookup(struct fcoe_ctlr *fip, u32 port_id, u8 *mac)
+{
+	struct fc_lport *lport = fip->lp;
+	struct fc_rport_priv *rdata;
+	struct fcoe_rport *frport;
+	int ret = -1;
+
+	rcu_read_lock();
+	rdata = lport->tt.rport_lookup(lport, port_id);
+	if (rdata) {
+		frport = fcoe_ctlr_rport(rdata);
+		memcpy(mac, frport->enode_mac, ETH_ALEN);
+		ret = 0;
+	}
+	rcu_read_unlock();
+	return ret;
+}
+
+/**
+ * fcoe_ctlr_vn_claim_notify() - handle received FIP VN2VN Claim Notification
+ * @fip: The FCoE controller
+ * @new: newly-parsed remote port with frport as a template for new rdata
+ *
+ * Called with ctlr_mutex held.
+ */
+static void fcoe_ctlr_vn_claim_notify(struct fcoe_ctlr *fip,
+				      struct fc_rport_priv *new)
+{
+	struct fcoe_rport *frport = fcoe_ctlr_rport(new);
+
+	if (frport->flags & FIP_FL_REC_OR_P2P) {
+		fcoe_ctlr_vn_send(fip, FIP_SC_VN_PROBE_REQ, fcoe_all_vn2vn, 0);
+		return;
+	}
+	switch (fip->state) {
+	case FIP_ST_VNMP_START:
+	case FIP_ST_VNMP_PROBE1:
+	case FIP_ST_VNMP_PROBE2:
+		if (new->ids.port_id == fip->port_id)
+			fcoe_ctlr_vn_restart(fip);
+		break;
+	case FIP_ST_VNMP_CLAIM:
+	case FIP_ST_VNMP_UP:
+		if (new->ids.port_id == fip->port_id) {
+			if (new->ids.port_name > fip->lp->wwpn) {
+				fcoe_ctlr_vn_restart(fip);
+				break;
+			}
+			fcoe_ctlr_vn_send_claim(fip);
+			break;
+		}
+		fcoe_ctlr_vn_send(fip, FIP_SC_VN_CLAIM_REP, frport->enode_mac,
+				  min((u32)frport->fcoe_len,
+				      fcoe_ctlr_fcoe_size(fip)));
+		fcoe_ctlr_vn_add(fip, new);
+		break;
+	default:
+		break;
+	}
+}
+
+/**
+ * fcoe_ctlr_vn_claim_resp() - handle received Claim Response
+ * @fip: The FCoE controller that received the frame
+ * @new: newly-parsed remote port with frport from the Claim Response
+ *
+ * Called with ctlr_mutex held.
+ */
+static void fcoe_ctlr_vn_claim_resp(struct fcoe_ctlr *fip,
+				    struct fc_rport_priv *new)
+{
+	LIBFCOE_FIP_DBG(fip, "claim resp from from rport %x - state %s\n",
+			new->ids.port_id, fcoe_ctlr_state(fip->state));
+	if (fip->state == FIP_ST_VNMP_UP || fip->state == FIP_ST_VNMP_CLAIM)
+		fcoe_ctlr_vn_add(fip, new);
+}
+
+/**
+ * fcoe_ctlr_vn_beacon() - handle received beacon.
+ * @fip: The FCoE controller that received the frame
+ * @new: newly-parsed remote port with frport from the Beacon
+ *
+ * Called with ctlr_mutex held.
+ */
+static void fcoe_ctlr_vn_beacon(struct fcoe_ctlr *fip,
+				struct fc_rport_priv *new)
+{
+	struct fc_lport *lport = fip->lp;
+	struct fc_rport_priv *rdata;
+	struct fcoe_rport *frport;
+
+	frport = fcoe_ctlr_rport(new);
+	if (frport->flags & FIP_FL_REC_OR_P2P) {
+		fcoe_ctlr_vn_send(fip, FIP_SC_VN_PROBE_REQ, fcoe_all_vn2vn, 0);
+		return;
+	}
+	mutex_lock(&lport->disc.disc_mutex);
+	rdata = lport->tt.rport_lookup(lport, new->ids.port_id);
+	if (rdata)
+		kref_get(&rdata->kref);
+	mutex_unlock(&lport->disc.disc_mutex);
+	if (rdata) {
+		if (rdata->ids.node_name == new->ids.node_name &&
+		    rdata->ids.port_name == new->ids.port_name) {
+			frport = fcoe_ctlr_rport(rdata);
+			if (!frport->time && fip->state == FIP_ST_VNMP_UP)
+				lport->tt.rport_login(rdata);
+			frport->time = jiffies;
+		}
+		kref_put(&rdata->kref, lport->tt.rport_destroy);
+		return;
+	}
+	if (fip->state != FIP_ST_VNMP_UP)
+		return;
+
+	/*
+	 * Beacon from a new neighbor.
+	 * Send a claim notify if one hasn't been sent recently.
+	 * Don't add the neighbor yet.
+	 */
+	LIBFCOE_FIP_DBG(fip, "beacon from new rport %x. sending claim notify\n",
+			new->ids.port_id);
+	if (time_after(jiffies,
+		       fip->sol_time + msecs_to_jiffies(FIP_VN_ANN_WAIT)))
+		fcoe_ctlr_vn_send_claim(fip);
+}
+
+/**
+ * fcoe_ctlr_vn_age() - Check for VN_ports without recent beacons
+ * @fip: The FCoE controller
+ *
+ * Called with ctlr_mutex held.
+ * Called only in state FIP_ST_VNMP_UP.
+ * Returns the soonest time for next age-out or a time far in the future.
+ */
+static unsigned long fcoe_ctlr_vn_age(struct fcoe_ctlr *fip)
+{
+	struct fc_lport *lport = fip->lp;
+	struct fc_rport_priv *rdata;
+	struct fcoe_rport *frport;
+	unsigned long next_time;
+	unsigned long deadline;
+
+	next_time = jiffies + msecs_to_jiffies(FIP_VN_BEACON_INT * 10);
+	mutex_lock(&lport->disc.disc_mutex);
+	list_for_each_entry_rcu(rdata, &lport->disc.rports, peers) {
+		frport = fcoe_ctlr_rport(rdata);
+		if (!frport->time)
+			continue;
+		deadline = frport->time +
+			   msecs_to_jiffies(FIP_VN_BEACON_INT * 25 / 10);
+		if (time_after_eq(jiffies, deadline)) {
+			frport->time = 0;
+			LIBFCOE_FIP_DBG(fip,
+				"port %16.16llx fc_id %6.6x beacon expired\n",
+				rdata->ids.port_name, rdata->ids.port_id);
+			lport->tt.rport_logoff(rdata);
+		} else if (time_before(deadline, next_time))
+			next_time = deadline;
+	}
+	mutex_unlock(&lport->disc.disc_mutex);
+	return next_time;
+}
+
+/**
+ * fcoe_ctlr_vn_recv() - Receive a FIP frame
+ * @fip: The FCoE controller that received the frame
+ * @skb: The received FIP frame
+ *
+ * Returns non-zero if the frame is dropped.
+ * Always consumes the frame.
+ */
+static int fcoe_ctlr_vn_recv(struct fcoe_ctlr *fip, struct sk_buff *skb)
+{
+	struct fip_header *fiph;
+	enum fip_vn2vn_subcode sub;
+	union {
+		struct fc_rport_priv rdata;
+		struct fcoe_rport frport;
+	} buf;
+	int rc;
+
+	fiph = (struct fip_header *)skb->data;
+	sub = fiph->fip_subcode;
+
+	rc = fcoe_ctlr_vn_parse(fip, skb, &buf.rdata);
+	if (rc) {
+		LIBFCOE_FIP_DBG(fip, "vn_recv vn_parse error %d\n", rc);
+		goto drop;
+	}
+
+	mutex_lock(&fip->ctlr_mutex);
+	switch (sub) {
+	case FIP_SC_VN_PROBE_REQ:
+		fcoe_ctlr_vn_probe_req(fip, &buf.rdata);
+		break;
+	case FIP_SC_VN_PROBE_REP:
+		fcoe_ctlr_vn_probe_reply(fip, &buf.rdata);
+		break;
+	case FIP_SC_VN_CLAIM_NOTIFY:
+		fcoe_ctlr_vn_claim_notify(fip, &buf.rdata);
+		break;
+	case FIP_SC_VN_CLAIM_REP:
+		fcoe_ctlr_vn_claim_resp(fip, &buf.rdata);
+		break;
+	case FIP_SC_VN_BEACON:
+		fcoe_ctlr_vn_beacon(fip, &buf.rdata);
+		break;
+	default:
+		LIBFCOE_FIP_DBG(fip, "vn_recv unknown subcode %d\n", sub);
+		rc = -1;
+		break;
+	}
+	mutex_unlock(&fip->ctlr_mutex);
+drop:
+	kfree_skb(skb);
+	return rc;
+}
+
+/**
+ * fcoe_ctlr_disc_recv - discovery receive handler for VN2VN mode.
+ * @fip: The FCoE controller
+ *
+ * This should never be called since we don't see RSCNs or other
+ * fabric-generated ELSes.
+ */
+static void fcoe_ctlr_disc_recv(struct fc_seq *seq, struct fc_frame *fp,
+				struct fc_lport *lport)
+{
+	struct fc_seq_els_data rjt_data;
+
+	rjt_data.fp = NULL;
+	rjt_data.reason = ELS_RJT_UNSUP;
+	rjt_data.explan = ELS_EXPL_NONE;
+	lport->tt.seq_els_rsp_send(seq, ELS_LS_RJT, &rjt_data);
+	fc_frame_free(fp);
+}
+
+/**
+ * fcoe_ctlr_disc_recv - start discovery for VN2VN mode.
+ * @fip: The FCoE controller
+ *
+ * This sets a flag indicating that remote ports should be created
+ * and started for the peers we discover.  We use the disc_callback
+ * pointer as that flag.  Peers already discovered are created here.
+ *
+ * The lport lock is held during this call. The callback must be done
+ * later, without holding either the lport or discovery locks.
+ * The fcoe_ctlr lock may also be held during this call.
+ */
+static void fcoe_ctlr_disc_start(void (*callback)(struct fc_lport *,
+						  enum fc_disc_event),
+				 struct fc_lport *lport)
+{
+	struct fc_disc *disc = &lport->disc;
+	struct fcoe_ctlr *fip = disc->priv;
+
+	mutex_lock(&disc->disc_mutex);
+	disc->disc_callback = callback;
+	disc->disc_id = (disc->disc_id + 2) | 1;
+	disc->pending = 1;
+	schedule_work(&fip->timer_work);
+	mutex_unlock(&disc->disc_mutex);
+}
+
+/**
+ * fcoe_ctlr_vn_disc() - report FIP VN_port discovery results after claim state.
+ * @fip: The FCoE controller
+ *
+ * Starts the FLOGI and PLOGI login process to each discovered rport for which
+ * we've received at least one beacon.
+ * Performs the discovery complete callback.
+ */
+static void fcoe_ctlr_vn_disc(struct fcoe_ctlr *fip)
+{
+	struct fc_lport *lport = fip->lp;
+	struct fc_disc *disc = &lport->disc;
+	struct fc_rport_priv *rdata;
+	struct fcoe_rport *frport;
+	void (*callback)(struct fc_lport *, enum fc_disc_event);
+
+	mutex_lock(&disc->disc_mutex);
+	callback = disc->pending ? disc->disc_callback : NULL;
+	disc->pending = 0;
+	list_for_each_entry_rcu(rdata, &disc->rports, peers) {
+		frport = fcoe_ctlr_rport(rdata);
+		if (frport->time)
+			lport->tt.rport_login(rdata);
+	}
+	mutex_unlock(&disc->disc_mutex);
+	if (callback)
+		callback(lport, DISC_EV_SUCCESS);
+}
+
+/**
+ * fcoe_ctlr_vn_timeout - timer work function for VN2VN mode.
+ * @fip: The FCoE controller
+ */
+static void fcoe_ctlr_vn_timeout(struct fcoe_ctlr *fip)
+{
+	unsigned long next_time;
+	u8 mac[ETH_ALEN];
+	u32 new_port_id = 0;
+
+	mutex_lock(&fip->ctlr_mutex);
+	switch (fip->state) {
+	case FIP_ST_VNMP_START:
+		fcoe_ctlr_set_state(fip, FIP_ST_VNMP_PROBE1);
+		fcoe_ctlr_vn_send(fip, FIP_SC_VN_PROBE_REQ, fcoe_all_vn2vn, 0);
+		next_time = jiffies + msecs_to_jiffies(FIP_VN_PROBE_WAIT);
+		break;
+	case FIP_ST_VNMP_PROBE1:
+		fcoe_ctlr_set_state(fip, FIP_ST_VNMP_PROBE2);
+		fcoe_ctlr_vn_send(fip, FIP_SC_VN_PROBE_REQ, fcoe_all_vn2vn, 0);
+		next_time = jiffies + msecs_to_jiffies(FIP_VN_ANN_WAIT);
+		break;
+	case FIP_ST_VNMP_PROBE2:
+		fcoe_ctlr_set_state(fip, FIP_ST_VNMP_CLAIM);
+		new_port_id = fip->port_id;
+		hton24(mac, FIP_VN_FC_MAP);
+		hton24(mac + 3, new_port_id);
+		fip->update_mac(fip->lp, mac);
+		fcoe_ctlr_vn_send_claim(fip);
+		next_time = jiffies + msecs_to_jiffies(FIP_VN_ANN_WAIT);
+		break;
+	case FIP_ST_VNMP_CLAIM:
+		/*
+		 * This may be invoked either by starting discovery so don't
+		 * go to the next state unless it's been long enough.
+		 */
+		next_time = fip->sol_time + msecs_to_jiffies(FIP_VN_ANN_WAIT);
+		if (time_after_eq(jiffies, next_time)) {
+			fcoe_ctlr_set_state(fip, FIP_ST_VNMP_UP);
+			fcoe_ctlr_vn_send(fip, FIP_SC_VN_BEACON,
+					  fcoe_all_vn2vn, 0);
+			next_time = jiffies + msecs_to_jiffies(FIP_VN_ANN_WAIT);
+			fip->port_ka_time = next_time;
+		}
+		fcoe_ctlr_vn_disc(fip);
+		break;
+	case FIP_ST_VNMP_UP:
+		next_time = fcoe_ctlr_vn_age(fip);
+		if (time_after_eq(jiffies, fip->port_ka_time)) {
+			fcoe_ctlr_vn_send(fip, FIP_SC_VN_BEACON,
+					  fcoe_all_vn2vn, 0);
+			fip->port_ka_time = jiffies +
+				 msecs_to_jiffies(FIP_VN_BEACON_INT +
+					(random32() % FIP_VN_BEACON_FUZZ));
+		}
+		if (time_before(fip->port_ka_time, next_time))
+			next_time = fip->port_ka_time;
+		break;
+	case FIP_ST_LINK_WAIT:
+		goto unlock;
+	default:
+		WARN(1, "unexpected state %d", fip->state);
+		goto unlock;
+	}
+	mod_timer(&fip->timer, next_time);
+unlock:
+	mutex_unlock(&fip->ctlr_mutex);
+
+	/* If port ID is new, notify local port after dropping ctlr_mutex */
+	if (new_port_id)
+		fc_lport_set_local_id(fip->lp, new_port_id);
+}
+
+/**
  * fcoe_libfc_config() - Sets up libfc related properties for local port
  * @lp: The local port to configure libfc for
+ * @fip: The FCoE controller in use by the local port
  * @tt: The libfc function template
+ * @init_fcp: If non-zero, the FCP portion of libfc should be initialized
  *
  * Returns : 0 for success
  */
-int fcoe_libfc_config(struct fc_lport *lport,
-		      struct libfc_function_template *tt)
+int fcoe_libfc_config(struct fc_lport *lport, struct fcoe_ctlr *fip,
+		      const struct libfc_function_template *tt, int init_fcp)
 {
 	/* Set the function pointers set by the LLDD */
 	memcpy(&lport->tt, tt, sizeof(*tt));
-	if (fc_fcp_init(lport))
+	if (init_fcp && fc_fcp_init(lport))
 		return -ENOMEM;
 	fc_exch_init(lport);
 	fc_elsct_init(lport);
 	fc_lport_init(lport);
+	if (fip->mode == FIP_MODE_VN2VN)
+		lport->rport_priv_size = sizeof(struct fcoe_rport);
 	fc_rport_init(lport);
-	fc_disc_init(lport);
-
+	if (fip->mode == FIP_MODE_VN2VN) {
+		lport->point_to_multipoint = 1;
+		lport->tt.disc_recv_req = fcoe_ctlr_disc_recv;
+		lport->tt.disc_start = fcoe_ctlr_disc_start;
+		lport->tt.disc_stop = fcoe_ctlr_disc_stop;
+		lport->tt.disc_stop_final = fcoe_ctlr_disc_stop_final;
+		mutex_init(&lport->disc.disc_mutex);
+		INIT_LIST_HEAD(&lport->disc.rports);
+		lport->disc.priv = fip;
+	} else {
+		fc_disc_init(lport);
+	}
 	return 0;
 }
 EXPORT_SYMBOL_GPL(fcoe_libfc_config);
-
diff --git a/drivers/scsi/fnic/fnic_main.c b/drivers/scsi/fnic/fnic_main.c
index d0fe1c3..9eb7a9e 100644
--- a/drivers/scsi/fnic/fnic_main.c
+++ b/drivers/scsi/fnic/fnic_main.c
@@ -673,7 +673,6 @@ static int __devinit fnic_probe(struct pci_dev *pdev,
 	/* Start local port initiatialization */
 
 	lp->link_up = 0;
-	lp->tt = fnic_transport_template;
 
 	lp->max_retry_count = fnic->config.flogi_retries;
 	lp->max_rport_retry_count = fnic->config.plogi_retries;
@@ -689,11 +688,7 @@ 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_lport_init(lp);
-	fc_exch_init(lp);
-	fc_elsct_init(lp);
-	fc_rport_init(lp);
-	fc_disc_init(lp);
+	fcoe_libfc_config(lp, &fnic->ctlr, &fnic_transport_template, 0);
 
 	if (!fc_exch_mgr_alloc(lp, FC_CLASS_3, FCPIO_HOST_EXCH_RANGE_START,
 			       FCPIO_HOST_EXCH_RANGE_END, NULL)) {
diff --git a/include/scsi/libfcoe.h b/include/scsi/libfcoe.h
index 1a84a31..06f1b5a 100644
--- a/include/scsi/libfcoe.h
+++ b/include/scsi/libfcoe.h
@@ -26,6 +26,7 @@
 #include <linux/netdevice.h>
 #include <linux/skbuff.h>
 #include <linux/workqueue.h>
+#include <linux/random.h>
 #include <scsi/fc/fc_fcoe.h>
 #include <scsi/libfc.h>
 
@@ -37,6 +38,7 @@
 #define FCOE_CTLR_START_DELAY	2000	/* mS after first adv. to choose FCF */
 #define FCOE_CTRL_SOL_TOV	2000	/* min. solicitation interval (mS) */
 #define FCOE_CTLR_FCF_LIMIT	20	/* max. number of FCF entries */
+#define FCOE_CTLR_VN2VN_LOGIN_LIMIT 3	/* max. VN2VN rport login retries */
 
 /**
  * enum fip_state - internal state of FCoE controller.
@@ -45,6 +47,11 @@
  * @FIP_ST_AUTO:	determining whether to use FIP or non-FIP mode.
  * @FIP_ST_NON_FIP:	non-FIP mode selected.
  * @FIP_ST_ENABLED:	FIP mode selected.
+ * @FIP_ST_VNMP_START:	VN2VN multipath mode start, wait
+ * @FIP_ST_VNMP_PROBE1:	VN2VN sent first probe, listening
+ * @FIP_ST_VNMP_PROBE2:	VN2VN sent second probe, listening
+ * @FIP_ST_VNMP_CLAIM:	VN2VN sent claim, waiting for responses
+ * @FIP_ST_VNMP_UP:	VN2VN multipath mode operation
  */
 enum fip_state {
 	FIP_ST_DISABLED,
@@ -52,6 +59,11 @@ enum fip_state {
 	FIP_ST_AUTO,
 	FIP_ST_NON_FIP,
 	FIP_ST_ENABLED,
+	FIP_ST_VNMP_START,
+	FIP_ST_VNMP_PROBE1,
+	FIP_ST_VNMP_PROBE2,
+	FIP_ST_VNMP_CLAIM,
+	FIP_ST_VNMP_UP,
 };
 
 /*
@@ -62,6 +74,7 @@ enum fip_state {
 #define FIP_MODE_AUTO		FIP_ST_AUTO
 #define FIP_MODE_NON_FIP	FIP_ST_NON_FIP
 #define FIP_MODE_FABRIC		FIP_ST_ENABLED
+#define FIP_MODE_VN2VN		FIP_ST_VNMP_START
 
 /**
  * struct fcoe_ctlr - FCoE Controller and FIP state
@@ -79,11 +92,14 @@ enum fip_state {
  * @timer_work:	   &work_struct for doing keep-alives and resets.
  * @recv_work:	   &work_struct for receiving FIP frames.
  * @fip_recv_list: list of received FIP frames.
+ * @rnd_state:	   state for pseudo-random number generator.
+ * @port_id:	   proposed or selected local-port ID.
  * @user_mfs:	   configured maximum FC frame size, including FC header.
  * @flogi_oxid:    exchange ID of most recent fabric login.
  * @flogi_count:   number of FLOGI attempts in AUTO mode.
  * @map_dest:	   use the FC_MAP mode for destination MAC addresses.
  * @spma:	   supports SPMA server-provided MACs mode
+ * @probe_tries:   number of FC_IDs probed
  * @dest_addr:	   MAC address of the selected FC forwarder.
  * @ctl_src_addr:  the native MAC address of our local port.
  * @send:	   LLD-supplied function to handle sending FIP Ethernet frames
@@ -110,11 +126,16 @@ struct fcoe_ctlr {
 	struct work_struct timer_work;
 	struct work_struct recv_work;
 	struct sk_buff_head fip_recv_list;
+
+	struct rnd_state rnd_state;
+	u32 port_id;
+
 	u16 user_mfs;
 	u16 flogi_oxid;
 	u8 flogi_count;
 	u8 map_dest;
 	u8 spma;
+	u8 probe_tries;
 	u8 dest_addr[ETH_ALEN];
 	u8 ctl_src_addr[ETH_ALEN];
 
@@ -160,6 +181,24 @@ struct fcoe_fcf {
 	u8 fd_flags:1;
 };
 
+/**
+ * struct fcoe_rport - VN2VN remote port
+ * @time:	time of create or last beacon packet received from node
+ * @fcoe_len:	max FCoE frame size, not including VLAN or Ethernet headers
+ * @flags:	flags from probe or claim
+ * @login_count: number of unsuccessful rport logins to this port
+ * @enode_mac:	E_Node control MAC address
+ * @vn_mac:	VN_Node assigned MAC address for data
+ */
+struct fcoe_rport {
+	unsigned long time;
+	u16 fcoe_len;
+	u16 flags;
+	u8 login_count;
+	u8 enode_mac[ETH_ALEN];
+	u8 vn_mac[ETH_ALEN];
+};
+
 /* FIP API functions */
 void fcoe_ctlr_init(struct fcoe_ctlr *, enum fip_state);
 void fcoe_ctlr_destroy(struct fcoe_ctlr *);
@@ -172,7 +211,8 @@ int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *, struct fc_lport *,
 
 /* libfcoe funcs */
 u64 fcoe_wwn_from_mac(unsigned char mac[], unsigned int, unsigned int);
-int fcoe_libfc_config(struct fc_lport *, struct libfc_function_template *);
+int fcoe_libfc_config(struct fc_lport *, struct fcoe_ctlr *,
+		      const struct libfc_function_template *, int init_fcp);
 
 /**
  * is_fip_mode() - returns true if FIP mode selected.


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 17/28] libfcoe: Fix FIP ELS encapsulation details for FLOGI responses
  2010-07-20 22:19 [PATCH 00/28] libfc, libfcoe and fcoe updates for scsi-misc Robert Love
                   ` (15 preceding siblings ...)
  2010-07-20 22:20 ` [PATCH 16/28] libfcoe: fcoe: fnic: add FIP VN2VN point-to-multipoint support Robert Love
@ 2010-07-20 22:20 ` Robert Love
  2010-07-20 22:20 ` [PATCH 18/28] fcoe libfcoe: use correct FC-MAP for VN2VN mode Robert Love
                   ` (10 subsequent siblings)
  27 siblings, 0 replies; 34+ messages in thread
From: Robert Love @ 2010-07-20 22:20 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi; +Cc: Joe Eykholt

From: Joe Eykholt <jeykholt@cisco.com>

When sending a FLOGI LS_ACC, which we only do in point-to-multipoint
mode, the MAC descriptor should have the granted MAC set to
0x0efd00 || D_ID.

When sending an LS_RJT, there should be no MAC descriptor.

When sending either an LS_ACC or LS_RJT, the subcode should indicate
an reply, not a request.

Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---
 drivers/scsi/fcoe/libfcoe.c |   42 ++++++++++++++++++++++++++++--------------
 1 files changed, 28 insertions(+), 14 deletions(-)

diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c
index 79df78f..4865e81 100644
--- a/drivers/scsi/fcoe/libfcoe.c
+++ b/drivers/scsi/fcoe/libfcoe.c
@@ -469,11 +469,15 @@ static int fcoe_ctlr_encaps(struct fcoe_ctlr *fip, struct fc_lport *lport,
 		struct fip_header fip;
 		struct fip_encaps encaps;
 	} __attribute__((packed)) *cap;
+	struct fc_frame_header *fh;
 	struct fip_mac_desc *mac;
 	struct fcoe_fcf *fcf;
 	size_t dlen;
 	u16 fip_flags;
+	u8 op;
 
+	fh = (struct fc_frame_header *)skb->data;
+	op = *(u8 *)(fh + 1);
 	dlen = sizeof(struct fip_encaps) + skb->len;	/* len before push */
 	cap = (struct fip_encaps_head *)skb_push(skb, sizeof(*cap));
 	memset(cap, 0, sizeof(*cap));
@@ -481,6 +485,7 @@ static int fcoe_ctlr_encaps(struct fcoe_ctlr *fip, struct fc_lport *lport,
 	if (lport->point_to_multipoint) {
 		if (fcoe_ctlr_vn_lookup(fip, d_id, cap->eth.h_dest))
 			return -ENODEV;
+		fip_flags = 0;
 	} else {
 		fcf = fip->sel_fcf;
 		if (!fcf)
@@ -497,26 +502,35 @@ static int fcoe_ctlr_encaps(struct fcoe_ctlr *fip, struct fc_lport *lport,
 
 	cap->fip.fip_ver = FIP_VER_ENCAPS(FIP_VER);
 	cap->fip.fip_op = htons(FIP_OP_LS);
-	cap->fip.fip_subcode = FIP_SC_REQ;
-	cap->fip.fip_dl_len = htons((dlen + sizeof(*mac)) / FIP_BPW);
+	if (op == ELS_LS_ACC || op == ELS_LS_RJT)
+		cap->fip.fip_subcode = FIP_SC_REP;
+	else
+		cap->fip.fip_subcode = FIP_SC_REQ;
 	cap->fip.fip_flags = htons(fip_flags);
 
 	cap->encaps.fd_desc.fip_dtype = dtype;
 	cap->encaps.fd_desc.fip_dlen = dlen / FIP_BPW;
 
-	mac = (struct fip_mac_desc *)skb_put(skb, sizeof(*mac));
-	memset(mac, 0, sizeof(*mac));
-	mac->fd_desc.fip_dtype = FIP_DT_MAC;
-	mac->fd_desc.fip_dlen = sizeof(*mac) / FIP_BPW;
-	if (dtype != FIP_DT_FLOGI && dtype != FIP_DT_FDISC) {
-		memcpy(mac->fd_mac, fip->get_src_addr(lport), ETH_ALEN);
-	} else if (fip_flags & FIP_FL_SPMA) {
-		LIBFCOE_FIP_DBG(fip, "FLOGI/FDISC sent with SPMA\n");
-		memcpy(mac->fd_mac, fip->ctl_src_addr, ETH_ALEN);
-	} else {
-		LIBFCOE_FIP_DBG(fip, "FLOGI/FDISC sent with FPMA\n");
-		/* FPMA only FLOGI must leave the MAC desc set to all 0s */
+	if (op != ELS_LS_RJT) {
+		dlen += sizeof(*mac);
+		mac = (struct fip_mac_desc *)skb_put(skb, sizeof(*mac));
+		memset(mac, 0, sizeof(*mac));
+		mac->fd_desc.fip_dtype = FIP_DT_MAC;
+		mac->fd_desc.fip_dlen = sizeof(*mac) / FIP_BPW;
+		if (dtype != FIP_DT_FLOGI && dtype != FIP_DT_FDISC) {
+			memcpy(mac->fd_mac, fip->get_src_addr(lport), ETH_ALEN);
+		} else if (fip->mode == FIP_MODE_VN2VN) {
+			hton24(mac->fd_mac, FIP_VN_FC_MAP);
+			hton24(mac->fd_mac + 3, fip->port_id);
+		} else if (fip_flags & FIP_FL_SPMA) {
+			LIBFCOE_FIP_DBG(fip, "FLOGI/FDISC sent with SPMA\n");
+			memcpy(mac->fd_mac, fip->ctl_src_addr, ETH_ALEN);
+		} else {
+			LIBFCOE_FIP_DBG(fip, "FLOGI/FDISC sent with FPMA\n");
+			/* FPMA only FLOGI.  Must leave the MAC desc zeroed. */
+		}
 	}
+	cap->fip.fip_dl_len = htons(dlen / FIP_BPW);
 
 	skb->protocol = htons(ETH_P_FIP);
 	skb_reset_mac_header(skb);


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 18/28] fcoe libfcoe: use correct FC-MAP for VN2VN mode
  2010-07-20 22:19 [PATCH 00/28] libfc, libfcoe and fcoe updates for scsi-misc Robert Love
                   ` (16 preceding siblings ...)
  2010-07-20 22:20 ` [PATCH 17/28] libfcoe: Fix FIP ELS encapsulation details for FLOGI responses Robert Love
@ 2010-07-20 22:20 ` Robert Love
  2010-07-20 22:20 ` [PATCH 19/28] fcoe: config via separate create_vn2vn module parameter Robert Love
                   ` (9 subsequent siblings)
  27 siblings, 0 replies; 34+ messages in thread
From: Robert Love @ 2010-07-20 22:20 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi; +Cc: Joe Eykholt

From: Joe Eykholt <jeykholt@cisco.com>

In VN2VN mode, map_dest means to use the default VN2VN OUI.
Change code that uses the default FCoE OUI to use the one
set in the fcoe_ctlr struct.

Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---
 drivers/scsi/fcoe/fcoe.c    |    6 ++----
 drivers/scsi/fcoe/libfcoe.c |   24 ++++++++++++++++++++----
 2 files changed, 22 insertions(+), 8 deletions(-)

diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c
index 216aba3..4d3e70a 100644
--- a/drivers/scsi/fcoe/fcoe.c
+++ b/drivers/scsi/fcoe/fcoe.c
@@ -1534,11 +1534,9 @@ int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp)
 	/* fill up mac and fcoe headers */
 	eh = eth_hdr(skb);
 	eh->h_proto = htons(ETH_P_FCOE);
+	memcpy(eh->h_dest, fcoe->ctlr.dest_addr, ETH_ALEN);
 	if (fcoe->ctlr.map_dest)
-		fc_fcoe_set_mac(eh->h_dest, fh->fh_d_id);
-	else
-		/* insert GW address */
-		memcpy(eh->h_dest, fcoe->ctlr.dest_addr, ETH_ALEN);
+		memcpy(eh->h_dest + 3, fh->fh_d_id, 3);
 
 	if (unlikely(fcoe->ctlr.flogi_oxid != FC_XID_UNKNOWN))
 		memcpy(eh->h_source, fcoe->ctlr.ctl_src_addr, ETH_ALEN);
diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c
index 4865e81..4de8ced 100644
--- a/drivers/scsi/fcoe/libfcoe.c
+++ b/drivers/scsi/fcoe/libfcoe.c
@@ -153,6 +153,20 @@ static inline int fcoe_ctlr_fcf_usable(struct fcoe_fcf *fcf)
 }
 
 /**
+ * fcoe_ctlr_map_dest() - Set flag and OUI for mapping destination addresses
+ * @fip: The FCoE controller
+ */
+static void fcoe_ctlr_map_dest(struct fcoe_ctlr *fip)
+{
+	if (fip->mode == FIP_MODE_VN2VN)
+		hton24(fip->dest_addr, FIP_VN_FC_MAP);
+	else
+		hton24(fip->dest_addr, FIP_DEF_FC_MAP);
+	hton24(fip->dest_addr + 3, 0);
+	fip->map_dest = 1;
+}
+
+/**
  * fcoe_ctlr_init() - Initialize the FCoE Controller instance
  * @fip: The FCoE controller to initialize
  */
@@ -345,7 +359,7 @@ static void fcoe_ctlr_reset(struct fcoe_ctlr *fip)
 	fip->port_ka_time = 0;
 	fip->sol_time = 0;
 	fip->flogi_oxid = FC_XID_UNKNOWN;
-	fip->map_dest = 0;
+	fcoe_ctlr_map_dest(fip);
 }
 
 /**
@@ -573,11 +587,11 @@ int fcoe_ctlr_els_send(struct fcoe_ctlr *fip, struct fc_lport *lport,
 			fip->flogi_count++;
 			if (fip->flogi_count < 3)
 				goto drop;
-			fip->map_dest = 1;
+			fcoe_ctlr_map_dest(fip);
 			return 0;
 		}
 		if (fip->state == FIP_ST_NON_FIP)
-			fip->map_dest = 1;
+			fcoe_ctlr_map_dest(fip);
 	}
 
 	if (fip->state == FIP_ST_NON_FIP)
@@ -1411,6 +1425,7 @@ static void fcoe_ctlr_timer_work(struct work_struct *work)
 			       "Fibre-Channel Forwarder MAC %pM\n",
 			       fip->lp->host->host_no, sel->fcf_mac);
 			memcpy(fip->dest_addr, sel->fcf_mac, ETH_ALEN);
+			fip->map_dest = 0;
 			fip->port_ka_time = jiffies +
 				msecs_to_jiffies(FIP_VN_KA_PERIOD);
 			fip->ctlr_ka_time = jiffies + sel->fka_period;
@@ -1527,7 +1542,7 @@ int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *fip, struct fc_lport *lport,
 		 * Otherwise we use the FCoE gateway addr
 		 */
 		if (!compare_ether_addr(sa, (u8[6])FC_FCOE_FLOGI_MAC)) {
-			fip->map_dest = 1;
+			fcoe_ctlr_map_dest(fip);
 		} else {
 			memcpy(fip->dest_addr, sa, ETH_ALEN);
 			fip->map_dest = 0;
@@ -2426,6 +2441,7 @@ static void fcoe_ctlr_vn_timeout(struct fcoe_ctlr *fip)
 		new_port_id = fip->port_id;
 		hton24(mac, FIP_VN_FC_MAP);
 		hton24(mac + 3, new_port_id);
+		fcoe_ctlr_map_dest(fip);
 		fip->update_mac(fip->lp, mac);
 		fcoe_ctlr_vn_send_claim(fip);
 		next_time = jiffies + msecs_to_jiffies(FIP_VN_ANN_WAIT);


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 19/28] fcoe: config via separate create_vn2vn module parameter
  2010-07-20 22:19 [PATCH 00/28] libfc, libfcoe and fcoe updates for scsi-misc Robert Love
                   ` (17 preceding siblings ...)
  2010-07-20 22:20 ` [PATCH 18/28] fcoe libfcoe: use correct FC-MAP for VN2VN mode Robert Love
@ 2010-07-20 22:20 ` Robert Love
  2010-07-20 22:20 ` [PATCH 20/28] libfc: eliminate rport LOGO state Robert Love
                   ` (8 subsequent siblings)
  27 siblings, 0 replies; 34+ messages in thread
From: Robert Love @ 2010-07-20 22:20 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi; +Cc: Joe Eykholt

From: Joe Eykholt <jeykholt@cisco.com>

Add module parameter create_vn2vn that behaves like the create
parameter except that the new instance is created in FIP vn2vn mode.

This can be replaced once we change create to allow modifying
per-instance attributes before starting the instance.

Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---
 drivers/scsi/fcoe/fcoe.c |   16 ++++++++++++----
 1 files changed, 12 insertions(+), 4 deletions(-)

diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c
index 4d3e70a..ab6ea60 100644
--- a/drivers/scsi/fcoe/fcoe.c
+++ b/drivers/scsi/fcoe/fcoe.c
@@ -117,9 +117,14 @@ static void fcoe_recv_frame(struct sk_buff *skb);
 
 static void fcoe_get_lesb(struct fc_lport *, struct fc_els_lesb *);
 
-module_param_call(create, fcoe_create, NULL, NULL, S_IWUSR);
+module_param_call(create, fcoe_create, NULL, (void *)FIP_MODE_AUTO, S_IWUSR);
 __MODULE_PARM_TYPE(create, "string");
 MODULE_PARM_DESC(create, " Creates fcoe instance on a ethernet interface");
+module_param_call(create_vn2vn, fcoe_create, NULL,
+		  (void *)FIP_MODE_VN2VN, S_IWUSR);
+__MODULE_PARM_TYPE(create_vn2vn, "string");
+MODULE_PARM_DESC(create_vn2vn, " Creates a VN_node to VN_node FCoE instance "
+		 "on an Ethernet interface");
 module_param_call(destroy, fcoe_destroy, NULL, NULL, S_IWUSR);
 __MODULE_PARM_TYPE(destroy, "string");
 MODULE_PARM_DESC(destroy, " Destroys fcoe instance on a ethernet interface");
@@ -341,10 +346,12 @@ static int fcoe_interface_setup(struct fcoe_interface *fcoe,
 /**
  * fcoe_interface_create() - Create a FCoE interface on a net device
  * @netdev: The net device to create the FCoE interface on
+ * @fip_mode: The mode to use for FIP
  *
  * Returns: pointer to a struct fcoe_interface or NULL on error
  */
-static struct fcoe_interface *fcoe_interface_create(struct net_device *netdev)
+static struct fcoe_interface *fcoe_interface_create(struct net_device *netdev,
+						    enum fip_state fip_mode)
 {
 	struct fcoe_interface *fcoe;
 	int err;
@@ -361,7 +368,7 @@ static struct fcoe_interface *fcoe_interface_create(struct net_device *netdev)
 	/*
 	 * Initialize FIP.
 	 */
-	fcoe_ctlr_init(&fcoe->ctlr, FIP_MODE_AUTO);
+	fcoe_ctlr_init(&fcoe->ctlr, fip_mode);
 	fcoe->ctlr.send = fcoe_fip_send;
 	fcoe->ctlr.update_mac = fcoe_update_src_mac;
 	fcoe->ctlr.get_src_addr = fcoe_get_src_mac;
@@ -2088,6 +2095,7 @@ static void fcoe_destroy_work(struct work_struct *work)
  */
 static int fcoe_create(const char *buffer, struct kernel_param *kp)
 {
+	enum fip_state fip_mode = (enum fip_state)(long)kp->arg;
 	int rc;
 	struct fcoe_interface *fcoe;
 	struct fc_lport *lport;
@@ -2129,7 +2137,7 @@ static int fcoe_create(const char *buffer, struct kernel_param *kp)
 		goto out_putdev;
 	}
 
-	fcoe = fcoe_interface_create(netdev);
+	fcoe = fcoe_interface_create(netdev, fip_mode);
 	if (!fcoe) {
 		rc = -ENOMEM;
 		goto out_putdev;


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 20/28] libfc: eliminate rport LOGO state
  2010-07-20 22:19 [PATCH 00/28] libfc, libfcoe and fcoe updates for scsi-misc Robert Love
                   ` (18 preceding siblings ...)
  2010-07-20 22:20 ` [PATCH 19/28] fcoe: config via separate create_vn2vn module parameter Robert Love
@ 2010-07-20 22:20 ` Robert Love
  2010-07-20 22:20 ` [PATCH 21/28] libfc: add fc_frame_sid() and fc_frame_did() functions Robert Love
                   ` (7 subsequent siblings)
  27 siblings, 0 replies; 34+ messages in thread
From: Robert Love @ 2010-07-20 22:20 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi; +Cc: Joe Eykholt

From: Joe Eykholt <jeykholt@cisco.com>

The LOGO state hasn't been used in a while, except in a brief
transition to DELETE state while holding the rport mutex.
All port LOGO responses have been ignored as well as any timeout
if we don't get a response.

So this patch just removes LOGO state and simplifies the response handler.

Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---
 drivers/scsi/libfc/fc_rport.c |   88 ++++++++++-------------------------------
 include/scsi/libfc.h          |    2 -
 2 files changed, 22 insertions(+), 68 deletions(-)

diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c
index 4d6adf2..c06d63e 100644
--- a/drivers/scsi/libfc/fc_rport.c
+++ b/drivers/scsi/libfc/fc_rport.c
@@ -89,7 +89,6 @@ static const char *fc_rport_state_names[] = {
 	[RPORT_ST_PRLI] = "PRLI",
 	[RPORT_ST_RTV] = "RTV",
 	[RPORT_ST_READY] = "Ready",
-	[RPORT_ST_LOGO] = "LOGO",
 	[RPORT_ST_ADISC] = "ADISC",
 	[RPORT_ST_DELETE] = "Delete",
 };
@@ -514,9 +513,6 @@ static void fc_rport_timeout(struct work_struct *work)
 	case RPORT_ST_RTV:
 		fc_rport_enter_rtv(rdata);
 		break;
-	case RPORT_ST_LOGO:
-		fc_rport_enter_logo(rdata);
-		break;
 	case RPORT_ST_ADISC:
 		fc_rport_enter_adisc(rdata);
 		break;
@@ -547,7 +543,6 @@ static void fc_rport_error(struct fc_rport_priv *rdata, struct fc_frame *fp)
 	switch (rdata->rp_state) {
 	case RPORT_ST_FLOGI:
 	case RPORT_ST_PLOGI:
-	case RPORT_ST_LOGO:
 		rdata->flags &= ~FC_RP_STARTED;
 		fc_rport_enter_delete(rdata, RPORT_EV_FAILED);
 		break;
@@ -791,7 +786,6 @@ static void fc_rport_recv_flogi_req(struct fc_lport *lport,
 
 	switch (rdata->rp_state) {
 	case RPORT_ST_INIT:
-	case RPORT_ST_LOGO:
 	case RPORT_ST_DELETE:
 		mutex_unlock(&rdata->rp_mutex);
 		rjt_data.reason = ELS_RJT_FIP;
@@ -1037,52 +1031,6 @@ err:
 }
 
 /**
- * fc_rport_logo_resp() - Handler for logout (LOGO) responses
- * @sp:	       The sequence the LOGO was on
- * @fp:	       The LOGO response frame
- * @rdata_arg: The remote port that sent the LOGO response
- *
- * Locking Note: This function will be called without the rport lock
- * held, but it will lock, call an _enter_* function or fc_rport_error
- * and then unlock the rport.
- */
-static void fc_rport_logo_resp(struct fc_seq *sp, struct fc_frame *fp,
-			       void *rdata_arg)
-{
-	struct fc_rport_priv *rdata = rdata_arg;
-	u8 op;
-
-	mutex_lock(&rdata->rp_mutex);
-
-	FC_RPORT_DBG(rdata, "Received a LOGO %s\n", fc_els_resp_type(fp));
-
-	if (rdata->rp_state != RPORT_ST_LOGO) {
-		FC_RPORT_DBG(rdata, "Received a LOGO response, but in state "
-			     "%s\n", fc_rport_state(rdata));
-		if (IS_ERR(fp))
-			goto err;
-		goto out;
-	}
-
-	if (IS_ERR(fp)) {
-		fc_rport_error_retry(rdata, fp);
-		goto err;
-	}
-
-	op = fc_frame_payload_op(fp);
-	if (op != ELS_LS_ACC)
-		FC_RPORT_DBG(rdata, "Bad ELS response op %x for LOGO command\n",
-			     op);
-	fc_rport_enter_delete(rdata, RPORT_EV_LOGO);
-
-out:
-	fc_frame_free(fp);
-err:
-	mutex_unlock(&rdata->rp_mutex);
-	kref_put(&rdata->kref, rdata->local_port->tt.rport_destroy);
-}
-
-/**
  * fc_rport_enter_prli() - Send Process Login (PRLI) request
  * @rdata: The remote port to send the PRLI request to
  *
@@ -1224,6 +1172,24 @@ static void fc_rport_enter_rtv(struct fc_rport_priv *rdata)
 }
 
 /**
+ * fc_rport_logo_resp() - Handler for logout (LOGO) responses
+ * @sp:	       The sequence the LOGO was on
+ * @fp:	       The LOGO response frame
+ * @lport_arg: The local port
+ */
+static void fc_rport_logo_resp(struct fc_seq *sp, struct fc_frame *fp,
+			       void *lport_arg)
+{
+	struct fc_lport *lport = lport_arg;
+
+	FC_RPORT_ID_DBG(lport, fc_seq_exch(sp)->did,
+			"Received a LOGO %s\n", fc_els_resp_type(fp));
+	if (IS_ERR(fp))
+		return;
+	fc_frame_free(fp);
+}
+
+/**
  * fc_rport_enter_logo() - Send a logout (LOGO) request
  * @rdata: The remote port to send the LOGO request to
  *
@@ -1235,23 +1201,14 @@ static void fc_rport_enter_logo(struct fc_rport_priv *rdata)
 	struct fc_lport *lport = rdata->local_port;
 	struct fc_frame *fp;
 
-	FC_RPORT_DBG(rdata, "Port entered LOGO state from %s state\n",
+	FC_RPORT_DBG(rdata, "Port sending LOGO from %s state\n",
 		     fc_rport_state(rdata));
 
-	fc_rport_state_enter(rdata, RPORT_ST_LOGO);
-
 	fp = fc_frame_alloc(lport, sizeof(struct fc_els_logo));
-	if (!fp) {
-		fc_rport_error_retry(rdata, fp);
+	if (!fp)
 		return;
-	}
-
-	if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_LOGO,
-				  fc_rport_logo_resp, rdata,
-				  2 * lport->r_a_tov))
-		fc_rport_error_retry(rdata, NULL);
-	else
-		kref_get(&rdata->kref);
+	(void)lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_LOGO,
+				   fc_rport_logo_resp, lport, 0);
 }
 
 /**
@@ -1670,7 +1627,6 @@ static void fc_rport_recv_plogi_req(struct fc_lport *lport,
 		break;
 	case RPORT_ST_FLOGI:
 	case RPORT_ST_DELETE:
-	case RPORT_ST_LOGO:
 		FC_RPORT_DBG(rdata, "Received PLOGI in state %s - send busy\n",
 			     fc_rport_state(rdata));
 		mutex_unlock(&rdata->rp_mutex);
diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h
index 8d297f9..e6f07fb 100644
--- a/include/scsi/libfc.h
+++ b/include/scsi/libfc.h
@@ -103,7 +103,6 @@ enum fc_disc_event {
  * @RPORT_ST_PRLI:    Waiting for PRLI completion
  * @RPORT_ST_RTV:     Waiting for RTV completion
  * @RPORT_ST_READY:   Ready for use
- * @RPORT_ST_LOGO:    Remote port logout (LOGO) sent
  * @RPORT_ST_ADISC:   Discover Address sent
  * @RPORT_ST_DELETE:  Remote port being deleted
 */
@@ -115,7 +114,6 @@ enum fc_rport_state {
 	RPORT_ST_PRLI,
 	RPORT_ST_RTV,
 	RPORT_ST_READY,
-	RPORT_ST_LOGO,
 	RPORT_ST_ADISC,
 	RPORT_ST_DELETE,
 };


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 21/28] libfc: add fc_frame_sid() and fc_frame_did() functions
  2010-07-20 22:19 [PATCH 00/28] libfc, libfcoe and fcoe updates for scsi-misc Robert Love
                   ` (19 preceding siblings ...)
  2010-07-20 22:20 ` [PATCH 20/28] libfc: eliminate rport LOGO state Robert Love
@ 2010-07-20 22:20 ` Robert Love
  2010-07-20 22:21 ` [PATCH 22/28] libfc: add fc_fill_reply_hdr() and fc_fill_hdr() Robert Love
                   ` (6 subsequent siblings)
  27 siblings, 0 replies; 34+ messages in thread
From: Robert Love @ 2010-07-20 22:20 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi; +Cc: Joe Eykholt

From: Joe Eykholt <jeykholt@cisco.com>

To pave the way for eliminating exchanges from incoming requests,
add simple inline fc_frame_sid() and fc_frame_did() functions
which get the FC_IDs from the frame header.  This can be almost
as efficient as getting them from the sequence/exchange.

Move ntohll, htonll, ntoh24 and hton24 to <scsi/fc_frame.h>
since we need them there and that's included by <scsi/libfc.h>

Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---
 drivers/scsi/libfc/fc_lport.c |   12 ++++-------
 drivers/scsi/libfc/fc_rport.c |   26 +++++-------------------
 include/scsi/fc_frame.h       |   45 ++++++++++++++++++++++++++++++++++++++++-
 include/scsi/libfc.h          |   18 ----------------
 4 files changed, 54 insertions(+), 47 deletions(-)

diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c
index ec9850c..be3c2ce 100644
--- a/drivers/scsi/libfc/fc_lport.c
+++ b/drivers/scsi/libfc/fc_lport.c
@@ -800,7 +800,6 @@ static void fc_lport_recv_flogi_req(struct fc_seq *sp_in,
 				    struct fc_lport *lport)
 {
 	struct fc_frame *fp;
-	struct fc_frame_header *fh;
 	struct fc_seq *sp;
 	struct fc_exch *ep;
 	struct fc_els_flogi *flp;
@@ -813,8 +812,7 @@ static void fc_lport_recv_flogi_req(struct fc_seq *sp_in,
 	FC_LPORT_DBG(lport, "Received FLOGI request while in state %s\n",
 		     fc_lport_state(lport));
 
-	fh = fc_frame_header_get(rx_fp);
-	remote_fid = ntoh24(fh->fh_s_id);
+	remote_fid = fc_frame_sid(rx_fp);
 	flp = fc_frame_payload_get(rx_fp, sizeof(*flp));
 	if (!flp)
 		goto out;
@@ -910,7 +908,7 @@ static void fc_lport_recv_req(struct fc_lport *lport, struct fc_seq *sp,
 				recv = fc_lport_recv_flogi_req;
 			break;
 		case ELS_LOGO:
-			if (ntoh24(fh->fh_s_id) == FC_FID_FLOGI)
+			if (fc_frame_sid(fp) == FC_FID_FLOGI)
 				recv = fc_lport_recv_logo_req;
 			break;
 		case ELS_RSCN:
@@ -1468,7 +1466,6 @@ void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp,
 			 void *lp_arg)
 {
 	struct fc_lport *lport = lp_arg;
-	struct fc_frame_header *fh;
 	struct fc_els_flogi *flp;
 	u32 did;
 	u16 csp_flags;
@@ -1496,8 +1493,7 @@ void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp,
 		goto err;
 	}
 
-	fh = fc_frame_header_get(fp);
-	did = ntoh24(fh->fh_d_id);
+	did = fc_frame_did(fp);
 	if (fc_frame_payload_op(fp) == ELS_LS_ACC && did != 0) {
 		flp = fc_frame_payload_get(fp, sizeof(*flp));
 		if (flp) {
@@ -1523,7 +1519,7 @@ void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp,
 				       "Port (%6.6x) entered "
 				       "point-to-point mode\n",
 				       lport->host->host_no, did);
-				fc_lport_ptp_setup(lport, ntoh24(fh->fh_s_id),
+				fc_lport_ptp_setup(lport, fc_frame_sid(fp),
 						   get_unaligned_be64(
 							   &flp->fl_wwpn),
 						   get_unaligned_be64(
diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c
index c06d63e..1234931 100644
--- a/drivers/scsi/libfc/fc_rport.c
+++ b/drivers/scsi/libfc/fc_rport.c
@@ -747,13 +747,11 @@ static void fc_rport_recv_flogi_req(struct fc_lport *lport,
 	struct fc_rport_priv *rdata;
 	struct fc_frame *fp = rx_fp;
 	struct fc_exch *ep;
-	struct fc_frame_header *fh;
 	struct fc_seq_els_data rjt_data;
 	u32 sid, f_ctl;
 
 	rjt_data.fp = NULL;
-	fh = fc_frame_header_get(fp);
-	sid = ntoh24(fh->fh_s_id);
+	sid = fc_frame_sid(fp);
 
 	FC_RPORT_ID_DBG(lport, sid, "Received FLOGI request\n");
 
@@ -1430,17 +1428,14 @@ static void fc_rport_recv_els_req(struct fc_lport *lport,
 				  struct fc_seq *sp, struct fc_frame *fp)
 {
 	struct fc_rport_priv *rdata;
-	struct fc_frame_header *fh;
 	struct fc_seq_els_data els_data;
 
 	els_data.fp = NULL;
 	els_data.reason = ELS_RJT_UNAB;
 	els_data.explan = ELS_EXPL_PLOGI_REQD;
 
-	fh = fc_frame_header_get(fp);
-
 	mutex_lock(&lport->disc.disc_mutex);
-	rdata = lport->tt.rport_lookup(lport, ntoh24(fh->fh_s_id));
+	rdata = lport->tt.rport_lookup(lport, fc_frame_sid(fp));
 	if (!rdata) {
 		mutex_unlock(&lport->disc.disc_mutex);
 		goto reject;
@@ -1555,14 +1550,12 @@ static void fc_rport_recv_plogi_req(struct fc_lport *lport,
 	struct fc_rport_priv *rdata;
 	struct fc_frame *fp = rx_fp;
 	struct fc_exch *ep;
-	struct fc_frame_header *fh;
 	struct fc_els_flogi *pl;
 	struct fc_seq_els_data rjt_data;
 	u32 sid, f_ctl;
 
 	rjt_data.fp = NULL;
-	fh = fc_frame_header_get(fp);
-	sid = ntoh24(fh->fh_s_id);
+	sid = fc_frame_sid(fp);
 
 	FC_RPORT_ID_DBG(lport, sid, "Received PLOGI request\n");
 
@@ -1682,7 +1675,6 @@ static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata,
 	struct fc_lport *lport = rdata->local_port;
 	struct fc_exch *ep;
 	struct fc_frame *fp;
-	struct fc_frame_header *fh;
 	struct {
 		struct fc_els_prli prli;
 		struct fc_els_spp spp;
@@ -1698,12 +1690,10 @@ static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata,
 	u32 roles = FC_RPORT_ROLE_UNKNOWN;
 
 	rjt_data.fp = NULL;
-	fh = fc_frame_header_get(rx_fp);
-
 	FC_RPORT_DBG(rdata, "Received PRLI request while in state %s\n",
 		     fc_rport_state(rdata));
 
-	len = fr_len(rx_fp) - sizeof(*fh);
+	len = fr_len(rx_fp) - sizeof(struct fc_frame_header);
 	pp = fc_frame_payload_get(rx_fp, sizeof(*pp));
 	if (!pp)
 		goto reject_len;
@@ -1817,7 +1807,6 @@ static void fc_rport_recv_prlo_req(struct fc_rport_priv *rdata,
 				   struct fc_frame *rx_fp)
 {
 	struct fc_lport *lport = rdata->local_port;
-	struct fc_frame_header *fh;
 	struct fc_exch *ep;
 	struct fc_frame *fp;
 	struct {
@@ -1832,12 +1821,11 @@ static void fc_rport_recv_prlo_req(struct fc_rport_priv *rdata,
 	struct fc_seq_els_data rjt_data;
 
 	rjt_data.fp = NULL;
-	fh = fc_frame_header_get(rx_fp);
 
 	FC_RPORT_DBG(rdata, "Received PRLO request while in state %s\n",
 		     fc_rport_state(rdata));
 
-	len = fr_len(rx_fp) - sizeof(*fh);
+	len = fr_len(rx_fp) - sizeof(struct fc_frame_header);
 	pp = fc_frame_payload_get(rx_fp, sizeof(*pp));
 	if (!pp)
 		goto reject_len;
@@ -1901,14 +1889,12 @@ static void fc_rport_recv_logo_req(struct fc_lport *lport,
 				   struct fc_seq *sp,
 				   struct fc_frame *fp)
 {
-	struct fc_frame_header *fh;
 	struct fc_rport_priv *rdata;
 	u32 sid;
 
 	lport->tt.seq_els_rsp_send(sp, ELS_LS_ACC, NULL);
 
-	fh = fc_frame_header_get(fp);
-	sid = ntoh24(fh->fh_s_id);
+	sid = fc_frame_sid(fp);
 
 	mutex_lock(&lport->disc.disc_mutex);
 	rdata = lport->tt.rport_lookup(lport, sid);
diff --git a/include/scsi/fc_frame.h b/include/scsi/fc_frame.h
index 29dd97d..4ad0204 100644
--- a/include/scsi/fc_frame.h
+++ b/include/scsi/fc_frame.h
@@ -30,6 +30,23 @@
 
 #include <linux/if_ether.h>
 
+/* some helpful macros */
+
+#define ntohll(x) be64_to_cpu(x)
+#define htonll(x) cpu_to_be64(x)
+
+static inline u32 ntoh24(const u8 *p)
+{
+	return (p[0] << 16) | (p[1] << 8) | p[2];
+}
+
+static inline void hton24(u8 *p, u32 v)
+{
+	p[0] = (v >> 16) & 0xff;
+	p[1] = (v >> 8) & 0xff;
+	p[2] = v & 0xff;
+}
+
 /*
  * The fc_frame interface is used to pass frame data between functions.
  * The frame includes the data buffer, length, and SOF / EOF delimiter types.
@@ -139,13 +156,39 @@ static inline int fc_frame_is_linear(struct fc_frame *fp)
 
 /*
  * Get frame header from message in fc_frame structure.
+ * This version doesn't do a length check.
+ */
+static inline
+struct fc_frame_header *__fc_frame_header_get(const struct fc_frame *fp)
+{
+	return (struct fc_frame_header *)fr_hdr(fp);
+}
+
+/*
+ * Get frame header from message in fc_frame structure.
  * This hides a cast and provides a place to add some checking.
  */
 static inline
 struct fc_frame_header *fc_frame_header_get(const struct fc_frame *fp)
 {
 	WARN_ON(fr_len(fp) < sizeof(struct fc_frame_header));
-	return (struct fc_frame_header *) fr_hdr(fp);
+	return __fc_frame_header_get(fp);
+}
+
+/*
+ * Get source FC_ID (S_ID) from frame header in message.
+ */
+static inline u32 fc_frame_sid(const struct fc_frame *fp)
+{
+	return ntoh24(__fc_frame_header_get(fp)->fh_s_id);
+}
+
+/*
+ * Get destination FC_ID (D_ID) from frame header in message.
+ */
+static inline u32 fc_frame_did(const struct fc_frame *fp)
+{
+	return ntoh24(__fc_frame_header_get(fp)->fh_d_id);
 }
 
 /*
diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h
index e6f07fb..f1ce793 100644
--- a/include/scsi/libfc.h
+++ b/include/scsi/libfc.h
@@ -42,24 +42,6 @@
 #define	FC_EX_TIMEOUT	1	/* Exchange timeout */
 #define	FC_EX_CLOSED	2	/* Exchange closed */
 
-/* some helpful macros */
-
-#define ntohll(x) be64_to_cpu(x)
-#define htonll(x) cpu_to_be64(x)
-
-
-static inline u32 ntoh24(const u8 *p)
-{
-	return (p[0] << 16) | (p[1] << 8) | p[2];
-}
-
-static inline void hton24(u8 *p, u32 v)
-{
-	p[0] = (v >> 16) & 0xff;
-	p[1] = (v >> 8) & 0xff;
-	p[2] = v & 0xff;
-}
-
 /**
  * enum fc_lport_state - Local port states
  * @LPORT_ST_DISABLED: Disabled


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 22/28] libfc: add fc_fill_reply_hdr() and fc_fill_hdr()
  2010-07-20 22:19 [PATCH 00/28] libfc, libfcoe and fcoe updates for scsi-misc Robert Love
                   ` (20 preceding siblings ...)
  2010-07-20 22:20 ` [PATCH 21/28] libfc: add fc_frame_sid() and fc_frame_did() functions Robert Love
@ 2010-07-20 22:21 ` Robert Love
  2010-07-20 22:21 ` [PATCH 23/28] libfc: add interface to allocate a sequence for incoming requests Robert Love
                   ` (5 subsequent siblings)
  27 siblings, 0 replies; 34+ messages in thread
From: Robert Love @ 2010-07-20 22:21 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi; +Cc: Joe Eykholt

From: Joe Eykholt <jeykholt@cisco.com>

Add functions to fill in an FC header given a request header.
These reduces code lines in fc_lport and fc_rport and works
without an exchange/sequence assigned.

fc_fill_reply_hdr() fills a header for a final reply frame.

fc_fill_hdr() which is similar but allows specifying the
f_ctl parameter.

Add defines for F_CTL values FC_FCTL_REQ and FC_FCTL_RESP.
These can be used for most request and response sequences.

v2 of patch adds a line to copy the frame encapsulation
info from the received frame.

Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---
 drivers/scsi/libfc/fc_elsct.c |    2 +
 drivers/scsi/libfc/fc_fcp.c   |    6 ++-
 drivers/scsi/libfc/fc_libfc.c |   78 +++++++++++++++++++++++++++++++++++++++++
 drivers/scsi/libfc/fc_lport.c |   39 ++++++---------------
 drivers/scsi/libfc/fc_rport.c |   64 ++++++++--------------------------
 include/scsi/fc_encode.h      |    7 ++++
 include/scsi/libfc.h          |    4 ++
 7 files changed, 121 insertions(+), 79 deletions(-)

diff --git a/drivers/scsi/libfc/fc_elsct.c b/drivers/scsi/libfc/fc_elsct.c
index e9412b7..9b25969 100644
--- a/drivers/scsi/libfc/fc_elsct.c
+++ b/drivers/scsi/libfc/fc_elsct.c
@@ -64,7 +64,7 @@ struct fc_seq *fc_elsct_send(struct fc_lport *lport, u32 did,
 	}
 
 	fc_fill_fc_hdr(fp, r_ctl, did, lport->port_id, fh_type,
-		       FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0);
+		       FC_FCTL_REQ, 0);
 
 	return lport->tt.exch_seq_send(lport, fp, resp, NULL, arg, timer_msec);
 }
diff --git a/drivers/scsi/libfc/fc_fcp.c b/drivers/scsi/libfc/fc_fcp.c
index 61a1297..eac4d09 100644
--- a/drivers/scsi/libfc/fc_fcp.c
+++ b/drivers/scsi/libfc/fc_fcp.c
@@ -1108,7 +1108,7 @@ static int fc_fcp_cmd_send(struct fc_lport *lport, struct fc_fcp_pkt *fsp,
 
 	fc_fill_fc_hdr(fp, FC_RCTL_DD_UNSOL_CMD, rport->port_id,
 		       rpriv->local_port->port_id, FC_TYPE_FCP,
-		       FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0);
+		       FC_FCTL_REQ, 0);
 
 	seq = lport->tt.exch_seq_send(lport, fp, resp, fc_fcp_pkt_destroy,
 				      fsp, 0);
@@ -1381,7 +1381,7 @@ static void fc_fcp_rec(struct fc_fcp_pkt *fsp)
 	fr_seq(fp) = fsp->seq_ptr;
 	fc_fill_fc_hdr(fp, FC_RCTL_ELS_REQ, rport->port_id,
 		       rpriv->local_port->port_id, FC_TYPE_ELS,
-		       FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0);
+		       FC_FCTL_REQ, 0);
 	if (lport->tt.elsct_send(lport, rport->port_id, fp, ELS_REC,
 				 fc_fcp_rec_resp, fsp,
 				 jiffies_to_msecs(FC_SCSI_REC_TOV))) {
@@ -1639,7 +1639,7 @@ static void fc_fcp_srr(struct fc_fcp_pkt *fsp, enum fc_rctl r_ctl, u32 offset)
 
 	fc_fill_fc_hdr(fp, FC_RCTL_ELS4_REQ, rport->port_id,
 		       rpriv->local_port->port_id, FC_TYPE_FCP,
-		       FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0);
+		       FC_FCTL_REQ, 0);
 
 	seq = lport->tt.exch_seq_send(lport, fp, fc_fcp_srr_resp, NULL,
 				      fsp, jiffies_to_msecs(FC_SCSI_REC_TOV));
diff --git a/drivers/scsi/libfc/fc_libfc.c b/drivers/scsi/libfc/fc_libfc.c
index 39f4b6a..6a48c28 100644
--- a/drivers/scsi/libfc/fc_libfc.c
+++ b/drivers/scsi/libfc/fc_libfc.c
@@ -23,6 +23,7 @@
 #include <linux/crc32.h>
 
 #include <scsi/libfc.h>
+#include <scsi/fc_encode.h>
 
 #include "fc_libfc.h"
 
@@ -132,3 +133,80 @@ u32 fc_copy_buffer_to_sglist(void *buf, size_t len,
 	}
 	return copy_len;
 }
+
+/**
+ * fc_fill_hdr() -  fill FC header fields based on request
+ * @fp: reply frame containing header to be filled in
+ * @in_fp: request frame containing header to use in filling in reply
+ * @r_ctl: R_CTL value for header
+ * @f_ctl: F_CTL value for header, with 0 pad
+ * @seq_cnt: sequence count for the header, ignored if frame has a sequence
+ * @parm_offset: parameter / offset value
+ */
+void fc_fill_hdr(struct fc_frame *fp, const struct fc_frame *in_fp,
+		 enum fc_rctl r_ctl, u32 f_ctl, u16 seq_cnt, u32 parm_offset)
+{
+	struct fc_frame_header *fh;
+	struct fc_frame_header *in_fh;
+	struct fc_seq *sp;
+	u32 fill;
+
+	fh = __fc_frame_header_get(fp);
+	in_fh = __fc_frame_header_get(in_fp);
+
+	if (f_ctl & FC_FC_END_SEQ) {
+		fill = -fr_len(fp) & 3;
+		if (fill) {
+			/* TODO, this may be a problem with fragmented skb */
+			memset(skb_put(fp_skb(fp), fill), 0, fill);
+			f_ctl |= fill;
+		}
+		fr_eof(fp) = FC_EOF_T;
+	} else {
+		WARN_ON(fr_len(fp) % 4 != 0);	/* no pad to non last frame */
+		fr_eof(fp) = FC_EOF_N;
+	}
+
+	fh->fh_r_ctl = r_ctl;
+	memcpy(fh->fh_d_id, in_fh->fh_s_id, sizeof(fh->fh_d_id));
+	memcpy(fh->fh_s_id, in_fh->fh_d_id, sizeof(fh->fh_s_id));
+	fh->fh_type = in_fh->fh_type;
+	hton24(fh->fh_f_ctl, f_ctl);
+	fh->fh_ox_id = in_fh->fh_ox_id;
+	fh->fh_rx_id = in_fh->fh_rx_id;
+	fh->fh_cs_ctl = 0;
+	fh->fh_df_ctl = 0;
+	fh->fh_parm_offset = htonl(parm_offset);
+
+	sp = fr_seq(in_fp);
+	if (sp) {
+		fr_seq(fp) = sp;
+		fh->fh_seq_id = sp->id;
+		seq_cnt = sp->cnt;
+	} else {
+		fh->fh_seq_id = 0;
+	}
+	fh->fh_seq_cnt = ntohs(seq_cnt);
+	fr_sof(fp) = seq_cnt ? FC_SOF_N3 : FC_SOF_I3;
+	fr_encaps(fp) = fr_encaps(in_fp);
+}
+EXPORT_SYMBOL(fc_fill_hdr);
+
+/**
+ * fc_fill_reply_hdr() -  fill FC reply header fields based on request
+ * @fp: reply frame containing header to be filled in
+ * @in_fp: request frame containing header to use in filling in reply
+ * @r_ctl: R_CTL value for reply
+ * @parm_offset: parameter / offset value
+ */
+void fc_fill_reply_hdr(struct fc_frame *fp, const struct fc_frame *in_fp,
+		       enum fc_rctl r_ctl, u32 parm_offset)
+{
+	struct fc_seq *sp;
+
+	sp = fr_seq(in_fp);
+	if (sp)
+		fr_seq(fp) = fr_dev(in_fp)->tt.seq_start_next(sp);
+	fc_fill_hdr(fp, in_fp, r_ctl, FC_FCTL_RESP, 0, parm_offset);
+}
+EXPORT_SYMBOL(fc_fill_reply_hdr);
diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c
index be3c2ce..e50a660 100644
--- a/drivers/scsi/libfc/fc_lport.c
+++ b/drivers/scsi/libfc/fc_lport.c
@@ -405,11 +405,9 @@ static void fc_lport_recv_echo_req(struct fc_seq *sp, struct fc_frame *in_fp,
 				   struct fc_lport *lport)
 {
 	struct fc_frame *fp;
-	struct fc_exch *ep = fc_seq_exch(sp);
 	unsigned int len;
 	void *pp;
 	void *dp;
-	u32 f_ctl;
 
 	FC_LPORT_DBG(lport, "Received ECHO request while in state %s\n",
 		     fc_lport_state(lport));
@@ -425,11 +423,8 @@ static void fc_lport_recv_echo_req(struct fc_seq *sp, struct fc_frame *in_fp,
 		dp = fc_frame_payload_get(fp, len);
 		memcpy(dp, pp, len);
 		*((__be32 *)dp) = htonl(ELS_LS_ACC << 24);
-		sp = lport->tt.seq_start_next(sp);
-		f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ | FC_FC_END_SEQ;
-		fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid,
-			       FC_TYPE_ELS, f_ctl, 0);
-		lport->tt.seq_send(lport, sp, fp);
+		fc_fill_reply_hdr(fp, in_fp, FC_RCTL_ELS_REP, 0);
+		lport->tt.frame_send(lport, fp);
 	}
 	fc_frame_free(in_fp);
 }
@@ -447,7 +442,6 @@ static void fc_lport_recv_rnid_req(struct fc_seq *sp, struct fc_frame *in_fp,
 				   struct fc_lport *lport)
 {
 	struct fc_frame *fp;
-	struct fc_exch *ep = fc_seq_exch(sp);
 	struct fc_els_rnid *req;
 	struct {
 		struct fc_els_rnid_resp rnid;
@@ -457,7 +451,6 @@ static void fc_lport_recv_rnid_req(struct fc_seq *sp, struct fc_frame *in_fp,
 	struct fc_seq_els_data rjt_data;
 	u8 fmt;
 	size_t len;
-	u32 f_ctl;
 
 	FC_LPORT_DBG(lport, "Received RNID request while in state %s\n",
 		     fc_lport_state(lport));
@@ -490,12 +483,8 @@ static void fc_lport_recv_rnid_req(struct fc_seq *sp, struct fc_frame *in_fp,
 				memcpy(&rp->gen, &lport->rnid_gen,
 				       sizeof(rp->gen));
 			}
-			sp = lport->tt.seq_start_next(sp);
-			f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ;
-			f_ctl |= FC_FC_END_SEQ | FC_FC_SEQ_INIT;
-			fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid,
-				       FC_TYPE_ELS, f_ctl, 0);
-			lport->tt.seq_send(lport, sp, fp);
+			fc_fill_reply_hdr(fp, in_fp, FC_RCTL_ELS_REP, 0);
+			lport->tt.frame_send(lport, fp);
 		}
 	}
 	fc_frame_free(in_fp);
@@ -800,14 +789,13 @@ static void fc_lport_recv_flogi_req(struct fc_seq *sp_in,
 				    struct fc_lport *lport)
 {
 	struct fc_frame *fp;
+	struct fc_frame_header *fh;
 	struct fc_seq *sp;
-	struct fc_exch *ep;
 	struct fc_els_flogi *flp;
 	struct fc_els_flogi *new_flp;
 	u64 remote_wwpn;
 	u32 remote_fid;
 	u32 local_fid;
-	u32 f_ctl;
 
 	FC_LPORT_DBG(lport, "Received FLOGI request while in state %s\n",
 		     fc_lport_state(lport));
@@ -843,7 +831,6 @@ static void fc_lport_recv_flogi_req(struct fc_seq *sp_in,
 
 	fp = fc_frame_alloc(lport, sizeof(*flp));
 	if (fp) {
-		sp = lport->tt.seq_start_next(fr_seq(rx_fp));
 		new_flp = fc_frame_payload_get(fp, sizeof(*flp));
 		fc_lport_flogi_fill(lport, new_flp, ELS_FLOGI);
 		new_flp->fl_cmd = (u8) ELS_LS_ACC;
@@ -852,11 +839,11 @@ static void fc_lport_recv_flogi_req(struct fc_seq *sp_in,
 		 * Send the response.  If this fails, the originator should
 		 * repeat the sequence.
 		 */
-		f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ | FC_FC_END_SEQ;
-		ep = fc_seq_exch(sp);
-		fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, remote_fid, local_fid,
-			       FC_TYPE_ELS, f_ctl, 0);
-		lport->tt.seq_send(lport, sp, fp);
+		fc_fill_reply_hdr(fp, rx_fp, FC_RCTL_ELS_REP, 0);
+		fh = fc_frame_header_get(fp);
+		hton24(fh->fh_s_id, local_fid);
+		hton24(fh->fh_d_id, remote_fid);
+		lport->tt.frame_send(lport, fp);
 
 	} else {
 		fc_lport_error(lport, fp);
@@ -1731,8 +1718,7 @@ static int fc_lport_els_request(struct fc_bsg_job *job,
 	hton24(fh->fh_d_id, did);
 	hton24(fh->fh_s_id, lport->port_id);
 	fh->fh_type = FC_TYPE_ELS;
-	hton24(fh->fh_f_ctl, FC_FC_FIRST_SEQ |
-	       FC_FC_END_SEQ | FC_FC_SEQ_INIT);
+	hton24(fh->fh_f_ctl, FC_FCTL_REQ);
 	fh->fh_cs_ctl = 0;
 	fh->fh_df_ctl = 0;
 	fh->fh_parm_offset = 0;
@@ -1791,8 +1777,7 @@ static int fc_lport_ct_request(struct fc_bsg_job *job,
 	hton24(fh->fh_d_id, did);
 	hton24(fh->fh_s_id, lport->port_id);
 	fh->fh_type = FC_TYPE_CT;
-	hton24(fh->fh_f_ctl, FC_FC_FIRST_SEQ |
-	       FC_FC_END_SEQ | FC_FC_SEQ_INIT);
+	hton24(fh->fh_f_ctl, FC_FCTL_REQ);
 	fh->fh_cs_ctl = 0;
 	fh->fh_df_ctl = 0;
 	fh->fh_parm_offset = 0;
diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c
index 1234931..5987951 100644
--- a/drivers/scsi/libfc/fc_rport.c
+++ b/drivers/scsi/libfc/fc_rport.c
@@ -746,9 +746,8 @@ static void fc_rport_recv_flogi_req(struct fc_lport *lport,
 	struct fc_els_flogi *flp;
 	struct fc_rport_priv *rdata;
 	struct fc_frame *fp = rx_fp;
-	struct fc_exch *ep;
 	struct fc_seq_els_data rjt_data;
-	u32 sid, f_ctl;
+	u32 sid;
 
 	rjt_data.fp = NULL;
 	sid = fc_frame_sid(fp);
@@ -813,7 +812,6 @@ static void fc_rport_recv_flogi_req(struct fc_lport *lport,
 		rjt_data.explan = ELS_EXPL_NONE;
 		goto reject;
 	}
-	fc_frame_free(rx_fp);
 
 	fp = fc_frame_alloc(lport, sizeof(*flp));
 	if (!fp)
@@ -824,11 +822,8 @@ static void fc_rport_recv_flogi_req(struct fc_lport *lport,
 	flp = fc_frame_payload_get(fp, sizeof(*flp));
 	flp->fl_cmd = ELS_LS_ACC;
 
-	f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT;
-	ep = fc_seq_exch(sp);
-	fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid,
-		       FC_TYPE_ELS, f_ctl, 0);
-	lport->tt.seq_send(lport, sp, fp);
+	fc_fill_reply_hdr(fp, rx_fp, FC_RCTL_ELS_REP, 0);
+	lport->tt.frame_send(lport, fp);
 
 	if (rdata->ids.port_name < lport->wwpn)
 		fc_rport_enter_plogi(rdata);
@@ -837,12 +832,13 @@ static void fc_rport_recv_flogi_req(struct fc_lport *lport,
 out:
 	mutex_unlock(&rdata->rp_mutex);
 	mutex_unlock(&disc->disc_mutex);
+	fc_frame_free(rx_fp);
 	return;
 
 reject:
 	mutex_unlock(&disc->disc_mutex);
 	lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data);
-	fc_frame_free(fp);
+	fc_frame_free(rx_fp);
 }
 
 /**
@@ -1310,10 +1306,8 @@ static void fc_rport_recv_adisc_req(struct fc_rport_priv *rdata,
 {
 	struct fc_lport *lport = rdata->local_port;
 	struct fc_frame *fp;
-	struct fc_exch *ep = fc_seq_exch(sp);
 	struct fc_els_adisc *adisc;
 	struct fc_seq_els_data rjt_data;
-	u32 f_ctl;
 
 	FC_RPORT_DBG(rdata, "Received ADISC request\n");
 
@@ -1332,11 +1326,8 @@ static void fc_rport_recv_adisc_req(struct fc_rport_priv *rdata,
 	fc_adisc_fill(lport, fp);
 	adisc = fc_frame_payload_get(fp, sizeof(*adisc));
 	adisc->adisc_cmd = ELS_LS_ACC;
-	sp = lport->tt.seq_start_next(sp);
-	f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT;
-	fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid,
-		       FC_TYPE_ELS, f_ctl, 0);
-	lport->tt.seq_send(lport, sp, fp);
+	fc_fill_reply_hdr(fp, in_fp, FC_RCTL_ELS_REP, 0);
+	lport->tt.frame_send(lport, fp);
 drop:
 	fc_frame_free(in_fp);
 }
@@ -1356,13 +1347,11 @@ static void fc_rport_recv_rls_req(struct fc_rport_priv *rdata,
 {
 	struct fc_lport *lport = rdata->local_port;
 	struct fc_frame *fp;
-	struct fc_exch *ep = fc_seq_exch(sp);
 	struct fc_els_rls *rls;
 	struct fc_els_rls_resp *rsp;
 	struct fc_els_lesb *lesb;
 	struct fc_seq_els_data rjt_data;
 	struct fc_host_statistics *hst;
-	u32 f_ctl;
 
 	FC_RPORT_DBG(rdata, "Received RLS request while in state %s\n",
 		     fc_rport_state(rdata));
@@ -1399,11 +1388,8 @@ static void fc_rport_recv_rls_req(struct fc_rport_priv *rdata,
 		lesb->lesb_inv_crc = htonl(hst->invalid_crc_count);
 	}
 
-	sp = lport->tt.seq_start_next(sp);
-	f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ | FC_FC_END_SEQ;
-	fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid,
-		       FC_TYPE_ELS, f_ctl, 0);
-	lport->tt.seq_send(lport, sp, fp);
+	fc_fill_reply_hdr(fp, rx_fp, FC_RCTL_ELS_REP, 0);
+	lport->tt.frame_send(lport, fp);
 	goto out;
 
 out_rjt:
@@ -1549,10 +1535,9 @@ static void fc_rport_recv_plogi_req(struct fc_lport *lport,
 	struct fc_disc *disc;
 	struct fc_rport_priv *rdata;
 	struct fc_frame *fp = rx_fp;
-	struct fc_exch *ep;
 	struct fc_els_flogi *pl;
 	struct fc_seq_els_data rjt_data;
-	u32 sid, f_ctl;
+	u32 sid;
 
 	rjt_data.fp = NULL;
 	sid = fc_frame_sid(fp);
@@ -1632,27 +1617,21 @@ static void fc_rport_recv_plogi_req(struct fc_lport *lport,
 	 * Get session payload size from incoming PLOGI.
 	 */
 	rdata->maxframe_size = fc_plogi_get_maxframe(pl, lport->mfs);
-	fc_frame_free(rx_fp);
 
 	/*
 	 * Send LS_ACC.	 If this fails, the originator should retry.
 	 */
-	sp = lport->tt.seq_start_next(sp);
-	if (!sp)
-		goto out;
 	fp = fc_frame_alloc(lport, sizeof(*pl));
 	if (!fp)
 		goto out;
 
 	fc_plogi_fill(lport, fp, ELS_LS_ACC);
-	f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT;
-	ep = fc_seq_exch(sp);
-	fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid,
-		       FC_TYPE_ELS, f_ctl, 0);
-	lport->tt.seq_send(lport, sp, fp);
+	fc_fill_reply_hdr(fp, rx_fp, FC_RCTL_ELS_REP, 0);
+	lport->tt.frame_send(lport, fp);
 	fc_rport_enter_prli(rdata);
 out:
 	mutex_unlock(&rdata->rp_mutex);
+	fc_frame_free(rx_fp);
 	return;
 
 reject:
@@ -1673,7 +1652,6 @@ static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata,
 				   struct fc_seq *sp, struct fc_frame *rx_fp)
 {
 	struct fc_lport *lport = rdata->local_port;
-	struct fc_exch *ep;
 	struct fc_frame *fp;
 	struct {
 		struct fc_els_prli prli;
@@ -1685,7 +1663,6 @@ static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata,
 	unsigned int plen;
 	enum fc_els_spp_resp resp;
 	struct fc_seq_els_data rjt_data;
-	u32 f_ctl;
 	u32 fcp_parm;
 	u32 roles = FC_RPORT_ROLE_UNKNOWN;
 
@@ -1714,8 +1691,6 @@ static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata,
 		rjt_data.explan = ELS_EXPL_INSUF_RES;
 		goto reject;
 	}
-	sp = lport->tt.seq_start_next(sp);
-	WARN_ON(!sp);
 	pp = fc_frame_payload_get(fp, len);
 	WARN_ON(!pp);
 	memset(pp, 0, len);
@@ -1768,12 +1743,8 @@ static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata,
 	/*
 	 * Send LS_ACC.	 If this fails, the originator should retry.
 	 */
-	f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ;
-	f_ctl |= FC_FC_END_SEQ | FC_FC_SEQ_INIT;
-	ep = fc_seq_exch(sp);
-	fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid,
-		       FC_TYPE_ELS, f_ctl, 0);
-	lport->tt.seq_send(lport, sp, fp);
+	fc_fill_reply_hdr(fp, rx_fp, FC_RCTL_ELS_REP, 0);
+	lport->tt.frame_send(lport, fp);
 
 	switch (rdata->rp_state) {
 	case RPORT_ST_PRLI:
@@ -1817,7 +1788,6 @@ static void fc_rport_recv_prlo_req(struct fc_rport_priv *rdata,
 	struct fc_els_spp *spp;		/* response spp */
 	unsigned int len;
 	unsigned int plen;
-	u32 f_ctl;
 	struct fc_seq_els_data rjt_data;
 
 	rjt_data.fp = NULL;
@@ -1859,11 +1829,9 @@ static void fc_rport_recv_prlo_req(struct fc_rport_priv *rdata,
 
 	fc_rport_enter_delete(rdata, RPORT_EV_LOGO);
 
-	f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ;
-	f_ctl |= FC_FC_END_SEQ | FC_FC_SEQ_INIT;
 	ep = fc_seq_exch(sp);
 	fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid,
-		       FC_TYPE_ELS, f_ctl, 0);
+		       FC_TYPE_ELS, FC_FCTL_RESP, 0);
 	lport->tt.seq_send(lport, sp, fp);
 	goto drop;
 
diff --git a/include/scsi/fc_encode.h b/include/scsi/fc_encode.h
index 9b4867c..6d293c8 100644
--- a/include/scsi/fc_encode.h
+++ b/include/scsi/fc_encode.h
@@ -21,6 +21,13 @@
 #define _FC_ENCODE_H_
 #include <asm/unaligned.h>
 
+/*
+ * F_CTL values for simple requests and responses.
+ */
+#define FC_FCTL_REQ	(FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT)
+#define FC_FCTL_RESP	(FC_FC_EX_CTX | FC_FC_LAST_SEQ | \
+			FC_FC_END_SEQ | FC_FC_SEQ_INIT)
+
 struct fc_ns_rft {
 	struct fc_ns_fid fid;	/* port ID object */
 	struct fc_ns_fts fts;	/* FC4-types object */
diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h
index f1ce793..a6414ec 100644
--- a/include/scsi/libfc.h
+++ b/include/scsi/libfc.h
@@ -1027,6 +1027,10 @@ struct fc_seq *fc_elsct_send(struct fc_lport *, u32 did,
 				    void *arg, u32 timer_msec);
 void fc_lport_flogi_resp(struct fc_seq *, struct fc_frame *, void *);
 void fc_lport_logo_resp(struct fc_seq *, struct fc_frame *, void *);
+void fc_fill_reply_hdr(struct fc_frame *, const struct fc_frame *,
+		       enum fc_rctl, u32 parm_offset);
+void fc_fill_hdr(struct fc_frame *, const struct fc_frame *,
+		 enum fc_rctl, u32 f_ctl, u16 seq_cnt, u32 parm_offset);
 
 
 /*


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 23/28] libfc: add interface to allocate a sequence for incoming requests
  2010-07-20 22:19 [PATCH 00/28] libfc, libfcoe and fcoe updates for scsi-misc Robert Love
                   ` (21 preceding siblings ...)
  2010-07-20 22:21 ` [PATCH 22/28] libfc: add fc_fill_reply_hdr() and fc_fill_hdr() Robert Love
@ 2010-07-20 22:21 ` Robert Love
  2010-07-20 22:21 ` [PATCH 24/28] libfc: don't require a local exchange " Robert Love
                   ` (4 subsequent siblings)
  27 siblings, 0 replies; 34+ messages in thread
From: Robert Love @ 2010-07-20 22:21 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi; +Cc: Joe Eykholt

From: Joe Eykholt <jeykholt@cisco.com>

For incoming ELS and FCP requests, we often don't require an
exchange and sequence, however, sometimes we do.  For those cases,
(primarily FCP requests for targets) add a function to set up
the exchange and sequence.

Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---
 drivers/scsi/libfc/fc_exch.c |   25 +++++++++++++++++++++++++
 include/scsi/libfc.h         |    7 +++++++
 2 files changed, 32 insertions(+), 0 deletions(-)

diff --git a/drivers/scsi/libfc/fc_exch.c b/drivers/scsi/libfc/fc_exch.c
index 61eabd3..027042a 100644
--- a/drivers/scsi/libfc/fc_exch.c
+++ b/drivers/scsi/libfc/fc_exch.c
@@ -1231,6 +1231,28 @@ free:
 }
 
 /**
+ * fc_seq_assign() - Assign exchange and sequence for incoming request
+ * @lport: The local port that received the request
+ * @fp:    The request frame
+ *
+ * On success, the sequence pointer will be returned and also in fr_seq(@fp).
+ */
+static struct fc_seq *fc_seq_assign(struct fc_lport *lport, struct fc_frame *fp)
+{
+	struct fc_exch_mgr_anchor *ema;
+
+	WARN_ON(lport != fr_dev(fp));
+	WARN_ON(fr_seq(fp));
+	fr_seq(fp) = NULL;
+
+	list_for_each_entry(ema, &lport->ema_list, ema_list)
+		if ((!ema->match || ema->match(fp)) &&
+		    fc_seq_lookup_recip(lport, ema->mp, fp) != FC_RJT_NONE)
+			break;
+	return fr_seq(fp);
+}
+
+/**
  * fc_exch_recv_req() - Handler for an incoming request where is other
  *			end is originating the sequence
  * @lport: The local port that received the request
@@ -2283,6 +2305,9 @@ int fc_exch_init(struct fc_lport *lport)
 	if (!lport->tt.seq_exch_abort)
 		lport->tt.seq_exch_abort = fc_seq_exch_abort;
 
+	if (!lport->tt.seq_assign)
+		lport->tt.seq_assign = fc_seq_assign;
+
 	return 0;
 }
 EXPORT_SYMBOL(fc_exch_init);
diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h
index a6414ec..605f1d7 100644
--- a/include/scsi/libfc.h
+++ b/include/scsi/libfc.h
@@ -556,6 +556,13 @@ struct libfc_function_template {
 	struct fc_seq *(*seq_start_next)(struct fc_seq *);
 
 	/*
+	 * Assign a sequence for an incoming request frame.
+	 *
+	 * STATUS: OPTIONAL
+	 */
+	struct fc_seq *(*seq_assign)(struct fc_lport *, struct fc_frame *);
+
+	/*
 	 * 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.


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 24/28] libfc: don't require a local exchange for incoming requests
  2010-07-20 22:19 [PATCH 00/28] libfc, libfcoe and fcoe updates for scsi-misc Robert Love
                   ` (22 preceding siblings ...)
  2010-07-20 22:21 ` [PATCH 23/28] libfc: add interface to allocate a sequence for incoming requests Robert Love
@ 2010-07-20 22:21 ` Robert Love
  2010-07-20 22:21 ` [PATCH 25/28] fcoe: fix offload feature flag change from netdev Robert Love
                   ` (3 subsequent siblings)
  27 siblings, 0 replies; 34+ messages in thread
From: Robert Love @ 2010-07-20 22:21 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi; +Cc: Joe Eykholt

From: Joe Eykholt <jeykholt@cisco.com>

Incoming requests shouldn't require a local exchange if we're
just going to reply with one or two frames and don't expect
anything further.  Don't allocate exchanges for such requests
until requested by the upper-layer protocol.

The sequence is always NULL for new requests, so remove
that as an argument to request handlers.

Also change the first argument to lport->tt.seq_els_rsp_send
from the sequence pointer to the received frame pointer, to
supply the exchange IDs and destination ID info.

Signed-off-by: Joe Eykholt <jeykholt@cisco.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---
 drivers/scsi/fcoe/libfcoe.c   |    9 +-
 drivers/scsi/libfc/fc_disc.c  |   19 ++--
 drivers/scsi/libfc/fc_exch.c  |  188 ++++++++++++++++++++++-------------------
 drivers/scsi/libfc/fc_lport.c |   58 ++++---------
 drivers/scsi/libfc/fc_rport.c |  112 +++++++++---------------
 include/scsi/libfc.h          |   16 +--
 6 files changed, 174 insertions(+), 228 deletions(-)

diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c
index 4de8ced..2c265fe 100644
--- a/drivers/scsi/fcoe/libfcoe.c
+++ b/drivers/scsi/fcoe/libfcoe.c
@@ -2341,20 +2341,19 @@ drop:
 
 /**
  * fcoe_ctlr_disc_recv - discovery receive handler for VN2VN mode.
- * @fip: The FCoE controller
+ * @lport: The local port
+ * @fp: The received frame
  *
  * This should never be called since we don't see RSCNs or other
  * fabric-generated ELSes.
  */
-static void fcoe_ctlr_disc_recv(struct fc_seq *seq, struct fc_frame *fp,
-				struct fc_lport *lport)
+static void fcoe_ctlr_disc_recv(struct fc_lport *lport, struct fc_frame *fp)
 {
 	struct fc_seq_els_data rjt_data;
 
-	rjt_data.fp = NULL;
 	rjt_data.reason = ELS_RJT_UNSUP;
 	rjt_data.explan = ELS_EXPL_NONE;
-	lport->tt.seq_els_rsp_send(seq, ELS_LS_RJT, &rjt_data);
+	lport->tt.seq_els_rsp_send(fp, ELS_LS_RJT, &rjt_data);
 	fc_frame_free(fp);
 }
 
diff --git a/drivers/scsi/libfc/fc_disc.c b/drivers/scsi/libfc/fc_disc.c
index 0447455..32f67c4 100644
--- a/drivers/scsi/libfc/fc_disc.c
+++ b/drivers/scsi/libfc/fc_disc.c
@@ -75,15 +75,13 @@ void fc_disc_stop_rports(struct fc_disc *disc)
 
 /**
  * fc_disc_recv_rscn_req() - Handle Registered State Change Notification (RSCN)
- * @sp:	   The sequence of the RSCN exchange
+ * @disc:  The discovery object to which the RSCN applies
  * @fp:	   The RSCN frame
- * @lport: The local port that the request will be sent on
  *
  * Locking Note: This function expects that the disc_mutex is locked
  *		 before it is called.
  */
-static void fc_disc_recv_rscn_req(struct fc_seq *sp, struct fc_frame *fp,
-				  struct fc_disc *disc)
+static void fc_disc_recv_rscn_req(struct fc_disc *disc, struct fc_frame *fp)
 {
 	struct fc_lport *lport;
 	struct fc_els_rscn *rp;
@@ -151,7 +149,7 @@ static void fc_disc_recv_rscn_req(struct fc_seq *sp, struct fc_frame *fp,
 			break;
 		}
 	}
-	lport->tt.seq_els_rsp_send(sp, ELS_LS_ACC, NULL);
+	lport->tt.seq_els_rsp_send(fp, ELS_LS_ACC, NULL);
 
 	/*
 	 * If not doing a complete rediscovery, do GPN_ID on
@@ -177,25 +175,22 @@ static void fc_disc_recv_rscn_req(struct fc_seq *sp, struct fc_frame *fp,
 	return;
 reject:
 	FC_DISC_DBG(disc, "Received a bad RSCN frame\n");
-	rjt_data.fp = NULL;
 	rjt_data.reason = ELS_RJT_LOGIC;
 	rjt_data.explan = ELS_EXPL_NONE;
-	lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data);
+	lport->tt.seq_els_rsp_send(fp, ELS_LS_RJT, &rjt_data);
 	fc_frame_free(fp);
 }
 
 /**
  * fc_disc_recv_req() - Handle incoming requests
- * @sp:	   The sequence of the request exchange
- * @fp:	   The request frame
  * @lport: The local port receiving the request
+ * @fp:	   The request frame
  *
  * Locking Note: This function is called from the EM and will lock
  *		 the disc_mutex before calling the handler for the
  *		 request.
  */
-static void fc_disc_recv_req(struct fc_seq *sp, struct fc_frame *fp,
-			     struct fc_lport *lport)
+static void fc_disc_recv_req(struct fc_lport *lport, struct fc_frame *fp)
 {
 	u8 op;
 	struct fc_disc *disc = &lport->disc;
@@ -204,7 +199,7 @@ static void fc_disc_recv_req(struct fc_seq *sp, struct fc_frame *fp,
 	switch (op) {
 	case ELS_RSCN:
 		mutex_lock(&disc->disc_mutex);
-		fc_disc_recv_rscn_req(sp, fp, disc);
+		fc_disc_recv_rscn_req(disc, fp);
 		mutex_unlock(&disc->disc_mutex);
 		break;
 	default:
diff --git a/drivers/scsi/libfc/fc_exch.c b/drivers/scsi/libfc/fc_exch.c
index 027042a..b8560ad 100644
--- a/drivers/scsi/libfc/fc_exch.c
+++ b/drivers/scsi/libfc/fc_exch.c
@@ -129,11 +129,11 @@ struct fc_exch_mgr_anchor {
 };
 
 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,
+static void fc_seq_ls_acc(struct fc_frame *);
+static void fc_seq_ls_rjt(struct fc_frame *, enum fc_els_rjt_reason,
 			  enum fc_els_rjt_explan);
-static void fc_exch_els_rec(struct fc_seq *, struct fc_frame *);
-static void fc_exch_els_rrq(struct fc_seq *, struct fc_frame *);
+static void fc_exch_els_rec(struct fc_frame *);
+static void fc_exch_els_rrq(struct fc_frame *);
 
 /*
  * Internal implementation notes.
@@ -1003,28 +1003,30 @@ static void fc_exch_set_addr(struct fc_exch *ep,
 /**
  * fc_seq_els_rsp_send() - Send an ELS response using infomation from
  *			   the existing sequence/exchange.
- * @sp:	      The sequence/exchange to get information from
+ * @fp:	      The received frame
  * @els_cmd:  The ELS command to be sent
  * @els_data: The ELS data to be sent
+ *
+ * The received frame is not freed.
  */
-static void fc_seq_els_rsp_send(struct fc_seq *sp, enum fc_els_cmd els_cmd,
+static void fc_seq_els_rsp_send(struct fc_frame *fp, enum fc_els_cmd els_cmd,
 				struct fc_seq_els_data *els_data)
 {
 	switch (els_cmd) {
 	case ELS_LS_RJT:
-		fc_seq_ls_rjt(sp, els_data->reason, els_data->explan);
+		fc_seq_ls_rjt(fp, els_data->reason, els_data->explan);
 		break;
 	case ELS_LS_ACC:
-		fc_seq_ls_acc(sp);
+		fc_seq_ls_acc(fp);
 		break;
 	case ELS_RRQ:
-		fc_exch_els_rrq(sp, els_data->fp);
+		fc_exch_els_rrq(fp);
 		break;
 	case ELS_REC:
-		fc_exch_els_rec(sp, els_data->fp);
+		fc_exch_els_rec(fp);
 		break;
 	default:
-		FC_EXCH_DBG(fc_seq_exch(sp), "Invalid ELS CMD:%x\n", els_cmd);
+		FC_LPORT_DBG(fr_dev(fp), "Invalid ELS CMD:%x\n", els_cmd);
 	}
 }
 
@@ -1253,11 +1255,13 @@ static struct fc_seq *fc_seq_assign(struct fc_lport *lport, struct fc_frame *fp)
 }
 
 /**
- * fc_exch_recv_req() - Handler for an incoming request where is other
- *			end is originating the sequence
+ * fc_exch_recv_req() - Handler for an incoming request
  * @lport: The local port that received the request
  * @mp:	   The EM that the exchange is on
  * @fp:	   The request frame
+ *
+ * This is used when the other end is originating the exchange
+ * and the sequence.
  */
 static void fc_exch_recv_req(struct fc_lport *lport, struct fc_exch_mgr *mp,
 			     struct fc_frame *fp)
@@ -1275,8 +1279,17 @@ static void fc_exch_recv_req(struct fc_lport *lport, struct fc_exch_mgr *mp,
 		fc_frame_free(fp);
 		return;
 	}
+	fr_dev(fp) = lport;
+
+	BUG_ON(fr_seq(fp));		/* XXX remove later */
+
+	/*
+	 * If the RX_ID is 0xffff, don't allocate an exchange.
+	 * The upper-level protocol may request one later, if needed.
+	 */
+	if (fh->fh_rx_id == htons(FC_XID_UNKNOWN))
+		return lport->tt.lport_recv(lport, fp);
 
-	fr_seq(fp) = NULL;
 	reject = fc_seq_lookup_recip(lport, mp, fp);
 	if (reject == FC_RJT_NONE) {
 		sp = fr_seq(fp);	/* sequence will be held */
@@ -1298,7 +1311,7 @@ static void fc_exch_recv_req(struct fc_lport *lport, struct fc_exch_mgr *mp,
 		if (ep->resp)
 			ep->resp(sp, fp, ep->arg);
 		else
-			lport->tt.lport_recv(lport, sp, fp);
+			lport->tt.lport_recv(lport, fp);
 		fc_exch_release(ep);	/* release from lookup */
 	} else {
 		FC_LPORT_DBG(lport, "exch/seq lookup failed: reject %x\n",
@@ -1566,53 +1579,55 @@ static void fc_exch_recv_bls(struct fc_exch_mgr *mp, struct fc_frame *fp)
 
 /**
  * fc_seq_ls_acc() - Accept sequence with LS_ACC
- * @req_sp: The request sequence
+ * @rx_fp: The received frame, not freed here.
  *
  * If this fails due to allocation or transmit congestion, assume the
  * originator will repeat the sequence.
  */
-static void fc_seq_ls_acc(struct fc_seq *req_sp)
+static void fc_seq_ls_acc(struct fc_frame *rx_fp)
 {
-	struct fc_seq *sp;
+	struct fc_lport *lport;
 	struct fc_els_ls_acc *acc;
 	struct fc_frame *fp;
 
-	sp = fc_seq_start_next(req_sp);
-	fp = fc_frame_alloc(fc_seq_exch(sp)->lp, sizeof(*acc));
-	if (fp) {
-		acc = fc_frame_payload_get(fp, sizeof(*acc));
-		memset(acc, 0, sizeof(*acc));
-		acc->la_cmd = ELS_LS_ACC;
-		fc_seq_send_last(sp, fp, FC_RCTL_ELS_REP, FC_TYPE_ELS);
-	}
+	lport = fr_dev(rx_fp);
+	fp = fc_frame_alloc(lport, sizeof(*acc));
+	if (!fp)
+		return;
+	acc = fc_frame_payload_get(fp, sizeof(*acc));
+	memset(acc, 0, sizeof(*acc));
+	acc->la_cmd = ELS_LS_ACC;
+	fc_fill_reply_hdr(fp, rx_fp, FC_RCTL_ELS_REP, 0);
+	lport->tt.frame_send(lport, fp);
 }
 
 /**
  * fc_seq_ls_rjt() - Reject a sequence with ELS LS_RJT
- * @req_sp: The request sequence
+ * @rx_fp: The received frame, not freed here.
  * @reason: The reason the sequence is being rejected
- * @explan: The explaination for the rejection
+ * @explan: The explanation for the rejection
  *
  * If this fails due to allocation or transmit congestion, assume the
  * originator will repeat the sequence.
  */
-static void fc_seq_ls_rjt(struct fc_seq *req_sp, enum fc_els_rjt_reason reason,
+static void fc_seq_ls_rjt(struct fc_frame *rx_fp, enum fc_els_rjt_reason reason,
 			  enum fc_els_rjt_explan explan)
 {
-	struct fc_seq *sp;
+	struct fc_lport *lport;
 	struct fc_els_ls_rjt *rjt;
 	struct fc_frame *fp;
 
-	sp = fc_seq_start_next(req_sp);
-	fp = fc_frame_alloc(fc_seq_exch(sp)->lp, sizeof(*rjt));
-	if (fp) {
-		rjt = fc_frame_payload_get(fp, sizeof(*rjt));
-		memset(rjt, 0, sizeof(*rjt));
-		rjt->er_cmd = ELS_LS_RJT;
-		rjt->er_reason = reason;
-		rjt->er_explan = explan;
-		fc_seq_send_last(sp, fp, FC_RCTL_ELS_REP, FC_TYPE_ELS);
-	}
+	lport = fr_dev(rx_fp);
+	fp = fc_frame_alloc(lport, sizeof(*rjt));
+	if (!fp)
+		return;
+	rjt = fc_frame_payload_get(fp, sizeof(*rjt));
+	memset(rjt, 0, sizeof(*rjt));
+	rjt->er_cmd = ELS_LS_RJT;
+	rjt->er_reason = reason;
+	rjt->er_explan = explan;
+	fc_fill_reply_hdr(fp, rx_fp, FC_RCTL_ELS_REP, 0);
+	lport->tt.frame_send(lport, fp);
 }
 
 /**
@@ -1715,17 +1730,33 @@ void fc_exch_mgr_reset(struct fc_lport *lport, u32 sid, u32 did)
 EXPORT_SYMBOL(fc_exch_mgr_reset);
 
 /**
+ * fc_exch_lookup() - find an exchange
+ * @lport: The local port
+ * @xid: The exchange ID
+ *
+ * Returns exchange pointer with hold for caller, or NULL if not found.
+ */
+static struct fc_exch *fc_exch_lookup(struct fc_lport *lport, u32 xid)
+{
+	struct fc_exch_mgr_anchor *ema;
+
+	list_for_each_entry(ema, &lport->ema_list, ema_list)
+		if (ema->mp->min_xid <= xid && xid <= ema->mp->max_xid)
+			return fc_exch_find(ema->mp, xid);
+	return NULL;
+}
+
+/**
  * fc_exch_els_rec() - Handler for ELS REC (Read Exchange Concise) requests
- * @sp:	 The sequence the REC is on
- * @rfp: The REC frame
+ * @rfp: The REC frame, not freed here.
  *
  * Note that the requesting port may be different than the S_ID in the request.
  */
-static void fc_exch_els_rec(struct fc_seq *sp, struct fc_frame *rfp)
+static void fc_exch_els_rec(struct fc_frame *rfp)
 {
+	struct fc_lport *lport;
 	struct fc_frame *fp;
 	struct fc_exch *ep;
-	struct fc_exch_mgr *em;
 	struct fc_els_rec *rp;
 	struct fc_els_rec_acc *acc;
 	enum fc_els_rjt_reason reason = ELS_RJT_LOGIC;
@@ -1734,6 +1765,7 @@ static void fc_exch_els_rec(struct fc_seq *sp, struct fc_frame *rfp)
 	u16 rxid;
 	u16 oxid;
 
+	lport = fr_dev(rfp);
 	rp = fc_frame_payload_get(rfp, sizeof(*rp));
 	explan = ELS_EXPL_INV_LEN;
 	if (!rp)
@@ -1742,35 +1774,19 @@ static void fc_exch_els_rec(struct fc_seq *sp, struct fc_frame *rfp)
 	rxid = ntohs(rp->rec_rx_id);
 	oxid = ntohs(rp->rec_ox_id);
 
-	/*
-	 * Currently it's hard to find the local S_ID from the exchange
-	 * manager.  This will eventually be fixed, but for now it's easier
-	 * to lookup the subject exchange twice, once as if we were
-	 * the initiator, and then again if we weren't.
-	 */
-	em = fc_seq_exch(sp)->em;
-	ep = fc_exch_find(em, oxid);
+	ep = fc_exch_lookup(lport,
+			    sid == fc_host_port_id(lport->host) ? oxid : rxid);
 	explan = ELS_EXPL_OXID_RXID;
-	if (ep && ep->oid == sid) {
-		if (ep->rxid != FC_XID_UNKNOWN &&
-		    rxid != FC_XID_UNKNOWN &&
-		    ep->rxid != rxid)
-			goto rel;
-	} else {
-		if (ep)
-			fc_exch_release(ep);
-		ep = NULL;
-		if (rxid != FC_XID_UNKNOWN)
-			ep = fc_exch_find(em, rxid);
-		if (!ep)
-			goto reject;
-	}
-
-	fp = fc_frame_alloc(fc_seq_exch(sp)->lp, sizeof(*acc));
-	if (!fp) {
-		fc_exch_done(sp);
+	if (!ep)
+		goto reject;
+	if (ep->oid != sid || oxid != ep->oxid)
+		goto rel;
+	if (rxid != FC_XID_UNKNOWN && rxid != ep->rxid)
+		goto rel;
+	fp = fc_frame_alloc(lport, sizeof(*acc));
+	if (!fp)
 		goto out;
-	}
+
 	acc = fc_frame_payload_get(fp, sizeof(*acc));
 	memset(acc, 0, sizeof(*acc));
 	acc->reca_cmd = ELS_LS_ACC;
@@ -1785,18 +1801,16 @@ static void fc_exch_els_rec(struct fc_seq *sp, struct fc_frame *rfp)
 	acc->reca_e_stat = htonl(ep->esb_stat & (ESB_ST_RESP |
 						 ESB_ST_SEQ_INIT |
 						 ESB_ST_COMPLETE));
-	sp = fc_seq_start_next(sp);
-	fc_seq_send_last(sp, fp, FC_RCTL_ELS_REP, FC_TYPE_ELS);
+	fc_fill_reply_hdr(fp, rfp, FC_RCTL_ELS_REP, 0);
+	lport->tt.frame_send(lport, fp);
 out:
 	fc_exch_release(ep);
-	fc_frame_free(rfp);
 	return;
 
 rel:
 	fc_exch_release(ep);
 reject:
-	fc_seq_ls_rjt(sp, reason, explan);
-	fc_frame_free(rfp);
+	fc_seq_ls_rjt(rfp, reason, explan);
 }
 
 /**
@@ -1971,20 +1985,20 @@ retry:
 	spin_unlock_bh(&ep->ex_lock);
 }
 
-
 /**
  * fc_exch_els_rrq() - Handler for ELS RRQ (Reset Recovery Qualifier) requests
- * @sp: The sequence that the RRQ is on
- * @fp: The RRQ frame
+ * @fp: The RRQ frame, not freed here.
  */
-static void fc_exch_els_rrq(struct fc_seq *sp, struct fc_frame *fp)
+static void fc_exch_els_rrq(struct fc_frame *fp)
 {
+	struct fc_lport *lport;
 	struct fc_exch *ep = NULL;	/* request or subject exchange */
 	struct fc_els_rrq *rp;
 	u32 sid;
 	u16 xid;
 	enum fc_els_rjt_explan explan;
 
+	lport = fr_dev(fp);
 	rp = fc_frame_payload_get(fp, sizeof(*rp));
 	explan = ELS_EXPL_INV_LEN;
 	if (!rp)
@@ -1993,11 +2007,10 @@ static void fc_exch_els_rrq(struct fc_seq *sp, struct fc_frame *fp)
 	/*
 	 * lookup subject exchange.
 	 */
-	ep = fc_seq_exch(sp);
 	sid = ntoh24(rp->rrq_s_id);		/* subject source */
-	xid = ep->did == sid ? ntohs(rp->rrq_ox_id) : ntohs(rp->rrq_rx_id);
-	ep = fc_exch_find(ep->em, xid);
-
+	xid = fc_host_port_id(lport->host) == sid ?
+			ntohs(rp->rrq_ox_id) : ntohs(rp->rrq_rx_id);
+	ep = fc_exch_lookup(lport, xid);
 	explan = ELS_EXPL_OXID_RXID;
 	if (!ep)
 		goto reject;
@@ -2028,15 +2041,14 @@ static void fc_exch_els_rrq(struct fc_seq *sp, struct fc_frame *fp)
 	/*
 	 * Send LS_ACC.
 	 */
-	fc_seq_ls_acc(sp);
+	fc_seq_ls_acc(fp);
 	goto out;
 
 unlock_reject:
 	spin_unlock_bh(&ep->ex_lock);
 reject:
-	fc_seq_ls_rjt(sp, ELS_RJT_LOGIC, explan);
+	fc_seq_ls_rjt(fp, ELS_RJT_LOGIC, explan);
 out:
-	fc_frame_free(fp);
 	if (ep)
 		fc_exch_release(ep);	/* drop hold from fc_exch_find */
 }
@@ -2267,7 +2279,7 @@ void fc_exch_recv(struct fc_lport *lport, struct fc_frame *fp)
 			fc_exch_recv_seq_resp(ema->mp, fp);
 		else if (f_ctl & FC_FC_SEQ_CTX)
 			fc_exch_recv_resp(ema->mp, fp);
-		else
+		else	/* no EX_CTX and no SEQ_CTX */
 			fc_exch_recv_req(lport, ema->mp, fp);
 		break;
 	default:
diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c
index e50a660..1998c03 100644
--- a/drivers/scsi/libfc/fc_lport.c
+++ b/drivers/scsi/libfc/fc_lport.c
@@ -375,34 +375,31 @@ static void fc_lport_add_fc4_type(struct fc_lport *lport, enum fc_fh_type type)
 
 /**
  * fc_lport_recv_rlir_req() - Handle received Registered Link Incident Report.
- * @sp:	   The sequence in the RLIR exchange
- * @fp:	   The RLIR request frame
  * @lport: Fibre Channel local port recieving the RLIR
+ * @fp:	   The RLIR request frame
  *
  * Locking Note: The lport lock is expected to be held before calling
  * this function.
  */
-static void fc_lport_recv_rlir_req(struct fc_seq *sp, struct fc_frame *fp,
-				   struct fc_lport *lport)
+static void fc_lport_recv_rlir_req(struct fc_lport *lport, struct fc_frame *fp)
 {
 	FC_LPORT_DBG(lport, "Received RLIR request while in state %s\n",
 		     fc_lport_state(lport));
 
-	lport->tt.seq_els_rsp_send(sp, ELS_LS_ACC, NULL);
+	lport->tt.seq_els_rsp_send(fp, ELS_LS_ACC, NULL);
 	fc_frame_free(fp);
 }
 
 /**
  * fc_lport_recv_echo_req() - Handle received ECHO request
- * @sp:	   The sequence in the ECHO exchange
- * @fp:	   ECHO request frame
  * @lport: The local port recieving the ECHO
+ * @fp:	   ECHO request frame
  *
  * Locking Note: The lport lock is expected to be held before calling
  * this function.
  */
-static void fc_lport_recv_echo_req(struct fc_seq *sp, struct fc_frame *in_fp,
-				   struct fc_lport *lport)
+static void fc_lport_recv_echo_req(struct fc_lport *lport,
+				   struct fc_frame *in_fp)
 {
 	struct fc_frame *fp;
 	unsigned int len;
@@ -431,15 +428,14 @@ static void fc_lport_recv_echo_req(struct fc_seq *sp, struct fc_frame *in_fp,
 
 /**
  * fc_lport_recv_rnid_req() - Handle received Request Node ID data request
- * @sp:	   The sequence in the RNID exchange
- * @fp:	   The RNID request frame
  * @lport: The local port recieving the RNID
+ * @fp:	   The RNID request frame
  *
  * Locking Note: The lport lock is expected to be held before calling
  * this function.
  */
-static void fc_lport_recv_rnid_req(struct fc_seq *sp, struct fc_frame *in_fp,
-				   struct fc_lport *lport)
+static void fc_lport_recv_rnid_req(struct fc_lport *lport,
+				   struct fc_frame *in_fp)
 {
 	struct fc_frame *fp;
 	struct fc_els_rnid *req;
@@ -457,10 +453,9 @@ static void fc_lport_recv_rnid_req(struct fc_seq *sp, struct fc_frame *in_fp,
 
 	req = fc_frame_payload_get(in_fp, sizeof(*req));
 	if (!req) {
-		rjt_data.fp = NULL;
 		rjt_data.reason = ELS_RJT_LOGIC;
 		rjt_data.explan = ELS_EXPL_NONE;
-		lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data);
+		lport->tt.seq_els_rsp_send(in_fp, ELS_LS_RJT, &rjt_data);
 	} else {
 		fmt = req->rnid_fmt;
 		len = sizeof(*rp);
@@ -492,17 +487,15 @@ static void fc_lport_recv_rnid_req(struct fc_seq *sp, struct fc_frame *in_fp,
 
 /**
  * fc_lport_recv_logo_req() - Handle received fabric LOGO request
- * @sp:	   The sequence in the LOGO exchange
- * @fp:	   The LOGO request frame
  * @lport: The local port recieving the LOGO
+ * @fp:	   The LOGO request frame
  *
  * Locking Note: The lport lock is exected to be held before calling
  * this function.
  */
-static void fc_lport_recv_logo_req(struct fc_seq *sp, struct fc_frame *fp,
-				   struct fc_lport *lport)
+static void fc_lport_recv_logo_req(struct fc_lport *lport, struct fc_frame *fp)
 {
-	lport->tt.seq_els_rsp_send(sp, ELS_LS_ACC, NULL);
+	lport->tt.seq_els_rsp_send(fp, ELS_LS_ACC, NULL);
 	fc_lport_enter_reset(lport);
 	fc_frame_free(fp);
 }
@@ -773,9 +766,8 @@ EXPORT_SYMBOL(fc_lport_set_local_id);
 
 /**
  * fc_lport_recv_flogi_req() - Receive a FLOGI request
- * @sp_in: The sequence the FLOGI is on
- * @rx_fp: The FLOGI frame
  * @lport: The local port that recieved the request
+ * @rx_fp: The FLOGI frame
  *
  * A received FLOGI request indicates a point-to-point connection.
  * Accept it with the common service parameters indicating our N port.
@@ -784,13 +776,11 @@ EXPORT_SYMBOL(fc_lport_set_local_id);
  * Locking Note: The lport lock is expected to be held before calling
  * this function.
  */
-static void fc_lport_recv_flogi_req(struct fc_seq *sp_in,
-				    struct fc_frame *rx_fp,
-				    struct fc_lport *lport)
+static void fc_lport_recv_flogi_req(struct fc_lport *lport,
+				    struct fc_frame *rx_fp)
 {
 	struct fc_frame *fp;
 	struct fc_frame_header *fh;
-	struct fc_seq *sp;
 	struct fc_els_flogi *flp;
 	struct fc_els_flogi *new_flp;
 	u64 remote_wwpn;
@@ -850,16 +840,13 @@ static void fc_lport_recv_flogi_req(struct fc_seq *sp_in,
 	}
 	fc_lport_ptp_setup(lport, remote_fid, remote_wwpn,
 			   get_unaligned_be64(&flp->fl_wwnn));
-
 out:
-	sp = fr_seq(rx_fp);
 	fc_frame_free(rx_fp);
 }
 
 /**
  * fc_lport_recv_req() - The generic lport request handler
  * @lport: The local port that received the request
- * @sp:	   The sequence the request is on
  * @fp:	   The request frame
  *
  * This function will see if the lport handles the request or
@@ -868,11 +855,10 @@ out:
  * Locking Note: This function should not be called with the lport
  *		 lock held becuase it will grab the lock.
  */
-static void fc_lport_recv_req(struct fc_lport *lport, struct fc_seq *sp,
-			      struct fc_frame *fp)
+static void fc_lport_recv_req(struct fc_lport *lport, struct fc_frame *fp)
 {
 	struct fc_frame_header *fh = fc_frame_header_get(fp);
-	void (*recv) (struct fc_seq *, struct fc_frame *, struct fc_lport *);
+	void (*recv)(struct fc_lport *, struct fc_frame *);
 
 	mutex_lock(&lport->lp_mutex);
 
@@ -912,19 +898,13 @@ static void fc_lport_recv_req(struct fc_lport *lport, struct fc_seq *sp,
 			break;
 		}
 
-		recv(sp, fp, lport);
+		recv(lport, fp);
 	} else {
 		FC_LPORT_DBG(lport, "dropping invalid frame (eof %x)\n",
 			     fr_eof(fp));
 		fc_frame_free(fp);
 	}
 	mutex_unlock(&lport->lp_mutex);
-
-	/*
-	 *  The common exch_done for all request may not be good
-	 *  if any request requires longer hold on exhange. XXX
-	 */
-	lport->tt.exch_done(sp);
 }
 
 /**
diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c
index 5987951..25479cc 100644
--- a/drivers/scsi/libfc/fc_rport.c
+++ b/drivers/scsi/libfc/fc_rport.c
@@ -68,14 +68,10 @@ static void fc_rport_enter_ready(struct fc_rport_priv *);
 static void fc_rport_enter_logo(struct fc_rport_priv *);
 static void fc_rport_enter_adisc(struct fc_rport_priv *);
 
-static void fc_rport_recv_plogi_req(struct fc_lport *,
-				    struct fc_seq *, struct fc_frame *);
-static void fc_rport_recv_prli_req(struct fc_rport_priv *,
-				   struct fc_seq *, struct fc_frame *);
-static void fc_rport_recv_prlo_req(struct fc_rport_priv *,
-				   struct fc_seq *, struct fc_frame *);
-static void fc_rport_recv_logo_req(struct fc_lport *,
-				   struct fc_seq *, struct fc_frame *);
+static void fc_rport_recv_plogi_req(struct fc_lport *, struct fc_frame *);
+static void fc_rport_recv_prli_req(struct fc_rport_priv *, struct fc_frame *);
+static void fc_rport_recv_prlo_req(struct fc_rport_priv *, struct fc_frame *);
+static void fc_rport_recv_logo_req(struct fc_lport *, struct fc_frame *);
 static void fc_rport_timeout(struct work_struct *);
 static void fc_rport_error(struct fc_rport_priv *, struct fc_frame *);
 static void fc_rport_error_retry(struct fc_rport_priv *, struct fc_frame *);
@@ -736,11 +732,10 @@ static void fc_rport_enter_flogi(struct fc_rport_priv *rdata)
 /**
  * fc_rport_recv_flogi_req() - Handle Fabric Login (FLOGI) request in p-mp mode
  * @lport: The local port that received the PLOGI request
- * @sp:	   The sequence that the PLOGI request was on
  * @rx_fp: The PLOGI request frame
  */
 static void fc_rport_recv_flogi_req(struct fc_lport *lport,
-				    struct fc_seq *sp, struct fc_frame *rx_fp)
+				    struct fc_frame *rx_fp)
 {
 	struct fc_disc *disc;
 	struct fc_els_flogi *flp;
@@ -749,7 +744,6 @@ static void fc_rport_recv_flogi_req(struct fc_lport *lport,
 	struct fc_seq_els_data rjt_data;
 	u32 sid;
 
-	rjt_data.fp = NULL;
 	sid = fc_frame_sid(fp);
 
 	FC_RPORT_ID_DBG(lport, sid, "Received FLOGI request\n");
@@ -817,7 +811,6 @@ static void fc_rport_recv_flogi_req(struct fc_lport *lport,
 	if (!fp)
 		goto out;
 
-	sp = lport->tt.seq_start_next(sp);
 	fc_flogi_fill(lport, fp);
 	flp = fc_frame_payload_get(fp, sizeof(*flp));
 	flp->fl_cmd = ELS_LS_ACC;
@@ -837,7 +830,7 @@ out:
 
 reject:
 	mutex_unlock(&disc->disc_mutex);
-	lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data);
+	lport->tt.seq_els_rsp_send(rx_fp, ELS_LS_RJT, &rjt_data);
 	fc_frame_free(rx_fp);
 }
 
@@ -1296,13 +1289,12 @@ static void fc_rport_enter_adisc(struct fc_rport_priv *rdata)
 /**
  * fc_rport_recv_adisc_req() - Handler for Address Discovery (ADISC) requests
  * @rdata: The remote port that sent the ADISC request
- * @sp:	   The sequence the ADISC request was on
  * @in_fp: The ADISC request frame
  *
  * Locking Note:  Called with the lport and rport locks held.
  */
 static void fc_rport_recv_adisc_req(struct fc_rport_priv *rdata,
-				    struct fc_seq *sp, struct fc_frame *in_fp)
+				    struct fc_frame *in_fp)
 {
 	struct fc_lport *lport = rdata->local_port;
 	struct fc_frame *fp;
@@ -1313,10 +1305,9 @@ static void fc_rport_recv_adisc_req(struct fc_rport_priv *rdata,
 
 	adisc = fc_frame_payload_get(in_fp, sizeof(*adisc));
 	if (!adisc) {
-		rjt_data.fp = NULL;
 		rjt_data.reason = ELS_RJT_PROT;
 		rjt_data.explan = ELS_EXPL_INV_LEN;
-		lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data);
+		lport->tt.seq_els_rsp_send(in_fp, ELS_LS_RJT, &rjt_data);
 		goto drop;
 	}
 
@@ -1335,14 +1326,13 @@ drop:
 /**
  * fc_rport_recv_rls_req() - Handle received Read Link Status request
  * @rdata: The remote port that sent the RLS request
- * @sp:	The sequence that the RLS was on
  * @rx_fp: The PRLI request frame
  *
  * Locking Note: The rport lock is expected to be held before calling
  * this function.
  */
 static void fc_rport_recv_rls_req(struct fc_rport_priv *rdata,
-				  struct fc_seq *sp, struct fc_frame *rx_fp)
+				  struct fc_frame *rx_fp)
 
 {
 	struct fc_lport *lport = rdata->local_port;
@@ -1393,8 +1383,7 @@ static void fc_rport_recv_rls_req(struct fc_rport_priv *rdata,
 	goto out;
 
 out_rjt:
-	rjt_data.fp = NULL;
-	lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data);
+	lport->tt.seq_els_rsp_send(rx_fp, ELS_LS_RJT, &rjt_data);
 out:
 	fc_frame_free(rx_fp);
 }
@@ -1402,7 +1391,6 @@ out:
 /**
  * fc_rport_recv_els_req() - Handler for validated ELS requests
  * @lport: The local port that received the ELS request
- * @sp:	   The sequence that the ELS request was on
  * @fp:	   The ELS request frame
  *
  * Handle incoming ELS requests that require port login.
@@ -1410,16 +1398,11 @@ out:
  *
  * Locking Note: Called with the lport lock held.
  */
-static void fc_rport_recv_els_req(struct fc_lport *lport,
-				  struct fc_seq *sp, struct fc_frame *fp)
+static void fc_rport_recv_els_req(struct fc_lport *lport, struct fc_frame *fp)
 {
 	struct fc_rport_priv *rdata;
 	struct fc_seq_els_data els_data;
 
-	els_data.fp = NULL;
-	els_data.reason = ELS_RJT_UNAB;
-	els_data.explan = ELS_EXPL_PLOGI_REQD;
-
 	mutex_lock(&lport->disc.disc_mutex);
 	rdata = lport->tt.rport_lookup(lport, fc_frame_sid(fp));
 	if (!rdata) {
@@ -1442,24 +1425,24 @@ static void fc_rport_recv_els_req(struct fc_lport *lport,
 
 	switch (fc_frame_payload_op(fp)) {
 	case ELS_PRLI:
-		fc_rport_recv_prli_req(rdata, sp, fp);
+		fc_rport_recv_prli_req(rdata, fp);
 		break;
 	case ELS_PRLO:
-		fc_rport_recv_prlo_req(rdata, sp, fp);
+		fc_rport_recv_prlo_req(rdata, fp);
 		break;
 	case ELS_ADISC:
-		fc_rport_recv_adisc_req(rdata, sp, fp);
+		fc_rport_recv_adisc_req(rdata, fp);
 		break;
 	case ELS_RRQ:
-		els_data.fp = fp;
-		lport->tt.seq_els_rsp_send(sp, ELS_RRQ, &els_data);
+		lport->tt.seq_els_rsp_send(fp, ELS_RRQ, NULL);
+		fc_frame_free(fp);
 		break;
 	case ELS_REC:
-		els_data.fp = fp;
-		lport->tt.seq_els_rsp_send(sp, ELS_REC, &els_data);
+		lport->tt.seq_els_rsp_send(fp, ELS_REC, NULL);
+		fc_frame_free(fp);
 		break;
 	case ELS_RLS:
-		fc_rport_recv_rls_req(rdata, sp, fp);
+		fc_rport_recv_rls_req(rdata, fp);
 		break;
 	default:
 		fc_frame_free(fp);	/* can't happen */
@@ -1470,20 +1453,20 @@ static void fc_rport_recv_els_req(struct fc_lport *lport,
 	return;
 
 reject:
-	lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &els_data);
+	els_data.reason = ELS_RJT_UNAB;
+	els_data.explan = ELS_EXPL_PLOGI_REQD;
+	lport->tt.seq_els_rsp_send(fp, ELS_LS_RJT, &els_data);
 	fc_frame_free(fp);
 }
 
 /**
  * fc_rport_recv_req() - Handler for requests
- * @sp:	   The sequence the request was on
- * @fp:	   The request frame
  * @lport: The local port that received the request
+ * @fp:	   The request frame
  *
  * Locking Note: Called with the lport lock held.
  */
-void fc_rport_recv_req(struct fc_seq *sp, struct fc_frame *fp,
-		       struct fc_lport *lport)
+void fc_rport_recv_req(struct fc_lport *lport, struct fc_frame *fp)
 {
 	struct fc_seq_els_data els_data;
 
@@ -1495,13 +1478,13 @@ void fc_rport_recv_req(struct fc_seq *sp, struct fc_frame *fp,
 	 */
 	switch (fc_frame_payload_op(fp)) {
 	case ELS_FLOGI:
-		fc_rport_recv_flogi_req(lport, sp, fp);
+		fc_rport_recv_flogi_req(lport, fp);
 		break;
 	case ELS_PLOGI:
-		fc_rport_recv_plogi_req(lport, sp, fp);
+		fc_rport_recv_plogi_req(lport, fp);
 		break;
 	case ELS_LOGO:
-		fc_rport_recv_logo_req(lport, sp, fp);
+		fc_rport_recv_logo_req(lport, fp);
 		break;
 	case ELS_PRLI:
 	case ELS_PRLO:
@@ -1509,14 +1492,13 @@ void fc_rport_recv_req(struct fc_seq *sp, struct fc_frame *fp,
 	case ELS_RRQ:
 	case ELS_REC:
 	case ELS_RLS:
-		fc_rport_recv_els_req(lport, sp, fp);
+		fc_rport_recv_els_req(lport, fp);
 		break;
 	default:
-		fc_frame_free(fp);
-		els_data.fp = NULL;
 		els_data.reason = ELS_RJT_UNSUP;
 		els_data.explan = ELS_EXPL_NONE;
-		lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &els_data);
+		lport->tt.seq_els_rsp_send(fp, ELS_LS_RJT, &els_data);
+		fc_frame_free(fp);
 		break;
 	}
 }
@@ -1524,13 +1506,12 @@ void fc_rport_recv_req(struct fc_seq *sp, struct fc_frame *fp,
 /**
  * fc_rport_recv_plogi_req() - Handler for Port Login (PLOGI) requests
  * @lport: The local port that received the PLOGI request
- * @sp:	   The sequence that the PLOGI request was on
  * @rx_fp: The PLOGI request frame
  *
  * Locking Note: The rport lock is held before calling this function.
  */
 static void fc_rport_recv_plogi_req(struct fc_lport *lport,
-				    struct fc_seq *sp, struct fc_frame *rx_fp)
+				    struct fc_frame *rx_fp)
 {
 	struct fc_disc *disc;
 	struct fc_rport_priv *rdata;
@@ -1539,7 +1520,6 @@ static void fc_rport_recv_plogi_req(struct fc_lport *lport,
 	struct fc_seq_els_data rjt_data;
 	u32 sid;
 
-	rjt_data.fp = NULL;
 	sid = fc_frame_sid(fp);
 
 	FC_RPORT_ID_DBG(lport, sid, "Received PLOGI request\n");
@@ -1635,21 +1615,20 @@ out:
 	return;
 
 reject:
-	lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data);
+	lport->tt.seq_els_rsp_send(fp, ELS_LS_RJT, &rjt_data);
 	fc_frame_free(fp);
 }
 
 /**
  * fc_rport_recv_prli_req() - Handler for process login (PRLI) requests
  * @rdata: The remote port that sent the PRLI request
- * @sp:	   The sequence that the PRLI was on
  * @rx_fp: The PRLI request frame
  *
  * Locking Note: The rport lock is exected to be held before calling
  * this function.
  */
 static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata,
-				   struct fc_seq *sp, struct fc_frame *rx_fp)
+				   struct fc_frame *rx_fp)
 {
 	struct fc_lport *lport = rdata->local_port;
 	struct fc_frame *fp;
@@ -1666,7 +1645,6 @@ static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata,
 	u32 fcp_parm;
 	u32 roles = FC_RPORT_ROLE_UNKNOWN;
 
-	rjt_data.fp = NULL;
 	FC_RPORT_DBG(rdata, "Received PRLI request while in state %s\n",
 		     fc_rport_state(rdata));
 
@@ -1759,7 +1737,7 @@ reject_len:
 	rjt_data.reason = ELS_RJT_PROT;
 	rjt_data.explan = ELS_EXPL_INV_LEN;
 reject:
-	lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data);
+	lport->tt.seq_els_rsp_send(rx_fp, ELS_LS_RJT, &rjt_data);
 drop:
 	fc_frame_free(rx_fp);
 }
@@ -1767,18 +1745,15 @@ drop:
 /**
  * fc_rport_recv_prlo_req() - Handler for process logout (PRLO) requests
  * @rdata: The remote port that sent the PRLO request
- * @sp:	   The sequence that the PRLO was on
  * @rx_fp: The PRLO request frame
  *
  * Locking Note: The rport lock is exected to be held before calling
  * this function.
  */
 static void fc_rport_recv_prlo_req(struct fc_rport_priv *rdata,
-				   struct fc_seq *sp,
 				   struct fc_frame *rx_fp)
 {
 	struct fc_lport *lport = rdata->local_port;
-	struct fc_exch *ep;
 	struct fc_frame *fp;
 	struct {
 		struct fc_els_prlo prlo;
@@ -1790,8 +1765,6 @@ static void fc_rport_recv_prlo_req(struct fc_rport_priv *rdata,
 	unsigned int plen;
 	struct fc_seq_els_data rjt_data;
 
-	rjt_data.fp = NULL;
-
 	FC_RPORT_DBG(rdata, "Received PRLO request while in state %s\n",
 		     fc_rport_state(rdata));
 
@@ -1814,8 +1787,6 @@ static void fc_rport_recv_prlo_req(struct fc_rport_priv *rdata,
 		goto reject;
 	}
 
-	sp = lport->tt.seq_start_next(sp);
-	WARN_ON(!sp);
 	pp = fc_frame_payload_get(fp, len);
 	WARN_ON(!pp);
 	memset(pp, 0, len);
@@ -1829,17 +1800,15 @@ static void fc_rport_recv_prlo_req(struct fc_rport_priv *rdata,
 
 	fc_rport_enter_delete(rdata, RPORT_EV_LOGO);
 
-	ep = fc_seq_exch(sp);
-	fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid,
-		       FC_TYPE_ELS, FC_FCTL_RESP, 0);
-	lport->tt.seq_send(lport, sp, fp);
+	fc_fill_reply_hdr(fp, rx_fp, FC_RCTL_ELS_REP, 0);
+	lport->tt.frame_send(lport, fp);
 	goto drop;
 
 reject_len:
 	rjt_data.reason = ELS_RJT_PROT;
 	rjt_data.explan = ELS_EXPL_INV_LEN;
 reject:
-	lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data);
+	lport->tt.seq_els_rsp_send(rx_fp, ELS_LS_RJT, &rjt_data);
 drop:
 	fc_frame_free(rx_fp);
 }
@@ -1847,20 +1816,17 @@ drop:
 /**
  * fc_rport_recv_logo_req() - Handler for logout (LOGO) requests
  * @lport: The local port that received the LOGO request
- * @sp:	   The sequence that the LOGO request was on
  * @fp:	   The LOGO request frame
  *
  * Locking Note: The rport lock is exected to be held before calling
  * this function.
  */
-static void fc_rport_recv_logo_req(struct fc_lport *lport,
-				   struct fc_seq *sp,
-				   struct fc_frame *fp)
+static void fc_rport_recv_logo_req(struct fc_lport *lport, struct fc_frame *fp)
 {
 	struct fc_rport_priv *rdata;
 	u32 sid;
 
-	lport->tt.seq_els_rsp_send(sp, ELS_LS_ACC, NULL);
+	lport->tt.seq_els_rsp_send(fp, ELS_LS_ACC, NULL);
 
 	sid = fc_frame_sid(fp);
 
diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h
index 605f1d7..14be49b 100644
--- a/include/scsi/libfc.h
+++ b/include/scsi/libfc.h
@@ -249,14 +249,12 @@ struct fcoe_dev_stats {
 
 /**
  * struct fc_seq_els_data - ELS data used for passing ELS specific responses
- * @fp:     The ELS frame
  * @reason: The reason for rejection
  * @explan: The explaination of the rejection
  *
  * Mainly used by the exchange manager layer.
  */
 struct fc_seq_els_data {
-	struct fc_frame *fp;
 	enum fc_els_rjt_reason reason;
 	enum fc_els_rjt_explan explan;
 };
@@ -519,12 +517,11 @@ struct libfc_function_template {
 			struct fc_frame *);
 
 	/*
-	 * Send an ELS response using infomation from a previous
-	 * exchange and sequence.
+	 * Send an ELS response using infomation from the received frame.
 	 *
 	 * STATUS: OPTIONAL
 	 */
-	void (*seq_els_rsp_send)(struct fc_seq *, enum fc_els_cmd,
+	void (*seq_els_rsp_send)(struct fc_frame *, enum fc_els_cmd,
 				 struct fc_seq_els_data *);
 
 	/*
@@ -583,8 +580,7 @@ struct libfc_function_template {
 	 *
 	 * STATUS: OPTIONAL
 	 */
-	void (*lport_recv)(struct fc_lport *, struct fc_seq *,
-			   struct fc_frame *);
+	void (*lport_recv)(struct fc_lport *, struct fc_frame *);
 
 	/*
 	 * Reset the local port.
@@ -646,8 +642,7 @@ struct libfc_function_template {
 	 *
 	 * STATUS: OPTIONAL
 	 */
-	void (*rport_recv_req)(struct fc_seq *, struct fc_frame *,
-			       struct fc_lport *);
+	void (*rport_recv_req)(struct fc_lport *, struct fc_frame *);
 
 	/*
 	 * lookup an rport by it's port ID.
@@ -693,8 +688,7 @@ struct libfc_function_template {
 	 *
 	 * STATUS: OPTIONAL
 	 */
-	void (*disc_recv_req)(struct fc_seq *, struct fc_frame *,
-			      struct fc_lport *);
+	void (*disc_recv_req)(struct fc_lport *, struct fc_frame *);
 
 	/*
 	 * Start discovery for a local port.


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 25/28] fcoe: fix offload feature flag change from netdev
  2010-07-20 22:19 [PATCH 00/28] libfc, libfcoe and fcoe updates for scsi-misc Robert Love
                   ` (23 preceding siblings ...)
  2010-07-20 22:21 ` [PATCH 24/28] libfc: don't require a local exchange " Robert Love
@ 2010-07-20 22:21 ` Robert Love
  2010-07-20 22:21 ` [PATCH 26/28] Revert "[SCSI] fcoe: Fix using VLAN ID in creating lport's WWWN/WWPN" Robert Love
                   ` (2 subsequent siblings)
  27 siblings, 0 replies; 34+ messages in thread
From: Robert Love @ 2010-07-20 22:21 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi; +Cc: Yi Zou

From: Yi Zou <yi.zou@intel.com>

Currently, when FCoE netdev feature flags are toggled by the LLD, lport's
corresponding flags are not updated. This causes the fc_fcp to still try to
offload the I/O. This patch adds support of NETDEV_FEAT_CHANGE event in fcoe
netdev device notification callback so we can update the lport offload flags
appropriately.

Signed-off-by: Yi Zou <yi.zou@intel.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---
 drivers/scsi/fcoe/fcoe.c |   68 +++++++++++++++++++++++++++++++++-------------
 1 files changed, 49 insertions(+), 19 deletions(-)

diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c
index ab6ea60..cf9d718 100644
--- a/drivers/scsi/fcoe/fcoe.c
+++ b/drivers/scsi/fcoe/fcoe.c
@@ -589,6 +589,50 @@ static int fcoe_get_wwn(struct net_device *netdev, u64 *wwn, int type)
 }
 
 /**
+ * fcoe_netdev_features_change - Updates the lport's offload flags based
+ * on the LLD netdev's FCoE feature flags
+ */
+static void fcoe_netdev_features_change(struct fc_lport *lport,
+					struct net_device *netdev)
+{
+	mutex_lock(&lport->lp_mutex);
+
+	if (netdev->features & NETIF_F_SG)
+		lport->sg_supp = 1;
+	else
+		lport->sg_supp = 0;
+
+	if (netdev->features & NETIF_F_FCOE_CRC) {
+		lport->crc_offload = 1;
+		FCOE_NETDEV_DBG(netdev, "Supports FCCRC offload\n");
+	} else {
+		lport->crc_offload = 0;
+	}
+
+	if (netdev->features & NETIF_F_FSO) {
+		lport->seq_offload = 1;
+		lport->lso_max = netdev->gso_max_size;
+		FCOE_NETDEV_DBG(netdev, "Supports LSO for max len 0x%x\n",
+				lport->lso_max);
+	} else {
+		lport->seq_offload = 0;
+		lport->lso_max = 0;
+	}
+
+	if (netdev->fcoe_ddp_xid) {
+		lport->lro_enabled = 1;
+		lport->lro_xid = netdev->fcoe_ddp_xid;
+		FCOE_NETDEV_DBG(netdev, "Supports LRO for max xid 0x%x\n",
+				lport->lro_xid);
+	} else {
+		lport->lro_enabled = 0;
+		lport->lro_xid = 0;
+	}
+
+	mutex_unlock(&lport->lp_mutex);
+}
+
+/**
  * fcoe_netdev_config() - Set up net devive for SW FCoE
  * @lport:  The local port that is associated with the net device
  * @netdev: The associated net device
@@ -624,25 +668,8 @@ static int fcoe_netdev_config(struct fc_lport *lport, struct net_device *netdev)
 		return -EINVAL;
 
 	/* offload features support */
-	if (netdev->features & NETIF_F_SG)
-		lport->sg_supp = 1;
+	fcoe_netdev_features_change(lport, netdev);
 
-	if (netdev->features & NETIF_F_FCOE_CRC) {
-		lport->crc_offload = 1;
-		FCOE_NETDEV_DBG(netdev, "Supports FCCRC offload\n");
-	}
-	if (netdev->features & NETIF_F_FSO) {
-		lport->seq_offload = 1;
-		lport->lso_max = netdev->gso_max_size;
-		FCOE_NETDEV_DBG(netdev, "Supports LSO for max len 0x%x\n",
-				lport->lso_max);
-	}
-	if (netdev->fcoe_ddp_xid) {
-		lport->lro_enabled = 1;
-		lport->lro_xid = netdev->fcoe_ddp_xid;
-		FCOE_NETDEV_DBG(netdev, "Supports LRO for max xid 0x%x\n",
-				lport->lro_xid);
-	}
 	skb_queue_head_init(&port->fcoe_pending_queue);
 	port->fcoe_pending_queue_active = 0;
 	setup_timer(&port->timer, fcoe_queue_timer, (unsigned long)lport);
@@ -1861,6 +1888,9 @@ static int fcoe_device_notification(struct notifier_block *notifier,
 		schedule_work(&port->destroy_work);
 		goto out;
 		break;
+	case NETDEV_FEAT_CHANGE:
+		fcoe_netdev_features_change(lport, netdev);
+		break;
 	default:
 		FCOE_NETDEV_DBG(netdev, "Unknown event %ld "
 				"from netdev netlink\n", event);
@@ -2056,8 +2086,8 @@ static int fcoe_destroy(const char *buffer, struct kernel_param *kp)
 		rc = -ENODEV;
 		goto out_putdev;
 	}
-	list_del(&fcoe->list);
 	fcoe_interface_cleanup(fcoe);
+	list_del(&fcoe->list);
 	/* RTNL mutex is dropped by fcoe_if_destroy */
 	fcoe_if_destroy(fcoe->ctlr.lp);
 


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 26/28] Revert "[SCSI] fcoe: Fix using VLAN ID in creating lport's WWWN/WWPN"
  2010-07-20 22:19 [PATCH 00/28] libfc, libfcoe and fcoe updates for scsi-misc Robert Love
                   ` (24 preceding siblings ...)
  2010-07-20 22:21 ` [PATCH 25/28] fcoe: fix offload feature flag change from netdev Robert Love
@ 2010-07-20 22:21 ` Robert Love
  2010-07-20 22:21 ` [PATCH 27/28] libfc: Add retry logic to lport state machine when receiving LS_RJT Robert Love
  2010-07-20 22:21 ` [PATCH 28/28] fcoe: remove check for zero fabric name Robert Love
  27 siblings, 0 replies; 34+ messages in thread
From: Robert Love @ 2010-07-20 22:21 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi; +Cc: Vasu Dev

From: Vasu Dev <vasu.dev@intel.com>

This reverts commit cc0136c2e9c10e889cb36e39710c0eb10707b396.

That commit introduced vlan id info to WWPN but WWPN needs to
remain static as an unique port identifier in the fabric, therefore
variable fabric vlan id info doesn't need to be coded inside WWPN.

After this revert, port arg to fcoe_wwn_from_mac is always zero
but still leaving it as-is okay to later allow users to use NAA 2
scheme with this additional port arg.

Note with this patch, existing zoning using WWPN would require
re-zoning this time only and later no more re-zoning due to any
vlan id changes.

Signed-off-by: Vasu Dev <vasu.dev@intel.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---
 drivers/scsi/fcoe/fcoe.c |   11 +----------
 1 files changed, 1 insertions(+), 10 deletions(-)

diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c
index cf9d718..ddd2ca2 100644
--- a/drivers/scsi/fcoe/fcoe.c
+++ b/drivers/scsi/fcoe/fcoe.c
@@ -647,7 +647,6 @@ static int fcoe_netdev_config(struct fc_lport *lport, struct net_device *netdev)
 	u64 wwnn, wwpn;
 	struct fcoe_interface *fcoe;
 	struct fcoe_port *port;
-	int vid = 0;
 
 	/* Setup lport private data to point to fcoe softc */
 	port = lport_priv(lport);
@@ -677,20 +676,12 @@ static int fcoe_netdev_config(struct fc_lport *lport, struct net_device *netdev)
 	fcoe_link_speed_update(lport);
 
 	if (!lport->vport) {
-		/*
-		 * Use NAA 1&2 (FC-FS Rev. 2.0, Sec. 15) to generate WWNN/WWPN:
-		 * For WWNN, we use NAA 1 w/ bit 27-16 of word 0 as 0.
-		 * For WWPN, we use NAA 2 w/ bit 27-16 of word 0 from VLAN ID
-		 */
-		if (netdev->priv_flags & IFF_802_1Q_VLAN)
-			vid = vlan_dev_vlan_id(netdev);
-
 		if (fcoe_get_wwn(netdev, &wwnn, NETDEV_FCOE_WWNN))
 			wwnn = fcoe_wwn_from_mac(fcoe->ctlr.ctl_src_addr, 1, 0);
 		fc_set_wwnn(lport, wwnn);
 		if (fcoe_get_wwn(netdev, &wwpn, NETDEV_FCOE_WWPN))
 			wwpn = fcoe_wwn_from_mac(fcoe->ctlr.ctl_src_addr,
-						 2, vid);
+						 2, 0);
 		fc_set_wwpn(lport, wwpn);
 	}
 


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 27/28] libfc: Add retry logic to lport state machine when receiving LS_RJT
  2010-07-20 22:19 [PATCH 00/28] libfc, libfcoe and fcoe updates for scsi-misc Robert Love
                   ` (25 preceding siblings ...)
  2010-07-20 22:21 ` [PATCH 26/28] Revert "[SCSI] fcoe: Fix using VLAN ID in creating lport's WWWN/WWPN" Robert Love
@ 2010-07-20 22:21 ` Robert Love
  2010-07-20 22:21 ` [PATCH 28/28] fcoe: remove check for zero fabric name Robert Love
  27 siblings, 0 replies; 34+ messages in thread
From: Robert Love @ 2010-07-20 22:21 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi; +Cc: Bhanu Prakash Gollapudi

From: Bhanu Prakash Gollapudi <bprakash@broadcom.com>

Call fc_lport_error to retry upto max retry count when
FLOGI/SCR/NS gets rejected.

Signed-off-by: Bhanu Prakash Gollapudi <bprakash@broadcom.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---
 drivers/scsi/libfc/fc_lport.c |   63 ++++++++++++++++++-----------------------
 1 files changed, 27 insertions(+), 36 deletions(-)

diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c
index 1998c03..6eb334a 100644
--- a/drivers/scsi/libfc/fc_lport.c
+++ b/drivers/scsi/libfc/fc_lport.c
@@ -1012,38 +1012,24 @@ static void fc_lport_error(struct fc_lport *lport, struct fc_frame *fp)
 		     PTR_ERR(fp), fc_lport_state(lport),
 		     lport->retry_count);
 
-	if (!fp || PTR_ERR(fp) == -FC_EX_TIMEOUT) {
-		/*
-		 * Memory allocation failure, or the exchange timed out.
-		 *  Retry after delay
-		 */
-		if (lport->retry_count < lport->max_retry_count) {
-			lport->retry_count++;
-			if (!fp)
-				delay = msecs_to_jiffies(500);
-			else
-				delay =	msecs_to_jiffies(lport->e_d_tov);
-
-			schedule_delayed_work(&lport->retry_work, delay);
-		} else {
-			switch (lport->state) {
-			case LPORT_ST_DISABLED:
-			case LPORT_ST_READY:
-			case LPORT_ST_RESET:
-			case LPORT_ST_RNN_ID:
-			case LPORT_ST_RSNN_NN:
-			case LPORT_ST_RSPN_ID:
-			case LPORT_ST_RFT_ID:
-			case LPORT_ST_RFF_ID:
-			case LPORT_ST_SCR:
-			case LPORT_ST_DNS:
-			case LPORT_ST_FLOGI:
-			case LPORT_ST_LOGO:
-				fc_lport_enter_reset(lport);
-				break;
-			}
-		}
-	}
+	if (PTR_ERR(fp) == -FC_EX_CLOSED)
+		return;
+
+	/*
+	 * Memory allocation failure, or the exchange timed out
+	 * or we received LS_RJT.
+	 * Retry after delay
+	 */
+	if (lport->retry_count < lport->max_retry_count) {
+		lport->retry_count++;
+		if (!fp)
+			delay = msecs_to_jiffies(500);
+		else
+			delay =	msecs_to_jiffies(lport->e_d_tov);
+
+		schedule_delayed_work(&lport->retry_work, delay);
+	} else
+		fc_lport_enter_reset(lport);
 }
 
 /**
@@ -1461,7 +1447,13 @@ void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp,
 	}
 
 	did = fc_frame_did(fp);
-	if (fc_frame_payload_op(fp) == ELS_LS_ACC && did != 0) {
+
+	if (!did) {
+		FC_LPORT_DBG(lport, "Bad FLOGI response\n");
+		goto out;
+	}
+
+	if (fc_frame_payload_op(fp) == ELS_LS_ACC) {
 		flp = fc_frame_payload_get(fp, sizeof(*flp));
 		if (flp) {
 			mfs = ntohs(flp->fl_csp.sp_bb_data) &
@@ -1500,9 +1492,8 @@ void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp,
 				fc_lport_enter_dns(lport);
 			}
 		}
-	} else {
-		FC_LPORT_DBG(lport, "Bad FLOGI response\n");
-	}
+	} else
+		fc_lport_error(lport, fp);
 
 out:
 	fc_frame_free(fp);


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH 28/28] fcoe: remove check for zero fabric name
  2010-07-20 22:19 [PATCH 00/28] libfc, libfcoe and fcoe updates for scsi-misc Robert Love
                   ` (26 preceding siblings ...)
  2010-07-20 22:21 ` [PATCH 27/28] libfc: Add retry logic to lport state machine when receiving LS_RJT Robert Love
@ 2010-07-20 22:21 ` Robert Love
  27 siblings, 0 replies; 34+ messages in thread
From: Robert Love @ 2010-07-20 22:21 UTC (permalink / raw)
  To: James.Bottomley, linux-scsi; +Cc: Vasu Dev

From: Vasu Dev <vasu.dev@intel.com>

This check prevents FCF selection in NPV mode due to zero fabric name
in that case and in turn flogi fails. Though NPV mode should not have
this zero and should be fixed there also but spec also does not require
initiator to ensure that fabric name must be non-zero, therefore dropping
this check to get flogi working in NPV mode.

Signed-off-by: Vasu Dev <vasu.dev@intel.com>
Signed-off-by: Robert Love <robert.w.love@intel.com>
---
 drivers/scsi/fcoe/libfcoe.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c
index 2c265fe..aa503d8 100644
--- a/drivers/scsi/fcoe/libfcoe.c
+++ b/drivers/scsi/fcoe/libfcoe.c
@@ -845,7 +845,7 @@ static int fcoe_ctlr_parse_adv(struct fcoe_ctlr *fip,
 	}
 	if (!fcf->fc_map || (fcf->fc_map & 0x10000))
 		return -EINVAL;
-	if (!fcf->switch_name || !fcf->fabric_name)
+	if (!fcf->switch_name)
 		return -EINVAL;
 	if (desc_mask) {
 		LIBFCOE_FIP_DBG(fip, "adv missing descriptors mask %x\n",


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* Re: [PATCH 03/28] libfc: IO errors on link down due to cable unplug
  2010-07-20 22:19 ` [PATCH 03/28] libfc: IO errors on link down due to cable unplug Robert Love
@ 2010-07-21  3:29   ` Mike Christie
  2010-07-27 21:32     ` Vasu Dev
  0 siblings, 1 reply; 34+ messages in thread
From: Mike Christie @ 2010-07-21  3:29 UTC (permalink / raw)
  To: Robert Love; +Cc: James.Bottomley, linux-scsi, Vasu Dev

On 07/20/2010 05:19 PM, Robert Love wrote:
> From: Vasu Dev<vasu.dev@intel.com>
>
> In this case, sync IO fails with EIO(5) errors as:-
>
> "Thread:1 System call error:5 - Input/output error (::pwrite() failed)".
>
> This is due to IO time out while libfc doing link down processing
> to block all rports and if timed out IO was at last retry
> attempt then it fails to user with EIO error followed by
> these log messages.
>
> [77848.612169] host2: rport bf0015: Delete port
> [77848.612221] host2: rport e10aef: work delete
> [77848.612232] host2: rport e10002: work event 3
> [77848.612422] sd 2:0:1:1: [sdi] Unhandled error code
> [77848.612426] sd 2:0:1:1: [sdi] Result: hostbyte=DID_ERROR
> driverbyte=DRIVER_OK
> [77848.612431] sd 2:0:1:1: [sdi] CDB: Write(10): 2a 00 00 00 11 20 00 00 20 00
> [77848.612445] end_request: I/O error, dev sdi, sector 4384
> [77848.612553] sd 2:0:1:2: [sdj] Unhandled error code
>
> To fix these EIO errors, such timed out incomplete IOs needs
> to be re-queued without counting retry attempt and this patch
> does that using DID_REQUEUE scsi code.
>
> Signed-off-by: Vasu Dev<vasu.dev@intel.com>
> Signed-off-by: Robert Love<robert.w.love@intel.com>
> ---
>   drivers/scsi/libfc/fc_fcp.c |    5 +++++
>   1 files changed, 5 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/scsi/libfc/fc_fcp.c b/drivers/scsi/libfc/fc_fcp.c
> index a0a3ae7..61a1297 100644
> --- a/drivers/scsi/libfc/fc_fcp.c
> +++ b/drivers/scsi/libfc/fc_fcp.c
> @@ -1971,6 +1971,11 @@ static void fc_io_compl(struct fc_fcp_pkt *fsp)
>   		break;
>   	}
>
> +	if (lport->state != LPORT_ST_READY&&  fsp->status_code != FC_COMPLETE) {
> +		sc_cmd->result = (DID_REQUEUE<<  16);
> +		FC_FCP_DBG(fsp, "Returning DID_REQUEUE to scsi-ml\n");
> +	}
> +

If it is a tape command, you do not want to use DID_REQUEUE do you?

You are using the fc class API to delete the rport in this scenario, 
right? If so normally I think you would use DID_TRANSPORT_DISRUPTED. 
This is going to give you an error if it is the last retry like 
DID_ERROR though.

It seems weird to try and work around the IO erorr in this case because 
it is the last retry so it should fail. If the port keeps going down and 
up, then with your patch you could keep retrying the IO forever.

This isn't one of those races where you are blocking the rport, but the 
IO keeps coming around so the retries are used really quickly right (the 
driver looks like it has the fc class and internal state checks to 
prevent this but I wanted to make sure).

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH 03/28] libfc: IO errors on link down due to cable unplug
  2010-07-21  3:29   ` Mike Christie
@ 2010-07-27 21:32     ` Vasu Dev
  2010-07-27 22:08       ` Mike Christie
  2010-07-28  7:32       ` Mike Christie
  0 siblings, 2 replies; 34+ messages in thread
From: Vasu Dev @ 2010-07-27 21:32 UTC (permalink / raw)
  To: Mike Christie; +Cc: Robert Love, James.Bottomley, linux-scsi, devel

On Tue, 2010-07-20 at 22:29 -0500, Mike Christie wrote:
> On 07/20/2010 05:19 PM, Robert Love wrote:
> > From: Vasu Dev<vasu.dev@intel.com>
> >
> > In this case, sync IO fails with EIO(5) errors as:-
> >
> > "Thread:1 System call error:5 - Input/output error (::pwrite() failed)".
> >
> > This is due to IO time out while libfc doing link down processing
> > to block all rports and if timed out IO was at last retry
> > attempt then it fails to user with EIO error followed by
> > these log messages.
> >
> > [77848.612169] host2: rport bf0015: Delete port
> > [77848.612221] host2: rport e10aef: work delete
> > [77848.612232] host2: rport e10002: work event 3
> > [77848.612422] sd 2:0:1:1: [sdi] Unhandled error code
> > [77848.612426] sd 2:0:1:1: [sdi] Result: hostbyte=DID_ERROR
> > driverbyte=DRIVER_OK
> > [77848.612431] sd 2:0:1:1: [sdi] CDB: Write(10): 2a 00 00 00 11 20 00 00 20 00
> > [77848.612445] end_request: I/O error, dev sdi, sector 4384
> > [77848.612553] sd 2:0:1:2: [sdj] Unhandled error code
> >
> > To fix these EIO errors, such timed out incomplete IOs needs
> > to be re-queued without counting retry attempt and this patch
> > does that using DID_REQUEUE scsi code.
> >
> > Signed-off-by: Vasu Dev<vasu.dev@intel.com>
> > Signed-off-by: Robert Love<robert.w.love@intel.com>
> > ---
> >   drivers/scsi/libfc/fc_fcp.c |    5 +++++
> >   1 files changed, 5 insertions(+), 0 deletions(-)
> >
> > diff --git a/drivers/scsi/libfc/fc_fcp.c b/drivers/scsi/libfc/fc_fcp.c
> > index a0a3ae7..61a1297 100644
> > --- a/drivers/scsi/libfc/fc_fcp.c
> > +++ b/drivers/scsi/libfc/fc_fcp.c
> > @@ -1971,6 +1971,11 @@ static void fc_io_compl(struct fc_fcp_pkt *fsp)
> >   		break;
> >   	}
> >
> > +	if (lport->state != LPORT_ST_READY&&  fsp->status_code != FC_COMPLETE) {
> > +		sc_cmd->result = (DID_REQUEUE<<  16);
> > +		FC_FCP_DBG(fsp, "Returning DID_REQUEUE to scsi-ml\n");
> > +	}
> > +
> 
> If it is a tape command, you do not want to use DID_REQUEUE do you?
> 

I wasn't sure of using DID_REQUEUE here and therefore I did RFC first at
http://www.open-fcoe.org/pipermail/devel/2010-June/010360.html.  As I
understand DID_REQUEUE would block the sdev and would do unconditional
retry and now I see that is issue with tapes as you pointed above, so
I'll remove its use here but help me understand use of DID_REQUEUE, I
mean are LLLD expected to make additional checks before indicating
DID_REQUEUE to scsi-ml ? 

> You are using the fc class API to delete the rport in this scenario, 
> right? 

Yes.

> If so normally I think you would use DID_TRANSPORT_DISRUPTED. 
> This is going to give you an error if it is the last retry like 
> DID_ERROR though.
> 

Effectively both would do same, so changing DID_TRANSPORT_DISRUPTED
won't change anything or perhaps I'm missing something here.

> It seems weird to try and work around the IO erorr in this case because 
> it is the last retry so it should fail. If the port keeps going down and 
> up, then with your patch you could keep retrying the IO forever.
> 

I think last try should not fail due to link down if rports comes back
before dev_loss_tmo or fast_io_fail_tmo, such IOs should get their last
try attempt again once rport are back instead throwing IO error just
before port is getting blocked, that is the whole purpose of rport block
states.

> This isn't one of those races where you are blocking the rport, but the 
> IO keeps coming around so the retries are used really quickly right (the 
> driver looks like it has the fc class and internal state checks to 
> prevent this but I wanted to make sure).

I'm trying to fix IO error due to failed last attempt on a IO just after
link down and its rport yet to move to block state from pending rport
event_work. The libfc already has checks once rport or lport no longer
ready for new IO try and that is not an issue here. It is very rare case
with very tiny time window as rport block, so I can replace DID_REQUEUE
here by DID_IMM_RETRY and I verified that works, will that be okay ?

This tiny time windows can be further reduced by use of wmb() on rport
state change to block and lport getting out of ready since their states
are checked w/o lock in queuecommand path. I can do that as separate
patch only if needed and it really helps some corner case.

Sorry for being late on this and I just noticed James just applied this
patch, so I'll provide separate patch to remove DID_REQUEUE for tape
devices.

	Thanks
	Vasu

> --
> To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH 03/28] libfc: IO errors on link down due to cable unplug
  2010-07-27 21:32     ` Vasu Dev
@ 2010-07-27 22:08       ` Mike Christie
  2010-07-28  7:32       ` Mike Christie
  1 sibling, 0 replies; 34+ messages in thread
From: Mike Christie @ 2010-07-27 22:08 UTC (permalink / raw)
  To: Vasu Dev; +Cc: Robert Love, James.Bottomley, linux-scsi, devel

On 07/27/2010 04:32 PM, Vasu Dev wrote:
> On Tue, 2010-07-20 at 22:29 -0500, Mike Christie wrote:
>> On 07/20/2010 05:19 PM, Robert Love wrote:
>>> From: Vasu Dev<vasu.dev@intel.com>
>>>
>>> In this case, sync IO fails with EIO(5) errors as:-
>>>
>>> "Thread:1 System call error:5 - Input/output error (::pwrite() failed)".
>>>
>>> This is due to IO time out while libfc doing link down processing
>>> to block all rports and if timed out IO was at last retry
>>> attempt then it fails to user with EIO error followed by
>>> these log messages.
>>>
>>> [77848.612169] host2: rport bf0015: Delete port
>>> [77848.612221] host2: rport e10aef: work delete
>>> [77848.612232] host2: rport e10002: work event 3
>>> [77848.612422] sd 2:0:1:1: [sdi] Unhandled error code
>>> [77848.612426] sd 2:0:1:1: [sdi] Result: hostbyte=DID_ERROR
>>> driverbyte=DRIVER_OK
>>> [77848.612431] sd 2:0:1:1: [sdi] CDB: Write(10): 2a 00 00 00 11 20 00 00 20 00
>>> [77848.612445] end_request: I/O error, dev sdi, sector 4384
>>> [77848.612553] sd 2:0:1:2: [sdj] Unhandled error code
>>>
>>> To fix these EIO errors, such timed out incomplete IOs needs
>>> to be re-queued without counting retry attempt and this patch
>>> does that using DID_REQUEUE scsi code.
>>>
>>> Signed-off-by: Vasu Dev<vasu.dev@intel.com>
>>> Signed-off-by: Robert Love<robert.w.love@intel.com>
>>> ---
>>>    drivers/scsi/libfc/fc_fcp.c |    5 +++++
>>>    1 files changed, 5 insertions(+), 0 deletions(-)
>>>
>>> diff --git a/drivers/scsi/libfc/fc_fcp.c b/drivers/scsi/libfc/fc_fcp.c
>>> index a0a3ae7..61a1297 100644
>>> --- a/drivers/scsi/libfc/fc_fcp.c
>>> +++ b/drivers/scsi/libfc/fc_fcp.c
>>> @@ -1971,6 +1971,11 @@ static void fc_io_compl(struct fc_fcp_pkt *fsp)
>>>    		break;
>>>    	}
>>>
>>> +	if (lport->state != LPORT_ST_READY&&   fsp->status_code != FC_COMPLETE) {
>>> +		sc_cmd->result = (DID_REQUEUE<<   16);
>>> +		FC_FCP_DBG(fsp, "Returning DID_REQUEUE to scsi-ml\n");
>>> +	}
>>> +
>>
>> If it is a tape command, you do not want to use DID_REQUEUE do you?
>>
>
> I wasn't sure of using DID_REQUEUE here and therefore I did RFC first at
> http://www.open-fcoe.org/pipermail/devel/2010-June/010360.html.  As I
> understand DID_REQUEUE would block the sdev and would do unconditional
> retry and now I see that is issue with tapes as you pointed above, so
> I'll remove its use here but help me understand use of DID_REQUEUE, I
> mean are LLLD expected to make additional checks before indicating
> DID_REQUEUE to scsi-ml ?


I think you would want to use it in case where you want to block the 
sdev and you know the IO had not started. So some drivers use it if they 
get an error the indicated the port/target rejected the IO before it 
even started and there might be a tmp resource issue.

>
>> You are using the fc class API to delete the rport in this scenario,
>> right?
>
> Yes.
>
>> If so normally I think you would use DID_TRANSPORT_DISRUPTED.
>> This is going to give you an error if it is the last retry like
>> DID_ERROR though.
>>
>
> Effectively both would do same, so changing DID_TRANSPORT_DISRUPTED
> won't change anything or perhaps I'm missing something here.

The behavior is the same, but the error code that gets logged is more 
clear I think.

>
>> It seems weird to try and work around the IO erorr in this case because
>> it is the last retry so it should fail. If the port keeps going down and
>> up, then with your patch you could keep retrying the IO forever.
>>
>
> I think last try should not fail due to link down if rports comes back
> before dev_loss_tmo or fast_io_fail_tmo, such IOs should get their last
> try attempt again once rport are back instead throwing IO error just
> before port is getting blocked, that is the whole purpose of rport block
> states.

Just to make sure we are on the same page. Didn't the command get a 
chance to run on its last retry, but the port came down while it was 
executing. I am just saying that should count as a try, but with your 
patch you are giving it another retry. So instead of 5 it gets 6. And 
actually you are throwing out retries for this case so it runs until it 
times out (times out in scsi_softirq_done).


If you want that behavior and people argee it is the right think to do 
then I think we should apply it to all drivers, so users can expect the 
same behavior. Use DID_TRANSPORT_DISRUPTED in the driver, but then in 
scsi_error.c change that to check for tape devices. In the non tape case 
then just always retry until the command times out (time out in 
scsi_softirq_done. I do not mean timeout like when the scsi eh runs).


If the command never got a chance to start, then forget all this. I am 
wrong and DID_REQUEUE is fine.


>
>> This isn't one of those races where you are blocking the rport, but the
>> IO keeps coming around so the retries are used really quickly right (the
>> driver looks like it has the fc class and internal state checks to
>> prevent this but I wanted to make sure).
>
> I'm trying to fix IO error due to failed last attempt on a IO just after
> link down and its rport yet to move to block state from pending rport
> event_work. The libfc already has checks once rport or lport no longer
> ready for new IO try and that is not an issue here. It is very rare case
> with very tiny time window as rport block, so I can replace DID_REQUEUE
> here by DID_IMM_RETRY and I verified that works, will that be okay ?

DID_IMM_RETRY has the exact same issues.


>
> This tiny time windows can be further reduced by use of wmb() on rport
> state change to block and lport getting out of ready since their states
> are checked w/o lock in queuecommand path. I can do that as separate
> patch only if needed and it really helps some corner case.
>

That is not really the issue.

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH 03/28] libfc: IO errors on link down due to cable unplug
  2010-07-27 21:32     ` Vasu Dev
  2010-07-27 22:08       ` Mike Christie
@ 2010-07-28  7:32       ` Mike Christie
  2010-07-30 20:34         ` Vasu Dev
  1 sibling, 1 reply; 34+ messages in thread
From: Mike Christie @ 2010-07-28  7:32 UTC (permalink / raw)
  To: Vasu Dev; +Cc: Robert Love, James.Bottomley, linux-scsi, devel

[-- Attachment #1: Type: text/plain, Size: 995 bytes --]

On 07/27/2010 04:32 PM, Vasu Dev wrote:
>> This isn't one of those races where you are blocking the rport, but the
>> IO keeps coming around so the retries are used really quickly right (the
>> driver looks like it has the fc class and internal state checks to
>> prevent this but I wanted to make sure).
>

I think I am hitting the above problem.

>
> This tiny time windows can be further reduced by use of wmb() on rport
> state change to block and lport getting out of ready since their states
> are checked w/o lock in queuecommand path.

I think you need something like this for the lport queue ready check. 
It looks like the initial failure from __fc_linkdown->fc_fcp_cleanup 
burned a timeout. Then because it missed the lport ready check, it will 
use an extra retry just sitting there timing out.

For the rport state check, I think you are supposed to call the fc 
chkready function under the host lock. The port state and flags are set 
under it. I did the attached patch for that.

[-- Attachment #2: libfc-hold-host-lock-when-checking-rport.patch --]
[-- Type: text/x-patch, Size: 910 bytes --]

libfc: call fc_remote_port_chkready under the host lock.

The rport port state and flags are set under the host lock,
so this patch calls fc_remote_port_chkready with the host lock
held like is also done in the other fc drivers.

Signed-off-by: Mike Christie <michaelc@cs.wisc.edu>

diff --git a/drivers/scsi/libfc/fc_fcp.c b/drivers/scsi/libfc/fc_fcp.c
index eac4d09..a116846 100644
--- a/drivers/scsi/libfc/fc_fcp.c
+++ b/drivers/scsi/libfc/fc_fcp.c
@@ -1765,14 +1768,14 @@ int fc_queuecommand(struct scsi_cmnd *sc_cmd, void (*done)(struct scsi_cmnd *))
 	struct fcoe_dev_stats *stats;
 
 	lport = shost_priv(sc_cmd->device->host);
-	spin_unlock_irq(lport->host->host_lock);
 
 	rval = fc_remote_port_chkready(rport);
 	if (rval) {
 		sc_cmd->result = rval;
 		done(sc_cmd);
-		goto out;
+		return 0;
 	}
+	spin_unlock_irq(lport->host->host_lock);
 
 	if (!*(struct fc_remote_port **)rport->dd_data) {
 		/*

^ permalink raw reply related	[flat|nested] 34+ messages in thread

* Re: [PATCH 03/28] libfc: IO errors on link down due to cable unplug
  2010-07-28  7:32       ` Mike Christie
@ 2010-07-30 20:34         ` Vasu Dev
  0 siblings, 0 replies; 34+ messages in thread
From: Vasu Dev @ 2010-07-30 20:34 UTC (permalink / raw)
  To: Mike Christie; +Cc: Robert Love, James.Bottomley, linux-scsi, devel

On Wed, 2010-07-28 at 02:32 -0500, Mike Christie wrote:
> On 07/27/2010 04:32 PM, Vasu Dev wrote:
> >> This isn't one of those races where you are blocking the rport, but the
> >> IO keeps coming around so the retries are used really quickly right (the
> >> driver looks like it has the fc class and internal state checks to
> >> prevent this but I wanted to make sure).
> >
> 
> I think I am hitting the above problem.
> 
> >
> > This tiny time windows can be further reduced by use of wmb() on rport
> > state change to block and lport getting out of ready since their states
> > are checked w/o lock in queuecommand path.
> 
> I think you need something like this for the lport queue ready check. 
> It looks like the initial failure from __fc_linkdown->fc_fcp_cleanup 
> burned a timeout. Then because it missed the lport ready check, it will 
> use an extra retry just sitting there timing out.
> 
> For the rport state check, I think you are supposed to call the fc 
> chkready function under the host lock. The port state and flags are set 
> under it. I did the attached patch for that.

I tried attached patch w/o this series and that didn't work and I got
same IO errors on link down. However once I included my next patch 4/28
related to IO error fix then attached patch worked w/o having this patch
under discussion, so attached patch works w/o this patch and I agree
rport needs to be checked under lock to get up-to-date correct rport
state checking in fc_queueucommand, so can you submit this patch ?

I'll submit another patch to replace added DID_REQUEUE here as this
series already applied to scsi-misc, I'll replace this by
DID_TRANSPORT_DISRUPTED since added DID_REQUEUE could be returned for
attempted tape IO as you pointed in other response and having
DID_TRANSPORT_DISRUPTED would be more meaningful code as lport is not
ready in this case to complete IOs.

Below is IO error log with attached patch w/o this series just after
ixgbe link goes down :-

[  452.168644] ixgbe: eth3 NIC Link is Down
[  452.180427] sd 5:0:0:2: [sdd] Done:
[  452.180436] sd 5:0:0:3: [sde] Done: RETRY
[  452.180441] sd 5:0:0:3: [sde] Result: hostbyte=DID_ERROR
driverbyte=DRIVER_OK
[  452.180445] sd 5:0:0:3: [sde] CDB: Write(10): 2a 00 00 07 91 80 00 00
80 00
[  452.180464] sd 5:0:0:1: [sdc] Done: RETRY
[  452.180467] sd 5:0:0:1: [sdc] Result: hostbyte=DID_ERROR
driverbyte=DRIVER_OK
[  452.180469] sd 5:0:0:1: [sdc] CDB: Write(10): 2a 00 00 07 cf 00 00 00
80 00
[  452.181792] RETRY
[  452.181963] sd 5:0:0:2: [sdd] Result: hostbyte=DID_ERROR
driverbyte=DRIVER_OK
[  452.182241] sd 5:0:0:2: [sdd] CDB: Write(10): 2a 00 00 07 ac 00 00 00
80 00
[  452.183294] sd 5:0:0:2: [sdd] Done:
[  452.183326] sd 5:0:0:0: [sdb] Done: RETRY
[  452.183329] sd 5:0:0:0: [sdb] Result: hostbyte=DID_ERROR
driverbyte=DRIVER_OK
[  452.183332] sd 5:0:0:0: [sdb] CDB: Write(10): 2a 00 00 07 99 00 00 00
80 00
[  452.184089] RETRY
[  452.184262] sd 5:0:0:2: [sdd] Result: hostbyte=DID_ERROR
driverbyte=DRIVER_OK
[  452.184514] sd 5:0:0:2: [sdd] CDB: Write(10): 2a 00 00 07 ac 00 00 00
80 00
[  452.185557] sd 5:0:0:2: [sdd] Done: RETRY
[  452.185798] sd 5:0:0:2: [sdd] Result: hostbyte=DID_ERROR
driverbyte=DRIVER_OK
[  452.186050] sd 5:0:0:2: [sdd] CDB: Write(10): 2a 00 00 07 ac 00 00 00
80 00
[  452.187101] sd 5:0:0:2: [sdd] Done: RETRY
[  452.187373] sd 5:0:0:2: [sdd] Result: hostbyte=DID_ERROR
driverbyte=DRIVER_OK
[  452.187633] sd 5:0:0:2: [sdd] CDB: Write(10): 2a 00 00 07 ac 00 00 00
80 00
[  452.188691] sd 5:0:0:2: [sdd] Done: RETRY
[  452.188932] sd 5:0:0:2: [sdd] Result: hostbyte=DID_ERROR
driverbyte=DRIVER_OK[  452.189184] sd 5:0:0:2: [sdd] CDB: Write(10): 2a
00 00 07 ac 00 00 00 80 00
[  452.190499] sd 5:0:0:2: [sdd] Done: SUCCESS
[  452.190748] sd 5:0:0:2: [sdd] Result: hostbyte=DID_ERROR
driverbyte=DRIVER_OK
[  452.190999] sd 5:0:0:2: [sdd] CDB: Write(10): 2a 00 00 07 ac 00 00 00
80 00
[  452.192033] sd 5:0:0:2: [sdd] Unhandled error code
[  452.192244] sd 5:0:0:2: [sdd] Result: hostbyte=DID_ERROR
driverbyte=DRIVER_OK
[  452.192495] sd 5:0:0:2: [sdd] CDB: Write(10): 2a 00 00 07 ac 00 00 00
80 00
[  452.193526] end_request: I/O error, dev sdd, sector 502784
[  452.193820] host5: libfc: Link down on port (050b01)
[  452.194006] sd 5:0:0:3: [sde] Done: RETRY
[  452.194262] sd 5:0:0:3: [sde] Result: hostbyte=DID_ERROR
driverbyte=DRIVER_OK
[  452.194507] sd 5:0:0:3: [sde] CDB: Write(10): 2a 00 00 07 91 80 00 00
80 00
[  452.195525] sd 5:0:0:1: [sdc] Done: RETRY
[  452.195790] sd 5:0:0:1: [sdc] Result: hostbyte=DID_ERROR
driverbyte=DRIVER_OK
[  452.196033] sd 5:0:0:1: [sdc] CDB: Write(10): 2a 00 00 07 cf 00 00 00
80 00
[  452.197076] sd 5:0:0:0: [sdb] Done: RETRY
[  452.197354] sd 5:0:0:0: [sdb] Result: hostbyte=DID_ERROR
driverbyte=DRIVER_OK
[  452.197620] sd 5:0:0:0: [sdb] CDB: Write(10): 2a 00 00 07 99 00 00 00
80 00
[  459.476688] ixgbe: eth3 NIC Link is Up 10 Gbps, Flow Control: None
[  464.287400] host5: libfc: Link up on port (050b01)
[  466.304340] libfcoe: host5: FIP selected Fibre-Channel Forwarder MAC
00:05:1e:d8:2f:03


	In above log, IO error occurred even before libfc could start acting on
link down at [452.193820] to block rports but once rport blocked your
attached patch helps as I said above.

	Thanks
	Vasu










^ permalink raw reply	[flat|nested] 34+ messages in thread

end of thread, other threads:[~2010-07-30 20:34 UTC | newest]

Thread overview: 34+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-07-20 22:19 [PATCH 00/28] libfc, libfcoe and fcoe updates for scsi-misc Robert Love
2010-07-20 22:19 ` [PATCH 01/28] libfc: fix slowpath error from WARN_ON in fc_fcp_send_data Robert Love
2010-07-20 22:19 ` [PATCH 02/28] fcoe: make it possible to verify fcoe with sparse Robert Love
2010-07-20 22:19 ` [PATCH 03/28] libfc: IO errors on link down due to cable unplug Robert Love
2010-07-21  3:29   ` Mike Christie
2010-07-27 21:32     ` Vasu Dev
2010-07-27 22:08       ` Mike Christie
2010-07-28  7:32       ` Mike Christie
2010-07-30 20:34         ` Vasu Dev
2010-07-20 22:19 ` [PATCH 04/28] fcoe: cleans up fcoe_disable and fcoe_enable Robert Love
2010-07-20 22:19 ` [PATCH 05/28] fcoe: adds src and dest mac address checking for fcoe frames Robert Love
2010-07-20 22:19 ` [PATCH 06/28] libfc: convert rport lookup to be RCU safe Robert Love
2010-07-20 22:19 ` [PATCH 07/28] libfc: provide space for LLD after remote port structure Robert Love
2010-07-20 22:19 ` [PATCH 08/28] libfcoe: convert FIP to lock with mutex instead of spin lock Robert Love
2010-07-20 22:19 ` [PATCH 09/28] libfc: add discovery-private pointer for LLD Robert Love
2010-07-20 22:19 ` [PATCH 10/28] libfcoe: fcoe: fnic: change fcoe_ctlr_init interface to specify mode Robert Love
2010-07-20 22:20 ` [PATCH 11/28] libfc: Add local port point-to-multipoint flag Robert Love
2010-07-20 22:20 ` [PATCH 12/28] libfc: add FLOGI state to rport for VN2VN Robert Love
2010-07-20 22:20 ` [PATCH 13/28] libfc: track FIP exchanges Robert Love
2010-07-20 22:20 ` [PATCH 14/28] libfcoe: add protocol description of FIP VN2VN mode Robert Love
2010-07-20 22:20 ` [PATCH 15/28] libfcoe: add state change debugging Robert Love
2010-07-20 22:20 ` [PATCH 16/28] libfcoe: fcoe: fnic: add FIP VN2VN point-to-multipoint support Robert Love
2010-07-20 22:20 ` [PATCH 17/28] libfcoe: Fix FIP ELS encapsulation details for FLOGI responses Robert Love
2010-07-20 22:20 ` [PATCH 18/28] fcoe libfcoe: use correct FC-MAP for VN2VN mode Robert Love
2010-07-20 22:20 ` [PATCH 19/28] fcoe: config via separate create_vn2vn module parameter Robert Love
2010-07-20 22:20 ` [PATCH 20/28] libfc: eliminate rport LOGO state Robert Love
2010-07-20 22:20 ` [PATCH 21/28] libfc: add fc_frame_sid() and fc_frame_did() functions Robert Love
2010-07-20 22:21 ` [PATCH 22/28] libfc: add fc_fill_reply_hdr() and fc_fill_hdr() Robert Love
2010-07-20 22:21 ` [PATCH 23/28] libfc: add interface to allocate a sequence for incoming requests Robert Love
2010-07-20 22:21 ` [PATCH 24/28] libfc: don't require a local exchange " Robert Love
2010-07-20 22:21 ` [PATCH 25/28] fcoe: fix offload feature flag change from netdev Robert Love
2010-07-20 22:21 ` [PATCH 26/28] Revert "[SCSI] fcoe: Fix using VLAN ID in creating lport's WWWN/WWPN" Robert Love
2010-07-20 22:21 ` [PATCH 27/28] libfc: Add retry logic to lport state machine when receiving LS_RJT Robert Love
2010-07-20 22:21 ` [PATCH 28/28] fcoe: remove check for zero fabric name Robert Love

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).