aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/xen-netback/netback.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/xen-netback/netback.c')
-rw-r--r--drivers/net/xen-netback/netback.c94
1 files changed, 64 insertions, 30 deletions
diff --git a/drivers/net/xen-netback/netback.c b/drivers/net/xen-netback/netback.c
index 956130c70036..f3e591c611de 100644
--- a/drivers/net/xen-netback/netback.c
+++ b/drivers/net/xen-netback/netback.c
@@ -212,6 +212,49 @@ static bool start_new_rx_buffer(int offset, unsigned long size, int head)
212 return false; 212 return false;
213} 213}
214 214
215struct xenvif_count_slot_state {
216 unsigned long copy_off;
217 bool head;
218};
219
220unsigned int xenvif_count_frag_slots(struct xenvif *vif,
221 unsigned long offset, unsigned long size,
222 struct xenvif_count_slot_state *state)
223{
224 unsigned count = 0;
225
226 offset &= ~PAGE_MASK;
227
228 while (size > 0) {
229 unsigned long bytes;
230
231 bytes = PAGE_SIZE - offset;
232
233 if (bytes > size)
234 bytes = size;
235
236 if (start_new_rx_buffer(state->copy_off, bytes, state->head)) {
237 count++;
238 state->copy_off = 0;
239 }
240
241 if (state->copy_off + bytes > MAX_BUFFER_OFFSET)
242 bytes = MAX_BUFFER_OFFSET - state->copy_off;
243
244 state->copy_off += bytes;
245
246 offset += bytes;
247 size -= bytes;
248
249 if (offset == PAGE_SIZE)
250 offset = 0;
251
252 state->head = false;
253 }
254
255 return count;
256}
257
215/* 258/*
216 * Figure out how many ring slots we're going to need to send @skb to 259 * Figure out how many ring slots we're going to need to send @skb to
217 * the guest. This function is essentially a dry run of 260 * the guest. This function is essentially a dry run of
@@ -219,48 +262,39 @@ static bool start_new_rx_buffer(int offset, unsigned long size, int head)
219 */ 262 */
220unsigned int xenvif_count_skb_slots(struct xenvif *vif, struct sk_buff *skb) 263unsigned int xenvif_count_skb_slots(struct xenvif *vif, struct sk_buff *skb)
221{ 264{
265 struct xenvif_count_slot_state state;
222 unsigned int count; 266 unsigned int count;
223 int i, copy_off; 267 unsigned char *data;
268 unsigned i;
224 269
225 count = DIV_ROUND_UP(skb_headlen(skb), PAGE_SIZE); 270 state.head = true;
271 state.copy_off = 0;
226 272
227 copy_off = skb_headlen(skb) % PAGE_SIZE; 273 /* Slot for the first (partial) page of data. */
274 count = 1;
228 275
276 /* Need a slot for the GSO prefix for GSO extra data? */
229 if (skb_shinfo(skb)->gso_size) 277 if (skb_shinfo(skb)->gso_size)
230 count++; 278 count++;
231 279
232 for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { 280 data = skb->data;
233 unsigned long size = skb_frag_size(&skb_shinfo(skb)->frags[i]); 281 while (data < skb_tail_pointer(skb)) {
234 unsigned long offset = skb_shinfo(skb)->frags[i].page_offset; 282 unsigned long offset = offset_in_page(data);
235 unsigned long bytes; 283 unsigned long size = PAGE_SIZE - offset;
236
237 offset &= ~PAGE_MASK;
238
239 while (size > 0) {
240 BUG_ON(offset >= PAGE_SIZE);
241 BUG_ON(copy_off > MAX_BUFFER_OFFSET);
242
243 bytes = PAGE_SIZE - offset;
244
245 if (bytes > size)
246 bytes = size;
247 284
248 if (start_new_rx_buffer(copy_off, bytes, 0)) { 285 if (data + size > skb_tail_pointer(skb))
249 count++; 286 size = skb_tail_pointer(skb) - data;
250 copy_off = 0;
251 }
252 287
253 if (copy_off + bytes > MAX_BUFFER_OFFSET) 288 count += xenvif_count_frag_slots(vif, offset, size, &state);
254 bytes = MAX_BUFFER_OFFSET - copy_off;
255 289
256 copy_off += bytes; 290 data += size;
291 }
257 292
258 offset += bytes; 293 for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
259 size -= bytes; 294 unsigned long size = skb_frag_size(&skb_shinfo(skb)->frags[i]);
295 unsigned long offset = skb_shinfo(skb)->frags[i].page_offset;
260 296
261 if (offset == PAGE_SIZE) 297 count += xenvif_count_frag_slots(vif, offset, size, &state);
262 offset = 0;
263 }
264 } 298 }
265 return count; 299 return count;
266} 300}