summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/sysemu/char.h11
-rw-r--r--qemu-char.c58
2 files changed, 53 insertions, 16 deletions
diff --git a/include/sysemu/char.h b/include/sysemu/char.h
index bd2b62d21a..c8b15f9729 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);
int (*chr_sync_read)(struct CharDriverState *s,
@@ -164,6 +165,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
*/
@@ -176,8 +178,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
@@ -192,7 +195,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
@@ -216,7 +219,7 @@ int qemu_chr_fe_read_all(CharDriverState *s, 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 9961b02ce6..a9025e642e 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;
}
@@ -315,11 +321,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;
@@ -865,6 +874,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;
@@ -1064,12 +1074,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)
@@ -1077,14 +1089,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;
@@ -1101,7 +1116,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;
@@ -1117,13 +1133,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);
@@ -1169,6 +1193,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;
@@ -1659,9 +1684,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 {
@@ -1771,6 +1799,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;
@@ -2213,6 +2242,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;
@@ -2403,6 +2433,7 @@ static int unix_send_msgfds(CharDriverState *chr, const uint8_t *buf, int len)
}
#endif
+/* Called with chr_write_lock held. */
static int tcp_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
{
TCPCharDriver *s = chr->opaque;
@@ -3000,6 +3031,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;
@@ -3024,9 +3056,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;
}