| @@ -1,14 +1,14 @@ | | | @@ -1,14 +1,14 @@ |
1 | /* $NetBSD: xennetback_xenbus.c,v 1.24 2008/10/24 18:02:58 jym Exp $ */ | | 1 | /* $NetBSD: xennetback_xenbus.c,v 1.24.4.1 2009/09/28 01:46:48 snj Exp $ */ |
2 | | | 2 | |
3 | /* | | 3 | /* |
4 | * Copyright (c) 2006 Manuel Bouyer. | | 4 | * Copyright (c) 2006 Manuel Bouyer. |
5 | * | | 5 | * |
6 | * Redistribution and use in source and binary forms, with or without | | 6 | * Redistribution and use in source and binary forms, with or without |
7 | * modification, are permitted provided that the following conditions | | 7 | * modification, are permitted provided that the following conditions |
8 | * are met: | | 8 | * are met: |
9 | * 1. Redistributions of source code must retain the above copyright | | 9 | * 1. Redistributions of source code must retain the above copyright |
10 | * notice, this list of conditions and the following disclaimer. | | 10 | * notice, this list of conditions and the following disclaimer. |
11 | * 2. Redistributions in binary form must reproduce the above copyright | | 11 | * 2. Redistributions in binary form must reproduce the above copyright |
12 | * notice, this list of conditions and the following disclaimer in the | | 12 | * notice, this list of conditions and the following disclaimer in the |
13 | * documentation and/or other materials provided with the distribution. | | 13 | * documentation and/or other materials provided with the distribution. |
14 | * 3. All advertising materials mentioning features or use of this software | | 14 | * 3. All advertising materials mentioning features or use of this software |
| @@ -121,27 +121,28 @@ struct xnetback_instance { | | | @@ -121,27 +121,28 @@ struct xnetback_instance { |
121 | netif_tx_back_ring_t xni_txring; | | 121 | netif_tx_back_ring_t xni_txring; |
122 | netif_rx_back_ring_t xni_rxring; | | 122 | netif_rx_back_ring_t xni_rxring; |
123 | grant_handle_t xni_tx_ring_handle; /* to unmap the ring */ | | 123 | grant_handle_t xni_tx_ring_handle; /* to unmap the ring */ |
124 | grant_handle_t xni_rx_ring_handle; | | 124 | grant_handle_t xni_rx_ring_handle; |
125 | vaddr_t xni_tx_ring_va; /* to unmap the ring */ | | 125 | vaddr_t xni_tx_ring_va; /* to unmap the ring */ |
126 | vaddr_t xni_rx_ring_va; | | 126 | vaddr_t xni_rx_ring_va; |
127 | }; | | 127 | }; |
128 | #define xni_if xni_ec.ec_if | | 128 | #define xni_if xni_ec.ec_if |
129 | #define xni_bpf xni_if.if_bpf | | 129 | #define xni_bpf xni_if.if_bpf |
130 | | | 130 | |
131 | void xvifattach(int); | | 131 | void xvifattach(int); |
132 | static int xennetback_ifioctl(struct ifnet *, u_long, void *); | | 132 | static int xennetback_ifioctl(struct ifnet *, u_long, void *); |
133 | static void xennetback_ifstart(struct ifnet *); | | 133 | static void xennetback_ifstart(struct ifnet *); |
134 | static void xennetback_ifsoftstart(void *); | | 134 | static void xennetback_ifsoftstart_transfer(void *); |
| | | 135 | static void xennetback_ifsoftstart_copy(void *); |
135 | static void xennetback_ifwatchdog(struct ifnet *); | | 136 | static void xennetback_ifwatchdog(struct ifnet *); |
136 | static int xennetback_ifinit(struct ifnet *); | | 137 | static int xennetback_ifinit(struct ifnet *); |
137 | static void xennetback_ifstop(struct ifnet *, int); | | 138 | static void xennetback_ifstop(struct ifnet *, int); |
138 | | | 139 | |
139 | static int xennetback_xenbus_create(struct xenbus_device *); | | 140 | static int xennetback_xenbus_create(struct xenbus_device *); |
140 | static int xennetback_xenbus_destroy(void *); | | 141 | static int xennetback_xenbus_destroy(void *); |
141 | static void xennetback_frontend_changed(void *, XenbusState); | | 142 | static void xennetback_frontend_changed(void *, XenbusState); |
142 | | | 143 | |
143 | static inline void xennetback_tx_response(struct xnetback_instance *, | | 144 | static inline void xennetback_tx_response(struct xnetback_instance *, |
144 | int, int); | | 145 | int, int); |
145 | static void xennetback_tx_free(struct mbuf * , void *, size_t, void *); | | 146 | static void xennetback_tx_free(struct mbuf * , void *, size_t, void *); |
146 | | | 147 | |
147 | SLIST_HEAD(, xnetback_instance) xnetback_instances; | | 148 | SLIST_HEAD(, xnetback_instance) xnetback_instances; |
| @@ -172,27 +173,28 @@ static void xennetback_get_new_mcl_pages | | | @@ -172,27 +173,28 @@ static void xennetback_get_new_mcl_pages |
172 | /* | | 173 | /* |
173 | * If we can't transfer the mbuf directly, we have to copy it to a page which | | 174 | * If we can't transfer the mbuf directly, we have to copy it to a page which |
174 | * will be transfered to the remote domain. We use a pool_cache | | 175 | * will be transfered to the remote domain. We use a pool_cache |
175 | * for this, or the mbuf cluster pool cache if MCLBYTES == PAGE_SIZE | | 176 | * for this, or the mbuf cluster pool cache if MCLBYTES == PAGE_SIZE |
176 | */ | | 177 | */ |
177 | #if MCLBYTES != PAGE_SIZE | | 178 | #if MCLBYTES != PAGE_SIZE |
178 | pool_cache_t xmit_pages_cache; | | 179 | pool_cache_t xmit_pages_cache; |
179 | #endif | | 180 | #endif |
180 | pool_cache_t xmit_pages_cachep; | | 181 | pool_cache_t xmit_pages_cachep; |
181 | | | 182 | |
182 | /* arrays used in xennetback_ifstart(), too large to allocate on stack */ | | 183 | /* arrays used in xennetback_ifstart(), too large to allocate on stack */ |
183 | static mmu_update_t xstart_mmu[NB_XMIT_PAGES_BATCH]; | | 184 | static mmu_update_t xstart_mmu[NB_XMIT_PAGES_BATCH]; |
184 | static multicall_entry_t xstart_mcl[NB_XMIT_PAGES_BATCH + 1]; | | 185 | static multicall_entry_t xstart_mcl[NB_XMIT_PAGES_BATCH + 1]; |
185 | static gnttab_transfer_t xstart_gop[NB_XMIT_PAGES_BATCH]; | | 186 | static gnttab_transfer_t xstart_gop_transfer[NB_XMIT_PAGES_BATCH]; |
| | | 187 | static gnttab_copy_t xstart_gop_copy[NB_XMIT_PAGES_BATCH]; |
186 | struct mbuf *mbufs_sent[NB_XMIT_PAGES_BATCH]; | | 188 | struct mbuf *mbufs_sent[NB_XMIT_PAGES_BATCH]; |
187 | struct _pages_pool_free { | | 189 | struct _pages_pool_free { |
188 | vaddr_t va; | | 190 | vaddr_t va; |
189 | paddr_t pa; | | 191 | paddr_t pa; |
190 | } pages_pool_free[NB_XMIT_PAGES_BATCH]; | | 192 | } pages_pool_free[NB_XMIT_PAGES_BATCH]; |
191 | | | 193 | |
192 | | | 194 | |
193 | static inline void | | 195 | static inline void |
194 | xni_pkt_unmap(struct xni_pkt *pkt, vaddr_t pkt_va) | | 196 | xni_pkt_unmap(struct xni_pkt *pkt, vaddr_t pkt_va) |
195 | { | | 197 | { |
196 | xen_shm_unmap(pkt_va, 1, &pkt->pkt_handle); | | 198 | xen_shm_unmap(pkt_va, 1, &pkt->pkt_handle); |
197 | pool_put(&xni_pkt_pool, pkt); | | 199 | pool_put(&xni_pkt_pool, pkt); |
198 | } | | 200 | } |
| @@ -234,26 +236,27 @@ xvifattach(int n) | | | @@ -234,26 +236,27 @@ xvifattach(int n) |
234 | SLIST_INIT(&xnetback_instances); | | 236 | SLIST_INIT(&xnetback_instances); |
235 | xenbus_backend_register(&xvif_backend_driver); | | 237 | xenbus_backend_register(&xvif_backend_driver); |
236 | } | | 238 | } |
237 | | | 239 | |
238 | static int | | 240 | static int |
239 | xennetback_xenbus_create(struct xenbus_device *xbusd) | | 241 | xennetback_xenbus_create(struct xenbus_device *xbusd) |
240 | { | | 242 | { |
241 | struct xnetback_instance *xneti; | | 243 | struct xnetback_instance *xneti; |
242 | long domid, handle; | | 244 | long domid, handle; |
243 | struct ifnet *ifp; | | 245 | struct ifnet *ifp; |
244 | extern int ifqmaxlen; /* XXX */ | | 246 | extern int ifqmaxlen; /* XXX */ |
245 | char *val, *e, *p; | | 247 | char *val, *e, *p; |
246 | int i, err; | | 248 | int i, err; |
| | | 249 | struct xenbus_transaction *xbt; |
247 | | | 250 | |
248 | if ((err = xenbus_read_ul(NULL, xbusd->xbusd_path, | | 251 | if ((err = xenbus_read_ul(NULL, xbusd->xbusd_path, |
249 | "frontend-id", &domid, 10)) != 0) { | | 252 | "frontend-id", &domid, 10)) != 0) { |
250 | aprint_error("xvif: can' read %s/frontend-id: %d\n", | | 253 | aprint_error("xvif: can' read %s/frontend-id: %d\n", |
251 | xbusd->xbusd_path, err); | | 254 | xbusd->xbusd_path, err); |
252 | return err; | | 255 | return err; |
253 | } | | 256 | } |
254 | if ((err = xenbus_read_ul(NULL, xbusd->xbusd_path, | | 257 | if ((err = xenbus_read_ul(NULL, xbusd->xbusd_path, |
255 | "handle", &handle, 10)) != 0) { | | 258 | "handle", &handle, 10)) != 0) { |
256 | aprint_error("xvif: can' read %s/handle: %d\n", | | 259 | aprint_error("xvif: can' read %s/handle: %d\n", |
257 | xbusd->xbusd_path, err); | | 260 | xbusd->xbusd_path, err); |
258 | return err; | | 261 | return err; |
259 | } | | 262 | } |
| @@ -264,33 +267,26 @@ xennetback_xenbus_create(struct xenbus_d | | | @@ -264,33 +267,26 @@ xennetback_xenbus_create(struct xenbus_d |
264 | xneti = malloc(sizeof(struct xnetback_instance), M_DEVBUF, | | 267 | xneti = malloc(sizeof(struct xnetback_instance), M_DEVBUF, |
265 | M_NOWAIT | M_ZERO); | | 268 | M_NOWAIT | M_ZERO); |
266 | if (xneti == NULL) { | | 269 | if (xneti == NULL) { |
267 | return ENOMEM; | | 270 | return ENOMEM; |
268 | } | | 271 | } |
269 | xneti->xni_domid = domid; | | 272 | xneti->xni_domid = domid; |
270 | xneti->xni_handle = handle; | | 273 | xneti->xni_handle = handle; |
271 | xneti->xni_status = DISCONNECTED; | | 274 | xneti->xni_status = DISCONNECTED; |
272 | | | 275 | |
273 | xbusd->xbusd_u.b.b_cookie = xneti; | | 276 | xbusd->xbusd_u.b.b_cookie = xneti; |
274 | xbusd->xbusd_u.b.b_detach = xennetback_xenbus_destroy; | | 277 | xbusd->xbusd_u.b.b_detach = xennetback_xenbus_destroy; |
275 | xneti->xni_xbusd = xbusd; | | 278 | xneti->xni_xbusd = xbusd; |
276 | | | 279 | |
277 | xneti->xni_softintr = softint_establish(SOFTINT_NET, | | | |
278 | xennetback_ifsoftstart, xneti); | | | |
279 | if (xneti->xni_softintr == NULL) { | | | |
280 | err = ENOMEM; | | | |
281 | goto fail; | | | |
282 | } | | | |
283 | | | | |
284 | ifp = &xneti->xni_if; | | 280 | ifp = &xneti->xni_if; |
285 | ifp->if_softc = xneti; | | 281 | ifp->if_softc = xneti; |
286 | | | 282 | |
287 | /* read mac address */ | | 283 | /* read mac address */ |
288 | if ((err = xenbus_read(NULL, xbusd->xbusd_path, "mac", NULL, &val))) { | | 284 | if ((err = xenbus_read(NULL, xbusd->xbusd_path, "mac", NULL, &val))) { |
289 | aprint_error("xvif: can' read %s/mac: %d\n", | | 285 | aprint_error("xvif: can' read %s/mac: %d\n", |
290 | xbusd->xbusd_path, err); | | 286 | xbusd->xbusd_path, err); |
291 | goto fail; | | 287 | goto fail; |
292 | } | | 288 | } |
293 | for (i = 0, p = val; i < 6; i++) { | | 289 | for (i = 0, p = val; i < 6; i++) { |
294 | xneti->xni_enaddr[i] = strtoul(p, &e, 16); | | 290 | xneti->xni_enaddr[i] = strtoul(p, &e, 16); |
295 | if ((e[0] == '\0' && i != 5) && e[0] != ':') { | | 291 | if ((e[0] == '\0' && i != 5) && e[0] != ':') { |
296 | aprint_error("xvif: %s is not a valid mac address\n", | | 292 | aprint_error("xvif: %s is not a valid mac address\n", |
| @@ -318,38 +314,67 @@ xennetback_xenbus_create(struct xenbus_d | | | @@ -318,38 +314,67 @@ xennetback_xenbus_create(struct xenbus_d |
318 | ifp->if_start = xennetback_ifstart; | | 314 | ifp->if_start = xennetback_ifstart; |
319 | ifp->if_watchdog = xennetback_ifwatchdog; | | 315 | ifp->if_watchdog = xennetback_ifwatchdog; |
320 | ifp->if_init = xennetback_ifinit; | | 316 | ifp->if_init = xennetback_ifinit; |
321 | ifp->if_stop = xennetback_ifstop; | | 317 | ifp->if_stop = xennetback_ifstop; |
322 | ifp->if_timer = 0; | | 318 | ifp->if_timer = 0; |
323 | IFQ_SET_READY(&ifp->if_snd); | | 319 | IFQ_SET_READY(&ifp->if_snd); |
324 | if_attach(ifp); | | 320 | if_attach(ifp); |
325 | ether_ifattach(&xneti->xni_if, xneti->xni_enaddr); | | 321 | ether_ifattach(&xneti->xni_if, xneti->xni_enaddr); |
326 | | | 322 | |
327 | SLIST_INSERT_HEAD(&xnetback_instances, xneti, next); | | 323 | SLIST_INSERT_HEAD(&xnetback_instances, xneti, next); |
328 | | | 324 | |
329 | xbusd->xbusd_otherend_changed = xennetback_frontend_changed; | | 325 | xbusd->xbusd_otherend_changed = xennetback_frontend_changed; |
330 | | | 326 | |
| | | 327 | do { |
| | | 328 | xbt = xenbus_transaction_start(); |
| | | 329 | if (xbt == NULL) { |
| | | 330 | printf("xbdback %s: can't start transaction\n", |
| | | 331 | xbusd->xbusd_path); |
| | | 332 | goto fail; |
| | | 333 | } |
| | | 334 | err = xenbus_printf(xbt, xbusd->xbusd_path, |
| | | 335 | "feature-rx-copy", "%d", 1); |
| | | 336 | if (err) { |
| | | 337 | printf("xbdback: failed to write %s/feature-rx-copy: " |
| | | 338 | "%d\n", xbusd->xbusd_path, err); |
| | | 339 | goto abort_xbt; |
| | | 340 | } |
| | | 341 | err = xenbus_printf(xbt, xbusd->xbusd_path, |
| | | 342 | "feature-rx-flip", "%d", 1); |
| | | 343 | if (err) { |
| | | 344 | printf("xbdback: failed to write %s/feature-rx-flip: " |
| | | 345 | "%d\n", xbusd->xbusd_path, err); |
| | | 346 | goto abort_xbt; |
| | | 347 | } |
| | | 348 | } while ((err = xenbus_transaction_end(xbt, 0)) == EAGAIN); |
| | | 349 | if (err) { |
| | | 350 | printf("xbdback %s: can't end transaction: %d\n", |
| | | 351 | xbusd->xbusd_path, err); |
| | | 352 | } |
| | | 353 | |
331 | err = xenbus_switch_state(xbusd, NULL, XenbusStateInitWait); | | 354 | err = xenbus_switch_state(xbusd, NULL, XenbusStateInitWait); |
332 | if (err) { | | 355 | if (err) { |
333 | printf("failed to switch state on %s: %d\n", | | 356 | printf("failed to switch state on %s: %d\n", |
334 | xbusd->xbusd_path, err); | | 357 | xbusd->xbusd_path, err); |
335 | goto fail; | | 358 | goto fail; |
336 | } | | 359 | } |
337 | if (err) { | | 360 | if (err) { |
338 | printf("failed to write %s/hotplug-status: %d\n", | | 361 | printf("failed to write %s/hotplug-status: %d\n", |
339 | xbusd->xbusd_path, err); | | 362 | xbusd->xbusd_path, err); |
340 | goto fail; | | 363 | goto fail; |
341 | } | | 364 | } |
342 | return 0; | | 365 | return 0; |
| | | 366 | abort_xbt: |
| | | 367 | xenbus_transaction_end(xbt, 1); |
343 | fail: | | 368 | fail: |
344 | free(xneti, M_DEVBUF); | | 369 | free(xneti, M_DEVBUF); |
345 | return err; | | 370 | return err; |
346 | } | | 371 | } |
347 | | | 372 | |
348 | int | | 373 | int |
349 | xennetback_xenbus_destroy(void *arg) | | 374 | xennetback_xenbus_destroy(void *arg) |
350 | { | | 375 | { |
351 | struct xnetback_instance *xneti = arg; | | 376 | struct xnetback_instance *xneti = arg; |
352 | struct gnttab_unmap_grant_ref op; | | 377 | struct gnttab_unmap_grant_ref op; |
353 | int err; | | 378 | int err; |
354 | | | 379 | |
355 | #if 0 | | 380 | #if 0 |
| @@ -397,27 +422,27 @@ xennetback_xenbus_destroy(void *arg) | | | @@ -397,27 +422,27 @@ xennetback_xenbus_destroy(void *arg) |
397 | } | | 422 | } |
398 | | | 423 | |
399 | static void | | 424 | static void |
400 | xennetback_frontend_changed(void *arg, XenbusState new_state) | | 425 | xennetback_frontend_changed(void *arg, XenbusState new_state) |
401 | { | | 426 | { |
402 | struct xnetback_instance *xneti = arg; | | 427 | struct xnetback_instance *xneti = arg; |
403 | struct xenbus_device *xbusd = xneti->xni_xbusd; | | 428 | struct xenbus_device *xbusd = xneti->xni_xbusd; |
404 | int err; | | 429 | int err; |
405 | netif_tx_sring_t *tx_ring; | | 430 | netif_tx_sring_t *tx_ring; |
406 | netif_rx_sring_t *rx_ring; | | 431 | netif_rx_sring_t *rx_ring; |
407 | struct gnttab_map_grant_ref op; | | 432 | struct gnttab_map_grant_ref op; |
408 | evtchn_op_t evop; | | 433 | evtchn_op_t evop; |
409 | u_long tx_ring_ref, rx_ring_ref; | | 434 | u_long tx_ring_ref, rx_ring_ref; |
410 | u_long revtchn; | | 435 | u_long revtchn, rx_copy; |
411 | | | 436 | |
412 | XENPRINTF(("%s: new state %d\n", xneti->xni_if.if_xname, new_state)); | | 437 | XENPRINTF(("%s: new state %d\n", xneti->xni_if.if_xname, new_state)); |
413 | switch(new_state) { | | 438 | switch(new_state) { |
414 | case XenbusStateInitialising: | | 439 | case XenbusStateInitialising: |
415 | case XenbusStateInitialised: | | 440 | case XenbusStateInitialised: |
416 | break; | | 441 | break; |
417 | | | 442 | |
418 | case XenbusStateConnected: | | 443 | case XenbusStateConnected: |
419 | /* read comunication informations */ | | 444 | /* read comunication informations */ |
420 | err = xenbus_read_ul(NULL, xbusd->xbusd_otherend, | | 445 | err = xenbus_read_ul(NULL, xbusd->xbusd_otherend, |
421 | "tx-ring-ref", &tx_ring_ref, 10); | | 446 | "tx-ring-ref", &tx_ring_ref, 10); |
422 | if (err) { | | 447 | if (err) { |
423 | xenbus_dev_fatal(xbusd, err, "reading %s/tx-ring-ref", | | 448 | xenbus_dev_fatal(xbusd, err, "reading %s/tx-ring-ref", |
| @@ -428,26 +453,49 @@ xennetback_frontend_changed(void *arg, X | | | @@ -428,26 +453,49 @@ xennetback_frontend_changed(void *arg, X |
428 | "rx-ring-ref", &rx_ring_ref, 10); | | 453 | "rx-ring-ref", &rx_ring_ref, 10); |
429 | if (err) { | | 454 | if (err) { |
430 | xenbus_dev_fatal(xbusd, err, "reading %s/rx-ring-ref", | | 455 | xenbus_dev_fatal(xbusd, err, "reading %s/rx-ring-ref", |
431 | xbusd->xbusd_otherend); | | 456 | xbusd->xbusd_otherend); |
432 | break; | | 457 | break; |
433 | } | | 458 | } |
434 | err = xenbus_read_ul(NULL, xbusd->xbusd_otherend, | | 459 | err = xenbus_read_ul(NULL, xbusd->xbusd_otherend, |
435 | "event-channel", &revtchn, 10); | | 460 | "event-channel", &revtchn, 10); |
436 | if (err) { | | 461 | if (err) { |
437 | xenbus_dev_fatal(xbusd, err, "reading %s/event-channel", | | 462 | xenbus_dev_fatal(xbusd, err, "reading %s/event-channel", |
438 | xbusd->xbusd_otherend); | | 463 | xbusd->xbusd_otherend); |
439 | break; | | 464 | break; |
440 | } | | 465 | } |
| | | 466 | err = xenbus_read_ul(NULL, xbusd->xbusd_otherend, |
| | | 467 | "request-rx-copy", &rx_copy, 10); |
| | | 468 | if (err == ENOENT) |
| | | 469 | rx_copy = 0; |
| | | 470 | else if (err) { |
| | | 471 | xenbus_dev_fatal(xbusd, err, "reading %s/request-rx-copy", |
| | | 472 | xbusd->xbusd_otherend); |
| | | 473 | break; |
| | | 474 | } |
| | | 475 | |
| | | 476 | if (rx_copy) |
| | | 477 | xneti->xni_softintr = softint_establish(SOFTINT_NET, |
| | | 478 | xennetback_ifsoftstart_copy, xneti); |
| | | 479 | else |
| | | 480 | xneti->xni_softintr = softint_establish(SOFTINT_NET, |
| | | 481 | xennetback_ifsoftstart_transfer, xneti); |
| | | 482 | if (xneti->xni_softintr == NULL) { |
| | | 483 | err = ENOMEM; |
| | | 484 | xenbus_dev_fatal(xbusd, ENOMEM, |
| | | 485 | "can't allocate softint", xbusd->xbusd_otherend); |
| | | 486 | break; |
| | | 487 | } |
| | | 488 | |
441 | /* allocate VA space and map rings */ | | 489 | /* allocate VA space and map rings */ |
442 | xneti->xni_tx_ring_va = uvm_km_alloc(kernel_map, PAGE_SIZE, 0, | | 490 | xneti->xni_tx_ring_va = uvm_km_alloc(kernel_map, PAGE_SIZE, 0, |
443 | UVM_KMF_VAONLY); | | 491 | UVM_KMF_VAONLY); |
444 | if (xneti->xni_tx_ring_va == 0) { | | 492 | if (xneti->xni_tx_ring_va == 0) { |
445 | xenbus_dev_fatal(xbusd, ENOMEM, | | 493 | xenbus_dev_fatal(xbusd, ENOMEM, |
446 | "can't get VA for tx ring", xbusd->xbusd_otherend); | | 494 | "can't get VA for tx ring", xbusd->xbusd_otherend); |
447 | break; | | 495 | break; |
448 | } | | 496 | } |
449 | tx_ring = (void *)xneti->xni_tx_ring_va; | | 497 | tx_ring = (void *)xneti->xni_tx_ring_va; |
450 | xneti->xni_rx_ring_va = uvm_km_alloc(kernel_map, PAGE_SIZE, 0, | | 498 | xneti->xni_rx_ring_va = uvm_km_alloc(kernel_map, PAGE_SIZE, 0, |
451 | UVM_KMF_VAONLY); | | 499 | UVM_KMF_VAONLY); |
452 | if (xneti->xni_rx_ring_va == 0) { | | 500 | if (xneti->xni_rx_ring_va == 0) { |
453 | xenbus_dev_fatal(xbusd, ENOMEM, | | 501 | xenbus_dev_fatal(xbusd, ENOMEM, |
| @@ -820,67 +868,67 @@ xennetback_ifioctl(struct ifnet *ifp, u_ | | | @@ -820,67 +868,67 @@ xennetback_ifioctl(struct ifnet *ifp, u_ |
820 | return error; | | 868 | return error; |
821 | } | | 869 | } |
822 | | | 870 | |
823 | static void | | 871 | static void |
824 | xennetback_ifstart(struct ifnet *ifp) | | 872 | xennetback_ifstart(struct ifnet *ifp) |
825 | { | | 873 | { |
826 | struct xnetback_instance *xneti = ifp->if_softc; | | 874 | struct xnetback_instance *xneti = ifp->if_softc; |
827 | | | 875 | |
828 | /* | | 876 | /* |
829 | * The Xen communication channel is much more efficient if we can | | 877 | * The Xen communication channel is much more efficient if we can |
830 | * schedule batch of packets for the domain. To achieve this, we | | 878 | * schedule batch of packets for the domain. To achieve this, we |
831 | * schedule a soft interrupt, and just return. This way, the network | | 879 | * schedule a soft interrupt, and just return. This way, the network |
832 | * stack will enqueue all pending mbufs in the interface's send queue | | 880 | * stack will enqueue all pending mbufs in the interface's send queue |
833 | * before it is processed by xennet_softstart(). | | 881 | * before it is processed by the soft inetrrupt handler(). |
834 | */ | | 882 | */ |
835 | softint_schedule(xneti->xni_softintr); | | 883 | softint_schedule(xneti->xni_softintr); |
836 | } | | 884 | } |
837 | | | 885 | |
838 | static void | | 886 | static void |
839 | xennetback_ifsoftstart(void *arg) | | 887 | xennetback_ifsoftstart_transfer(void *arg) |
840 | { | | 888 | { |
841 | struct xnetback_instance *xneti = arg; | | 889 | struct xnetback_instance *xneti = arg; |
842 | struct ifnet *ifp = &xneti->xni_if; | | 890 | struct ifnet *ifp = &xneti->xni_if; |
843 | struct mbuf *m; | | 891 | struct mbuf *m; |
844 | vaddr_t xmit_va; | | 892 | vaddr_t xmit_va; |
845 | paddr_t xmit_pa; | | 893 | paddr_t xmit_pa; |
846 | paddr_t xmit_ma; | | 894 | paddr_t xmit_ma; |
847 | paddr_t newp_ma = 0; /* XXX gcc */ | | 895 | paddr_t newp_ma = 0; /* XXX gcc */ |
848 | int i, j, nppitems; | | 896 | int i, j, nppitems; |
849 | mmu_update_t *mmup; | | 897 | mmu_update_t *mmup; |
850 | multicall_entry_t *mclp; | | 898 | multicall_entry_t *mclp; |
851 | netif_rx_response_t *rxresp; | | 899 | netif_rx_response_t *rxresp; |
852 | RING_IDX req_prod, resp_prod; | | 900 | RING_IDX req_prod, resp_prod; |
853 | int do_event = 0; | | 901 | int do_event = 0; |
854 | gnttab_transfer_t *gop; | | 902 | gnttab_transfer_t *gop; |
855 | int id, offset; | | 903 | int id, offset; |
856 | | | 904 | |
857 | XENPRINTF(("xennetback_ifsoftstart ")); | | 905 | XENPRINTF(("xennetback_ifsoftstart_transfer ")); |
858 | int s = splnet(); | | 906 | int s = splnet(); |
859 | if (__predict_false( | | 907 | if (__predict_false( |
860 | (ifp->if_flags & (IFF_RUNNING|IFF_OACTIVE)) != IFF_RUNNING)) { | | 908 | (ifp->if_flags & (IFF_RUNNING|IFF_OACTIVE)) != IFF_RUNNING)) { |
861 | splx(s); | | 909 | splx(s); |
862 | return; | | 910 | return; |
863 | } | | 911 | } |
864 | | | 912 | |
865 | while (!IFQ_IS_EMPTY(&ifp->if_snd)) { | | 913 | while (!IFQ_IS_EMPTY(&ifp->if_snd)) { |
866 | XENPRINTF(("pkt\n")); | | 914 | XENPRINTF(("pkt\n")); |
867 | req_prod = xneti->xni_rxring.sring->req_prod; | | 915 | req_prod = xneti->xni_rxring.sring->req_prod; |
868 | resp_prod = xneti->xni_rxring.rsp_prod_pvt; | | 916 | resp_prod = xneti->xni_rxring.rsp_prod_pvt; |
869 | x86_lfence(); | | 917 | x86_lfence(); |
870 | | | 918 | |
871 | mmup = xstart_mmu; | | 919 | mmup = xstart_mmu; |
872 | mclp = xstart_mcl; | | 920 | mclp = xstart_mcl; |
873 | gop = xstart_gop; | | 921 | gop = xstart_gop_transfer; |
874 | for (nppitems = 0, i = 0; !IFQ_IS_EMPTY(&ifp->if_snd);) { | | 922 | for (nppitems = 0, i = 0; !IFQ_IS_EMPTY(&ifp->if_snd);) { |
875 | XENPRINTF(("have a packet\n")); | | 923 | XENPRINTF(("have a packet\n")); |
876 | IFQ_POLL(&ifp->if_snd, m); | | 924 | IFQ_POLL(&ifp->if_snd, m); |
877 | if (__predict_false(m == NULL)) | | 925 | if (__predict_false(m == NULL)) |
878 | panic("xennetback_ifstart: IFQ_POLL"); | | 926 | panic("xennetback_ifstart: IFQ_POLL"); |
879 | if (__predict_false( | | 927 | if (__predict_false( |
880 | req_prod == xneti->xni_rxring.req_cons || | | 928 | req_prod == xneti->xni_rxring.req_cons || |
881 | xneti->xni_rxring.req_cons - resp_prod == | | 929 | xneti->xni_rxring.req_cons - resp_prod == |
882 | NET_RX_RING_SIZE)) { | | 930 | NET_RX_RING_SIZE)) { |
883 | /* out of ring space */ | | 931 | /* out of ring space */ |
884 | XENPRINTF(("xennetback_ifstart: ring full " | | 932 | XENPRINTF(("xennetback_ifstart: ring full " |
885 | "req_prod 0x%x req_cons 0x%x resp_prod " | | 933 | "req_prod 0x%x req_cons 0x%x resp_prod " |
886 | "0x%x\n", | | 934 | "0x%x\n", |
| @@ -999,36 +1047,36 @@ xennetback_ifsoftstart(void *arg) | | | @@ -999,36 +1047,36 @@ xennetback_ifsoftstart(void *arg) |
999 | j, xstart_mcl[j].result); | | 1047 | j, xstart_mcl[j].result); |
1000 | printf("%s: req_prod %u req_cons " | | 1048 | printf("%s: req_prod %u req_cons " |
1001 | "%u rsp_prod %u rsp_prod_pvt %u " | | 1049 | "%u rsp_prod %u rsp_prod_pvt %u " |
1002 | "i %u\n", | | 1050 | "i %u\n", |
1003 | ifp->if_xname, | | 1051 | ifp->if_xname, |
1004 | xneti->xni_rxring.sring->req_prod, | | 1052 | xneti->xni_rxring.sring->req_prod, |
1005 | xneti->xni_rxring.req_cons, | | 1053 | xneti->xni_rxring.req_cons, |
1006 | xneti->xni_rxring.sring->rsp_prod, | | 1054 | xneti->xni_rxring.sring->rsp_prod, |
1007 | xneti->xni_rxring.rsp_prod_pvt, | | 1055 | xneti->xni_rxring.rsp_prod_pvt, |
1008 | i); | | 1056 | i); |
1009 | } | | 1057 | } |
1010 | } | | 1058 | } |
1011 | if (HYPERVISOR_grant_table_op(GNTTABOP_transfer, | | 1059 | if (HYPERVISOR_grant_table_op(GNTTABOP_transfer, |
1012 | xstart_gop, i) != 0) { | | 1060 | xstart_gop_transfer, i) != 0) { |
1013 | panic("%s: GNTTABOP_transfer failed", | | 1061 | panic("%s: GNTTABOP_transfer failed", |
1014 | ifp->if_xname); | | 1062 | ifp->if_xname); |
1015 | } | | 1063 | } |
1016 | | | 1064 | |
1017 | for (j = 0; j < i; j++) { | | 1065 | for (j = 0; j < i; j++) { |
1018 | if (xstart_gop[j].status != 0) { | | 1066 | if (xstart_gop_transfer[j].status != GNTST_okay) { |
1019 | printf("%s GNTTABOP_transfer[%d] %d\n", | | 1067 | printf("%s GNTTABOP_transfer[%d] %d\n", |
1020 | ifp->if_xname, | | 1068 | ifp->if_xname, |
1021 | j, xstart_gop[j].status); | | 1069 | j, xstart_gop_transfer[j].status); |
1022 | printf("%s: req_prod %u req_cons " | | 1070 | printf("%s: req_prod %u req_cons " |
1023 | "%u rsp_prod %u rsp_prod_pvt %u " | | 1071 | "%u rsp_prod %u rsp_prod_pvt %u " |
1024 | "i %d\n", | | 1072 | "i %d\n", |
1025 | ifp->if_xname, | | 1073 | ifp->if_xname, |
1026 | xneti->xni_rxring.sring->req_prod, | | 1074 | xneti->xni_rxring.sring->req_prod, |
1027 | xneti->xni_rxring.req_cons, | | 1075 | xneti->xni_rxring.req_cons, |
1028 | xneti->xni_rxring.sring->rsp_prod, | | 1076 | xneti->xni_rxring.sring->rsp_prod, |
1029 | xneti->xni_rxring.rsp_prod_pvt, | | 1077 | xneti->xni_rxring.rsp_prod_pvt, |
1030 | i); | | 1078 | i); |
1031 | rxresp = RING_GET_RESPONSE( | | 1079 | rxresp = RING_GET_RESPONSE( |
1032 | &xneti->xni_rxring, | | 1080 | &xneti->xni_rxring, |
1033 | xneti->xni_rxring.rsp_prod_pvt + j); | | 1081 | xneti->xni_rxring.rsp_prod_pvt + j); |
1034 | rxresp->status = NETIF_RSP_ERROR; | | 1082 | rxresp->status = NETIF_RSP_ERROR; |
| @@ -1079,26 +1127,243 @@ xennetback_ifsoftstart(void *arg) | | | @@ -1079,26 +1127,243 @@ xennetback_ifsoftstart(void *arg) |
1079 | * note that we don't use RING_FINAL_CHECK_FOR_REQUESTS() | | 1127 | * note that we don't use RING_FINAL_CHECK_FOR_REQUESTS() |
1080 | * here, as the frontend doesn't notify when adding | | 1128 | * here, as the frontend doesn't notify when adding |
1081 | * requests anyway | | 1129 | * requests anyway |
1082 | */ | | 1130 | */ |
1083 | if (__predict_false( | | 1131 | if (__predict_false( |
1084 | !RING_HAS_UNCONSUMED_REQUESTS(&xneti->xni_rxring))) { | | 1132 | !RING_HAS_UNCONSUMED_REQUESTS(&xneti->xni_rxring))) { |
1085 | /* ring full */ | | 1133 | /* ring full */ |
1086 | break; | | 1134 | break; |
1087 | } | | 1135 | } |
1088 | } | | 1136 | } |
1089 | splx(s); | | 1137 | splx(s); |
1090 | } | | 1138 | } |
1091 | | | 1139 | |
| | | 1140 | static void |
| | | 1141 | xennetback_ifsoftstart_copy(void *arg) |
| | | 1142 | { |
| | | 1143 | struct xnetback_instance *xneti = arg; |
| | | 1144 | struct ifnet *ifp = &xneti->xni_if; |
| | | 1145 | struct mbuf *m, *new_m; |
| | | 1146 | paddr_t xmit_pa; |
| | | 1147 | paddr_t xmit_ma; |
| | | 1148 | int i, j; |
| | | 1149 | netif_rx_response_t *rxresp; |
| | | 1150 | RING_IDX req_prod, resp_prod; |
| | | 1151 | int do_event = 0; |
| | | 1152 | gnttab_copy_t *gop; |
| | | 1153 | int id, offset; |
| | | 1154 | |
| | | 1155 | XENPRINTF(("xennetback_ifsoftstart_transfer ")); |
| | | 1156 | int s = splnet(); |
| | | 1157 | if (__predict_false( |
| | | 1158 | (ifp->if_flags & (IFF_RUNNING|IFF_OACTIVE)) != IFF_RUNNING)) { |
| | | 1159 | splx(s); |
| | | 1160 | return; |
| | | 1161 | } |
| | | 1162 | |
| | | 1163 | while (!IFQ_IS_EMPTY(&ifp->if_snd)) { |
| | | 1164 | XENPRINTF(("pkt\n")); |
| | | 1165 | req_prod = xneti->xni_rxring.sring->req_prod; |
| | | 1166 | resp_prod = xneti->xni_rxring.rsp_prod_pvt; |
| | | 1167 | xen_rmb(); |
| | | 1168 | |
| | | 1169 | gop = xstart_gop_copy; |
| | | 1170 | for (i = 0; !IFQ_IS_EMPTY(&ifp->if_snd);) { |
| | | 1171 | XENPRINTF(("have a packet\n")); |
| | | 1172 | IFQ_POLL(&ifp->if_snd, m); |
| | | 1173 | if (__predict_false(m == NULL)) |
| | | 1174 | panic("xennetback_ifstart: IFQ_POLL"); |
| | | 1175 | if (__predict_false( |
| | | 1176 | req_prod == xneti->xni_rxring.req_cons || |
| | | 1177 | xneti->xni_rxring.req_cons - resp_prod == |
| | | 1178 | NET_RX_RING_SIZE)) { |
| | | 1179 | /* out of ring space */ |
| | | 1180 | XENPRINTF(("xennetback_ifstart: ring full " |
| | | 1181 | "req_prod 0x%x req_cons 0x%x resp_prod " |
| | | 1182 | "0x%x\n", |
| | | 1183 | req_prod, xneti->xni_rxring.req_cons, |
| | | 1184 | resp_prod)); |
| | | 1185 | ifp->if_timer = 1; |
| | | 1186 | break; |
| | | 1187 | } |
| | | 1188 | if (__predict_false(i == NB_XMIT_PAGES_BATCH)) |
| | | 1189 | break; /* we filled the array */ |
| | | 1190 | switch (m->m_flags & (M_EXT|M_EXT_CLUSTER)) { |
| | | 1191 | case M_EXT|M_EXT_CLUSTER: |
| | | 1192 | KASSERT(m->m_ext.ext_paddr != M_PADDR_INVALID); |
| | | 1193 | xmit_pa = m->m_ext.ext_paddr; |
| | | 1194 | offset = m->m_data - m->m_ext.ext_buf; |
| | | 1195 | break; |
| | | 1196 | case 0: |
| | | 1197 | KASSERT(m->m_paddr != M_PADDR_INVALID); |
| | | 1198 | xmit_pa = m->m_paddr; |
| | | 1199 | offset = M_BUFOFFSET(m) + |
| | | 1200 | (m->m_data - M_BUFADDR(m)); |
| | | 1201 | break; |
| | | 1202 | default: |
| | | 1203 | if (__predict_false( |
| | | 1204 | !pmap_extract(pmap_kernel(), |
| | | 1205 | (vaddr_t)m->m_data, &xmit_pa))) { |
| | | 1206 | panic("xennet_start: no pa"); |
| | | 1207 | } |
| | | 1208 | offset = 0; |
| | | 1209 | break; |
| | | 1210 | } |
| | | 1211 | offset += (xmit_pa & ~PG_FRAME); |
| | | 1212 | xmit_pa = (xmit_pa & PG_FRAME); |
| | | 1213 | if (m->m_pkthdr.len != m->m_len || |
| | | 1214 | (offset + m->m_pkthdr.len) > PAGE_SIZE) { |
| | | 1215 | MGETHDR(new_m, M_DONTWAIT, MT_DATA); |
| | | 1216 | if (__predict_false(new_m == NULL)) { |
| | | 1217 | printf("%s: cannot allocate new mbuf\n", |
| | | 1218 | ifp->if_xname); |
| | | 1219 | break; |
| | | 1220 | } |
| | | 1221 | if (m->m_pkthdr.len > MHLEN) { |
| | | 1222 | MCLGET(new_m, M_DONTWAIT); |
| | | 1223 | if (__predict_false( |
| | | 1224 | (new_m->m_flags & M_EXT) == 0)) { |
| | | 1225 | XENPRINTF(( |
| | | 1226 | "%s: no mbuf cluster\n", |
| | | 1227 | ifp->if_xname)); |
| | | 1228 | m_freem(new_m); |
| | | 1229 | break; |
| | | 1230 | } |
| | | 1231 | xmit_pa = new_m->m_ext.ext_paddr; |
| | | 1232 | offset = new_m->m_data - |
| | | 1233 | new_m->m_ext.ext_buf; |
| | | 1234 | } else { |
| | | 1235 | xmit_pa = new_m->m_paddr; |
| | | 1236 | offset = M_BUFOFFSET(new_m) + |
| | | 1237 | (new_m->m_data - M_BUFADDR(new_m)); |
| | | 1238 | } |
| | | 1239 | offset += (xmit_pa & ~PG_FRAME); |
| | | 1240 | xmit_pa = (xmit_pa & PG_FRAME); |
| | | 1241 | m_copydata(m, 0, m->m_pkthdr.len, |
| | | 1242 | mtod(new_m, void *)); |
| | | 1243 | new_m->m_len = new_m->m_pkthdr.len = |
| | | 1244 | m->m_pkthdr.len; |
| | | 1245 | IFQ_DEQUEUE(&ifp->if_snd, m); |
| | | 1246 | m_freem(m); |
| | | 1247 | m = new_m; |
| | | 1248 | } else { |
| | | 1249 | IFQ_DEQUEUE(&ifp->if_snd, m); |
| | | 1250 | } |
| | | 1251 | |
| | | 1252 | KASSERT(xmit_pa != POOL_PADDR_INVALID); |
| | | 1253 | KASSERT((offset + m->m_pkthdr.len) <= PAGE_SIZE); |
| | | 1254 | xmit_ma = xpmap_ptom(xmit_pa); |
| | | 1255 | /* start filling ring */ |
| | | 1256 | gop->flags = GNTCOPY_dest_gref; |
| | | 1257 | gop->source.offset = offset; |
| | | 1258 | gop->source.domid = DOMID_SELF; |
| | | 1259 | gop->source.u.gmfn = xmit_ma >> PAGE_SHIFT; |
| | | 1260 | |
| | | 1261 | gop->dest.u.ref = RING_GET_REQUEST(&xneti->xni_rxring, |
| | | 1262 | xneti->xni_rxring.req_cons)->gref; |
| | | 1263 | gop->dest.offset = 0; |
| | | 1264 | gop->dest.domid = xneti->xni_domid; |
| | | 1265 | |
| | | 1266 | gop->len = m->m_pkthdr.len; |
| | | 1267 | gop++; |
| | | 1268 | |
| | | 1269 | id = RING_GET_REQUEST(&xneti->xni_rxring, |
| | | 1270 | xneti->xni_rxring.req_cons)->id; |
| | | 1271 | xen_rmb(); |
| | | 1272 | xneti->xni_rxring.req_cons++; |
| | | 1273 | rxresp = RING_GET_RESPONSE(&xneti->xni_rxring, |
| | | 1274 | resp_prod); |
| | | 1275 | rxresp->id = id; |
| | | 1276 | rxresp->offset = 0; |
| | | 1277 | rxresp->status = m->m_pkthdr.len; |
| | | 1278 | if ((m->m_pkthdr.csum_flags & |
| | | 1279 | (M_CSUM_TCPv4 | M_CSUM_UDPv4)) != 0) { |
| | | 1280 | rxresp->flags = NETRXF_csum_blank; |
| | | 1281 | } else { |
| | | 1282 | rxresp->flags = 0; |
| | | 1283 | } |
| | | 1284 | |
| | | 1285 | mbufs_sent[i] = m; |
| | | 1286 | resp_prod++; |
| | | 1287 | i++; /* this packet has been queued */ |
| | | 1288 | ifp->if_opackets++; |
| | | 1289 | #if NBPFILTER > 0 |
| | | 1290 | if (ifp->if_bpf) |
| | | 1291 | bpf_mtap(ifp->if_bpf, m); |
| | | 1292 | #endif |
| | | 1293 | } |
| | | 1294 | if (i != 0) { |
| | | 1295 | if (HYPERVISOR_grant_table_op(GNTTABOP_copy, |
| | | 1296 | xstart_gop_copy, i) != 0) { |
| | | 1297 | panic("%s: GNTTABOP_copy failed", |
| | | 1298 | ifp->if_xname); |
| | | 1299 | } |
| | | 1300 | |
| | | 1301 | for (j = 0; j < i; j++) { |
| | | 1302 | if (xstart_gop_copy[j].status != GNTST_okay) { |
| | | 1303 | printf("%s GNTTABOP_copy[%d] %d\n", |
| | | 1304 | ifp->if_xname, |
| | | 1305 | j, xstart_gop_copy[j].status); |
| | | 1306 | printf("%s: req_prod %u req_cons " |
| | | 1307 | "%u rsp_prod %u rsp_prod_pvt %u " |
| | | 1308 | "i %d\n", |
| | | 1309 | ifp->if_xname, |
| | | 1310 | xneti->xni_rxring.sring->req_prod, |
| | | 1311 | xneti->xni_rxring.req_cons, |
| | | 1312 | xneti->xni_rxring.sring->rsp_prod, |
| | | 1313 | xneti->xni_rxring.rsp_prod_pvt, |
| | | 1314 | i); |
| | | 1315 | rxresp = RING_GET_RESPONSE( |
| | | 1316 | &xneti->xni_rxring, |
| | | 1317 | xneti->xni_rxring.rsp_prod_pvt + j); |
| | | 1318 | rxresp->status = NETIF_RSP_ERROR; |
| | | 1319 | } |
| | | 1320 | } |
| | | 1321 | |
| | | 1322 | /* update pointer */ |
| | | 1323 | KASSERT( |
| | | 1324 | xneti->xni_rxring.rsp_prod_pvt + i == resp_prod); |
| | | 1325 | xneti->xni_rxring.rsp_prod_pvt = resp_prod; |
| | | 1326 | RING_PUSH_RESPONSES_AND_CHECK_NOTIFY( |
| | | 1327 | &xneti->xni_rxring, j); |
| | | 1328 | if (j) |
| | | 1329 | do_event = 1; |
| | | 1330 | /* now we can free the mbufs */ |
| | | 1331 | for (j = 0; j < i; j++) { |
| | | 1332 | m_freem(mbufs_sent[j]); |
| | | 1333 | } |
| | | 1334 | } |
| | | 1335 | /* send event */ |
| | | 1336 | if (do_event) { |
| | | 1337 | xen_rmb(); |
| | | 1338 | XENPRINTF(("%s receive event\n", |
| | | 1339 | xneti->xni_if.if_xname)); |
| | | 1340 | hypervisor_notify_via_evtchn(xneti->xni_evtchn); |
| | | 1341 | do_event = 0; |
| | | 1342 | } |
| | | 1343 | /* |
| | | 1344 | * note that we don't use RING_FINAL_CHECK_FOR_REQUESTS() |
| | | 1345 | * here, as the frontend doesn't notify when adding |
| | | 1346 | * requests anyway |
| | | 1347 | */ |
| | | 1348 | if (__predict_false( |
| | | 1349 | !RING_HAS_UNCONSUMED_REQUESTS(&xneti->xni_rxring))) { |
| | | 1350 | /* ring full */ |
| | | 1351 | break; |
| | | 1352 | } |
| | | 1353 | } |
| | | 1354 | splx(s); |
| | | 1355 | } |
| | | 1356 | |
1092 | | | 1357 | |
1093 | static void | | 1358 | static void |
1094 | xennetback_ifwatchdog(struct ifnet * ifp) | | 1359 | xennetback_ifwatchdog(struct ifnet * ifp) |
1095 | { | | 1360 | { |
1096 | /* | | 1361 | /* |
1097 | * We can get to the following condition: | | 1362 | * We can get to the following condition: |
1098 | * transmit stalls because the ring is full when the ifq is full too. | | 1363 | * transmit stalls because the ring is full when the ifq is full too. |
1099 | * In this case (as, unfortunably, we don't get an interrupt from xen | | 1364 | * In this case (as, unfortunably, we don't get an interrupt from xen |
1100 | * on transmit) noting will ever call xennetback_ifstart() again. | | 1365 | * on transmit) noting will ever call xennetback_ifstart() again. |
1101 | * Here we abuse the watchdog to get out of this condition. | | 1366 | * Here we abuse the watchdog to get out of this condition. |
1102 | */ | | 1367 | */ |
1103 | XENPRINTF(("xennetback_ifwatchdog\n")); | | 1368 | XENPRINTF(("xennetback_ifwatchdog\n")); |
1104 | xennetback_ifstart(ifp); | | 1369 | xennetback_ifstart(ifp); |