]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
IB/ipoib: Add handling for sending of skb with many frags
authorHans Westgaard Ry <hans.westgaard.ry@oracle.com>
Wed, 16 Mar 2016 13:01:02 +0000 (14:01 +0100)
committerChuck Anderson <chuck.anderson@oracle.com>
Thu, 5 May 2016 21:11:43 +0000 (14:11 -0700)
Orabug: 21498734

IPoIB puts skb-fragments in SGEs adding 1 extra SGE when SG is enabled.
Current codepath assumes that the max number of SGEs a device supports
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 SGEs 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.

Change-Id: Ia81e69d7231987208ac298300fc5b9734f193a2d
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>
drivers/infiniband/ulp/ipoib/ipoib.h
drivers/infiniband/ulp/ipoib/ipoib_cm.c
drivers/infiniband/ulp/ipoib/ipoib_ib.c
drivers/infiniband/ulp/ipoib/ipoib_verbs.c

index 693b9da6a2637800e766a221a0278045595e49cd..1343ea796e880cfa2a8af41c494a8d37b414e5a1 100644 (file)
@@ -260,6 +260,8 @@ struct ipoib_cm_tx {
        unsigned long        flags;
        u32                  mtu;
        u16                  caps;
+       /* Used when checking for need to linearize SKBs with many frags */
+       unsigned             max_send_sge;
 };
 
 struct ipoib_cm_rx_buf {
@@ -407,6 +409,10 @@ struct ipoib_dev_priv {
        int     hca_caps;
        struct ipoib_ethtool_st ethtool;
        struct timer_list poll_timer;
+       /* Used when checking for need to linearize SKBs with many frags */
+       unsigned max_send_sge;
+       /* Device specific; obtained from query_device */
+       unsigned max_sge;
 };
 
 struct ipoib_ah {
@@ -765,11 +771,15 @@ static inline int ipoib_register_debugfs(void) { return 0; }
 static inline void ipoib_unregister_debugfs(void) { }
 #endif
 
+#define ipoib_dev_name(priv) (((struct ipoib_dev_priv *) priv)->dev->name)
 #define ipoib_printk(level, priv, format, arg...)      \
-       printk(level "%s: " format, ((struct ipoib_dev_priv *) priv)->dev->name , ## arg)
+       printk(level "%s: " format, ipoib_dev_name(priv), ## arg)
 #define ipoib_warn(priv, format, arg...)               \
        ipoib_printk(KERN_WARNING, priv, format , ## arg)
 
+#define ipoib_warn_ratelimited(priv, format, arg...) \
+       pr_warn_ratelimited("%s: " format, ipoib_dev_name(priv), ## arg)
+
 extern int ipoib_sendq_size;
 extern int ipoib_recvq_size;
 
@@ -810,4 +820,35 @@ extern int ipoib_debug_level;
 
 extern const char ipoib_driver_version[];
 
+static inline int ipoib_linearize_skb(struct net_device *dev,
+                                     struct sk_buff *skb,
+                                     struct ipoib_dev_priv *priv,
+                                     unsigned max_send_sge)
+{
+       unsigned usable_sge = max_send_sge - !!skb_headlen(skb);
+
+       if (skb_shinfo(skb)->nr_frags > usable_sge) {
+               if (skb_linearize(skb) < 0) {
+                       ipoib_warn_ratelimited(priv,
+                                              "skb could not be linearized\n");
+                       ++dev->stats.tx_dropped;
+                       ++dev->stats.tx_errors;
+                       dev_kfree_skb_any(skb);
+                       return -1;
+               }
+
+               /* skb_linearize returned ok but still not reducing nr_frags */
+               if (skb_shinfo(skb)->nr_frags > usable_sge) {
+                       ipoib_warn_ratelimited(priv,
+                                              "too many frags after skb linearize\n");
+                       ++dev->stats.tx_dropped;
+                       ++dev->stats.tx_errors;
+                       dev_kfree_skb_any(skb);
+                       return -1;
+               }
+       }
+       return 0;
+
+}
+
 #endif /* _IPOIB_H */
index d84531d49722b9137d05ddf833b3839cd55965f1..e59d4ce87ddaa02d31c8368ec52b7b54c27508e2 100644 (file)
@@ -794,6 +794,8 @@ void ipoib_cm_send(struct net_device *dev, struct sk_buff *skb, struct ipoib_cm_
                ipoib_cm_skb_too_long(dev, skb, tx->mtu - IPOIB_ENCAP_LEN);
                return;
        }
+       if (ipoib_linearize_skb(dev, skb, priv, tx->max_send_sge) < 0)
+               return;
 
        ipoib_dbg_data(priv, "sending packet: head 0x%x length %d connection 0x%x\n",
                       tx->tx_head, skb->len, tx->qp->qp_num);
@@ -1152,7 +1154,8 @@ static struct ib_qp *ipoib_cm_create_tx_qp(struct net_device *dev, struct ipoib_
        struct ib_qp *tx_qp;
 
        if (dev->features & NETIF_F_SG)
-               attr.cap.max_send_sge = MAX_SKB_FRAGS + 1;
+               attr.cap.max_send_sge =
+                       min_t(u32, priv->max_sge, MAX_SKB_FRAGS + 1);
 
        tx_qp = ib_create_qp(priv->pd, &attr);
        if (PTR_ERR(tx_qp) == -EINVAL) {
@@ -1161,6 +1164,7 @@ static struct ib_qp *ipoib_cm_create_tx_qp(struct net_device *dev, struct ipoib_
                attr.create_flags &= ~IB_QP_CREATE_USE_GFP_NOIO;
                tx_qp = ib_create_qp(priv->pd, &attr);
        }
+       tx->max_send_sge = attr.cap.max_send_sge;
        return tx_qp;
 }
 
@@ -1714,6 +1718,7 @@ int ipoib_cm_dev_init(struct net_device *dev)
 
        ipoib_dbg(priv, "max_srq_sge=%d\n", attr.max_srq_sge);
 
+       priv->max_sge = attr.max_sge;
        attr.max_srq_sge = min_t(int, IPOIB_CM_RX_SG, attr.max_srq_sge);
        ipoib_cm_create_srq(dev, attr.max_srq_sge);
        if (ipoib_cm_has_srq(dev)) {
index 1fb82d84aaa4fc3f7957ae64ecc0b94914daf77b..8f28dbda80db70591f64e0e646cb04885cd20c64 100644 (file)
@@ -578,6 +578,8 @@ void ipoib_send(struct net_device *dev, struct sk_buff *skb,
                phead = NULL;
                hlen  = 0;
        }
+       if (ipoib_linearize_skb(dev, skb, priv, priv->max_send_sge) < 0)
+               return;
 
        ipoib_dbg_data(priv, "sending packet, length=%d address=%p qpn=0x%06x\n",
                       skb->len, address, qpn);
index 66d140358ba7f7c723592dea24651032b68af14a..bc5f636b74eb754ad14eefb96560a092837cc7da 100644 (file)
@@ -207,7 +207,8 @@ int ipoib_transport_dev_init(struct net_device *dev, struct ib_device *ca)
                init_attr.create_flags |= IB_QP_CREATE_NETIF_QP;
 
        if (dev->features & NETIF_F_SG)
-               init_attr.cap.max_send_sge = MAX_SKB_FRAGS + 1;
+               init_attr.cap.max_send_sge =
+                       min_t(u32, priv->max_sge, MAX_SKB_FRAGS + 1);
 
        priv->qp = ib_create_qp(priv->pd, &init_attr);
        if (IS_ERR(priv->qp)) {
@@ -234,6 +235,8 @@ int ipoib_transport_dev_init(struct net_device *dev, struct ib_device *ca)
        priv->rx_wr.next = NULL;
        priv->rx_wr.sg_list = priv->rx_sge;
 
+       priv->max_send_sge = init_attr.cap.max_send_sge;
+
        return 0;
 
 out_free_send_cq: