xref: /openbmc/qemu/hw/net/xen_nic.c (revision 2df1eb27)
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(bql_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(bql_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,
329                                &xendev->qdev.mem_reentrancy_guard, netdev);
330 
331     nc = qemu_get_queue(netdev->nic);
332     qemu_format_nic_info_str(nc, netdev->conf.macaddr.a);
333 
334     /* fill info */
335     xen_device_backend_printf(xendev, "feature-rx-copy", "%u", 1);
336     xen_device_backend_printf(xendev, "feature-rx-flip", "%u", 0);
337 
338     trace_xen_netdev_realize(netdev->dev, nc->info_str, nc->peer ?
339                              nc->peer->name : "(none)");
340 }
341 
342 static bool net_event(void *_xendev)
343 {
344     XenNetDev *netdev = XEN_NET_DEVICE(_xendev);
345     bool done_something;
346 
347     done_something = net_tx_packets(netdev);
348     qemu_flush_queued_packets(qemu_get_queue(netdev->nic));
349     return done_something;
350 }
351 
352 static bool xen_netdev_connect(XenDevice *xendev, Error **errp)
353 {
354     XenNetDev *netdev = XEN_NET_DEVICE(xendev);
355     unsigned int port, rx_copy;
356 
357     assert(bql_locked());
358 
359     if (xen_device_frontend_scanf(xendev, "tx-ring-ref", "%u",
360                                   &netdev->tx_ring_ref) != 1) {
361         error_setg(errp, "failed to read tx-ring-ref");
362         return false;
363     }
364 
365     if (xen_device_frontend_scanf(xendev, "rx-ring-ref", "%u",
366                                   &netdev->rx_ring_ref) != 1) {
367         error_setg(errp, "failed to read rx-ring-ref");
368         return false;
369     }
370 
371     if (xen_device_frontend_scanf(xendev, "event-channel", "%u",
372                                   &port) != 1) {
373         error_setg(errp, "failed to read event-channel");
374         return false;
375     }
376 
377     if (xen_device_frontend_scanf(xendev, "request-rx-copy", "%u",
378                                   &rx_copy) != 1) {
379         rx_copy = 0;
380     }
381     if (rx_copy == 0) {
382         error_setg(errp, "frontend doesn't support rx-copy");
383         return false;
384     }
385 
386     netdev->txs = xen_device_map_grant_refs(xendev,
387                                             &netdev->tx_ring_ref, 1,
388                                             PROT_READ | PROT_WRITE,
389                                             errp);
390     if (!netdev->txs) {
391         error_prepend(errp, "failed to map tx grant ref: ");
392         return false;
393     }
394 
395     netdev->rxs = xen_device_map_grant_refs(xendev,
396                                             &netdev->rx_ring_ref, 1,
397                                             PROT_READ | PROT_WRITE,
398                                             errp);
399     if (!netdev->rxs) {
400         error_prepend(errp, "failed to map rx grant ref: ");
401         return false;
402     }
403 
404     BACK_RING_INIT(&netdev->tx_ring, netdev->txs, XEN_PAGE_SIZE);
405     BACK_RING_INIT(&netdev->rx_ring, netdev->rxs, XEN_PAGE_SIZE);
406 
407     netdev->event_channel = xen_device_bind_event_channel(xendev, port,
408                                                           net_event,
409                                                           netdev,
410                                                           errp);
411     if (!netdev->event_channel) {
412         return false;
413     }
414 
415     trace_xen_netdev_connect(netdev->dev, netdev->tx_ring_ref,
416                              netdev->rx_ring_ref, port);
417 
418     net_tx_packets(netdev);
419     return true;
420 }
421 
422 static void xen_netdev_disconnect(XenDevice *xendev, Error **errp)
423 {
424     XenNetDev *netdev = XEN_NET_DEVICE(xendev);
425 
426     trace_xen_netdev_disconnect(netdev->dev);
427 
428     assert(bql_locked());
429 
430     netdev->tx_ring.sring = NULL;
431     netdev->rx_ring.sring = NULL;
432 
433     if (netdev->event_channel) {
434         xen_device_unbind_event_channel(xendev, netdev->event_channel,
435                                         errp);
436         netdev->event_channel = NULL;
437     }
438     if (netdev->txs) {
439         xen_device_unmap_grant_refs(xendev, netdev->txs,
440                                     &netdev->tx_ring_ref, 1, errp);
441         netdev->txs = NULL;
442     }
443     if (netdev->rxs) {
444         xen_device_unmap_grant_refs(xendev, netdev->rxs,
445                                     &netdev->rx_ring_ref, 1, errp);
446         netdev->rxs = NULL;
447     }
448 }
449 
450 /* -------------------------------------------------------------------- */
451 
452 
453 static void xen_netdev_frontend_changed(XenDevice *xendev,
454                                        enum xenbus_state frontend_state,
455                                        Error **errp)
456 {
457     ERRP_GUARD();
458     enum xenbus_state backend_state = xen_device_backend_get_state(xendev);
459 
460     trace_xen_netdev_frontend_changed(xendev->name, frontend_state);
461 
462     switch (frontend_state) {
463     case XenbusStateConnected:
464         if (backend_state == XenbusStateConnected) {
465             break;
466         }
467 
468         xen_netdev_disconnect(xendev, errp);
469         if (*errp) {
470             break;
471         }
472 
473         if (!xen_netdev_connect(xendev, errp)) {
474             xen_netdev_disconnect(xendev, NULL);
475             xen_device_backend_set_state(xendev, XenbusStateClosing);
476             break;
477         }
478 
479         xen_device_backend_set_state(xendev, XenbusStateConnected);
480         break;
481 
482     case XenbusStateClosing:
483         xen_device_backend_set_state(xendev, XenbusStateClosing);
484         break;
485 
486     case XenbusStateClosed:
487     case XenbusStateUnknown:
488         xen_netdev_disconnect(xendev, errp);
489         if (*errp) {
490             break;
491         }
492 
493         xen_device_backend_set_state(xendev, XenbusStateClosed);
494         break;
495 
496     case XenbusStateInitialised:
497         /*
498          * Linux netback does nothing on the frontend going (back) to
499          * XenbusStateInitialised, so do the same here.
500          */
501     default:
502         break;
503     }
504 }
505 
506 static char *xen_netdev_get_name(XenDevice *xendev, Error **errp)
507 {
508     XenNetDev *netdev = XEN_NET_DEVICE(xendev);
509 
510     if (netdev->dev == -1) {
511         XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev)));
512         char fe_path[XENSTORE_ABS_PATH_MAX + 1];
513         int idx = (xen_mode == XEN_EMULATE) ? 0 : 1;
514         char *value;
515 
516         /* Theoretically we could go up to INT_MAX here but that's overkill */
517         while (idx < 100) {
518             snprintf(fe_path, sizeof(fe_path),
519                      "/local/domain/%u/device/vif/%u",
520                      xendev->frontend_id, idx);
521             value = qemu_xen_xs_read(xenbus->xsh, XBT_NULL, fe_path, NULL);
522             if (!value) {
523                 if (errno == ENOENT) {
524                     netdev->dev = idx;
525                     goto found;
526                 }
527                 error_setg(errp, "cannot read %s: %s", fe_path,
528                            strerror(errno));
529                 return NULL;
530             }
531             free(value);
532             idx++;
533         }
534         error_setg(errp, "cannot find device index for netdev device");
535         return NULL;
536     }
537  found:
538     return g_strdup_printf("%u", netdev->dev);
539 }
540 
541 static void xen_netdev_unrealize(XenDevice *xendev)
542 {
543     XenNetDev *netdev = XEN_NET_DEVICE(xendev);
544 
545     trace_xen_netdev_unrealize(netdev->dev);
546 
547     /* Disconnect from the frontend in case this has not already happened */
548     xen_netdev_disconnect(xendev, NULL);
549 
550     if (netdev->nic) {
551         qemu_del_nic(netdev->nic);
552     }
553 }
554 
555 /* ------------------------------------------------------------- */
556 
557 static Property xen_netdev_properties[] = {
558     DEFINE_NIC_PROPERTIES(XenNetDev, conf),
559     DEFINE_PROP_INT32("idx", XenNetDev, dev, -1),
560     DEFINE_PROP_END_OF_LIST(),
561 };
562 
563 static void xen_netdev_class_init(ObjectClass *class, void *data)
564 {
565     DeviceClass *dev_class = DEVICE_CLASS(class);
566     XenDeviceClass *xendev_class = XEN_DEVICE_CLASS(class);
567 
568     xendev_class->backend = "qnic";
569     xendev_class->device = "vif";
570     xendev_class->get_name = xen_netdev_get_name;
571     xendev_class->realize = xen_netdev_realize;
572     xendev_class->frontend_changed = xen_netdev_frontend_changed;
573     xendev_class->unrealize = xen_netdev_unrealize;
574     set_bit(DEVICE_CATEGORY_NETWORK, dev_class->categories);
575     dev_class->user_creatable = true;
576 
577     device_class_set_props(dev_class, xen_netdev_properties);
578 }
579 
580 static const TypeInfo xen_net_type_info = {
581     .name = TYPE_XEN_NET_DEVICE,
582     .parent = TYPE_XEN_DEVICE,
583     .instance_size = sizeof(XenNetDev),
584     .class_init = xen_netdev_class_init,
585 };
586 
587 static void xen_net_register_types(void)
588 {
589     type_register_static(&xen_net_type_info);
590 }
591 
592 type_init(xen_net_register_types)
593 
594 /* Called to instantiate a XenNetDev when the backend is detected. */
595 static void xen_net_device_create(XenBackendInstance *backend,
596                                   QDict *opts, Error **errp)
597 {
598     ERRP_GUARD();
599     XenBus *xenbus = xen_backend_get_bus(backend);
600     const char *name = xen_backend_get_name(backend);
601     XenDevice *xendev = NULL;
602     unsigned long number;
603     const char *macstr;
604     XenNetDev *net;
605     MACAddr mac;
606 
607     if (qemu_strtoul(name, NULL, 10, &number) || number >= INT_MAX) {
608         error_setg(errp, "failed to parse name '%s'", name);
609         goto fail;
610     }
611 
612     trace_xen_netdev_create(number);
613 
614     macstr = qdict_get_try_str(opts, "mac");
615     if (macstr == NULL) {
616         error_setg(errp, "no MAC address found");
617         goto fail;
618     }
619 
620     if (net_parse_macaddr(mac.a, macstr) < 0) {
621         error_setg(errp, "failed to parse MAC address");
622         goto fail;
623     }
624 
625     xendev = XEN_DEVICE(qdev_new(TYPE_XEN_NET_DEVICE));
626     net = XEN_NET_DEVICE(xendev);
627 
628     net->dev = number;
629     memcpy(&net->conf.macaddr, &mac, sizeof(mac));
630 
631     if (qdev_realize_and_unref(DEVICE(xendev), BUS(xenbus), errp)) {
632         xen_backend_set_device(backend, xendev);
633         return;
634     }
635 
636     error_prepend(errp, "realization of net device %lu failed: ",
637                   number);
638 
639  fail:
640     if (xendev) {
641         object_unparent(OBJECT(xendev));
642     }
643 }
644 
645 static void xen_net_device_destroy(XenBackendInstance *backend,
646                                    Error **errp)
647 {
648     ERRP_GUARD();
649     XenDevice *xendev = xen_backend_get_device(backend);
650     XenNetDev *netdev = XEN_NET_DEVICE(xendev);
651 
652     trace_xen_netdev_destroy(netdev->dev);
653 
654     object_unparent(OBJECT(xendev));
655 }
656 
657 static const XenBackendInfo xen_net_backend_info  = {
658     .type = "qnic",
659     .create = xen_net_device_create,
660     .destroy = xen_net_device_destroy,
661 };
662 
663 static void xen_net_register_backend(void)
664 {
665     xen_backend_register(&xen_net_backend_info);
666 }
667 
668 xen_backend_init(xen_net_register_backend);
669