diff options
-rw-r--r-- | Makefile.objs | 3 | ||||
-rw-r--r-- | cpus.c | 2 | ||||
-rw-r--r-- | hw/pc_piix.c | 20 | ||||
-rw-r--r-- | hw/pci.c | 24 | ||||
-rw-r--r-- | hw/pci.h | 7 | ||||
-rw-r--r-- | hw/pcie.c | 8 | ||||
-rw-r--r-- | hw/pcie_aer.c | 111 | ||||
-rw-r--r-- | hw/virtio-net.c | 69 | ||||
-rw-r--r-- | migration.c | 2 | ||||
-rw-r--r-- | net.c | 4 | ||||
-rw-r--r-- | net/socket.c | 52 | ||||
-rw-r--r-- | qemu-options.hx | 11 | ||||
-rw-r--r-- | savevm.c | 4 |
13 files changed, 195 insertions, 122 deletions
diff --git a/Makefile.objs b/Makefile.objs index cebb9458f1..07e46140d0 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -168,7 +168,8 @@ hw-obj-$(CONFIG_VIRTIO) += virtio.o virtio-console.o hw-obj-y += fw_cfg.o # FIXME: Core PCI code and its direct dependencies are required by the # QMP query-pci command. -hw-obj-y += pci.o pci_bridge.o msix.o msi.o +hw-obj-y += pci.o pci_bridge.o +hw-obj-$(CONFIG_PCI) += msix.o msi.o hw-obj-$(CONFIG_PCI) += pci_host.o pcie_host.o hw-obj-$(CONFIG_PCI) += ioh3420.o xio3130_upstream.o xio3130_downstream.o hw-obj-y += watchdog.o @@ -111,6 +111,8 @@ static void do_vm_stop(int reason) vm_running = 0; pause_all_vcpus(); vm_state_notify(0, reason); + qemu_aio_flush(); + bdrv_flush_all(); monitor_protocol_event(QEVENT_STOP, NULL); } } diff --git a/hw/pc_piix.c b/hw/pc_piix.c index 7d29d43190..a2fb554aa2 100644 --- a/hw/pc_piix.c +++ b/hw/pc_piix.c @@ -217,6 +217,14 @@ static QEMUMachine pc_machine = { .desc = "Standard PC", .init = pc_init_pci, .max_cpus = 255, + .compat_props = (GlobalProperty[]) { + { + .driver = "PCI", + .property = "command_serr_enable", + .value = "off", + }, + { /* end of list */ } + }, .is_default = 1, }; @@ -265,6 +273,10 @@ static QEMUMachine pc_machine_v0_12 = { .driver = "vmware-svga", .property = "rombar", .value = stringify(0), + },{ + .driver = "PCI", + .property = "command_serr_enable", + .value = "off", }, { /* end of list */ } } @@ -300,6 +312,10 @@ static QEMUMachine pc_machine_v0_11 = { .driver = "PCI", .property = "rombar", .value = stringify(0), + },{ + .driver = "PCI", + .property = "command_serr_enable", + .value = "off", }, { /* end of list */ } } @@ -347,6 +363,10 @@ static QEMUMachine pc_machine_v0_10 = { .driver = "PCI", .property = "rombar", .value = stringify(0), + },{ + .driver = "PCI", + .property = "command_serr_enable", + .value = "off", }, { /* end of list */ } }, @@ -25,8 +25,6 @@ #include "pci.h" #include "pci_bridge.h" #include "pci_internals.h" -#include "msix.h" -#include "msi.h" #include "monitor.h" #include "net.h" #include "sysemu.h" @@ -59,6 +57,8 @@ struct BusInfo pci_bus_info = { DEFINE_PROP_UINT32("rombar", PCIDevice, rom_bar, 1), DEFINE_PROP_BIT("multifunction", PCIDevice, cap_present, QEMU_PCI_CAP_MULTIFUNCTION_BITNR, false), + DEFINE_PROP_BIT("command_serr_enable", PCIDevice, cap_present, + QEMU_PCI_CAP_SERR_BITNR, true), DEFINE_PROP_END_OF_LIST() } }; @@ -570,6 +570,9 @@ static void pci_init_wmask(PCIDevice *dev) pci_set_word(dev->wmask + PCI_COMMAND, PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | PCI_COMMAND_INTX_DISABLE); + if (dev->cap_present & QEMU_PCI_CAP_SERR) { + pci_word_test_and_set_mask(dev->wmask + PCI_COMMAND, PCI_COMMAND_SERR); + } memset(dev->wmask + PCI_CONFIG_HEADER_SIZE, 0xff, config_size - PCI_CONFIG_HEADER_SIZE); @@ -1096,23 +1099,6 @@ static void pci_set_irq(void *opaque, int irq_num, int level) pci_change_irq_level(pci_dev, irq_num, change); } -bool pci_msi_enabled(PCIDevice *dev) -{ - return msix_enabled(dev) || msi_enabled(dev); -} - -void pci_msi_notify(PCIDevice *dev, unsigned int vector) -{ - if (msix_enabled(dev)) { - msix_notify(dev, vector); - } else if (msi_enabled(dev)) { - msi_notify(dev, vector); - } else { - /* MSI/MSI-X must be enabled */ - abort(); - } -} - /***********************************************************/ /* monitor info on PCI */ @@ -118,6 +118,10 @@ enum { /* multifunction capable device */ #define QEMU_PCI_CAP_MULTIFUNCTION_BITNR 3 QEMU_PCI_CAP_MULTIFUNCTION = (1 << QEMU_PCI_CAP_MULTIFUNCTION_BITNR), + + /* command register SERR bit enabled */ +#define QEMU_PCI_CAP_SERR_BITNR 4 + QEMU_PCI_CAP_SERR = (1 << QEMU_PCI_CAP_SERR_BITNR), }; struct PCIDevice { @@ -257,9 +261,6 @@ void do_pci_info_print(Monitor *mon, const QObject *data); void do_pci_info(Monitor *mon, QObject **ret_data); void pci_bridge_update_mappings(PCIBus *b); -bool pci_msi_enabled(PCIDevice *dev); -void pci_msi_notify(PCIDevice *dev, unsigned int vector); - static inline void pci_set_byte(uint8_t *config, uint8_t val) { @@ -167,10 +167,12 @@ static void hotplug_event_notify(PCIDevice *dev) * The Port may optionally send an MSI when there are hot-plug events that * occur while interrupt generation is disabled, and interrupt generation is * subsequently enabled. */ - if (!pci_msi_enabled(dev)) { + if (msix_enabled(dev)) { + msix_notify(dev, pcie_cap_flags_get_vector(dev)); + } else if (msi_enabled(dev)) { + msi_notify(dev, pcie_cap_flags_get_vector(dev)); + } else { qemu_set_irq(dev->irq[dev->exp.hpev_intx], dev->exp.hpev_notified); - } else if (dev->exp.hpev_notified) { - pci_msi_notify(dev, pcie_cap_flags_get_vector(dev)); } } diff --git a/hw/pcie_aer.c b/hw/pcie_aer.c index 47d64003fc..cb97a95d61 100644 --- a/hw/pcie_aer.c +++ b/hw/pcie_aer.c @@ -257,30 +257,49 @@ static unsigned int pcie_aer_root_get_vector(PCIDevice *dev) return (root_status & PCI_ERR_ROOT_IRQ) >> PCI_ERR_ROOT_IRQ_SHIFT; } +/* Given a status register, get corresponding bits in the command register */ +static uint32_t pcie_aer_status_to_cmd(uint32_t status) +{ + uint32_t cmd = 0; + if (status & PCI_ERR_ROOT_COR_RCV) { + cmd |= PCI_ERR_ROOT_CMD_COR_EN; + } + if (status & PCI_ERR_ROOT_NONFATAL_RCV) { + cmd |= PCI_ERR_ROOT_CMD_NONFATAL_EN; + } + if (status & PCI_ERR_ROOT_FATAL_RCV) { + cmd |= PCI_ERR_ROOT_CMD_FATAL_EN; + } + return cmd; +} + +static void pcie_aer_root_notify(PCIDevice *dev) +{ + if (msix_enabled(dev)) { + msix_notify(dev, pcie_aer_root_get_vector(dev)); + } else if (msi_enabled(dev)) { + msi_notify(dev, pcie_aer_root_get_vector(dev)); + } else { + qemu_set_irq(dev->irq[dev->exp.aer_intx], 1); + } +} + /* - * return value: - * true: error message is sent up - * false: error message is masked - * * 6.2.6 Error Message Control * Figure 6-3 * root port part */ -static bool pcie_aer_msg_root_port(PCIDevice *dev, const PCIEAERMsg *msg) +static void pcie_aer_msg_root_port(PCIDevice *dev, const PCIEAERMsg *msg) { - bool msg_sent; uint16_t cmd; uint8_t *aer_cap; uint32_t root_cmd; - uint32_t root_status; - bool msi_trigger; + uint32_t root_status, prev_status; - msg_sent = false; cmd = pci_get_word(dev->config + PCI_COMMAND); aer_cap = dev->config + dev->exp.aer_cap; root_cmd = pci_get_long(aer_cap + PCI_ERR_ROOT_COMMAND); - root_status = pci_get_long(aer_cap + PCI_ERR_ROOT_STATUS); - msi_trigger = false; + prev_status = root_status = pci_get_long(aer_cap + PCI_ERR_ROOT_STATUS); if (cmd & PCI_COMMAND_SERR) { /* System Error. @@ -299,25 +318,14 @@ static bool pcie_aer_msg_root_port(PCIDevice *dev, const PCIEAERMsg *msg) if (root_status & PCI_ERR_ROOT_COR_RCV) { root_status |= PCI_ERR_ROOT_MULTI_COR_RCV; } else { - if (root_cmd & PCI_ERR_ROOT_CMD_COR_EN) { - msi_trigger = true; - } pci_set_word(aer_cap + PCI_ERR_ROOT_COR_SRC, msg->source_id); } root_status |= PCI_ERR_ROOT_COR_RCV; break; case PCI_ERR_ROOT_CMD_NONFATAL_EN: - if (!(root_status & PCI_ERR_ROOT_NONFATAL_RCV) && - root_cmd & PCI_ERR_ROOT_CMD_NONFATAL_EN) { - msi_trigger = true; - } root_status |= PCI_ERR_ROOT_NONFATAL_RCV; break; case PCI_ERR_ROOT_CMD_FATAL_EN: - if (!(root_status & PCI_ERR_ROOT_FATAL_RCV) && - root_cmd & PCI_ERR_ROOT_CMD_FATAL_EN) { - msi_trigger = true; - } if (!(root_status & PCI_ERR_ROOT_UNCOR_RCV)) { root_status |= PCI_ERR_ROOT_FIRST_FATAL; } @@ -337,18 +345,17 @@ static bool pcie_aer_msg_root_port(PCIDevice *dev, const PCIEAERMsg *msg) } pci_set_long(aer_cap + PCI_ERR_ROOT_STATUS, root_status); - if (root_cmd & msg->severity) { - /* 6.2.4.1.2 Interrupt Generation */ - if (pci_msi_enabled(dev)) { - if (msi_trigger) { - pci_msi_notify(dev, pcie_aer_root_get_vector(dev)); - } - } else { - qemu_set_irq(dev->irq[dev->exp.aer_intx], 1); - } - msg_sent = true; + /* 6.2.4.1.2 Interrupt Generation */ + /* All the above did was set some bits in the status register. + * Specifically these that match message severity. + * The below code relies on this fact. */ + if (!(root_cmd & msg->severity) || + (pcie_aer_status_to_cmd(prev_status) & root_cmd)) { + /* Condition is not being set or was already true so nothing to do. */ + return; } - return msg_sent; + + pcie_aer_root_notify(dev); } /* @@ -739,40 +746,26 @@ void pcie_aer_root_reset(PCIDevice *dev) */ } -static bool pcie_aer_root_does_trigger(uint32_t cmd, uint32_t status) -{ - return - ((cmd & PCI_ERR_ROOT_CMD_COR_EN) && (status & PCI_ERR_ROOT_COR_RCV)) || - ((cmd & PCI_ERR_ROOT_CMD_NONFATAL_EN) && - (status & PCI_ERR_ROOT_NONFATAL_RCV)) || - ((cmd & PCI_ERR_ROOT_CMD_FATAL_EN) && - (status & PCI_ERR_ROOT_FATAL_RCV)); -} - void pcie_aer_root_write_config(PCIDevice *dev, uint32_t addr, uint32_t val, int len, uint32_t root_cmd_prev) { uint8_t *aer_cap = dev->config + dev->exp.aer_cap; - - /* root command register */ + uint32_t root_status = pci_get_long(aer_cap + PCI_ERR_ROOT_STATUS); + uint32_t enabled_cmd = pcie_aer_status_to_cmd(root_status); uint32_t root_cmd = pci_get_long(aer_cap + PCI_ERR_ROOT_COMMAND); - if (root_cmd & PCI_ERR_ROOT_CMD_EN_MASK) { - /* 6.2.4.1.2 Interrupt Generation */ - - /* 0 -> 1 */ - uint32_t root_cmd_set = (root_cmd_prev ^ root_cmd) & root_cmd; - uint32_t root_status = pci_get_long(aer_cap + PCI_ERR_ROOT_STATUS); + /* 6.2.4.1.2 Interrupt Generation */ + if (!msix_enabled(dev) && !msi_enabled(dev)) { + qemu_set_irq(dev->irq[dev->exp.aer_intx], !!(root_cmd & enabled_cmd)); + return; + } - if (pci_msi_enabled(dev)) { - if (pcie_aer_root_does_trigger(root_cmd_set, root_status)) { - pci_msi_notify(dev, pcie_aer_root_get_vector(dev)); - } - } else { - int int_level = pcie_aer_root_does_trigger(root_cmd, root_status); - qemu_set_irq(dev->irq[dev->exp.aer_intx], int_level); - } + if ((root_cmd_prev & enabled_cmd) || !(root_cmd & enabled_cmd)) { + /* Send MSI on transition from false to true. */ + return; } + + pcie_aer_root_notify(dev); } static const VMStateDescription vmstate_pcie_aer_err = { diff --git a/hw/virtio-net.c b/hw/virtio-net.c index 3472f6b28d..ec1bf8dda7 100644 --- a/hw/virtio-net.c +++ b/hw/virtio-net.c @@ -99,9 +99,14 @@ static void virtio_net_set_config(VirtIODevice *vdev, const uint8_t *config) } } -static void virtio_net_set_status(struct VirtIODevice *vdev, uint8_t status) +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->vm_running; +} + +static void virtio_net_vhost_status(VirtIONet *n, uint8_t status) { - VirtIONet *n = to_virtio_net(vdev); if (!n->nic->nc.peer) { return; } @@ -112,9 +117,7 @@ static void virtio_net_set_status(struct VirtIODevice *vdev, uint8_t status) if (!tap_get_vhost_net(n->nic->nc.peer)) { return; } - if (!!n->vhost_started == ((status & VIRTIO_CONFIG_S_DRIVER_OK) && - (n->status & VIRTIO_NET_S_LINK_UP) && - n->vm_running)) { + if (!!n->vhost_started == virtio_net_started(n, status)) { return; } if (!n->vhost_started) { @@ -131,6 +134,32 @@ static void virtio_net_set_status(struct VirtIODevice *vdev, uint8_t status) } } +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(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(VLANClientState *nc) { VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque; @@ -424,6 +453,9 @@ static void virtio_net_handle_rx(VirtIODevice *vdev, VirtQueue *vq) static int virtio_net_can_receive(VLANClientState *nc) { VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque; + if (!n->vm_running) { + return 0; + } if (!virtio_queue_ready(n->rx_vq) || !(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK)) @@ -672,11 +704,12 @@ static int32_t virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq) { VirtQueueElement elem; int32_t num_packets = 0; - if (!(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK)) { return num_packets; } + assert(n->vm_running); + if (n->async_tx.elem.out_num) { virtio_queue_set_notification(n->tx_vq, 0); return num_packets; @@ -735,6 +768,12 @@ static void virtio_net_handle_tx_timer(VirtIODevice *vdev, VirtQueue *vq) { VirtIONet *n = to_virtio_net(vdev); + /* This happens when device was stopped but VCPU wasn't. */ + if (!n->vm_running) { + n->tx_waiting = 1; + return; + } + if (n->tx_waiting) { virtio_queue_set_notification(vq, 1); qemu_del_timer(n->tx_timer); @@ -755,14 +794,19 @@ static void virtio_net_handle_tx_bh(VirtIODevice *vdev, VirtQueue *vq) if (unlikely(n->tx_waiting)) { return; } + n->tx_waiting = 1; + /* This happens when device was stopped but VCPU wasn't. */ + if (!n->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->vm_running); n->tx_waiting = 0; @@ -779,6 +823,8 @@ static void virtio_net_tx_bh(void *opaque) VirtIONet *n = opaque; int32_t ret; + assert(n->vm_running); + n->tx_waiting = 0; /* Just in case the driver is not ready on more */ @@ -923,15 +969,6 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id) } } 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; } diff --git a/migration.c b/migration.c index 622a9d2d95..e5ba51c314 100644 --- a/migration.c +++ b/migration.c @@ -370,8 +370,6 @@ void migrate_fd_put_ready(void *opaque) DPRINTF("done iterating\n"); vm_stop(0); - qemu_aio_flush(); - bdrv_flush_all(); if ((qemu_savevm_state_complete(s->mon, s->file)) < 0) { if (old_vm_running) { vm_start(); @@ -1050,6 +1050,10 @@ static const struct { .name = "mcast", .type = QEMU_OPT_STRING, .help = "UDP multicast address and port number", + }, { + .name = "localaddr", + .type = QEMU_OPT_STRING, + .help = "source address for multicast packets", }, { /* end of list */ } }, diff --git a/net/socket.c b/net/socket.c index 1c4e153e3f..916c2a8e10 100644 --- a/net/socket.c +++ b/net/socket.c @@ -149,7 +149,7 @@ static void net_socket_send_dgram(void *opaque) qemu_send_packet(&s->nc, s->buf, size); } -static int net_socket_mcast_create(struct sockaddr_in *mcastaddr) +static int net_socket_mcast_create(struct sockaddr_in *mcastaddr, struct in_addr *localaddr) { struct ip_mreq imr; int fd; @@ -183,7 +183,11 @@ static int net_socket_mcast_create(struct sockaddr_in *mcastaddr) /* Add host to multicast group */ imr.imr_multiaddr = mcastaddr->sin_addr; - imr.imr_interface.s_addr = htonl(INADDR_ANY); + if (localaddr) { + imr.imr_interface = *localaddr; + } else { + imr.imr_interface.s_addr = htonl(INADDR_ANY); + } ret = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const char *)&imr, sizeof(struct ip_mreq)); @@ -201,6 +205,15 @@ static int net_socket_mcast_create(struct sockaddr_in *mcastaddr) goto fail; } + /* If a bind address is given, only send packets from that address */ + if (localaddr != NULL) { + ret = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, localaddr, sizeof(*localaddr)); + if (ret < 0) { + perror("setsockopt(IP_MULTICAST_IF)"); + goto fail; + } + } + socket_set_nonblock(fd); return fd; fail: @@ -248,7 +261,7 @@ static NetSocketState *net_socket_fd_init_dgram(VLANState *vlan, return NULL; } /* clone dgram socket */ - newfd = net_socket_mcast_create(&saddr); + newfd = net_socket_mcast_create(&saddr, NULL); if (newfd < 0) { /* error already reported by net_socket_mcast_create() */ close(fd); @@ -468,17 +481,26 @@ static int net_socket_connect_init(VLANState *vlan, static int net_socket_mcast_init(VLANState *vlan, const char *model, const char *name, - const char *host_str) + const char *host_str, + const char *localaddr_str) { NetSocketState *s; int fd; struct sockaddr_in saddr; + struct in_addr localaddr, *param_localaddr; if (parse_host_port(&saddr, host_str) < 0) return -1; + if (localaddr_str != NULL) { + if (inet_aton(localaddr_str, &localaddr) == 0) + return -1; + param_localaddr = &localaddr; + } else { + param_localaddr = NULL; + } - fd = net_socket_mcast_create(&saddr); + fd = net_socket_mcast_create(&saddr, param_localaddr); if (fd < 0) return -1; @@ -505,8 +527,9 @@ int net_init_socket(QemuOpts *opts, if (qemu_opt_get(opts, "listen") || qemu_opt_get(opts, "connect") || - qemu_opt_get(opts, "mcast")) { - error_report("listen=, connect= and mcast= is invalid with fd="); + qemu_opt_get(opts, "mcast") || + qemu_opt_get(opts, "localaddr")) { + error_report("listen=, connect=, mcast= and localaddr= is invalid with fd=\n"); return -1; } @@ -524,8 +547,9 @@ int net_init_socket(QemuOpts *opts, if (qemu_opt_get(opts, "fd") || qemu_opt_get(opts, "connect") || - qemu_opt_get(opts, "mcast")) { - error_report("fd=, connect= and mcast= is invalid with listen="); + qemu_opt_get(opts, "mcast") || + qemu_opt_get(opts, "localaddr")) { + error_report("fd=, connect=, mcast= and localaddr= is invalid with listen=\n"); return -1; } @@ -539,8 +563,9 @@ int net_init_socket(QemuOpts *opts, if (qemu_opt_get(opts, "fd") || qemu_opt_get(opts, "listen") || - qemu_opt_get(opts, "mcast")) { - error_report("fd=, listen= and mcast= is invalid with connect="); + qemu_opt_get(opts, "mcast") || + qemu_opt_get(opts, "localaddr")) { + error_report("fd=, listen=, mcast= and localaddr= is invalid with connect=\n"); return -1; } @@ -550,7 +575,7 @@ int net_init_socket(QemuOpts *opts, return -1; } } else if (qemu_opt_get(opts, "mcast")) { - const char *mcast; + const char *mcast, *localaddr; if (qemu_opt_get(opts, "fd") || qemu_opt_get(opts, "connect") || @@ -560,8 +585,9 @@ int net_init_socket(QemuOpts *opts, } mcast = qemu_opt_get(opts, "mcast"); + localaddr = qemu_opt_get(opts, "localaddr"); - if (net_socket_mcast_init(vlan, "socket", name, mcast) == -1) { + if (net_socket_mcast_init(vlan, "socket", name, mcast, localaddr) == -1) { return -1; } } else { diff --git a/qemu-options.hx b/qemu-options.hx index 4d99a58fc5..accd16a55c 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -1061,8 +1061,9 @@ DEF("net", HAS_ARG, QEMU_OPTION_net, #endif "-net socket[,vlan=n][,name=str][,fd=h][,listen=[host]:port][,connect=host:port]\n" " connect the vlan 'n' to another VLAN using a socket connection\n" - "-net socket[,vlan=n][,name=str][,fd=h][,mcast=maddr:port]\n" + "-net socket[,vlan=n][,name=str][,fd=h][,mcast=maddr:port[,localaddr=addr]]\n" " connect the vlan 'n' to multicast maddr and port\n" + " use 'localaddr=addr' to specify the host address to send packets from\n" #ifdef CONFIG_VDE "-net vde[,vlan=n][,name=str][,sock=socketpath][,port=n][,group=groupname][,mode=octalmode]\n" " connect the vlan 'n' to port 'n' of a vde switch running\n" @@ -1256,7 +1257,7 @@ qemu linux.img -net nic,macaddr=52:54:00:12:34:57 \ -net socket,connect=127.0.0.1:1234 @end example -@item -net socket[,vlan=@var{n}][,name=@var{name}][,fd=@var{h}] [,mcast=@var{maddr}:@var{port}] +@item -net socket[,vlan=@var{n}][,name=@var{name}][,fd=@var{h}][,mcast=@var{maddr}:@var{port}[,localaddr=@var{addr}]] Create a VLAN @var{n} shared with another QEMU virtual machines using a UDP multicast socket, effectively making a bus for @@ -1296,6 +1297,12 @@ qemu linux.img -net nic,macaddr=52:54:00:12:34:56 \ /path/to/linux ubd0=/path/to/root_fs eth0=mcast @end example +Example (send packets from host's 1.2.3.4): +@example +qemu linux.img -net nic,macaddr=52:54:00:12:34:56 \ + -net socket,mcast=239.192.168.1:1102,localaddr=1.2.3.4 +@end example + @item -net vde[,vlan=@var{n}][,name=@var{name}][,sock=@var{socketpath}] [,port=@var{n}][,group=@var{groupname}][,mode=@var{octalmode}] Connect VLAN @var{n} to PORT @var{n} of a vde switch running on host and listening for incoming connections on @var{socketpath}. Use GROUP @var{groupname} @@ -1575,8 +1575,6 @@ static int qemu_savevm_state(Monitor *mon, QEMUFile *f) saved_vm_running = vm_running; vm_stop(0); - bdrv_flush_all(); - ret = qemu_savevm_state_begin(mon, f, 0, 0); if (ret < 0) goto out; @@ -1885,8 +1883,6 @@ void do_savevm(Monitor *mon, const QDict *qdict) monitor_printf(mon, "No block device can accept snapshots\n"); return; } - /* ??? Should this occur after vm_stop? */ - qemu_aio_flush(); saved_vm_running = vm_running; vm_stop(0); |