Skip to contentSkip to conversation

libfc, libfcoe and fcoe updates for scsi-misc.git (2.6.33)

PATCH New Robert Love 1 participant 10 messages Nov 20, 2009

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

cover libfc, libfcoe and fcoe updates for scsi-misc.git (2.6.33)

The following series adds the following-

1) Support for a link status error block (LESB) command.

2) A performance optimization. If an incoming FCP frame is already on the correct CPU then bypass the Rx threads and process in irq context.

3) At least one fix.

---

Chris Leech (1):
fcoe: allow SCSI-FCP to be processed directly in softirq context

Yi Zou (8):
libfc: add support of receiving ELS_RLS
fcoe, libfc: add get_lesb() to allow LLD to fill the link error status block (LESB)
libfc: add fcoe_fc_els_lesb to fc_fcoe.h for FC-BB-5 LESB definitions
libfcoe: add tracking FIP Missing Discovery Advertisement count
libfcoe: add tracking FIP Virtual Link Failure count
libfcoe: add checking disable flag in FIP_FKA_ADV
libfc: add FC-BB-5 LESB counters to fcoe_dev_stats
libfc: fix payload size passed to fc_frame_alloc() in fc_lport_els_request

 drivers/scsi/fcoe/fcoe.c      |  279 +++++++++++++++++++++++++----------------
 drivers/scsi/fcoe/libfcoe.c   |   20 +++
 drivers/scsi/libfc/fc_lport.c |    3 
 drivers/scsi/libfc/fc_rport.c |   76 +++++++++++
 include/scsi/fc/fc_fcoe.h     |   12 ++
 include/scsi/fc/fc_fip.h      |   12 ++
 include/scsi/libfc.h          |   10 +
 include/scsi/libfcoe.h        |    1 
 8 files changed, 299 insertions(+), 114 deletions(-)
Signature · 1 line
//Rob
#1 lore raw

PATCH 1/9 libfc: fix payload size passed to fc_frame_alloc() in fc_lport_els_request

From: Yi Zou <[email protected]>

Frame header room is already incluced, just pass the length of payload.

---

 drivers/scsi/libfc/fc_lport.c |    3 +--
 1 files changed, 1 insertions(+), 2 deletions(-)
drivers/scsi/libfc/fc_lport.c +1 -2
index c841d54..bbf4152 100644
@@ -1679,8 +1679,7 @@ static int fc_lport_els_request(struct fc_bsg_job *job,
 	char *pp;
 	int len;
 
-	fp = fc_frame_alloc(lport, sizeof(struct fc_frame_header) +
-			    job->request_payload.payload_len);
+	fp = fc_frame_alloc(lport, job->request_payload.payload_len);
 	if (!fp)
 		return -ENOMEM;
 
#2 lore raw

PATCH 2/9 fcoe: allow SCSI-FCP to be processed directly in softirq context

In reply to Robert Love

From: Chris Leech <[email protected]>

Allow FCP frames to bypass the FCoE receive processing threads and handle them directly in softirq context, if they are received on the correct CPU. This preserves the queuing to threads for scaling out receive processing to multiple CPUs, but allows FCoE-aware multi-queue network drivers that direct frames to the originating CPUs to handle FCP processing with less scheduling latency.

Only FCP is handled directly, because libfc makes use of mutexes in ELS handling routines.

The bulk of this change is just moving the FCoE receive processing out of the receive thread function, leaving behind just the thread and queue management. The interesting bits are in fcoe_rcv()

---

 drivers/scsi/fcoe/fcoe.c |  245 +++++++++++++++++++++++++---------------------
 1 files changed, 135 insertions(+), 110 deletions(-)
drivers/scsi/fcoe/fcoe.c +135 -110
index 4a43b74..32298ed 100644
@@ -109,6 +109,7 @@ static struct fc_seq *fcoe_elsct_send(struct fc_lport *,
 						   struct fc_frame *,
 						   void *),
 				      void *, u32 timeout);
+static void fcoe_recv_frame(struct sk_buff *skb);
 
 module_param_call(create, fcoe_create, NULL, NULL, S_IWUSR);
 __MODULE_PARM_TYPE(create, "string");
@@ -1241,11 +1242,25 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *netdev,
 	 * this skb. We also have this receive thread locked,
 	 * so we're free to queue skbs into it's queue.
 	 */
-	__skb_queue_tail(&fps->fcoe_rx_list, skb);
-	if (fps->fcoe_rx_list.qlen == 1)
-		wake_up_process(fps->thread);
 
-	spin_unlock_bh(&fps->fcoe_rx_list.lock);
+	/* If this is a SCSI-FCP frame, and this is already executing on the
+	 * correct CPU, and the queue for this CPU is empty, then go ahead
+	 * and process the frame directly in the softirq context.
+	 * This lets us process completions without context switching from the
+	 * NET_RX softirq, to our receive processing thread, and then back to
+	 * BLOCK softirq context.
+	 */
+	if (fh->fh_type == FC_TYPE_FCP &&
+	    cpu == smp_processor_id() &&
+	    skb_queue_empty(&fps->fcoe_rx_list)) {
+		spin_unlock_bh(&fps->fcoe_rx_list.lock);
+		fcoe_recv_frame(skb);
+	} else {
+		__skb_queue_tail(&fps->fcoe_rx_list, skb);
+		if (fps->fcoe_rx_list.qlen == 1)
+			wake_up_process(fps->thread);
+		spin_unlock_bh(&fps->fcoe_rx_list.lock);
+	}
 
 	return 0;
 err:
@@ -1503,26 +1518,134 @@ static void fcoe_percpu_flush_done(struct sk_buff *skb)
 }
 
 /**
- * fcoe_percpu_receive_thread() - The per-CPU packet receive thread
- * @arg: The per-CPU context
- *
- * Return: 0 for success
+ * fcoe_recv_frame() - process a single received frame
+ * @skb: frame to process
  */
-int fcoe_percpu_receive_thread(void *arg)
+static void fcoe_recv_frame(struct sk_buff *skb)
 {
-	struct fcoe_percpu_s *p = arg;
 	u32 fr_len;
 	struct fc_lport *lport;
 	struct fcoe_rcv_info *fr;
 	struct fcoe_dev_stats *stats;
 	struct fc_frame_header *fh;
-	struct sk_buff *skb;
 	struct fcoe_crc_eof crc_eof;
 	struct fc_frame *fp;
 	u8 *mac = NULL;
 	struct fcoe_port *port;
 	struct fcoe_hdr *hp;
 
+	fr = fcoe_dev_from_skb(skb);
+	lport = fr->fr_dev;
+	if (unlikely(!lport)) {
+		if (skb->destructor != fcoe_percpu_flush_done)
+			FCOE_NETDEV_DBG(skb->dev, "NULL lport in skb");
+		kfree_skb(skb);
+		return;
+	}
+
+	FCOE_NETDEV_DBG(skb->dev, "skb_info: len:%d data_len:%d "
+			"head:%p data:%p tail:%p end:%p sum:%d dev:%s",
+			skb->len, skb->data_len,
+			skb->head, skb->data, skb_tail_pointer(skb),
+			skb_end_pointer(skb), skb->csum,
+			skb->dev ? skb->dev->name : "<NULL>");
+
+	/*
+	 * Save source MAC address before discarding header.
+	 */
+	port = lport_priv(lport);
+	if (skb_is_nonlinear(skb))
+		skb_linearize(skb);	/* not ideal */
+	mac = eth_hdr(skb)->h_source;
+
+	/*
+	 * Frame length checks and setting up the header pointers
+	 * was done in fcoe_rcv already.
+	 */
+	hp = (struct fcoe_hdr *) skb_network_header(skb);
+	fh = (struct fc_frame_header *) skb_transport_header(skb);
+
+	stats = fc_lport_get_stats(lport);
+	if (unlikely(FC_FCOE_DECAPS_VER(hp) != FC_FCOE_VER)) {
+		if (stats->ErrorFrames < 5)
+			printk(KERN_WARNING "fcoe: FCoE version "
+			       "mismatch: The frame has "
+			       "version %x, but the "
+			       "initiator supports version "
+			       "%x\n", FC_FCOE_DECAPS_VER(hp),
+			       FC_FCOE_VER);
+		stats->ErrorFrames++;
+		kfree_skb(skb);
+		return;
+	}
+
+	skb_pull(skb, sizeof(struct fcoe_hdr));
+	fr_len = skb->len - sizeof(struct fcoe_crc_eof);
+
+	stats->RxFrames++;
+	stats->RxWords += fr_len / FCOE_WORD_TO_BYTE;
+
+	fp = (struct fc_frame *)skb;
+	fc_frame_init(fp);
+	fr_dev(fp) = lport;
+	fr_sof(fp) = hp->fcoe_sof;
+
+	/* Copy out the CRC and EOF trailer for access */
+	if (skb_copy_bits(skb, fr_len, &crc_eof, sizeof(crc_eof))) {
+		kfree_skb(skb);
+		return;
+	}
+	fr_eof(fp) = crc_eof.fcoe_eof;
+	fr_crc(fp) = crc_eof.fcoe_crc32;
+	if (pskb_trim(skb, fr_len)) {
+		kfree_skb(skb);
+		return;
+	}
+
+	/*
+	 * We only check CRC if no offload is available and if it is
+	 * it's solicited data, in which case, the FCP layer would
+	 * check it during the copy.
+	 */
+	if (lport->crc_offload &&
+	    skb->ip_summed == CHECKSUM_UNNECESSARY)
+		fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED;
+	else
+		fr_flags(fp) |= FCPHF_CRC_UNCHECKED;
+
+	fh = fc_frame_header_get(fp);
+	if (fh->fh_r_ctl == FC_RCTL_DD_SOL_DATA &&
+	    fh->fh_type == FC_TYPE_FCP) {
+		fc_exch_recv(lport, fp);
+		return;
+	}
+	if (fr_flags(fp) & FCPHF_CRC_UNCHECKED) {
+		if (le32_to_cpu(fr_crc(fp)) !=
+		    ~crc32(~0, skb->data, fr_len)) {
+			if (stats->InvalidCRCCount < 5)
+				printk(KERN_WARNING "fcoe: dropping "
+				       "frame with CRC error\n");
+			stats->InvalidCRCCount++;
+			stats->ErrorFrames++;
+			fc_frame_free(fp);
+			return;
+		}
+		fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED;
+	}
+	fc_exch_recv(lport, fp);
+}
+
+/**
+ * fcoe_percpu_receive_thread() - The per-CPU packet receive thread
+ * @arg: The per-CPU context
+ *
+ * Return: 0 for success
+ */
+int fcoe_percpu_receive_thread(void *arg)
+{
+	struct fcoe_percpu_s *p = arg;
+	struct sk_buff *skb;
+
 	set_user_nice(current, -20);
 
 	while (!kthread_should_stop()) {
@@ -1538,105 +1661,7 @@ int fcoe_percpu_receive_thread(void *arg)
 			spin_lock_bh(&p->fcoe_rx_list.lock);
 		}
 		spin_unlock_bh(&p->fcoe_rx_list.lock);
-		fr = fcoe_dev_from_skb(skb);
-		lport = fr->fr_dev;
-		if (unlikely(!lport)) {
-			if (skb->destructor != fcoe_percpu_flush_done)
-				FCOE_NETDEV_DBG(skb->dev, "NULL lport in skb");
-			kfree_skb(skb);
-			continue;
-		}
-
-		FCOE_NETDEV_DBG(skb->dev, "skb_info: len:%d data_len:%d "
-				"head:%p data:%p tail:%p end:%p sum:%d dev:%s",
-				skb->len, skb->data_len,
-				skb->head, skb->data, skb_tail_pointer(skb),
-				skb_end_pointer(skb), skb->csum,
-				skb->dev ? skb->dev->name : "<NULL>");
-
-		/*
-		 * Save source MAC address before discarding header.
-		 */
-		port = lport_priv(lport);
-		if (skb_is_nonlinear(skb))
-			skb_linearize(skb);	/* not ideal */
-		mac = eth_hdr(skb)->h_source;
-
-		/*
-		 * Frame length checks and setting up the header pointers
-		 * was done in fcoe_rcv already.
-		 */
-		hp = (struct fcoe_hdr *) skb_network_header(skb);
-		fh = (struct fc_frame_header *) skb_transport_header(skb);
-
-		stats = fc_lport_get_stats(lport);
-		if (unlikely(FC_FCOE_DECAPS_VER(hp) != FC_FCOE_VER)) {
-			if (stats->ErrorFrames < 5)
-				printk(KERN_WARNING "fcoe: FCoE version "
-				       "mismatch: The frame has "
-				       "version %x, but the "
-				       "initiator supports version "
-				       "%x\n", FC_FCOE_DECAPS_VER(hp),
-				       FC_FCOE_VER);
-			stats->ErrorFrames++;
-			kfree_skb(skb);
-			continue;
-		}
-
-		skb_pull(skb, sizeof(struct fcoe_hdr));
-		fr_len = skb->len - sizeof(struct fcoe_crc_eof);
-
-		stats->RxFrames++;
-		stats->RxWords += fr_len / FCOE_WORD_TO_BYTE;
-
-		fp = (struct fc_frame *)skb;
-		fc_frame_init(fp);
-		fr_dev(fp) = lport;
-		fr_sof(fp) = hp->fcoe_sof;
-
-		/* Copy out the CRC and EOF trailer for access */
-		if (skb_copy_bits(skb, fr_len, &crc_eof, sizeof(crc_eof))) {
-			kfree_skb(skb);
-			continue;
-		}
-		fr_eof(fp) = crc_eof.fcoe_eof;
-		fr_crc(fp) = crc_eof.fcoe_crc32;
-		if (pskb_trim(skb, fr_len)) {
-			kfree_skb(skb);
-			continue;
-		}
-
-		/*
-		 * We only check CRC if no offload is available and if it is
-		 * it's solicited data, in which case, the FCP layer would
-		 * check it during the copy.
-		 */
-		if (lport->crc_offload &&
-		    skb->ip_summed == CHECKSUM_UNNECESSARY)
-			fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED;
-		else
-			fr_flags(fp) |= FCPHF_CRC_UNCHECKED;
-
-		fh = fc_frame_header_get(fp);
-		if (fh->fh_r_ctl == FC_RCTL_DD_SOL_DATA &&
-		    fh->fh_type == FC_TYPE_FCP) {
-			fc_exch_recv(lport, fp);
-			continue;
-		}
-		if (fr_flags(fp) & FCPHF_CRC_UNCHECKED) {
-			if (le32_to_cpu(fr_crc(fp)) !=
-			    ~crc32(~0, skb->data, fr_len)) {
-				if (stats->InvalidCRCCount < 5)
-					printk(KERN_WARNING "fcoe: dropping "
-					       "frame with CRC error\n");
-				stats->InvalidCRCCount++;
-				stats->ErrorFrames++;
-				fc_frame_free(fp);
-				continue;
-			}
-			fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED;
-		}
-		fc_exch_recv(lport, fp);
+		fcoe_recv_frame(skb);
 	}
 	return 0;
 }
#3 lore raw

PATCH 3/9 libfc: add FC-BB-5 LESB counters to fcoe_dev_stats

In reply to Robert Love

From: Yi Zou <[email protected]>

FC-BB-5 Rev2.0, Clause 7.10 extends the FC-LS-3 LESB for FC-BB_E. We are already tracking Link Failure Count so add the rest in this patch.

For VLinkFailureCount and MissDiscAdvCount, they are part of the per-cpu fcoe_dev_stats. For SymbolErrorCount, ErroredBlockCount, and FCSErrorCount, they are defined in IEEE 802.3-2008 and are per LLD. They are expected to come from LLD.

---

 include/scsi/libfc.h |    4 ++++
 1 files changed, 4 insertions(+), 0 deletions(-)
include/scsi/libfc.h +4 -0
index 2936fba..b97be29 100644
@@ -231,6 +231,8 @@ struct fc_rport_priv {
  * @ControlRequests:       Number of control requests
  * @InputMegabytes:        Number of received megabytes
  * @OutputMegabytes:       Number of transmitted megabytes
+ * @VLinkFailureCount:     Number of virtual link failures
+ * @MissDiscAdvCount:      Number of missing FIP discovery advertisement
  */
 struct fcoe_dev_stats {
 	u64		SecondsSinceLastReset;
@@ -249,6 +251,8 @@ struct fcoe_dev_stats {
 	u64		ControlRequests;
 	u64		InputMegabytes;
 	u64		OutputMegabytes;
+	u64		VLinkFailureCount;
+	u64		MissDiscAdvCount;
 };
 
 /**
#4 lore raw

PATCH 4/9 libfcoe: add checking disable flag in FIP_FKA_ADV

In reply to Robert Love

From: Yi Zou <[email protected]>

When the D bit is set if the FKA_ADV_Period of the FIP Discovery Advertisement, the ENode should not transmit period ENode FIP Keep Alive and VN_Port FIP Keep Alive (FC-BB-5 Rev2, 7.8.3.13).

Note that fcf->flags is taken directly from the fip_header, I am claiming one bit for the purpose of the FIP_FKA_Period D bit as FIP_FL_FK_ADV_B, and use FIP_HEADER_FLAGS as bitmask for bits used in fip_header.

---

 drivers/scsi/fcoe/libfcoe.c |    4 +++-
 include/scsi/fc/fc_fip.h    |   12 +++++++++++-
 include/scsi/libfcoe.h      |    1 +
 3 files changed, 15 insertions(+), 2 deletions(-)
drivers/scsi/fcoe/libfcoe.c +3 -1
index 3c501d4..9961fd7 100644
@@ -665,6 +665,8 @@ static int fcoe_ctlr_parse_adv(struct fcoe_ctlr *fip,
 			if (dlen != sizeof(struct fip_fka_desc))
 				goto len_err;
 			fka = (struct fip_fka_desc *)desc;
+			if (fka->fd_flags & FIP_FKA_ADV_D)
+				fcf->fd_flags = 1;
 			t = ntohl(fka->fd_fka_period);
 			if (t >= FCOE_CTLR_MIN_FKA)
 				fcf->fka_period = msecs_to_jiffies(t);
@@ -1160,7 +1162,7 @@ static void fcoe_ctlr_timeout(unsigned long arg)
 		}
 	}
 
-	if (sel) {
+	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;
include/scsi/fc/fc_fip.h +11 -1
index 3d138c1..17baa19 100644
@@ -214,11 +214,21 @@ struct fip_vn_desc {
  */
 struct fip_fka_desc {
 	struct fip_desc fd_desc;
-	__u8		fd_resvd[2];
+	__u8		fd_resvd;
+	__u8		fd_flags;	/* bit0 is fka disable flag */
 	__be32		fd_fka_period;	/* adv./keep-alive period in mS */
 } __attribute__((packed));
 
 /*
+ * flags for fip_fka_desc.fd_flags
+ */
+enum fip_fka_flags {
+	FIP_FKA_ADV_D =	0x01,		/* no need for FKA from ENode */
+};
+
+/* FIP_DT_FKA flags */
+
+/*
  * FIP_DT_VENDOR descriptor.
  */
 struct fip_vendor_desc {
include/scsi/libfcoe.h +1 -0
index 3837872..c603f4a 100644
@@ -155,6 +155,7 @@ struct fcoe_fcf {
 	u8 pri;
 	u16 flags;
 	u32 fka_period;
+	u8 fd_flags:1;
 };
 
 /* FIP API functions */
#5 lore raw

PATCH 5/9 libfcoe: add tracking FIP Virtual Link Failure count

In reply to Robert Love

From: Yi Zou <[email protected]>

Add tracking the Virtual Link Failure count when either we have found the FCF as "aged" or we are receiving FIP Clear Virtual Link from the FCF.

---

 drivers/scsi/fcoe/libfcoe.c |    2 ++
 1 files changed, 2 insertions(+), 0 deletions(-)
drivers/scsi/fcoe/libfcoe.c +2 -0
index 9961fd7..34800af 100644
@@ -578,6 +578,7 @@ static void fcoe_ctlr_age_fcfs(struct fcoe_ctlr *fip)
 			WARN_ON(!fip->fcf_count);
 			fip->fcf_count--;
 			kfree(fcf);
+			fc_lport_get_stats(fip->lp)->VLinkFailureCount++;
 		} else if (fcoe_ctlr_mtu_valid(fcf) &&
 			   (!sel_time || time_before(sel_time, fcf->time))) {
 			sel_time = fcf->time;
@@ -990,6 +991,7 @@ static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip,
 		LIBFCOE_FIP_DBG(fip, "performing Clear Virtual Link\n");
 
 		spin_lock_bh(&fip->lock);
+		fc_lport_get_stats(lport)->VLinkFailureCount++;
 		fcoe_ctlr_reset(fip);
 		spin_unlock_bh(&fip->lock);
 
#6 lore raw

PATCH 6/9 libfcoe: add tracking FIP Missing Discovery Advertisement count

In reply to Robert Love

From: Yi Zou <[email protected]>

Add tracking the Missing Discovery Advertisement count for FIP Fiber Channel Forwarder (FCF) as described in FC-BB-5 Rev2.0 for LESB. The time is 1.5 times the FKA_ADV_PERIOD of the corresponding FCF.

---

 drivers/scsi/fcoe/libfcoe.c |   14 ++++++++++++++
 1 files changed, 14 insertions(+), 0 deletions(-)
drivers/scsi/fcoe/libfcoe.c +14 -0
index 34800af..9823291 100644
@@ -562,14 +562,28 @@ EXPORT_SYMBOL(fcoe_ctlr_els_send);
  * times its keep-alive period including fuzz.
  *
  * In addition, determine the time when an FCF selection can occur.
+ *
+ * Also, increment the MissDiscAdvCount when no advertisement is received
+ * for the corresponding FCF for 1.5 * FKA_ADV_PERIOD (FC-BB-5 LESB).
  */
 static void fcoe_ctlr_age_fcfs(struct fcoe_ctlr *fip)
 {
 	struct fcoe_fcf *fcf;
 	struct fcoe_fcf *next;
 	unsigned long sel_time = 0;
+	unsigned long mda_time = 0;
 
 	list_for_each_entry_safe(fcf, next, &fip->fcfs, list) {
+		mda_time = fcf->fka_period + (fcf->fka_period >> 1);
+		if ((fip->sel_fcf == fcf) &&
+		    (time_after(jiffies, fcf->time + mda_time))) {
+			mod_timer(&fip->timer, jiffies + mda_time);
+			fc_lport_get_stats(fip->lp)->MissDiscAdvCount++;
+			printk(KERN_INFO "libfcoe: host%d: Missing Discovery "
+			       "Advertisement for fab %llx count %lld\n",
+			       fip->lp->host->host_no, fcf->fabric_name,
+			       fc_lport_get_stats(fip->lp)->MissDiscAdvCount);
+		}
 		if (time_after(jiffies, fcf->time + fcf->fka_period * 3 +
 			       msecs_to_jiffies(FIP_FCF_FUZZ * 3))) {
 			if (fip->sel_fcf == fcf)
#7 lore raw

PATCH 7/9 libfc: add fcoe_fc_els_lesb to fc_fcoe.h for FC-BB-5 LESB definitions

In reply to Robert Love

From: Yi Zou <[email protected]>

Add struct fcoe_fc_els_lesb as described in FC-BB-5 LESB for FCoE. It has the same size as LESB defined in FC-FS-3 (struct fc_els_lesb) but members have different meanings according to FC-BB-5.

---

 include/scsi/fc/fc_fcoe.h |   12 ++++++++++++
 1 files changed, 12 insertions(+), 0 deletions(-)
include/scsi/fc/fc_fcoe.h +12 -0
index ccb3dbe..e6ad3d2 100644
@@ -86,6 +86,18 @@ struct fcoe_crc_eof {
 #define FCOE_MIN_FRAME 46
 
 /*
+ * FCoE Link Error Status Block: T11 FC-BB-5 Rev2.0, Clause 7.10.
+ */
+struct fcoe_fc_els_lesb {
+	__be32		lesb_link_fail;	/* link failure count */
+	__be32		lesb_vlink_fail; /* virtual link failure count */
+	__be32		lesb_miss_fka;	/* missing FIP keep-alive count */
+	__be32		lesb_symb_err;	/* symbol error during carrier count */
+	__be32		lesb_err_block;	/* errored block count */
+	__be32		lesb_fcs_error; /* frame check sequence error count */
+};
+
+/*
  * fc_fcoe_set_mac - Store OUI + DID into MAC address field.
  * @mac: mac address to be set
  * @did: fc dest id to use
#8 lore raw

PATCH 8/9 fcoe, libfc: add get_lesb() to allow LLD to fill the link error status block (LESB)

In reply to Robert Love

From: Yi Zou <[email protected]>

Add a member function pointer as get_lesb to libfc_function_template so LLD can fill the LESB based on its own statistics. For fcoe, it fills the LESB as a fcoe_fc_els_lesb struct according to FC-BB-5.

---

 drivers/scsi/fcoe/fcoe.c |   34 ++++++++++++++++++++++++++++++++++
 include/scsi/libfc.h     |    6 ++++++
 2 files changed, 40 insertions(+), 0 deletions(-)
drivers/scsi/fcoe/fcoe.c +34 -0
index 32298ed..a30ffaa 100644
@@ -111,6 +111,8 @@ static struct fc_seq *fcoe_elsct_send(struct fc_lport *,
 				      void *, u32 timeout);
 static void fcoe_recv_frame(struct sk_buff *skb);
 
+static void fcoe_get_lesb(struct fc_lport *, struct fc_els_lesb *);
+
 module_param_call(create, fcoe_create, NULL, NULL, S_IWUSR);
 __MODULE_PARM_TYPE(create, "string");
 MODULE_PARM_DESC(create, "Create fcoe fcoe using net device passed in.");
@@ -141,6 +143,7 @@ static struct libfc_function_template fcoe_libfc_fcn_templ = {
 	.ddp_setup = fcoe_ddp_setup,
 	.ddp_done = fcoe_ddp_done,
 	.elsct_send = fcoe_elsct_send,
+	.get_lesb = fcoe_get_lesb,
 };
 
 struct fc_function_template fcoe_transport_function = {
@@ -2455,3 +2458,34 @@ static void fcoe_set_vport_symbolic_name(struct fc_vport *vport)
 	lport->tt.elsct_send(lport, FC_FID_DIR_SERV, fp, FC_NS_RSPN_ID,
 			     NULL, NULL, 3 * lport->r_a_tov);
 }
+
+/**
+ * fcoe_get_lesb() - Fill the FCoE Link Error Status Block
+ * @lport: the local port
+ * @fc_lesb: the link error status block
+ */
+static void fcoe_get_lesb(struct fc_lport *lport,
+			 struct fc_els_lesb *fc_lesb)
+{
+	unsigned int cpu;
+	u32 lfc, vlfc, mdac;
+	struct fcoe_dev_stats *devst;
+	struct fcoe_fc_els_lesb *lesb;
+	struct net_device *netdev = fcoe_netdev(lport);
+
+	lfc = 0;
+	vlfc = 0;
+	mdac = 0;
+	lesb = (struct fcoe_fc_els_lesb *)fc_lesb;
+	memset(lesb, 0, sizeof(*lesb));
+	for_each_possible_cpu(cpu) {
+		devst = per_cpu_ptr(lport->dev_stats, cpu);
+		lfc += devst->LinkFailureCount;
+		vlfc += devst->VLinkFailureCount;
+		mdac += devst->MissDiscAdvCount;
+	}
+	lesb->lesb_link_fail = htonl(lfc);
+	lesb->lesb_vlink_fail = htonl(vlfc);
+	lesb->lesb_miss_fka = htonl(mdac);
+	lesb->lesb_fcs_error = htonl(dev_get_stats(netdev)->rx_crc_errors);
+}
include/scsi/libfc.h +6 -0
index b97be29..4b912ee 100644
@@ -511,6 +511,12 @@ struct libfc_function_template {
 	 */
 	int (*ddp_done)(struct fc_lport *, u16);
 	/*
+	 * Allow LLD to fill its own Link Error Status Block
+	 *
+	 * STATUS: OPTIONAL
+	 */
+	void (*get_lesb)(struct fc_lport *, struct fc_els_lesb *lesb);
+	/*
 	 * Send a frame using an existing sequence and exchange.
 	 *
 	 * STATUS: OPTIONAL
#9 lore raw

PATCH 9/9 libfc: add support of receiving ELS_RLS

In reply to Robert Love

From: Yi Zou <[email protected]>

Upon receiving ELS_RLS, send the Link Error Status Block (LESB) back.

---

 drivers/scsi/libfc/fc_rport.c |   76 +++++++++++++++++++++++++++++++++++++++++
 1 files changed, 76 insertions(+), 0 deletions(-)
drivers/scsi/libfc/fc_rport.c +76 -0
index 91e2ba2..35ca0e7 100644
@@ -1098,6 +1098,78 @@ 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_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));
+
+	rls = fc_frame_payload_get(rx_fp, sizeof(*rls));
+	if (!rls) {
+		rjt_data.reason = ELS_RJT_PROT;
+		rjt_data.explan = ELS_EXPL_INV_LEN;
+		goto out_rjt;
+	}
+
+	fp = fc_frame_alloc(lport, sizeof(*rsp));
+	if (!fp) {
+		rjt_data.reason = ELS_RJT_UNAB;
+		rjt_data.explan = ELS_EXPL_INSUF_RES;
+		goto out_rjt;
+	}
+
+	rsp = fc_frame_payload_get(fp, sizeof(*rsp));
+	memset(rsp, 0, sizeof(*rsp));
+	rsp->rls_cmd = ELS_LS_ACC;
+	lesb = &rsp->rls_lesb;
+	if (lport->tt.get_lesb) {
+		/* get LESB from LLD if it supports it */
+		lport->tt.get_lesb(lport, lesb);
+	} else {
+		fc_get_host_stats(lport->host);
+		hst = &lport->host_stats;
+		lesb->lesb_link_fail = htonl(hst->link_failure_count);
+		lesb->lesb_sync_loss = htonl(hst->loss_of_sync_count);
+		lesb->lesb_sig_loss = htonl(hst->loss_of_signal_count);
+		lesb->lesb_prim_err = htonl(hst->prim_seq_protocol_err_count);
+		lesb->lesb_inv_word = htonl(hst->invalid_tx_word_count);
+		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);
+	goto out;
+
+out_rjt:
+	rjt_data.fp = NULL;
+	lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data);
+out:
+	fc_frame_free(rx_fp);
+}
+
+/**
  * 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
@@ -1159,6 +1231,9 @@ static void fc_rport_recv_els_req(struct fc_lport *lport,
 		els_data.fp = fp;
 		lport->tt.seq_els_rsp_send(sp, ELS_REC, &els_data);
 		break;
+	case ELS_RLS:
+		fc_rport_recv_rls_req(rdata, sp, fp);
+		break;
 	default:
 		fc_frame_free(fp);	/* can't happen */
 		break;
@@ -1203,6 +1278,7 @@ void fc_rport_recv_req(struct fc_seq *sp, struct fc_frame *fp,
 	case ELS_ADISC:
 	case ELS_RRQ:
 	case ELS_REC:
+	case ELS_RLS:
 		fc_rport_recv_els_req(lport, sp, fp);
 		break;
 	default: