yeahuh / rpms / qemu-kvm

Forked from rpms/qemu-kvm 2 years ago
Clone
9ae3a8
From 8eab8301249fa8f5b3f68a91a87edac676ff7f0f Mon Sep 17 00:00:00 2001
9ae3a8
From: Amos Kong <akong@redhat.com>
9ae3a8
Date: Fri, 8 Nov 2013 06:13:59 +0100
9ae3a8
Subject: [PATCH 4/4] net: add support of mac-programming over macvtap in QEMU side
9ae3a8
9ae3a8
RH-Author: Amos Kong <akong@redhat.com>
9ae3a8
Message-id: <1383891239-29531-5-git-send-email-akong@redhat.com>
9ae3a8
Patchwork-id: 55611
9ae3a8
O-Subject: [RHEL-7.0 qemu-kvm PATCH v2 4/4] net: add support of mac-programming over macvtap in QEMU side
9ae3a8
Bugzilla: 848203
9ae3a8
RH-Acked-by: Vlad Yasevich <vyasevic@redhat.com>
9ae3a8
RH-Acked-by: Laszlo Ersek <lersek@redhat.com>
9ae3a8
RH-Acked-by: Paolo Bonzini <pbonzini@redhat.com>
9ae3a8
9ae3a8
Currently macvtap based macvlan device is working in promiscuous
9ae3a8
mode, we want to implement mac-programming over macvtap through
9ae3a8
Libvirt for better performance.
9ae3a8
9ae3a8
Design:
9ae3a8
 QEMU notifies Libvirt when rx-filter config is changed in guest,
9ae3a8
 then Libvirt query the rx-filter information by a monitor command,
9ae3a8
 and sync the change to macvtap device. Related rx-filter config
9ae3a8
 of the nic contains main mac, rx-mode items and vlan table.
9ae3a8
9ae3a8
This patch adds a QMP event to notify management of rx-filter change,
9ae3a8
and adds a monitor command for management to query rx-filter
9ae3a8
information.
9ae3a8
9ae3a8
Test:
9ae3a8
 If we repeatedly add/remove vlan, and change macaddr of vlan
9ae3a8
 interfaces in guest by a loop script.
9ae3a8
9ae3a8
Result:
9ae3a8
 The events will flood the QMP client(management), management takes
9ae3a8
 too much resource to process the events.
9ae3a8
9ae3a8
 Event_throttle API (set rate to 1 ms) can avoid the events to flood
9ae3a8
 QMP client, but it could cause an unexpected delay (~1ms), guests
9ae3a8
 guests normally expect rx-filter updates immediately.
9ae3a8
9ae3a8
 So we use a flag for each nic to avoid events flooding, the event
9ae3a8
 is emitted once until the query command is executed. The flag
9ae3a8
 implementation could not introduce unexpected delay.
9ae3a8
9ae3a8
There maybe exist an uncontrollable delay if we let Libvirt do the
9ae3a8
real change, guests normally expect rx-filter updates immediately.
9ae3a8
But it's another separate issue, we can investigate it when the
9ae3a8
work in Libvirt side is done.
9ae3a8
9ae3a8
Michael S. Tsirkin: tweaked to enable events on start
9ae3a8
Michael S. Tsirkin: fixed not to crash when no id
9ae3a8
Michael S. Tsirkin: fold in patch:
9ae3a8
   "additional fixes for mac-programming feature"
9ae3a8
Amos Kong: always notify QMP client if mactable is changed
9ae3a8
Amos Kong: return NULL list if no net client supports rx-filter query
9ae3a8
9ae3a8
Reviewed-by: Eric Blake <eblake@redhat.com>
9ae3a8
Reviewed-by: Markus Armbruster <armbru@redhat.com>
9ae3a8
Signed-off-by: Amos Kong <akong@redhat.com>
9ae3a8
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
9ae3a8
(cherry picked from commit b1be42803b31a913bab65bab563a8760ad2e7f7f)
9ae3a8
---
9ae3a8
 QMP/qmp-events.txt        |   17 ++++++
9ae3a8
 hw/net/virtio-net.c       |  133 +++++++++++++++++++++++++++++++++++++++++++--
9ae3a8
 include/monitor/monitor.h |    1 +
9ae3a8
 include/net/net.h         |    3 +
9ae3a8
 monitor.c                 |    1 +
9ae3a8
 net/net.c                 |   48 ++++++++++++++++
9ae3a8
 qapi-schema.json          |   77 ++++++++++++++++++++++++++-
9ae3a8
 qmp-commands.hx           |   64 ++++++++++++++++++++++
9ae3a8
 8 files changed, 337 insertions(+), 7 deletions(-)
9ae3a8
9ae3a8
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
9ae3a8
---
9ae3a8
 QMP/qmp-events.txt        |   17 ++++++
9ae3a8
 hw/net/virtio-net.c       |  133 +++++++++++++++++++++++++++++++++++++++++++--
9ae3a8
 include/monitor/monitor.h |    1 +
9ae3a8
 include/net/net.h         |    3 +
9ae3a8
 monitor.c                 |    1 +
9ae3a8
 net/net.c                 |   48 ++++++++++++++++
9ae3a8
 qapi-schema.json          |   77 ++++++++++++++++++++++++++-
9ae3a8
 qmp-commands.hx           |   64 ++++++++++++++++++++++
9ae3a8
 8 files changed, 337 insertions(+), 7 deletions(-)
9ae3a8
9ae3a8
diff --git a/QMP/qmp-events.txt b/QMP/qmp-events.txt
9ae3a8
index e185030..79fb1c9 100644
9ae3a8
--- a/QMP/qmp-events.txt
9ae3a8
+++ b/QMP/qmp-events.txt
9ae3a8
@@ -194,6 +194,23 @@ Data:
9ae3a8
   },
9ae3a8
   "timestamp": { "seconds": 1265044230, "microseconds": 450486 } }
9ae3a8
 
9ae3a8
+NIC_RX_FILTER_CHANGED
9ae3a8
+-----------------
9ae3a8
+
9ae3a8
+The event is emitted once until the query command is executed,
9ae3a8
+the first event will always be emitted.
9ae3a8
+
9ae3a8
+Data:
9ae3a8
+
9ae3a8
+- "name": net client name (json-string)
9ae3a8
+- "path": device path (json-string)
9ae3a8
+
9ae3a8
+{ "event": "NIC_RX_FILTER_CHANGED",
9ae3a8
+  "data": { "name": "vnet0",
9ae3a8
+            "path": "/machine/peripheral/vnet0/virtio-backend" },
9ae3a8
+  "timestamp": { "seconds": 1368697518, "microseconds": 326866 } }
9ae3a8
+}
9ae3a8
+
9ae3a8
 RESET
9ae3a8
 -----
9ae3a8
 
9ae3a8
diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
9ae3a8
index 3290013..19c5030 100644
9ae3a8
--- a/hw/net/virtio-net.c
9ae3a8
+++ b/hw/net/virtio-net.c
9ae3a8
@@ -21,6 +21,8 @@
9ae3a8
 #include "hw/virtio/virtio-net.h"
9ae3a8
 #include "net/vhost_net.h"
9ae3a8
 #include "hw/virtio/virtio-bus.h"
9ae3a8
+#include "qapi/qmp/qjson.h"
9ae3a8
+#include "monitor/monitor.h"
9ae3a8
 
9ae3a8
 #define VIRTIO_NET_VM_VERSION    11
9ae3a8
 
9ae3a8
@@ -192,6 +194,105 @@ static void virtio_net_set_link_status(NetClientState *nc)
9ae3a8
     virtio_net_set_status(vdev, vdev->status);
9ae3a8
 }
9ae3a8
 
9ae3a8
+static void rxfilter_notify(NetClientState *nc)
9ae3a8
+{
9ae3a8
+    QObject *event_data;
9ae3a8
+    VirtIONet *n = qemu_get_nic_opaque(nc);
9ae3a8
+
9ae3a8
+    if (nc->rxfilter_notify_enabled) {
9ae3a8
+        if (n->netclient_name) {
9ae3a8
+            event_data = qobject_from_jsonf("{ 'name': %s, 'path': %s }",
9ae3a8
+                                    n->netclient_name,
9ae3a8
+                                    object_get_canonical_path(OBJECT(n->qdev)));
9ae3a8
+        } else {
9ae3a8
+            event_data = qobject_from_jsonf("{ 'path': %s }",
9ae3a8
+                                    object_get_canonical_path(OBJECT(n->qdev)));
9ae3a8
+        }
9ae3a8
+        monitor_protocol_event(QEVENT_NIC_RX_FILTER_CHANGED, event_data);
9ae3a8
+        qobject_decref(event_data);
9ae3a8
+
9ae3a8
+        /* disable event notification to avoid events flooding */
9ae3a8
+        nc->rxfilter_notify_enabled = 0;
9ae3a8
+    }
9ae3a8
+}
9ae3a8
+
9ae3a8
+static char *mac_strdup_printf(const uint8_t *mac)
9ae3a8
+{
9ae3a8
+    return g_strdup_printf("%.2x:%.2x:%.2x:%.2x:%.2x:%.2x", mac[0],
9ae3a8
+                            mac[1], mac[2], mac[3], mac[4], mac[5]);
9ae3a8
+}
9ae3a8
+
9ae3a8
+static RxFilterInfo *virtio_net_query_rxfilter(NetClientState *nc)
9ae3a8
+{
9ae3a8
+    VirtIONet *n = qemu_get_nic_opaque(nc);
9ae3a8
+    RxFilterInfo *info;
9ae3a8
+    strList *str_list, *entry;
9ae3a8
+    intList *int_list, *int_entry;
9ae3a8
+    int i, j;
9ae3a8
+
9ae3a8
+    info = g_malloc0(sizeof(*info));
9ae3a8
+    info->name = g_strdup(nc->name);
9ae3a8
+    info->promiscuous = n->promisc;
9ae3a8
+
9ae3a8
+    if (n->nouni) {
9ae3a8
+        info->unicast = RX_STATE_NONE;
9ae3a8
+    } else if (n->alluni) {
9ae3a8
+        info->unicast = RX_STATE_ALL;
9ae3a8
+    } else {
9ae3a8
+        info->unicast = RX_STATE_NORMAL;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    if (n->nomulti) {
9ae3a8
+        info->multicast = RX_STATE_NONE;
9ae3a8
+    } else if (n->allmulti) {
9ae3a8
+        info->multicast = RX_STATE_ALL;
9ae3a8
+    } else {
9ae3a8
+        info->multicast = RX_STATE_NORMAL;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    info->broadcast_allowed = n->nobcast;
9ae3a8
+    info->multicast_overflow = n->mac_table.multi_overflow;
9ae3a8
+    info->unicast_overflow = n->mac_table.uni_overflow;
9ae3a8
+
9ae3a8
+    info->main_mac = mac_strdup_printf(n->mac);
9ae3a8
+
9ae3a8
+    str_list = NULL;
9ae3a8
+    for (i = 0; i < n->mac_table.first_multi; i++) {
9ae3a8
+        entry = g_malloc0(sizeof(*entry));
9ae3a8
+        entry->value = mac_strdup_printf(n->mac_table.macs + i * ETH_ALEN);
9ae3a8
+        entry->next = str_list;
9ae3a8
+        str_list = entry;
9ae3a8
+    }
9ae3a8
+    info->unicast_table = str_list;
9ae3a8
+
9ae3a8
+    str_list = NULL;
9ae3a8
+    for (i = n->mac_table.first_multi; i < n->mac_table.in_use; i++) {
9ae3a8
+        entry = g_malloc0(sizeof(*entry));
9ae3a8
+        entry->value = mac_strdup_printf(n->mac_table.macs + i * ETH_ALEN);
9ae3a8
+        entry->next = str_list;
9ae3a8
+        str_list = entry;
9ae3a8
+    }
9ae3a8
+    info->multicast_table = str_list;
9ae3a8
+
9ae3a8
+    int_list = NULL;
9ae3a8
+    for (i = 0; i < MAX_VLAN >> 5; i++) {
9ae3a8
+        for (j = 0; n->vlans[i] && j < 0x1f; j++) {
9ae3a8
+            if (n->vlans[i] & (1U << j)) {
9ae3a8
+                int_entry = g_malloc0(sizeof(*int_entry));
9ae3a8
+                int_entry->value = (i << 5) + j;
9ae3a8
+                int_entry->next = int_list;
9ae3a8
+                int_list = int_entry;
9ae3a8
+            }
9ae3a8
+        }
9ae3a8
+    }
9ae3a8
+    info->vlan_table = int_list;
9ae3a8
+
9ae3a8
+    /* enable event notification after query */
9ae3a8
+    nc->rxfilter_notify_enabled = 1;
9ae3a8
+
9ae3a8
+    return info;
9ae3a8
+}
9ae3a8
+
9ae3a8
 static void virtio_net_reset(VirtIODevice *vdev)
9ae3a8
 {
9ae3a8
     VirtIONet *n = VIRTIO_NET(vdev);
9ae3a8
@@ -396,6 +497,7 @@ static int virtio_net_handle_rx_mode(VirtIONet *n, uint8_t cmd,
9ae3a8
 {
9ae3a8
     uint8_t on;
9ae3a8
     size_t s;
9ae3a8
+    NetClientState *nc = qemu_get_queue(n->nic);
9ae3a8
 
9ae3a8
     s = iov_to_buf(iov, iov_cnt, 0, &on, sizeof(on));
9ae3a8
     if (s != sizeof(on)) {
9ae3a8
@@ -418,6 +520,8 @@ static int virtio_net_handle_rx_mode(VirtIONet *n, uint8_t cmd,
9ae3a8
         return VIRTIO_NET_ERR;
9ae3a8
     }
9ae3a8
 
9ae3a8
+    rxfilter_notify(nc);
9ae3a8
+
9ae3a8
     return VIRTIO_NET_OK;
9ae3a8
 }
9ae3a8
 
9ae3a8
@@ -426,6 +530,7 @@ static int virtio_net_handle_mac(VirtIONet *n, uint8_t cmd,
9ae3a8
 {
9ae3a8
     struct virtio_net_ctrl_mac mac_data;
9ae3a8
     size_t s;
9ae3a8
+    NetClientState *nc = qemu_get_queue(n->nic);
9ae3a8
 
9ae3a8
     if (cmd == VIRTIO_NET_CTRL_MAC_ADDR_SET) {
9ae3a8
         if (iov_size(iov, iov_cnt) != sizeof(n->mac)) {
9ae3a8
@@ -434,6 +539,8 @@ static int virtio_net_handle_mac(VirtIONet *n, uint8_t cmd,
9ae3a8
         s = iov_to_buf(iov, iov_cnt, 0, &n->mac, sizeof(n->mac));
9ae3a8
         assert(s == sizeof(n->mac));
9ae3a8
         qemu_format_nic_info_str(qemu_get_queue(n->nic), n->mac);
9ae3a8
+        rxfilter_notify(nc);
9ae3a8
+
9ae3a8
         return VIRTIO_NET_OK;
9ae3a8
     }
9ae3a8
 
9ae3a8
@@ -451,19 +558,19 @@ static int virtio_net_handle_mac(VirtIONet *n, uint8_t cmd,
9ae3a8
                    sizeof(mac_data.entries));
9ae3a8
     mac_data.entries = ldl_p(&mac_data.entries);
9ae3a8
     if (s != sizeof(mac_data.entries)) {
9ae3a8
-        return VIRTIO_NET_ERR;
9ae3a8
+        goto error;
9ae3a8
     }
9ae3a8
     iov_discard_front(&iov, &iov_cnt, s);
9ae3a8
 
9ae3a8
     if (mac_data.entries * ETH_ALEN > iov_size(iov, iov_cnt)) {
9ae3a8
-        return VIRTIO_NET_ERR;
9ae3a8
+        goto error;
9ae3a8
     }
9ae3a8
 
9ae3a8
     if (mac_data.entries <= MAC_TABLE_ENTRIES) {
9ae3a8
         s = iov_to_buf(iov, iov_cnt, 0, n->mac_table.macs,
9ae3a8
                        mac_data.entries * ETH_ALEN);
9ae3a8
         if (s != mac_data.entries * ETH_ALEN) {
9ae3a8
-            return VIRTIO_NET_ERR;
9ae3a8
+            goto error;
9ae3a8
         }
9ae3a8
         n->mac_table.in_use += mac_data.entries;
9ae3a8
     } else {
9ae3a8
@@ -478,27 +585,33 @@ static int virtio_net_handle_mac(VirtIONet *n, uint8_t cmd,
9ae3a8
                    sizeof(mac_data.entries));
9ae3a8
     mac_data.entries = ldl_p(&mac_data.entries);
9ae3a8
     if (s != sizeof(mac_data.entries)) {
9ae3a8
-        return VIRTIO_NET_ERR;
9ae3a8
+        goto error;
9ae3a8
     }
9ae3a8
 
9ae3a8
     iov_discard_front(&iov, &iov_cnt, s);
9ae3a8
 
9ae3a8
     if (mac_data.entries * ETH_ALEN != iov_size(iov, iov_cnt)) {
9ae3a8
-        return VIRTIO_NET_ERR;
9ae3a8
+        goto error;
9ae3a8
     }
9ae3a8
 
9ae3a8
     if (n->mac_table.in_use + mac_data.entries <= MAC_TABLE_ENTRIES) {
9ae3a8
         s = iov_to_buf(iov, iov_cnt, 0, n->mac_table.macs,
9ae3a8
                        mac_data.entries * ETH_ALEN);
9ae3a8
         if (s != mac_data.entries * ETH_ALEN) {
9ae3a8
-            return VIRTIO_NET_ERR;
9ae3a8
+            goto error;
9ae3a8
         }
9ae3a8
         n->mac_table.in_use += mac_data.entries;
9ae3a8
     } else {
9ae3a8
         n->mac_table.multi_overflow = 1;
9ae3a8
     }
9ae3a8
 
9ae3a8
+    rxfilter_notify(nc);
9ae3a8
+
9ae3a8
     return VIRTIO_NET_OK;
9ae3a8
+
9ae3a8
+error:
9ae3a8
+    rxfilter_notify(nc);
9ae3a8
+    return VIRTIO_NET_ERR;
9ae3a8
 }
9ae3a8
 
9ae3a8
 static int virtio_net_handle_vlan_table(VirtIONet *n, uint8_t cmd,
9ae3a8
@@ -506,6 +619,7 @@ static int virtio_net_handle_vlan_table(VirtIONet *n, uint8_t cmd,
9ae3a8
 {
9ae3a8
     uint16_t vid;
9ae3a8
     size_t s;
9ae3a8
+    NetClientState *nc = qemu_get_queue(n->nic);
9ae3a8
 
9ae3a8
     s = iov_to_buf(iov, iov_cnt, 0, &vid, sizeof(vid));
9ae3a8
     vid = lduw_p(&vid);
9ae3a8
@@ -523,6 +637,8 @@ static int virtio_net_handle_vlan_table(VirtIONet *n, uint8_t cmd,
9ae3a8
     else
9ae3a8
         return VIRTIO_NET_ERR;
9ae3a8
 
9ae3a8
+    rxfilter_notify(nc);
9ae3a8
+
9ae3a8
     return VIRTIO_NET_OK;
9ae3a8
 }
9ae3a8
 
9ae3a8
@@ -1244,6 +1360,7 @@ static NetClientInfo net_virtio_info = {
9ae3a8
     .receive = virtio_net_receive,
9ae3a8
         .cleanup = virtio_net_cleanup,
9ae3a8
     .link_status_changed = virtio_net_set_link_status,
9ae3a8
+    .query_rx_filter = virtio_net_query_rxfilter,
9ae3a8
 };
9ae3a8
 
9ae3a8
 static bool virtio_net_guest_notifier_pending(VirtIODevice *vdev, int idx)
9ae3a8
@@ -1305,6 +1422,7 @@ static int virtio_net_device_init(VirtIODevice *vdev)
9ae3a8
 
9ae3a8
     DeviceState *qdev = DEVICE(vdev);
9ae3a8
     VirtIONet *n = VIRTIO_NET(vdev);
9ae3a8
+    NetClientState *nc;
9ae3a8
 
9ae3a8
     virtio_init(VIRTIO_DEVICE(n), "virtio-net", VIRTIO_ID_NET,
9ae3a8
                                   n->config_size);
9ae3a8
@@ -1371,6 +1489,9 @@ static int virtio_net_device_init(VirtIODevice *vdev)
9ae3a8
 
9ae3a8
     n->vlans = g_malloc0(MAX_VLAN >> 3);
9ae3a8
 
9ae3a8
+    nc = qemu_get_queue(n->nic);
9ae3a8
+    nc->rxfilter_notify_enabled = 1;
9ae3a8
+
9ae3a8
     n->qdev = qdev;
9ae3a8
     register_savevm(qdev, "virtio-net", -1, VIRTIO_NET_VM_VERSION,
9ae3a8
                     virtio_net_save, virtio_net_load, n);
9ae3a8
diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h
9ae3a8
index 07b41a6..10fa0e3 100644
9ae3a8
--- a/include/monitor/monitor.h
9ae3a8
+++ b/include/monitor/monitor.h
9ae3a8
@@ -41,6 +41,7 @@ typedef enum MonitorEvent {
9ae3a8
     QEVENT_BLOCK_JOB_READY,
9ae3a8
     QEVENT_DEVICE_DELETED,
9ae3a8
     QEVENT_DEVICE_TRAY_MOVED,
9ae3a8
+    QEVENT_NIC_RX_FILTER_CHANGED,
9ae3a8
     QEVENT_SUSPEND,
9ae3a8
     QEVENT_SUSPEND_DISK,
9ae3a8
     QEVENT_WAKEUP,
9ae3a8
diff --git a/include/net/net.h b/include/net/net.h
9ae3a8
index 43d85a1..30e4b04 100644
9ae3a8
--- a/include/net/net.h
9ae3a8
+++ b/include/net/net.h
9ae3a8
@@ -49,6 +49,7 @@ typedef ssize_t (NetReceiveIOV)(NetClientState *, const struct iovec *, int);
9ae3a8
 typedef void (NetCleanup) (NetClientState *);
9ae3a8
 typedef void (LinkStatusChanged)(NetClientState *);
9ae3a8
 typedef void (NetClientDestructor)(NetClientState *);
9ae3a8
+typedef RxFilterInfo *(QueryRxFilter)(NetClientState *);
9ae3a8
 
9ae3a8
 typedef struct NetClientInfo {
9ae3a8
     NetClientOptionsKind type;
9ae3a8
@@ -59,6 +60,7 @@ typedef struct NetClientInfo {
9ae3a8
     NetCanReceive *can_receive;
9ae3a8
     NetCleanup *cleanup;
9ae3a8
     LinkStatusChanged *link_status_changed;
9ae3a8
+    QueryRxFilter *query_rx_filter;
9ae3a8
     NetPoll *poll;
9ae3a8
 } NetClientInfo;
9ae3a8
 
9ae3a8
@@ -74,6 +76,7 @@ struct NetClientState {
9ae3a8
     unsigned receive_disabled : 1;
9ae3a8
     NetClientDestructor *destructor;
9ae3a8
     unsigned int queue_index;
9ae3a8
+    unsigned rxfilter_notify_enabled:1;
9ae3a8
 };
9ae3a8
 
9ae3a8
 typedef struct NICState {
9ae3a8
diff --git a/monitor.c b/monitor.c
9ae3a8
index c226acf..8f36f91 100644
9ae3a8
--- a/monitor.c
9ae3a8
+++ b/monitor.c
9ae3a8
@@ -498,6 +498,7 @@ static const char *monitor_event_names[] = {
9ae3a8
     [QEVENT_BLOCK_JOB_READY] = "BLOCK_JOB_READY",
9ae3a8
     [QEVENT_DEVICE_DELETED] = "DEVICE_DELETED",
9ae3a8
     [QEVENT_DEVICE_TRAY_MOVED] = "DEVICE_TRAY_MOVED",
9ae3a8
+    [QEVENT_NIC_RX_FILTER_CHANGED] = "NIC_RX_FILTER_CHANGED",
9ae3a8
     [QEVENT_SUSPEND] = "SUSPEND",
9ae3a8
     [QEVENT_SUSPEND_DISK] = "SUSPEND_DISK",
9ae3a8
     [QEVENT_WAKEUP] = "WAKEUP",
9ae3a8
diff --git a/net/net.c b/net/net.c
9ae3a8
index 43a74e4..c0d61bf 100644
9ae3a8
--- a/net/net.c
9ae3a8
+++ b/net/net.c
9ae3a8
@@ -961,6 +961,54 @@ void print_net_client(Monitor *mon, NetClientState *nc)
9ae3a8
                    nc->info_str);
9ae3a8
 }
9ae3a8
 
9ae3a8
+RxFilterInfoList *qmp_query_rx_filter(bool has_name, const char *name,
9ae3a8
+                                      Error **errp)
9ae3a8
+{
9ae3a8
+    NetClientState *nc;
9ae3a8
+    RxFilterInfoList *filter_list = NULL, *last_entry = NULL;
9ae3a8
+
9ae3a8
+    QTAILQ_FOREACH(nc, &net_clients, next) {
9ae3a8
+        RxFilterInfoList *entry;
9ae3a8
+        RxFilterInfo *info;
9ae3a8
+
9ae3a8
+        if (has_name && strcmp(nc->name, name) != 0) {
9ae3a8
+            continue;
9ae3a8
+        }
9ae3a8
+
9ae3a8
+        /* only query rx-filter information of NIC */
9ae3a8
+        if (nc->info->type != NET_CLIENT_OPTIONS_KIND_NIC) {
9ae3a8
+            if (has_name) {
9ae3a8
+                error_setg(errp, "net client(%s) isn't a NIC", name);
9ae3a8
+                break;
9ae3a8
+            }
9ae3a8
+            continue;
9ae3a8
+        }
9ae3a8
+
9ae3a8
+        if (nc->info->query_rx_filter) {
9ae3a8
+            info = nc->info->query_rx_filter(nc);
9ae3a8
+            entry = g_malloc0(sizeof(*entry));
9ae3a8
+            entry->value = info;
9ae3a8
+
9ae3a8
+            if (!filter_list) {
9ae3a8
+                filter_list = entry;
9ae3a8
+            } else {
9ae3a8
+                last_entry->next = entry;
9ae3a8
+            }
9ae3a8
+            last_entry = entry;
9ae3a8
+        } else if (has_name) {
9ae3a8
+            error_setg(errp, "net client(%s) doesn't support"
9ae3a8
+                       " rx-filter querying", name);
9ae3a8
+            break;
9ae3a8
+        }
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    if (filter_list == NULL && !error_is_set(errp) && has_name) {
9ae3a8
+        error_setg(errp, "invalid net client name: %s", name);
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    return filter_list;
9ae3a8
+}
9ae3a8
+
9ae3a8
 void do_info_network(Monitor *mon, const QDict *qdict)
9ae3a8
 {
9ae3a8
     NetClientState *nc, *peer;
9ae3a8
diff --git a/qapi-schema.json b/qapi-schema.json
9ae3a8
index 64696a9..92fcd54 100644
9ae3a8
--- a/qapi-schema.json
9ae3a8
+++ b/qapi-schema.json
9ae3a8
@@ -3737,7 +3737,6 @@
9ae3a8
             'cpuid-register': 'X86CPURegister32',
9ae3a8
             'features': 'int' } }
9ae3a8
 
9ae3a8
-
9ae3a8
 ##
9ae3a8
 # @BlockdevDiscardOptions
9ae3a8
 #
9ae3a8
@@ -3972,3 +3971,79 @@
9ae3a8
 # Since: 1.7
9ae3a8
 ##
9ae3a8
 { 'command': 'blockdev-add', 'data': { 'options': 'BlockdevOptions' } }
9ae3a8
+
9ae3a8
+##
9ae3a8
+# @RxState:
9ae3a8
+#
9ae3a8
+# Packets receiving state
9ae3a8
+#
9ae3a8
+# @normal: filter assigned packets according to the mac-table
9ae3a8
+#
9ae3a8
+# @none: don't receive any assigned packet
9ae3a8
+#
9ae3a8
+# @all: receive all assigned packets
9ae3a8
+#
9ae3a8
+# Since: 1.6
9ae3a8
+##
9ae3a8
+{ 'enum': 'RxState', 'data': [ 'normal', 'none', 'all' ] }
9ae3a8
+
9ae3a8
+##
9ae3a8
+# @RxFilterInfo:
9ae3a8
+#
9ae3a8
+# Rx-filter information for a NIC.
9ae3a8
+#
9ae3a8
+# @name: net client name
9ae3a8
+#
9ae3a8
+# @promiscuous: whether promiscuous mode is enabled
9ae3a8
+#
9ae3a8
+# @multicast: multicast receive state
9ae3a8
+#
9ae3a8
+# @unicast: unicast receive state
9ae3a8
+#
9ae3a8
+# @broadcast-allowed: whether to receive broadcast
9ae3a8
+#
9ae3a8
+# @multicast-overflow: multicast table is overflowed or not
9ae3a8
+#
9ae3a8
+# @unicast-overflow: unicast table is overflowed or not
9ae3a8
+#
9ae3a8
+# @main-mac: the main macaddr string
9ae3a8
+#
9ae3a8
+# @vlan-table: a list of active vlan id
9ae3a8
+#
9ae3a8
+# @unicast-table: a list of unicast macaddr string
9ae3a8
+#
9ae3a8
+# @multicast-table: a list of multicast macaddr string
9ae3a8
+#
9ae3a8
+# Since 1.6
9ae3a8
+##
9ae3a8
+
9ae3a8
+{ 'type': 'RxFilterInfo',
9ae3a8
+  'data': {
9ae3a8
+    'name':               'str',
9ae3a8
+    'promiscuous':        'bool',
9ae3a8
+    'multicast':          'RxState',
9ae3a8
+    'unicast':            'RxState',
9ae3a8
+    'broadcast-allowed':  'bool',
9ae3a8
+    'multicast-overflow': 'bool',
9ae3a8
+    'unicast-overflow':   'bool',
9ae3a8
+    'main-mac':           'str',
9ae3a8
+    'vlan-table':         ['int'],
9ae3a8
+    'unicast-table':      ['str'],
9ae3a8
+    'multicast-table':    ['str'] }}
9ae3a8
+
9ae3a8
+##
9ae3a8
+# @query-rx-filter:
9ae3a8
+#
9ae3a8
+# Return rx-filter information for all NICs (or for the given NIC).
9ae3a8
+#
9ae3a8
+# @name: #optional net client name
9ae3a8
+#
9ae3a8
+# Returns: list of @RxFilterInfo for all NICs (or for the given NIC).
9ae3a8
+#          Returns an error if the given @name doesn't exist, or given
9ae3a8
+#          NIC doesn't support rx-filter querying, or given net client
9ae3a8
+#          isn't a NIC.
9ae3a8
+#
9ae3a8
+# Since: 1.6
9ae3a8
+##
9ae3a8
+{ 'command': 'query-rx-filter', 'data': { '*name': 'str' },
9ae3a8
+  'returns': ['RxFilterInfo'] }
9ae3a8
diff --git a/qmp-commands.hx b/qmp-commands.hx
9ae3a8
index f71c34e..4942590 100644
9ae3a8
--- a/qmp-commands.hx
9ae3a8
+++ b/qmp-commands.hx
9ae3a8
@@ -3061,3 +3061,67 @@ Example (2):
9ae3a8
 <- { "return": {} }
9ae3a8
 
9ae3a8
 EQMP
9ae3a8
+
9ae3a8
+    {
9ae3a8
+        .name       = "query-rx-filter",
9ae3a8
+        .args_type  = "name:s?",
9ae3a8
+        .mhandler.cmd_new = qmp_marshal_input_query_rx_filter,
9ae3a8
+    },
9ae3a8
+
9ae3a8
+SQMP
9ae3a8
+query-rx-filter
9ae3a8
+---------------
9ae3a8
+
9ae3a8
+Show rx-filter information.
9ae3a8
+
9ae3a8
+Returns a json-array of rx-filter information for all NICs (or for the
9ae3a8
+given NIC), returning an error if the given NIC doesn't exist, or
9ae3a8
+given NIC doesn't support rx-filter querying, or given net client
9ae3a8
+isn't a NIC.
9ae3a8
+
9ae3a8
+The query will clear the event notification flag of each NIC, then qemu
9ae3a8
+will start to emit event to QMP monitor.
9ae3a8
+
9ae3a8
+Each array entry contains the following:
9ae3a8
+
9ae3a8
+- "name": net client name (json-string)
9ae3a8
+- "promiscuous": promiscuous mode is enabled (json-bool)
9ae3a8
+- "multicast": multicast receive state (one of 'normal', 'none', 'all')
9ae3a8
+- "unicast": unicast receive state  (one of 'normal', 'none', 'all')
9ae3a8
+- "broadcast-allowed": allow to receive broadcast (json-bool)
9ae3a8
+- "multicast-overflow": multicast table is overflowed (json-bool)
9ae3a8
+- "unicast-overflow": unicast table is overflowed (json-bool)
9ae3a8
+- "main-mac": main macaddr string (json-string)
9ae3a8
+- "vlan-table": a json-array of active vlan id
9ae3a8
+- "unicast-table": a json-array of unicast macaddr string
9ae3a8
+- "multicast-table": a json-array of multicast macaddr string
9ae3a8
+
9ae3a8
+Example:
9ae3a8
+
9ae3a8
+-> { "execute": "query-rx-filter", "arguments": { "name": "vnet0" } }
9ae3a8
+<- { "return": [
9ae3a8
+        {
9ae3a8
+            "promiscuous": true,
9ae3a8
+            "name": "vnet0",
9ae3a8
+            "main-mac": "52:54:00:12:34:56",
9ae3a8
+            "unicast": "normal",
9ae3a8
+            "vlan-table": [
9ae3a8
+                4,
9ae3a8
+                0
9ae3a8
+            ],
9ae3a8
+            "unicast-table": [
9ae3a8
+            ],
9ae3a8
+            "multicast": "normal",
9ae3a8
+            "multicast-overflow": false,
9ae3a8
+            "unicast-overflow": false,
9ae3a8
+            "multicast-table": [
9ae3a8
+                "01:00:5e:00:00:01",
9ae3a8
+                "33:33:00:00:00:01",
9ae3a8
+                "33:33:ff:12:34:56"
9ae3a8
+            ],
9ae3a8
+            "broadcast-allowed": false
9ae3a8
+        }
9ae3a8
+      ]
9ae3a8
+   }
9ae3a8
+
9ae3a8
+EQMP
9ae3a8
-- 
9ae3a8
1.7.1
9ae3a8