diff options
Diffstat (limited to 'drivers/net/xen-netfront.c')
| -rw-r--r-- | drivers/net/xen-netfront.c | 258 | 
1 files changed, 89 insertions, 169 deletions
diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c index d8c10764f130..e9b960f0ff32 100644 --- a/drivers/net/xen-netfront.c +++ b/drivers/net/xen-netfront.c @@ -142,10 +142,6 @@ struct netfront_queue {  	struct sk_buff *rx_skbs[NET_RX_RING_SIZE];  	grant_ref_t gref_rx_head;  	grant_ref_t grant_rx_ref[NET_RX_RING_SIZE]; - -	unsigned long rx_pfn_array[NET_RX_RING_SIZE]; -	struct multicall_entry rx_mcl[NET_RX_RING_SIZE+1]; -	struct mmu_update rx_mmu[NET_RX_RING_SIZE];  };  struct netfront_info { @@ -223,11 +219,7 @@ static grant_ref_t xennet_get_rx_ref(struct netfront_queue *queue,  }  #ifdef CONFIG_SYSFS -static int xennet_sysfs_addif(struct net_device *netdev); -static void xennet_sysfs_delif(struct net_device *netdev); -#else /* !CONFIG_SYSFS */ -#define xennet_sysfs_addif(dev) (0) -#define xennet_sysfs_delif(dev) do { } while (0) +static const struct attribute_group xennet_dev_group;  #endif  static bool xennet_can_sg(struct net_device *dev) @@ -424,109 +416,68 @@ static void xennet_tx_buf_gc(struct netfront_queue *queue)  	xennet_maybe_wake_tx(queue);  } -static void xennet_make_frags(struct sk_buff *skb, struct netfront_queue *queue, -			      struct xen_netif_tx_request *tx) +static struct xen_netif_tx_request *xennet_make_one_txreq( +	struct netfront_queue *queue, struct sk_buff *skb, +	struct page *page, unsigned int offset, unsigned int len)  { -	char *data = skb->data; -	unsigned long mfn; -	RING_IDX prod = queue->tx.req_prod_pvt; -	int frags = skb_shinfo(skb)->nr_frags; -	unsigned int offset = offset_in_page(data); -	unsigned int len = skb_headlen(skb);  	unsigned int id; +	struct xen_netif_tx_request *tx;  	grant_ref_t ref; -	int i; -	/* While the header overlaps a page boundary (including being -	   larger than a page), split it it into page-sized chunks. */ -	while (len > PAGE_SIZE - offset) { -		tx->size = PAGE_SIZE - offset; -		tx->flags |= XEN_NETTXF_more_data; -		len -= tx->size; -		data += tx->size; -		offset = 0; +	len = min_t(unsigned int, PAGE_SIZE - offset, len); -		id = get_id_from_freelist(&queue->tx_skb_freelist, queue->tx_skbs); -		queue->tx_skbs[id].skb = skb_get(skb); -		tx = RING_GET_REQUEST(&queue->tx, prod++); -		tx->id = id; -		ref = gnttab_claim_grant_reference(&queue->gref_tx_head); -		BUG_ON((signed short)ref < 0); +	id = get_id_from_freelist(&queue->tx_skb_freelist, queue->tx_skbs); +	tx = RING_GET_REQUEST(&queue->tx, queue->tx.req_prod_pvt++); +	ref = gnttab_claim_grant_reference(&queue->gref_tx_head); +	BUG_ON((signed short)ref < 0); -		mfn = virt_to_mfn(data); -		gnttab_grant_foreign_access_ref(ref, queue->info->xbdev->otherend_id, -						mfn, GNTMAP_readonly); +	gnttab_grant_foreign_access_ref(ref, queue->info->xbdev->otherend_id, +					page_to_mfn(page), GNTMAP_readonly); -		queue->grant_tx_page[id] = virt_to_page(data); -		tx->gref = queue->grant_tx_ref[id] = ref; -		tx->offset = offset; -		tx->size = len; -		tx->flags = 0; -	} +	queue->tx_skbs[id].skb = skb; +	queue->grant_tx_page[id] = page; +	queue->grant_tx_ref[id] = ref; -	/* Grant backend access to each skb fragment page. */ -	for (i = 0; i < frags; i++) { -		skb_frag_t *frag = skb_shinfo(skb)->frags + i; -		struct page *page = skb_frag_page(frag); +	tx->id = id; +	tx->gref = ref; +	tx->offset = offset; +	tx->size = len; +	tx->flags = 0; -		len = skb_frag_size(frag); -		offset = frag->page_offset; +	return tx; +} -		/* Skip unused frames from start of page */ -		page += offset >> PAGE_SHIFT; -		offset &= ~PAGE_MASK; +static struct xen_netif_tx_request *xennet_make_txreqs( +	struct netfront_queue *queue, struct xen_netif_tx_request *tx, +	struct sk_buff *skb, struct page *page, +	unsigned int offset, unsigned int len) +{ +	/* Skip unused frames from start of page */ +	page += offset >> PAGE_SHIFT; +	offset &= ~PAGE_MASK; -		while (len > 0) { -			unsigned long bytes; - -			bytes = PAGE_SIZE - offset; -			if (bytes > len) -				bytes = len; - -			tx->flags |= XEN_NETTXF_more_data; - -			id = get_id_from_freelist(&queue->tx_skb_freelist, -						  queue->tx_skbs); -			queue->tx_skbs[id].skb = skb_get(skb); -			tx = RING_GET_REQUEST(&queue->tx, prod++); -			tx->id = id; -			ref = gnttab_claim_grant_reference(&queue->gref_tx_head); -			BUG_ON((signed short)ref < 0); - -			mfn = pfn_to_mfn(page_to_pfn(page)); -			gnttab_grant_foreign_access_ref(ref, -							queue->info->xbdev->otherend_id, -							mfn, GNTMAP_readonly); - -			queue->grant_tx_page[id] = page; -			tx->gref = queue->grant_tx_ref[id] = ref; -			tx->offset = offset; -			tx->size = bytes; -			tx->flags = 0; - -			offset += bytes; -			len -= bytes; - -			/* Next frame */ -			if (offset == PAGE_SIZE && len) { -				BUG_ON(!PageCompound(page)); -				page++; -				offset = 0; -			} -		} +	while (len) { +		tx->flags |= XEN_NETTXF_more_data; +		tx = xennet_make_one_txreq(queue, skb_get(skb), +					   page, offset, len); +		page++; +		offset = 0; +		len -= tx->size;  	} -	queue->tx.req_prod_pvt = prod; +	return tx;  }  /* - * Count how many ring slots are required to send the frags of this - * skb. Each frag might be a compound page. + * Count how many ring slots are required to send this skb. Each frag + * might be a compound page.   */ -static int xennet_count_skb_frag_slots(struct sk_buff *skb) +static int xennet_count_skb_slots(struct sk_buff *skb)  {  	int i, frags = skb_shinfo(skb)->nr_frags; -	int pages = 0; +	int pages; + +	pages = PFN_UP(offset_in_page(skb->data) + skb_headlen(skb));  	for (i = 0; i < frags; i++) {  		skb_frag_t *frag = skb_shinfo(skb)->frags + i; @@ -562,18 +513,15 @@ static u16 xennet_select_queue(struct net_device *dev, struct sk_buff *skb,  static int xennet_start_xmit(struct sk_buff *skb, struct net_device *dev)  { -	unsigned short id;  	struct netfront_info *np = netdev_priv(dev);  	struct netfront_stats *tx_stats = this_cpu_ptr(np->tx_stats); -	struct xen_netif_tx_request *tx; -	char *data = skb->data; -	RING_IDX i; -	grant_ref_t ref; -	unsigned long mfn; +	struct xen_netif_tx_request *tx, *first_tx; +	unsigned int i;  	int notify;  	int slots; -	unsigned int offset = offset_in_page(data); -	unsigned int len = skb_headlen(skb); +	struct page *page; +	unsigned int offset; +	unsigned int len;  	unsigned long flags;  	struct netfront_queue *queue = NULL;  	unsigned int num_queues = dev->real_num_tx_queues; @@ -596,18 +544,18 @@ static int xennet_start_xmit(struct sk_buff *skb, struct net_device *dev)  		goto drop;  	} -	slots = DIV_ROUND_UP(offset + len, PAGE_SIZE) + -		xennet_count_skb_frag_slots(skb); +	slots = xennet_count_skb_slots(skb);  	if (unlikely(slots > MAX_SKB_FRAGS + 1)) {  		net_dbg_ratelimited("xennet: skb rides the rocket: %d slots, %d bytes\n",  				    slots, skb->len);  		if (skb_linearize(skb))  			goto drop; -		data = skb->data; -		offset = offset_in_page(data); -		len = skb_headlen(skb);  	} +	page = virt_to_page(skb->data); +	offset = offset_in_page(skb->data); +	len = skb_headlen(skb); +  	spin_lock_irqsave(&queue->tx_lock, flags);  	if (unlikely(!netif_carrier_ok(dev) || @@ -617,25 +565,13 @@ static int xennet_start_xmit(struct sk_buff *skb, struct net_device *dev)  		goto drop;  	} -	i = queue->tx.req_prod_pvt; - -	id = get_id_from_freelist(&queue->tx_skb_freelist, queue->tx_skbs); -	queue->tx_skbs[id].skb = skb; - -	tx = RING_GET_REQUEST(&queue->tx, i); +	/* First request for the linear area. */ +	first_tx = tx = xennet_make_one_txreq(queue, skb, +					      page, offset, len); +	page++; +	offset = 0; +	len -= tx->size; -	tx->id   = id; -	ref = gnttab_claim_grant_reference(&queue->gref_tx_head); -	BUG_ON((signed short)ref < 0); -	mfn = virt_to_mfn(data); -	gnttab_grant_foreign_access_ref( -		ref, queue->info->xbdev->otherend_id, mfn, GNTMAP_readonly); -	queue->grant_tx_page[id] = virt_to_page(data); -	tx->gref = queue->grant_tx_ref[id] = ref; -	tx->offset = offset; -	tx->size = len; - -	tx->flags = 0;  	if (skb->ip_summed == CHECKSUM_PARTIAL)  		/* local packet? */  		tx->flags |= XEN_NETTXF_csum_blank | XEN_NETTXF_data_validated; @@ -643,11 +579,12 @@ static int xennet_start_xmit(struct sk_buff *skb, struct net_device *dev)  		/* remote but checksummed. */  		tx->flags |= XEN_NETTXF_data_validated; +	/* Optional extra info after the first request. */  	if (skb_shinfo(skb)->gso_size) {  		struct xen_netif_extra_info *gso;  		gso = (struct xen_netif_extra_info *) -			RING_GET_REQUEST(&queue->tx, ++i); +			RING_GET_REQUEST(&queue->tx, queue->tx.req_prod_pvt++);  		tx->flags |= XEN_NETTXF_extra_info; @@ -662,10 +599,19 @@ static int xennet_start_xmit(struct sk_buff *skb, struct net_device *dev)  		gso->flags = 0;  	} -	queue->tx.req_prod_pvt = i + 1; +	/* Requests for the rest of the linear area. */ +	tx = xennet_make_txreqs(queue, tx, skb, page, offset, len); + +	/* Requests for all the frags. */ +	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { +		skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; +		tx = xennet_make_txreqs(queue, tx, skb, +					skb_frag_page(frag), frag->page_offset, +					skb_frag_size(frag)); +	} -	xennet_make_frags(skb, queue, tx); -	tx->size = skb->len; +	/* First request has the packet length. */ +	first_tx->size = skb->len;  	RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&queue->tx, notify);  	if (notify) @@ -1367,20 +1313,15 @@ static int netfront_probe(struct xenbus_device *dev,  	info = netdev_priv(netdev);  	dev_set_drvdata(&dev->dev, info); - +#ifdef CONFIG_SYSFS +	info->netdev->sysfs_groups[0] = &xennet_dev_group; +#endif  	err = register_netdev(info->netdev);  	if (err) {  		pr_warn("%s: register_netdev err=%d\n", __func__, err);  		goto fail;  	} -	err = xennet_sysfs_addif(info->netdev); -	if (err) { -		unregister_netdev(info->netdev); -		pr_warn("%s: add sysfs failed err=%d\n", __func__, err); -		goto fail; -	} -  	return 0;   fail: @@ -2144,39 +2085,20 @@ static ssize_t store_rxbuf(struct device *dev,  	return len;  } -static struct device_attribute xennet_attrs[] = { -	__ATTR(rxbuf_min, S_IRUGO|S_IWUSR, show_rxbuf, store_rxbuf), -	__ATTR(rxbuf_max, S_IRUGO|S_IWUSR, show_rxbuf, store_rxbuf), -	__ATTR(rxbuf_cur, S_IRUGO, show_rxbuf, NULL), -}; - -static int xennet_sysfs_addif(struct net_device *netdev) -{ -	int i; -	int err; - -	for (i = 0; i < ARRAY_SIZE(xennet_attrs); i++) { -		err = device_create_file(&netdev->dev, -					   &xennet_attrs[i]); -		if (err) -			goto fail; -	} -	return 0; - - fail: -	while (--i >= 0) -		device_remove_file(&netdev->dev, &xennet_attrs[i]); -	return err; -} - -static void xennet_sysfs_delif(struct net_device *netdev) -{ -	int i; +static DEVICE_ATTR(rxbuf_min, S_IRUGO|S_IWUSR, show_rxbuf, store_rxbuf); +static DEVICE_ATTR(rxbuf_max, S_IRUGO|S_IWUSR, show_rxbuf, store_rxbuf); +static DEVICE_ATTR(rxbuf_cur, S_IRUGO, show_rxbuf, NULL); -	for (i = 0; i < ARRAY_SIZE(xennet_attrs); i++) -		device_remove_file(&netdev->dev, &xennet_attrs[i]); -} +static struct attribute *xennet_dev_attrs[] = { +	&dev_attr_rxbuf_min.attr, +	&dev_attr_rxbuf_max.attr, +	&dev_attr_rxbuf_cur.attr, +	NULL +}; +static const struct attribute_group xennet_dev_group = { +	.attrs = xennet_dev_attrs +};  #endif /* CONFIG_SYSFS */  static int xennet_remove(struct xenbus_device *dev) @@ -2190,8 +2112,6 @@ static int xennet_remove(struct xenbus_device *dev)  	xennet_disconnect_backend(info); -	xennet_sysfs_delif(info->netdev); -  	unregister_netdev(info->netdev);  	for (i = 0; i < num_queues; ++i) {  | 
