Commit ff67df55 authored by Benjamin LaHaise's avatar Benjamin LaHaise Committed by David S. Miller

vxge: fix pktgen hangs (don't abuse skb->cb[])

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: default avatarBenjamin LaHaise <ben.lahaise@neterion.com>
Signed-off-by: default avatarSreenivasa Honnur <sreenivasa.honnur@neterion.com>
Signed-off-by: default avatarRamkrishna Vepa <ram.vepa@neterion.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 4403b371
...@@ -978,7 +978,9 @@ struct __vxge_hw_fifo { ...@@ -978,7 +978,9 @@ struct __vxge_hw_fifo {
void *txdlh, void *txdlh,
enum vxge_hw_fifo_tcode t_code, enum vxge_hw_fifo_tcode t_code,
void *userdata, void *userdata,
void **skb_ptr); struct sk_buff ***skb_ptr,
int nr_skb,
int *more);
void (*txdl_term)( void (*txdl_term)(
void *txdlh, void *txdlh,
...@@ -1779,7 +1781,8 @@ struct vxge_hw_fifo_attr { ...@@ -1779,7 +1781,8 @@ struct vxge_hw_fifo_attr {
void *txdlh, void *txdlh,
enum vxge_hw_fifo_tcode t_code, enum vxge_hw_fifo_tcode t_code,
void *userdata, void *userdata,
void **skb_ptr); struct sk_buff ***skb_ptr,
int nr_skb, int *more);
void (*txdl_term)( void (*txdl_term)(
void *txdlh, void *txdlh,
......
...@@ -87,22 +87,25 @@ static inline int is_vxge_card_up(struct vxgedev *vdev) ...@@ -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) static inline void VXGE_COMPLETE_VPATH_TX(struct vxge_fifo *fifo)
{ {
unsigned long flags = 0; unsigned long flags = 0;
struct sk_buff *skb_ptr = NULL; struct sk_buff **skb_ptr = NULL;
struct sk_buff **temp, *head, *skb; struct sk_buff **temp;
#define NR_SKB_COMPLETED 128
if (spin_trylock_irqsave(&fifo->tx_lock, flags)) { struct sk_buff *completed[NR_SKB_COMPLETED];
vxge_hw_vpath_poll_tx(fifo->handle, (void **)&skb_ptr); int more;
spin_unlock_irqrestore(&fifo->tx_lock, flags);
} do {
/* free SKBs */ more = 0;
head = skb_ptr; skb_ptr = completed;
while (head) {
skb = head; if (spin_trylock_irqsave(&fifo->tx_lock, flags)) {
temp = (struct sk_buff **)&skb->cb; vxge_hw_vpath_poll_tx(fifo->handle, &skb_ptr,
head = *temp; NR_SKB_COMPLETED, &more);
*temp = NULL; spin_unlock_irqrestore(&fifo->tx_lock, flags);
dev_kfree_skb_irq(skb); }
} /* 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) 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, ...@@ -600,11 +603,10 @@ vxge_rx_1b_compl(struct __vxge_hw_ring *ringh, void *dtr,
enum vxge_hw_status enum vxge_hw_status
vxge_xmit_compl(struct __vxge_hw_fifo *fifo_hw, void *dtr, vxge_xmit_compl(struct __vxge_hw_fifo *fifo_hw, void *dtr,
enum vxge_hw_fifo_tcode t_code, void *userdata, 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 vxge_fifo *fifo = (struct vxge_fifo *)userdata;
struct sk_buff *skb, *head = NULL; struct sk_buff *skb, **done_skb = *skb_ptr;
struct sk_buff **temp;
int pkt_cnt = 0; int pkt_cnt = 0;
vxge_debug_entryexit(VXGE_TRACE, vxge_debug_entryexit(VXGE_TRACE,
...@@ -657,9 +659,12 @@ vxge_xmit_compl(struct __vxge_hw_fifo *fifo_hw, void *dtr, ...@@ -657,9 +659,12 @@ vxge_xmit_compl(struct __vxge_hw_fifo *fifo_hw, void *dtr,
fifo->stats.tx_frms++; fifo->stats.tx_frms++;
fifo->stats.tx_bytes += skb->len; fifo->stats.tx_bytes += skb->len;
temp = (struct sk_buff **)&skb->cb; *done_skb++ = skb;
*temp = head;
head = skb; if (--nr_skb <= 0) {
*more = 1;
break;
}
pkt_cnt++; pkt_cnt++;
if (pkt_cnt > fifo->indicate_max_pkts) if (pkt_cnt > fifo->indicate_max_pkts)
...@@ -668,11 +673,9 @@ vxge_xmit_compl(struct __vxge_hw_fifo *fifo_hw, void *dtr, ...@@ -668,11 +673,9 @@ vxge_xmit_compl(struct __vxge_hw_fifo *fifo_hw, void *dtr,
} while (vxge_hw_fifo_txdl_next_completed(fifo_hw, } while (vxge_hw_fifo_txdl_next_completed(fifo_hw,
&dtr, &t_code) == VXGE_HW_OK); &dtr, &t_code) == VXGE_HW_OK);
*skb_ptr = done_skb;
vxge_wake_tx_queue(fifo, skb); vxge_wake_tx_queue(fifo, skb);
if (skb_ptr)
*skb_ptr = (void *) head;
vxge_debug_entryexit(VXGE_TRACE, vxge_debug_entryexit(VXGE_TRACE,
"%s: %s:%d Exiting...", "%s: %s:%d Exiting...",
fifo->ndev->name, __func__, __LINE__); fifo->ndev->name, __func__, __LINE__);
......
...@@ -428,7 +428,8 @@ vxge_rx_1b_compl(struct __vxge_hw_ring *ringh, void *dtr, ...@@ -428,7 +428,8 @@ vxge_rx_1b_compl(struct __vxge_hw_ring *ringh, void *dtr,
enum vxge_hw_status enum vxge_hw_status
vxge_xmit_compl(struct __vxge_hw_fifo *fifo_hw, void *dtr, 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); int vxge_close(struct net_device *dev);
......
...@@ -2508,7 +2508,8 @@ enum vxge_hw_status vxge_hw_vpath_poll_rx(struct __vxge_hw_ring *ring) ...@@ -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(). * See also: vxge_hw_vpath_poll_tx().
*/ */
enum vxge_hw_status vxge_hw_vpath_poll_tx(struct __vxge_hw_fifo *fifo, 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; enum vxge_hw_fifo_tcode t_code;
void *first_txdlh; void *first_txdlh;
...@@ -2520,8 +2521,8 @@ enum vxge_hw_status vxge_hw_vpath_poll_tx(struct __vxge_hw_fifo *fifo, ...@@ -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, status = vxge_hw_fifo_txdl_next_completed(fifo,
&first_txdlh, &t_code); &first_txdlh, &t_code);
if (status == VXGE_HW_OK) if (status == VXGE_HW_OK)
if (fifo->callback(fifo, first_txdlh, if (fifo->callback(fifo, first_txdlh, t_code,
t_code, channel->userdata, skb_ptr) != VXGE_HW_OK) channel->userdata, skb_ptr, nr_skb, more) != VXGE_HW_OK)
status = VXGE_HW_COMPLETIONS_REMAIN; status = VXGE_HW_COMPLETIONS_REMAIN;
return status; return status;
......
...@@ -2326,7 +2326,7 @@ enum vxge_hw_status vxge_hw_vpath_poll_rx( ...@@ -2326,7 +2326,7 @@ enum vxge_hw_status vxge_hw_vpath_poll_rx(
enum vxge_hw_status vxge_hw_vpath_poll_tx( enum vxge_hw_status vxge_hw_vpath_poll_tx(
struct __vxge_hw_fifo *fifoh, 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( enum vxge_hw_status vxge_hw_vpath_alarm_process(
struct __vxge_hw_vpath_handle *vpath_handle, struct __vxge_hw_vpath_handle *vpath_handle,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment