xref: /qemu/hw/net/xen_nic.c (revision 06b40d250ecfa1633209c2e431a7a38acfd03a98)
1e613b064Saliguori /*
2e613b064Saliguori  *  xen paravirt network card backend
3e613b064Saliguori  *
4e613b064Saliguori  *  (c) Gerd Hoffmann <kraxel@redhat.com>
5e613b064Saliguori  *
6e613b064Saliguori  *  This program is free software; you can redistribute it and/or modify
7e613b064Saliguori  *  it under the terms of the GNU General Public License as published by
8e613b064Saliguori  *  the Free Software Foundation; under version 2 of the License.
9e613b064Saliguori  *
10e613b064Saliguori  *  This program is distributed in the hope that it will be useful,
11e613b064Saliguori  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12e613b064Saliguori  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13e613b064Saliguori  *  GNU General Public License for more details.
14e613b064Saliguori  *
15e613b064Saliguori  *  You should have received a copy of the GNU General Public License along
168167ee88SBlue Swirl  *  with this program; if not, see <http://www.gnu.org/licenses/>.
176b620ca3SPaolo Bonzini  *
186b620ca3SPaolo Bonzini  *  Contributions after 2012-01-13 are licensed under the terms of the
196b620ca3SPaolo Bonzini  *  GNU GPL, version 2 or (at your option) any later version.
20e613b064Saliguori  */
21e613b064Saliguori 
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"
27407bc4bfSDaniel P. Berrangé #include "qobject/qdict.h"
2825967ff6SDavid Woodhouse #include "qapi/error.h"
2925967ff6SDavid Woodhouse 
30e613b064Saliguori #include <sys/socket.h>
31e613b064Saliguori #include <sys/ioctl.h>
32e613b064Saliguori #include <sys/wait.h>
33e613b064Saliguori 
341422e32dSPaolo Bonzini #include "net/net.h"
357200ac3cSMark McLoughlin #include "net/checksum.h"
36658788c5SMark McLoughlin #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"
42e613b064Saliguori 
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"
47b41f6719SAnthony PERARD 
48e613b064Saliguori /* ------------------------------------------------------------- */
49e613b064Saliguori 
50e613b064Saliguori struct XenNetDev {
5125967ff6SDavid Woodhouse     struct XenDevice      xendev;  /* must be first */
5225967ff6SDavid Woodhouse     XenEventChannel       *event_channel;
5325967ff6SDavid Woodhouse     int                   dev;
54e613b064Saliguori     int                   tx_work;
5525967ff6SDavid Woodhouse     unsigned int          tx_ring_ref;
5625967ff6SDavid Woodhouse     unsigned int          rx_ring_ref;
57e613b064Saliguori     struct netif_tx_sring *txs;
58e613b064Saliguori     struct netif_rx_sring *rxs;
59e613b064Saliguori     netif_tx_back_ring_t  tx_ring;
60e613b064Saliguori     netif_rx_back_ring_t  rx_ring;
61658788c5SMark McLoughlin     NICConf               conf;
62658788c5SMark McLoughlin     NICState              *nic;
63e613b064Saliguori };
64e613b064Saliguori 
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 
70e613b064Saliguori /* ------------------------------------------------------------- */
71e613b064Saliguori 
72e613b064Saliguori static void net_tx_response(struct XenNetDev *netdev, netif_tx_request_t *txp, int8_t st)
73e613b064Saliguori {
74e613b064Saliguori     RING_IDX i = netdev->tx_ring.rsp_prod_pvt;
75e613b064Saliguori     netif_tx_response_t *resp;
76e613b064Saliguori     int notify;
77e613b064Saliguori 
78e613b064Saliguori     resp = RING_GET_RESPONSE(&netdev->tx_ring, i);
79e613b064Saliguori     resp->id     = txp->id;
80e613b064Saliguori     resp->status = st;
81e613b064Saliguori 
82e613b064Saliguori #if 0
83209cd7abSAnthony PERARD     if (txp->flags & NETTXF_extra_info) {
84e613b064Saliguori         RING_GET_RESPONSE(&netdev->tx_ring, ++i)->status = NETIF_RSP_NULL;
85209cd7abSAnthony PERARD     }
86e613b064Saliguori #endif
87e613b064Saliguori 
88e613b064Saliguori     netdev->tx_ring.rsp_prod_pvt = ++i;
89e613b064Saliguori     RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&netdev->tx_ring, notify);
90209cd7abSAnthony PERARD     if (notify) {
9125967ff6SDavid Woodhouse         xen_device_notify_event_channel(XEN_DEVICE(netdev),
9225967ff6SDavid Woodhouse                                         netdev->event_channel, NULL);
93209cd7abSAnthony PERARD     }
94e613b064Saliguori 
95e613b064Saliguori     if (i == netdev->tx_ring.req_cons) {
96e613b064Saliguori         int more_to_do;
97e613b064Saliguori         RING_FINAL_CHECK_FOR_REQUESTS(&netdev->tx_ring, more_to_do);
98209cd7abSAnthony PERARD         if (more_to_do) {
99e613b064Saliguori             netdev->tx_work++;
100e613b064Saliguori         }
101e613b064Saliguori     }
102209cd7abSAnthony PERARD }
103e613b064Saliguori 
net_tx_error(struct XenNetDev * netdev,netif_tx_request_t * txp,RING_IDX end)104e613b064Saliguori static void net_tx_error(struct XenNetDev *netdev, netif_tx_request_t *txp, RING_IDX end)
105e613b064Saliguori {
106e613b064Saliguori #if 0
107e613b064Saliguori     /*
108e613b064Saliguori      * Hmm, why netback fails everything in the ring?
109e613b064Saliguori      * Should we do that even when not supporting SG and TSO?
110e613b064Saliguori      */
111e613b064Saliguori     RING_IDX cons = netdev->tx_ring.req_cons;
112e613b064Saliguori 
113e613b064Saliguori     do {
114e613b064Saliguori         make_tx_response(netif, txp, NETIF_RSP_ERROR);
115209cd7abSAnthony PERARD         if (cons >= end) {
116e613b064Saliguori             break;
117209cd7abSAnthony PERARD         }
118e613b064Saliguori         txp = RING_GET_REQUEST(&netdev->tx_ring, cons++);
119e613b064Saliguori     } while (1);
120e613b064Saliguori     netdev->tx_ring.req_cons = cons;
121e613b064Saliguori     netif_schedule_work(netif);
122e613b064Saliguori     netif_put(netif);
123e613b064Saliguori #else
124e613b064Saliguori     net_tx_response(netdev, txp, NETIF_RSP_ERROR);
125e613b064Saliguori #endif
126e613b064Saliguori }
127e613b064Saliguori 
net_tx_packets(struct XenNetDev * netdev)12825967ff6SDavid Woodhouse static bool net_tx_packets(struct XenNetDev *netdev)
129e613b064Saliguori {
13025967ff6SDavid Woodhouse     bool done_something = false;
131e613b064Saliguori     netif_tx_request_t txreq;
132e613b064Saliguori     RING_IDX rc, rp;
133e613b064Saliguori     void *page;
134e613b064Saliguori     void *tmpbuf = NULL;
135e613b064Saliguori 
136195801d7SStefan Hajnoczi     assert(bql_locked());
13725967ff6SDavid Woodhouse 
138e613b064Saliguori     for (;;) {
139e613b064Saliguori         rc = netdev->tx_ring.req_cons;
140e613b064Saliguori         rp = netdev->tx_ring.sring->req_prod;
141e613b064Saliguori         xen_rmb(); /* Ensure we see queued requests up to 'rp'. */
142e613b064Saliguori 
143e613b064Saliguori         while ((rc != rp)) {
144209cd7abSAnthony PERARD             if (RING_REQUEST_CONS_OVERFLOW(&netdev->tx_ring, rc)) {
145e613b064Saliguori                 break;
146209cd7abSAnthony PERARD             }
147e613b064Saliguori             memcpy(&txreq, RING_GET_REQUEST(&netdev->tx_ring, rc), sizeof(txreq));
148e613b064Saliguori             netdev->tx_ring.req_cons = ++rc;
14925967ff6SDavid Woodhouse             done_something = true;
150e613b064Saliguori 
151e613b064Saliguori #if 1
152e613b064Saliguori             /* should not happen in theory, we don't announce the *
153e613b064Saliguori              * feature-{sg,gso,whatelse} flags in xenstore (yet?) */
154e613b064Saliguori             if (txreq.flags & NETTXF_extra_info) {
15525967ff6SDavid Woodhouse                 qemu_log_mask(LOG_UNIMP, "vif%u: FIXME: extra info flag\n",
15625967ff6SDavid Woodhouse                               netdev->dev);
157e613b064Saliguori                 net_tx_error(netdev, &txreq, rc);
158e613b064Saliguori                 continue;
159e613b064Saliguori             }
160e613b064Saliguori             if (txreq.flags & NETTXF_more_data) {
16125967ff6SDavid Woodhouse                 qemu_log_mask(LOG_UNIMP, "vif%u: FIXME: more data flag\n",
16225967ff6SDavid Woodhouse                               netdev->dev);
163e613b064Saliguori                 net_tx_error(netdev, &txreq, rc);
164e613b064Saliguori                 continue;
165e613b064Saliguori             }
166e613b064Saliguori #endif
167e613b064Saliguori 
168e613b064Saliguori             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);
171e613b064Saliguori                 net_tx_error(netdev, &txreq, rc);
172e613b064Saliguori                 continue;
173e613b064Saliguori             }
174e613b064Saliguori 
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);
178e613b064Saliguori                 net_tx_error(netdev, &txreq, rc);
179e613b064Saliguori                 continue;
180e613b064Saliguori             }
181e613b064Saliguori 
18225967ff6SDavid Woodhouse             trace_xen_netdev_tx(netdev->dev, txreq.gref, txreq.offset,
18325967ff6SDavid Woodhouse                                 txreq.size, txreq.flags,
184e613b064Saliguori                                 (txreq.flags & NETTXF_csum_blank)     ? " csum_blank"     : "",
185e613b064Saliguori                                 (txreq.flags & NETTXF_data_validated) ? " data_validated" : "",
186e613b064Saliguori                                 (txreq.flags & NETTXF_more_data)      ? " more_data"      : "",
187e613b064Saliguori                                 (txreq.flags & NETTXF_extra_info)     ? " extra_info"     : "");
188e613b064Saliguori 
18925967ff6SDavid Woodhouse             page = xen_device_map_grant_refs(&netdev->xendev, &txreq.gref, 1,
19025967ff6SDavid Woodhouse                                              PROT_READ, NULL);
191e613b064Saliguori             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);
195e613b064Saliguori                 net_tx_error(netdev, &txreq, rc);
196e613b064Saliguori                 continue;
197e613b064Saliguori             }
198e613b064Saliguori             if (txreq.flags & NETTXF_csum_blank) {
199e613b064Saliguori                 /* have read-only mapping -> can't fill checksum in-place */
200209cd7abSAnthony PERARD                 if (!tmpbuf) {
201a9ae1418SDavid Woodhouse                     tmpbuf = g_malloc(XEN_PAGE_SIZE);
202209cd7abSAnthony PERARD                 }
203e613b064Saliguori                 memcpy(tmpbuf, page + txreq.offset, txreq.size);
204f5746335SBin Meng                 net_checksum_calculate(tmpbuf, txreq.size, CSUM_ALL);
205b356f76dSJason Wang                 qemu_send_packet(qemu_get_queue(netdev->nic), tmpbuf,
206b356f76dSJason Wang                                  txreq.size);
207e613b064Saliguori             } else {
208b356f76dSJason Wang                 qemu_send_packet(qemu_get_queue(netdev->nic),
209b356f76dSJason Wang                                  page + txreq.offset, txreq.size);
210e613b064Saliguori             }
21125967ff6SDavid Woodhouse             xen_device_unmap_grant_refs(&netdev->xendev, page, &txreq.gref, 1,
21225967ff6SDavid Woodhouse                                         NULL);
213e613b064Saliguori             net_tx_response(netdev, &txreq, NETIF_RSP_OKAY);
214e613b064Saliguori         }
215209cd7abSAnthony PERARD         if (!netdev->tx_work) {
216e613b064Saliguori             break;
217209cd7abSAnthony PERARD         }
218e613b064Saliguori         netdev->tx_work = 0;
219e613b064Saliguori     }
2207267c094SAnthony Liguori     g_free(tmpbuf);
22125967ff6SDavid Woodhouse     return done_something;
222e613b064Saliguori }
223e613b064Saliguori 
224e613b064Saliguori /* ------------------------------------------------------------- */
225e613b064Saliguori 
net_rx_response(struct XenNetDev * netdev,netif_rx_request_t * req,int8_t st,uint16_t offset,uint16_t size,uint16_t flags)226e613b064Saliguori static void net_rx_response(struct XenNetDev *netdev,
227e613b064Saliguori                             netif_rx_request_t *req, int8_t st,
228e613b064Saliguori                             uint16_t offset, uint16_t size,
229e613b064Saliguori                             uint16_t flags)
230e613b064Saliguori {
231e613b064Saliguori     RING_IDX i = netdev->rx_ring.rsp_prod_pvt;
232e613b064Saliguori     netif_rx_response_t *resp;
233e613b064Saliguori     int notify;
234e613b064Saliguori 
235e613b064Saliguori     resp = RING_GET_RESPONSE(&netdev->rx_ring, i);
236e613b064Saliguori     resp->offset     = offset;
237e613b064Saliguori     resp->flags      = flags;
238e613b064Saliguori     resp->id         = req->id;
239e613b064Saliguori     resp->status     = (int16_t)size;
240209cd7abSAnthony PERARD     if (st < 0) {
241e613b064Saliguori         resp->status = (int16_t)st;
242209cd7abSAnthony PERARD     }
243e613b064Saliguori 
24425967ff6SDavid Woodhouse     trace_xen_netdev_rx(netdev->dev, i, resp->status, resp->flags);
245e613b064Saliguori 
246e613b064Saliguori     netdev->rx_ring.rsp_prod_pvt = ++i;
247e613b064Saliguori     RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&netdev->rx_ring, notify);
248209cd7abSAnthony PERARD     if (notify) {
24925967ff6SDavid Woodhouse         xen_device_notify_event_channel(XEN_DEVICE(netdev),
25025967ff6SDavid Woodhouse                                         netdev->event_channel, NULL);
251e613b064Saliguori     }
252209cd7abSAnthony PERARD }
253e613b064Saliguori 
254e613b064Saliguori #define NET_IP_ALIGN 2
255e613b064Saliguori 
net_rx_packet(NetClientState * nc,const uint8_t * buf,size_t size)2564e68f7a0SStefan Hajnoczi static ssize_t net_rx_packet(NetClientState *nc, const uint8_t *buf, size_t size)
257e613b064Saliguori {
258cc1f0f45SJason Wang     struct XenNetDev *netdev = qemu_get_nic_opaque(nc);
259e613b064Saliguori     netif_rx_request_t rxreq;
260e613b064Saliguori     RING_IDX rc, rp;
261e613b064Saliguori     void *page;
262e613b064Saliguori 
263195801d7SStefan Hajnoczi     assert(bql_locked());
26425967ff6SDavid Woodhouse 
26525967ff6SDavid Woodhouse     if (xen_device_backend_get_state(&netdev->xendev) != XenbusStateConnected) {
2664f1c942bSMark McLoughlin         return -1;
267209cd7abSAnthony PERARD     }
268e613b064Saliguori 
269e613b064Saliguori     rc = netdev->rx_ring.req_cons;
270e613b064Saliguori     rp = netdev->rx_ring.sring->req_prod;
271e613b064Saliguori     xen_rmb(); /* Ensure we see queued requests up to 'rp'. */
272e613b064Saliguori 
273e613b064Saliguori     if (rc == rp || RING_REQUEST_CONS_OVERFLOW(&netdev->rx_ring, rc)) {
2747bba83bfSFam Zheng         return 0;
275e613b064Saliguori     }
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);
2804f1c942bSMark McLoughlin         return -1;
281e613b064Saliguori     }
282e613b064Saliguori 
283e613b064Saliguori     memcpy(&rxreq, RING_GET_REQUEST(&netdev->rx_ring, rc), sizeof(rxreq));
284e613b064Saliguori     netdev->rx_ring.req_cons = ++rc;
285e613b064Saliguori 
28625967ff6SDavid Woodhouse     page = xen_device_map_grant_refs(&netdev->xendev, &rxreq.gref, 1,
28725967ff6SDavid Woodhouse                                      PROT_WRITE, NULL);
288e613b064Saliguori     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);
292e613b064Saliguori         net_rx_response(netdev, &rxreq, NETIF_RSP_ERROR, 0, 0, 0);
2934f1c942bSMark McLoughlin         return -1;
294e613b064Saliguori     }
295e613b064Saliguori     memcpy(page + NET_IP_ALIGN, buf, size);
29625967ff6SDavid Woodhouse     xen_device_unmap_grant_refs(&netdev->xendev, page, &rxreq.gref, 1, NULL);
297e613b064Saliguori     net_rx_response(netdev, &rxreq, NETIF_RSP_OKAY, NET_IP_ALIGN, size, 0);
2984f1c942bSMark McLoughlin 
2994f1c942bSMark McLoughlin     return size;
300e613b064Saliguori }
301e613b064Saliguori 
302e613b064Saliguori /* ------------------------------------------------------------- */
303e613b064Saliguori 
304658788c5SMark McLoughlin static NetClientInfo net_xen_info = {
305f394b2e2SEric Blake     .type = NET_CLIENT_DRIVER_NIC,
306658788c5SMark McLoughlin     .size = sizeof(NICState),
307658788c5SMark McLoughlin     .receive = net_rx_packet,
308658788c5SMark McLoughlin };
309658788c5SMark McLoughlin 
xen_netdev_realize(XenDevice * xendev,Error ** errp)31025967ff6SDavid Woodhouse static void xen_netdev_realize(XenDevice *xendev, Error **errp)
311e613b064Saliguori {
31225967ff6SDavid Woodhouse     ERRP_GUARD();
31325967ff6SDavid Woodhouse     XenNetDev *netdev = XEN_NET_DEVICE(xendev);
31425967ff6SDavid Woodhouse     NetClientState *nc;
315e613b064Saliguori 
31625967ff6SDavid Woodhouse     qemu_macaddr_default_if_unset(&netdev->conf.macaddr);
317e613b064Saliguori 
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]);
325658788c5SMark McLoughlin 
326658788c5SMark McLoughlin     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);
330658788c5SMark McLoughlin 
33125967ff6SDavid Woodhouse     nc = qemu_get_queue(netdev->nic);
33225967ff6SDavid Woodhouse     qemu_format_nic_info_str(nc, netdev->conf.macaddr.a);
333e613b064Saliguori 
334e613b064Saliguori     /* 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);
337e613b064Saliguori 
33825967ff6SDavid Woodhouse     trace_xen_netdev_realize(netdev->dev, nc->info_str, nc->peer ?
33925967ff6SDavid Woodhouse                              nc->peer->name : "(none)");
340e613b064Saliguori }
341e613b064Saliguori 
net_event(void * _xendev)34225967ff6SDavid Woodhouse static bool net_event(void *_xendev)
343e613b064Saliguori {
34425967ff6SDavid Woodhouse     XenNetDev *netdev = XEN_NET_DEVICE(_xendev);
34525967ff6SDavid Woodhouse     bool done_something;
346e613b064Saliguori 
34725967ff6SDavid Woodhouse     done_something = net_tx_packets(netdev);
34825967ff6SDavid Woodhouse     qemu_flush_queued_packets(qemu_get_queue(netdev->nic));
34925967ff6SDavid Woodhouse     return done_something;
350209cd7abSAnthony PERARD }
351e613b064Saliguori 
xen_netdev_connect(XenDevice * xendev,Error ** errp)35225967ff6SDavid Woodhouse static bool xen_netdev_connect(XenDevice *xendev, Error **errp)
35325967ff6SDavid Woodhouse {
3548538ceecSZhao 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) {
380e613b064Saliguori         rx_copy = 0;
381209cd7abSAnthony PERARD     }
382e613b064Saliguori     if (rx_copy == 0) {
38325967ff6SDavid Woodhouse         error_setg(errp, "frontend doesn't support rx-copy");
38425967ff6SDavid Woodhouse         return false;
385e613b064Saliguori     }
386e613b064Saliguori 
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;
403209cd7abSAnthony PERARD     }
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);
407e613b064Saliguori 
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;
414e613b064Saliguori     }
415e613b064Saliguori 
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)
424e613b064Saliguori {
42525967ff6SDavid Woodhouse     XenNetDev *netdev = XEN_NET_DEVICE(xendev);
426e613b064Saliguori 
42725967ff6SDavid Woodhouse     trace_xen_netdev_disconnect(netdev->dev);
428e613b064Saliguori 
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     }
439e613b064Saliguori     if (netdev->txs) {
44025967ff6SDavid Woodhouse         xen_device_unmap_grant_refs(xendev, netdev->txs,
44125967ff6SDavid Woodhouse                                     &netdev->tx_ring_ref, 1, errp);
442e613b064Saliguori         netdev->txs = NULL;
443e613b064Saliguori     }
444e613b064Saliguori     if (netdev->rxs) {
44525967ff6SDavid Woodhouse         xen_device_unmap_grant_refs(xendev, netdev->rxs,
44625967ff6SDavid Woodhouse                                     &netdev->rx_ring_ref, 1, errp);
447e613b064Saliguori         netdev->rxs = NULL;
448e613b064Saliguori     }
449e613b064Saliguori }
450e613b064Saliguori 
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)
457e613b064Saliguori {
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;
467e613b064Saliguori         }
468e613b064Saliguori 
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)
508e613b064Saliguori {
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         int idx = (xen_mode == XEN_EMULATE) ? 0 : 1;
514e4e113ecSDavid Woodhouse         Error *local_err = NULL;
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) {
519e4e113ecSDavid Woodhouse             value = xs_node_read(xenbus->xsh, XBT_NULL, NULL, &local_err,
52025967ff6SDavid Woodhouse                                  "/local/domain/%u/device/vif/%u",
52125967ff6SDavid Woodhouse                                  xendev->frontend_id, idx);
52225967ff6SDavid Woodhouse             if (!value) {
52325967ff6SDavid Woodhouse                 if (errno == ENOENT) {
52425967ff6SDavid Woodhouse                     netdev->dev = idx;
525e4e113ecSDavid Woodhouse                     error_free(local_err);
52625967ff6SDavid Woodhouse                     goto found;
52725967ff6SDavid Woodhouse                 }
528e4e113ecSDavid Woodhouse                 error_propagate(errp, local_err);
52925967ff6SDavid Woodhouse                 return NULL;
53025967ff6SDavid Woodhouse             }
53125967ff6SDavid Woodhouse             free(value);
53225967ff6SDavid Woodhouse             idx++;
53325967ff6SDavid Woodhouse         }
53425967ff6SDavid Woodhouse         error_setg(errp, "cannot find device index for netdev device");
53525967ff6SDavid Woodhouse         return NULL;
53625967ff6SDavid Woodhouse     }
53725967ff6SDavid Woodhouse  found:
53825967ff6SDavid Woodhouse     return g_strdup_printf("%u", netdev->dev);
53925967ff6SDavid Woodhouse }
54025967ff6SDavid Woodhouse 
xen_netdev_unrealize(XenDevice * xendev)54125967ff6SDavid Woodhouse static void xen_netdev_unrealize(XenDevice *xendev)
54225967ff6SDavid Woodhouse {
54325967ff6SDavid Woodhouse     XenNetDev *netdev = XEN_NET_DEVICE(xendev);
54425967ff6SDavid Woodhouse 
54525967ff6SDavid Woodhouse     trace_xen_netdev_unrealize(netdev->dev);
54625967ff6SDavid Woodhouse 
54725967ff6SDavid Woodhouse     /* Disconnect from the frontend in case this has not already happened */
54825967ff6SDavid Woodhouse     xen_netdev_disconnect(xendev, NULL);
549e613b064Saliguori 
550d4685837SChen Gang     if (netdev->nic) {
551d4685837SChen Gang         qemu_del_nic(netdev->nic);
552d4685837SChen Gang     }
553e613b064Saliguori }
554e613b064Saliguori 
555e613b064Saliguori /* ------------------------------------------------------------- */
556e613b064Saliguori 
557e732f00fSRichard Henderson static const Property xen_netdev_properties[] = {
55825967ff6SDavid Woodhouse     DEFINE_NIC_PROPERTIES(XenNetDev, conf),
55925967ff6SDavid Woodhouse     DEFINE_PROP_INT32("idx", XenNetDev, dev, -1),
560e613b064Saliguori };
56125967ff6SDavid Woodhouse 
xen_netdev_class_init(ObjectClass * class,const void * data)562*12d1a768SPhilippe Mathieu-Daudé static void xen_netdev_class_init(ObjectClass *class, const void *data)
56325967ff6SDavid Woodhouse {
56425967ff6SDavid Woodhouse     DeviceClass *dev_class = DEVICE_CLASS(class);
56525967ff6SDavid Woodhouse     XenDeviceClass *xendev_class = XEN_DEVICE_CLASS(class);
56625967ff6SDavid Woodhouse 
56725967ff6SDavid Woodhouse     xendev_class->backend = "qnic";
56825967ff6SDavid Woodhouse     xendev_class->device = "vif";
56925967ff6SDavid Woodhouse     xendev_class->get_name = xen_netdev_get_name;
57025967ff6SDavid Woodhouse     xendev_class->realize = xen_netdev_realize;
57125967ff6SDavid Woodhouse     xendev_class->frontend_changed = xen_netdev_frontend_changed;
57225967ff6SDavid Woodhouse     xendev_class->unrealize = xen_netdev_unrealize;
57325967ff6SDavid Woodhouse     set_bit(DEVICE_CATEGORY_NETWORK, dev_class->categories);
57425967ff6SDavid Woodhouse     dev_class->user_creatable = true;
57525967ff6SDavid Woodhouse 
57625967ff6SDavid Woodhouse     device_class_set_props(dev_class, xen_netdev_properties);
57725967ff6SDavid Woodhouse }
57825967ff6SDavid Woodhouse 
57925967ff6SDavid Woodhouse static const TypeInfo xen_net_type_info = {
58025967ff6SDavid Woodhouse     .name = TYPE_XEN_NET_DEVICE,
58125967ff6SDavid Woodhouse     .parent = TYPE_XEN_DEVICE,
58225967ff6SDavid Woodhouse     .instance_size = sizeof(XenNetDev),
58325967ff6SDavid Woodhouse     .class_init = xen_netdev_class_init,
58425967ff6SDavid Woodhouse };
58525967ff6SDavid Woodhouse 
xen_net_register_types(void)58625967ff6SDavid Woodhouse static void xen_net_register_types(void)
58725967ff6SDavid Woodhouse {
58825967ff6SDavid Woodhouse     type_register_static(&xen_net_type_info);
58925967ff6SDavid Woodhouse }
59025967ff6SDavid Woodhouse 
type_init(xen_net_register_types)59125967ff6SDavid Woodhouse type_init(xen_net_register_types)
59225967ff6SDavid Woodhouse 
59325967ff6SDavid Woodhouse /* Called to instantiate a XenNetDev when the backend is detected. */
59425967ff6SDavid Woodhouse static void xen_net_device_create(XenBackendInstance *backend,
59525967ff6SDavid Woodhouse                                   QDict *opts, Error **errp)
59625967ff6SDavid Woodhouse {
59725967ff6SDavid Woodhouse     ERRP_GUARD();
59825967ff6SDavid Woodhouse     XenBus *xenbus = xen_backend_get_bus(backend);
59925967ff6SDavid Woodhouse     const char *name = xen_backend_get_name(backend);
60025967ff6SDavid Woodhouse     XenDevice *xendev = NULL;
60125967ff6SDavid Woodhouse     unsigned long number;
60225967ff6SDavid Woodhouse     const char *macstr;
60325967ff6SDavid Woodhouse     XenNetDev *net;
60425967ff6SDavid Woodhouse     MACAddr mac;
60525967ff6SDavid Woodhouse 
60625967ff6SDavid Woodhouse     if (qemu_strtoul(name, NULL, 10, &number) || number >= INT_MAX) {
60725967ff6SDavid Woodhouse         error_setg(errp, "failed to parse name '%s'", name);
60825967ff6SDavid Woodhouse         goto fail;
60925967ff6SDavid Woodhouse     }
61025967ff6SDavid Woodhouse 
61125967ff6SDavid Woodhouse     trace_xen_netdev_create(number);
61225967ff6SDavid Woodhouse 
61325967ff6SDavid Woodhouse     macstr = qdict_get_try_str(opts, "mac");
61425967ff6SDavid Woodhouse     if (macstr == NULL) {
61525967ff6SDavid Woodhouse         error_setg(errp, "no MAC address found");
61625967ff6SDavid Woodhouse         goto fail;
61725967ff6SDavid Woodhouse     }
61825967ff6SDavid Woodhouse 
61925967ff6SDavid Woodhouse     if (net_parse_macaddr(mac.a, macstr) < 0) {
62025967ff6SDavid Woodhouse         error_setg(errp, "failed to parse MAC address");
62125967ff6SDavid Woodhouse         goto fail;
62225967ff6SDavid Woodhouse     }
62325967ff6SDavid Woodhouse 
62425967ff6SDavid Woodhouse     xendev = XEN_DEVICE(qdev_new(TYPE_XEN_NET_DEVICE));
62525967ff6SDavid Woodhouse     net = XEN_NET_DEVICE(xendev);
62625967ff6SDavid Woodhouse 
62725967ff6SDavid Woodhouse     net->dev = number;
62825967ff6SDavid Woodhouse     memcpy(&net->conf.macaddr, &mac, sizeof(mac));
62925967ff6SDavid Woodhouse 
63025967ff6SDavid Woodhouse     if (qdev_realize_and_unref(DEVICE(xendev), BUS(xenbus), errp)) {
63125967ff6SDavid Woodhouse         xen_backend_set_device(backend, xendev);
63225967ff6SDavid Woodhouse         return;
63325967ff6SDavid Woodhouse     }
63425967ff6SDavid Woodhouse 
63525967ff6SDavid Woodhouse     error_prepend(errp, "realization of net device %lu failed: ",
63625967ff6SDavid Woodhouse                   number);
63725967ff6SDavid Woodhouse 
63825967ff6SDavid Woodhouse  fail:
63925967ff6SDavid Woodhouse     if (xendev) {
64025967ff6SDavid Woodhouse         object_unparent(OBJECT(xendev));
64125967ff6SDavid Woodhouse     }
64225967ff6SDavid Woodhouse }
64325967ff6SDavid Woodhouse 
xen_net_device_destroy(XenBackendInstance * backend,Error ** errp)64425967ff6SDavid Woodhouse static void xen_net_device_destroy(XenBackendInstance *backend,
64525967ff6SDavid Woodhouse                                    Error **errp)
64625967ff6SDavid Woodhouse {
64725967ff6SDavid Woodhouse     ERRP_GUARD();
64825967ff6SDavid Woodhouse     XenDevice *xendev = xen_backend_get_device(backend);
64925967ff6SDavid Woodhouse     XenNetDev *netdev = XEN_NET_DEVICE(xendev);
65025967ff6SDavid Woodhouse 
65125967ff6SDavid Woodhouse     trace_xen_netdev_destroy(netdev->dev);
65225967ff6SDavid Woodhouse 
65325967ff6SDavid Woodhouse     object_unparent(OBJECT(xendev));
65425967ff6SDavid Woodhouse }
65525967ff6SDavid Woodhouse 
65625967ff6SDavid Woodhouse static const XenBackendInfo xen_net_backend_info  = {
65725967ff6SDavid Woodhouse     .type = "qnic",
65825967ff6SDavid Woodhouse     .create = xen_net_device_create,
65925967ff6SDavid Woodhouse     .destroy = xen_net_device_destroy,
66025967ff6SDavid Woodhouse };
66125967ff6SDavid Woodhouse 
xen_net_register_backend(void)66225967ff6SDavid Woodhouse static void xen_net_register_backend(void)
66325967ff6SDavid Woodhouse {
66425967ff6SDavid Woodhouse     xen_backend_register(&xen_net_backend_info);
66525967ff6SDavid Woodhouse }
66625967ff6SDavid Woodhouse 
66725967ff6SDavid Woodhouse xen_backend_init(xen_net_register_backend);
668