1 From 4c06034001e20ff9f6e2a1a3dfa155bf3f31440c Mon Sep 17 00:00:00 2001
2 From: KY Srinivasan <kys@microsoft.com>
3 Date: Sat, 8 Mar 2014 19:23:13 -0800
4 Subject: [PATCH 05/25] Drivers: net: hyperv: Enable scatter gather I/O
6 Cleanup the code and enable scatter gather I/O.
8 Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
9 Reviewed-by: Haiyang Zhang <haiyangz@microsoft.com>
10 Signed-off-by: David S. Miller <davem@davemloft.net>
12 drivers/net/hyperv/netvsc_drv.c | 153 ++++++++++++++++++++++++++++++----------
13 1 file changed, 114 insertions(+), 39 deletions(-)
15 diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c
16 index 8e3a0b00099b..72961741be54 100644
17 --- a/drivers/net/hyperv/netvsc_drv.c
18 +++ b/drivers/net/hyperv/netvsc_drv.c
19 @@ -140,22 +140,124 @@ static void netvsc_xmit_completion(void *context)
20 dev_kfree_skb_any(skb);
23 +static u32 fill_pg_buf(struct page *page, u32 offset, u32 len,
24 + struct hv_page_buffer *pb)
28 + /* Deal with compund pages by ignoring unused part
31 + page += (offset >> PAGE_SHIFT);
32 + offset &= ~PAGE_MASK;
35 + unsigned long bytes;
37 + bytes = PAGE_SIZE - offset;
40 + pb[j].pfn = page_to_pfn(page);
41 + pb[j].offset = offset;
47 + if (offset == PAGE_SIZE && len) {
57 +static void init_page_array(void *hdr, u32 len, struct sk_buff *skb,
58 + struct hv_page_buffer *pb)
61 + char *data = skb->data;
62 + int frags = skb_shinfo(skb)->nr_frags;
65 + /* The packet is laid out thus:
67 + * 2. skb linear data
68 + * 3. skb fragment data
71 + slots_used += fill_pg_buf(virt_to_page(hdr),
72 + offset_in_page(hdr),
73 + len, &pb[slots_used]);
75 + slots_used += fill_pg_buf(virt_to_page(data),
76 + offset_in_page(data),
77 + skb_headlen(skb), &pb[slots_used]);
79 + for (i = 0; i < frags; i++) {
80 + skb_frag_t *frag = skb_shinfo(skb)->frags + i;
82 + slots_used += fill_pg_buf(skb_frag_page(frag),
84 + skb_frag_size(frag), &pb[slots_used]);
88 +static int count_skb_frag_slots(struct sk_buff *skb)
90 + int i, frags = skb_shinfo(skb)->nr_frags;
93 + for (i = 0; i < frags; i++) {
94 + skb_frag_t *frag = skb_shinfo(skb)->frags + i;
95 + unsigned long size = skb_frag_size(frag);
96 + unsigned long offset = frag->page_offset;
98 + /* Skip unused frames from start of page */
99 + offset &= ~PAGE_MASK;
100 + pages += PFN_UP(offset + size);
105 +static int netvsc_get_slots(struct sk_buff *skb)
107 + char *data = skb->data;
108 + unsigned int offset = offset_in_page(data);
109 + unsigned int len = skb_headlen(skb);
113 + slots = DIV_ROUND_UP(offset + len, PAGE_SIZE);
114 + frag_slots = count_skb_frag_slots(skb);
115 + return slots + frag_slots;
118 static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
120 struct net_device_context *net_device_ctx = netdev_priv(net);
121 struct hv_netvsc_packet *packet;
123 - unsigned int i, num_pages, npg_data;
124 + unsigned int num_data_pages;
125 u32 skb_length = skb->len;
127 - /* Add multipages for skb->data and additional 2 for RNDIS */
128 - npg_data = (((unsigned long)skb->data + skb_headlen(skb) - 1)
129 - >> PAGE_SHIFT) - ((unsigned long)skb->data >> PAGE_SHIFT) + 1;
130 - num_pages = skb_shinfo(skb)->nr_frags + npg_data + 2;
131 + /* We will atmost need two pages to describe the rndis
132 + * header. We can only transmit MAX_PAGE_BUFFER_COUNT number
133 + * of pages in a single packet.
135 + num_data_pages = netvsc_get_slots(skb) + 2;
136 + if (num_data_pages > MAX_PAGE_BUFFER_COUNT) {
137 + netdev_err(net, "Packet too big: %u\n", skb->len);
138 + dev_kfree_skb(skb);
139 + net->stats.tx_dropped++;
140 + return NETDEV_TX_OK;
143 /* Allocate a netvsc packet based on # of frags. */
144 packet = kzalloc(sizeof(struct hv_netvsc_packet) +
145 - (num_pages * sizeof(struct hv_page_buffer)) +
146 + (num_data_pages * sizeof(struct hv_page_buffer)) +
147 sizeof(struct rndis_message) +
148 NDIS_VLAN_PPI_SIZE, GFP_ATOMIC);
150 @@ -170,44 +272,17 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net)
151 packet->vlan_tci = skb->vlan_tci;
153 packet->extension = (void *)(unsigned long)packet +
154 - sizeof(struct hv_netvsc_packet) +
155 - (num_pages * sizeof(struct hv_page_buffer));
156 + sizeof(struct hv_netvsc_packet) +
157 + (num_data_pages * sizeof(struct hv_page_buffer));
159 /* If the rndis msg goes beyond 1 page, we will add 1 later */
160 - packet->page_buf_cnt = num_pages - 1;
161 + packet->page_buf_cnt = num_data_pages - 1;
163 /* Initialize it from the skb */
164 packet->total_data_buflen = skb->len;
166 /* Start filling in the page buffers starting after RNDIS buffer. */
167 - packet->page_buf[1].pfn = virt_to_phys(skb->data) >> PAGE_SHIFT;
168 - packet->page_buf[1].offset
169 - = (unsigned long)skb->data & (PAGE_SIZE - 1);
171 - packet->page_buf[1].len = skb_headlen(skb);
173 - packet->page_buf[1].len = PAGE_SIZE
174 - - packet->page_buf[1].offset;
176 - for (i = 2; i <= npg_data; i++) {
177 - packet->page_buf[i].pfn = virt_to_phys(skb->data
178 - + PAGE_SIZE * (i-1)) >> PAGE_SHIFT;
179 - packet->page_buf[i].offset = 0;
180 - packet->page_buf[i].len = PAGE_SIZE;
183 - packet->page_buf[npg_data].len = (((unsigned long)skb->data
184 - + skb_headlen(skb) - 1) & (PAGE_SIZE - 1)) + 1;
186 - /* Additional fragments are after SKB data */
187 - for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
188 - const skb_frag_t *f = &skb_shinfo(skb)->frags[i];
190 - packet->page_buf[i+npg_data+1].pfn =
191 - page_to_pfn(skb_frag_page(f));
192 - packet->page_buf[i+npg_data+1].offset = f->page_offset;
193 - packet->page_buf[i+npg_data+1].len = skb_frag_size(f);
195 + init_page_array(NULL, 0, skb, &packet->page_buf[1]);
197 /* Set the completion routine */
198 packet->completion.send.send_completion = netvsc_xmit_completion;
199 @@ -454,8 +529,8 @@ static int netvsc_probe(struct hv_device *dev,
200 net->netdev_ops = &device_ops;
202 /* TODO: Add GSO and Checksum offload */
203 - net->hw_features = 0;
204 - net->features = NETIF_F_HW_VLAN_CTAG_TX;
205 + net->hw_features = NETIF_F_SG;
206 + net->features = NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_SG;
208 SET_ETHTOOL_OPS(net, ðtool_ops);
209 SET_NETDEV_DEV(net, &dev->device);