[ofa-general] [PATCH 9/16 v3] IB/ipoib: Add LSO support to ipoib
Eli Cohen
eli at dev.mellanox.co.il
Tue Jan 29 08:17:43 PST 2008
Add LSO support to ipoib
Signed-off-by: Eli Cohen <eli at mellnaox.co.il>
---
changes:
create flags require TSO only if device supports that.
drivers/infiniband/ulp/ipoib/ipoib.h | 54 ++++++++++++-------
drivers/infiniband/ulp/ipoib/ipoib_cm.c | 7 ++-
drivers/infiniband/ulp/ipoib/ipoib_ib.c | 80 +++++++++++++++++++++------
drivers/infiniband/ulp/ipoib/ipoib_main.c | 8 +++-
drivers/infiniband/ulp/ipoib/ipoib_verbs.c | 5 ++-
5 files changed, 113 insertions(+), 41 deletions(-)
diff --git a/drivers/infiniband/ulp/ipoib/ipoib.h b/drivers/infiniband/ulp/ipoib/ipoib.h
index d13e481..70f8b5c 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib.h
+++ b/drivers/infiniband/ulp/ipoib/ipoib.h
@@ -152,31 +152,40 @@ static inline int ipoib_dma_map_tx(struct ib_device *ca,
{
struct sk_buff *skb = tx_req->skb;
u64 *mapping = tx_req->mapping;
- int frags;
int i;
-
- mapping[0] = ib_dma_map_single(ca, skb->data, skb_headlen(skb),
- DMA_TO_DEVICE);
- if (unlikely(ib_dma_mapping_error(ca, mapping[0])))
- return -EIO;
-
- frags = skb_shinfo(skb)->nr_frags;
- for (i = 0; i < frags; ++i) {
+ int nfrags;
+ int off;
+
+ if (skb_headlen(skb)) {
+ mapping[0] = ib_dma_map_single(ca, skb->data, skb_headlen(skb),
+ DMA_TO_DEVICE);
+ if (unlikely(ib_dma_mapping_error(ca, mapping[0])))
+ return -EIO;
+ off = 1;
+ } else
+ off = 0;
+
+ nfrags = skb_shinfo(skb)->nr_frags;
+ for (i = 0; i < nfrags; ++i) {
skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
- mapping[i + 1] = ib_dma_map_page(ca, frag->page,
- frag->page_offset, frag->size,
- DMA_TO_DEVICE);
- if (unlikely(ib_dma_mapping_error(ca, mapping[i + 1])))
+ mapping[i + off] = ib_dma_map_page(ca, frag->page, frag->page_offset,
+ frag->size, DMA_TO_DEVICE);
+ if (unlikely(ib_dma_mapping_error(ca, mapping[i + off])))
goto partial_error;
}
return 0;
partial_error:
- ib_dma_unmap_single(ca, mapping[0], skb_headlen(skb), DMA_TO_DEVICE);
+ if (skb_headlen(skb)) {
+ ib_dma_unmap_single(ca, mapping[0], skb_headlen(skb), DMA_TO_DEVICE);
+ off = 0;
+ } else
+ off = 1;
for (; i > 0; --i) {
skb_frag_t *frag = &skb_shinfo(skb)->frags[i - 1];
- ib_dma_unmap_page(ca, mapping[i], frag->size, DMA_TO_DEVICE);
+ ib_dma_unmap_page(ca, mapping[i - off], frag->size,
+ DMA_TO_DEVICE);
}
return -EIO;
}
@@ -186,15 +195,20 @@ static inline void ipoib_dma_unmap_tx(struct ib_device *ca,
{
struct sk_buff *skb = tx_req->skb;
u64 *mapping = tx_req->mapping;
- int frags;
int i;
+ int nfrags;
+ int off;
- ib_dma_unmap_single(ca, mapping[0], skb_headlen(skb), DMA_TO_DEVICE);
+ if (skb_headlen(skb)) {
+ ib_dma_unmap_single(ca, mapping[0], skb_headlen(skb), DMA_TO_DEVICE);
+ off = 1;
+ } else
+ off = 0;
- frags = skb_shinfo(skb)->nr_frags;
- for (i = 0; i < frags; ++i) {
+ nfrags = skb_shinfo(skb)->nr_frags;
+ for (i = 0; i < nfrags; ++i) {
skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
- ib_dma_unmap_page(ca, mapping[i + 1], frag->size,
+ ib_dma_unmap_page(ca, mapping[i + off], frag->size,
DMA_TO_DEVICE);
}
}
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_cm.c b/drivers/infiniband/ulp/ipoib/ipoib_cm.c
index e94ec0a..4f5604d 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_cm.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_cm.c
@@ -1379,7 +1379,7 @@ static ssize_t set_mode(struct device *d, struct device_attribute *attr,
ipoib_warn(priv, "enabling connected mode "
"will cause multicast packet drops\n");
- dev->features &= ~(NETIF_F_IP_CSUM | NETIF_F_SG);
+ dev->features &= ~(NETIF_F_IP_CSUM | NETIF_F_SG | NETIF_F_TSO);
ipoib_flush_paths(dev);
return count;
@@ -1393,6 +1393,11 @@ static ssize_t set_mode(struct device *d, struct device_attribute *attr,
if (priv->ca->flags & IB_DEVICE_IP_CSUM)
dev->features |= NETIF_F_IP_CSUM | NETIF_F_SG;
+
+ if (priv->dev->features & NETIF_F_SG &&
+ priv->ca->flags & IB_DEVICE_TCP_TSO)
+ priv->dev->features |= NETIF_F_TSO;
+
return count;
}
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_ib.c b/drivers/infiniband/ulp/ipoib/ipoib_ib.c
index 0f616f6..c3af51b 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_ib.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_ib.c
@@ -38,6 +38,7 @@
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/ip.h>
+#include <linux/tcp.h>
#include <rdma/ib_cache.h>
@@ -346,24 +347,40 @@ void ipoib_ib_completion(struct ib_cq *cq, void *dev_ptr)
static inline int post_send(struct ipoib_dev_priv *priv,
unsigned int wr_id,
struct ib_ah *address, u32 qpn,
- u64 *mapping, int headlen,
- skb_frag_t *frags,
- int nr_frags)
+ struct ipoib_tx_buf *tx_req,
+ void *head, int hlen)
{
struct ib_send_wr *bad_wr;
- int i;
+ int i, off;
+ struct sk_buff *skb = tx_req->skb;
+ skb_frag_t *frags = skb_shinfo(skb)->frags;
+ int nr_frags = skb_shinfo(skb)->nr_frags;
+ u64 *mapping = tx_req->mapping;
+
+ if (skb_headlen(skb)) {
+ priv->tx_sge[0].addr = mapping[0];
+ priv->tx_sge[0].length = skb_headlen(skb);
+ off = 1;
+ } else
+ off = 0;
- priv->tx_sge[0].addr = mapping[0];
- priv->tx_sge[0].length = headlen;
for (i = 0; i < nr_frags; ++i) {
- priv->tx_sge[i + 1].addr = mapping[i + 1];
- priv->tx_sge[i + 1].length = frags[i].size;
+ priv->tx_sge[i + off].addr = mapping[i + off];
+ priv->tx_sge[i + off].length = frags[i].size;
}
- priv->tx_wr.num_sge = nr_frags + 1;
+ priv->tx_wr.num_sge = nr_frags + off;
priv->tx_wr.wr_id = wr_id;
priv->tx_wr.wr.ud.remote_qpn = qpn;
priv->tx_wr.wr.ud.ah = address;
+ if (head) {
+ priv->tx_wr.wr.ud.mss = skb_shinfo(skb)->gso_size;
+ priv->tx_wr.wr.ud.header = head;
+ priv->tx_wr.wr.ud.hlen = hlen;
+ priv->tx_wr.opcode = IB_WR_LSO;
+ } else
+ priv->tx_wr.opcode = IB_WR_SEND;
+
return ib_post_send(priv->qp, &priv->tx_wr, &bad_wr);
}
@@ -372,14 +389,36 @@ void ipoib_send(struct net_device *dev, struct sk_buff *skb,
{
struct ipoib_dev_priv *priv = netdev_priv(dev);
struct ipoib_tx_buf *tx_req;
+ int hlen;
+ void *phead;
+
+ if (!skb_is_gso(skb)) {
+ if (unlikely(skb->len > priv->mcast_mtu + IPOIB_ENCAP_LEN)) {
+ ipoib_warn(priv, "packet len %d (> %d) too long to send, dropping\n",
+ skb->len, priv->mcast_mtu + IPOIB_ENCAP_LEN);
+ ++dev->stats.tx_dropped;
+ ++dev->stats.tx_errors;
+ ipoib_cm_skb_too_long(dev, skb, priv->mcast_mtu);
+ return;
+ }
+ phead = 0;
+ hlen = 0;
+ } else {
+ /*
+ * LSO header is limited to max 60 bytes
+ */
+ if (unlikely((ip_hdr(skb)->ihl + tcp_hdr(skb)->doff) > 15)) {
+ ipoib_warn(priv, "ip(%d) and tcp(%d) headers too long, dropping skb\n",
+ ip_hdr(skb)->ihl << 2, tcp_hdr(skb)->doff << 2);
+ goto drop;
+ }
- if (unlikely(skb->len > priv->mcast_mtu + IPOIB_ENCAP_LEN)) {
- ipoib_warn(priv, "packet len %d (> %d) too long to send, dropping\n",
- skb->len, priv->mcast_mtu + IPOIB_ENCAP_LEN);
- ++dev->stats.tx_dropped;
- ++dev->stats.tx_errors;
- ipoib_cm_skb_too_long(dev, skb, priv->mcast_mtu);
- return;
+ hlen = ((ip_hdr(skb)->ihl + tcp_hdr(skb)->doff) << 2) + IPOIB_ENCAP_LEN;
+ phead = skb->data;
+ if (unlikely(!skb_pull(skb, hlen))) {
+ ipoib_warn(priv, "linear data too small\n");
+ goto drop;
+ }
}
ipoib_dbg_data(priv, "sending packet, length=%d address=%p qpn=0x%06x\n",
@@ -408,8 +447,7 @@ void ipoib_send(struct net_device *dev, struct sk_buff *skb,
if (unlikely(post_send(priv, priv->tx_head & (ipoib_sendq_size - 1),
address->ah, qpn,
- tx_req->mapping, skb_headlen(skb),
- skb_shinfo(skb)->frags, skb_shinfo(skb)->nr_frags))) {
+ tx_req, phead, hlen))) {
ipoib_warn(priv, "post_send failed\n");
++dev->stats.tx_errors;
ipoib_dma_unmap_tx(priv->ca, tx_req);
@@ -425,6 +463,12 @@ void ipoib_send(struct net_device *dev, struct sk_buff *skb,
netif_stop_queue(dev);
}
}
+ return;
+
+drop:
+ ++dev->stats.tx_errors;
+ dev_kfree_skb_any(skb);
+ return;
}
static void __ipoib_reap_ah(struct net_device *dev)
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c
index 83f8b85..9063f28 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_main.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c
@@ -706,7 +706,9 @@ static int ipoib_start_xmit(struct sk_buff *skb, struct net_device *dev)
goto out;
}
- ipoib_send(dev, skb, neigh->ah, IPOIB_QPN(skb->dst->neighbour->ha));
+ ipoib_send(dev, skb, neigh->ah,
+ IPOIB_QPN(skb->dst->neighbour->ha));
+
goto out;
}
@@ -1170,6 +1172,10 @@ static struct net_device *ipoib_add_port(const char *format,
goto event_failed;
}
+ if (priv->dev->features & NETIF_F_SG && priv->ca->flags & IB_DEVICE_TCP_TSO)
+ priv->dev->features |= NETIF_F_TSO;
+
+
result = register_netdev(priv->dev);
if (result) {
printk(KERN_WARNING "%s: couldn't register ipoib port %d; error %d\n",
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_verbs.c b/drivers/infiniband/ulp/ipoib/ipoib_verbs.c
index 5e392e0..e20f2af 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_verbs.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_verbs.c
@@ -153,7 +153,7 @@ int ipoib_transport_dev_init(struct net_device *dev, struct ib_device *ca)
.max_recv_sge = 1
},
.sq_sig_type = IB_SIGNAL_ALL_WR,
- .qp_type = IB_QPT_UD
+ .qp_type = IB_QPT_UD,
};
int i, ret, size;
@@ -191,6 +191,9 @@ int ipoib_transport_dev_init(struct net_device *dev, struct ib_device *ca)
init_attr.send_cq = priv->cq;
init_attr.recv_cq = priv->cq;
+ if (ca->flags & IB_DEVICE_TCP_TSO)
+ init_attr.create_flags = QP_CREATE_LSO;
+
priv->qp = ib_create_qp(priv->pd, &init_attr);
if (IS_ERR(priv->qp)) {
printk(KERN_WARNING "%s: failed to create QP\n", ca->name);
--
1.5.3.8
More information about the general
mailing list