xref: /openbmc/qemu/hw/net/xen_nic.c (revision 7489f7f3f81dcb776df8c1b9a9db281fc21bf05f)
149ab747fSPaolo Bonzini /*
249ab747fSPaolo Bonzini  *  xen paravirt network card backend
349ab747fSPaolo Bonzini  *
449ab747fSPaolo Bonzini  *  (c) Gerd Hoffmann <kraxel@redhat.com>
549ab747fSPaolo Bonzini  *
649ab747fSPaolo Bonzini  *  This program is free software; you can redistribute it and/or modify
749ab747fSPaolo Bonzini  *  it under the terms of the GNU General Public License as published by
849ab747fSPaolo Bonzini  *  the Free Software Foundation; under version 2 of the License.
949ab747fSPaolo Bonzini  *
1049ab747fSPaolo Bonzini  *  This program is distributed in the hope that it will be useful,
1149ab747fSPaolo Bonzini  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
1249ab747fSPaolo Bonzini  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1349ab747fSPaolo Bonzini  *  GNU General Public License for more details.
1449ab747fSPaolo Bonzini  *
1549ab747fSPaolo Bonzini  *  You should have received a copy of the GNU General Public License along
1649ab747fSPaolo Bonzini  *  with this program; if not, see <http://www.gnu.org/licenses/>.
1749ab747fSPaolo Bonzini  *
1849ab747fSPaolo Bonzini  *  Contributions after 2012-01-13 are licensed under the terms of the
1949ab747fSPaolo Bonzini  *  GNU GPL, version 2 or (at your option) any later version.
2049ab747fSPaolo Bonzini  */
2149ab747fSPaolo Bonzini 
2221cbfe5fSPeter Maydell #include "qemu/osdep.h"
2325967ff6SDavid Woodhouse #include "qemu/main-loop.h"
2425967ff6SDavid Woodhouse #include "qemu/cutils.h"
2525967ff6SDavid Woodhouse #include "qemu/log.h"
2625967ff6SDavid Woodhouse #include "qemu/qemu-print.h"
2725967ff6SDavid Woodhouse #include "qapi/qmp/qdict.h"
2825967ff6SDavid Woodhouse #include "qapi/error.h"
2925967ff6SDavid Woodhouse 
3049ab747fSPaolo Bonzini #include <sys/socket.h>
3149ab747fSPaolo Bonzini #include <sys/ioctl.h>
3249ab747fSPaolo Bonzini #include <sys/wait.h>
3349ab747fSPaolo Bonzini 
3449ab747fSPaolo Bonzini #include "net/net.h"
3549ab747fSPaolo Bonzini #include "net/checksum.h"
3649ab747fSPaolo Bonzini #include "net/util.h"
3725967ff6SDavid Woodhouse 
3825967ff6SDavid Woodhouse #include "hw/xen/xen-backend.h"
3925967ff6SDavid Woodhouse #include "hw/xen/xen-bus-helper.h"
4025967ff6SDavid Woodhouse #include "hw/qdev-properties.h"
4125967ff6SDavid Woodhouse #include "hw/qdev-properties-system.h"
4249ab747fSPaolo Bonzini 
43a3434a2dSAnthony PERARD #include "hw/xen/interface/io/netif.h"
4425967ff6SDavid Woodhouse #include "hw/xen/interface/io/xs_wire.h"
4525967ff6SDavid Woodhouse 
4625967ff6SDavid Woodhouse #include "trace.h"
4749ab747fSPaolo Bonzini 
4849ab747fSPaolo Bonzini /* ------------------------------------------------------------- */
4949ab747fSPaolo Bonzini 
5049ab747fSPaolo Bonzini struct XenNetDev {
5125967ff6SDavid Woodhouse     struct XenDevice      xendev;  /* must be first */
5225967ff6SDavid Woodhouse     XenEventChannel       *event_channel;
5325967ff6SDavid Woodhouse     int                   dev;
5449ab747fSPaolo Bonzini     int                   tx_work;
5525967ff6SDavid Woodhouse     unsigned int          tx_ring_ref;
5625967ff6SDavid Woodhouse     unsigned int          rx_ring_ref;
5749ab747fSPaolo Bonzini     struct netif_tx_sring *txs;
5849ab747fSPaolo Bonzini     struct netif_rx_sring *rxs;
5949ab747fSPaolo Bonzini     netif_tx_back_ring_t  tx_ring;
6049ab747fSPaolo Bonzini     netif_rx_back_ring_t  rx_ring;
6149ab747fSPaolo Bonzini     NICConf               conf;
6249ab747fSPaolo Bonzini     NICState              *nic;
6349ab747fSPaolo Bonzini };
6449ab747fSPaolo Bonzini 
6525967ff6SDavid Woodhouse typedef struct XenNetDev XenNetDev;
6625967ff6SDavid Woodhouse 
6725967ff6SDavid Woodhouse #define TYPE_XEN_NET_DEVICE "xen-net-device"
OBJECT_DECLARE_SIMPLE_TYPE(XenNetDev,XEN_NET_DEVICE)6825967ff6SDavid Woodhouse OBJECT_DECLARE_SIMPLE_TYPE(XenNetDev, XEN_NET_DEVICE)
6925967ff6SDavid Woodhouse 
7049ab747fSPaolo Bonzini /* ------------------------------------------------------------- */
7149ab747fSPaolo Bonzini 
7249ab747fSPaolo Bonzini static void net_tx_response(struct XenNetDev *netdev, netif_tx_request_t *txp, int8_t st)
7349ab747fSPaolo Bonzini {
7449ab747fSPaolo Bonzini     RING_IDX i = netdev->tx_ring.rsp_prod_pvt;
7549ab747fSPaolo Bonzini     netif_tx_response_t *resp;
7649ab747fSPaolo Bonzini     int notify;
7749ab747fSPaolo Bonzini 
7849ab747fSPaolo Bonzini     resp = RING_GET_RESPONSE(&netdev->tx_ring, i);
7949ab747fSPaolo Bonzini     resp->id     = txp->id;
8049ab747fSPaolo Bonzini     resp->status = st;
8149ab747fSPaolo Bonzini 
8249ab747fSPaolo Bonzini #if 0
8349ab747fSPaolo Bonzini     if (txp->flags & NETTXF_extra_info) {
8449ab747fSPaolo Bonzini         RING_GET_RESPONSE(&netdev->tx_ring, ++i)->status = NETIF_RSP_NULL;
8549ab747fSPaolo Bonzini     }
8649ab747fSPaolo Bonzini #endif
8749ab747fSPaolo Bonzini 
8849ab747fSPaolo Bonzini     netdev->tx_ring.rsp_prod_pvt = ++i;
8949ab747fSPaolo Bonzini     RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&netdev->tx_ring, notify);
9049ab747fSPaolo Bonzini     if (notify) {
9125967ff6SDavid Woodhouse         xen_device_notify_event_channel(XEN_DEVICE(netdev),
9225967ff6SDavid Woodhouse                                         netdev->event_channel, NULL);
9349ab747fSPaolo Bonzini     }
9449ab747fSPaolo Bonzini 
9549ab747fSPaolo Bonzini     if (i == netdev->tx_ring.req_cons) {
9649ab747fSPaolo Bonzini         int more_to_do;
9749ab747fSPaolo Bonzini         RING_FINAL_CHECK_FOR_REQUESTS(&netdev->tx_ring, more_to_do);
9849ab747fSPaolo Bonzini         if (more_to_do) {
9949ab747fSPaolo Bonzini             netdev->tx_work++;
10049ab747fSPaolo Bonzini         }
10149ab747fSPaolo Bonzini     }
10249ab747fSPaolo Bonzini }
10349ab747fSPaolo Bonzini 
net_tx_error(struct XenNetDev * netdev,netif_tx_request_t * txp,RING_IDX end)10449ab747fSPaolo Bonzini static void net_tx_error(struct XenNetDev *netdev, netif_tx_request_t *txp, RING_IDX end)
10549ab747fSPaolo Bonzini {
10649ab747fSPaolo Bonzini #if 0
10749ab747fSPaolo Bonzini     /*
10849ab747fSPaolo Bonzini      * Hmm, why netback fails everything in the ring?
10949ab747fSPaolo Bonzini      * Should we do that even when not supporting SG and TSO?
11049ab747fSPaolo Bonzini      */
11149ab747fSPaolo Bonzini     RING_IDX cons = netdev->tx_ring.req_cons;
11249ab747fSPaolo Bonzini 
11349ab747fSPaolo Bonzini     do {
11449ab747fSPaolo Bonzini         make_tx_response(netif, txp, NETIF_RSP_ERROR);
11549ab747fSPaolo Bonzini         if (cons >= end) {
11649ab747fSPaolo Bonzini             break;
11749ab747fSPaolo Bonzini         }
11849ab747fSPaolo Bonzini         txp = RING_GET_REQUEST(&netdev->tx_ring, cons++);
11949ab747fSPaolo Bonzini     } while (1);
12049ab747fSPaolo Bonzini     netdev->tx_ring.req_cons = cons;
12149ab747fSPaolo Bonzini     netif_schedule_work(netif);
12249ab747fSPaolo Bonzini     netif_put(netif);
12349ab747fSPaolo Bonzini #else
12449ab747fSPaolo Bonzini     net_tx_response(netdev, txp, NETIF_RSP_ERROR);
12549ab747fSPaolo Bonzini #endif
12649ab747fSPaolo Bonzini }
12749ab747fSPaolo Bonzini 
net_tx_packets(struct XenNetDev * netdev)12825967ff6SDavid Woodhouse static bool net_tx_packets(struct XenNetDev *netdev)
12949ab747fSPaolo Bonzini {
13025967ff6SDavid Woodhouse     bool done_something = false;
13149ab747fSPaolo Bonzini     netif_tx_request_t txreq;
13249ab747fSPaolo Bonzini     RING_IDX rc, rp;
13349ab747fSPaolo Bonzini     void *page;
13449ab747fSPaolo Bonzini     void *tmpbuf = NULL;
13549ab747fSPaolo Bonzini 
136195801d7SStefan Hajnoczi     assert(bql_locked());
13725967ff6SDavid Woodhouse 
13849ab747fSPaolo Bonzini     for (;;) {
13949ab747fSPaolo Bonzini         rc = netdev->tx_ring.req_cons;
14049ab747fSPaolo Bonzini         rp = netdev->tx_ring.sring->req_prod;
14149ab747fSPaolo Bonzini         xen_rmb(); /* Ensure we see queued requests up to 'rp'. */
14249ab747fSPaolo Bonzini 
14349ab747fSPaolo Bonzini         while ((rc != rp)) {
14449ab747fSPaolo Bonzini             if (RING_REQUEST_CONS_OVERFLOW(&netdev->tx_ring, rc)) {
14549ab747fSPaolo Bonzini                 break;
14649ab747fSPaolo Bonzini             }
14749ab747fSPaolo Bonzini             memcpy(&txreq, RING_GET_REQUEST(&netdev->tx_ring, rc), sizeof(txreq));
14849ab747fSPaolo Bonzini             netdev->tx_ring.req_cons = ++rc;
14925967ff6SDavid Woodhouse             done_something = true;
15049ab747fSPaolo Bonzini 
15149ab747fSPaolo Bonzini #if 1
15249ab747fSPaolo Bonzini             /* should not happen in theory, we don't announce the *
15349ab747fSPaolo Bonzini              * feature-{sg,gso,whatelse} flags in xenstore (yet?) */
15449ab747fSPaolo Bonzini             if (txreq.flags & NETTXF_extra_info) {
15525967ff6SDavid Woodhouse                 qemu_log_mask(LOG_UNIMP, "vif%u: FIXME: extra info flag\n",
15625967ff6SDavid Woodhouse                               netdev->dev);
15749ab747fSPaolo Bonzini                 net_tx_error(netdev, &txreq, rc);
15849ab747fSPaolo Bonzini                 continue;
15949ab747fSPaolo Bonzini             }
16049ab747fSPaolo Bonzini             if (txreq.flags & NETTXF_more_data) {
16125967ff6SDavid Woodhouse                 qemu_log_mask(LOG_UNIMP, "vif%u: FIXME: more data flag\n",
16225967ff6SDavid Woodhouse                               netdev->dev);
16349ab747fSPaolo Bonzini                 net_tx_error(netdev, &txreq, rc);
16449ab747fSPaolo Bonzini                 continue;
16549ab747fSPaolo Bonzini             }
16649ab747fSPaolo Bonzini #endif
16749ab747fSPaolo Bonzini 
16849ab747fSPaolo Bonzini             if (txreq.size < 14) {
16925967ff6SDavid Woodhouse                 qemu_log_mask(LOG_GUEST_ERROR, "vif%u: bad packet size: %d\n",
17025967ff6SDavid Woodhouse                               netdev->dev, txreq.size);
17149ab747fSPaolo Bonzini                 net_tx_error(netdev, &txreq, rc);
17249ab747fSPaolo Bonzini                 continue;
17349ab747fSPaolo Bonzini             }
17449ab747fSPaolo Bonzini 
175a9ae1418SDavid Woodhouse             if ((txreq.offset + txreq.size) > XEN_PAGE_SIZE) {
17625967ff6SDavid Woodhouse                 qemu_log_mask(LOG_GUEST_ERROR, "vif%u: error: page crossing\n",
17725967ff6SDavid Woodhouse                               netdev->dev);
17849ab747fSPaolo Bonzini                 net_tx_error(netdev, &txreq, rc);
17949ab747fSPaolo Bonzini                 continue;
18049ab747fSPaolo Bonzini             }
18149ab747fSPaolo Bonzini 
18225967ff6SDavid Woodhouse             trace_xen_netdev_tx(netdev->dev, txreq.gref, txreq.offset,
18325967ff6SDavid Woodhouse                                 txreq.size, txreq.flags,
18449ab747fSPaolo Bonzini                                 (txreq.flags & NETTXF_csum_blank)     ? " csum_blank"     : "",
18549ab747fSPaolo Bonzini                                 (txreq.flags & NETTXF_data_validated) ? " data_validated" : "",
18649ab747fSPaolo Bonzini                                 (txreq.flags & NETTXF_more_data)      ? " more_data"      : "",
18749ab747fSPaolo Bonzini                                 (txreq.flags & NETTXF_extra_info)     ? " extra_info"     : "");
18849ab747fSPaolo Bonzini 
18925967ff6SDavid Woodhouse             page = xen_device_map_grant_refs(&netdev->xendev, &txreq.gref, 1,
19025967ff6SDavid Woodhouse                                              PROT_READ, NULL);
19149ab747fSPaolo Bonzini             if (page == NULL) {
19225967ff6SDavid Woodhouse                 qemu_log_mask(LOG_GUEST_ERROR,
19325967ff6SDavid Woodhouse                               "vif%u: tx gref dereference failed (%d)\n",
19425967ff6SDavid Woodhouse                               netdev->dev, txreq.gref);
19549ab747fSPaolo Bonzini                 net_tx_error(netdev, &txreq, rc);
19649ab747fSPaolo Bonzini                 continue;
19749ab747fSPaolo Bonzini             }
19849ab747fSPaolo Bonzini             if (txreq.flags & NETTXF_csum_blank) {
19949ab747fSPaolo Bonzini                 /* have read-only mapping -> can't fill checksum in-place */
20049ab747fSPaolo Bonzini                 if (!tmpbuf) {
201a9ae1418SDavid Woodhouse                     tmpbuf = g_malloc(XEN_PAGE_SIZE);
20249ab747fSPaolo Bonzini                 }
20349ab747fSPaolo Bonzini                 memcpy(tmpbuf, page + txreq.offset, txreq.size);
204f5746335SBin Meng                 net_checksum_calculate(tmpbuf, txreq.size, CSUM_ALL);
20549ab747fSPaolo Bonzini                 qemu_send_packet(qemu_get_queue(netdev->nic), tmpbuf,
20649ab747fSPaolo Bonzini                                  txreq.size);
20749ab747fSPaolo Bonzini             } else {
20849ab747fSPaolo Bonzini                 qemu_send_packet(qemu_get_queue(netdev->nic),
20949ab747fSPaolo Bonzini                                  page + txreq.offset, txreq.size);
21049ab747fSPaolo Bonzini             }
21125967ff6SDavid Woodhouse             xen_device_unmap_grant_refs(&netdev->xendev, page, &txreq.gref, 1,
21225967ff6SDavid Woodhouse                                         NULL);
21349ab747fSPaolo Bonzini             net_tx_response(netdev, &txreq, NETIF_RSP_OKAY);
21449ab747fSPaolo Bonzini         }
21549ab747fSPaolo Bonzini         if (!netdev->tx_work) {
21649ab747fSPaolo Bonzini             break;
21749ab747fSPaolo Bonzini         }
21849ab747fSPaolo Bonzini         netdev->tx_work = 0;
21949ab747fSPaolo Bonzini     }
22049ab747fSPaolo Bonzini     g_free(tmpbuf);
22125967ff6SDavid Woodhouse     return done_something;
22249ab747fSPaolo Bonzini }
22349ab747fSPaolo Bonzini 
22449ab747fSPaolo Bonzini /* ------------------------------------------------------------- */
22549ab747fSPaolo Bonzini 
net_rx_response(struct XenNetDev * netdev,netif_rx_request_t * req,int8_t st,uint16_t offset,uint16_t size,uint16_t flags)22649ab747fSPaolo Bonzini static void net_rx_response(struct XenNetDev *netdev,
22749ab747fSPaolo Bonzini                             netif_rx_request_t *req, int8_t st,
22849ab747fSPaolo Bonzini                             uint16_t offset, uint16_t size,
22949ab747fSPaolo Bonzini                             uint16_t flags)
23049ab747fSPaolo Bonzini {
23149ab747fSPaolo Bonzini     RING_IDX i = netdev->rx_ring.rsp_prod_pvt;
23249ab747fSPaolo Bonzini     netif_rx_response_t *resp;
23349ab747fSPaolo Bonzini     int notify;
23449ab747fSPaolo Bonzini 
23549ab747fSPaolo Bonzini     resp = RING_GET_RESPONSE(&netdev->rx_ring, i);
23649ab747fSPaolo Bonzini     resp->offset     = offset;
23749ab747fSPaolo Bonzini     resp->flags      = flags;
23849ab747fSPaolo Bonzini     resp->id         = req->id;
23949ab747fSPaolo Bonzini     resp->status     = (int16_t)size;
24049ab747fSPaolo Bonzini     if (st < 0) {
24149ab747fSPaolo Bonzini         resp->status = (int16_t)st;
24249ab747fSPaolo Bonzini     }
24349ab747fSPaolo Bonzini 
24425967ff6SDavid Woodhouse     trace_xen_netdev_rx(netdev->dev, i, resp->status, resp->flags);
24549ab747fSPaolo Bonzini 
24649ab747fSPaolo Bonzini     netdev->rx_ring.rsp_prod_pvt = ++i;
24749ab747fSPaolo Bonzini     RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&netdev->rx_ring, notify);
24849ab747fSPaolo Bonzini     if (notify) {
24925967ff6SDavid Woodhouse         xen_device_notify_event_channel(XEN_DEVICE(netdev),
25025967ff6SDavid Woodhouse                                         netdev->event_channel, NULL);
25149ab747fSPaolo Bonzini     }
25249ab747fSPaolo Bonzini }
25349ab747fSPaolo Bonzini 
25449ab747fSPaolo Bonzini #define NET_IP_ALIGN 2
25549ab747fSPaolo Bonzini 
net_rx_packet(NetClientState * nc,const uint8_t * buf,size_t size)25649ab747fSPaolo Bonzini static ssize_t net_rx_packet(NetClientState *nc, const uint8_t *buf, size_t size)
25749ab747fSPaolo Bonzini {
25849ab747fSPaolo Bonzini     struct XenNetDev *netdev = qemu_get_nic_opaque(nc);
25949ab747fSPaolo Bonzini     netif_rx_request_t rxreq;
26049ab747fSPaolo Bonzini     RING_IDX rc, rp;
26149ab747fSPaolo Bonzini     void *page;
26249ab747fSPaolo Bonzini 
263195801d7SStefan Hajnoczi     assert(bql_locked());
26425967ff6SDavid Woodhouse 
26525967ff6SDavid Woodhouse     if (xen_device_backend_get_state(&netdev->xendev) != XenbusStateConnected) {
26649ab747fSPaolo Bonzini         return -1;
26749ab747fSPaolo Bonzini     }
26849ab747fSPaolo Bonzini 
26949ab747fSPaolo Bonzini     rc = netdev->rx_ring.req_cons;
27049ab747fSPaolo Bonzini     rp = netdev->rx_ring.sring->req_prod;
27149ab747fSPaolo Bonzini     xen_rmb(); /* Ensure we see queued requests up to 'rp'. */
27249ab747fSPaolo Bonzini 
27349ab747fSPaolo Bonzini     if (rc == rp || RING_REQUEST_CONS_OVERFLOW(&netdev->rx_ring, rc)) {
2747bba83bfSFam Zheng         return 0;
27549ab747fSPaolo Bonzini     }
276a9ae1418SDavid Woodhouse     if (size > XEN_PAGE_SIZE - NET_IP_ALIGN) {
27725967ff6SDavid Woodhouse         qemu_log_mask(LOG_GUEST_ERROR, "vif%u: packet too big (%lu > %ld)",
27825967ff6SDavid Woodhouse                       netdev->dev, (unsigned long)size,
27925967ff6SDavid Woodhouse                       XEN_PAGE_SIZE - NET_IP_ALIGN);
28049ab747fSPaolo Bonzini         return -1;
28149ab747fSPaolo Bonzini     }
28249ab747fSPaolo Bonzini 
28349ab747fSPaolo Bonzini     memcpy(&rxreq, RING_GET_REQUEST(&netdev->rx_ring, rc), sizeof(rxreq));
28449ab747fSPaolo Bonzini     netdev->rx_ring.req_cons = ++rc;
28549ab747fSPaolo Bonzini 
28625967ff6SDavid Woodhouse     page = xen_device_map_grant_refs(&netdev->xendev, &rxreq.gref, 1,
28725967ff6SDavid Woodhouse                                      PROT_WRITE, NULL);
28849ab747fSPaolo Bonzini     if (page == NULL) {
28925967ff6SDavid Woodhouse         qemu_log_mask(LOG_GUEST_ERROR,
29025967ff6SDavid Woodhouse                       "vif%u: rx gref dereference failed (%d)\n",
29125967ff6SDavid Woodhouse                       netdev->dev, rxreq.gref);
29249ab747fSPaolo Bonzini         net_rx_response(netdev, &rxreq, NETIF_RSP_ERROR, 0, 0, 0);
29349ab747fSPaolo Bonzini         return -1;
29449ab747fSPaolo Bonzini     }
29549ab747fSPaolo Bonzini     memcpy(page + NET_IP_ALIGN, buf, size);
29625967ff6SDavid Woodhouse     xen_device_unmap_grant_refs(&netdev->xendev, page, &rxreq.gref, 1, NULL);
29749ab747fSPaolo Bonzini     net_rx_response(netdev, &rxreq, NETIF_RSP_OKAY, NET_IP_ALIGN, size, 0);
29849ab747fSPaolo Bonzini 
29949ab747fSPaolo Bonzini     return size;
30049ab747fSPaolo Bonzini }
30149ab747fSPaolo Bonzini 
30249ab747fSPaolo Bonzini /* ------------------------------------------------------------- */
30349ab747fSPaolo Bonzini 
30449ab747fSPaolo Bonzini static NetClientInfo net_xen_info = {
305f394b2e2SEric Blake     .type = NET_CLIENT_DRIVER_NIC,
30649ab747fSPaolo Bonzini     .size = sizeof(NICState),
30749ab747fSPaolo Bonzini     .receive = net_rx_packet,
30849ab747fSPaolo Bonzini };
30949ab747fSPaolo Bonzini 
xen_netdev_realize(XenDevice * xendev,Error ** errp)31025967ff6SDavid Woodhouse static void xen_netdev_realize(XenDevice *xendev, Error **errp)
31149ab747fSPaolo Bonzini {
31225967ff6SDavid Woodhouse     ERRP_GUARD();
31325967ff6SDavid Woodhouse     XenNetDev *netdev = XEN_NET_DEVICE(xendev);
31425967ff6SDavid Woodhouse     NetClientState *nc;
31549ab747fSPaolo Bonzini 
31625967ff6SDavid Woodhouse     qemu_macaddr_default_if_unset(&netdev->conf.macaddr);
31749ab747fSPaolo Bonzini 
31825967ff6SDavid Woodhouse     xen_device_frontend_printf(xendev, "mac", "%02x:%02x:%02x:%02x:%02x:%02x",
31925967ff6SDavid Woodhouse                                netdev->conf.macaddr.a[0],
32025967ff6SDavid Woodhouse                                netdev->conf.macaddr.a[1],
32125967ff6SDavid Woodhouse                                netdev->conf.macaddr.a[2],
32225967ff6SDavid Woodhouse                                netdev->conf.macaddr.a[3],
32325967ff6SDavid Woodhouse                                netdev->conf.macaddr.a[4],
32425967ff6SDavid Woodhouse                                netdev->conf.macaddr.a[5]);
32549ab747fSPaolo Bonzini 
32649ab747fSPaolo Bonzini     netdev->nic = qemu_new_nic(&net_xen_info, &netdev->conf,
32725967ff6SDavid Woodhouse                                object_get_typename(OBJECT(xendev)),
3287d0fefdfSAkihiko Odaki                                DEVICE(xendev)->id,
3297d0fefdfSAkihiko Odaki                                &xendev->qdev.mem_reentrancy_guard, netdev);
33049ab747fSPaolo Bonzini 
33125967ff6SDavid Woodhouse     nc = qemu_get_queue(netdev->nic);
33225967ff6SDavid Woodhouse     qemu_format_nic_info_str(nc, netdev->conf.macaddr.a);
33349ab747fSPaolo Bonzini 
33449ab747fSPaolo Bonzini     /* fill info */
33525967ff6SDavid Woodhouse     xen_device_backend_printf(xendev, "feature-rx-copy", "%u", 1);
33625967ff6SDavid Woodhouse     xen_device_backend_printf(xendev, "feature-rx-flip", "%u", 0);
33749ab747fSPaolo Bonzini 
33825967ff6SDavid Woodhouse     trace_xen_netdev_realize(netdev->dev, nc->info_str, nc->peer ?
33925967ff6SDavid Woodhouse                              nc->peer->name : "(none)");
34049ab747fSPaolo Bonzini }
34149ab747fSPaolo Bonzini 
net_event(void * _xendev)34225967ff6SDavid Woodhouse static bool net_event(void *_xendev)
34349ab747fSPaolo Bonzini {
34425967ff6SDavid Woodhouse     XenNetDev *netdev = XEN_NET_DEVICE(_xendev);
34525967ff6SDavid Woodhouse     bool done_something;
34649ab747fSPaolo Bonzini 
34725967ff6SDavid Woodhouse     done_something = net_tx_packets(netdev);
34825967ff6SDavid Woodhouse     qemu_flush_queued_packets(qemu_get_queue(netdev->nic));
34925967ff6SDavid Woodhouse     return done_something;
35049ab747fSPaolo Bonzini }
35149ab747fSPaolo Bonzini 
xen_netdev_connect(XenDevice * xendev,Error ** errp)35225967ff6SDavid Woodhouse static bool xen_netdev_connect(XenDevice *xendev, Error **errp)
35325967ff6SDavid Woodhouse {
354*8538ceecSZhao Liu     ERRP_GUARD();
35525967ff6SDavid Woodhouse     XenNetDev *netdev = XEN_NET_DEVICE(xendev);
35625967ff6SDavid Woodhouse     unsigned int port, rx_copy;
35725967ff6SDavid Woodhouse 
358195801d7SStefan Hajnoczi     assert(bql_locked());
35925967ff6SDavid Woodhouse 
36025967ff6SDavid Woodhouse     if (xen_device_frontend_scanf(xendev, "tx-ring-ref", "%u",
36125967ff6SDavid Woodhouse                                   &netdev->tx_ring_ref) != 1) {
36225967ff6SDavid Woodhouse         error_setg(errp, "failed to read tx-ring-ref");
36325967ff6SDavid Woodhouse         return false;
36425967ff6SDavid Woodhouse     }
36525967ff6SDavid Woodhouse 
36625967ff6SDavid Woodhouse     if (xen_device_frontend_scanf(xendev, "rx-ring-ref", "%u",
36725967ff6SDavid Woodhouse                                   &netdev->rx_ring_ref) != 1) {
36825967ff6SDavid Woodhouse         error_setg(errp, "failed to read rx-ring-ref");
36925967ff6SDavid Woodhouse         return false;
37025967ff6SDavid Woodhouse     }
37125967ff6SDavid Woodhouse 
37225967ff6SDavid Woodhouse     if (xen_device_frontend_scanf(xendev, "event-channel", "%u",
37325967ff6SDavid Woodhouse                                   &port) != 1) {
37425967ff6SDavid Woodhouse         error_setg(errp, "failed to read event-channel");
37525967ff6SDavid Woodhouse         return false;
37625967ff6SDavid Woodhouse     }
37725967ff6SDavid Woodhouse 
37825967ff6SDavid Woodhouse     if (xen_device_frontend_scanf(xendev, "request-rx-copy", "%u",
37925967ff6SDavid Woodhouse                                   &rx_copy) != 1) {
38049ab747fSPaolo Bonzini         rx_copy = 0;
38149ab747fSPaolo Bonzini     }
38249ab747fSPaolo Bonzini     if (rx_copy == 0) {
38325967ff6SDavid Woodhouse         error_setg(errp, "frontend doesn't support rx-copy");
38425967ff6SDavid Woodhouse         return false;
38549ab747fSPaolo Bonzini     }
38649ab747fSPaolo Bonzini 
38725967ff6SDavid Woodhouse     netdev->txs = xen_device_map_grant_refs(xendev,
38825967ff6SDavid Woodhouse                                             &netdev->tx_ring_ref, 1,
38925967ff6SDavid Woodhouse                                             PROT_READ | PROT_WRITE,
39025967ff6SDavid Woodhouse                                             errp);
391b4f72e31SChen Gang     if (!netdev->txs) {
39225967ff6SDavid Woodhouse         error_prepend(errp, "failed to map tx grant ref: ");
39325967ff6SDavid Woodhouse         return false;
394b4f72e31SChen Gang     }
39525967ff6SDavid Woodhouse 
39625967ff6SDavid Woodhouse     netdev->rxs = xen_device_map_grant_refs(xendev,
39725967ff6SDavid Woodhouse                                             &netdev->rx_ring_ref, 1,
39825967ff6SDavid Woodhouse                                             PROT_READ | PROT_WRITE,
39925967ff6SDavid Woodhouse                                             errp);
400b4f72e31SChen Gang     if (!netdev->rxs) {
40125967ff6SDavid Woodhouse         error_prepend(errp, "failed to map rx grant ref: ");
40225967ff6SDavid Woodhouse         return false;
40349ab747fSPaolo Bonzini     }
40425967ff6SDavid Woodhouse 
405a9ae1418SDavid Woodhouse     BACK_RING_INIT(&netdev->tx_ring, netdev->txs, XEN_PAGE_SIZE);
406a9ae1418SDavid Woodhouse     BACK_RING_INIT(&netdev->rx_ring, netdev->rxs, XEN_PAGE_SIZE);
40749ab747fSPaolo Bonzini 
40825967ff6SDavid Woodhouse     netdev->event_channel = xen_device_bind_event_channel(xendev, port,
40925967ff6SDavid Woodhouse                                                           net_event,
41025967ff6SDavid Woodhouse                                                           netdev,
41125967ff6SDavid Woodhouse                                                           errp);
41225967ff6SDavid Woodhouse     if (!netdev->event_channel) {
41325967ff6SDavid Woodhouse         return false;
41449ab747fSPaolo Bonzini     }
41549ab747fSPaolo Bonzini 
41625967ff6SDavid Woodhouse     trace_xen_netdev_connect(netdev->dev, netdev->tx_ring_ref,
41725967ff6SDavid Woodhouse                              netdev->rx_ring_ref, port);
41825967ff6SDavid Woodhouse 
41925967ff6SDavid Woodhouse     net_tx_packets(netdev);
42025967ff6SDavid Woodhouse     return true;
42125967ff6SDavid Woodhouse }
42225967ff6SDavid Woodhouse 
xen_netdev_disconnect(XenDevice * xendev,Error ** errp)42325967ff6SDavid Woodhouse static void xen_netdev_disconnect(XenDevice *xendev, Error **errp)
42449ab747fSPaolo Bonzini {
42525967ff6SDavid Woodhouse     XenNetDev *netdev = XEN_NET_DEVICE(xendev);
42649ab747fSPaolo Bonzini 
42725967ff6SDavid Woodhouse     trace_xen_netdev_disconnect(netdev->dev);
42849ab747fSPaolo Bonzini 
429195801d7SStefan Hajnoczi     assert(bql_locked());
43025967ff6SDavid Woodhouse 
43125967ff6SDavid Woodhouse     netdev->tx_ring.sring = NULL;
43225967ff6SDavid Woodhouse     netdev->rx_ring.sring = NULL;
43325967ff6SDavid Woodhouse 
43425967ff6SDavid Woodhouse     if (netdev->event_channel) {
43525967ff6SDavid Woodhouse         xen_device_unbind_event_channel(xendev, netdev->event_channel,
43625967ff6SDavid Woodhouse                                         errp);
43725967ff6SDavid Woodhouse         netdev->event_channel = NULL;
43825967ff6SDavid Woodhouse     }
43949ab747fSPaolo Bonzini     if (netdev->txs) {
44025967ff6SDavid Woodhouse         xen_device_unmap_grant_refs(xendev, netdev->txs,
44125967ff6SDavid Woodhouse                                     &netdev->tx_ring_ref, 1, errp);
44249ab747fSPaolo Bonzini         netdev->txs = NULL;
44349ab747fSPaolo Bonzini     }
44449ab747fSPaolo Bonzini     if (netdev->rxs) {
44525967ff6SDavid Woodhouse         xen_device_unmap_grant_refs(xendev, netdev->rxs,
44625967ff6SDavid Woodhouse                                     &netdev->rx_ring_ref, 1, errp);
44749ab747fSPaolo Bonzini         netdev->rxs = NULL;
44849ab747fSPaolo Bonzini     }
44949ab747fSPaolo Bonzini }
45049ab747fSPaolo Bonzini 
45125967ff6SDavid Woodhouse /* -------------------------------------------------------------------- */
45225967ff6SDavid Woodhouse 
45325967ff6SDavid Woodhouse 
xen_netdev_frontend_changed(XenDevice * xendev,enum xenbus_state frontend_state,Error ** errp)45425967ff6SDavid Woodhouse static void xen_netdev_frontend_changed(XenDevice *xendev,
45525967ff6SDavid Woodhouse                                        enum xenbus_state frontend_state,
45625967ff6SDavid Woodhouse                                        Error **errp)
45749ab747fSPaolo Bonzini {
45825967ff6SDavid Woodhouse     ERRP_GUARD();
45925967ff6SDavid Woodhouse     enum xenbus_state backend_state = xen_device_backend_get_state(xendev);
46025967ff6SDavid Woodhouse 
46125967ff6SDavid Woodhouse     trace_xen_netdev_frontend_changed(xendev->name, frontend_state);
46225967ff6SDavid Woodhouse 
46325967ff6SDavid Woodhouse     switch (frontend_state) {
46425967ff6SDavid Woodhouse     case XenbusStateConnected:
46525967ff6SDavid Woodhouse         if (backend_state == XenbusStateConnected) {
46625967ff6SDavid Woodhouse             break;
46749ab747fSPaolo Bonzini         }
46849ab747fSPaolo Bonzini 
46925967ff6SDavid Woodhouse         xen_netdev_disconnect(xendev, errp);
47025967ff6SDavid Woodhouse         if (*errp) {
47125967ff6SDavid Woodhouse             break;
47225967ff6SDavid Woodhouse         }
47325967ff6SDavid Woodhouse 
47425967ff6SDavid Woodhouse         if (!xen_netdev_connect(xendev, errp)) {
47525967ff6SDavid Woodhouse             xen_netdev_disconnect(xendev, NULL);
47625967ff6SDavid Woodhouse             xen_device_backend_set_state(xendev, XenbusStateClosing);
47725967ff6SDavid Woodhouse             break;
47825967ff6SDavid Woodhouse         }
47925967ff6SDavid Woodhouse 
48025967ff6SDavid Woodhouse         xen_device_backend_set_state(xendev, XenbusStateConnected);
48125967ff6SDavid Woodhouse         break;
48225967ff6SDavid Woodhouse 
48325967ff6SDavid Woodhouse     case XenbusStateClosing:
48425967ff6SDavid Woodhouse         xen_device_backend_set_state(xendev, XenbusStateClosing);
48525967ff6SDavid Woodhouse         break;
48625967ff6SDavid Woodhouse 
48725967ff6SDavid Woodhouse     case XenbusStateClosed:
48825967ff6SDavid Woodhouse     case XenbusStateUnknown:
48925967ff6SDavid Woodhouse         xen_netdev_disconnect(xendev, errp);
49025967ff6SDavid Woodhouse         if (*errp) {
49125967ff6SDavid Woodhouse             break;
49225967ff6SDavid Woodhouse         }
49325967ff6SDavid Woodhouse 
49425967ff6SDavid Woodhouse         xen_device_backend_set_state(xendev, XenbusStateClosed);
49525967ff6SDavid Woodhouse         break;
49625967ff6SDavid Woodhouse 
49725967ff6SDavid Woodhouse     case XenbusStateInitialised:
49825967ff6SDavid Woodhouse         /*
49925967ff6SDavid Woodhouse          * Linux netback does nothing on the frontend going (back) to
50025967ff6SDavid Woodhouse          * XenbusStateInitialised, so do the same here.
50125967ff6SDavid Woodhouse          */
50225967ff6SDavid Woodhouse     default:
50325967ff6SDavid Woodhouse         break;
50425967ff6SDavid Woodhouse     }
50525967ff6SDavid Woodhouse }
50625967ff6SDavid Woodhouse 
xen_netdev_get_name(XenDevice * xendev,Error ** errp)50725967ff6SDavid Woodhouse static char *xen_netdev_get_name(XenDevice *xendev, Error **errp)
50849ab747fSPaolo Bonzini {
50925967ff6SDavid Woodhouse     XenNetDev *netdev = XEN_NET_DEVICE(xendev);
51025967ff6SDavid Woodhouse 
51125967ff6SDavid Woodhouse     if (netdev->dev == -1) {
51225967ff6SDavid Woodhouse         XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev)));
51325967ff6SDavid Woodhouse         char fe_path[XENSTORE_ABS_PATH_MAX + 1];
51425967ff6SDavid Woodhouse         int idx = (xen_mode == XEN_EMULATE) ? 0 : 1;
51525967ff6SDavid Woodhouse         char *value;
51625967ff6SDavid Woodhouse 
51725967ff6SDavid Woodhouse         /* Theoretically we could go up to INT_MAX here but that's overkill */
51825967ff6SDavid Woodhouse         while (idx < 100) {
51925967ff6SDavid Woodhouse             snprintf(fe_path, sizeof(fe_path),
52025967ff6SDavid Woodhouse                      "/local/domain/%u/device/vif/%u",
52125967ff6SDavid Woodhouse                      xendev->frontend_id, idx);
52225967ff6SDavid Woodhouse             value = qemu_xen_xs_read(xenbus->xsh, XBT_NULL, fe_path, NULL);
52325967ff6SDavid Woodhouse             if (!value) {
52425967ff6SDavid Woodhouse                 if (errno == ENOENT) {
52525967ff6SDavid Woodhouse                     netdev->dev = idx;
52625967ff6SDavid Woodhouse                     goto found;
52725967ff6SDavid Woodhouse                 }
52825967ff6SDavid Woodhouse                 error_setg(errp, "cannot read %s: %s", fe_path,
52925967ff6SDavid Woodhouse                            strerror(errno));
53025967ff6SDavid Woodhouse                 return NULL;
53125967ff6SDavid Woodhouse             }
53225967ff6SDavid Woodhouse             free(value);
53325967ff6SDavid Woodhouse             idx++;
53425967ff6SDavid Woodhouse         }
53525967ff6SDavid Woodhouse         error_setg(errp, "cannot find device index for netdev device");
53625967ff6SDavid Woodhouse         return NULL;
53725967ff6SDavid Woodhouse     }
53825967ff6SDavid Woodhouse  found:
53925967ff6SDavid Woodhouse     return g_strdup_printf("%u", netdev->dev);
54025967ff6SDavid Woodhouse }
54125967ff6SDavid Woodhouse 
xen_netdev_unrealize(XenDevice * xendev)54225967ff6SDavid Woodhouse static void xen_netdev_unrealize(XenDevice *xendev)
54325967ff6SDavid Woodhouse {
54425967ff6SDavid Woodhouse     XenNetDev *netdev = XEN_NET_DEVICE(xendev);
54525967ff6SDavid Woodhouse 
54625967ff6SDavid Woodhouse     trace_xen_netdev_unrealize(netdev->dev);
54725967ff6SDavid Woodhouse 
54825967ff6SDavid Woodhouse     /* Disconnect from the frontend in case this has not already happened */
54925967ff6SDavid Woodhouse     xen_netdev_disconnect(xendev, NULL);
55049ab747fSPaolo Bonzini 
551d4685837SChen Gang     if (netdev->nic) {
552d4685837SChen Gang         qemu_del_nic(netdev->nic);
553d4685837SChen Gang     }
55449ab747fSPaolo Bonzini }
55549ab747fSPaolo Bonzini 
55649ab747fSPaolo Bonzini /* ------------------------------------------------------------- */
55749ab747fSPaolo Bonzini 
55825967ff6SDavid Woodhouse static Property xen_netdev_properties[] = {
55925967ff6SDavid Woodhouse     DEFINE_NIC_PROPERTIES(XenNetDev, conf),
56025967ff6SDavid Woodhouse     DEFINE_PROP_INT32("idx", XenNetDev, dev, -1),
56125967ff6SDavid Woodhouse     DEFINE_PROP_END_OF_LIST(),
56249ab747fSPaolo Bonzini };
56325967ff6SDavid Woodhouse 
xen_netdev_class_init(ObjectClass * class,void * data)56425967ff6SDavid Woodhouse static void xen_netdev_class_init(ObjectClass *class, void *data)
56525967ff6SDavid Woodhouse {
56625967ff6SDavid Woodhouse     DeviceClass *dev_class = DEVICE_CLASS(class);
56725967ff6SDavid Woodhouse     XenDeviceClass *xendev_class = XEN_DEVICE_CLASS(class);
56825967ff6SDavid Woodhouse 
56925967ff6SDavid Woodhouse     xendev_class->backend = "qnic";
57025967ff6SDavid Woodhouse     xendev_class->device = "vif";
57125967ff6SDavid Woodhouse     xendev_class->get_name = xen_netdev_get_name;
57225967ff6SDavid Woodhouse     xendev_class->realize = xen_netdev_realize;
57325967ff6SDavid Woodhouse     xendev_class->frontend_changed = xen_netdev_frontend_changed;
57425967ff6SDavid Woodhouse     xendev_class->unrealize = xen_netdev_unrealize;
57525967ff6SDavid Woodhouse     set_bit(DEVICE_CATEGORY_NETWORK, dev_class->categories);
57625967ff6SDavid Woodhouse     dev_class->user_creatable = true;
57725967ff6SDavid Woodhouse 
57825967ff6SDavid Woodhouse     device_class_set_props(dev_class, xen_netdev_properties);
57925967ff6SDavid Woodhouse }
58025967ff6SDavid Woodhouse 
58125967ff6SDavid Woodhouse static const TypeInfo xen_net_type_info = {
58225967ff6SDavid Woodhouse     .name = TYPE_XEN_NET_DEVICE,
58325967ff6SDavid Woodhouse     .parent = TYPE_XEN_DEVICE,
58425967ff6SDavid Woodhouse     .instance_size = sizeof(XenNetDev),
58525967ff6SDavid Woodhouse     .class_init = xen_netdev_class_init,
58625967ff6SDavid Woodhouse };
58725967ff6SDavid Woodhouse 
xen_net_register_types(void)58825967ff6SDavid Woodhouse static void xen_net_register_types(void)
58925967ff6SDavid Woodhouse {
59025967ff6SDavid Woodhouse     type_register_static(&xen_net_type_info);
59125967ff6SDavid Woodhouse }
59225967ff6SDavid Woodhouse 
type_init(xen_net_register_types)59325967ff6SDavid Woodhouse type_init(xen_net_register_types)
59425967ff6SDavid Woodhouse 
59525967ff6SDavid Woodhouse /* Called to instantiate a XenNetDev when the backend is detected. */
59625967ff6SDavid Woodhouse static void xen_net_device_create(XenBackendInstance *backend,
59725967ff6SDavid Woodhouse                                   QDict *opts, Error **errp)
59825967ff6SDavid Woodhouse {
59925967ff6SDavid Woodhouse     ERRP_GUARD();
60025967ff6SDavid Woodhouse     XenBus *xenbus = xen_backend_get_bus(backend);
60125967ff6SDavid Woodhouse     const char *name = xen_backend_get_name(backend);
60225967ff6SDavid Woodhouse     XenDevice *xendev = NULL;
60325967ff6SDavid Woodhouse     unsigned long number;
60425967ff6SDavid Woodhouse     const char *macstr;
60525967ff6SDavid Woodhouse     XenNetDev *net;
60625967ff6SDavid Woodhouse     MACAddr mac;
60725967ff6SDavid Woodhouse 
60825967ff6SDavid Woodhouse     if (qemu_strtoul(name, NULL, 10, &number) || number >= INT_MAX) {
60925967ff6SDavid Woodhouse         error_setg(errp, "failed to parse name '%s'", name);
61025967ff6SDavid Woodhouse         goto fail;
61125967ff6SDavid Woodhouse     }
61225967ff6SDavid Woodhouse 
61325967ff6SDavid Woodhouse     trace_xen_netdev_create(number);
61425967ff6SDavid Woodhouse 
61525967ff6SDavid Woodhouse     macstr = qdict_get_try_str(opts, "mac");
61625967ff6SDavid Woodhouse     if (macstr == NULL) {
61725967ff6SDavid Woodhouse         error_setg(errp, "no MAC address found");
61825967ff6SDavid Woodhouse         goto fail;
61925967ff6SDavid Woodhouse     }
62025967ff6SDavid Woodhouse 
62125967ff6SDavid Woodhouse     if (net_parse_macaddr(mac.a, macstr) < 0) {
62225967ff6SDavid Woodhouse         error_setg(errp, "failed to parse MAC address");
62325967ff6SDavid Woodhouse         goto fail;
62425967ff6SDavid Woodhouse     }
62525967ff6SDavid Woodhouse 
62625967ff6SDavid Woodhouse     xendev = XEN_DEVICE(qdev_new(TYPE_XEN_NET_DEVICE));
62725967ff6SDavid Woodhouse     net = XEN_NET_DEVICE(xendev);
62825967ff6SDavid Woodhouse 
62925967ff6SDavid Woodhouse     net->dev = number;
63025967ff6SDavid Woodhouse     memcpy(&net->conf.macaddr, &mac, sizeof(mac));
63125967ff6SDavid Woodhouse 
63225967ff6SDavid Woodhouse     if (qdev_realize_and_unref(DEVICE(xendev), BUS(xenbus), errp)) {
63325967ff6SDavid Woodhouse         xen_backend_set_device(backend, xendev);
63425967ff6SDavid Woodhouse         return;
63525967ff6SDavid Woodhouse     }
63625967ff6SDavid Woodhouse 
63725967ff6SDavid Woodhouse     error_prepend(errp, "realization of net device %lu failed: ",
63825967ff6SDavid Woodhouse                   number);
63925967ff6SDavid Woodhouse 
64025967ff6SDavid Woodhouse  fail:
64125967ff6SDavid Woodhouse     if (xendev) {
64225967ff6SDavid Woodhouse         object_unparent(OBJECT(xendev));
64325967ff6SDavid Woodhouse     }
64425967ff6SDavid Woodhouse }
64525967ff6SDavid Woodhouse 
xen_net_device_destroy(XenBackendInstance * backend,Error ** errp)64625967ff6SDavid Woodhouse static void xen_net_device_destroy(XenBackendInstance *backend,
64725967ff6SDavid Woodhouse                                    Error **errp)
64825967ff6SDavid Woodhouse {
64925967ff6SDavid Woodhouse     ERRP_GUARD();
65025967ff6SDavid Woodhouse     XenDevice *xendev = xen_backend_get_device(backend);
65125967ff6SDavid Woodhouse     XenNetDev *netdev = XEN_NET_DEVICE(xendev);
65225967ff6SDavid Woodhouse 
65325967ff6SDavid Woodhouse     trace_xen_netdev_destroy(netdev->dev);
65425967ff6SDavid Woodhouse 
65525967ff6SDavid Woodhouse     object_unparent(OBJECT(xendev));
65625967ff6SDavid Woodhouse }
65725967ff6SDavid Woodhouse 
65825967ff6SDavid Woodhouse static const XenBackendInfo xen_net_backend_info  = {
65925967ff6SDavid Woodhouse     .type = "qnic",
66025967ff6SDavid Woodhouse     .create = xen_net_device_create,
66125967ff6SDavid Woodhouse     .destroy = xen_net_device_destroy,
66225967ff6SDavid Woodhouse };
66325967ff6SDavid Woodhouse 
xen_net_register_backend(void)66425967ff6SDavid Woodhouse static void xen_net_register_backend(void)
66525967ff6SDavid Woodhouse {
66625967ff6SDavid Woodhouse     xen_backend_register(&xen_net_backend_info);
66725967ff6SDavid Woodhouse }
66825967ff6SDavid Woodhouse 
66925967ff6SDavid Woodhouse xen_backend_init(xen_net_register_backend);
670