aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.objs3
-rw-r--r--cpus.c2
-rw-r--r--hw/pc_piix.c20
-rw-r--r--hw/pci.c24
-rw-r--r--hw/pci.h7
-rw-r--r--hw/pcie.c8
-rw-r--r--hw/pcie_aer.c111
-rw-r--r--hw/virtio-net.c69
-rw-r--r--migration.c2
-rw-r--r--net.c4
-rw-r--r--net/socket.c52
-rw-r--r--qemu-options.hx11
-rw-r--r--savevm.c4
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
diff --git a/cpus.c b/cpus.c
index 91a0fb1146..0309189bfd 100644
--- a/cpus.c
+++ b/cpus.c
@@ -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 */ }
},
diff --git a/hw/pci.c b/hw/pci.c
index 24e650a442..ef00d20d5f 100644
--- a/hw/pci.c
+++ b/hw/pci.c
@@ -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 */
diff --git a/hw/pci.h b/hw/pci.h
index 89f7b761e7..aa3afe9684 100644
--- a/hw/pci.h
+++ b/hw/pci.h
@@ -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)
{
diff --git a/hw/pcie.c b/hw/pcie.c
index f461c1cfbe..d1f0086559 100644
--- a/hw/pcie.c
+++ b/hw/pcie.c
@@ -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();
diff --git a/net.c b/net.c
index c5e6063fcf..9ba5be22dd 100644
--- a/net.c
+++ b/net.c
@@ -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}
diff --git a/savevm.c b/savevm.c
index d38f79e6bd..90aa237c9c 100644
--- a/savevm.c
+++ b/savevm.c
@@ -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);