All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCHv2 net-next 0/3] cxgb4/cxgb4i: add iscsi LRO  support for chelsio t4/t5 adapters
@ 2016-01-07  9:24 Hariprasad Shenai
  2016-01-07  9:24 ` [PATCHv2 net-next 1/3] cxgb4: Add LRO infrastructure for cxgb4i driver Hariprasad Shenai
                   ` (3 more replies)
  0 siblings, 4 replies; 6+ messages in thread
From: Hariprasad Shenai @ 2016-01-07  9:24 UTC (permalink / raw)
  To: netdev, linux-scsi
  Cc: davem, JBottomley, martin.petersen, leedom, kxie, manojmalviya,
	nirranjan, Hariprasad Shenai

Hi

This patch series adds LRO iscsi support for chelsio T4/T5 adapters.

This patch series has been created against net-next tree and includes
patches on cxgb4 and cxgb4i driver.

We have included all the maintainers of respective drivers. Kindly review
the change and let us know in case of any review comments.

Thanks

V2:
 Missed a new header file cxgbi_lro.h in PATCH 2/3. Adding it.
 Based on failure reported by by kbuild test bot <lkp@intel.com>

Hariprasad Shenai (3):
  cxgb4: Add LRO infrastructure for cxgb4i driver
  libcxgbi: add pdu parsing of LRO'ed skbs
  cxgb4i: add iscsi LRO for chelsio t4/t5 adapters

 drivers/net/ethernet/chelsio/cxgb4/cxgb4.h      |  15 +-
 drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c |  40 ++-
 drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h  |   5 +
 drivers/net/ethernet/chelsio/cxgb4/sge.c        |  10 +-
 drivers/scsi/cxgbi/cxgb4i/cxgb4i.c              | 396 +++++++++++++++++++++-
 drivers/scsi/cxgbi/cxgbi_lro.h                  |  72 ++++
 drivers/scsi/cxgbi/libcxgbi.c                   | 415 +++++++++++++++++++-----
 drivers/scsi/cxgbi/libcxgbi.h                   |   4 +
 8 files changed, 840 insertions(+), 117 deletions(-)
 create mode 100644 drivers/scsi/cxgbi/cxgbi_lro.h

-- 
2.3.4

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

* [PATCHv2 net-next 1/3] cxgb4: Add LRO infrastructure for cxgb4i driver
  2016-01-07  9:24 [PATCHv2 net-next 0/3] cxgb4/cxgb4i: add iscsi LRO support for chelsio t4/t5 adapters Hariprasad Shenai
@ 2016-01-07  9:24 ` Hariprasad Shenai
  2016-01-09  2:53   ` David Miller
  2016-01-07  9:24 ` [PATCHv2 net-next 2/3] libcxgbi: add pdu parsing of LRO'ed skbs Hariprasad Shenai
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 6+ messages in thread
From: Hariprasad Shenai @ 2016-01-07  9:24 UTC (permalink / raw)
  To: netdev, linux-scsi
  Cc: davem, JBottomley, martin.petersen, leedom, kxie, manojmalviya,
	nirranjan, Hariprasad Shenai

Signed-off-by: Hariprasad Shenai <hariprasad@chelsio.com>
---
 drivers/net/ethernet/chelsio/cxgb4/cxgb4.h      | 15 +++++++++-
 drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c | 40 ++++++++++++++++++-------
 drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h  |  5 ++++
 drivers/net/ethernet/chelsio/cxgb4/sge.c        | 10 +++++--
 4 files changed, 56 insertions(+), 14 deletions(-)

diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
index ec6e849..a89e64e 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
@@ -508,6 +508,16 @@ struct pkt_gl {
 
 typedef int (*rspq_handler_t)(struct sge_rspq *q, const __be64 *rsp,
 			      const struct pkt_gl *gl);
+typedef void (*rspq_flush_handler_t)(struct sge_rspq *q);
+
+/* LRO related declarations for ULD */
+struct t4_lro_mgr {
+#define MAX_LRO_SESSIONS		64
+	u8 lro_session_cnt;         /* # of sessions to aggregate */
+	unsigned long lro_pkts;     /* # of LRO super packets */
+	unsigned long lro_merged;   /* # of wire packets merged by LRO */
+	struct sk_buff *skb_head, *skb_tail; /* list of aggregated sessions */
+};
 
 struct sge_rspq {                   /* state for an SGE response queue */
 	struct napi_struct napi;
@@ -532,6 +542,8 @@ struct sge_rspq {                   /* state for an SGE response queue */
 	struct adapter *adap;
 	struct net_device *netdev;  /* associated net device */
 	rspq_handler_t handler;
+	rspq_flush_handler_t flush_handler;
+	struct t4_lro_mgr lro_mgr;
 #ifdef CONFIG_NET_RX_BUSY_POLL
 #define CXGB_POLL_STATE_IDLE		0
 #define CXGB_POLL_STATE_NAPI		BIT(0) /* NAPI owns this poll */
@@ -1107,7 +1119,8 @@ int t4_mgmt_tx(struct adapter *adap, struct sk_buff *skb);
 int t4_ofld_send(struct adapter *adap, struct sk_buff *skb);
 int t4_sge_alloc_rxq(struct adapter *adap, struct sge_rspq *iq, bool fwevtq,
 		     struct net_device *dev, int intr_idx,
-		     struct sge_fl *fl, rspq_handler_t hnd, int cong);
+		     struct sge_fl *fl, rspq_handler_t hnd,
+		     rspq_flush_handler_t flush_hnd, int cong);
 int t4_sge_alloc_eth_txq(struct adapter *adap, struct sge_eth_txq *txq,
 			 struct net_device *dev, struct netdev_queue *netdevq,
 			 unsigned int iqid);
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
index b8a5fb0..68c2aae 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
@@ -640,6 +640,13 @@ out:
 	return 0;
 }
 
+/* Flush the aggregated lro sessions */
+static void uldrx_flush_handler(struct sge_rspq *q)
+{
+	if (ulds[q->uld].lro_flush)
+		ulds[q->uld].lro_flush(&q->lro_mgr);
+}
+
 /**
  *	uldrx_handler - response queue handler for ULD queues
  *	@q: the response queue that received the packet
@@ -653,6 +660,8 @@ static int uldrx_handler(struct sge_rspq *q, const __be64 *rsp,
 			 const struct pkt_gl *gl)
 {
 	struct sge_ofld_rxq *rxq = container_of(q, struct sge_ofld_rxq, rspq);
+	unsigned int napi_id = q->napi.napi_id;
+	int ret;
 
 	/* FW can send CPLs encapsulated in a CPL_FW4_MSG.
 	 */
@@ -660,7 +669,15 @@ static int uldrx_handler(struct sge_rspq *q, const __be64 *rsp,
 	    ((const struct cpl_fw4_msg *)(rsp + 1))->type == FW_TYPE_RSSCPL)
 		rsp += 2;
 
-	if (ulds[q->uld].rx_handler(q->adap->uld_handle[q->uld], rsp, gl)) {
+	if (q->flush_handler)
+		ret = ulds[q->uld].lro_rx_handler(q->adap->uld_handle[q->uld],
+						  rsp, gl, &q->lro_mgr,
+						  napi_id);
+	else
+		ret = ulds[q->uld].rx_handler(q->adap->uld_handle[q->uld],
+					      rsp, gl);
+
+	if (ret) {
 		rxq->stats.nomem++;
 		return -1;
 	}
@@ -960,7 +977,7 @@ static void enable_rx(struct adapter *adap)
 
 static int alloc_ofld_rxqs(struct adapter *adap, struct sge_ofld_rxq *q,
 			   unsigned int nq, unsigned int per_chan, int msi_idx,
-			   u16 *ids)
+			   u16 *ids, bool lro)
 {
 	int i, err;
 
@@ -970,7 +987,8 @@ static int alloc_ofld_rxqs(struct adapter *adap, struct sge_ofld_rxq *q,
 		err = t4_sge_alloc_rxq(adap, &q->rspq, false,
 				       adap->port[i / per_chan],
 				       msi_idx, q->fl.size ? &q->fl : NULL,
-				       uldrx_handler, 0);
+				       uldrx_handler,
+				       lro ? uldrx_flush_handler : NULL, 0);
 		if (err)
 			return err;
 		memset(&q->stats, 0, sizeof(q->stats));
@@ -1000,7 +1018,7 @@ static int setup_sge_queues(struct adapter *adap)
 		msi_idx = 1;         /* vector 0 is for non-queue interrupts */
 	else {
 		err = t4_sge_alloc_rxq(adap, &s->intrq, false, adap->port[0], 0,
-				       NULL, NULL, -1);
+				       NULL, NULL, NULL, -1);
 		if (err)
 			return err;
 		msi_idx = -((int)s->intrq.abs_id + 1);
@@ -1020,7 +1038,7 @@ static int setup_sge_queues(struct adapter *adap)
 	 *    new/deleted queues.
 	 */
 	err = t4_sge_alloc_rxq(adap, &s->fw_evtq, true, adap->port[0],
-			       msi_idx, NULL, fwevtq_handler, -1);
+			       msi_idx, NULL, fwevtq_handler, NULL, -1);
 	if (err) {
 freeout:	t4_free_sge_resources(adap);
 		return err;
@@ -1037,7 +1055,7 @@ freeout:	t4_free_sge_resources(adap);
 				msi_idx++;
 			err = t4_sge_alloc_rxq(adap, &q->rspq, false, dev,
 					       msi_idx, &q->fl,
-					       t4_ethrx_handler,
+					       t4_ethrx_handler, NULL,
 					       t4_get_mps_bg_map(adap,
 								 pi->tx_chan));
 			if (err)
@@ -1063,18 +1081,18 @@ freeout:	t4_free_sge_resources(adap);
 			goto freeout;
 	}
 
-#define ALLOC_OFLD_RXQS(firstq, nq, per_chan, ids) do { \
-	err = alloc_ofld_rxqs(adap, firstq, nq, per_chan, msi_idx, ids); \
+#define ALLOC_OFLD_RXQS(firstq, nq, per_chan, ids, lro) do { \
+	err = alloc_ofld_rxqs(adap, firstq, nq, per_chan, msi_idx, ids, lro); \
 	if (err) \
 		goto freeout; \
 	if (msi_idx > 0) \
 		msi_idx += nq; \
 } while (0)
 
-	ALLOC_OFLD_RXQS(s->iscsirxq, s->iscsiqsets, j, s->iscsi_rxq);
-	ALLOC_OFLD_RXQS(s->rdmarxq, s->rdmaqs, 1, s->rdma_rxq);
+	ALLOC_OFLD_RXQS(s->iscsirxq, s->iscsiqsets, j, s->iscsi_rxq, true);
+	ALLOC_OFLD_RXQS(s->rdmarxq, s->rdmaqs, 1, s->rdma_rxq, false);
 	j = s->rdmaciqs / adap->params.nports; /* rdmaq queues per channel */
-	ALLOC_OFLD_RXQS(s->rdmaciq, s->rdmaciqs, j, s->rdma_ciq);
+	ALLOC_OFLD_RXQS(s->rdmaciq, s->rdmaciqs, j, s->rdma_ciq, false);
 
 #undef ALLOC_OFLD_RXQS
 
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h
index cf711d5..ef75eb3 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h
@@ -212,6 +212,7 @@ struct l2t_data;
 struct net_device;
 struct pkt_gl;
 struct tp_tcp_stats;
+struct t4_lro_mgr;
 
 struct cxgb4_range {
 	unsigned int start;
@@ -283,6 +284,10 @@ struct cxgb4_uld_info {
 			  const struct pkt_gl *gl);
 	int (*state_change)(void *handle, enum cxgb4_state new_state);
 	int (*control)(void *handle, enum cxgb4_control control, ...);
+	int (*lro_rx_handler)(void *handle, const __be64 *rsp,
+			      const struct pkt_gl *gl,
+			      struct t4_lro_mgr *lro_mgr, unsigned int napi_id);
+	void (*lro_flush)(struct t4_lro_mgr *);
 };
 
 int cxgb4_register_uld(enum cxgb4_uld type, const struct cxgb4_uld_info *p);
diff --git a/drivers/net/ethernet/chelsio/cxgb4/sge.c b/drivers/net/ethernet/chelsio/cxgb4/sge.c
index b4eb468..3590d9c 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/sge.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/sge.c
@@ -2157,8 +2157,11 @@ static int process_responses(struct sge_rspq *q, int budget)
 
 	while (likely(budget_left)) {
 		rc = (void *)q->cur_desc + (q->iqe_len - sizeof(*rc));
-		if (!is_new_response(rc, q))
+		if (!is_new_response(rc, q)) {
+			if (q->flush_handler)
+				q->flush_handler(q);
 			break;
+		}
 
 		dma_rmb();
 		rsp_type = RSPD_TYPE_G(rc->type_gen);
@@ -2544,7 +2547,8 @@ static void __iomem *bar2_address(struct adapter *adapter,
  */
 int t4_sge_alloc_rxq(struct adapter *adap, struct sge_rspq *iq, bool fwevtq,
 		     struct net_device *dev, int intr_idx,
-		     struct sge_fl *fl, rspq_handler_t hnd, int cong)
+		     struct sge_fl *fl, rspq_handler_t hnd,
+		     rspq_flush_handler_t flush_hnd, int cong)
 {
 	int ret, flsz = 0;
 	struct fw_iq_cmd c;
@@ -2638,6 +2642,8 @@ int t4_sge_alloc_rxq(struct adapter *adap, struct sge_rspq *iq, bool fwevtq,
 	iq->size--;                           /* subtract status entry */
 	iq->netdev = dev;
 	iq->handler = hnd;
+	iq->flush_handler = flush_hnd;
+	memset(&iq->lro_mgr, 0, sizeof(struct t4_lro_mgr));
 
 	/* set offset to -1 to distinguish ingress queues without FL */
 	iq->offset = fl ? 0 : -1;
-- 
2.3.4


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

* [PATCHv2 net-next 2/3] libcxgbi: add pdu parsing of LRO'ed skbs
  2016-01-07  9:24 [PATCHv2 net-next 0/3] cxgb4/cxgb4i: add iscsi LRO support for chelsio t4/t5 adapters Hariprasad Shenai
  2016-01-07  9:24 ` [PATCHv2 net-next 1/3] cxgb4: Add LRO infrastructure for cxgb4i driver Hariprasad Shenai
@ 2016-01-07  9:24 ` Hariprasad Shenai
  2016-01-07  9:24 ` [PATCHv2 net-next 3/3] cxgb4i: add iscsi LRO for chelsio t4/t5 adapters Hariprasad Shenai
  2016-01-11 20:41 ` [PATCHv2 net-next 0/3] cxgb4/cxgb4i: add iscsi LRO support " Mike Christie
  3 siblings, 0 replies; 6+ messages in thread
From: Hariprasad Shenai @ 2016-01-07  9:24 UTC (permalink / raw)
  To: netdev, linux-scsi
  Cc: davem, JBottomley, martin.petersen, leedom, kxie, manojmalviya,
	nirranjan, Hariprasad Shenai

The skbs could contain both paritial pdus and multiple completed pdus.

Signed-off-by: Karen Xie <kxie@chelsio.com>
Signed-off-by: Manoj Malviya <manojmalviya@chelsio.com>
Signed-off-by: Hariprasad Shenai <hariprasad@chelsio.com>
---
 drivers/scsi/cxgbi/cxgbi_lro.h |  72 +++++++
 drivers/scsi/cxgbi/libcxgbi.c  | 415 ++++++++++++++++++++++++++++++++---------
 drivers/scsi/cxgbi/libcxgbi.h  |   4 +
 3 files changed, 402 insertions(+), 89 deletions(-)
 create mode 100644 drivers/scsi/cxgbi/cxgbi_lro.h

diff --git a/drivers/scsi/cxgbi/cxgbi_lro.h b/drivers/scsi/cxgbi/cxgbi_lro.h
new file mode 100644
index 0000000..5a849f4
--- /dev/null
+++ b/drivers/scsi/cxgbi/cxgbi_lro.h
@@ -0,0 +1,72 @@
+/*
+ * cxgbi_lro.h: Chelsio iSCSI LRO functions for T4/5 iSCSI driver.
+ *
+ * Copyright (c) 2015 Chelsio Communications, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation.
+ *
+ * Written by: Karen Xie (kxie@chelsio.com)
+ */
+
+#ifndef	__CXGBI_LRO_H__
+#define	__CXGBI_LRO_H__
+
+#include <linux/errno.h>
+#include <linux/types.h>
+
+enum {
+	CXGBI_LRO_CB_USED	= (1 << 0),
+};
+
+#define LRO_FLUSH_TOTALLEN_MAX	65535
+struct cxgbi_rx_lro_cb {
+	struct cxgbi_sock *csk;
+	__u32 pdu_totallen;
+	u8 pdu_cnt;
+	u8 flags;
+};
+
+struct cxgbi_rx_pdu_cb {
+	unsigned long flags;
+	unsigned int seq;
+	__u32 ddigest;
+	__u32 pdulen;
+	u32 frags;
+};
+
+#define LRO_SKB_MAX_HEADROOM  \
+		(sizeof(struct cxgbi_rx_lro_cb) + \
+		 MAX_SKB_FRAGS * sizeof(struct cxgbi_rx_pdu_cb))
+
+#define LRO_SKB_MIN_HEADROOM  \
+		(sizeof(struct cxgbi_rx_lro_cb) + \
+		 sizeof(struct cxgbi_rx_pdu_cb))
+
+#define cxgbi_skb_rx_lro_cb(skb)	((struct cxgbi_rx_lro_cb *)skb->head)
+#define cxgbi_skb_rx_pdu_cb(skb, i)	\
+	((struct cxgbi_rx_pdu_cb *)(skb->head + sizeof(struct cxgbi_rx_lro_cb) \
+					+ i * sizeof(struct cxgbi_rx_pdu_cb)))
+
+static inline void cxgbi_rx_cb_set_flag(struct cxgbi_rx_pdu_cb *cb,
+					int flag)
+{
+	__set_bit(flag, &cb->flags);
+}
+
+static inline void cxgbi_rx_cb_clear_flag(struct cxgbi_rx_pdu_cb *cb,
+					  int flag)
+{
+	__clear_bit(flag, &cb->flags);
+}
+
+static inline int cxgbi_rx_cb_test_flag(struct cxgbi_rx_pdu_cb *cb,
+					int flag)
+{
+	return test_bit(flag, &cb->flags);
+}
+
+void cxgbi_lro_skb_dump(struct sk_buff *);
+
+#endif	/*__CXGBI_LRO_H__*/
diff --git a/drivers/scsi/cxgbi/libcxgbi.c b/drivers/scsi/cxgbi/libcxgbi.c
index f3bb7af..dc9b470 100644
--- a/drivers/scsi/cxgbi/libcxgbi.c
+++ b/drivers/scsi/cxgbi/libcxgbi.c
@@ -35,6 +35,7 @@
 static unsigned int dbg_level;
 
 #include "libcxgbi.h"
+#include "cxgbi_lro.h"
 
 #define DRV_MODULE_NAME		"libcxgbi"
 #define DRV_MODULE_DESC		"Chelsio iSCSI driver library"
@@ -533,6 +534,7 @@ static void sock_put_port(struct cxgbi_sock *csk)
 /*
  * iscsi tcp connection
  */
+static void skb_lro_hold_done(struct cxgbi_sock *csk);
 void cxgbi_sock_free_cpl_skbs(struct cxgbi_sock *csk)
 {
 	if (csk->cpl_close) {
@@ -547,6 +549,11 @@ void cxgbi_sock_free_cpl_skbs(struct cxgbi_sock *csk)
 		kfree_skb(csk->cpl_abort_rpl);
 		csk->cpl_abort_rpl = NULL;
 	}
+	if (csk->skb_lro_hold) {
+		skb_lro_hold_done(csk);
+		kfree_skb(csk->skb_lro_hold);
+		csk->skb_lro_hold = NULL;
+	}
 }
 EXPORT_SYMBOL_GPL(cxgbi_sock_free_cpl_skbs);
 
@@ -1145,15 +1152,15 @@ static int cxgbi_sock_send_pdus(struct cxgbi_sock *csk, struct sk_buff *skb)
 
 		if (unlikely(skb_headroom(skb) < cdev->skb_tx_rsvd)) {
 			pr_err("csk 0x%p, skb head %u < %u.\n",
-				csk, skb_headroom(skb), cdev->skb_tx_rsvd);
+			       csk, skb_headroom(skb), cdev->skb_tx_rsvd);
 			err = -EINVAL;
 			goto out_err;
 		}
 
 		if (frags >= SKB_WR_LIST_SIZE) {
 			pr_err("csk 0x%p, frags %d, %u,%u >%u.\n",
-				csk, skb_shinfo(skb)->nr_frags, skb->len,
-				skb->data_len, (uint)(SKB_WR_LIST_SIZE));
+			       csk, skb_shinfo(skb)->nr_frags, skb->len,
+			       skb->data_len, (uint)(SKB_WR_LIST_SIZE));
 			err = -EINVAL;
 			goto out_err;
 		}
@@ -1776,10 +1783,8 @@ EXPORT_SYMBOL_GPL(cxgbi_conn_tx_open);
 /*
  * pdu receive, interact with libiscsi_tcp
  */
-static inline int read_pdu_skb(struct iscsi_conn *conn,
-			       struct sk_buff *skb,
-			       unsigned int offset,
-			       int offloaded)
+static inline int read_pdu_skb(struct iscsi_conn *conn, struct sk_buff *skb,
+			       unsigned int offset, int offloaded)
 {
 	int status = 0;
 	int bytes_read;
@@ -1817,7 +1822,8 @@ static inline int read_pdu_skb(struct iscsi_conn *conn,
 	}
 }
 
-static int skb_read_pdu_bhs(struct iscsi_conn *conn, struct sk_buff *skb)
+static int skb_read_pdu_bhs(struct iscsi_conn *conn, struct sk_buff *skb,
+			    unsigned int offset, unsigned long *flag)
 {
 	struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
 
@@ -1831,18 +1837,18 @@ static int skb_read_pdu_bhs(struct iscsi_conn *conn, struct sk_buff *skb)
 		return -EIO;
 	}
 
-	if (conn->hdrdgst_en &&
-	    cxgbi_skcb_test_flag(skb, SKCBF_RX_HCRC_ERR)) {
+	if (conn->hdrdgst_en && test_bit(SKCBF_RX_HCRC_ERR, flag)) {
 		pr_info("conn 0x%p, skb 0x%p, hcrc.\n", conn, skb);
 		iscsi_conn_failure(conn, ISCSI_ERR_HDR_DGST);
 		return -EIO;
 	}
 
-	return read_pdu_skb(conn, skb, 0, 0);
+	return read_pdu_skb(conn, skb, offset, 0);
 }
 
 static int skb_read_pdu_data(struct iscsi_conn *conn, struct sk_buff *lskb,
-			     struct sk_buff *skb, unsigned int offset)
+			     struct sk_buff *skb, unsigned int offset,
+			     unsigned long *flag)
 {
 	struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
 	bool offloaded = 0;
@@ -1850,12 +1856,11 @@ static int skb_read_pdu_data(struct iscsi_conn *conn, struct sk_buff *lskb,
 
 	log_debug(1 << CXGBI_DBG_PDU_RX,
 		"conn 0x%p, skb 0x%p, len %u, flag 0x%lx.\n",
-		conn, skb, skb->len, cxgbi_skcb_flags(skb));
+		conn, skb, skb->len, *flag);
 
-	if (conn->datadgst_en &&
-	    cxgbi_skcb_test_flag(lskb, SKCBF_RX_DCRC_ERR)) {
+	if (conn->datadgst_en && test_bit(SKCBF_RX_DCRC_ERR, flag)) {
 		pr_info("conn 0x%p, skb 0x%p, dcrc 0x%lx.\n",
-			conn, lskb, cxgbi_skcb_flags(lskb));
+			conn, lskb, *flag);
 		iscsi_conn_failure(conn, ISCSI_ERR_DATA_DGST);
 		return -EIO;
 	}
@@ -1867,7 +1872,7 @@ static int skb_read_pdu_data(struct iscsi_conn *conn, struct sk_buff *lskb,
 	if (lskb == skb && conn->hdrdgst_en)
 		offset += ISCSI_DIGEST_SIZE;
 
-	if (cxgbi_skcb_test_flag(lskb, SKCBF_RX_DATA_DDPD))
+	if (test_bit(SKCBF_RX_DATA_DDPD, flag))
 		offloaded = 1;
 
 	if (opcode == ISCSI_OP_SCSI_DATA_IN)
@@ -1905,11 +1910,304 @@ static void csk_return_rx_credits(struct cxgbi_sock *csk, int copied)
 		csk->rcv_wup += cdev->csk_send_rx_credits(csk, credits);
 }
 
+static struct sk_buff *cxgbi_conn_rxq_get_skb(struct cxgbi_sock *csk)
+{
+	struct sk_buff *skb = skb_peek(&csk->receive_queue);
+
+	if (!skb)
+		return NULL;
+	if (!(cxgbi_skcb_test_flag(skb, SKCBF_RX_LRO)) &&
+	    !(cxgbi_skcb_test_flag(skb, SKCBF_RX_STATUS))) {
+		log_debug(1 << CXGBI_DBG_PDU_RX,
+			  "skb 0x%p, NOT ready 0x%lx.\n",
+			  skb, cxgbi_skcb_flags(skb));
+		return NULL;
+	}
+	__skb_unlink(skb, &csk->receive_queue);
+	return skb;
+}
+
+void cxgbi_lro_skb_dump(struct sk_buff *skb)
+{
+	struct skb_shared_info *ssi = skb_shinfo(skb);
+	struct cxgbi_rx_lro_cb *lro_cb = cxgbi_skb_rx_lro_cb(skb);
+	struct cxgbi_rx_pdu_cb *pdu_cb = cxgbi_skb_rx_pdu_cb(skb, 0);
+	int i;
+
+	pr_info("skb 0x%p, head 0x%p, 0x%p, len %u,%u, frags %u.\n",
+		skb, skb->head, skb->data, skb->len, skb->data_len,
+		ssi->nr_frags);
+	pr_info("skb 0x%p, lro_cb, csk 0x%p, pdu %u, %u.\n",
+		skb, lro_cb->csk, lro_cb->pdu_cnt, lro_cb->pdu_totallen);
+
+	for (i = 0; i < lro_cb->pdu_cnt; i++, pdu_cb++)
+		pr_info("0x%p, pdu %d,%u,0x%lx,seq 0x%x,dcrc 0x%x,frags %u.\n",
+			skb, i, pdu_cb->pdulen, pdu_cb->flags, pdu_cb->seq,
+			pdu_cb->ddigest, pdu_cb->frags);
+	for (i = 0; i < ssi->nr_frags; i++)
+		pr_info("skb 0x%p, frag %d, off %u, sz %u.\n",
+			skb, i, ssi->frags[i].page_offset, ssi->frags[i].size);
+}
+EXPORT_SYMBOL_GPL(cxgbi_lro_skb_dump);
+
+static void skb_lro_hold_done(struct cxgbi_sock *csk)
+{
+	struct sk_buff *skb = csk->skb_lro_hold;
+	struct cxgbi_rx_lro_cb *lro_cb = cxgbi_skb_rx_lro_cb(skb);
+
+	if (lro_cb->flags & CXGBI_LRO_CB_USED) {
+		struct skb_shared_info *ssi = skb_shinfo(skb);
+		int i;
+
+		memset(skb->data, 0, LRO_SKB_MIN_HEADROOM);
+		for (i = 0; i < ssi->nr_frags; i++)
+			put_page(skb_frag_page(&ssi->frags[i]));
+		ssi->nr_frags = 0;
+	}
+}
+
+static void skb_lro_hold_merge(struct cxgbi_sock *csk, struct sk_buff *skb,
+			       int pdu_idx)
+{
+	struct sk_buff *hskb = csk->skb_lro_hold;
+	struct skb_shared_info *hssi = skb_shinfo(hskb);
+	struct cxgbi_rx_lro_cb *hlro_cb = cxgbi_skb_rx_lro_cb(hskb);
+	struct cxgbi_rx_pdu_cb *hpdu_cb = cxgbi_skb_rx_pdu_cb(hskb, 0);
+	struct skb_shared_info *ssi = skb_shinfo(skb);
+	struct cxgbi_rx_pdu_cb *pdu_cb = cxgbi_skb_rx_pdu_cb(skb, pdu_idx);
+	int frag_idx = 0;
+	int hfrag_idx = 0;
+
+	/* the partial pdu is either 1st or last */
+	if (pdu_idx)
+		frag_idx = ssi->nr_frags - pdu_cb->frags;
+
+	if (cxgbi_rx_cb_test_flag(pdu_cb, SKCBF_RX_HDR)) {
+		unsigned int len = 0;
+
+		skb_lro_hold_done(csk);
+
+		hlro_cb->csk = csk;
+		hlro_cb->pdu_cnt = 1;
+		hlro_cb->flags = CXGBI_LRO_CB_USED;
+
+		hpdu_cb->flags = pdu_cb->flags;
+		hpdu_cb->seq = pdu_cb->seq;
+
+		memcpy(&hssi->frags[hfrag_idx], &ssi->frags[frag_idx],
+		       sizeof(skb_frag_t));
+		get_page(skb_frag_page(&hssi->frags[hfrag_idx]));
+		frag_idx++;
+		hfrag_idx++;
+		hssi->nr_frags = 1;
+		hpdu_cb->frags = 1;
+
+		len = hssi->frags[0].size;
+		hskb->len = len;
+		hskb->data_len = len;
+		hskb->truesize = len;
+	}
+
+	if (cxgbi_rx_cb_test_flag(pdu_cb, SKCBF_RX_DATA)) {
+		unsigned int len = 0;
+		int i, n;
+
+		hpdu_cb->flags |= pdu_cb->flags;
+
+		for (i = 1, n = hfrag_idx; n < pdu_cb->frags;
+		     i++, frag_idx++, n++) {
+			memcpy(&hssi->frags[i], &ssi->frags[frag_idx],
+			       sizeof(skb_frag_t));
+			get_page(skb_frag_page(&hssi->frags[i]));
+			len += hssi->frags[i].size;
+
+			hssi->nr_frags++;
+			hpdu_cb->frags++;
+		}
+		hskb->len += len;
+		hskb->data_len += len;
+		hskb->truesize += len;
+	}
+
+	if (cxgbi_rx_cb_test_flag(pdu_cb, SKCBF_RX_STATUS)) {
+		hpdu_cb->flags |= pdu_cb->flags;
+		hpdu_cb->ddigest = pdu_cb->ddigest;
+		hpdu_cb->pdulen = pdu_cb->pdulen;
+		hlro_cb->pdu_totallen = pdu_cb->pdulen;
+	}
+}
+
+static int rx_skb_lro_read_pdu(struct sk_buff *skb, struct iscsi_conn *conn,
+			       unsigned int *off_p, int idx)
+{
+	struct cxgbi_rx_pdu_cb *pdu_cb = cxgbi_skb_rx_pdu_cb(skb, idx);
+	unsigned int offset = *off_p;
+	int err = 0;
+
+	err = skb_read_pdu_bhs(conn, skb, offset, &pdu_cb->flags);
+	if (err < 0) {
+		pr_err("conn 0x%p, bhs, skb 0x%p, pdu %d, offset %u.\n",
+		       conn, skb, idx, offset);
+		cxgbi_lro_skb_dump(skb);
+		goto done;
+	}
+	offset += err;
+
+	err = skb_read_pdu_data(conn, skb, skb, offset, &pdu_cb->flags);
+	if (err < 0) {
+		pr_err("%s: conn 0x%p data, skb 0x%p, pdu %d.\n",
+		       __func__, conn, skb, idx);
+		cxgbi_lro_skb_dump(skb);
+		goto done;
+	}
+	offset += err;
+	if (conn->hdrdgst_en)
+		offset += ISCSI_DIGEST_SIZE;
+
+done:
+	*off_p = offset;
+	return err;
+}
+
+static int rx_skb_lro(struct sk_buff *skb, struct cxgbi_device *cdev,
+		      struct cxgbi_sock *csk, struct iscsi_conn *conn,
+		      unsigned int *read)
+{
+	struct sk_buff *hskb = csk->skb_lro_hold;
+	struct cxgbi_rx_lro_cb *lro_cb = cxgbi_skb_rx_lro_cb(skb);
+	struct cxgbi_rx_pdu_cb *pdu_cb = cxgbi_skb_rx_pdu_cb(skb, 0);
+	int last = lro_cb->pdu_cnt - 1;
+	int i = 0;
+	int err = 0;
+	unsigned int offset = 0;
+
+	if (!cxgbi_rx_cb_test_flag(pdu_cb, SKCBF_RX_HDR)) {
+		/* there could be partial pdus spanning 2 lro skbs */
+		skb_lro_hold_merge(csk, skb, 0);
+
+		if (cxgbi_rx_cb_test_flag(pdu_cb, SKCBF_RX_STATUS)) {
+			unsigned int dummy = 0;
+
+			err = rx_skb_lro_read_pdu(hskb, conn, &dummy, 0);
+			if (err < 0)
+				goto done;
+
+			if (pdu_cb->frags) {
+				struct skb_shared_info *ssi = skb_shinfo(skb);
+				int k;
+
+				for (k = 0; k < pdu_cb->frags; k++)
+					offset += ssi->frags[k].size;
+			}
+		}
+		i = 1;
+	}
+
+	for (; i < last; i++, pdu_cb++) {
+		err = rx_skb_lro_read_pdu(skb, conn, &offset, i);
+		if (err < 0)
+			goto done;
+	}
+
+	if (i == last) {
+		pdu_cb = cxgbi_skb_rx_pdu_cb(skb, last);
+		if (!cxgbi_rx_cb_test_flag(pdu_cb, SKCBF_RX_STATUS)) {
+			skb_lro_hold_merge(csk, skb, last);
+		} else {
+			err = rx_skb_lro_read_pdu(skb, conn, &offset, last);
+			if (err < 0)
+				goto done;
+		}
+	}
+
+	*read += lro_cb->pdu_totallen;
+
+done:
+	__kfree_skb(skb);
+	return err;
+}
+
+static int rx_skb_coalesced(struct sk_buff *skb, struct cxgbi_device *cdev,
+			    struct cxgbi_sock *csk, struct iscsi_conn *conn,
+			    unsigned int *read)
+{
+	int err = 0;
+
+	*read += cxgbi_skcb_rx_pdulen(skb);
+
+	err = skb_read_pdu_bhs(conn, skb, 0, &cxgbi_skcb_flags(skb));
+	if (err < 0) {
+		pr_err("bhs, csk 0x%p, skb 0x%p,%u, f 0x%lx, plen %u.\n",
+		       csk, skb, skb->len, cxgbi_skcb_flags(skb),
+		       cxgbi_skcb_rx_pdulen(skb));
+		pr_info("bhs %*ph\n", 48, skb->data);
+		goto done;
+	}
+
+	err = skb_read_pdu_data(conn, skb, skb, err + cdev->skb_rx_extra,
+				&cxgbi_skcb_flags(skb));
+	if (err < 0) {
+		pr_err("data, csk 0x%p, skb 0x%p,%u, f 0x%lx, plen %u.\n",
+		       csk, skb, skb->len, cxgbi_skcb_flags(skb),
+		       cxgbi_skcb_rx_pdulen(skb));
+		pr_info("bhs %*ph\n", 48, skb->data);
+	}
+
+done:
+	__kfree_skb(skb);
+	return err;
+}
+
+static int rx_skb(struct sk_buff *skb, struct cxgbi_device *cdev,
+		  struct cxgbi_sock *csk, struct iscsi_conn *conn,
+		  unsigned int *read)
+{
+	int err = 0;
+
+	*read += cxgbi_skcb_rx_pdulen(skb);
+
+	err = skb_read_pdu_bhs(conn, skb, 0, &cxgbi_skcb_flags(skb));
+	if (err < 0) {
+		pr_err("bhs, csk 0x%p, skb 0x%p,%u, f 0x%lx, plen %u.\n",
+		       csk, skb, skb->len, cxgbi_skcb_flags(skb),
+		       cxgbi_skcb_rx_pdulen(skb));
+		pr_err("bhs %*ph\n", 48, skb->data);
+		goto done;
+	}
+
+	if (cxgbi_skcb_test_flag(skb, SKCBF_RX_DATA)) {
+		struct sk_buff *dskb = skb_peek(&csk->receive_queue);
+
+		if (!dskb) {
+			pr_err("csk 0x%p, NO data.\n", csk);
+			err = -EAGAIN;
+			goto done;
+		}
+		__skb_unlink(dskb, &csk->receive_queue);
+
+		err = skb_read_pdu_data(conn, skb, dskb, 0,
+					&cxgbi_skcb_flags(skb));
+		if (err < 0) {
+			pr_err("data, csk 0x%p, 0x%p,%u, 0x%p,%u,0x%lx,%u.\n",
+			       csk, skb, skb->len, dskb, dskb->len,
+			       cxgbi_skcb_flags(dskb),
+			       cxgbi_skcb_rx_pdulen(dskb));
+			pr_info("bhs %*ph\n", 48, skb->data);
+		}
+		__kfree_skb(dskb);
+	} else
+		err = skb_read_pdu_data(conn, skb, skb, 0,
+					&cxgbi_skcb_flags(skb));
+
+done:
+	__kfree_skb(skb);
+	return err;
+}
+
 void cxgbi_conn_pdu_ready(struct cxgbi_sock *csk)
 {
 	struct cxgbi_device *cdev = csk->cdev;
 	struct iscsi_conn *conn = csk->user_data;
-	struct sk_buff *skb;
 	unsigned int read = 0;
 	int err = 0;
 
@@ -1925,83 +2223,22 @@ void cxgbi_conn_pdu_ready(struct cxgbi_sock *csk)
 	}
 
 	while (!err) {
-		skb = skb_peek(&csk->receive_queue);
-		if (!skb ||
-		    !(cxgbi_skcb_test_flag(skb, SKCBF_RX_STATUS))) {
-			if (skb)
-				log_debug(1 << CXGBI_DBG_PDU_RX,
-					"skb 0x%p, NOT ready 0x%lx.\n",
-					skb, cxgbi_skcb_flags(skb));
+		struct sk_buff *skb = cxgbi_conn_rxq_get_skb(csk);
+
+		if (!skb)
 			break;
-		}
-		__skb_unlink(skb, &csk->receive_queue);
 
-		read += cxgbi_skcb_rx_pdulen(skb);
 		log_debug(1 << CXGBI_DBG_PDU_RX,
 			"csk 0x%p, skb 0x%p,%u,f 0x%lx, pdu len %u.\n",
 			csk, skb, skb->len, cxgbi_skcb_flags(skb),
 			cxgbi_skcb_rx_pdulen(skb));
 
-		if (cxgbi_skcb_test_flag(skb, SKCBF_RX_COALESCED)) {
-			err = skb_read_pdu_bhs(conn, skb);
-			if (err < 0) {
-				pr_err("coalesced bhs, csk 0x%p, skb 0x%p,%u, "
-					"f 0x%lx, plen %u.\n",
-					csk, skb, skb->len,
-					cxgbi_skcb_flags(skb),
-					cxgbi_skcb_rx_pdulen(skb));
-				goto skb_done;
-			}
-			err = skb_read_pdu_data(conn, skb, skb,
-						err + cdev->skb_rx_extra);
-			if (err < 0)
-				pr_err("coalesced data, csk 0x%p, skb 0x%p,%u, "
-					"f 0x%lx, plen %u.\n",
-					csk, skb, skb->len,
-					cxgbi_skcb_flags(skb),
-					cxgbi_skcb_rx_pdulen(skb));
-		} else {
-			err = skb_read_pdu_bhs(conn, skb);
-			if (err < 0) {
-				pr_err("bhs, csk 0x%p, skb 0x%p,%u, "
-					"f 0x%lx, plen %u.\n",
-					csk, skb, skb->len,
-					cxgbi_skcb_flags(skb),
-					cxgbi_skcb_rx_pdulen(skb));
-				goto skb_done;
-			}
-
-			if (cxgbi_skcb_test_flag(skb, SKCBF_RX_DATA)) {
-				struct sk_buff *dskb;
-
-				dskb = skb_peek(&csk->receive_queue);
-				if (!dskb) {
-					pr_err("csk 0x%p, skb 0x%p,%u, f 0x%lx,"
-						" plen %u, NO data.\n",
-						csk, skb, skb->len,
-						cxgbi_skcb_flags(skb),
-						cxgbi_skcb_rx_pdulen(skb));
-					err = -EIO;
-					goto skb_done;
-				}
-				__skb_unlink(dskb, &csk->receive_queue);
-
-				err = skb_read_pdu_data(conn, skb, dskb, 0);
-				if (err < 0)
-					pr_err("data, csk 0x%p, skb 0x%p,%u, "
-						"f 0x%lx, plen %u, dskb 0x%p,"
-						"%u.\n",
-						csk, skb, skb->len,
-						cxgbi_skcb_flags(skb),
-						cxgbi_skcb_rx_pdulen(skb),
-						dskb, dskb->len);
-				__kfree_skb(dskb);
-			} else
-				err = skb_read_pdu_data(conn, skb, skb, 0);
-		}
-skb_done:
-		__kfree_skb(skb);
-
+		if (cxgbi_skcb_test_flag(skb, SKCBF_RX_LRO))
+			err = rx_skb_lro(skb, cdev, csk, conn, &read);
+		else if (cxgbi_skcb_test_flag(skb, SKCBF_RX_COALESCED))
+			err = rx_skb_coalesced(skb, cdev, csk, conn, &read);
+		else
+			err = rx_skb(skb, cdev, csk, conn, &read);
 		if (err < 0)
 			break;
 	}
@@ -2014,7 +2251,7 @@ skb_done:
 	}
 
 	if (err < 0) {
-		pr_info("csk 0x%p, 0x%p, rx failed %d, read %u.\n",
+		pr_info("csk 0x%p,0x%p, rx failed %d, read %u.\n",
 			csk, conn, err, read);
 		iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
 	}
diff --git a/drivers/scsi/cxgbi/libcxgbi.h b/drivers/scsi/cxgbi/libcxgbi.h
index 9842301..13ec7d7 100644
--- a/drivers/scsi/cxgbi/libcxgbi.h
+++ b/drivers/scsi/cxgbi/libcxgbi.h
@@ -208,6 +208,8 @@ struct cxgbi_sock {
 	struct sk_buff *cpl_abort_req;
 	struct sk_buff *cpl_abort_rpl;
 	struct sk_buff *skb_ulp_lhdr;
+	struct sk_buff *skb_lro;	/* accumulated rx so far */
+	struct sk_buff *skb_lro_hold;	/* holds a partial pdu */
 	spinlock_t lock;
 	struct kref refcnt;
 	unsigned int state;
@@ -279,6 +281,7 @@ struct cxgbi_skb_tx_cb {
 
 enum cxgbi_skcb_flags {
 	SKCBF_TX_NEED_HDR,	/* packet needs a header */
+	SKCBF_RX_LRO,		/* received via lro */
 	SKCBF_RX_COALESCED,	/* received whole pdu */
 	SKCBF_RX_HDR,		/* received pdu header */
 	SKCBF_RX_DATA,		/* received pdu payload */
@@ -752,4 +755,5 @@ void cxgbi_ddp_page_size_factor(int *);
 void cxgbi_ddp_ppod_clear(struct cxgbi_pagepod *);
 void cxgbi_ddp_ppod_set(struct cxgbi_pagepod *, struct cxgbi_pagepod_hdr *,
 			struct cxgbi_gather_list *, unsigned int);
+
 #endif	/*__LIBCXGBI_H__*/
-- 
2.3.4


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

* [PATCHv2 net-next 3/3] cxgb4i: add iscsi LRO for chelsio t4/t5 adapters
  2016-01-07  9:24 [PATCHv2 net-next 0/3] cxgb4/cxgb4i: add iscsi LRO support for chelsio t4/t5 adapters Hariprasad Shenai
  2016-01-07  9:24 ` [PATCHv2 net-next 1/3] cxgb4: Add LRO infrastructure for cxgb4i driver Hariprasad Shenai
  2016-01-07  9:24 ` [PATCHv2 net-next 2/3] libcxgbi: add pdu parsing of LRO'ed skbs Hariprasad Shenai
@ 2016-01-07  9:24 ` Hariprasad Shenai
  2016-01-11 20:41 ` [PATCHv2 net-next 0/3] cxgb4/cxgb4i: add iscsi LRO support " Mike Christie
  3 siblings, 0 replies; 6+ messages in thread
From: Hariprasad Shenai @ 2016-01-07  9:24 UTC (permalink / raw)
  To: netdev, linux-scsi
  Cc: davem, JBottomley, martin.petersen, leedom, kxie, manojmalviya,
	nirranjan, Hariprasad Shenai

Signed-off-by: Karen Xie <kxie@chelsio.com>
Signed-off-by: Manoj Malviya <manojmalviya@chelsio.com>
Signed-off-by: Hariprasad Shenai <hariprasad@chelsio.com>
---
 drivers/scsi/cxgbi/cxgb4i/cxgb4i.c | 396 +++++++++++++++++++++++++++++++++++--
 1 file changed, 382 insertions(+), 14 deletions(-)

diff --git a/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c b/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c
index 804806e..68e94e6 100644
--- a/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c
+++ b/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c
@@ -33,6 +33,7 @@
 static unsigned int dbg_level;
 
 #include "../libcxgbi.h"
+#include "../cxgbi_lro.h"
 
 #define	DRV_MODULE_NAME		"cxgb4i"
 #define DRV_MODULE_DESC		"Chelsio T4/T5 iSCSI Driver"
@@ -81,13 +82,6 @@ static int t4_uld_rx_handler(void *, const __be64 *, const struct pkt_gl *);
 static int t4_uld_state_change(void *, enum cxgb4_state state);
 static inline int send_tx_flowc_wr(struct cxgbi_sock *);
 
-static const struct cxgb4_uld_info cxgb4i_uld_info = {
-	.name = DRV_MODULE_NAME,
-	.add = t4_uld_add,
-	.rx_handler = t4_uld_rx_handler,
-	.state_change = t4_uld_state_change,
-};
-
 static struct scsi_host_template cxgb4i_host_template = {
 	.module		= THIS_MODULE,
 	.name		= DRV_MODULE_NAME,
@@ -1182,8 +1176,9 @@ static void do_rx_data_ddp(struct cxgbi_device *cdev,
 	}
 
 	log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_PDU_RX,
-		"csk 0x%p,%u,0x%lx, skb 0x%p,0x%x, lhdr 0x%p.\n",
-		csk, csk->state, csk->flags, skb, status, csk->skb_ulp_lhdr);
+		"csk 0x%p,%u,0x%lx, skb 0x%p,0x%x, lhdr 0x%p, len %u.\n",
+		csk, csk->state, csk->flags, skb, status, csk->skb_ulp_lhdr,
+		ntohs(rpl->len));
 
 	spin_lock_bh(&csk->lock);
 
@@ -1212,13 +1207,13 @@ static void do_rx_data_ddp(struct cxgbi_device *cdev,
 			csk->tid, ntohs(rpl->len), cxgbi_skcb_rx_pdulen(lskb));
 
 	if (status & (1 << CPL_RX_DDP_STATUS_HCRC_SHIFT)) {
-		pr_info("csk 0x%p, lhdr 0x%p, status 0x%x, hcrc bad 0x%lx.\n",
-			csk, lskb, status, cxgbi_skcb_flags(lskb));
+		pr_info("csk 0x%p, lhdr 0x%p, status 0x%x, hcrc bad.\n",
+			csk, lskb, status);
 		cxgbi_skcb_set_flag(lskb, SKCBF_RX_HCRC_ERR);
 	}
 	if (status & (1 << CPL_RX_DDP_STATUS_DCRC_SHIFT)) {
-		pr_info("csk 0x%p, lhdr 0x%p, status 0x%x, dcrc bad 0x%lx.\n",
-			csk, lskb, status, cxgbi_skcb_flags(lskb));
+		pr_info("csk 0x%p, lhdr 0x%p, status 0x%x, dcrc bad.\n",
+			csk, lskb, status);
 		cxgbi_skcb_set_flag(lskb, SKCBF_RX_DCRC_ERR);
 	}
 	if (status & (1 << CPL_RX_DDP_STATUS_PAD_SHIFT)) {
@@ -1311,6 +1306,12 @@ static int alloc_cpls(struct cxgbi_sock *csk)
 					0, GFP_KERNEL);
 	if (!csk->cpl_abort_rpl)
 		goto free_cpls;
+
+	csk->skb_lro_hold = alloc_skb(LRO_SKB_MIN_HEADROOM, GFP_KERNEL);
+	if (unlikely(!csk->skb_lro_hold))
+		goto free_cpls;
+	memset(csk->skb_lro_hold->data, 0, LRO_SKB_MIN_HEADROOM);
+
 	return 0;
 
 free_cpls:
@@ -1539,7 +1540,7 @@ int cxgb4i_ofld_init(struct cxgbi_device *cdev)
 	cdev->csk_alloc_cpls = alloc_cpls;
 	cdev->csk_init_act_open = init_act_open;
 
-	pr_info("cdev 0x%p, offload up, added.\n", cdev);
+	pr_info("cdev 0x%p, offload up, added, f 0x%x.\n", cdev, cdev->flags);
 	return 0;
 }
 
@@ -1891,6 +1892,373 @@ static int t4_uld_state_change(void *handle, enum cxgb4_state state)
 	return 0;
 }
 
+static void proc_ddp_status(unsigned int tid, struct cpl_rx_data_ddp *cpl,
+			    struct cxgbi_rx_pdu_cb *pdu_cb)
+{
+	unsigned int status = ntohl(cpl->ddpvld);
+
+	cxgbi_rx_cb_set_flag(pdu_cb, SKCBF_RX_STATUS);
+
+	pdu_cb->ddigest = ntohl(cpl->ulp_crc);
+	pdu_cb->pdulen = ntohs(cpl->len);
+
+	if (status & (1 << CPL_RX_DDP_STATUS_HCRC_SHIFT)) {
+		pr_info("tid 0x%x, status 0x%x, hcrc bad.\n", tid, status);
+		cxgbi_rx_cb_set_flag(pdu_cb, SKCBF_RX_HCRC_ERR);
+	}
+	if (status & (1 << CPL_RX_DDP_STATUS_DCRC_SHIFT)) {
+		pr_info("tid 0x%x, status 0x%x, dcrc bad.\n", tid, status);
+		cxgbi_rx_cb_set_flag(pdu_cb, SKCBF_RX_DCRC_ERR);
+	}
+	if (status & (1 << CPL_RX_DDP_STATUS_PAD_SHIFT)) {
+		pr_info("tid 0x%x, status 0x%x, pad bad.\n", tid, status);
+		cxgbi_rx_cb_set_flag(pdu_cb, SKCBF_RX_PAD_ERR);
+	}
+	if ((status & (1 << CPL_RX_DDP_STATUS_DDP_SHIFT)) &&
+	    !cxgbi_rx_cb_test_flag(pdu_cb, SKCBF_RX_DATA)) {
+		cxgbi_rx_cb_set_flag(pdu_cb, SKCBF_RX_DATA_DDPD);
+	}
+}
+
+static void lro_skb_add_packet_rsp(struct sk_buff *skb, u8 op,
+				   const __be64 *rsp)
+{
+	struct cxgbi_rx_lro_cb *lro_cb = cxgbi_skb_rx_lro_cb(skb);
+	struct cxgbi_rx_pdu_cb *pdu_cb = cxgbi_skb_rx_pdu_cb(skb,
+						lro_cb->pdu_cnt);
+	struct cpl_rx_data_ddp *cpl = (struct cpl_rx_data_ddp *)(rsp + 1);
+
+	proc_ddp_status(lro_cb->csk->tid, cpl, pdu_cb);
+
+	lro_cb->pdu_totallen += pdu_cb->pdulen;
+	lro_cb->pdu_cnt++;
+}
+
+static void lro_skb_add_packet_gl(struct sk_buff *skb, u8 op,
+				  const struct pkt_gl *gl)
+{
+	struct cxgbi_rx_lro_cb *lro_cb = cxgbi_skb_rx_lro_cb(skb);
+	struct cxgbi_rx_pdu_cb *pdu_cb = cxgbi_skb_rx_pdu_cb(skb,
+						lro_cb->pdu_cnt);
+	struct skb_shared_info *ssi = skb_shinfo(skb);
+	int i = ssi->nr_frags;
+	unsigned int offset;
+	unsigned int len;
+
+	if (op == CPL_ISCSI_HDR) {
+		struct cpl_iscsi_hdr *cpl = (struct cpl_iscsi_hdr *)gl->va;
+
+		offset = sizeof(struct cpl_iscsi_hdr);
+		cxgbi_rx_cb_set_flag(pdu_cb, SKCBF_RX_HDR);
+
+		pdu_cb->seq = ntohl(cpl->seq);
+		len = ntohs(cpl->len);
+	} else {
+		struct cpl_rx_data *cpl = (struct cpl_rx_data *)gl->va;
+
+		offset = sizeof(struct cpl_rx_data);
+		cxgbi_rx_cb_set_flag(pdu_cb, SKCBF_RX_DATA);
+
+		len = ntohs(cpl->len);
+	}
+
+	memcpy(&ssi->frags[i], &gl->frags[0], gl->nfrags * sizeof(skb_frag_t));
+	ssi->frags[i].page_offset += offset;
+	ssi->frags[i].size -= offset;
+	ssi->nr_frags += gl->nfrags;
+	pdu_cb->frags += gl->nfrags;
+
+	skb->len += len;
+	skb->data_len += len;
+	skb->truesize += len;
+
+	for (i = 0; i < gl->nfrags; i++)
+		get_page(gl->frags[i].page);
+}
+
+static inline int cxgbi_sock_check_rx_state(struct cxgbi_sock *csk)
+{
+	if (unlikely(csk->state >= CTP_PASSIVE_CLOSE)) {
+		log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK,
+			  "csk 0x%p,%u,0x%lx,%u, bad state.\n",
+			  csk, csk->state, csk->flags, csk->tid);
+		if (csk->state != CTP_ABORTING)
+			send_abort_req(csk);
+		return -1;
+	}
+	return 0;
+}
+
+static void do_rx_iscsi_lro(struct cxgbi_sock *csk, struct sk_buff *skb)
+{
+	struct cxgbi_rx_lro_cb *lro_cb = cxgbi_skb_rx_lro_cb(skb);
+	struct cxgbi_rx_pdu_cb *pdu_cb = cxgbi_skb_rx_pdu_cb(skb, 0);
+
+	log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_PDU_RX,
+		  "%s: csk 0x%p,%u,0x%lx, tid %u, skb 0x%p,%u, %u.\n",
+		  __func__, csk, csk->state, csk->flags, csk->tid, skb,
+		  skb->len, skb->data_len);
+
+	cxgbi_skcb_set_flag(skb, SKCBF_RX_LRO);
+
+	spin_lock_bh(&csk->lock);
+
+	if (cxgbi_sock_check_rx_state(csk) < 0)
+		goto discard;
+
+	if (cxgbi_rx_cb_test_flag(pdu_cb, SKCBF_RX_HDR) &&
+	    pdu_cb->seq != csk->rcv_nxt) {
+		pr_info("%s, csk 0x%p, tid 0x%x, seq 0x%x != 0x%x.\n",
+			__func__, csk, csk->tid, pdu_cb->seq, csk->rcv_nxt);
+		cxgbi_lro_skb_dump(skb);
+		goto abort_conn;
+	}
+
+	/* partial pdus */
+	if (!lro_cb->pdu_cnt) {
+		lro_cb->pdu_cnt = 1;
+	} else {
+		struct cxgbi_rx_pdu_cb *pdu_cb = cxgbi_skb_rx_pdu_cb(skb,
+							lro_cb->pdu_cnt);
+
+		if (!(cxgbi_rx_cb_test_flag(pdu_cb, SKCBF_RX_STATUS)) &&
+		    pdu_cb->frags)
+			lro_cb->pdu_cnt++;
+	}
+
+	csk->rcv_nxt += lro_cb->pdu_totallen;
+
+	skb_reset_transport_header(skb);
+	__skb_queue_tail(&csk->receive_queue, skb);
+
+	cxgbi_conn_pdu_ready(csk);
+	spin_unlock_bh(&csk->lock);
+
+	return;
+
+abort_conn:
+	send_abort_req(csk);
+discard:
+	spin_unlock_bh(&csk->lock);
+	__kfree_skb(skb);
+}
+
+static struct sk_buff *lro_init_skb(struct cxgbi_sock *csk, u8 op,
+				    const struct pkt_gl *gl, const __be64 *rsp)
+{
+	struct sk_buff *skb;
+	struct cxgbi_rx_lro_cb *lro_cb;
+
+	skb = alloc_skb(LRO_SKB_MAX_HEADROOM, GFP_ATOMIC);
+	if (unlikely(!skb))
+		return NULL;
+	memset(skb->data, 0, LRO_SKB_MAX_HEADROOM);
+
+	lro_cb = cxgbi_skb_rx_lro_cb(skb);
+	cxgbi_sock_get(csk);
+	lro_cb->csk = csk;
+
+	return skb;
+}
+
+static void lro_flush(struct t4_lro_mgr *lro_mgr, struct sk_buff *skb)
+{
+	struct cxgbi_rx_lro_cb *lro_cb = cxgbi_skb_rx_lro_cb(skb);
+	struct cxgbi_sock *csk = lro_cb->csk;
+
+	 /* Update head pointer */
+	if (skb == lro_mgr->skb_head) {
+		if (!skb->next) {
+			/* Only skb in the list */
+			lro_mgr->skb_head = NULL;
+			lro_mgr->skb_tail = NULL;
+		} else {
+			lro_mgr->skb_head = skb->next;
+		}
+	} else if (skb == lro_mgr->skb_tail) {
+		/* Update tail pointer */
+		skb->prev->next = NULL;
+		lro_mgr->skb_tail = skb->prev;
+	} else {
+		skb->prev->next = skb->next;
+		skb->next->prev = skb->prev;
+	}
+
+	csk->skb_lro = NULL;
+	do_rx_iscsi_lro(csk, skb);
+	cxgbi_sock_put(csk);
+
+	lro_mgr->lro_pkts++;
+	lro_mgr->lro_session_cnt--;
+}
+
+static int lro_receive(struct cxgbi_sock *csk, u8 op, const __be64 *rsp,
+		       const struct pkt_gl *gl, struct t4_lro_mgr *lro_mgr)
+{
+	struct sk_buff *skb;
+	struct cxgbi_rx_lro_cb *lro_cb;
+	struct cxgb4_lld_info *lldi = cxgbi_cdev_priv(csk->cdev);
+
+	if (!is_t5(lldi->adapter_type))
+		return -EOPNOTSUPP;
+
+	if (!csk) {
+		pr_err("%s: csk NULL, op 0x%x.\n", __func__, op);
+		goto out;
+	}
+
+	if (csk->skb_lro)
+		goto add_packet;
+
+start_lro:
+	/* Did we reach the hash size limit */
+	if (lro_mgr->lro_session_cnt >= MAX_LRO_SESSIONS)
+		goto out;
+
+	skb = lro_init_skb(csk, op, gl, rsp);
+	csk->skb_lro = skb;
+	if (unlikely(!skb))
+		goto out;
+	lro_mgr->lro_session_cnt++;
+
+	if (lro_mgr->skb_tail) {
+		lro_mgr->skb_tail->next = skb;
+		skb->prev = lro_mgr->skb_tail;
+	} else {
+		lro_mgr->skb_head = skb;
+	}
+	lro_mgr->skb_tail = skb;
+
+	/* continue to add the packet */
+add_packet:
+	skb = csk->skb_lro;
+	lro_cb = cxgbi_skb_rx_lro_cb(skb);
+
+	/* Check if this packet can be aggregated */
+	if ((gl && ((skb_shinfo(skb)->nr_frags + gl->nfrags) >= MAX_SKB_FRAGS ||
+		    lro_cb->pdu_totallen >= LRO_FLUSH_TOTALLEN_MAX)) ||
+	    /* lro_cb->pdu_cnt must be less than MAX_SKB_FRAGS */
+	    lro_cb->pdu_cnt >= (MAX_SKB_FRAGS - 1)) {
+		lro_flush(lro_mgr, skb);
+		goto start_lro;
+	}
+
+	if (gl)
+		lro_skb_add_packet_gl(skb, op, gl);
+	else
+		lro_skb_add_packet_rsp(skb, op, rsp);
+	lro_mgr->lro_merged++;
+
+	return 0;
+
+out:
+	return -1;
+}
+
+static int t4_uld_rx_lro_handler(void *hndl, const __be64 *rsp,
+				 const struct pkt_gl *gl,
+				 struct t4_lro_mgr *lro_mgr,
+				unsigned int napi_id)
+{
+	struct cxgbi_device *cdev = hndl;
+	struct cxgb4_lld_info *lldi = cxgbi_cdev_priv(cdev);
+	struct cpl_act_establish *rpl = NULL;
+	struct cxgbi_sock *csk = NULL;
+	unsigned int tid = 0;
+	struct sk_buff *skb;
+	unsigned int op = *(u8 *)rsp;
+
+	if (lro_mgr && (op != CPL_FW6_MSG) && (op != CPL_RX_PKT) &&
+	    (op != CPL_ACT_OPEN_RPL)) {
+		/* Get the TID of this connection */
+		rpl = gl ? (struct cpl_act_establish *)gl->va :
+				(struct cpl_act_establish *)(rsp + 1);
+		tid = GET_TID(rpl);
+		csk = lookup_tid(lldi->tids, tid);
+	}
+
+	/*
+	 * Flush the LROed skb on receiving any cpl other than FW4_ACK and
+	 * CPL_ISCSI_XXX
+	 */
+	if (csk && csk->skb_lro && op != CPL_FW6_MSG && op != CPL_ISCSI_HDR &&
+	    op != CPL_ISCSI_DATA && op != CPL_RX_ISCSI_DDP) {
+		lro_flush(lro_mgr, csk->skb_lro);
+	}
+
+	if (!gl) {
+		unsigned int len;
+
+		if (op == CPL_RX_ISCSI_DDP) {
+			if (!lro_receive(csk, op, rsp, NULL, lro_mgr))
+				return 0;
+		}
+
+		len = 64 - sizeof(struct rsp_ctrl) - 8;
+		skb = alloc_wr(len, 0, GFP_ATOMIC);
+		if (!skb)
+			goto nomem;
+		skb_copy_to_linear_data(skb, &rsp[1], len);
+	} else {
+		if (unlikely(op != *(u8 *)gl->va)) {
+			pr_info("? FL 0x%p,RSS%#llx,FL %#llx,len %u.\n",
+				gl->va, be64_to_cpu(*rsp),
+				be64_to_cpu(*(u64 *)gl->va),
+				gl->tot_len);
+			return 0;
+		}
+
+		if (op == CPL_ISCSI_HDR || op == CPL_ISCSI_DATA) {
+			if (!lro_receive(csk, op, rsp, gl, lro_mgr))
+				return 0;
+		}
+
+		skb = cxgb4_pktgl_to_skb(gl, RX_PULL_LEN, RX_PULL_LEN);
+		if (unlikely(!skb))
+			goto nomem;
+	}
+
+	rpl = (struct cpl_act_establish *)skb->data;
+	op = rpl->ot.opcode;
+	log_debug(1 << CXGBI_DBG_TOE,
+		  "cdev %p, opcode 0x%x(0x%x,0x%x), skb %p.\n",
+		  cdev, op, rpl->ot.opcode_tid, ntohl(rpl->ot.opcode_tid), skb);
+
+	if (op < NUM_CPL_CMDS && cxgb4i_cplhandlers[op]) {
+		cxgb4i_cplhandlers[op](cdev, skb);
+	} else {
+		pr_err("No handler for opcode 0x%x.\n", op);
+		__kfree_skb(skb);
+	}
+	return 0;
+nomem:
+	log_debug(1 << CXGBI_DBG_TOE, "OOM bailing out.\n");
+	return 1;
+}
+
+static void t4_uld_lro_flush_all(struct t4_lro_mgr *lro_mgr)
+{
+	struct sk_buff *skb = lro_mgr->skb_head;
+	struct sk_buff *temp;
+
+	while (skb) {
+		temp = skb->next;
+		lro_flush(lro_mgr, skb);
+		skb = temp;
+	}
+	lro_mgr->skb_head = NULL;
+	lro_mgr->skb_tail = NULL;
+}
+
+static const struct cxgb4_uld_info cxgb4i_uld_info = {
+	.name = DRV_MODULE_NAME,
+	.add = t4_uld_add,
+	.rx_handler = t4_uld_rx_handler,
+	.state_change = t4_uld_state_change,
+	.lro_rx_handler = t4_uld_rx_lro_handler,
+	.lro_flush = t4_uld_lro_flush_all,
+};
+
 static int __init cxgb4i_init_module(void)
 {
 	int rc;
-- 
2.3.4

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

* Re: [PATCHv2 net-next 1/3] cxgb4: Add LRO infrastructure for cxgb4i driver
  2016-01-07  9:24 ` [PATCHv2 net-next 1/3] cxgb4: Add LRO infrastructure for cxgb4i driver Hariprasad Shenai
@ 2016-01-09  2:53   ` David Miller
  0 siblings, 0 replies; 6+ messages in thread
From: David Miller @ 2016-01-09  2:53 UTC (permalink / raw)
  To: hariprasad
  Cc: netdev, linux-scsi, JBottomley, martin.petersen, leedom, kxie,
	manojmalviya, nirranjan

From: Hariprasad Shenai <hariprasad@chelsio.com>
Date: Thu,  7 Jan 2016 14:54:05 +0530

> +	struct sk_buff *skb_head, *skb_tail; /* list of aggregated sessions */

Don't invent your own list scheme, use struct sk_buff_head and
the appropriate initializers and helpers.

If people do their own list handling, then it's extremely difficult
to change the skb queue implementation.

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

* Re: [PATCHv2 net-next 0/3] cxgb4/cxgb4i: add iscsi LRO  support for chelsio t4/t5 adapters
  2016-01-07  9:24 [PATCHv2 net-next 0/3] cxgb4/cxgb4i: add iscsi LRO support for chelsio t4/t5 adapters Hariprasad Shenai
                   ` (2 preceding siblings ...)
  2016-01-07  9:24 ` [PATCHv2 net-next 3/3] cxgb4i: add iscsi LRO for chelsio t4/t5 adapters Hariprasad Shenai
@ 2016-01-11 20:41 ` Mike Christie
  3 siblings, 0 replies; 6+ messages in thread
From: Mike Christie @ 2016-01-11 20:41 UTC (permalink / raw)
  To: Hariprasad Shenai, netdev, linux-scsi
  Cc: davem, JBottomley, martin.petersen, leedom, kxie, manojmalviya,
	nirranjan

On 01/07/2016 03:24 AM, Hariprasad Shenai wrote:
> Hi
> 
> This patch series adds LRO iscsi support for chelsio T4/T5 adapters.
> 
> This patch series has been created against net-next tree and includes
> patches on cxgb4 and cxgb4i driver.
> 
> We have included all the maintainers of respective drivers. Kindly review
> the change and let us know in case of any review comments.
> 
> Thanks
> 
> V2:
>  Missed a new header file cxgbi_lro.h in PATCH 2/3. Adding it.
>  Based on failure reported by by kbuild test bot <lkp@intel.com>
> 
> Hariprasad Shenai (3):
>   cxgb4: Add LRO infrastructure for cxgb4i driver
>   libcxgbi: add pdu parsing of LRO'ed skbs
>   cxgb4i: add iscsi LRO for chelsio t4/t5 adapters
> 
>  drivers/net/ethernet/chelsio/cxgb4/cxgb4.h      |  15 +-
>  drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c |  40 ++-
>  drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h  |   5 +
>  drivers/net/ethernet/chelsio/cxgb4/sge.c        |  10 +-
>  drivers/scsi/cxgbi/cxgb4i/cxgb4i.c              | 396 +++++++++++++++++++++-
>  drivers/scsi/cxgbi/cxgbi_lro.h                  |  72 ++++
>  drivers/scsi/cxgbi/libcxgbi.c                   | 415 +++++++++++++++++++-----
>  drivers/scsi/cxgbi/libcxgbi.h                   |   4 +
>  8 files changed, 840 insertions(+), 117 deletions(-)
>  create mode 100644 drivers/scsi/cxgbi/cxgbi_lro.h
> 

For the iscsi/scsi parts:

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

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

end of thread, other threads:[~2016-01-11 20:41 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-01-07  9:24 [PATCHv2 net-next 0/3] cxgb4/cxgb4i: add iscsi LRO support for chelsio t4/t5 adapters Hariprasad Shenai
2016-01-07  9:24 ` [PATCHv2 net-next 1/3] cxgb4: Add LRO infrastructure for cxgb4i driver Hariprasad Shenai
2016-01-09  2:53   ` David Miller
2016-01-07  9:24 ` [PATCHv2 net-next 2/3] libcxgbi: add pdu parsing of LRO'ed skbs Hariprasad Shenai
2016-01-07  9:24 ` [PATCHv2 net-next 3/3] cxgb4i: add iscsi LRO for chelsio t4/t5 adapters Hariprasad Shenai
2016-01-11 20:41 ` [PATCHv2 net-next 0/3] cxgb4/cxgb4i: add iscsi LRO support " Mike Christie

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.