Monitor patches for 2018-06-30

-----BEGIN PGP SIGNATURE-----
 
 iQIcBAABAgAGBQJbN640AAoJEDhwtADrkYZTGxsQAKxuoAn1P40s3T2HQjFjUnHz
 BV2V5R0bUpnTOph9CAGXZg+y5Ea/XhN+W7qSpYpHTaNoKkbmOuhInWD5UywZrU0n
 3+97PuUXw3wqr1XjzgkGGu5AaxL4dP8rkS/trOlkqSnXFZOO4ODgnPpWshjC1uTk
 FiMGNa2fSnz2sW2qTVasygQuQ1P6OXZwFP/0TH6S94JbofR2LRMqCARjOKquvECR
 gFRzL0yD5Oqvq3vJuJX8dyyCXItJK3LxVB4Af/pgmonRb5eLTMcd6q3JZyA6OBGO
 X5qZDBBoX9rq4WDU/3abkPTvsNIk4wkpnuIF8x/9qslw7KYz7d91NslDVjPwD8+3
 RK1IrDtJtiILST37iv2lsfJxGAMPWERhW6ulO2966yB+GHZ9/mz1SsX0D8rNlPc5
 W4uPFbawjQ80xjPILSHj0uusoZJ+4L1tCTr3LqFQFTzfKI09Kwizy+zgPvd8nPBO
 9CKDai4Kci6SIiUGuoWN04QsgQCSIVnb6U2Zx1p2h51MRiJg+Sfrk9K4682u6Cua
 2WlQmMruuWYf6XvT/y36qdlnjBQNdur0NY9Y77mK6Ok2VumnT0Lask4elqK0w8Dp
 C2HRWd4NvFWyOqXi+vTlW/sKIGY87BpYXroj2/RIDqPGDb592+FcUJw3MVicxre0
 fNbK2PRlAhU4/oah5RdC
 =DW+w
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/armbru/tags/pull-monitor-2018-06-30' into staging

Monitor patches for 2018-06-30

# gpg: Signature made Sat 30 Jun 2018 17:22:12 BST
# gpg:                using RSA key 3870B400EB918653
# gpg: Good signature from "Markus Armbruster <armbru@redhat.com>"
# gpg:                 aka "Markus Armbruster <armbru@pond.sub.org>"
# Primary key fingerprint: 354B C8B3 D7EB 2A6B 6867  4E5F 3870 B400 EB91 8653

* remotes/armbru/tags/pull-monitor-2018-06-30:
  docs: mention shared state protect for OOB
  tests: iotests: drop some stderr line
  monitor: flush qmp responses when CLOSED
  monitor: rename *_pop_one to *_pop_any
  chardev: comment details for CLOSED event

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2018-06-30 17:30:09 +01:00
commit e3800998e6
5 changed files with 68 additions and 23 deletions

View File

@ -666,22 +666,27 @@ command:
- They are executed in order, - They are executed in order,
- They run only in main thread of QEMU, - They run only in main thread of QEMU,
- They have the BQL taken during execution. - They run with the BQL held.
When a command is executed with OOB, the following changes occur: When a command is executed with OOB, the following changes occur:
- They can be completed before a pending in-band command, - They can be completed before a pending in-band command,
- They run in a dedicated monitor thread, - They run in a dedicated monitor thread,
- They do not take the BQL during execution. - They run with the BQL not held.
OOB command handlers must satisfy the following conditions: OOB command handlers must satisfy the following conditions:
- It executes extremely fast, - It terminates quickly,
- It does not take any lock, or, it can take very small locks if all
critical regions also follow the rules for OOB command handler code,
- It does not invoke system calls that may block, - It does not invoke system calls that may block,
- It does not access guest RAM that may block when userfaultfd is - It does not access guest RAM that may block when userfaultfd is
enabled for postcopy live migration. enabled for postcopy live migration,
- It takes only "fast" locks, i.e. all critical sections protected by
any lock it takes also satisfy the conditions for OOB command
handler code.
The restrictions on locking limit access to shared state. Such access
requires synchronization, but OOB commands can't take the BQL or any
other "slow" lock.
If in doubt, do not implement OOB execution support. If in doubt, do not implement OOB execution support.

View File

@ -22,7 +22,16 @@ typedef enum {
CHR_EVENT_OPENED, /* new connection established */ CHR_EVENT_OPENED, /* new connection established */
CHR_EVENT_MUX_IN, /* mux-focus was set to this terminal */ CHR_EVENT_MUX_IN, /* mux-focus was set to this terminal */
CHR_EVENT_MUX_OUT, /* mux-focus will move on */ CHR_EVENT_MUX_OUT, /* mux-focus will move on */
CHR_EVENT_CLOSED /* connection closed */ CHR_EVENT_CLOSED /* connection closed. NOTE: currently this event
* is only bound to the read port of the chardev.
* Normally the read port and write port of a
* chardev should be the same, but it can be
* different, e.g., for fd chardevs, when the two
* fds are different. So when we received the
* CLOSED event it's still possible that the out
* port is still open. TODO: we should only send
* the CLOSED event when both ports are closed.
*/
} QEMUChrEvent; } QEMUChrEvent;
#define CHR_READ_BUF_LEN 4096 #define CHR_READ_BUF_LEN 4096

View File

@ -541,37 +541,54 @@ struct QMPResponse {
}; };
typedef struct QMPResponse QMPResponse; typedef struct QMPResponse QMPResponse;
static QObject *monitor_qmp_response_pop_one(Monitor *mon)
{
QObject *data;
qemu_mutex_lock(&mon->qmp.qmp_queue_lock);
data = g_queue_pop_head(mon->qmp.qmp_responses);
qemu_mutex_unlock(&mon->qmp.qmp_queue_lock);
return data;
}
static void monitor_qmp_response_flush(Monitor *mon)
{
QObject *data;
while ((data = monitor_qmp_response_pop_one(mon))) {
monitor_json_emitter_raw(mon, data);
qobject_unref(data);
}
}
/* /*
* Return one QMPResponse. The response is only valid if * Pop a QMPResponse from any monitor's response queue into @response.
* response.data is not NULL. * Return false if all the queues are empty; else true.
*/ */
static QMPResponse monitor_qmp_response_pop_one(void) static bool monitor_qmp_response_pop_any(QMPResponse *response)
{ {
Monitor *mon; Monitor *mon;
QObject *data = NULL; QObject *data = NULL;
qemu_mutex_lock(&monitor_lock); qemu_mutex_lock(&monitor_lock);
QTAILQ_FOREACH(mon, &mon_list, entry) { QTAILQ_FOREACH(mon, &mon_list, entry) {
qemu_mutex_lock(&mon->qmp.qmp_queue_lock); data = monitor_qmp_response_pop_one(mon);
data = g_queue_pop_head(mon->qmp.qmp_responses);
qemu_mutex_unlock(&mon->qmp.qmp_queue_lock);
if (data) { if (data) {
response->mon = mon;
response->data = data;
break; break;
} }
} }
qemu_mutex_unlock(&monitor_lock); qemu_mutex_unlock(&monitor_lock);
return (QMPResponse) { .mon = mon, .data = data }; return data != NULL;
} }
static void monitor_qmp_bh_responder(void *opaque) static void monitor_qmp_bh_responder(void *opaque)
{ {
QMPResponse response; QMPResponse response;
while (true) { while (monitor_qmp_response_pop_any(&response)) {
response = monitor_qmp_response_pop_one();
if (!response.data) {
break;
}
monitor_json_emitter_raw(response.mon, response.data); monitor_json_emitter_raw(response.mon, response.data);
qobject_unref(response.data); qobject_unref(response.data);
} }
@ -4199,7 +4216,7 @@ static void monitor_qmp_dispatch_one(QMPRequest *req_obj)
* when we process one request on a specific monitor, we put that * when we process one request on a specific monitor, we put that
* monitor to the end of mon_list queue. * monitor to the end of mon_list queue.
*/ */
static QMPRequest *monitor_qmp_requests_pop_one(void) static QMPRequest *monitor_qmp_requests_pop_any(void)
{ {
QMPRequest *req_obj = NULL; QMPRequest *req_obj = NULL;
Monitor *mon; Monitor *mon;
@ -4231,7 +4248,7 @@ static QMPRequest *monitor_qmp_requests_pop_one(void)
static void monitor_qmp_bh_dispatcher(void *data) static void monitor_qmp_bh_dispatcher(void *data)
{ {
QMPRequest *req_obj = monitor_qmp_requests_pop_one(); QMPRequest *req_obj = monitor_qmp_requests_pop_any();
if (req_obj) { if (req_obj) {
trace_monitor_qmp_cmd_in_band(qobject_get_try_str(req_obj->id) ?: ""); trace_monitor_qmp_cmd_in_band(qobject_get_try_str(req_obj->id) ?: "");
@ -4458,6 +4475,13 @@ static void monitor_qmp_event(void *opaque, int event)
mon_refcount++; mon_refcount++;
break; break;
case CHR_EVENT_CLOSED: case CHR_EVENT_CLOSED:
/*
* Note: this is only useful when the output of the chardev
* backend is still open. For example, when the backend is
* stdio, it's possible that stdout is still open when stdin
* is closed.
*/
monitor_qmp_response_flush(mon);
monitor_qmp_cleanup_queues(mon); monitor_qmp_cleanup_queues(mon);
json_message_parser_destroy(&mon->qmp.parser); json_message_parser_destroy(&mon->qmp.parser);
json_message_parser_init(&mon->qmp.parser, handle_qmp_command); json_message_parser_init(&mon->qmp.parser, handle_qmp_command);

View File

@ -33,6 +33,14 @@ _cleanup()
} }
trap "_cleanup; exit \$status" 0 1 2 3 15 trap "_cleanup; exit \$status" 0 1 2 3 15
# Sometimes the error line might be dumped before/after an event
# randomly. Mask it out for specific test that may trigger this
# uncertainty for current test for now.
_filter_io_error()
{
sed '/Input\/output error/d'
}
# get standard environment, filters and checks # get standard environment, filters and checks
. ./common.rc . ./common.rc
. ./common.filter . ./common.filter
@ -464,7 +472,7 @@ echo "{'execute': 'qmp_capabilities'}
}}" \ }}" \
-incoming exec:'cat /dev/null' \ -incoming exec:'cat /dev/null' \
2>&1 \ 2>&1 \
| _filter_qmp | _filter_qemu_io | _filter_qmp | _filter_qemu_io | _filter_io_error
echo echo
# Image should not have been marked corrupt # Image should not have been marked corrupt

View File

@ -428,7 +428,6 @@ QMP_VERSION
{"return": {}} {"return": {}}
qcow2: Image is corrupt: L2 table offset 0x2a2a2a00 unaligned (L1 index: 0); further non-fatal corruption events will be suppressed qcow2: Image is corrupt: L2 table offset 0x2a2a2a00 unaligned (L1 index: 0); further non-fatal corruption events will be suppressed
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_IMAGE_CORRUPTED", "data": {"device": "", "msg": "L2 table offset 0x2a2a2a00 unaligned (L1 index: 0)", "node-name": "drive", "fatal": false}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_IMAGE_CORRUPTED", "data": {"device": "", "msg": "L2 table offset 0x2a2a2a00 unaligned (L1 index: 0)", "node-name": "drive", "fatal": false}}
read failed: Input/output error
{"return": ""} {"return": ""}
{"return": {}} {"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}