diff options
Diffstat (limited to 'drivers/net/xen-netback/netback.c')
| -rw-r--r-- | drivers/net/xen-netback/netback.c | 123 | 
1 files changed, 75 insertions, 48 deletions
diff --git a/drivers/net/xen-netback/netback.c b/drivers/net/xen-netback/netback.c index f2d6b78d901..cd49ba94963 100644 --- a/drivers/net/xen-netback/netback.c +++ b/drivers/net/xen-netback/netback.c @@ -147,7 +147,8 @@ void xen_netbk_remove_xenvif(struct xenvif *vif)  	atomic_dec(&netbk->netfront_count);  } -static void xen_netbk_idx_release(struct xen_netbk *netbk, u16 pending_idx); +static void xen_netbk_idx_release(struct xen_netbk *netbk, u16 pending_idx, +				  u8 status);  static void make_tx_response(struct xenvif *vif,  			     struct xen_netif_tx_request *txp,  			     s8       st); @@ -879,7 +880,7 @@ static void netbk_tx_err(struct xenvif *vif,  	do {  		make_tx_response(vif, txp, XEN_NETIF_RSP_ERROR); -		if (cons >= end) +		if (cons == end)  			break;  		txp = RING_GET_REQUEST(&vif->tx, cons++);  	} while (1); @@ -888,6 +889,13 @@ static void netbk_tx_err(struct xenvif *vif,  	xenvif_put(vif);  } +static void netbk_fatal_tx_err(struct xenvif *vif) +{ +	netdev_err(vif->dev, "fatal error; disabling device\n"); +	xenvif_carrier_off(vif); +	xenvif_put(vif); +} +  static int netbk_count_requests(struct xenvif *vif,  				struct xen_netif_tx_request *first,  				struct xen_netif_tx_request *txp, @@ -901,29 +909,33 @@ static int netbk_count_requests(struct xenvif *vif,  	do {  		if (frags >= work_to_do) { -			netdev_dbg(vif->dev, "Need more frags\n"); -			return -frags; +			netdev_err(vif->dev, "Need more frags\n"); +			netbk_fatal_tx_err(vif); +			return -ENODATA;  		}  		if (unlikely(frags >= MAX_SKB_FRAGS)) { -			netdev_dbg(vif->dev, "Too many frags\n"); -			return -frags; +			netdev_err(vif->dev, "Too many frags\n"); +			netbk_fatal_tx_err(vif); +			return -E2BIG;  		}  		memcpy(txp, RING_GET_REQUEST(&vif->tx, cons + frags),  		       sizeof(*txp));  		if (txp->size > first->size) { -			netdev_dbg(vif->dev, "Frags galore\n"); -			return -frags; +			netdev_err(vif->dev, "Frag is bigger than frame.\n"); +			netbk_fatal_tx_err(vif); +			return -EIO;  		}  		first->size -= txp->size;  		frags++;  		if (unlikely((txp->offset + txp->size) > PAGE_SIZE)) { -			netdev_dbg(vif->dev, "txp->offset: %x, size: %u\n", +			netdev_err(vif->dev, "txp->offset: %x, size: %u\n",  				 txp->offset, txp->size); -			return -frags; +			netbk_fatal_tx_err(vif); +			return -EINVAL;  		}  	} while ((txp++)->flags & XEN_NETTXF_more_data);  	return frags; @@ -966,7 +978,7 @@ static struct gnttab_copy *xen_netbk_get_requests(struct xen_netbk *netbk,  		pending_idx = netbk->pending_ring[index];  		page = xen_netbk_alloc_page(netbk, skb, pending_idx);  		if (!page) -			return NULL; +			goto err;  		gop->source.u.ref = txp->gref;  		gop->source.domid = vif->domid; @@ -988,6 +1000,17 @@ static struct gnttab_copy *xen_netbk_get_requests(struct xen_netbk *netbk,  	}  	return gop; +err: +	/* Unwind, freeing all pages and sending error responses. */ +	while (i-- > start) { +		xen_netbk_idx_release(netbk, frag_get_pending_idx(&frags[i]), +				      XEN_NETIF_RSP_ERROR); +	} +	/* The head too, if necessary. */ +	if (start) +		xen_netbk_idx_release(netbk, pending_idx, XEN_NETIF_RSP_ERROR); + +	return NULL;  }  static int xen_netbk_tx_check_gop(struct xen_netbk *netbk, @@ -996,30 +1019,20 @@ static int xen_netbk_tx_check_gop(struct xen_netbk *netbk,  {  	struct gnttab_copy *gop = *gopp;  	u16 pending_idx = *((u16 *)skb->data); -	struct pending_tx_info *pending_tx_info = netbk->pending_tx_info; -	struct xenvif *vif = pending_tx_info[pending_idx].vif; -	struct xen_netif_tx_request *txp;  	struct skb_shared_info *shinfo = skb_shinfo(skb);  	int nr_frags = shinfo->nr_frags;  	int i, err, start;  	/* Check status of header. */  	err = gop->status; -	if (unlikely(err)) { -		pending_ring_idx_t index; -		index = pending_index(netbk->pending_prod++); -		txp = &pending_tx_info[pending_idx].req; -		make_tx_response(vif, txp, XEN_NETIF_RSP_ERROR); -		netbk->pending_ring[index] = pending_idx; -		xenvif_put(vif); -	} +	if (unlikely(err)) +		xen_netbk_idx_release(netbk, pending_idx, XEN_NETIF_RSP_ERROR);  	/* Skip first skb fragment if it is on same page as header fragment. */  	start = (frag_get_pending_idx(&shinfo->frags[0]) == pending_idx);  	for (i = start; i < nr_frags; i++) {  		int j, newerr; -		pending_ring_idx_t index;  		pending_idx = frag_get_pending_idx(&shinfo->frags[i]); @@ -1028,16 +1041,12 @@ static int xen_netbk_tx_check_gop(struct xen_netbk *netbk,  		if (likely(!newerr)) {  			/* Had a previous error? Invalidate this fragment. */  			if (unlikely(err)) -				xen_netbk_idx_release(netbk, pending_idx); +				xen_netbk_idx_release(netbk, pending_idx, XEN_NETIF_RSP_OKAY);  			continue;  		}  		/* Error on this fragment: respond to client with an error. */ -		txp = &netbk->pending_tx_info[pending_idx].req; -		make_tx_response(vif, txp, XEN_NETIF_RSP_ERROR); -		index = pending_index(netbk->pending_prod++); -		netbk->pending_ring[index] = pending_idx; -		xenvif_put(vif); +		xen_netbk_idx_release(netbk, pending_idx, XEN_NETIF_RSP_ERROR);  		/* Not the first error? Preceding frags already invalidated. */  		if (err) @@ -1045,10 +1054,10 @@ static int xen_netbk_tx_check_gop(struct xen_netbk *netbk,  		/* First error: invalidate header and preceding fragments. */  		pending_idx = *((u16 *)skb->data); -		xen_netbk_idx_release(netbk, pending_idx); +		xen_netbk_idx_release(netbk, pending_idx, XEN_NETIF_RSP_OKAY);  		for (j = start; j < i; j++) {  			pending_idx = frag_get_pending_idx(&shinfo->frags[j]); -			xen_netbk_idx_release(netbk, pending_idx); +			xen_netbk_idx_release(netbk, pending_idx, XEN_NETIF_RSP_OKAY);  		}  		/* Remember the error: invalidate all subsequent fragments. */ @@ -1082,7 +1091,7 @@ static void xen_netbk_fill_frags(struct xen_netbk *netbk, struct sk_buff *skb)  		/* Take an extra reference to offset xen_netbk_idx_release */  		get_page(netbk->mmap_pages[pending_idx]); -		xen_netbk_idx_release(netbk, pending_idx); +		xen_netbk_idx_release(netbk, pending_idx, XEN_NETIF_RSP_OKAY);  	}  } @@ -1095,7 +1104,8 @@ static int xen_netbk_get_extras(struct xenvif *vif,  	do {  		if (unlikely(work_to_do-- <= 0)) { -			netdev_dbg(vif->dev, "Missing extra info\n"); +			netdev_err(vif->dev, "Missing extra info\n"); +			netbk_fatal_tx_err(vif);  			return -EBADR;  		} @@ -1104,8 +1114,9 @@ static int xen_netbk_get_extras(struct xenvif *vif,  		if (unlikely(!extra.type ||  			     extra.type >= XEN_NETIF_EXTRA_TYPE_MAX)) {  			vif->tx.req_cons = ++cons; -			netdev_dbg(vif->dev, +			netdev_err(vif->dev,  				   "Invalid extra type: %d\n", extra.type); +			netbk_fatal_tx_err(vif);  			return -EINVAL;  		} @@ -1121,13 +1132,15 @@ static int netbk_set_skb_gso(struct xenvif *vif,  			     struct xen_netif_extra_info *gso)  {  	if (!gso->u.gso.size) { -		netdev_dbg(vif->dev, "GSO size must not be zero.\n"); +		netdev_err(vif->dev, "GSO size must not be zero.\n"); +		netbk_fatal_tx_err(vif);  		return -EINVAL;  	}  	/* Currently only TCPv4 S.O. is supported. */  	if (gso->u.gso.type != XEN_NETIF_GSO_TYPE_TCPV4) { -		netdev_dbg(vif->dev, "Bad GSO type %d.\n", gso->u.gso.type); +		netdev_err(vif->dev, "Bad GSO type %d.\n", gso->u.gso.type); +		netbk_fatal_tx_err(vif);  		return -EINVAL;  	} @@ -1264,9 +1277,25 @@ static unsigned xen_netbk_tx_build_gops(struct xen_netbk *netbk)  		/* Get a netif from the list with work to do. */  		vif = poll_net_schedule_list(netbk); +		/* This can sometimes happen because the test of +		 * list_empty(net_schedule_list) at the top of the +		 * loop is unlocked.  Just go back and have another +		 * look. +		 */  		if (!vif)  			continue; +		if (vif->tx.sring->req_prod - vif->tx.req_cons > +		    XEN_NETIF_TX_RING_SIZE) { +			netdev_err(vif->dev, +				   "Impossible number of requests. " +				   "req_prod %d, req_cons %d, size %ld\n", +				   vif->tx.sring->req_prod, vif->tx.req_cons, +				   XEN_NETIF_TX_RING_SIZE); +			netbk_fatal_tx_err(vif); +			continue; +		} +  		RING_FINAL_CHECK_FOR_REQUESTS(&vif->tx, work_to_do);  		if (!work_to_do) {  			xenvif_put(vif); @@ -1294,17 +1323,14 @@ static unsigned xen_netbk_tx_build_gops(struct xen_netbk *netbk)  			work_to_do = xen_netbk_get_extras(vif, extras,  							  work_to_do);  			idx = vif->tx.req_cons; -			if (unlikely(work_to_do < 0)) { -				netbk_tx_err(vif, &txreq, idx); +			if (unlikely(work_to_do < 0))  				continue; -			}  		}  		ret = netbk_count_requests(vif, &txreq, txfrags, work_to_do); -		if (unlikely(ret < 0)) { -			netbk_tx_err(vif, &txreq, idx - ret); +		if (unlikely(ret < 0))  			continue; -		} +  		idx += ret;  		if (unlikely(txreq.size < ETH_HLEN)) { @@ -1316,11 +1342,11 @@ static unsigned xen_netbk_tx_build_gops(struct xen_netbk *netbk)  		/* No crossing a page as the payload mustn't fragment. */  		if (unlikely((txreq.offset + txreq.size) > PAGE_SIZE)) { -			netdev_dbg(vif->dev, +			netdev_err(vif->dev,  				   "txreq.offset: %x, size: %u, end: %lu\n",  				   txreq.offset, txreq.size,  				   (txreq.offset&~PAGE_MASK) + txreq.size); -			netbk_tx_err(vif, &txreq, idx); +			netbk_fatal_tx_err(vif);  			continue;  		} @@ -1348,8 +1374,8 @@ static unsigned xen_netbk_tx_build_gops(struct xen_netbk *netbk)  			gso = &extras[XEN_NETIF_EXTRA_TYPE_GSO - 1];  			if (netbk_set_skb_gso(vif, skb, gso)) { +				/* Failure in netbk_set_skb_gso is fatal. */  				kfree_skb(skb); -				netbk_tx_err(vif, &txreq, idx);  				continue;  			}  		} @@ -1448,7 +1474,7 @@ static void xen_netbk_tx_submit(struct xen_netbk *netbk)  			txp->size -= data_len;  		} else {  			/* Schedule a response immediately. */ -			xen_netbk_idx_release(netbk, pending_idx); +			xen_netbk_idx_release(netbk, pending_idx, XEN_NETIF_RSP_OKAY);  		}  		if (txp->flags & XEN_NETTXF_csum_blank) @@ -1500,7 +1526,8 @@ static void xen_netbk_tx_action(struct xen_netbk *netbk)  	xen_netbk_tx_submit(netbk);  } -static void xen_netbk_idx_release(struct xen_netbk *netbk, u16 pending_idx) +static void xen_netbk_idx_release(struct xen_netbk *netbk, u16 pending_idx, +				  u8 status)  {  	struct xenvif *vif;  	struct pending_tx_info *pending_tx_info; @@ -1514,7 +1541,7 @@ static void xen_netbk_idx_release(struct xen_netbk *netbk, u16 pending_idx)  	vif = pending_tx_info->vif; -	make_tx_response(vif, &pending_tx_info->req, XEN_NETIF_RSP_OKAY); +	make_tx_response(vif, &pending_tx_info->req, status);  	index = pending_index(netbk->pending_prod++);  	netbk->pending_ring[index] = pending_idx;  | 
