uint8_t nouni;
uint8_t nobcast;
uint8_t vhost_started;
- VMChangeStateEntry *vmstate;
struct {
int in_use;
int first_multi;
VirtIONet *n = to_virtio_net(vdev);
struct virtio_net_config netcfg;
- netcfg.status = n->status;
+ stw_p(&netcfg.status, n->status);
memcpy(netcfg.mac, n->mac, ETH_ALEN);
memcpy(config, &netcfg, sizeof(netcfg));
}
}
}
-static void virtio_net_set_link_status(VLANClientState *nc)
+static bool virtio_net_started(VirtIONet *n, uint8_t status)
+{
+ return (status & VIRTIO_CONFIG_S_DRIVER_OK) &&
+ (n->status & VIRTIO_NET_S_LINK_UP) && n->vdev.vm_running;
+}
+
+static void virtio_net_vhost_status(VirtIONet *n, uint8_t status)
+{
+ if (!n->nic->nc.peer) {
+ return;
+ }
+ if (n->nic->nc.peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) {
+ return;
+ }
+
+ if (!tap_get_vhost_net(n->nic->nc.peer)) {
+ return;
+ }
+ if (!!n->vhost_started == virtio_net_started(n, status) &&
+ !n->nic->nc.peer->link_down) {
+ return;
+ }
+ if (!n->vhost_started) {
+ int r;
+ if (!vhost_net_query(tap_get_vhost_net(n->nic->nc.peer), &n->vdev)) {
+ return;
+ }
+ r = vhost_net_start(tap_get_vhost_net(n->nic->nc.peer), &n->vdev);
+ if (r < 0) {
+ error_report("unable to start vhost net: %d: "
+ "falling back on userspace virtio", -r);
+ } else {
+ n->vhost_started = 1;
+ }
+ } else {
+ vhost_net_stop(tap_get_vhost_net(n->nic->nc.peer), &n->vdev);
+ n->vhost_started = 0;
+ }
+}
+
+static void virtio_net_set_status(struct VirtIODevice *vdev, uint8_t status)
+{
+ VirtIONet *n = to_virtio_net(vdev);
+
+ virtio_net_vhost_status(n, status);
+
+ if (!n->tx_waiting) {
+ return;
+ }
+
+ if (virtio_net_started(n, status) && !n->vhost_started) {
+ if (n->tx_timer) {
+ qemu_mod_timer(n->tx_timer,
+ qemu_get_clock_ns(vm_clock) + n->tx_timeout);
+ } else {
+ qemu_bh_schedule(n->tx_bh);
+ }
+ } else {
+ if (n->tx_timer) {
+ qemu_del_timer(n->tx_timer);
+ } else {
+ qemu_bh_cancel(n->tx_bh);
+ }
+ }
+}
+
+static void virtio_net_set_link_status(NetClientState *nc)
{
VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque;
uint16_t old_status = n->status;
if (n->status != old_status)
virtio_notify_config(&n->vdev);
+
+ virtio_net_set_status(&n->vdev, n->vdev.status);
}
static void virtio_net_reset(VirtIODevice *vdev)
n->nomulti = 0;
n->nouni = 0;
n->nobcast = 0;
- if (n->vhost_started) {
- vhost_net_stop(tap_get_vhost_net(n->nic->nc.peer), vdev);
- n->vhost_started = 0;
- }
/* Flush any MAC and VLAN filter table state */
n->mac_table.in_use = 0;
if (!n->nic->nc.peer)
return 0;
- if (n->nic->nc.peer->info->type != NET_CLIENT_TYPE_TAP)
+ if (n->nic->nc.peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP)
return 0;
n->has_vnet_hdr = tap_has_vnet_hdr(n->nic->nc.peer);
}
if (!n->nic->nc.peer ||
- n->nic->nc.peer->info->type != NET_CLIENT_TYPE_TAP) {
+ n->nic->nc.peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) {
return features;
}
if (!tap_get_vhost_net(n->nic->nc.peer)) {
(features >> VIRTIO_NET_F_GUEST_UFO) & 1);
}
if (!n->nic->nc.peer ||
- n->nic->nc.peer->info->type != NET_CLIENT_TYPE_TAP) {
+ n->nic->nc.peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) {
return;
}
if (!tap_get_vhost_net(n->nic->nc.peer)) {
uint8_t on;
if (elem->out_num != 2 || elem->out_sg[1].iov_len != sizeof(on)) {
- fprintf(stderr, "virtio-net ctrl invalid rx mode command\n");
+ error_report("virtio-net ctrl invalid rx mode command");
exit(1);
}
n->mac_table.multi_overflow = 0;
memset(n->mac_table.macs, 0, MAC_TABLE_ENTRIES * ETH_ALEN);
- mac_data.entries = ldl_le_p(elem->out_sg[1].iov_base);
+ mac_data.entries = ldl_p(elem->out_sg[1].iov_base);
if (sizeof(mac_data.entries) +
(mac_data.entries * ETH_ALEN) > elem->out_sg[1].iov_len)
n->mac_table.first_multi = n->mac_table.in_use;
- mac_data.entries = ldl_le_p(elem->out_sg[2].iov_base);
+ mac_data.entries = ldl_p(elem->out_sg[2].iov_base);
if (sizeof(mac_data.entries) +
(mac_data.entries * ETH_ALEN) > elem->out_sg[2].iov_len)
uint16_t vid;
if (elem->out_num != 2 || elem->out_sg[1].iov_len != sizeof(vid)) {
- fprintf(stderr, "virtio-net ctrl invalid vlan command\n");
+ error_report("virtio-net ctrl invalid vlan command");
return VIRTIO_NET_ERR;
}
- vid = lduw_le_p(elem->out_sg[1].iov_base);
+ vid = lduw_p(elem->out_sg[1].iov_base);
if (vid >= MAX_VLAN)
return VIRTIO_NET_ERR;
while (virtqueue_pop(vq, &elem)) {
if ((elem.in_num < 1) || (elem.out_num < 1)) {
- fprintf(stderr, "virtio-net ctrl missing headers\n");
+ error_report("virtio-net ctrl missing headers");
exit(1);
}
if (elem.out_sg[0].iov_len < sizeof(ctrl) ||
elem.in_sg[elem.in_num - 1].iov_len < sizeof(status)) {
- fprintf(stderr, "virtio-net ctrl header not in correct element\n");
+ error_report("virtio-net ctrl header not in correct element");
exit(1);
}
qemu_notify_event();
}
-static int virtio_net_can_receive(VLANClientState *nc)
+static int virtio_net_can_receive(NetClientState *nc)
{
VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque;
+ if (!n->vdev.vm_running) {
+ return 0;
+ }
if (!virtio_queue_ready(n->rx_vq) ||
!(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK))
return 0;
}
-static ssize_t virtio_net_receive(VLANClientState *nc, const uint8_t *buf, size_t size)
+static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf, size_t size)
{
VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque;
struct virtio_net_hdr_mrg_rxbuf *mhdr = NULL;
if (virtqueue_pop(n->rx_vq, &elem) == 0) {
if (i == 0)
return -1;
- fprintf(stderr, "virtio-net unexpected empty queue: "
+ error_report("virtio-net unexpected empty queue: "
"i %zd mergeable %d offset %zd, size %zd, "
- "guest hdr len %zd, host hdr len %zd guest features 0x%x\n",
+ "guest hdr len %zd, host hdr len %zd guest features 0x%x",
i, n->mergeable_rx_bufs, offset, size,
guest_hdr_len, host_hdr_len, n->vdev.guest_features);
exit(1);
}
if (elem.in_num < 1) {
- fprintf(stderr, "virtio-net receive queue contains no in buffers\n");
+ error_report("virtio-net receive queue contains no in buffers");
exit(1);
}
if (!n->mergeable_rx_bufs && elem.in_sg[0].iov_len != guest_hdr_len) {
- fprintf(stderr, "virtio-net header not in first element\n");
+ error_report("virtio-net header not in first element");
exit(1);
}
}
/* copy in packet. ugh */
- len = iov_from_buf(sg, elem.in_num,
+ len = iov_from_buf(sg, elem.in_num, 0,
buf + offset, size - offset);
total += len;
offset += len;
* Otherwise, drop it. */
if (!n->mergeable_rx_bufs && offset < size) {
#if 0
- fprintf(stderr, "virtio-net truncated non-mergeable packet: "
-
- "i %zd mergeable %d offset %zd, size %zd, "
- "guest hdr len %zd, host hdr len %zd\n",
- i, n->mergeable_rx_bufs,
- offset, size, guest_hdr_len, host_hdr_len);
+ error_report("virtio-net truncated non-mergeable packet: "
+ "i %zd mergeable %d offset %zd, size %zd, "
+ "guest hdr len %zd, host hdr len %zd",
+ i, n->mergeable_rx_bufs,
+ offset, size, guest_hdr_len, host_hdr_len);
#endif
return size;
}
virtqueue_fill(n->rx_vq, &elem, total, i++);
}
- if (mhdr)
- mhdr->num_buffers = i;
+ if (mhdr) {
+ stw_p(&mhdr->num_buffers, i);
+ }
virtqueue_flush(n->rx_vq, i);
virtio_notify(&n->vdev, n->rx_vq);
static int32_t virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq);
-static void virtio_net_tx_complete(VLANClientState *nc, ssize_t len)
+static void virtio_net_tx_complete(NetClientState *nc, ssize_t len)
{
VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque;
{
VirtQueueElement elem;
int32_t num_packets = 0;
-
if (!(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK)) {
return num_packets;
}
+ assert(n->vdev.vm_running);
+
if (n->async_tx.elem.out_num) {
virtio_queue_set_notification(n->tx_vq, 0);
return num_packets;
sizeof(struct virtio_net_hdr);
if (out_num < 1 || out_sg->iov_len != hdr_len) {
- fprintf(stderr, "virtio-net header not in first element\n");
+ error_report("virtio-net header not in first element");
exit(1);
}
{
VirtIONet *n = to_virtio_net(vdev);
+ /* This happens when device was stopped but VCPU wasn't. */
+ if (!n->vdev.vm_running) {
+ n->tx_waiting = 1;
+ return;
+ }
+
if (n->tx_waiting) {
virtio_queue_set_notification(vq, 1);
qemu_del_timer(n->tx_timer);
virtio_net_flush_tx(n, vq);
} else {
qemu_mod_timer(n->tx_timer,
- qemu_get_clock(vm_clock) + n->tx_timeout);
+ qemu_get_clock_ns(vm_clock) + n->tx_timeout);
n->tx_waiting = 1;
virtio_queue_set_notification(vq, 0);
}
if (unlikely(n->tx_waiting)) {
return;
}
+ n->tx_waiting = 1;
+ /* This happens when device was stopped but VCPU wasn't. */
+ if (!n->vdev.vm_running) {
+ return;
+ }
virtio_queue_set_notification(vq, 0);
qemu_bh_schedule(n->tx_bh);
- n->tx_waiting = 1;
}
static void virtio_net_tx_timer(void *opaque)
{
VirtIONet *n = opaque;
+ assert(n->vdev.vm_running);
n->tx_waiting = 0;
VirtIONet *n = opaque;
int32_t ret;
+ assert(n->vdev.vm_running);
+
n->tx_waiting = 0;
/* Just in case the driver is not ready on more */
{
VirtIONet *n = opaque;
- if (n->vhost_started) {
- /* TODO: should we really stop the backend?
- * If we don't, it might keep writing to memory. */
- vhost_net_stop(tap_get_vhost_net(n->nic->nc.peer), &n->vdev);
- n->vhost_started = 0;
- }
+ /* At this point, backend must be stopped, otherwise
+ * it might keep writing to memory. */
+ assert(!n->vhost_started);
virtio_save(&n->vdev, f);
qemu_put_buffer(f, n->mac, ETH_ALEN);
{
VirtIONet *n = opaque;
int i;
+ int ret;
if (version_id < 2 || version_id > VIRTIO_NET_VM_VERSION)
return -EINVAL;
- virtio_load(&n->vdev, f);
+ ret = virtio_load(&n->vdev, f);
+ if (ret) {
+ return ret;
+ }
qemu_get_buffer(f, n->mac, ETH_ALEN);
n->tx_waiting = qemu_get_be32(f);
}
}
n->mac_table.first_multi = i;
-
- if (n->tx_waiting) {
- if (n->tx_timer) {
- qemu_mod_timer(n->tx_timer,
- qemu_get_clock(vm_clock) + n->tx_timeout);
- } else {
- qemu_bh_schedule(n->tx_bh);
- }
- }
return 0;
}
-static void virtio_net_cleanup(VLANClientState *nc)
+static void virtio_net_cleanup(NetClientState *nc)
{
VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque;
}
static NetClientInfo net_virtio_info = {
- .type = NET_CLIENT_TYPE_NIC,
+ .type = NET_CLIENT_OPTIONS_KIND_NIC,
.size = sizeof(NICState),
.can_receive = virtio_net_can_receive,
.receive = virtio_net_receive,
.link_status_changed = virtio_net_set_link_status,
};
-static void virtio_net_set_status(struct VirtIODevice *vdev, uint8_t status)
-{
- VirtIONet *n = to_virtio_net(vdev);
- if (!n->nic->nc.peer) {
- return;
- }
- if (n->nic->nc.peer->info->type != NET_CLIENT_TYPE_TAP) {
- return;
- }
-
- if (!tap_get_vhost_net(n->nic->nc.peer)) {
- return;
- }
- if (!!n->vhost_started == !!(status & VIRTIO_CONFIG_S_DRIVER_OK)) {
- return;
- }
- if (status & VIRTIO_CONFIG_S_DRIVER_OK) {
- int r = vhost_net_start(tap_get_vhost_net(n->nic->nc.peer), vdev);
- if (r < 0) {
- fprintf(stderr, "unable to start vhost net: %d: "
- "falling back on userspace virtio\n", -r);
- } else {
- n->vhost_started = 1;
- }
- } else {
- vhost_net_stop(tap_get_vhost_net(n->nic->nc.peer), vdev);
- n->vhost_started = 0;
- }
-}
-
-static void virtio_net_vmstate_change(void *opaque, int running, int reason)
-{
- VirtIONet *n = opaque;
- uint8_t status = running ? VIRTIO_CONFIG_S_DRIVER_OK : 0;
- /* This is called when vm is started/stopped,
- * it will start/stop vhost backend if * appropriate
- * e.g. after migration. */
- virtio_net_set_status(&n->vdev, n->vdev.status & status);
-}
-
VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf,
virtio_net_conf *net)
{
n->rx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_rx);
if (net->tx && strcmp(net->tx, "timer") && strcmp(net->tx, "bh")) {
- fprintf(stderr, "virtio-net: "
- "Unknown option tx=%s, valid options: \"timer\" \"bh\"\n",
- net->tx);
- fprintf(stderr, "Defaulting to \"bh\"\n");
+ error_report("virtio-net: "
+ "Unknown option tx=%s, valid options: \"timer\" \"bh\"",
+ net->tx);
+ error_report("Defaulting to \"bh\"");
}
if (net->tx && !strcmp(net->tx, "timer")) {
n->tx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_tx_timer);
- n->tx_timer = qemu_new_timer(vm_clock, virtio_net_tx_timer, n);
+ n->tx_timer = qemu_new_timer_ns(vm_clock, virtio_net_tx_timer, n);
n->tx_timeout = net->txtimer;
} else {
n->tx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_tx_bh);
memcpy(&n->mac[0], &conf->macaddr, sizeof(n->mac));
n->status = VIRTIO_NET_S_LINK_UP;
- n->nic = qemu_new_nic(&net_virtio_info, conf, dev->info->name, dev->id, n);
+ n->nic = qemu_new_nic(&net_virtio_info, conf, object_get_typename(OBJECT(dev)), dev->id, n);
qemu_format_nic_info_str(&n->nic->nc, conf->macaddr.a);
n->mergeable_rx_bufs = 0;
n->promisc = 1; /* for compatibility */
- n->mac_table.macs = qemu_mallocz(MAC_TABLE_ENTRIES * ETH_ALEN);
+ n->mac_table.macs = g_malloc0(MAC_TABLE_ENTRIES * ETH_ALEN);
- n->vlans = qemu_mallocz(MAX_VLAN >> 3);
+ n->vlans = g_malloc0(MAX_VLAN >> 3);
n->qdev = dev;
register_savevm(dev, "virtio-net", -1, VIRTIO_NET_VM_VERSION,
virtio_net_save, virtio_net_load, n);
- n->vmstate = qemu_add_vm_change_state_handler(virtio_net_vmstate_change, n);
+
+ add_boot_device_path(conf->bootindex, dev, "/ethernet-phy@0");
return &n->vdev;
}
void virtio_net_exit(VirtIODevice *vdev)
{
VirtIONet *n = DO_UPCAST(VirtIONet, vdev, vdev);
- qemu_del_vm_change_state_handler(n->vmstate);
- if (n->vhost_started) {
- vhost_net_stop(tap_get_vhost_net(n->nic->nc.peer), vdev);
- }
+ /* This will stop vhost backend if appropriate. */
+ virtio_net_set_status(vdev, 0);
qemu_purge_queued_packets(&n->nic->nc);
unregister_savevm(n->qdev, "virtio-net", n);
- qemu_free(n->mac_table.macs);
- qemu_free(n->vlans);
+ g_free(n->mac_table.macs);
+ g_free(n->vlans);
if (n->tx_timer) {
qemu_del_timer(n->tx_timer);
qemu_bh_delete(n->tx_bh);
}
- virtio_cleanup(&n->vdev);
qemu_del_vlan_client(&n->nic->nc);
+ virtio_cleanup(&n->vdev);
}