From: Luiz Capitulino <lcapitulino@redhat.com>
To: peter.maydell@linaro.org
Cc: qemu-devel@nongnu.org, anthony@codemonkey.ws
Subject: [Qemu-devel] [PULL 38/40] qemu-char: make writes thread-safe
Date: Thu, 19 Jun 2014 15:39:50 -0400 [thread overview]
Message-ID: <1403206792-15387-39-git-send-email-lcapitulino@redhat.com> (raw)
In-Reply-To: <1403206792-15387-1-git-send-email-lcapitulino@redhat.com>
From: Paolo Bonzini <pbonzini@redhat.com>
This will let threads other than the I/O thread raise QMP events.
GIOChannel is thread-safe, and send and receive state is usually
well-separated. The only driver that requires some care is the
pty driver, where some of the state is shared by the read and write
sides. That state is protected with the chr_write_lock too.
Reviewed-by: Fam Zheng <famz@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
---
include/sysemu/char.h | 11 ++++++----
qemu-char.c | 58 ++++++++++++++++++++++++++++++++++++++++-----------
2 files changed, 53 insertions(+), 16 deletions(-)
diff --git a/include/sysemu/char.h b/include/sysemu/char.h
index 65782c0..b3e4735 100644
--- a/include/sysemu/char.h
+++ b/include/sysemu/char.h
@@ -54,6 +54,7 @@ typedef struct {
typedef void IOEventHandler(void *opaque, int event);
struct CharDriverState {
+ QemuMutex chr_write_lock;
void (*init)(struct CharDriverState *s);
int (*chr_write)(struct CharDriverState *s, const uint8_t *buf, int len);
GSource *(*chr_add_watch)(struct CharDriverState *s, GIOCondition cond);
@@ -160,6 +161,7 @@ void qemu_chr_fe_event(CharDriverState *s, int event);
* @qemu_chr_fe_printf:
*
* Write to a character backend using a printf style interface.
+ * This function is thread-safe.
*
* @fmt see #printf
*/
@@ -172,8 +174,9 @@ int qemu_chr_fe_add_watch(CharDriverState *s, GIOCondition cond,
/**
* @qemu_chr_fe_write:
*
- * Write data to a character backend from the front end. This function will
- * send data from the front end to the back end.
+ * Write data to a character backend from the front end. This function
+ * will send data from the front end to the back end. This function
+ * is thread-safe.
*
* @buf the data
* @len the number of bytes to send
@@ -188,7 +191,7 @@ int qemu_chr_fe_write(CharDriverState *s, const uint8_t *buf, int len);
* Write data to a character backend from the front end. This function will
* send data from the front end to the back end. Unlike @qemu_chr_fe_write,
* this function will block if the back end cannot consume all of the data
- * attempted to be written.
+ * attempted to be written. This function is thread-safe.
*
* @buf the data
* @len the number of bytes to send
@@ -200,7 +203,7 @@ int qemu_chr_fe_write_all(CharDriverState *s, const uint8_t *buf, int len);
/**
* @qemu_chr_fe_ioctl:
*
- * Issue a device specific ioctl to a backend.
+ * Issue a device specific ioctl to a backend. This function is thread-safe.
*
* @cmd see CHR_IOCTL_*
* @arg the data associated with @cmd
diff --git a/qemu-char.c b/qemu-char.c
index 9470ea2..b0f3ff4 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -121,7 +121,12 @@ void qemu_chr_be_generic_open(CharDriverState *s)
int qemu_chr_fe_write(CharDriverState *s, const uint8_t *buf, int len)
{
- return s->chr_write(s, buf, len);
+ int ret;
+
+ qemu_mutex_lock(&s->chr_write_lock);
+ ret = s->chr_write(s, buf, len);
+ qemu_mutex_unlock(&s->chr_write_lock);
+ return ret;
}
int qemu_chr_fe_write_all(CharDriverState *s, const uint8_t *buf, int len)
@@ -129,6 +134,7 @@ int qemu_chr_fe_write_all(CharDriverState *s, const uint8_t *buf, int len)
int offset = 0;
int res;
+ qemu_mutex_lock(&s->chr_write_lock);
while (offset < len) {
do {
res = s->chr_write(s, buf + offset, len - offset);
@@ -137,17 +143,17 @@ int qemu_chr_fe_write_all(CharDriverState *s, const uint8_t *buf, int len)
}
} while (res == -1 && errno == EAGAIN);
- if (res == 0) {
+ if (res <= 0) {
break;
}
- if (res < 0) {
- return res;
- }
-
offset += res;
}
+ qemu_mutex_unlock(&s->chr_write_lock);
+ if (res < 0) {
+ return res;
+ }
return offset;
}
@@ -269,11 +275,14 @@ typedef struct {
int prod[MAX_MUX];
int cons[MAX_MUX];
int timestamps;
+
+ /* Protected by the CharDriverState chr_write_lock. */
int linestart;
int64_t timestamps_start;
} MuxDriver;
+/* Called with chr_write_lock held. */
static int mux_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
{
MuxDriver *d = chr->opaque;
@@ -819,6 +828,7 @@ typedef struct FDCharDriver {
QTAILQ_ENTRY(FDCharDriver) node;
} FDCharDriver;
+/* Called with chr_write_lock held. */
static int fd_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
{
FDCharDriver *s = chr->opaque;
@@ -1018,12 +1028,14 @@ static CharDriverState *qemu_chr_open_stdio(ChardevStdio *opts)
typedef struct {
GIOChannel *fd;
- int connected;
int read_bytes;
+
+ /* Protected by the CharDriverState chr_write_lock. */
+ int connected;
guint timer_tag;
} PtyCharDriver;
-static void pty_chr_update_read_handler(CharDriverState *chr);
+static void pty_chr_update_read_handler_locked(CharDriverState *chr);
static void pty_chr_state(CharDriverState *chr, int connected);
static gboolean pty_chr_timer(gpointer opaque)
@@ -1031,14 +1043,17 @@ static gboolean pty_chr_timer(gpointer opaque)
struct CharDriverState *chr = opaque;
PtyCharDriver *s = chr->opaque;
+ qemu_mutex_lock(&chr->chr_write_lock);
s->timer_tag = 0;
if (!s->connected) {
/* Next poll ... */
- pty_chr_update_read_handler(chr);
+ pty_chr_update_read_handler_locked(chr);
}
+ qemu_mutex_unlock(&chr->chr_write_lock);
return FALSE;
}
+/* Called with chr_write_lock held. */
static void pty_chr_rearm_timer(CharDriverState *chr, int ms)
{
PtyCharDriver *s = chr->opaque;
@@ -1055,7 +1070,8 @@ static void pty_chr_rearm_timer(CharDriverState *chr, int ms)
}
}
-static void pty_chr_update_read_handler(CharDriverState *chr)
+/* Called with chr_write_lock held. */
+static void pty_chr_update_read_handler_locked(CharDriverState *chr)
{
PtyCharDriver *s = chr->opaque;
GPollFD pfd;
@@ -1071,13 +1087,21 @@ static void pty_chr_update_read_handler(CharDriverState *chr)
}
}
+static void pty_chr_update_read_handler(CharDriverState *chr)
+{
+ qemu_mutex_lock(&chr->chr_write_lock);
+ pty_chr_update_read_handler_locked(chr);
+ qemu_mutex_unlock(&chr->chr_write_lock);
+}
+
+/* Called with chr_write_lock held. */
static int pty_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
{
PtyCharDriver *s = chr->opaque;
if (!s->connected) {
/* guest sends data, check for (re-)connect */
- pty_chr_update_read_handler(chr);
+ pty_chr_update_read_handler_locked(chr);
return 0;
}
return io_channel_send(s->fd, buf, len);
@@ -1123,6 +1147,7 @@ static gboolean pty_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque)
return TRUE;
}
+/* Called with chr_write_lock held. */
static void pty_chr_state(CharDriverState *chr, int connected)
{
PtyCharDriver *s = chr->opaque;
@@ -1613,9 +1638,12 @@ static CharDriverState *qemu_chr_open_pp_fd(int fd)
typedef struct {
int max_size;
HANDLE hcom, hrecv, hsend;
- OVERLAPPED orecv, osend;
+ OVERLAPPED orecv;
BOOL fpipe;
DWORD len;
+
+ /* Protected by the CharDriverState chr_write_lock. */
+ OVERLAPPED osend;
} WinCharState;
typedef struct {
@@ -1725,6 +1753,7 @@ static int win_chr_init(CharDriverState *chr, const char *filename)
return -1;
}
+/* Called with chr_write_lock held. */
static int win_chr_write(CharDriverState *chr, const uint8_t *buf, int len1)
{
WinCharState *s = chr->opaque;
@@ -2167,6 +2196,7 @@ typedef struct {
int max_size;
} NetCharDriver;
+/* Called with chr_write_lock held. */
static int udp_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
{
NetCharDriver *s = chr->opaque;
@@ -2307,6 +2337,7 @@ typedef struct {
static gboolean tcp_chr_accept(GIOChannel *chan, GIOCondition cond, void *opaque);
+/* Called with chr_write_lock held. */
static int tcp_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
{
TCPCharDriver *s = chr->opaque;
@@ -2788,6 +2819,7 @@ static size_t ringbuf_count(const CharDriverState *chr)
return d->prod - d->cons;
}
+/* Called with chr_write_lock held. */
static int ringbuf_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
{
RingBufCharDriver *d = chr->opaque;
@@ -2812,9 +2844,11 @@ static int ringbuf_chr_read(CharDriverState *chr, uint8_t *buf, int len)
RingBufCharDriver *d = chr->opaque;
int i;
+ qemu_mutex_lock(&chr->chr_write_lock);
for (i = 0; i < len && d->cons != d->prod; i++) {
buf[i] = d->cbuf[d->cons++ & (d->size - 1)];
}
+ qemu_mutex_unlock(&chr->chr_write_lock);
return i;
}
--
1.9.3
next prev parent reply other threads:[~2014-06-19 19:40 UTC|newest]
Thread overview: 50+ messages / expand[flat|nested] mbox.gz Atom feed top
2014-06-19 19:39 [Qemu-devel] [PULL for-2.1 00/40] QMP queue Luiz Capitulino
2014-06-19 19:39 ` [Qemu-devel] [PULL 01/40] qapi: fix coding style in parameters list Luiz Capitulino
2014-06-19 19:39 ` [Qemu-devel] [PULL 02/40] qapi: add const prefix to 'char *' insider c_type() Luiz Capitulino
2014-06-19 19:39 ` [Qemu-devel] [PULL 03/40] qapi: Suppress unwanted space between type and identifier Luiz Capitulino
2014-06-19 19:39 ` [Qemu-devel] [PULL 04/40] json-lexer: fix escaped backslash in single-quoted string Luiz Capitulino
2014-06-19 19:39 ` [Qemu-devel] [PULL 05/40] os-posix: include sys/time.h Luiz Capitulino
2014-06-19 19:39 ` [Qemu-devel] [PULL 06/40] qapi: Add includes from qapi/ as dependencies Luiz Capitulino
2014-06-19 19:39 ` [Qemu-devel] [PULL 07/40] qapi: add event helper functions Luiz Capitulino
2014-06-19 19:39 ` [Qemu-devel] [PULL 08/40] qapi script: add event support Luiz Capitulino
2014-06-19 19:39 ` [Qemu-devel] [PULL 09/40] test: add test cases for qapi event Luiz Capitulino
2014-06-19 19:39 ` [Qemu-devel] [PULL 10/40] qapi: adjust existing defines Luiz Capitulino
2014-06-19 19:39 ` [Qemu-devel] [PULL 11/40] monitor: add an implemention of qapi event emit method Luiz Capitulino
2014-06-19 19:39 ` [Qemu-devel] [PULL 12/40] qapi: add new schema file qapi-event.json Luiz Capitulino
2014-06-19 19:39 ` [Qemu-devel] [PULL 13/40] qapi event: convert SHUTDOWN Luiz Capitulino
2014-06-19 19:39 ` [Qemu-devel] [PULL 14/40] qapi event: convert POWERDOWN Luiz Capitulino
2014-06-19 19:39 ` [Qemu-devel] [PULL 15/40] qapi event: convert RESET Luiz Capitulino
2014-06-19 19:39 ` [Qemu-devel] [PULL 16/40] qapi event: convert STOP Luiz Capitulino
2014-06-19 19:39 ` [Qemu-devel] [PULL 17/40] qapi event: convert RESUME Luiz Capitulino
2014-06-19 19:39 ` [Qemu-devel] [PULL 18/40] qapi event: convert SUSPEND Luiz Capitulino
2014-06-19 19:39 ` [Qemu-devel] [PULL 19/40] qapi event: convert SUSPEND_DISK Luiz Capitulino
2014-06-19 19:39 ` [Qemu-devel] [PULL 20/40] qapi event: convert WAKEUP Luiz Capitulino
2014-06-19 19:39 ` [Qemu-devel] [PULL 21/40] qapi event: convert RTC_CHANGE Luiz Capitulino
2014-06-19 19:39 ` [Qemu-devel] [PULL 22/40] qapi event: convert WATCHDOG Luiz Capitulino
2014-06-19 19:39 ` [Qemu-devel] [PULL 23/40] qapi event: convert DEVICE_DELETED Luiz Capitulino
2014-06-19 19:39 ` [Qemu-devel] [PULL 24/40] qapi event: convert DEVICE_TRAY_MOVED Luiz Capitulino
2014-06-19 19:39 ` [Qemu-devel] [PULL 25/40] qapi event: convert BLOCK_IO_ERROR and BLOCK_JOB_ERROR Luiz Capitulino
2014-06-19 19:39 ` [Qemu-devel] [PULL 26/40] qapi event: convert BLOCK_IMAGE_CORRUPTED Luiz Capitulino
2014-06-19 19:39 ` [Qemu-devel] [PULL 27/40] qapi event: convert other BLOCK_JOB events Luiz Capitulino
2014-06-19 19:39 ` [Qemu-devel] [PULL 28/40] qapi event: convert NIC_RX_FILTER_CHANGED Luiz Capitulino
2014-06-19 19:39 ` [Qemu-devel] [PULL 29/40] qapi event: convert VNC events Luiz Capitulino
2014-06-19 19:39 ` [Qemu-devel] [PULL 30/40] qapi event: convert SPICE events Luiz Capitulino
2014-06-19 19:39 ` [Qemu-devel] [PULL 31/40] qapi event: convert BALLOON_CHANGE Luiz Capitulino
2014-06-19 19:39 ` [Qemu-devel] [PULL 32/40] qapi event: convert GUEST_PANICKED Luiz Capitulino
2014-06-19 19:39 ` [Qemu-devel] [PULL 33/40] qapi event: convert QUORUM events Luiz Capitulino
2014-06-19 19:39 ` [Qemu-devel] [PULL 34/40] qapi event: clean up Luiz Capitulino
2014-06-19 19:39 ` [Qemu-devel] [PULL 35/40] qemu-char: introduce qemu_chr_alloc Luiz Capitulino
2014-06-19 19:39 ` [Qemu-devel] [PULL 36/40] qemu-char: do not call chr_write directly Luiz Capitulino
2014-06-19 19:39 ` [Qemu-devel] [PULL 37/40] qemu-char: move pty_chr_update_read_handler around Luiz Capitulino
2014-06-19 19:39 ` Luiz Capitulino [this message]
2014-06-19 19:39 ` [Qemu-devel] [PULL 39/40] monitor: protect outbuf and mux_out with mutex Luiz Capitulino
2014-06-19 19:39 ` [Qemu-devel] [PULL 40/40] monitor: protect event emission Luiz Capitulino
2014-06-20 18:24 ` [Qemu-devel] [PULL for-2.1 00/40] QMP queue Peter Maydell
2014-06-20 18:44 ` Luiz Capitulino
2014-06-20 19:17 ` Paolo Bonzini
2014-06-20 19:49 ` Paolo Bonzini
2014-06-20 20:02 ` Eric Blake
2014-06-24 13:08 ` Wenchao Xia
2014-06-24 13:20 ` Eric Blake
2014-06-24 13:24 ` Peter Maydell
2014-06-24 14:52 ` Wenchao Xia
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1403206792-15387-39-git-send-email-lcapitulino@redhat.com \
--to=lcapitulino@redhat.com \
--cc=anthony@codemonkey.ws \
--cc=peter.maydell@linaro.org \
--cc=qemu-devel@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).