vxge: fix pktgen hangs (don't abuse skb->cb[])
authorBenjamin LaHaise <ben.lahaise@neterion.com>
Tue, 4 Aug 2009 10:21:03 +0000 (10:21 +0000)
committerDavid S. Miller <davem@davemloft.net>
Wed, 5 Aug 2009 20:10:43 +0000 (13:10 -0700)
This patch fixes a case in the transmit completion code which was resulting
in pktgen hanging at the end of a run.  The cause is due to the fact that
the ->cb[] area of an skb cannot be used in a network driver's transmit
path, as that area belongs to the network protocol.  Pktgen hangs, as it
sends out the same packet multiple times, and vxge's use of this area of
the skb for a temporary list can only add the packet to the temporary list
once (while it may be on the queue many times).  The fix is to remove this
abuse of skb->cb[].  Instead, skb pointers are placed into a temporary
stack array, and then free outside of the tx lock.  This retains the smp
optimization of doing dev_kfree_skb() outside of the tx lock.

Signed-off-by: Benjamin LaHaise <ben.lahaise@neterion.com>
Signed-off-by: Sreenivasa Honnur <sreenivasa.honnur@neterion.com>
Signed-off-by: Ramkrishna Vepa <ram.vepa@neterion.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/vxge/vxge-config.h
drivers/net/vxge/vxge-main.c
drivers/net/vxge/vxge-main.h
drivers/net/vxge/vxge-traffic.c
drivers/net/vxge/vxge-traffic.h

index 224acea771ed8cadc7d2bac3b8196ed569f45f5d..62779a520ca1d5d8d2c0ac4b110e916756fba1fc 100644 (file)
@@ -978,7 +978,9 @@ struct __vxge_hw_fifo {
                        void *txdlh,
                        enum vxge_hw_fifo_tcode t_code,
                        void *userdata,
-                       void **skb_ptr);
+                       struct sk_buff ***skb_ptr,
+                       int nr_skb,
+                       int *more);
 
        void (*txdl_term)(
                        void *txdlh,
@@ -1779,7 +1781,8 @@ struct vxge_hw_fifo_attr {
                        void *txdlh,
                        enum vxge_hw_fifo_tcode t_code,
                        void *userdata,
-                       void **skb_ptr);
+                       struct sk_buff ***skb_ptr,
+                       int nr_skb, int *more);
 
        void (*txdl_term)(
                        void *txdlh,
index beee4ab2ed30e07359e163765e3bd1c48e6b5c38..4b22513bed402ec195000eb206b94d76d56ad2c5 100644 (file)
@@ -87,22 +87,25 @@ static inline int is_vxge_card_up(struct vxgedev *vdev)
 static inline void VXGE_COMPLETE_VPATH_TX(struct vxge_fifo *fifo)
 {
        unsigned long flags = 0;
-       struct sk_buff *skb_ptr = NULL;
-       struct sk_buff **temp, *head, *skb;
-
-       if (spin_trylock_irqsave(&fifo->tx_lock, flags)) {
-               vxge_hw_vpath_poll_tx(fifo->handle, (void **)&skb_ptr);
-               spin_unlock_irqrestore(&fifo->tx_lock, flags);
-       }
-       /* free SKBs */
-       head = skb_ptr;
-       while (head) {
-               skb = head;
-               temp = (struct sk_buff **)&skb->cb;
-               head = *temp;
-               *temp = NULL;
-               dev_kfree_skb_irq(skb);
-       }
+       struct sk_buff **skb_ptr = NULL;
+       struct sk_buff **temp;
+#define NR_SKB_COMPLETED 128
+       struct sk_buff *completed[NR_SKB_COMPLETED];
+       int more;
+
+       do {
+               more = 0;
+               skb_ptr = completed;
+
+               if (spin_trylock_irqsave(&fifo->tx_lock, flags)) {
+                       vxge_hw_vpath_poll_tx(fifo->handle, &skb_ptr,
+                                               NR_SKB_COMPLETED, &more);
+                       spin_unlock_irqrestore(&fifo->tx_lock, flags);
+               }
+               /* free SKBs */
+               for (temp = completed; temp != skb_ptr; temp++)
+                       dev_kfree_skb_irq(*temp);
+       } while (more) ;
 }
 
 static inline void VXGE_COMPLETE_ALL_TX(struct vxgedev *vdev)
@@ -600,11 +603,10 @@ vxge_rx_1b_compl(struct __vxge_hw_ring *ringh, void *dtr,
 enum vxge_hw_status
 vxge_xmit_compl(struct __vxge_hw_fifo *fifo_hw, void *dtr,
                enum vxge_hw_fifo_tcode t_code, void *userdata,
-               void **skb_ptr)
+               struct sk_buff ***skb_ptr, int nr_skb, int *more)
 {
        struct vxge_fifo *fifo = (struct vxge_fifo *)userdata;
-       struct sk_buff *skb, *head = NULL;
-       struct sk_buff **temp;
+       struct sk_buff *skb, **done_skb = *skb_ptr;
        int pkt_cnt = 0;
 
        vxge_debug_entryexit(VXGE_TRACE,
@@ -657,9 +659,12 @@ vxge_xmit_compl(struct __vxge_hw_fifo *fifo_hw, void *dtr,
                fifo->stats.tx_frms++;
                fifo->stats.tx_bytes += skb->len;
 
-               temp = (struct sk_buff **)&skb->cb;
-               *temp = head;
-               head = skb;
+               *done_skb++ = skb;
+
+               if (--nr_skb <= 0) {
+                       *more = 1;
+                       break;
+               }
 
                pkt_cnt++;
                if (pkt_cnt > fifo->indicate_max_pkts)
@@ -668,11 +673,9 @@ vxge_xmit_compl(struct __vxge_hw_fifo *fifo_hw, void *dtr,
        } while (vxge_hw_fifo_txdl_next_completed(fifo_hw,
                                &dtr, &t_code) == VXGE_HW_OK);
 
+       *skb_ptr = done_skb;
        vxge_wake_tx_queue(fifo, skb);
 
-       if (skb_ptr)
-               *skb_ptr = (void *) head;
-
        vxge_debug_entryexit(VXGE_TRACE,
                                "%s: %s:%d  Exiting...",
                                fifo->ndev->name, __func__, __LINE__);
index 18d824c3ab93ea3f94f4b5e0334f562d55f512ee..8b3989bc707f935f3f678053dadb9935e9930c01 100644 (file)
@@ -428,7 +428,8 @@ vxge_rx_1b_compl(struct __vxge_hw_ring *ringh, void *dtr,
 
 enum vxge_hw_status
 vxge_xmit_compl(struct __vxge_hw_fifo *fifo_hw, void *dtr,
-       enum vxge_hw_fifo_tcode t_code, void *userdata, void **skb_ptr);
+       enum vxge_hw_fifo_tcode t_code, void *userdata,
+       struct sk_buff ***skb_ptr, int nr_skbs, int *more);
 
 int vxge_close(struct net_device *dev);
 
index 370f55cbbad71947eb7cac983ec9b0985331f953..c249cd25a5d0415957dffbb606bd218cda76c5b9 100644 (file)
@@ -2508,7 +2508,8 @@ enum vxge_hw_status vxge_hw_vpath_poll_rx(struct __vxge_hw_ring *ring)
  * See also: vxge_hw_vpath_poll_tx().
  */
 enum vxge_hw_status vxge_hw_vpath_poll_tx(struct __vxge_hw_fifo *fifo,
-                                       void **skb_ptr)
+                                       struct sk_buff ***skb_ptr, int nr_skb,
+                                       int *more)
 {
        enum vxge_hw_fifo_tcode t_code;
        void *first_txdlh;
@@ -2520,8 +2521,8 @@ enum vxge_hw_status vxge_hw_vpath_poll_tx(struct __vxge_hw_fifo *fifo,
        status = vxge_hw_fifo_txdl_next_completed(fifo,
                                &first_txdlh, &t_code);
        if (status == VXGE_HW_OK)
-               if (fifo->callback(fifo, first_txdlh,
-                       t_code, channel->userdata, skb_ptr) != VXGE_HW_OK)
+               if (fifo->callback(fifo, first_txdlh, t_code,
+                       channel->userdata, skb_ptr, nr_skb, more) != VXGE_HW_OK)
                        status = VXGE_HW_COMPLETIONS_REMAIN;
 
        return status;
index 8260b91fd79515605df1a36c6a8df85273831a0e..461742b4442beae34d438026b740a72e68a07949 100644 (file)
@@ -2326,7 +2326,7 @@ enum vxge_hw_status vxge_hw_vpath_poll_rx(
 
 enum vxge_hw_status vxge_hw_vpath_poll_tx(
        struct __vxge_hw_fifo *fifoh,
-       void **skb_ptr);
+       struct sk_buff ***skb_ptr, int nr_skb, int *more);
 
 enum vxge_hw_status vxge_hw_vpath_alarm_process(
        struct __vxge_hw_vpath_handle *vpath_handle,