xref: /qemu/hw/net/xen_nic.c (revision 940bb5fa)
1 /*
2  *  xen paravirt network card backend
3  *
4  *  (c) Gerd Hoffmann <kraxel@redhat.com>
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; under version 2 of the License.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License along
16  *  with this program; if not, see <http://www.gnu.org/licenses/>.
17  *
18  *  Contributions after 2012-01-13 are licensed under the terms of the
19  *  GNU GPL, version 2 or (at your option) any later version.
20  */
21 
22 #include "qemu/osdep.h"
23 #include "qemu/main-loop.h"
24 #include "qemu/cutils.h"
25 #include "qemu/log.h"
26 #include "qemu/qemu-print.h"
27 #include "qapi/qmp/qdict.h"
28 #include "qapi/error.h"
29 
30 #include <sys/socket.h>
31 #include <sys/ioctl.h>
32 #include <sys/wait.h>
33 
34 #include "net/net.h"
35 #include "net/checksum.h"
36 #include "net/util.h"
37 
38 #include "hw/xen/xen-backend.h"
39 #include "hw/xen/xen-bus-helper.h"
40 #include "hw/qdev-properties.h"
41 #include "hw/qdev-properties-system.h"
42 
43 #include "hw/xen/interface/io/netif.h"
44 #include "hw/xen/interface/io/xs_wire.h"
45 
46 #include "trace.h"
47 
48 /* ------------------------------------------------------------- */
49 
50 struct XenNetDev {
51     struct XenDevice      xendev;  /* must be first */
52     XenEventChannel       *event_channel;
53     int                   dev;
54     int                   tx_work;
55     unsigned int          tx_ring_ref;
56     unsigned int          rx_ring_ref;
57     struct netif_tx_sring *txs;
58     struct netif_rx_sring *rxs;
59     netif_tx_back_ring_t  tx_ring;
60     netif_rx_back_ring_t  rx_ring;
61     NICConf               conf;
62     NICState              *nic;
63 };
64 
65 typedef struct XenNetDev XenNetDev;
66 
67 #define TYPE_XEN_NET_DEVICE "xen-net-device"
68 OBJECT_DECLARE_SIMPLE_TYPE(XenNetDev, XEN_NET_DEVICE)
69 
70 /* ------------------------------------------------------------- */
71 
72 static void net_tx_response(struct XenNetDev *netdev, netif_tx_request_t *txp, int8_t st)
73 {
74     RING_IDX i = netdev->tx_ring.rsp_prod_pvt;
75     netif_tx_response_t *resp;
76     int notify;
77 
78     resp = RING_GET_RESPONSE(&netdev->tx_ring, i);
79     resp->id     = txp->id;
80     resp->status = st;
81 
82 #if 0
83     if (txp->flags & NETTXF_extra_info) {
84         RING_GET_RESPONSE(&netdev->tx_ring, ++i)->status = NETIF_RSP_NULL;
85     }
86 #endif
87 
88     netdev->tx_ring.rsp_prod_pvt = ++i;
89     RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&netdev->tx_ring, notify);
90     if (notify) {
91         xen_device_notify_event_channel(XEN_DEVICE(netdev),
92                                         netdev->event_channel, NULL);
93     }
94 
95     if (i == netdev->tx_ring.req_cons) {
96         int more_to_do;
97         RING_FINAL_CHECK_FOR_REQUESTS(&netdev->tx_ring, more_to_do);
98         if (more_to_do) {
99             netdev->tx_work++;
100         }
101     }
102 }
103 
104 static void net_tx_error(struct XenNetDev *netdev, netif_tx_request_t *txp, RING_IDX end)
105 {
106 #if 0
107     /*
108      * Hmm, why netback fails everything in the ring?
109      * Should we do that even when not supporting SG and TSO?
110      */
111     RING_IDX cons = netdev->tx_ring.req_cons;
112 
113     do {
114         make_tx_response(netif, txp, NETIF_RSP_ERROR);
115         if (cons >= end) {
116             break;
117         }
118         txp = RING_GET_REQUEST(&netdev->tx_ring, cons++);
119     } while (1);
120     netdev->tx_ring.req_cons = cons;
121     netif_schedule_work(netif);
122     netif_put(netif);
123 #else
124     net_tx_response(netdev, txp, NETIF_RSP_ERROR);
125 #endif
126 }
127 
128 static bool net_tx_packets(struct XenNetDev *netdev)
129 {
130     bool done_something = false;
131     netif_tx_request_t txreq;
132     RING_IDX rc, rp;
133     void *page;
134     void *tmpbuf = NULL;
135 
136     assert(qemu_mutex_iothread_locked());
137 
138     for (;;) {
139         rc = netdev->tx_ring.req_cons;
140         rp = netdev->tx_ring.sring->req_prod;
141         xen_rmb(); /* Ensure we see queued requests up to 'rp'. */
142 
143         while ((rc != rp)) {
144             if (RING_REQUEST_CONS_OVERFLOW(&netdev->tx_ring, rc)) {
145                 break;
146             }
147             memcpy(&txreq, RING_GET_REQUEST(&netdev->tx_ring, rc), sizeof(txreq));
148             netdev->tx_ring.req_cons = ++rc;
149             done_something = true;
150 
151 #if 1
152             /* should not happen in theory, we don't announce the *
153              * feature-{sg,gso,whatelse} flags in xenstore (yet?) */
154             if (txreq.flags & NETTXF_extra_info) {
155                 qemu_log_mask(LOG_UNIMP, "vif%u: FIXME: extra info flag\n",
156                               netdev->dev);
157                 net_tx_error(netdev, &txreq, rc);
158                 continue;
159             }
160             if (txreq.flags & NETTXF_more_data) {
161                 qemu_log_mask(LOG_UNIMP, "vif%u: FIXME: more data flag\n",
162                               netdev->dev);
163                 net_tx_error(netdev, &txreq, rc);
164                 continue;
165             }
166 #endif
167 
168             if (txreq.size < 14) {
169                 qemu_log_mask(LOG_GUEST_ERROR, "vif%u: bad packet size: %d\n",
170                               netdev->dev, txreq.size);
171                 net_tx_error(netdev, &txreq, rc);
172                 continue;
173             }
174 
175             if ((txreq.offset + txreq.size) > XEN_PAGE_SIZE) {
176                 qemu_log_mask(LOG_GUEST_ERROR, "vif%u: error: page crossing\n",
177                               netdev->dev);
178                 net_tx_error(netdev, &txreq, rc);
179                 continue;
180             }
181 
182             trace_xen_netdev_tx(netdev->dev, txreq.gref, txreq.offset,
183                                 txreq.size, txreq.flags,
184                                 (txreq.flags & NETTXF_csum_blank)     ? " csum_blank"     : "",
185                                 (txreq.flags & NETTXF_data_validated) ? " data_validated" : "",
186                                 (txreq.flags & NETTXF_more_data)      ? " more_data"      : "",
187                                 (txreq.flags & NETTXF_extra_info)     ? " extra_info"     : "");
188 
189             page = xen_device_map_grant_refs(&netdev->xendev, &txreq.gref, 1,
190                                              PROT_READ, NULL);
191             if (page == NULL) {
192                 qemu_log_mask(LOG_GUEST_ERROR,
193                               "vif%u: tx gref dereference failed (%d)\n",
194                               netdev->dev, txreq.gref);
195                 net_tx_error(netdev, &txreq, rc);
196                 continue;
197             }
198             if (txreq.flags & NETTXF_csum_blank) {
199                 /* have read-only mapping -> can't fill checksum in-place */
200                 if (!tmpbuf) {
201                     tmpbuf = g_malloc(XEN_PAGE_SIZE);
202                 }
203                 memcpy(tmpbuf, page + txreq.offset, txreq.size);
204                 net_checksum_calculate(tmpbuf, txreq.size, CSUM_ALL);
205                 qemu_send_packet(qemu_get_queue(netdev->nic), tmpbuf,
206                                  txreq.size);
207             } else {
208                 qemu_send_packet(qemu_get_queue(netdev->nic),
209                                  page + txreq.offset, txreq.size);
210             }
211             xen_device_unmap_grant_refs(&netdev->xendev, page, &txreq.gref, 1,
212                                         NULL);
213             net_tx_response(netdev, &txreq, NETIF_RSP_OKAY);
214         }
215         if (!netdev->tx_work) {
216             break;
217         }
218         netdev->tx_work = 0;
219     }
220     g_free(tmpbuf);
221     return done_something;
222 }
223 
224 /* ------------------------------------------------------------- */
225 
226 static void net_rx_response(struct XenNetDev *netdev,
227                             netif_rx_request_t *req, int8_t st,
228                             uint16_t offset, uint16_t size,
229                             uint16_t flags)
230 {
231     RING_IDX i = netdev->rx_ring.rsp_prod_pvt;
232     netif_rx_response_t *resp;
233     int notify;
234 
235     resp = RING_GET_RESPONSE(&netdev->rx_ring, i);
236     resp->offset     = offset;
237     resp->flags      = flags;
238     resp->id         = req->id;
239     resp->status     = (int16_t)size;
240     if (st < 0) {
241         resp->status = (int16_t)st;
242     }
243 
244     trace_xen_netdev_rx(netdev->dev, i, resp->status, resp->flags);
245 
246     netdev->rx_ring.rsp_prod_pvt = ++i;
247     RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&netdev->rx_ring, notify);
248     if (notify) {
249         xen_device_notify_event_channel(XEN_DEVICE(netdev),
250                                         netdev->event_channel, NULL);
251     }
252 }
253 
254 #define NET_IP_ALIGN 2
255 
256 static ssize_t net_rx_packet(NetClientState *nc, const uint8_t *buf, size_t size)
257 {
258     struct XenNetDev *netdev = qemu_get_nic_opaque(nc);
259     netif_rx_request_t rxreq;
260     RING_IDX rc, rp;
261     void *page;
262 
263     assert(qemu_mutex_iothread_locked());
264 
265     if (xen_device_backend_get_state(&netdev->xendev) != XenbusStateConnected) {
266         return -1;
267     }
268 
269     rc = netdev->rx_ring.req_cons;
270     rp = netdev->rx_ring.sring->req_prod;
271     xen_rmb(); /* Ensure we see queued requests up to 'rp'. */
272 
273     if (rc == rp || RING_REQUEST_CONS_OVERFLOW(&netdev->rx_ring, rc)) {
274         return 0;
275     }
276     if (size > XEN_PAGE_SIZE - NET_IP_ALIGN) {
277         qemu_log_mask(LOG_GUEST_ERROR, "vif%u: packet too big (%lu > %ld)",
278                       netdev->dev, (unsigned long)size,
279                       XEN_PAGE_SIZE - NET_IP_ALIGN);
280         return -1;
281     }
282 
283     memcpy(&rxreq, RING_GET_REQUEST(&netdev->rx_ring, rc), sizeof(rxreq));
284     netdev->rx_ring.req_cons = ++rc;
285 
286     page = xen_device_map_grant_refs(&netdev->xendev, &rxreq.gref, 1,
287                                      PROT_WRITE, NULL);
288     if (page == NULL) {
289         qemu_log_mask(LOG_GUEST_ERROR,
290                       "vif%u: rx gref dereference failed (%d)\n",
291                       netdev->dev, rxreq.gref);
292         net_rx_response(netdev, &rxreq, NETIF_RSP_ERROR, 0, 0, 0);
293         return -1;
294     }
295     memcpy(page + NET_IP_ALIGN, buf, size);
296     xen_device_unmap_grant_refs(&netdev->xendev, page, &rxreq.gref, 1, NULL);
297     net_rx_response(netdev, &rxreq, NETIF_RSP_OKAY, NET_IP_ALIGN, size, 0);
298 
299     return size;
300 }
301 
302 /* ------------------------------------------------------------- */
303 
304 static NetClientInfo net_xen_info = {
305     .type = NET_CLIENT_DRIVER_NIC,
306     .size = sizeof(NICState),
307     .receive = net_rx_packet,
308 };
309 
310 static void xen_netdev_realize(XenDevice *xendev, Error **errp)
311 {
312     ERRP_GUARD();
313     XenNetDev *netdev = XEN_NET_DEVICE(xendev);
314     NetClientState *nc;
315 
316     qemu_macaddr_default_if_unset(&netdev->conf.macaddr);
317 
318     xen_device_frontend_printf(xendev, "mac", "%02x:%02x:%02x:%02x:%02x:%02x",
319                                netdev->conf.macaddr.a[0],
320                                netdev->conf.macaddr.a[1],
321                                netdev->conf.macaddr.a[2],
322                                netdev->conf.macaddr.a[3],
323                                netdev->conf.macaddr.a[4],
324                                netdev->conf.macaddr.a[5]);
325 
326     netdev->nic = qemu_new_nic(&net_xen_info, &netdev->conf,
327                                object_get_typename(OBJECT(xendev)),
328                                DEVICE(xendev)->id, netdev);
329 
330     nc = qemu_get_queue(netdev->nic);
331     qemu_format_nic_info_str(nc, netdev->conf.macaddr.a);
332 
333     /* fill info */
334     xen_device_backend_printf(xendev, "feature-rx-copy", "%u", 1);
335     xen_device_backend_printf(xendev, "feature-rx-flip", "%u", 0);
336 
337     trace_xen_netdev_realize(netdev->dev, nc->info_str, nc->peer ?
338                              nc->peer->name : "(none)");
339 }
340 
341 static bool net_event(void *_xendev)
342 {
343     XenNetDev *netdev = XEN_NET_DEVICE(_xendev);
344     bool done_something;
345 
346     done_something = net_tx_packets(netdev);
347     qemu_flush_queued_packets(qemu_get_queue(netdev->nic));
348     return done_something;
349 }
350 
351 static bool xen_netdev_connect(XenDevice *xendev, Error **errp)
352 {
353     XenNetDev *netdev = XEN_NET_DEVICE(xendev);
354     unsigned int port, rx_copy;
355 
356     assert(qemu_mutex_iothread_locked());
357 
358     if (xen_device_frontend_scanf(xendev, "tx-ring-ref", "%u",
359                                   &netdev->tx_ring_ref) != 1) {
360         error_setg(errp, "failed to read tx-ring-ref");
361         return false;
362     }
363 
364     if (xen_device_frontend_scanf(xendev, "rx-ring-ref", "%u",
365                                   &netdev->rx_ring_ref) != 1) {
366         error_setg(errp, "failed to read rx-ring-ref");
367         return false;
368     }
369 
370     if (xen_device_frontend_scanf(xendev, "event-channel", "%u",
371                                   &port) != 1) {
372         error_setg(errp, "failed to read event-channel");
373         return false;
374     }
375 
376     if (xen_device_frontend_scanf(xendev, "request-rx-copy", "%u",
377                                   &rx_copy) != 1) {
378         rx_copy = 0;
379     }
380     if (rx_copy == 0) {
381         error_setg(errp, "frontend doesn't support rx-copy");
382         return false;
383     }
384 
385     netdev->txs = xen_device_map_grant_refs(xendev,
386                                             &netdev->tx_ring_ref, 1,
387                                             PROT_READ | PROT_WRITE,
388                                             errp);
389     if (!netdev->txs) {
390         error_prepend(errp, "failed to map tx grant ref: ");
391         return false;
392     }
393 
394     netdev->rxs = xen_device_map_grant_refs(xendev,
395                                             &netdev->rx_ring_ref, 1,
396                                             PROT_READ | PROT_WRITE,
397                                             errp);
398     if (!netdev->rxs) {
399         error_prepend(errp, "failed to map rx grant ref: ");
400         return false;
401     }
402 
403     BACK_RING_INIT(&netdev->tx_ring, netdev->txs, XEN_PAGE_SIZE);
404     BACK_RING_INIT(&netdev->rx_ring, netdev->rxs, XEN_PAGE_SIZE);
405 
406     netdev->event_channel = xen_device_bind_event_channel(xendev, port,
407                                                           net_event,
408                                                           netdev,
409                                                           errp);
410     if (!netdev->event_channel) {
411         return false;
412     }
413 
414     trace_xen_netdev_connect(netdev->dev, netdev->tx_ring_ref,
415                              netdev->rx_ring_ref, port);
416 
417     net_tx_packets(netdev);
418     return true;
419 }
420 
421 static void xen_netdev_disconnect(XenDevice *xendev, Error **errp)
422 {
423     XenNetDev *netdev = XEN_NET_DEVICE(xendev);
424 
425     trace_xen_netdev_disconnect(netdev->dev);
426 
427     assert(qemu_mutex_iothread_locked());
428 
429     netdev->tx_ring.sring = NULL;
430     netdev->rx_ring.sring = NULL;
431 
432     if (netdev->event_channel) {
433         xen_device_unbind_event_channel(xendev, netdev->event_channel,
434                                         errp);
435         netdev->event_channel = NULL;
436     }
437     if (netdev->txs) {
438         xen_device_unmap_grant_refs(xendev, netdev->txs,
439                                     &netdev->tx_ring_ref, 1, errp);
440         netdev->txs = NULL;
441     }
442     if (netdev->rxs) {
443         xen_device_unmap_grant_refs(xendev, netdev->rxs,
444                                     &netdev->rx_ring_ref, 1, errp);
445         netdev->rxs = NULL;
446     }
447 }
448 
449 /* -------------------------------------------------------------------- */
450 
451 
452 static void xen_netdev_frontend_changed(XenDevice *xendev,
453                                        enum xenbus_state frontend_state,
454                                        Error **errp)
455 {
456     ERRP_GUARD();
457     enum xenbus_state backend_state = xen_device_backend_get_state(xendev);
458 
459     trace_xen_netdev_frontend_changed(xendev->name, frontend_state);
460 
461     switch (frontend_state) {
462     case XenbusStateConnected:
463         if (backend_state == XenbusStateConnected) {
464             break;
465         }
466 
467         xen_netdev_disconnect(xendev, errp);
468         if (*errp) {
469             break;
470         }
471 
472         if (!xen_netdev_connect(xendev, errp)) {
473             xen_netdev_disconnect(xendev, NULL);
474             xen_device_backend_set_state(xendev, XenbusStateClosing);
475             break;
476         }
477 
478         xen_device_backend_set_state(xendev, XenbusStateConnected);
479         break;
480 
481     case XenbusStateClosing:
482         xen_device_backend_set_state(xendev, XenbusStateClosing);
483         break;
484 
485     case XenbusStateClosed:
486     case XenbusStateUnknown:
487         xen_netdev_disconnect(xendev, errp);
488         if (*errp) {
489             break;
490         }
491 
492         xen_device_backend_set_state(xendev, XenbusStateClosed);
493         break;
494 
495     case XenbusStateInitialised:
496         /*
497          * Linux netback does nothing on the frontend going (back) to
498          * XenbusStateInitialised, so do the same here.
499          */
500     default:
501         break;
502     }
503 }
504 
505 static char *xen_netdev_get_name(XenDevice *xendev, Error **errp)
506 {
507     XenNetDev *netdev = XEN_NET_DEVICE(xendev);
508 
509     if (netdev->dev == -1) {
510         XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev)));
511         char fe_path[XENSTORE_ABS_PATH_MAX + 1];
512         int idx = (xen_mode == XEN_EMULATE) ? 0 : 1;
513         char *value;
514 
515         /* Theoretically we could go up to INT_MAX here but that's overkill */
516         while (idx < 100) {
517             snprintf(fe_path, sizeof(fe_path),
518                      "/local/domain/%u/device/vif/%u",
519                      xendev->frontend_id, idx);
520             value = qemu_xen_xs_read(xenbus->xsh, XBT_NULL, fe_path, NULL);
521             if (!value) {
522                 if (errno == ENOENT) {
523                     netdev->dev = idx;
524                     goto found;
525                 }
526                 error_setg(errp, "cannot read %s: %s", fe_path,
527                            strerror(errno));
528                 return NULL;
529             }
530             free(value);
531             idx++;
532         }
533         error_setg(errp, "cannot find device index for netdev device");
534         return NULL;
535     }
536  found:
537     return g_strdup_printf("%u", netdev->dev);
538 }
539 
540 static void xen_netdev_unrealize(XenDevice *xendev)
541 {
542     XenNetDev *netdev = XEN_NET_DEVICE(xendev);
543 
544     trace_xen_netdev_unrealize(netdev->dev);
545 
546     /* Disconnect from the frontend in case this has not already happened */
547     xen_netdev_disconnect(xendev, NULL);
548 
549     if (netdev->nic) {
550         qemu_del_nic(netdev->nic);
551     }
552 }
553 
554 /* ------------------------------------------------------------- */
555 
556 static Property xen_netdev_properties[] = {
557     DEFINE_NIC_PROPERTIES(XenNetDev, conf),
558     DEFINE_PROP_INT32("idx", XenNetDev, dev, -1),
559     DEFINE_PROP_END_OF_LIST(),
560 };
561 
562 static void xen_netdev_class_init(ObjectClass *class, void *data)
563 {
564     DeviceClass *dev_class = DEVICE_CLASS(class);
565     XenDeviceClass *xendev_class = XEN_DEVICE_CLASS(class);
566 
567     xendev_class->backend = "qnic";
568     xendev_class->device = "vif";
569     xendev_class->get_name = xen_netdev_get_name;
570     xendev_class->realize = xen_netdev_realize;
571     xendev_class->frontend_changed = xen_netdev_frontend_changed;
572     xendev_class->unrealize = xen_netdev_unrealize;
573     set_bit(DEVICE_CATEGORY_NETWORK, dev_class->categories);
574     dev_class->user_creatable = true;
575 
576     device_class_set_props(dev_class, xen_netdev_properties);
577 }
578 
579 static const TypeInfo xen_net_type_info = {
580     .name = TYPE_XEN_NET_DEVICE,
581     .parent = TYPE_XEN_DEVICE,
582     .instance_size = sizeof(XenNetDev),
583     .class_init = xen_netdev_class_init,
584 };
585 
586 static void xen_net_register_types(void)
587 {
588     type_register_static(&xen_net_type_info);
589 }
590 
591 type_init(xen_net_register_types)
592 
593 /* Called to instantiate a XenNetDev when the backend is detected. */
594 static void xen_net_device_create(XenBackendInstance *backend,
595                                   QDict *opts, Error **errp)
596 {
597     ERRP_GUARD();
598     XenBus *xenbus = xen_backend_get_bus(backend);
599     const char *name = xen_backend_get_name(backend);
600     XenDevice *xendev = NULL;
601     unsigned long number;
602     const char *macstr;
603     XenNetDev *net;
604     MACAddr mac;
605 
606     if (qemu_strtoul(name, NULL, 10, &number) || number >= INT_MAX) {
607         error_setg(errp, "failed to parse name '%s'", name);
608         goto fail;
609     }
610 
611     trace_xen_netdev_create(number);
612 
613     macstr = qdict_get_try_str(opts, "mac");
614     if (macstr == NULL) {
615         error_setg(errp, "no MAC address found");
616         goto fail;
617     }
618 
619     if (net_parse_macaddr(mac.a, macstr) < 0) {
620         error_setg(errp, "failed to parse MAC address");
621         goto fail;
622     }
623 
624     xendev = XEN_DEVICE(qdev_new(TYPE_XEN_NET_DEVICE));
625     net = XEN_NET_DEVICE(xendev);
626 
627     net->dev = number;
628     memcpy(&net->conf.macaddr, &mac, sizeof(mac));
629 
630     if (qdev_realize_and_unref(DEVICE(xendev), BUS(xenbus), errp)) {
631         xen_backend_set_device(backend, xendev);
632         return;
633     }
634 
635     error_prepend(errp, "realization of net device %lu failed: ",
636                   number);
637 
638  fail:
639     if (xendev) {
640         object_unparent(OBJECT(xendev));
641     }
642 }
643 
644 static void xen_net_device_destroy(XenBackendInstance *backend,
645                                    Error **errp)
646 {
647     ERRP_GUARD();
648     XenDevice *xendev = xen_backend_get_device(backend);
649     XenNetDev *netdev = XEN_NET_DEVICE(xendev);
650 
651     trace_xen_netdev_destroy(netdev->dev);
652 
653     object_unparent(OBJECT(xendev));
654 }
655 
656 static const XenBackendInfo xen_net_backend_info  = {
657     .type = "qnic",
658     .create = xen_net_device_create,
659     .destroy = xen_net_device_destroy,
660 };
661 
662 static void xen_net_register_backend(void)
663 {
664     xen_backend_register(&xen_net_backend_info);
665 }
666 
667 xen_backend_init(xen_net_register_backend);
668