* [Qemu-devel] [PATCH v6 1/4] monitor: rename out_lock to mon_lock
2018-05-18 11:07 [Qemu-devel] [PATCH v6 0/4] monitor: let Monitor be thread safe Peter Xu
@ 2018-05-18 11:07 ` Peter Xu
2018-05-18 11:07 ` [Qemu-devel] [PATCH v6 2/4] monitor: protect mon->fds with mon_lock Peter Xu
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Peter Xu @ 2018-05-18 11:07 UTC (permalink / raw)
To: qemu-devel
Cc: Eric Blake, Marc-André Lureau, Markus Armbruster,
Stefan Hajnoczi, Dr . David Alan Gilbert, peterx
The out_lock is protecting a few Monitor fields. In the future the
monitor code will start to run in multiple threads. We are going to
turn it into a bigger lock to protect not only the out buffer but also
all the rest.
Since at it, rearrange the Monitor struct a bit.
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Peter Xu <peterx@redhat.com>
---
monitor.c | 53 +++++++++++++++++++++++++++++------------------------
1 file changed, 29 insertions(+), 24 deletions(-)
diff --git a/monitor.c b/monitor.c
index 46814af533..14c681dc8a 100644
--- a/monitor.c
+++ b/monitor.c
@@ -207,15 +207,6 @@ struct Monitor {
int suspend_cnt; /* Needs to be accessed atomically */
bool skip_flush;
bool use_io_thr;
-
- /* We can't access guest memory when holding the lock */
- QemuMutex out_lock;
- QString *outbuf;
- guint out_watch;
-
- /* Read under either BQL or out_lock, written with BQL+out_lock. */
- int mux_out;
-
ReadLineState *rs;
MonitorQMP qmp;
gchar *mon_cpu_path;
@@ -224,6 +215,20 @@ struct Monitor {
mon_cmd_t *cmd_table;
QLIST_HEAD(,mon_fd_t) fds;
QTAILQ_ENTRY(Monitor) entry;
+
+ /*
+ * The per-monitor lock. We can't access guest memory when holding
+ * the lock.
+ */
+ QemuMutex mon_lock;
+
+ /*
+ * Fields that are protected by the per-monitor lock.
+ */
+ QString *outbuf;
+ guint out_watch;
+ /* Read under either BQL or mon_lock, written with BQL+mon_lock. */
+ int mux_out;
};
/* Let's add monitor global variables to this struct. */
@@ -366,14 +371,14 @@ static gboolean monitor_unblocked(GIOChannel *chan, GIOCondition cond,
{
Monitor *mon = opaque;
- qemu_mutex_lock(&mon->out_lock);
+ qemu_mutex_lock(&mon->mon_lock);
mon->out_watch = 0;
monitor_flush_locked(mon);
- qemu_mutex_unlock(&mon->out_lock);
+ qemu_mutex_unlock(&mon->mon_lock);
return FALSE;
}
-/* Called with mon->out_lock held. */
+/* Called with mon->mon_lock held. */
static void monitor_flush_locked(Monitor *mon)
{
int rc;
@@ -411,9 +416,9 @@ static void monitor_flush_locked(Monitor *mon)
void monitor_flush(Monitor *mon)
{
- qemu_mutex_lock(&mon->out_lock);
+ qemu_mutex_lock(&mon->mon_lock);
monitor_flush_locked(mon);
- qemu_mutex_unlock(&mon->out_lock);
+ qemu_mutex_unlock(&mon->mon_lock);
}
/* flush at every end of line */
@@ -421,7 +426,7 @@ static void monitor_puts(Monitor *mon, const char *str)
{
char c;
- qemu_mutex_lock(&mon->out_lock);
+ qemu_mutex_lock(&mon->mon_lock);
for(;;) {
c = *str++;
if (c == '\0')
@@ -434,7 +439,7 @@ static void monitor_puts(Monitor *mon, const char *str)
monitor_flush_locked(mon);
}
}
- qemu_mutex_unlock(&mon->out_lock);
+ qemu_mutex_unlock(&mon->mon_lock);
}
void monitor_vprintf(Monitor *mon, const char *fmt, va_list ap)
@@ -725,7 +730,7 @@ static void monitor_data_init(Monitor *mon, bool skip_flush,
bool use_io_thr)
{
memset(mon, 0, sizeof(Monitor));
- qemu_mutex_init(&mon->out_lock);
+ qemu_mutex_init(&mon->mon_lock);
qemu_mutex_init(&mon->qmp.qmp_queue_lock);
mon->outbuf = qstring_new();
/* Use *mon_cmds by default. */
@@ -745,7 +750,7 @@ static void monitor_data_destroy(Monitor *mon)
}
readline_free(mon->rs);
qobject_unref(mon->outbuf);
- qemu_mutex_destroy(&mon->out_lock);
+ qemu_mutex_destroy(&mon->mon_lock);
qemu_mutex_destroy(&mon->qmp.qmp_queue_lock);
monitor_qmp_cleanup_req_queue_locked(mon);
monitor_qmp_cleanup_resp_queue_locked(mon);
@@ -777,13 +782,13 @@ char *qmp_human_monitor_command(const char *command_line, bool has_cpu_index,
handle_hmp_command(&hmp, command_line);
cur_mon = old_mon;
- qemu_mutex_lock(&hmp.out_lock);
+ qemu_mutex_lock(&hmp.mon_lock);
if (qstring_get_length(hmp.outbuf) > 0) {
output = g_strdup(qstring_get_str(hmp.outbuf));
} else {
output = g_strdup("");
}
- qemu_mutex_unlock(&hmp.out_lock);
+ qemu_mutex_unlock(&hmp.mon_lock);
out:
monitor_data_destroy(&hmp);
@@ -4377,9 +4382,9 @@ static void monitor_event(void *opaque, int event)
switch (event) {
case CHR_EVENT_MUX_IN:
- qemu_mutex_lock(&mon->out_lock);
+ qemu_mutex_lock(&mon->mon_lock);
mon->mux_out = 0;
- qemu_mutex_unlock(&mon->out_lock);
+ qemu_mutex_unlock(&mon->mon_lock);
if (mon->reset_seen) {
readline_restart(mon->rs);
monitor_resume(mon);
@@ -4399,9 +4404,9 @@ static void monitor_event(void *opaque, int event)
} else {
atomic_inc(&mon->suspend_cnt);
}
- qemu_mutex_lock(&mon->out_lock);
+ qemu_mutex_lock(&mon->mon_lock);
mon->mux_out = 1;
- qemu_mutex_unlock(&mon->out_lock);
+ qemu_mutex_unlock(&mon->mon_lock);
break;
case CHR_EVENT_OPENED:
--
2.17.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [Qemu-devel] [PATCH v6 2/4] monitor: protect mon->fds with mon_lock
2018-05-18 11:07 [Qemu-devel] [PATCH v6 0/4] monitor: let Monitor be thread safe Peter Xu
2018-05-18 11:07 ` [Qemu-devel] [PATCH v6 1/4] monitor: rename out_lock to mon_lock Peter Xu
@ 2018-05-18 11:07 ` Peter Xu
2018-05-18 11:07 ` [Qemu-devel] [PATCH v6 3/4] monitor: more comments on lock-free fleids/funcs Peter Xu
2018-05-18 11:07 ` [Qemu-devel] [PATCH v6 4/4] monitor: add lock to protect mon_fdsets Peter Xu
3 siblings, 0 replies; 5+ messages in thread
From: Peter Xu @ 2018-05-18 11:07 UTC (permalink / raw)
To: qemu-devel
Cc: Eric Blake, Marc-André Lureau, Markus Armbruster,
Stefan Hajnoczi, Dr . David Alan Gilbert, peterx
mon->fds were protected by BQL. Now protect it by mon_lock so that it
can even be used in monitor iothread.
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Peter Xu <peterx@redhat.com>
---
monitor.c | 22 ++++++++++++++++++----
1 file changed, 18 insertions(+), 4 deletions(-)
diff --git a/monitor.c b/monitor.c
index 14c681dc8a..d6c3c08932 100644
--- a/monitor.c
+++ b/monitor.c
@@ -213,7 +213,6 @@ struct Monitor {
BlockCompletionFunc *password_completion_cb;
void *password_opaque;
mon_cmd_t *cmd_table;
- QLIST_HEAD(,mon_fd_t) fds;
QTAILQ_ENTRY(Monitor) entry;
/*
@@ -225,6 +224,7 @@ struct Monitor {
/*
* Fields that are protected by the per-monitor lock.
*/
+ QLIST_HEAD(, mon_fd_t) fds;
QString *outbuf;
guint out_watch;
/* Read under either BQL or mon_lock, written with BQL+mon_lock. */
@@ -2189,7 +2189,7 @@ static void hmp_acl_remove(Monitor *mon, const QDict *qdict)
void qmp_getfd(const char *fdname, Error **errp)
{
mon_fd_t *monfd;
- int fd;
+ int fd, tmp_fd;
fd = qemu_chr_fe_get_msgfd(&cur_mon->chr);
if (fd == -1) {
@@ -2204,13 +2204,17 @@ void qmp_getfd(const char *fdname, Error **errp)
return;
}
+ qemu_mutex_lock(&cur_mon->mon_lock);
QLIST_FOREACH(monfd, &cur_mon->fds, next) {
if (strcmp(monfd->name, fdname) != 0) {
continue;
}
- close(monfd->fd);
+ tmp_fd = monfd->fd;
monfd->fd = fd;
+ qemu_mutex_unlock(&cur_mon->mon_lock);
+ /* Make sure close() is out of critical section */
+ close(tmp_fd);
return;
}
@@ -2219,24 +2223,31 @@ void qmp_getfd(const char *fdname, Error **errp)
monfd->fd = fd;
QLIST_INSERT_HEAD(&cur_mon->fds, monfd, next);
+ qemu_mutex_unlock(&cur_mon->mon_lock);
}
void qmp_closefd(const char *fdname, Error **errp)
{
mon_fd_t *monfd;
+ int tmp_fd;
+ qemu_mutex_lock(&cur_mon->mon_lock);
QLIST_FOREACH(monfd, &cur_mon->fds, next) {
if (strcmp(monfd->name, fdname) != 0) {
continue;
}
QLIST_REMOVE(monfd, next);
- close(monfd->fd);
+ tmp_fd = monfd->fd;
g_free(monfd->name);
g_free(monfd);
+ qemu_mutex_unlock(&cur_mon->mon_lock);
+ /* Make sure close() is out of critical section */
+ close(tmp_fd);
return;
}
+ qemu_mutex_unlock(&cur_mon->mon_lock);
error_setg(errp, QERR_FD_NOT_FOUND, fdname);
}
@@ -2244,6 +2255,7 @@ int monitor_get_fd(Monitor *mon, const char *fdname, Error **errp)
{
mon_fd_t *monfd;
+ qemu_mutex_lock(&mon->mon_lock);
QLIST_FOREACH(monfd, &mon->fds, next) {
int fd;
@@ -2257,10 +2269,12 @@ int monitor_get_fd(Monitor *mon, const char *fdname, Error **errp)
QLIST_REMOVE(monfd, next);
g_free(monfd->name);
g_free(monfd);
+ qemu_mutex_unlock(&mon->mon_lock);
return fd;
}
+ qemu_mutex_unlock(&mon->mon_lock);
error_setg(errp, "File descriptor named '%s' has not been found", fdname);
return -1;
}
--
2.17.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [Qemu-devel] [PATCH v6 3/4] monitor: more comments on lock-free fleids/funcs
2018-05-18 11:07 [Qemu-devel] [PATCH v6 0/4] monitor: let Monitor be thread safe Peter Xu
2018-05-18 11:07 ` [Qemu-devel] [PATCH v6 1/4] monitor: rename out_lock to mon_lock Peter Xu
2018-05-18 11:07 ` [Qemu-devel] [PATCH v6 2/4] monitor: protect mon->fds with mon_lock Peter Xu
@ 2018-05-18 11:07 ` Peter Xu
2018-05-18 11:07 ` [Qemu-devel] [PATCH v6 4/4] monitor: add lock to protect mon_fdsets Peter Xu
3 siblings, 0 replies; 5+ messages in thread
From: Peter Xu @ 2018-05-18 11:07 UTC (permalink / raw)
To: qemu-devel
Cc: Eric Blake, Marc-André Lureau, Markus Armbruster,
Stefan Hajnoczi, Dr . David Alan Gilbert, peterx
Add some explicit comment for both Readline and cpu_set/cpu_get helpers
that they do not need the mon_lock protection.
Signed-off-by: Peter Xu <peterx@redhat.com>
---
monitor.c | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/monitor.c b/monitor.c
index d6c3c08932..696109acb4 100644
--- a/monitor.c
+++ b/monitor.c
@@ -207,7 +207,14 @@ struct Monitor {
int suspend_cnt; /* Needs to be accessed atomically */
bool skip_flush;
bool use_io_thr;
+
+ /*
+ * ReadLineState is only used in parser, and the parser of a
+ * monitor will only be run either in main thread or monitor
+ * IOThread but never both, so no lock is needed when accessing.
+ */
ReadLineState *rs;
+
MonitorQMP qmp;
gchar *mon_cpu_path;
BlockCompletionFunc *password_completion_cb;
@@ -1313,7 +1320,7 @@ void qmp_qmp_capabilities(bool has_enable, QMPCapabilityList *enable,
cur_mon->qmp.commands = &qmp_commands;
}
-/* set the current CPU defined by the user */
+/* Set the current CPU defined by the user. Callers must hold BQL. */
int monitor_set_cpu(int cpu_index)
{
CPUState *cpu;
@@ -1327,6 +1334,7 @@ int monitor_set_cpu(int cpu_index)
return 0;
}
+/* Callers must hold BQL. */
static CPUState *mon_get_cpu_sync(bool synchronize)
{
CPUState *cpu;
--
2.17.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [Qemu-devel] [PATCH v6 4/4] monitor: add lock to protect mon_fdsets
2018-05-18 11:07 [Qemu-devel] [PATCH v6 0/4] monitor: let Monitor be thread safe Peter Xu
` (2 preceding siblings ...)
2018-05-18 11:07 ` [Qemu-devel] [PATCH v6 3/4] monitor: more comments on lock-free fleids/funcs Peter Xu
@ 2018-05-18 11:07 ` Peter Xu
3 siblings, 0 replies; 5+ messages in thread
From: Peter Xu @ 2018-05-18 11:07 UTC (permalink / raw)
To: qemu-devel
Cc: Eric Blake, Marc-André Lureau, Markus Armbruster,
Stefan Hajnoczi, Dr . David Alan Gilbert, peterx
Similar to previous patch, but introduce a new global big lock for
mon_fdsets. Take it where needed.
The monitor_fdset_get_fd() handling is a bit tricky: now we need to call
qemu_mutex_unlock() which might pollute errno, so we need to make sure
the correct errno be passed up to the callers.
Signed-off-by: Peter Xu <peterx@redhat.com>
---
monitor.c | 79 +++++++++++++++++++++++++++++++++++++++++++++----------
1 file changed, 65 insertions(+), 14 deletions(-)
diff --git a/monitor.c b/monitor.c
index 696109acb4..84064a0b89 100644
--- a/monitor.c
+++ b/monitor.c
@@ -269,6 +269,9 @@ typedef struct QMPRequest QMPRequest;
/* Protects mon_list, monitor_event_state. */
static QemuMutex monitor_lock;
+/* Protects mon_fdsets */
+static QemuMutex mon_fdsets_lock;
+
static QTAILQ_HEAD(mon_list, Monitor) mon_list;
static QLIST_HEAD(mon_fdsets, MonFdset) mon_fdsets;
static int mon_refcount;
@@ -285,6 +288,16 @@ static QEMUClockType event_clock_type = QEMU_CLOCK_REALTIME;
static void monitor_command_cb(void *opaque, const char *cmdline,
void *readline_opaque);
+/*
+ * This lock can be used very early, even during param parsing.
+ * Meanwhile it can also be used even at the end of main. Let's keep
+ * it initialized for the whole lifecycle of QEMU.
+ */
+static void __attribute__((constructor)) mon_fdsets_lock_init(void)
+{
+ qemu_mutex_init(&mon_fdsets_lock);
+}
+
/**
* Is @mon a QMP monitor?
*/
@@ -2314,9 +2327,11 @@ static void monitor_fdsets_cleanup(void)
MonFdset *mon_fdset;
MonFdset *mon_fdset_next;
+ qemu_mutex_lock(&mon_fdsets_lock);
QLIST_FOREACH_SAFE(mon_fdset, &mon_fdsets, next, mon_fdset_next) {
monitor_fdset_cleanup(mon_fdset);
}
+ qemu_mutex_unlock(&mon_fdsets_lock);
}
AddfdInfo *qmp_add_fd(bool has_fdset_id, int64_t fdset_id, bool has_opaque,
@@ -2351,6 +2366,7 @@ void qmp_remove_fd(int64_t fdset_id, bool has_fd, int64_t fd, Error **errp)
MonFdsetFd *mon_fdset_fd;
char fd_str[60];
+ qemu_mutex_lock(&mon_fdsets_lock);
QLIST_FOREACH(mon_fdset, &mon_fdsets, next) {
if (mon_fdset->id != fdset_id) {
continue;
@@ -2370,10 +2386,12 @@ void qmp_remove_fd(int64_t fdset_id, bool has_fd, int64_t fd, Error **errp)
goto error;
}
monitor_fdset_cleanup(mon_fdset);
+ qemu_mutex_unlock(&mon_fdsets_lock);
return;
}
error:
+ qemu_mutex_unlock(&mon_fdsets_lock);
if (has_fd) {
snprintf(fd_str, sizeof(fd_str), "fdset-id:%" PRId64 ", fd:%" PRId64,
fdset_id, fd);
@@ -2389,6 +2407,7 @@ FdsetInfoList *qmp_query_fdsets(Error **errp)
MonFdsetFd *mon_fdset_fd;
FdsetInfoList *fdset_list = NULL;
+ qemu_mutex_lock(&mon_fdsets_lock);
QLIST_FOREACH(mon_fdset, &mon_fdsets, next) {
FdsetInfoList *fdset_info = g_malloc0(sizeof(*fdset_info));
FdsetFdInfoList *fdsetfd_list = NULL;
@@ -2418,6 +2437,7 @@ FdsetInfoList *qmp_query_fdsets(Error **errp)
fdset_info->next = fdset_list;
fdset_list = fdset_info;
}
+ qemu_mutex_unlock(&mon_fdsets_lock);
return fdset_list;
}
@@ -2430,6 +2450,7 @@ AddfdInfo *monitor_fdset_add_fd(int fd, bool has_fdset_id, int64_t fdset_id,
MonFdsetFd *mon_fdset_fd;
AddfdInfo *fdinfo;
+ qemu_mutex_lock(&mon_fdsets_lock);
if (has_fdset_id) {
QLIST_FOREACH(mon_fdset, &mon_fdsets, next) {
/* Break if match found or match impossible due to ordering by ID */
@@ -2450,6 +2471,7 @@ AddfdInfo *monitor_fdset_add_fd(int fd, bool has_fdset_id, int64_t fdset_id,
if (fdset_id < 0) {
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "fdset-id",
"a non-negative value");
+ qemu_mutex_unlock(&mon_fdsets_lock);
return NULL;
}
/* Use specified fdset ID */
@@ -2500,16 +2522,22 @@ AddfdInfo *monitor_fdset_add_fd(int fd, bool has_fdset_id, int64_t fdset_id,
fdinfo->fdset_id = mon_fdset->id;
fdinfo->fd = mon_fdset_fd->fd;
+ qemu_mutex_unlock(&mon_fdsets_lock);
return fdinfo;
}
int monitor_fdset_get_fd(int64_t fdset_id, int flags)
{
-#ifndef _WIN32
+#ifdef _WIN32
+ errno = ENOENT;
+ return -1;
+#else
MonFdset *mon_fdset;
MonFdsetFd *mon_fdset_fd;
int mon_fd_flags;
+ int ret = -ENOENT;
+ qemu_mutex_lock(&mon_fdsets_lock);
QLIST_FOREACH(mon_fdset, &mon_fdsets, next) {
if (mon_fdset->id != fdset_id) {
continue;
@@ -2517,49 +2545,68 @@ int monitor_fdset_get_fd(int64_t fdset_id, int flags)
QLIST_FOREACH(mon_fdset_fd, &mon_fdset->fds, next) {
mon_fd_flags = fcntl(mon_fdset_fd->fd, F_GETFL);
if (mon_fd_flags == -1) {
- return -1;
+ ret = -errno;
+ goto out;
}
if ((flags & O_ACCMODE) == (mon_fd_flags & O_ACCMODE)) {
- return mon_fdset_fd->fd;
+ ret = mon_fdset_fd->fd;
+ goto out;
}
}
- errno = EACCES;
- return -1;
+ ret = -EACCES;
+ break;
+ }
+out:
+ qemu_mutex_unlock(&mon_fdsets_lock);
+ /*
+ * qemu_mutex_unlock might pollute errno, so we translate the
+ * "ret" value into errno if error happened.
+ */
+ if (ret < 0) {
+ errno = -ret;
+ ret = -1;
}
+ return ret;
#endif
-
- errno = ENOENT;
- return -1;
}
int monitor_fdset_dup_fd_add(int64_t fdset_id, int dup_fd)
{
MonFdset *mon_fdset;
MonFdsetFd *mon_fdset_fd_dup;
+ int ret = -1;
+ qemu_mutex_lock(&mon_fdsets_lock);
QLIST_FOREACH(mon_fdset, &mon_fdsets, next) {
if (mon_fdset->id != fdset_id) {
continue;
}
QLIST_FOREACH(mon_fdset_fd_dup, &mon_fdset->dup_fds, next) {
if (mon_fdset_fd_dup->fd == dup_fd) {
- return -1;
+ ret = -1;
+ goto out;
}
}
mon_fdset_fd_dup = g_malloc0(sizeof(*mon_fdset_fd_dup));
mon_fdset_fd_dup->fd = dup_fd;
QLIST_INSERT_HEAD(&mon_fdset->dup_fds, mon_fdset_fd_dup, next);
- return 0;
+ ret = 0;
+ break;
}
- return -1;
+
+out:
+ qemu_mutex_unlock(&mon_fdsets_lock);
+ return ret;
}
static int monitor_fdset_dup_fd_find_remove(int dup_fd, bool remove)
{
MonFdset *mon_fdset;
MonFdsetFd *mon_fdset_fd_dup;
+ int ret = -1;
+ qemu_mutex_lock(&mon_fdsets_lock);
QLIST_FOREACH(mon_fdset, &mon_fdsets, next) {
QLIST_FOREACH(mon_fdset_fd_dup, &mon_fdset->dup_fds, next) {
if (mon_fdset_fd_dup->fd == dup_fd) {
@@ -2568,14 +2615,18 @@ static int monitor_fdset_dup_fd_find_remove(int dup_fd, bool remove)
if (QLIST_EMPTY(&mon_fdset->dup_fds)) {
monitor_fdset_cleanup(mon_fdset);
}
- return -1;
+ ret = -1;
+ goto out;
} else {
- return mon_fdset->id;
+ ret = mon_fdset->id;
+ goto out;
}
}
}
}
- return -1;
+out:
+ qemu_mutex_unlock(&mon_fdsets_lock);
+ return ret;
}
int monitor_fdset_dup_fd_find(int dup_fd)
--
2.17.0
^ permalink raw reply related [flat|nested] 5+ messages in thread