win-pvdrivers

view xennet/xennet_rx.c @ 382:ecfd4ddaaf3d

remove dead code
author Andy Grover <andy.grover@oracle.com>
date Wed Jul 09 12:35:41 2008 -0700 (2008-07-09)
parents 5a762fd1fba9
children 8e10579159a0
line source
1 /*
2 PV Net Driver for Windows Xen HVM Domains
3 Copyright (C) 2007 James Harper
4 Copyright (C) 2007 Andrew Grover <andy.grover@oracle.com>
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 */
21 #include "xennet.h"
23 // Called at DISPATCH_LEVEL with rx lock held
24 static NDIS_STATUS
25 XenNet_RxBufferAlloc(struct xennet_info *xi)
26 {
27 unsigned short id;
28 PMDL mdl;
29 int i, batch_target, notify;
30 RING_IDX req_prod = xi->rx.req_prod_pvt;
31 netif_rx_request_t *req;
32 #if DBG
33 int cycles = 0;
34 #endif
35 #if defined(XEN_PROFILE)
36 LARGE_INTEGER tsc, dummy;
37 #endif
39 //KdPrint((__DRIVER_NAME " --> " __FUNCTION__ "\n"));
40 #if defined(XEN_PROFILE)
41 tsc = KeQueryPerformanceCounter(&dummy);
42 #endif
44 batch_target = xi->rx_target - (req_prod - xi->rx.rsp_cons);
46 for (i = 0; i < batch_target; i++)
47 {
48 ASSERT(cycles++ < 256);
49 if (xi->rx_id_free == 0)
50 {
51 KdPrint((__DRIVER_NAME " Added %d out of %d buffers to rx ring (ran out of id's)\n", i, batch_target));
52 break;
53 }
54 mdl = XenFreelist_GetPage(&xi->rx_freelist);
55 if (mdl == NULL)
56 {
57 KdPrint((__DRIVER_NAME " Added %d out of %d buffers to rx ring (no free pages)\n", i, batch_target));
58 break;
59 }
60 xi->rx_id_free--;
62 /* Give to netback */
63 id = (USHORT)((req_prod + i) & (NET_RX_RING_SIZE - 1));
64 // KdPrint((__DRIVER_NAME " id = %d\n", id));
65 ASSERT(xi->rx_mdls[id] == NULL);
66 xi->rx_mdls[id] = mdl;
67 req = RING_GET_REQUEST(&xi->rx, req_prod + i);
68 req->gref = get_grant_ref(mdl);
69 req->id = id;
70 }
72 xi->rx.req_prod_pvt = req_prod + i;
73 RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&xi->rx, notify);
74 if (notify)
75 {
76 xi->vectors.EvtChn_Notify(xi->vectors.context, xi->event_channel);
77 }
80 // KdPrint((__DRIVER_NAME " <-- " __FUNCTION__ "\n"));
82 #if defined(XEN_PROFILE)
83 ProfTime_RxBufferAlloc.QuadPart += KeQueryPerformanceCounter(&dummy).QuadPart - tsc.QuadPart;
84 ProfCount_RxBufferAlloc++;
85 #endif
87 return NDIS_STATUS_SUCCESS;
88 }
90 /*
91 Windows appears to insist that the checksum on received packets is correct, and won't
92 believe us when we lie about it, which happens when the packet is generated on the
93 same bridge in Dom0. Doh!
94 This is only for TCP and UDP packets. IP checksums appear to be correct anyways.
95 */
96 static VOID
97 XenNet_SumPacketData(
98 packet_info_t *pi,
99 PNDIS_PACKET packet
100 )
101 {
102 USHORT i;
103 PUCHAR buffer;
104 PMDL mdl;
105 UINT total_length;
106 UINT buffer_length;
107 USHORT buffer_offset;
108 ULONG csum;
109 PUSHORT csum_ptr;
110 USHORT remaining;
111 USHORT ip4_length;
113 // KdPrint((__DRIVER_NAME " --> " __FUNCTION__ "\n"));
115 #if defined(XEN_PROFILE)
116 ProfCount_RxPacketsCsumOffload++;
117 #endif
119 NdisGetFirstBufferFromPacketSafe(packet, &mdl, &buffer, &buffer_length, &total_length, NormalPagePriority);
120 ASSERT(mdl);
122 ip4_length = GET_NET_USHORT(buffer[XN_HDR_SIZE + 2]);
124 if ((USHORT)(ip4_length + XN_HDR_SIZE) != total_length)
125 {
126 KdPrint((__DRIVER_NAME " Size Mismatch %d (ip4_length + XN_HDR_SIZE) != %d (total_length)\n", ip4_length + XN_HDR_SIZE, total_length));
127 }
129 switch (pi->ip_proto)
130 {
131 case 6:
132 csum_ptr = (USHORT *)&buffer[XN_HDR_SIZE + pi->ip4_header_length + 16];
133 break;
134 case 17:
135 csum_ptr = (USHORT *)&buffer[XN_HDR_SIZE + pi->ip4_header_length + 6];
136 break;
137 default:
138 KdPrint((__DRIVER_NAME " Don't know how to calc sum for IP Proto %d\n", pi->ip_proto));
139 return;
140 }
142 *csum_ptr = 0;
144 csum = 0;
145 csum += GET_NET_USHORT(buffer[XN_HDR_SIZE + 12]) + GET_NET_USHORT(buffer[XN_HDR_SIZE + 14]); // src
146 csum += GET_NET_USHORT(buffer[XN_HDR_SIZE + 16]) + GET_NET_USHORT(buffer[XN_HDR_SIZE + 18]); // dst
147 csum += ((USHORT)buffer[XN_HDR_SIZE + 9]);
149 remaining = ip4_length - pi->ip4_header_length;
151 csum += remaining;
153 for (buffer_offset = i = XN_HDR_SIZE + pi->ip4_header_length; i < total_length - 1; i += 2, buffer_offset += 2)
154 {
155 if (buffer_offset == buffer_length - 1) // deal with a buffer ending on an odd byte boundary
156 {
157 csum += (USHORT)buffer[buffer_offset] << 8;
158 NdisGetNextBuffer(mdl, &mdl);
159 if (mdl == NULL)
160 {
161 KdPrint((__DRIVER_NAME " Ran out of buffers\n"));
162 return;
163 }
164 NdisQueryBufferSafe(mdl, (PVOID) &buffer, &buffer_length, NormalPagePriority);
165 csum += ((USHORT)buffer[0]);
166 buffer_offset = (USHORT)-1;
167 }
168 else
169 {
170 if (buffer_offset == buffer_length)
171 {
172 // KdPrint((__DRIVER_NAME " New buffer - aligned...\n"));
173 NdisGetNextBuffer(mdl, &mdl);
174 if (mdl == NULL)
175 {
176 KdPrint((__DRIVER_NAME " Ran out of buffers\n"));
177 return;
178 }
179 NdisQueryBufferSafe(mdl, (PVOID) &buffer, &buffer_length, NormalPagePriority);
180 buffer_offset = 0;
181 }
182 csum += GET_NET_USHORT(buffer[buffer_offset]);
183 }
184 }
185 if (i != total_length) // last odd byte
186 {
187 csum += ((USHORT)buffer[buffer_offset] << 8);
188 }
189 while (csum & 0xFFFF0000)
190 csum = (csum & 0xFFFF) + (csum >> 16);
191 *csum_ptr = (USHORT)~GET_NET_USHORT(csum);
193 // KdPrint((__DRIVER_NAME " csum = %04x\n", *csum_ptr));
195 // KdPrint((__DRIVER_NAME " <-- " __FUNCTION__ "\n"));
196 }
198 static PNDIS_PACKET
199 get_packet_from_freelist(struct xennet_info *xi)
200 {
201 NDIS_STATUS status;
202 PNDIS_PACKET packet;
204 if (!xi->rx_packet_free)
205 {
206 NdisAllocatePacket(&status, &packet, xi->packet_pool);
207 ASSERT(status == NDIS_STATUS_SUCCESS);
208 NDIS_SET_PACKET_HEADER_SIZE(packet, XN_HDR_SIZE);
209 }
210 else
211 {
212 xi->rx_packet_free--;
213 packet = xi->rx_packet_list[xi->rx_packet_free];
214 }
215 return packet;
216 }
218 static VOID
219 put_packet_on_freelist(struct xennet_info *xi, PNDIS_PACKET packet)
220 {
221 NdisReinitializePacket(packet);
222 xi->rx_packet_list[xi->rx_packet_free] = packet;
223 xi->rx_packet_free++;
224 }
226 static VOID
227 packet_freelist_dispose(struct xennet_info *xi)
228 {
229 while(xi->rx_packet_free != 0)
230 {
231 xi->rx_packet_free--;
232 NdisFreePacket(xi->rx_packet_list[xi->rx_packet_free]);
233 }
234 }
236 static PNDIS_PACKET
237 XenNet_MakePacket(
238 struct xennet_info *xi
239 )
240 {
241 PNDIS_PACKET packet;
242 PUCHAR in_buffer;
243 PNDIS_BUFFER out_mdl;
244 PUCHAR out_buffer;
245 USHORT out_offset;
246 USHORT out_remaining;
247 USHORT length;
248 USHORT new_ip4_length;
249 //NDIS_STATUS status;
250 USHORT i;
252 // KdPrint((__DRIVER_NAME " --> " __FUNCTION__ "\n"));
254 packet = get_packet_from_freelist(xi);
255 xi->rx_outstanding++;
257 if (!xi->rxpi.split_required)
258 {
259 for (i = 0; i < xi->rxpi.mdl_count; i++)
260 NdisChainBufferAtBack(packet, xi->rxpi.mdls[i]);
261 NDIS_SET_PACKET_STATUS(packet, NDIS_STATUS_SUCCESS);
262 }
263 else
264 {
265 out_mdl = XenFreelist_GetPage(&xi->rx_freelist);
266 out_buffer = MmGetMdlVirtualAddress(out_mdl);
267 out_offset = XN_HDR_SIZE + xi->rxpi.ip4_header_length + xi->rxpi.tcp_header_length;
268 out_remaining = min(xi->rxpi.mss, xi->rxpi.tcp_remaining);
269 NdisAdjustBufferLength(out_mdl, out_offset + out_remaining);
270 memcpy(out_buffer, xi->rxpi.header, out_offset);
271 new_ip4_length = out_remaining + xi->rxpi.ip4_header_length + xi->rxpi.tcp_header_length;
272 SET_NET_USHORT(out_buffer[XN_HDR_SIZE + 2], new_ip4_length);
273 SET_NET_ULONG(out_buffer[XN_HDR_SIZE + xi->rxpi.ip4_header_length + 4], xi->rxpi.tcp_seq);
274 xi->rxpi.tcp_seq += out_remaining;
275 xi->rxpi.tcp_remaining = xi->rxpi.tcp_remaining - out_remaining;
276 do
277 {
278 ASSERT(xi->rxpi.curr_mdl < xi->rxpi.mdl_count);
279 in_buffer = XenNet_GetData(&xi->rxpi, out_remaining, &length);
280 memcpy(&out_buffer[out_offset], in_buffer, length);
281 out_remaining = out_remaining - length;
282 out_offset = out_offset + length;
283 } while (out_remaining != 0); // && in_buffer != NULL);
284 NdisChainBufferAtBack(packet, out_mdl);
285 XenNet_SumIpHeader(out_buffer, xi->rxpi.ip4_header_length);
286 NDIS_SET_PACKET_STATUS(packet, NDIS_STATUS_SUCCESS);
287 }
289 // KdPrint((__DRIVER_NAME " <-- " __FUNCTION__ " (%p)\n", packet));
290 return packet;
291 }
293 static VOID
294 XenNet_MakePackets(
295 struct xennet_info *xi,
296 PNDIS_PACKET *packets,
297 PULONG packet_count_p
298 )
299 {
300 USHORT i;
302 // KdPrint((__DRIVER_NAME " --> " __FUNCTION__ "(packets = %p, packet_count = %d)\n", packets, *packet_count_p));
304 XenNet_ParsePacketHeader(&xi->rxpi);
305 switch (xi->rxpi.ip_proto)
306 {
307 case 6: // TCP
308 if (xi->rxpi.split_required)
309 break;
310 // fallthrough
311 case 17: // UDP
312 ASSERT(*packet_count_p < NET_RX_RING_SIZE);
313 packets[*packet_count_p] = XenNet_MakePacket(xi);
314 if (xi->rxpi.csum_calc_required)
315 XenNet_SumPacketData(&xi->rxpi, packets[*packet_count_p]);
316 (*packet_count_p)++;
317 return;
318 default:
319 ASSERT(*packet_count_p < NET_RX_RING_SIZE);
320 packets[*packet_count_p] = XenNet_MakePacket(xi);
321 (*packet_count_p)++;
322 return;
323 }
324 // KdPrint((__DRIVER_NAME " splitting packet\n"));
325 xi->rxpi.tcp_remaining = xi->rxpi.tcp_length;
326 if (MmGetMdlByteCount(xi->rxpi.mdls[0]) > (ULONG)(XN_HDR_SIZE + xi->rxpi.ip4_header_length + xi->rxpi.tcp_header_length))
327 xi->rxpi.curr_mdl_offset = XN_HDR_SIZE + xi->rxpi.ip4_header_length + xi->rxpi.tcp_header_length;
328 else
329 xi->rxpi.curr_mdl = 1;
331 while (xi->rxpi.tcp_remaining)
332 {
333 ASSERT(*packet_count_p < NET_RX_RING_SIZE);
334 packets[*packet_count_p] = XenNet_MakePacket(xi);
335 XenNet_SumPacketData(&xi->rxpi, packets[*packet_count_p]);
336 (*packet_count_p)++;
337 }
339 ASSERT(xi->rxpi.curr_mdl == xi->rxpi.mdl_count);
340 // TODO: restore psh status to last packet
341 for (i = 0; i < xi->rxpi.mdl_count; i++)
342 {
343 NdisAdjustBufferLength(xi->rxpi.mdls[i], PAGE_SIZE);
344 XenFreelist_PutPage(&xi->rx_freelist, xi->rxpi.mdls[i]);
345 }
346 // KdPrint((__DRIVER_NAME " <-- " __FUNCTION__ " (split)\n"));
347 }
349 #define MAXIMUM_PACKETS_PER_INTERRUPT 128
350 #define MAXIMUM_PACKETS_PER_INDICATE 32
352 // Called at DISPATCH_LEVEL
353 NDIS_STATUS
354 XenNet_RxBufferCheck(struct xennet_info *xi)
355 {
356 RING_IDX cons, prod;
357 /* the highest number of packets we receive could be (65535 - header) / mss
358 for a low mss this could be even higher than NET_RX_RING_SIZE...
359 what can we do? */
360 PNDIS_PACKET packets[NET_RX_RING_SIZE];
361 ULONG packet_count = 0;
362 ULONG total_packets = 0;
363 PMDL mdl;
364 int moretodo;
365 struct netif_rx_response *rxrsp = NULL;
366 struct netif_extra_info *ei;
367 USHORT id;
369 // KdPrint((__DRIVER_NAME " --> " __FUNCTION__ "\n"));
371 ASSERT(xi->connected);
373 KeAcquireSpinLockAtDpcLevel(&xi->rx_lock);
375 //KdPrint((__DRIVER_NAME " --- " __FUNCTION__ " xi->rx.sring->rsp_prod = %d, xi->rx.rsp_cons = %d\n", xi->rx.sring->rsp_prod, xi->rx.rsp_cons));
377 do {
378 prod = xi->rx.sring->rsp_prod;
379 KeMemoryBarrier(); /* Ensure we see responses up to 'rp'. */
381 for (cons = xi->rx.rsp_cons; cons != prod && packet_count < MAXIMUM_PACKETS_PER_INDICATE; cons++)
382 {
383 id = (USHORT)(cons & (NET_RX_RING_SIZE - 1));
384 ASSERT(xi->rx_mdls[id]);
385 mdl = xi->rx_mdls[id];
386 xi->rx_mdls[id] = NULL;
387 xi->rx_id_free++;
388 if (xi->rxpi.extra_info)
389 {
390 XenFreelist_PutPage(&xi->rx_freelist, mdl);
391 ei = (struct netif_extra_info *)RING_GET_RESPONSE(&xi->rx, cons);
392 xi->rxpi.extra_info = (BOOLEAN)!!(ei->flags & XEN_NETIF_EXTRA_FLAG_MORE);
393 switch (ei->type)
394 {
395 case XEN_NETIF_EXTRA_TYPE_GSO:
396 switch (ei->u.gso.type)
397 {
398 case XEN_NETIF_GSO_TYPE_TCPV4:
399 xi->rxpi.mss = ei->u.gso.size;
400 // TODO - put this assertion somewhere ASSERT(header_len + xi->rxpi.mss <= PAGE_SIZE); // this limits MTU to PAGE_SIZE - XN_HEADER_LEN
401 break;
402 default:
403 KdPrint((__DRIVER_NAME " Unknown GSO type (%d) detected\n", ei->u.gso.type));
404 break;
405 }
406 break;
407 default:
408 KdPrint((__DRIVER_NAME " Unknown extra info type (%d) detected\n", ei->type));
409 break;
410 }
411 }
412 else
413 {
414 rxrsp = RING_GET_RESPONSE(&xi->rx, cons);
415 if (rxrsp->status <= 0
416 || rxrsp->offset + rxrsp->status > PAGE_SIZE)
417 {
418 KdPrint((__DRIVER_NAME ": Error: rxrsp offset %d, size %d\n",
419 rxrsp->offset, rxrsp->status));
420 ASSERT(!xi->rxpi.extra_info);
421 XenFreelist_PutPage(&xi->rx_freelist, mdl);
422 continue;
423 }
424 ASSERT(rxrsp->id == id);
425 if (!xi->rxpi.more_frags) // handling the packet's 1st buffer
426 {
427 if (rxrsp->flags & NETRXF_csum_blank)
428 xi->rxpi.csum_calc_required = TRUE;
429 }
430 NdisAdjustBufferLength(mdl, rxrsp->status);
431 xi->rxpi.mdls[xi->rxpi.mdl_count++] = mdl;
432 xi->rxpi.extra_info = (BOOLEAN)!!(rxrsp->flags & NETRXF_extra_info);
433 xi->rxpi.more_frags = (BOOLEAN)!!(rxrsp->flags & NETRXF_more_data);
434 xi->rxpi.total_length = xi->rxpi.total_length + rxrsp->status;
435 }
437 /* Packet done, add it to the list */
438 if (!xi->rxpi.more_frags && !xi->rxpi.extra_info)
439 {
440 /* we should probably check here and make sure that we have enough
441 space for these packets, and if we don't, defer MakePackets until
442 we have Indicated the current packets... */
443 XenNet_MakePackets(xi, packets, &packet_count);
444 RtlZeroMemory(&xi->rxpi, sizeof(xi->rxpi));
445 }
446 }
447 xi->rx.rsp_cons = cons;
449 XenNet_RxBufferAlloc(xi);
451 if (packet_count > 0)
452 {
453 KeReleaseSpinLockFromDpcLevel(&xi->rx_lock);
454 NdisMIndicateReceivePacket(xi->adapter_handle, packets, packet_count);
455 KeAcquireSpinLockAtDpcLevel(&xi->rx_lock);
456 total_packets += packet_count;
457 packet_count = 0;
458 }
460 RING_FINAL_CHECK_FOR_RESPONSES(&xi->rx, moretodo);
461 } while (moretodo && total_packets < MAXIMUM_PACKETS_PER_INTERRUPT);
463 /* Give netback more buffers */
464 XenNet_RxBufferAlloc(xi);
466 if (xi->rxpi.more_frags || xi->rxpi.extra_info)
467 KdPrint(("Partial receive (more_frags = %d, extra_info = %d, total_length = %d, mdl_count = %d)\n", xi->rxpi.more_frags, xi->rxpi.extra_info, xi->rxpi.total_length, xi->rxpi.mdl_count));
469 KeReleaseSpinLockFromDpcLevel(&xi->rx_lock);
471 return NDIS_STATUS_SUCCESS;
472 }
474 /* called at DISPATCH_LEVEL */
475 /* it's okay for return packet to be called while resume_state != RUNNING as the packet will simply be added back to the freelist, the grants will be fixed later */
476 VOID DDKAPI
477 XenNet_ReturnPacket(
478 IN NDIS_HANDLE MiniportAdapterContext,
479 IN PNDIS_PACKET Packet
480 )
481 {
482 struct xennet_info *xi = MiniportAdapterContext;
483 PMDL mdl;
484 KeAcquireSpinLockAtDpcLevel(&xi->rx_lock);
486 NdisUnchainBufferAtBack(Packet, &mdl);
487 while (mdl)
488 {
489 NdisAdjustBufferLength(mdl, PAGE_SIZE);
490 XenFreelist_PutPage(&xi->rx_freelist, mdl);
491 NdisUnchainBufferAtBack(Packet, &mdl);
492 }
494 put_packet_on_freelist(xi, Packet);
495 xi->rx_outstanding--;
497 KeReleaseSpinLockFromDpcLevel(&xi->rx_lock);
499 // KdPrint((__DRIVER_NAME " <-- " __FUNCTION__ "\n"));
500 }
502 /*
503 Free all Rx buffers (on halt, for example)
504 The ring must be stopped at this point.
505 */
507 static void
508 XenNet_RxBufferFree(struct xennet_info *xi)
509 {
510 int i;
511 PMDL mdl;
513 XenFreelist_Dispose(&xi->rx_freelist);
515 ASSERT(!xi->connected);
517 for (i = 0; i < NET_RX_RING_SIZE; i++)
518 {
519 KdPrint((__DRIVER_NAME " Ring slot %d = %p\n", i, xi->rx_mdls[i]));
520 if (!xi->rx_mdls[i])
521 continue;
523 mdl = xi->rx_mdls[i];
524 NdisAdjustBufferLength(mdl, PAGE_SIZE);
525 KdPrint((__DRIVER_NAME " Calling PutPage - page_free = %d\n", xi->rx_freelist.page_free));
526 XenFreelist_PutPage(&xi->rx_freelist, mdl);
527 }
528 }
530 VOID
531 XenNet_RxResumeStart(xennet_info_t *xi)
532 {
533 int i;
534 KIRQL old_irql;
536 KeAcquireSpinLock(&xi->rx_lock, &old_irql);
537 for (i = 0; i < NET_RX_RING_SIZE; i++)
538 {
539 if (xi->rx_mdls[i])
540 {
541 XenFreelist_PutPage(&xi->rx_freelist, xi->rx_mdls[i]);
542 xi->rx_mdls[i] = NULL;
543 }
544 }
545 XenFreelist_ResumeStart(&xi->rx_freelist);
546 xi->rx_id_free = NET_RX_RING_SIZE;
547 xi->rx_outstanding = 0;
548 KeReleaseSpinLock(&xi->rx_lock, old_irql);
549 }
551 VOID
552 XenNet_RxResumeEnd(xennet_info_t *xi)
553 {
554 KIRQL old_irql;
556 KeAcquireSpinLock(&xi->rx_lock, &old_irql);
557 XenFreelist_ResumeEnd(&xi->rx_freelist);
558 XenNet_RxBufferAlloc(xi);
559 KeReleaseSpinLock(&xi->rx_lock, old_irql);
560 }
562 BOOLEAN
563 XenNet_RxInit(xennet_info_t *xi)
564 {
565 int i;
567 KdPrint((__DRIVER_NAME " --> " __FUNCTION__ "\n"));
569 xi->rx_id_free = NET_RX_RING_SIZE;
571 for (i = 0; i < NET_RX_RING_SIZE; i++)
572 {
573 xi->rx_mdls[i] = NULL;
574 }
576 xi->rx_outstanding = 0;
578 XenFreelist_Init(xi, &xi->rx_freelist, &xi->rx_lock);
580 XenNet_RxBufferAlloc(xi);
582 KdPrint((__DRIVER_NAME " <-- " __FUNCTION__ "\n"));
584 return TRUE;
585 }
587 BOOLEAN
588 XenNet_RxShutdown(xennet_info_t *xi)
589 {
590 KIRQL OldIrql;
592 KdPrint((__DRIVER_NAME " --> " __FUNCTION__ "\n"));
594 KeAcquireSpinLock(&xi->rx_lock, &OldIrql);
596 XenNet_RxBufferFree(xi);
598 XenFreelist_Dispose(&xi->rx_freelist);
600 packet_freelist_dispose(xi);
602 /* free RX resources */
604 ASSERT(xi->rx_outstanding == 0);
606 KeReleaseSpinLock(&xi->rx_lock, OldIrql);
608 KdPrint((__DRIVER_NAME " <-- " __FUNCTION__ "\n"));
610 return TRUE;
611 }