Merge remote-tracking branch 'luiz/queue/qmp' into staging
# By Amos Kong (1) and Luiz Capitulino (1)
# Via Luiz Capitulino
* luiz/queue/qmp:
qmp: update send-key document
qapi: qapi-commands: fix possible leaks on visitor dealloc
Message-id: 1374093679-29213-1-git-send-email-lcapitulino@redhat.com
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
diff --git a/QMP/qmp-events.txt b/QMP/qmp-events.txt
index 24e804e9..39b6016 100644
--- a/QMP/qmp-events.txt
+++ b/QMP/qmp-events.txt
@@ -172,6 +172,23 @@
},
"timestamp": { "seconds": 1265044230, "microseconds": 450486 } }
+NIC_RX_FILTER_CHANGED
+-----------------
+
+The event is emitted once until the query command is executed,
+the first event will always be emitted.
+
+Data:
+
+- "name": net client name (json-string)
+- "path": device path (json-string)
+
+{ "event": "NIC_RX_FILTER_CHANGED",
+ "data": { "name": "vnet0",
+ "path": "/machine/peripheral/vnet0/virtio-backend" },
+ "timestamp": { "seconds": 1368697518, "microseconds": 326866 } }
+}
+
RESET
-----
diff --git a/block/iscsi.c b/block/iscsi.c
index 0bbf0b1..5f28c6a 100644
--- a/block/iscsi.c
+++ b/block/iscsi.c
@@ -32,6 +32,7 @@
#include "block/block_int.h"
#include "trace.h"
#include "block/scsi.h"
+#include "qemu/iov.h"
#include <iscsi/iscsi.h>
#include <iscsi/scsi-lowlevel.h>
@@ -61,8 +62,6 @@
int status;
int canceled;
int retries;
- size_t read_size;
- size_t read_offset;
int64_t sector_num;
int nb_sectors;
#ifdef __linux__
@@ -233,11 +232,28 @@
iscsi_schedule_bh(acb);
}
+static int64_t sector_lun2qemu(int64_t sector, IscsiLun *iscsilun)
+{
+ return sector * iscsilun->block_size / BDRV_SECTOR_SIZE;
+}
+
static int64_t sector_qemu2lun(int64_t sector, IscsiLun *iscsilun)
{
return sector * BDRV_SECTOR_SIZE / iscsilun->block_size;
}
+static bool is_request_lun_aligned(int64_t sector_num, int nb_sectors,
+ IscsiLun *iscsilun)
+{
+ if ((sector_num * BDRV_SECTOR_SIZE) % iscsilun->block_size ||
+ (nb_sectors * BDRV_SECTOR_SIZE) % iscsilun->block_size) {
+ error_report("iSCSI misaligned request: iscsilun->block_size %u, sector_num %ld, nb_sectors %d",
+ iscsilun->block_size, sector_num, nb_sectors);
+ return 0;
+ }
+ return 1;
+}
+
static int
iscsi_aio_writev_acb(IscsiAIOCB *acb)
{
@@ -285,7 +301,7 @@
lba = sector_qemu2lun(acb->sector_num, acb->iscsilun);
*(uint32_t *)&acb->task->cdb[2] = htonl(lba >> 32);
*(uint32_t *)&acb->task->cdb[6] = htonl(lba & 0xffffffff);
- num_sectors = size / acb->iscsilun->block_size;
+ num_sectors = sector_qemu2lun(acb->nb_sectors, acb->iscsilun);
*(uint32_t *)&acb->task->cdb[10] = htonl(num_sectors);
acb->task->expxferlen = size;
@@ -322,6 +338,10 @@
IscsiLun *iscsilun = bs->opaque;
IscsiAIOCB *acb;
+ if (!is_request_lun_aligned(sector_num, nb_sectors, iscsilun)) {
+ return NULL;
+ }
+
acb = qemu_aio_get(&iscsi_aiocb_info, bs, cb, opaque);
trace_iscsi_aio_writev(iscsilun->iscsi, sector_num, nb_sectors, opaque, acb);
@@ -379,6 +399,7 @@
iscsi_aio_readv_acb(IscsiAIOCB *acb)
{
struct iscsi_context *iscsi = acb->iscsilun->iscsi;
+ size_t size;
uint64_t lba;
uint32_t num_sectors;
int ret;
@@ -391,20 +412,7 @@
acb->status = -EINPROGRESS;
acb->buf = NULL;
- /* If LUN blocksize is bigger than BDRV_BLOCK_SIZE a read from QEMU
- * may be misaligned to the LUN, so we may need to read some extra
- * data.
- */
- acb->read_offset = 0;
- if (acb->iscsilun->block_size > BDRV_SECTOR_SIZE) {
- uint64_t bdrv_offset = BDRV_SECTOR_SIZE * acb->sector_num;
-
- acb->read_offset = bdrv_offset % acb->iscsilun->block_size;
- }
-
- num_sectors = (acb->read_size + acb->iscsilun->block_size
- + acb->read_offset - 1)
- / acb->iscsilun->block_size;
+ size = acb->nb_sectors * BDRV_SECTOR_SIZE;
acb->task = malloc(sizeof(struct scsi_task));
if (acb->task == NULL) {
@@ -415,8 +423,9 @@
memset(acb->task, 0, sizeof(struct scsi_task));
acb->task->xfer_dir = SCSI_XFER_READ;
+ acb->task->expxferlen = size;
lba = sector_qemu2lun(acb->sector_num, acb->iscsilun);
- acb->task->expxferlen = acb->read_size;
+ num_sectors = sector_qemu2lun(acb->nb_sectors, acb->iscsilun);
switch (acb->iscsilun->type) {
case TYPE_DISK:
@@ -464,6 +473,10 @@
IscsiLun *iscsilun = bs->opaque;
IscsiAIOCB *acb;
+ if (!is_request_lun_aligned(sector_num, nb_sectors, iscsilun)) {
+ return NULL;
+ }
+
acb = qemu_aio_get(&iscsi_aiocb_info, bs, cb, opaque);
trace_iscsi_aio_readv(iscsilun->iscsi, sector_num, nb_sectors, opaque, acb);
@@ -471,7 +484,6 @@
acb->sector_num = sector_num;
acb->iscsilun = iscsilun;
acb->qiov = qiov;
- acb->read_size = BDRV_SECTOR_SIZE * (size_t)acb->nb_sectors;
acb->retries = ISCSI_CMD_RETRIES;
if (iscsi_aio_readv_acb(acb) != 0) {
@@ -651,6 +663,9 @@
{
IscsiAIOCB *acb = opaque;
+ g_free(acb->buf);
+ acb->buf = NULL;
+
if (acb->canceled != 0) {
return;
}
@@ -727,14 +742,30 @@
memcpy(&acb->task->cdb[0], acb->ioh->cmdp, acb->ioh->cmd_len);
acb->task->expxferlen = acb->ioh->dxfer_len;
+ data.size = 0;
if (acb->task->xfer_dir == SCSI_XFER_WRITE) {
- data.data = acb->ioh->dxferp;
- data.size = acb->ioh->dxfer_len;
+ if (acb->ioh->iovec_count == 0) {
+ data.data = acb->ioh->dxferp;
+ data.size = acb->ioh->dxfer_len;
+ } else {
+#if defined(LIBISCSI_FEATURE_IOVECTOR)
+ scsi_task_set_iov_out(acb->task,
+ (struct scsi_iovec *) acb->ioh->dxferp,
+ acb->ioh->iovec_count);
+#else
+ struct iovec *iov = (struct iovec *)acb->ioh->dxferp;
+
+ acb->buf = g_malloc(acb->ioh->dxfer_len);
+ data.data = acb->buf;
+ data.size = iov_to_buf(iov, acb->ioh->iovec_count, 0,
+ acb->buf, acb->ioh->dxfer_len);
+#endif
+ }
}
+
if (iscsi_scsi_command_async(iscsi, iscsilun->lun, acb->task,
iscsi_aio_ioctl_cb,
- (acb->task->xfer_dir == SCSI_XFER_WRITE) ?
- &data : NULL,
+ (data.size > 0) ? &data : NULL,
acb) != 0) {
scsi_free_scsi_task(acb->task);
qemu_aio_release(acb);
@@ -743,9 +774,26 @@
/* tell libiscsi to read straight into the buffer we got from ioctl */
if (acb->task->xfer_dir == SCSI_XFER_READ) {
- scsi_task_add_data_in_buffer(acb->task,
- acb->ioh->dxfer_len,
- acb->ioh->dxferp);
+ if (acb->ioh->iovec_count == 0) {
+ scsi_task_add_data_in_buffer(acb->task,
+ acb->ioh->dxfer_len,
+ acb->ioh->dxferp);
+ } else {
+#if defined(LIBISCSI_FEATURE_IOVECTOR)
+ scsi_task_set_iov_in(acb->task,
+ (struct scsi_iovec *) acb->ioh->dxferp,
+ acb->ioh->iovec_count);
+#else
+ int i;
+ for (i = 0; i < acb->ioh->iovec_count; i++) {
+ struct iovec *iov = (struct iovec *)acb->ioh->dxferp;
+
+ scsi_task_add_data_in_buffer(acb->task,
+ iov[i].iov_len,
+ iov[i].iov_base);
+ }
+#endif
+ }
}
iscsi_set_events(iscsilun);
@@ -1118,8 +1166,7 @@
if ((ret = iscsi_readcapacity_sync(iscsilun)) != 0) {
goto out;
}
- bs->total_sectors = iscsilun->num_blocks *
- iscsilun->block_size / BDRV_SECTOR_SIZE ;
+ bs->total_sectors = sector_lun2qemu(iscsilun->num_blocks, iscsilun);
/* Medium changer or tape. We dont have any emulation for this so this must
* be sg ioctl compatible. We force it to be sg, otherwise qemu will try
@@ -1235,6 +1282,7 @@
}
if (bs.total_sectors < total_size) {
ret = -ENOSPC;
+ goto out;
}
ret = 0;
diff --git a/hw/i386/pc.c b/hw/i386/pc.c
index c5d8570..2a87563 100644
--- a/hw/i386/pc.c
+++ b/hw/i386/pc.c
@@ -1005,7 +1005,7 @@
static void pc_fw_cfg_guest_info(PcGuestInfo *guest_info)
{
PcRomPciInfo *info;
- if (!guest_info->has_pci_info) {
+ if (!guest_info->has_pci_info || !guest_info->fw_cfg) {
return;
}
diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
index 1ea9556..679f50c 100644
--- a/hw/net/virtio-net.c
+++ b/hw/net/virtio-net.c
@@ -21,6 +21,8 @@
#include "hw/virtio/virtio-net.h"
#include "net/vhost_net.h"
#include "hw/virtio/virtio-bus.h"
+#include "qapi/qmp/qjson.h"
+#include "monitor/monitor.h"
#define VIRTIO_NET_VM_VERSION 11
@@ -192,6 +194,105 @@
virtio_net_set_status(vdev, vdev->status);
}
+static void rxfilter_notify(NetClientState *nc)
+{
+ QObject *event_data;
+ VirtIONet *n = qemu_get_nic_opaque(nc);
+
+ if (nc->rxfilter_notify_enabled) {
+ if (n->netclient_name) {
+ event_data = qobject_from_jsonf("{ 'name': %s, 'path': %s }",
+ n->netclient_name,
+ object_get_canonical_path(OBJECT(n->qdev)));
+ } else {
+ event_data = qobject_from_jsonf("{ 'path': %s }",
+ object_get_canonical_path(OBJECT(n->qdev)));
+ }
+ monitor_protocol_event(QEVENT_NIC_RX_FILTER_CHANGED, event_data);
+ qobject_decref(event_data);
+
+ /* disable event notification to avoid events flooding */
+ nc->rxfilter_notify_enabled = 0;
+ }
+}
+
+static char *mac_strdup_printf(const uint8_t *mac)
+{
+ return g_strdup_printf("%.2x:%.2x:%.2x:%.2x:%.2x:%.2x", mac[0],
+ mac[1], mac[2], mac[3], mac[4], mac[5]);
+}
+
+static RxFilterInfo *virtio_net_query_rxfilter(NetClientState *nc)
+{
+ VirtIONet *n = qemu_get_nic_opaque(nc);
+ RxFilterInfo *info;
+ strList *str_list, *entry;
+ intList *int_list, *int_entry;
+ int i, j;
+
+ info = g_malloc0(sizeof(*info));
+ info->name = g_strdup(nc->name);
+ info->promiscuous = n->promisc;
+
+ if (n->nouni) {
+ info->unicast = RX_STATE_NONE;
+ } else if (n->alluni) {
+ info->unicast = RX_STATE_ALL;
+ } else {
+ info->unicast = RX_STATE_NORMAL;
+ }
+
+ if (n->nomulti) {
+ info->multicast = RX_STATE_NONE;
+ } else if (n->allmulti) {
+ info->multicast = RX_STATE_ALL;
+ } else {
+ info->multicast = RX_STATE_NORMAL;
+ }
+
+ info->broadcast_allowed = n->nobcast;
+ info->multicast_overflow = n->mac_table.multi_overflow;
+ info->unicast_overflow = n->mac_table.uni_overflow;
+
+ info->main_mac = mac_strdup_printf(n->mac);
+
+ str_list = NULL;
+ for (i = 0; i < n->mac_table.first_multi; i++) {
+ entry = g_malloc0(sizeof(*entry));
+ entry->value = mac_strdup_printf(n->mac_table.macs + i * ETH_ALEN);
+ entry->next = str_list;
+ str_list = entry;
+ }
+ info->unicast_table = str_list;
+
+ str_list = NULL;
+ for (i = n->mac_table.first_multi; i < n->mac_table.in_use; i++) {
+ entry = g_malloc0(sizeof(*entry));
+ entry->value = mac_strdup_printf(n->mac_table.macs + i * ETH_ALEN);
+ entry->next = str_list;
+ str_list = entry;
+ }
+ info->multicast_table = str_list;
+
+ int_list = NULL;
+ for (i = 0; i < MAX_VLAN >> 5; i++) {
+ for (j = 0; n->vlans[i] && j < 0x1f; j++) {
+ if (n->vlans[i] & (1U << j)) {
+ int_entry = g_malloc0(sizeof(*int_entry));
+ int_entry->value = (i << 5) + j;
+ int_entry->next = int_list;
+ int_list = int_entry;
+ }
+ }
+ }
+ info->vlan_table = int_list;
+
+ /* enable event notification after query */
+ nc->rxfilter_notify_enabled = 1;
+
+ return info;
+}
+
static void virtio_net_reset(VirtIODevice *vdev)
{
VirtIONet *n = VIRTIO_NET(vdev);
@@ -420,6 +521,7 @@
{
uint8_t on;
size_t s;
+ NetClientState *nc = qemu_get_queue(n->nic);
s = iov_to_buf(iov, iov_cnt, 0, &on, sizeof(on));
if (s != sizeof(on)) {
@@ -442,6 +544,8 @@
return VIRTIO_NET_ERR;
}
+ rxfilter_notify(nc);
+
return VIRTIO_NET_OK;
}
@@ -487,6 +591,7 @@
{
struct virtio_net_ctrl_mac mac_data;
size_t s;
+ NetClientState *nc = qemu_get_queue(n->nic);
if (cmd == VIRTIO_NET_CTRL_MAC_ADDR_SET) {
if (iov_size(iov, iov_cnt) != sizeof(n->mac)) {
@@ -495,6 +600,8 @@
s = iov_to_buf(iov, iov_cnt, 0, &n->mac, sizeof(n->mac));
assert(s == sizeof(n->mac));
qemu_format_nic_info_str(qemu_get_queue(n->nic), n->mac);
+ rxfilter_notify(nc);
+
return VIRTIO_NET_OK;
}
@@ -512,19 +619,19 @@
sizeof(mac_data.entries));
mac_data.entries = ldl_p(&mac_data.entries);
if (s != sizeof(mac_data.entries)) {
- return VIRTIO_NET_ERR;
+ goto error;
}
iov_discard_front(&iov, &iov_cnt, s);
if (mac_data.entries * ETH_ALEN > iov_size(iov, iov_cnt)) {
- return VIRTIO_NET_ERR;
+ goto error;
}
if (mac_data.entries <= MAC_TABLE_ENTRIES) {
s = iov_to_buf(iov, iov_cnt, 0, n->mac_table.macs,
mac_data.entries * ETH_ALEN);
if (s != mac_data.entries * ETH_ALEN) {
- return VIRTIO_NET_ERR;
+ goto error;
}
n->mac_table.in_use += mac_data.entries;
} else {
@@ -539,27 +646,33 @@
sizeof(mac_data.entries));
mac_data.entries = ldl_p(&mac_data.entries);
if (s != sizeof(mac_data.entries)) {
- return VIRTIO_NET_ERR;
+ goto error;
}
iov_discard_front(&iov, &iov_cnt, s);
if (mac_data.entries * ETH_ALEN != iov_size(iov, iov_cnt)) {
- return VIRTIO_NET_ERR;
+ goto error;
}
if (n->mac_table.in_use + mac_data.entries <= MAC_TABLE_ENTRIES) {
s = iov_to_buf(iov, iov_cnt, 0, n->mac_table.macs,
mac_data.entries * ETH_ALEN);
if (s != mac_data.entries * ETH_ALEN) {
- return VIRTIO_NET_ERR;
+ goto error;
}
n->mac_table.in_use += mac_data.entries;
} else {
n->mac_table.multi_overflow = 1;
}
+ rxfilter_notify(nc);
+
return VIRTIO_NET_OK;
+
+error:
+ rxfilter_notify(nc);
+ return VIRTIO_NET_ERR;
}
static int virtio_net_handle_vlan_table(VirtIONet *n, uint8_t cmd,
@@ -567,6 +680,7 @@
{
uint16_t vid;
size_t s;
+ NetClientState *nc = qemu_get_queue(n->nic);
s = iov_to_buf(iov, iov_cnt, 0, &vid, sizeof(vid));
vid = lduw_p(&vid);
@@ -584,6 +698,8 @@
else
return VIRTIO_NET_ERR;
+ rxfilter_notify(nc);
+
return VIRTIO_NET_OK;
}
@@ -1312,6 +1428,7 @@
.receive = virtio_net_receive,
.cleanup = virtio_net_cleanup,
.link_status_changed = virtio_net_set_link_status,
+ .query_rx_filter = virtio_net_query_rxfilter,
};
static bool virtio_net_guest_notifier_pending(VirtIODevice *vdev, int idx)
@@ -1373,6 +1490,7 @@
DeviceState *qdev = DEVICE(vdev);
VirtIONet *n = VIRTIO_NET(vdev);
+ NetClientState *nc;
virtio_init(VIRTIO_DEVICE(n), "virtio-net", VIRTIO_ID_NET,
n->config_size);
@@ -1439,6 +1557,9 @@
n->vlans = g_malloc0(MAX_VLAN >> 3);
+ nc = qemu_get_queue(n->nic);
+ nc->rxfilter_notify_enabled = 1;
+
n->qdev = qdev;
register_savevm(qdev, "virtio-net", -1, VIRTIO_NET_VM_VERSION,
virtio_net_save, virtio_net_load, n);
diff --git a/hw/pci-bridge/i82801b11.c b/hw/pci-bridge/i82801b11.c
index 5807a92..b98bfb0 100644
--- a/hw/pci-bridge/i82801b11.c
+++ b/hw/pci-bridge/i82801b11.c
@@ -69,7 +69,7 @@
if (rc < 0) {
goto err_bridge;
}
- pci_config_set_prog_interface(d->config, PCI_CLASS_BRDIGE_PCI_INF_SUB);
+ pci_config_set_prog_interface(d->config, PCI_CLASS_BRIDGE_PCI_INF_SUB);
return 0;
err_bridge:
diff --git a/hw/pci/pci.c b/hw/pci/pci.c
index dcc85ef..81cf5a9 100644
--- a/hw/pci/pci.c
+++ b/hw/pci/pci.c
@@ -378,6 +378,7 @@
static int get_pci_config_device(QEMUFile *f, void *pv, size_t size)
{
PCIDevice *s = container_of(pv, PCIDevice, config);
+ PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(s);
uint8_t *config;
int i;
@@ -395,6 +396,10 @@
memcpy(s->config, config, size);
pci_update_mappings(s);
+ if (pc->is_bridge) {
+ PCIBridge *b = container_of(s, PCIBridge, dev);
+ pci_bridge_update_mappings(b);
+ }
memory_region_set_enabled(&s->bus_master_enable_region,
pci_get_word(s->config + PCI_COMMAND)
diff --git a/hw/pci/pci_bridge.c b/hw/pci/pci_bridge.c
index ecdeab0..02a396b 100644
--- a/hw/pci/pci_bridge.c
+++ b/hw/pci/pci_bridge.c
@@ -224,7 +224,7 @@
g_free(w);
}
-static void pci_bridge_update_mappings(PCIBridge *br)
+void pci_bridge_update_mappings(PCIBridge *br)
{
PCIBridgeWindows *w = br->windows;
diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h
index 61ff154..7fb97b0 100644
--- a/include/hw/i386/pc.h
+++ b/include/hw/i386/pc.h
@@ -231,6 +231,10 @@
.driver = "Nehalem-" TYPE_X86_CPU,\
.property = "level",\
.value = stringify(2),\
+ },{\
+ .driver = "virtio-net-pci",\
+ .property = "any_layout",\
+ .value = "off",\
}
#define PC_COMPAT_1_4 \
diff --git a/include/hw/pci/pci_bridge.h b/include/hw/pci/pci_bridge.h
index 1868f7a..1d8f997 100644
--- a/include/hw/pci/pci_bridge.h
+++ b/include/hw/pci/pci_bridge.h
@@ -37,6 +37,7 @@
pcibus_t pci_bridge_get_base(const PCIDevice *bridge, uint8_t type);
pcibus_t pci_bridge_get_limit(const PCIDevice *bridge, uint8_t type);
+void pci_bridge_update_mappings(PCIBridge *br);
void pci_bridge_write_config(PCIDevice *d,
uint32_t address, uint32_t val, int len);
void pci_bridge_disable_base_limit(PCIDevice *dev);
diff --git a/include/hw/pci/pci_ids.h b/include/hw/pci/pci_ids.h
index 08f8161..d7933bf 100644
--- a/include/hw/pci/pci_ids.h
+++ b/include/hw/pci/pci_ids.h
@@ -39,7 +39,7 @@
#define PCI_CLASS_BRIDGE_HOST 0x0600
#define PCI_CLASS_BRIDGE_ISA 0x0601
#define PCI_CLASS_BRIDGE_PCI 0x0604
-#define PCI_CLASS_BRDIGE_PCI_INF_SUB 0x01
+#define PCI_CLASS_BRIDGE_PCI_INF_SUB 0x01
#define PCI_CLASS_BRIDGE_OTHER 0x0680
#define PCI_CLASS_COMMUNICATION_SERIAL 0x0700
diff --git a/include/hw/virtio/virtio-net.h b/include/hw/virtio/virtio-net.h
index b315ac9..df60f16 100644
--- a/include/hw/virtio/virtio-net.h
+++ b/include/hw/virtio/virtio-net.h
@@ -243,6 +243,7 @@
#define DEFINE_VIRTIO_NET_FEATURES(_state, _field) \
DEFINE_VIRTIO_COMMON_FEATURES(_state, _field), \
+ DEFINE_PROP_BIT("any_layout", _state, _field, VIRTIO_F_ANY_LAYOUT, true), \
DEFINE_PROP_BIT("csum", _state, _field, VIRTIO_NET_F_CSUM, true), \
DEFINE_PROP_BIT("guest_csum", _state, _field, VIRTIO_NET_F_GUEST_CSUM, true), \
DEFINE_PROP_BIT("gso", _state, _field, VIRTIO_NET_F_GSO, true), \
diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h
index a6c5c53..5d1d2be 100644
--- a/include/hw/virtio/virtio.h
+++ b/include/hw/virtio/virtio.h
@@ -43,6 +43,8 @@
/* We notify when the ring is completely used, even if the guest is suppressing
* callbacks */
#define VIRTIO_F_NOTIFY_ON_EMPTY 24
+/* Can the device handle any descriptor layout? */
+#define VIRTIO_F_ANY_LAYOUT 27
/* We support indirect buffer descriptors */
#define VIRTIO_RING_F_INDIRECT_DESC 28
/* The Guest publishes the used index for which it expects an interrupt
diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h
index 1a6cfcf..1942cc4 100644
--- a/include/monitor/monitor.h
+++ b/include/monitor/monitor.h
@@ -41,6 +41,7 @@
QEVENT_BLOCK_JOB_READY,
QEVENT_DEVICE_DELETED,
QEVENT_DEVICE_TRAY_MOVED,
+ QEVENT_NIC_RX_FILTER_CHANGED,
QEVENT_SUSPEND,
QEVENT_SUSPEND_DISK,
QEVENT_WAKEUP,
diff --git a/include/net/net.h b/include/net/net.h
index 43d85a1..30e4b04 100644
--- a/include/net/net.h
+++ b/include/net/net.h
@@ -49,6 +49,7 @@
typedef void (NetCleanup) (NetClientState *);
typedef void (LinkStatusChanged)(NetClientState *);
typedef void (NetClientDestructor)(NetClientState *);
+typedef RxFilterInfo *(QueryRxFilter)(NetClientState *);
typedef struct NetClientInfo {
NetClientOptionsKind type;
@@ -59,6 +60,7 @@
NetCanReceive *can_receive;
NetCleanup *cleanup;
LinkStatusChanged *link_status_changed;
+ QueryRxFilter *query_rx_filter;
NetPoll *poll;
} NetClientInfo;
@@ -74,6 +76,7 @@
unsigned receive_disabled : 1;
NetClientDestructor *destructor;
unsigned int queue_index;
+ unsigned rxfilter_notify_enabled:1;
};
typedef struct NICState {
diff --git a/monitor.c b/monitor.c
index 2ba7876..99b6ed9 100644
--- a/monitor.c
+++ b/monitor.c
@@ -490,6 +490,7 @@
[QEVENT_BLOCK_JOB_READY] = "BLOCK_JOB_READY",
[QEVENT_DEVICE_DELETED] = "DEVICE_DELETED",
[QEVENT_DEVICE_TRAY_MOVED] = "DEVICE_TRAY_MOVED",
+ [QEVENT_NIC_RX_FILTER_CHANGED] = "NIC_RX_FILTER_CHANGED",
[QEVENT_SUSPEND] = "SUSPEND",
[QEVENT_SUSPEND_DISK] = "SUSPEND_DISK",
[QEVENT_WAKEUP] = "WAKEUP",
diff --git a/net/net.c b/net/net.c
index 43a74e4..c0d61bf 100644
--- a/net/net.c
+++ b/net/net.c
@@ -961,6 +961,54 @@
nc->info_str);
}
+RxFilterInfoList *qmp_query_rx_filter(bool has_name, const char *name,
+ Error **errp)
+{
+ NetClientState *nc;
+ RxFilterInfoList *filter_list = NULL, *last_entry = NULL;
+
+ QTAILQ_FOREACH(nc, &net_clients, next) {
+ RxFilterInfoList *entry;
+ RxFilterInfo *info;
+
+ if (has_name && strcmp(nc->name, name) != 0) {
+ continue;
+ }
+
+ /* only query rx-filter information of NIC */
+ if (nc->info->type != NET_CLIENT_OPTIONS_KIND_NIC) {
+ if (has_name) {
+ error_setg(errp, "net client(%s) isn't a NIC", name);
+ break;
+ }
+ continue;
+ }
+
+ if (nc->info->query_rx_filter) {
+ info = nc->info->query_rx_filter(nc);
+ entry = g_malloc0(sizeof(*entry));
+ entry->value = info;
+
+ if (!filter_list) {
+ filter_list = entry;
+ } else {
+ last_entry->next = entry;
+ }
+ last_entry = entry;
+ } else if (has_name) {
+ error_setg(errp, "net client(%s) doesn't support"
+ " rx-filter querying", name);
+ break;
+ }
+ }
+
+ if (filter_list == NULL && !error_is_set(errp) && has_name) {
+ error_setg(errp, "invalid net client name: %s", name);
+ }
+
+ return filter_list;
+}
+
void do_info_network(Monitor *mon, const QDict *qdict)
{
NetClientState *nc, *peer;
diff --git a/qapi-schema.json b/qapi-schema.json
index 7b9fef1..8d33d52 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -3679,3 +3679,79 @@
'*cpuid-input-ecx': 'int',
'cpuid-register': 'X86CPURegister32',
'features': 'int' } }
+
+##
+# @RxState:
+#
+# Packets receiving state
+#
+# @normal: filter assigned packets according to the mac-table
+#
+# @none: don't receive any assigned packet
+#
+# @all: receive all assigned packets
+#
+# Since: 1.6
+##
+{ 'enum': 'RxState', 'data': [ 'normal', 'none', 'all' ] }
+
+##
+# @RxFilterInfo:
+#
+# Rx-filter information for a NIC.
+#
+# @name: net client name
+#
+# @promiscuous: whether promiscuous mode is enabled
+#
+# @multicast: multicast receive state
+#
+# @unicast: unicast receive state
+#
+# @broadcast-allowed: whether to receive broadcast
+#
+# @multicast-overflow: multicast table is overflowed or not
+#
+# @unicast-overflow: unicast table is overflowed or not
+#
+# @main-mac: the main macaddr string
+#
+# @vlan-table: a list of active vlan id
+#
+# @unicast-table: a list of unicast macaddr string
+#
+# @multicast-table: a list of multicast macaddr string
+#
+# Since 1.6
+##
+
+{ 'type': 'RxFilterInfo',
+ 'data': {
+ 'name': 'str',
+ 'promiscuous': 'bool',
+ 'multicast': 'RxState',
+ 'unicast': 'RxState',
+ 'broadcast-allowed': 'bool',
+ 'multicast-overflow': 'bool',
+ 'unicast-overflow': 'bool',
+ 'main-mac': 'str',
+ 'vlan-table': ['int'],
+ 'unicast-table': ['str'],
+ 'multicast-table': ['str'] }}
+
+##
+# @query-rx-filter:
+#
+# Return rx-filter information for all NICs (or for the given NIC).
+#
+# @name: #optional net client name
+#
+# Returns: list of @RxFilterInfo for all NICs (or for the given NIC).
+# Returns an error if the given @name doesn't exist, or given
+# NIC doesn't support rx-filter querying, or given net client
+# isn't a NIC.
+#
+# Since: 1.6
+##
+{ 'command': 'query-rx-filter', 'data': { '*name': 'str' },
+ 'returns': ['RxFilterInfo'] }
diff --git a/qmp-commands.hx b/qmp-commands.hx
index b8e7758..65a9e26 100644
--- a/qmp-commands.hx
+++ b/qmp-commands.hx
@@ -3050,3 +3050,66 @@
<- { "return": {} }
EQMP
+ {
+ .name = "query-rx-filter",
+ .args_type = "name:s?",
+ .mhandler.cmd_new = qmp_marshal_input_query_rx_filter,
+ },
+
+SQMP
+query-rx-filter
+---------------
+
+Show rx-filter information.
+
+Returns a json-array of rx-filter information for all NICs (or for the
+given NIC), returning an error if the given NIC doesn't exist, or
+given NIC doesn't support rx-filter querying, or given net client
+isn't a NIC.
+
+The query will clear the event notification flag of each NIC, then qemu
+will start to emit event to QMP monitor.
+
+Each array entry contains the following:
+
+- "name": net client name (json-string)
+- "promiscuous": promiscuous mode is enabled (json-bool)
+- "multicast": multicast receive state (one of 'normal', 'none', 'all')
+- "unicast": unicast receive state (one of 'normal', 'none', 'all')
+- "broadcast-allowed": allow to receive broadcast (json-bool)
+- "multicast-overflow": multicast table is overflowed (json-bool)
+- "unicast-overflow": unicast table is overflowed (json-bool)
+- "main-mac": main macaddr string (json-string)
+- "vlan-table": a json-array of active vlan id
+- "unicast-table": a json-array of unicast macaddr string
+- "multicast-table": a json-array of multicast macaddr string
+
+Example:
+
+-> { "execute": "query-rx-filter", "arguments": { "name": "vnet0" } }
+<- { "return": [
+ {
+ "promiscuous": true,
+ "name": "vnet0",
+ "main-mac": "52:54:00:12:34:56",
+ "unicast": "normal",
+ "vlan-table": [
+ 4,
+ 0
+ ],
+ "unicast-table": [
+ ],
+ "multicast": "normal",
+ "multicast-overflow": false,
+ "unicast-overflow": false,
+ "multicast-table": [
+ "01:00:5e:00:00:01",
+ "33:33:00:00:00:01",
+ "33:33:ff:12:34:56"
+ ],
+ "broadcast-allowed": false
+ }
+ ]
+ }
+
+EQMP