+/*
+ * 1. This function takes ownership of rsp, err, and id.
+ * 2. rsp, err, and id may be NULL.
+ * 3. If err != NULL then rsp must be NULL.
+ */
+static void monitor_qmp_respond(Monitor *mon, QObject *rsp,
+ Error *err, QObject *id)
+{
+ QDict *qdict = NULL;
+
+ if (err) {
+ assert(!rsp);
+ qdict = qdict_new();
+ qdict_put_obj(qdict, "error", qmp_build_error_object(err));
+ error_free(err);
+ rsp = QOBJECT(qdict);
+ }
+
+ if (rsp) {
+ if (id) {
+ qdict_put_obj(qobject_to(QDict, rsp), "id", qobject_ref(id));
+ }
+
+ monitor_json_emitter(mon, rsp);
+ }
+
+ qobject_unref(id);
+ qobject_unref(rsp);
+}
+
+/*
+ * Dispatch one single QMP request. The function will free the req_obj
+ * and objects inside it before return.
+ */
+static void monitor_qmp_dispatch_one(QMPRequest *req_obj)
+{
+ Monitor *mon, *old_mon;
+ QObject *req, *rsp = NULL, *id;
+ bool need_resume;
+
+ req = req_obj->req;
+ mon = req_obj->mon;
+ id = req_obj->id;
+ need_resume = req_obj->need_resume;
+
+ g_free(req_obj);
+
+ if (trace_event_get_state_backends(TRACE_HANDLE_QMP_COMMAND)) {
+ QString *req_json = qobject_to_json(req);
+ trace_handle_qmp_command(mon, qstring_get_str(req_json));
+ qobject_unref(req_json);
+ }
+
+ old_mon = cur_mon;
+ cur_mon = mon;
+
+ rsp = qmp_dispatch(mon->qmp.commands, req);
+
+ cur_mon = old_mon;
+
+ /* Respond if necessary */
+ monitor_qmp_respond(mon, rsp, NULL, id);
+
+ /* This pairs with the monitor_suspend() in handle_qmp_command(). */
+ if (need_resume) {
+ monitor_resume(mon);
+ }
+
+ qobject_unref(req);
+}
+
+/*
+ * Pop one QMP request from monitor queues, return NULL if not found.
+ * We are using round-robin fashion to pop the request, to avoid
+ * processing commands only on a very busy monitor. To achieve that,
+ * when we process one request on a specific monitor, we put that
+ * monitor to the end of mon_list queue.
+ */
+static QMPRequest *monitor_qmp_requests_pop_one(void)
+{
+ QMPRequest *req_obj = NULL;
+ Monitor *mon;
+
+ qemu_mutex_lock(&monitor_lock);
+
+ QTAILQ_FOREACH(mon, &mon_list, entry) {
+ qemu_mutex_lock(&mon->qmp.qmp_queue_lock);
+ req_obj = g_queue_pop_head(mon->qmp.qmp_requests);
+ qemu_mutex_unlock(&mon->qmp.qmp_queue_lock);
+ if (req_obj) {
+ break;
+ }
+ }
+
+ if (req_obj) {
+ /*
+ * We found one request on the monitor. Degrade this monitor's
+ * priority to lowest by re-inserting it to end of queue.
+ */
+ QTAILQ_REMOVE(&mon_list, mon, entry);
+ QTAILQ_INSERT_TAIL(&mon_list, mon, entry);
+ }
+
+ qemu_mutex_unlock(&monitor_lock);
+
+ return req_obj;
+}
+
+static void monitor_qmp_bh_dispatcher(void *data)
+{
+ QMPRequest *req_obj = monitor_qmp_requests_pop_one();
+
+ if (req_obj) {
+ trace_monitor_qmp_cmd_in_band(qobject_get_try_str(req_obj->id) ?: "");
+ monitor_qmp_dispatch_one(req_obj);
+ /* Reschedule instead of looping so the main loop stays responsive */
+ qemu_bh_schedule(mon_global.qmp_dispatcher_bh);
+ }
+}
+
+#define QMP_REQ_QUEUE_LEN_MAX (8)
+