diff options
Diffstat (limited to 'drivers/net/xen-netback/netback.c')
| -rw-r--r-- | drivers/net/xen-netback/netback.c | 54 | 
1 files changed, 34 insertions, 20 deletions
diff --git a/drivers/net/xen-netback/netback.c b/drivers/net/xen-netback/netback.c index f7a31d2cb3f1..4de46aa61d95 100644 --- a/drivers/net/xen-netback/netback.c +++ b/drivers/net/xen-netback/netback.c @@ -96,6 +96,7 @@ static void xenvif_idx_release(struct xenvif_queue *queue, u16 pending_idx,  static void make_tx_response(struct xenvif_queue *queue,  			     struct xen_netif_tx_request *txp,  			     s8       st); +static void push_tx_responses(struct xenvif_queue *queue);  static inline int tx_work_todo(struct xenvif_queue *queue); @@ -641,7 +642,7 @@ static void tx_add_credit(struct xenvif_queue *queue)  	queue->remaining_credit = min(max_credit, max_burst);  } -static void tx_credit_callback(unsigned long data) +void xenvif_tx_credit_callback(unsigned long data)  {  	struct xenvif_queue *queue = (struct xenvif_queue *)data;  	tx_add_credit(queue); @@ -657,6 +658,7 @@ static void xenvif_tx_err(struct xenvif_queue *queue,  	do {  		spin_lock_irqsave(&queue->response_lock, flags);  		make_tx_response(queue, txp, XEN_NETIF_RSP_ERROR); +		push_tx_responses(queue);  		spin_unlock_irqrestore(&queue->response_lock, flags);  		if (cons == end)  			break; @@ -1163,8 +1165,6 @@ static bool tx_credit_exceeded(struct xenvif_queue *queue, unsigned size)  	if (size > queue->remaining_credit) {  		queue->credit_timeout.data     =  			(unsigned long)queue; -		queue->credit_timeout.function = -			tx_credit_callback;  		mod_timer(&queue->credit_timeout,  			  next_credit);  		queue->credit_window_start = next_credit; @@ -1343,7 +1343,7 @@ static int xenvif_handle_frag_list(struct xenvif_queue *queue, struct sk_buff *s  {  	unsigned int offset = skb_headlen(skb);  	skb_frag_t frags[MAX_SKB_FRAGS]; -	int i; +	int i, f;  	struct ubuf_info *uarg;  	struct sk_buff *nskb = skb_shinfo(skb)->frag_list; @@ -1383,23 +1383,25 @@ static int xenvif_handle_frag_list(struct xenvif_queue *queue, struct sk_buff *s  		frags[i].page_offset = 0;  		skb_frag_size_set(&frags[i], len);  	} -	/* swap out with old one */ -	memcpy(skb_shinfo(skb)->frags, -	       frags, -	       i * sizeof(skb_frag_t)); -	skb_shinfo(skb)->nr_frags = i; -	skb->truesize += i * PAGE_SIZE; -	/* remove traces of mapped pages and frag_list */ +	/* Copied all the bits from the frag list -- free it. */  	skb_frag_list_init(skb); +	xenvif_skb_zerocopy_prepare(queue, nskb); +	kfree_skb(nskb); + +	/* Release all the original (foreign) frags. */ +	for (f = 0; f < skb_shinfo(skb)->nr_frags; f++) +		skb_frag_unref(skb, f);  	uarg = skb_shinfo(skb)->destructor_arg;  	/* increase inflight counter to offset decrement in callback */  	atomic_inc(&queue->inflight_packets);  	uarg->callback(uarg, true);  	skb_shinfo(skb)->destructor_arg = NULL; -	xenvif_skb_zerocopy_prepare(queue, nskb); -	kfree_skb(nskb); +	/* Fill the skb with the new (local) frags. */ +	memcpy(skb_shinfo(skb)->frags, frags, i * sizeof(skb_frag_t)); +	skb_shinfo(skb)->nr_frags = i; +	skb->truesize += i * PAGE_SIZE;  	return 0;  } @@ -1652,13 +1654,20 @@ static void xenvif_idx_release(struct xenvif_queue *queue, u16 pending_idx,  	unsigned long flags;  	pending_tx_info = &queue->pending_tx_info[pending_idx]; +  	spin_lock_irqsave(&queue->response_lock, flags); +  	make_tx_response(queue, &pending_tx_info->req, status); -	index = pending_index(queue->pending_prod); + +	/* Release the pending index before pusing the Tx response so +	 * its available before a new Tx request is pushed by the +	 * frontend. +	 */ +	index = pending_index(queue->pending_prod++);  	queue->pending_ring[index] = pending_idx; -	/* TX shouldn't use the index before we give it back here */ -	mb(); -	queue->pending_prod++; + +	push_tx_responses(queue); +  	spin_unlock_irqrestore(&queue->response_lock, flags);  } @@ -1669,7 +1678,6 @@ static void make_tx_response(struct xenvif_queue *queue,  {  	RING_IDX i = queue->tx.rsp_prod_pvt;  	struct xen_netif_tx_response *resp; -	int notify;  	resp = RING_GET_RESPONSE(&queue->tx, i);  	resp->id     = txp->id; @@ -1679,6 +1687,12 @@ static void make_tx_response(struct xenvif_queue *queue,  		RING_GET_RESPONSE(&queue->tx, ++i)->status = XEN_NETIF_RSP_NULL;  	queue->tx.rsp_prod_pvt = ++i; +} + +static void push_tx_responses(struct xenvif_queue *queue) +{ +	int notify; +  	RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&queue->tx, notify);  	if (notify)  		notify_remote_via_irq(queue->tx_irq); @@ -1766,7 +1780,7 @@ int xenvif_map_frontend_rings(struct xenvif_queue *queue,  	int err = -ENOMEM;  	err = xenbus_map_ring_valloc(xenvif_to_xenbus_device(queue->vif), -				     tx_ring_ref, &addr); +				     &tx_ring_ref, 1, &addr);  	if (err)  		goto err; @@ -1774,7 +1788,7 @@ int xenvif_map_frontend_rings(struct xenvif_queue *queue,  	BACK_RING_INIT(&queue->tx, txs, PAGE_SIZE);  	err = xenbus_map_ring_valloc(xenvif_to_xenbus_device(queue->vif), -				     rx_ring_ref, &addr); +				     &rx_ring_ref, 1, &addr);  	if (err)  		goto err;  |