diff options
author | Hans Westgaard Ry <hans.westgaard.ry@oracle.com> | 2016-03-02 07:44:28 -0500 |
---|---|---|
committer | Doug Ledford <dledford@redhat.com> | 2016-03-03 09:49:44 -0500 |
commit | 78a50a5e6068955494117b37b03379dacaf830b7 (patch) | |
tree | f3d16185b8e004c08c63aa37ff78fb0ddefe1457 | |
parent | fc77dbd34c5c99bce46d40a2491937c3bcbd10af (diff) |
IB/ipoib: Add handling for sending of skb with many frags
IPoIB converts skb-fragments to sge adding 1 extra sge when SG is enabled.
Current codepath assumes that the max number of sge a device support
is at least MAX_SKB_FRAGS+1, there is no interaction with upper layers
to limit number of fragments in an skb if a device suports fewer
sges. The assumptions also lead to requesting a fixed number of sge
when IPoIB creates queue-pairs with SG enabled.
A fallback/slowpath is implemented using skb_linearize to
handle cases where the conversion would result in more sges than supported.
Signed-off-by: Hans Westgaard Ry <hans.westgaard.ry@oracle.com>
Reviewed-by: HÃ¥kon Bugge <haakon.bugge@oracle.com>
Reviewed-by: Wei Lin Guay <wei.lin.guay@oracle.com>
Reviewed-by: Yuval Shaia <yuval.shaia@oracle.com>
Signed-off-by: Doug Ledford <dledford@redhat.com>
-rw-r--r-- | drivers/infiniband/ulp/ipoib/ipoib.h | 2 | ||||
-rw-r--r-- | drivers/infiniband/ulp/ipoib/ipoib_cm.c | 23 | ||||
-rw-r--r-- | drivers/infiniband/ulp/ipoib/ipoib_ib.c | 18 | ||||
-rw-r--r-- | drivers/infiniband/ulp/ipoib/ipoib_verbs.c | 5 |
4 files changed, 45 insertions, 3 deletions
diff --git a/drivers/infiniband/ulp/ipoib/ipoib.h b/drivers/infiniband/ulp/ipoib/ipoib.h index a6f3eab0f350..85be0de3ab26 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib.h +++ b/drivers/infiniband/ulp/ipoib/ipoib.h | |||
@@ -244,6 +244,7 @@ struct ipoib_cm_tx { | |||
244 | unsigned tx_tail; | 244 | unsigned tx_tail; |
245 | unsigned long flags; | 245 | unsigned long flags; |
246 | u32 mtu; | 246 | u32 mtu; |
247 | unsigned max_send_sge; | ||
247 | }; | 248 | }; |
248 | 249 | ||
249 | struct ipoib_cm_rx_buf { | 250 | struct ipoib_cm_rx_buf { |
@@ -390,6 +391,7 @@ struct ipoib_dev_priv { | |||
390 | int hca_caps; | 391 | int hca_caps; |
391 | struct ipoib_ethtool_st ethtool; | 392 | struct ipoib_ethtool_st ethtool; |
392 | struct timer_list poll_timer; | 393 | struct timer_list poll_timer; |
394 | unsigned max_send_sge; | ||
393 | }; | 395 | }; |
394 | 396 | ||
395 | struct ipoib_ah { | 397 | struct ipoib_ah { |
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_cm.c b/drivers/infiniband/ulp/ipoib/ipoib_cm.c index 917e46ea3bf6..c8ed53562c9b 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_cm.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_cm.c | |||
@@ -710,6 +710,7 @@ void ipoib_cm_send(struct net_device *dev, struct sk_buff *skb, struct ipoib_cm_ | |||
710 | struct ipoib_dev_priv *priv = netdev_priv(dev); | 710 | struct ipoib_dev_priv *priv = netdev_priv(dev); |
711 | struct ipoib_tx_buf *tx_req; | 711 | struct ipoib_tx_buf *tx_req; |
712 | int rc; | 712 | int rc; |
713 | unsigned usable_sge = tx->max_send_sge - !!skb_headlen(skb); | ||
713 | 714 | ||
714 | if (unlikely(skb->len > tx->mtu)) { | 715 | if (unlikely(skb->len > tx->mtu)) { |
715 | ipoib_warn(priv, "packet len %d (> %d) too long to send, dropping\n", | 716 | ipoib_warn(priv, "packet len %d (> %d) too long to send, dropping\n", |
@@ -719,7 +720,23 @@ void ipoib_cm_send(struct net_device *dev, struct sk_buff *skb, struct ipoib_cm_ | |||
719 | ipoib_cm_skb_too_long(dev, skb, tx->mtu - IPOIB_ENCAP_LEN); | 720 | ipoib_cm_skb_too_long(dev, skb, tx->mtu - IPOIB_ENCAP_LEN); |
720 | return; | 721 | return; |
721 | } | 722 | } |
722 | 723 | if (skb_shinfo(skb)->nr_frags > usable_sge) { | |
724 | if (skb_linearize(skb) < 0) { | ||
725 | ipoib_warn(priv, "skb could not be linearized\n"); | ||
726 | ++dev->stats.tx_dropped; | ||
727 | ++dev->stats.tx_errors; | ||
728 | dev_kfree_skb_any(skb); | ||
729 | return; | ||
730 | } | ||
731 | /* Does skb_linearize return ok without reducing nr_frags? */ | ||
732 | if (skb_shinfo(skb)->nr_frags > usable_sge) { | ||
733 | ipoib_warn(priv, "too many frags after skb linearize\n"); | ||
734 | ++dev->stats.tx_dropped; | ||
735 | ++dev->stats.tx_errors; | ||
736 | dev_kfree_skb_any(skb); | ||
737 | return; | ||
738 | } | ||
739 | } | ||
723 | ipoib_dbg_data(priv, "sending packet: head 0x%x length %d connection 0x%x\n", | 740 | ipoib_dbg_data(priv, "sending packet: head 0x%x length %d connection 0x%x\n", |
724 | tx->tx_head, skb->len, tx->qp->qp_num); | 741 | tx->tx_head, skb->len, tx->qp->qp_num); |
725 | 742 | ||
@@ -1031,7 +1048,8 @@ static struct ib_qp *ipoib_cm_create_tx_qp(struct net_device *dev, struct ipoib_ | |||
1031 | struct ib_qp *tx_qp; | 1048 | struct ib_qp *tx_qp; |
1032 | 1049 | ||
1033 | if (dev->features & NETIF_F_SG) | 1050 | if (dev->features & NETIF_F_SG) |
1034 | attr.cap.max_send_sge = MAX_SKB_FRAGS + 1; | 1051 | attr.cap.max_send_sge = |
1052 | min_t(u32, priv->ca->attrs.max_sge, MAX_SKB_FRAGS + 1); | ||
1035 | 1053 | ||
1036 | tx_qp = ib_create_qp(priv->pd, &attr); | 1054 | tx_qp = ib_create_qp(priv->pd, &attr); |
1037 | if (PTR_ERR(tx_qp) == -EINVAL) { | 1055 | if (PTR_ERR(tx_qp) == -EINVAL) { |
@@ -1040,6 +1058,7 @@ static struct ib_qp *ipoib_cm_create_tx_qp(struct net_device *dev, struct ipoib_ | |||
1040 | attr.create_flags &= ~IB_QP_CREATE_USE_GFP_NOIO; | 1058 | attr.create_flags &= ~IB_QP_CREATE_USE_GFP_NOIO; |
1041 | tx_qp = ib_create_qp(priv->pd, &attr); | 1059 | tx_qp = ib_create_qp(priv->pd, &attr); |
1042 | } | 1060 | } |
1061 | tx->max_send_sge = attr.cap.max_send_sge; | ||
1043 | return tx_qp; | 1062 | return tx_qp; |
1044 | } | 1063 | } |
1045 | 1064 | ||
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_ib.c b/drivers/infiniband/ulp/ipoib/ipoib_ib.c index fa9c42ff1fb0..899e6b7fb8a5 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_ib.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_ib.c | |||
@@ -538,6 +538,7 @@ void ipoib_send(struct net_device *dev, struct sk_buff *skb, | |||
538 | struct ipoib_tx_buf *tx_req; | 538 | struct ipoib_tx_buf *tx_req; |
539 | int hlen, rc; | 539 | int hlen, rc; |
540 | void *phead; | 540 | void *phead; |
541 | unsigned usable_sge = priv->max_send_sge - !!skb_headlen(skb); | ||
541 | 542 | ||
542 | if (skb_is_gso(skb)) { | 543 | if (skb_is_gso(skb)) { |
543 | hlen = skb_transport_offset(skb) + tcp_hdrlen(skb); | 544 | hlen = skb_transport_offset(skb) + tcp_hdrlen(skb); |
@@ -561,6 +562,23 @@ void ipoib_send(struct net_device *dev, struct sk_buff *skb, | |||
561 | phead = NULL; | 562 | phead = NULL; |
562 | hlen = 0; | 563 | hlen = 0; |
563 | } | 564 | } |
565 | if (skb_shinfo(skb)->nr_frags > usable_sge) { | ||
566 | if (skb_linearize(skb) < 0) { | ||
567 | ipoib_warn(priv, "skb could not be linearized\n"); | ||
568 | ++dev->stats.tx_dropped; | ||
569 | ++dev->stats.tx_errors; | ||
570 | dev_kfree_skb_any(skb); | ||
571 | return; | ||
572 | } | ||
573 | /* Does skb_linearize return ok without reducing nr_frags? */ | ||
574 | if (skb_shinfo(skb)->nr_frags > usable_sge) { | ||
575 | ipoib_warn(priv, "too many frags after skb linearize\n"); | ||
576 | ++dev->stats.tx_dropped; | ||
577 | ++dev->stats.tx_errors; | ||
578 | dev_kfree_skb_any(skb); | ||
579 | return; | ||
580 | } | ||
581 | } | ||
564 | 582 | ||
565 | ipoib_dbg_data(priv, "sending packet, length=%d address=%p qpn=0x%06x\n", | 583 | ipoib_dbg_data(priv, "sending packet, length=%d address=%p qpn=0x%06x\n", |
566 | skb->len, address, qpn); | 584 | skb->len, address, qpn); |
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_verbs.c b/drivers/infiniband/ulp/ipoib/ipoib_verbs.c index d48c5bae7877..b809c373e40e 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_verbs.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_verbs.c | |||
@@ -206,7 +206,8 @@ int ipoib_transport_dev_init(struct net_device *dev, struct ib_device *ca) | |||
206 | init_attr.create_flags |= IB_QP_CREATE_NETIF_QP; | 206 | init_attr.create_flags |= IB_QP_CREATE_NETIF_QP; |
207 | 207 | ||
208 | if (dev->features & NETIF_F_SG) | 208 | if (dev->features & NETIF_F_SG) |
209 | init_attr.cap.max_send_sge = MAX_SKB_FRAGS + 1; | 209 | init_attr.cap.max_send_sge = |
210 | min_t(u32, priv->ca->attrs.max_sge, MAX_SKB_FRAGS + 1); | ||
210 | 211 | ||
211 | priv->qp = ib_create_qp(priv->pd, &init_attr); | 212 | priv->qp = ib_create_qp(priv->pd, &init_attr); |
212 | if (IS_ERR(priv->qp)) { | 213 | if (IS_ERR(priv->qp)) { |
@@ -233,6 +234,8 @@ int ipoib_transport_dev_init(struct net_device *dev, struct ib_device *ca) | |||
233 | priv->rx_wr.next = NULL; | 234 | priv->rx_wr.next = NULL; |
234 | priv->rx_wr.sg_list = priv->rx_sge; | 235 | priv->rx_wr.sg_list = priv->rx_sge; |
235 | 236 | ||
237 | priv->max_send_sge = init_attr.cap.max_send_sge; | ||
238 | |||
236 | return 0; | 239 | return 0; |
237 | 240 | ||
238 | out_free_send_cq: | 241 | out_free_send_cq: |