* [Qemu-devel] [RFC PATCH 0/4] *** Half-cooked vhost-pci implementation patch *** @ 2016-11-10 2:47 Wei Wang 2016-11-10 2:47 ` [Qemu-devel] [RFC PATCH 1/4] qemu-char: add the "1-server-N-client" support Wei Wang ` (3 more replies) 0 siblings, 4 replies; 9+ messages in thread From: Wei Wang @ 2016-11-10 2:47 UTC (permalink / raw) To: marcandre.lureau, mst, stefanha, pbonzini, qemu-devel, virtio-dev Cc: Wei Wang As the design patches to the spec are in discussion, I'm sending out the draft code to show how the vhost-pci deign works. Comments on the implementation are also very welcommed. Thanks. Code base: v2.7.0 (git: df03468cad) Wei Wang (4): qemu-char: add the "1-server-N-client" support vhost-user: add the vhost-user extension to support the vhost-pci based inter-vm communication vhost-pci-net device support vhost-user: extend the vhost-user Master (client) part to support vhost-pci hw/net/Makefile.objs | 2 +- hw/net/vhost-pci-net.c | 142 +++++++ hw/net/vhost_net.c | 20 + hw/virtio/Makefile.objs | 1 + hw/virtio/vhost-pci-server.c | 469 ++++++++++++++++++++++ hw/virtio/vhost-user.c | 152 ++++---- hw/virtio/virtio-pci.c | 67 ++++ hw/virtio/virtio-pci.h | 14 + include/hw/pci/pci.h | 1 + include/hw/virtio/vhost-backend.h | 3 + include/hw/virtio/vhost-pci-net.h | 39 ++ include/hw/virtio/vhost-pci-server.h | 45 +++ include/hw/virtio/vhost-user.h | 110 ++++++ include/net/vhost_net.h | 5 + include/standard-headers/linux/vhost_pci_net.h | 45 +++ include/standard-headers/linux/virtio_ids.h | 1 + include/sysemu/char.h | 64 +++- include/sysemu/sysemu.h | 1 + qapi-schema.json | 3 +- qemu-char.c | 512 +++++++++++++++++++------ qemu-options.hx | 4 + vl.c | 26 ++ 22 files changed, 1517 insertions(+), 209 deletions(-) create mode 100644 hw/net/vhost-pci-net.c create mode 100644 hw/virtio/vhost-pci-server.c create mode 100644 include/hw/virtio/vhost-pci-net.h create mode 100644 include/hw/virtio/vhost-pci-server.h create mode 100644 include/hw/virtio/vhost-user.h create mode 100644 include/standard-headers/linux/vhost_pci_net.h -- 2.7.4 ^ permalink raw reply [flat|nested] 9+ messages in thread
* [Qemu-devel] [RFC PATCH 1/4] qemu-char: add the "1-server-N-client" support 2016-11-10 2:47 [Qemu-devel] [RFC PATCH 0/4] *** Half-cooked vhost-pci implementation patch *** Wei Wang @ 2016-11-10 2:47 ` Wei Wang 2016-11-10 11:38 ` Marc-André Lureau 2016-11-10 2:47 ` [Qemu-devel] [RFC PATCH 2/4] vhost-user: add the vhost-user extension to support the vhost-pci based inter-vm communication Wei Wang ` (2 subsequent siblings) 3 siblings, 1 reply; 9+ messages in thread From: Wei Wang @ 2016-11-10 2:47 UTC (permalink / raw) To: marcandre.lureau, mst, stefanha, pbonzini, qemu-devel, virtio-dev Cc: Wei Wang This patch enables a qemu server socket to be connected by multiple client sockets. Signed-off-by: Wei Wang <wei.w.wang@intel.com> --- include/sysemu/char.h | 64 ++++++- qapi-schema.json | 3 +- qemu-char.c | 512 ++++++++++++++++++++++++++++++++++++++------------ 3 files changed, 456 insertions(+), 123 deletions(-) diff --git a/include/sysemu/char.h b/include/sysemu/char.h index ee7e554..ff5dda6 100644 --- a/include/sysemu/char.h +++ b/include/sysemu/char.h @@ -58,17 +58,24 @@ struct ParallelIOArg { typedef void IOEventHandler(void *opaque, int event); +#define MAX_CLIENTS 256 +#define ANONYMOUS_CLIENT (~((uint64_t)0)) 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_write_n)(struct CharDriverState *s, uint64_t id, const uint8_t *buf, int len); int (*chr_sync_read)(struct CharDriverState *s, const uint8_t *buf, int len); + int (*chr_sync_read_n)(struct CharDriverState *s, uint64_t id, + const uint8_t *buf, int len); GSource *(*chr_add_watch)(struct CharDriverState *s, GIOCondition cond); void (*chr_update_read_handler)(struct CharDriverState *s); int (*chr_ioctl)(struct CharDriverState *s, int cmd, void *arg); int (*get_msgfds)(struct CharDriverState *s, int* fds, int num); + int (*get_msgfds_n)(struct CharDriverState *s, uint64_t id, int* fds, int num); int (*set_msgfds)(struct CharDriverState *s, int *fds, int num); + int (*set_msgfds_n)(struct CharDriverState *s, uint64_t id, int *fds, int num); int (*chr_add_client)(struct CharDriverState *chr, int fd); int (*chr_wait_connected)(struct CharDriverState *chr, Error **errp); IOEventHandler *chr_event; @@ -77,6 +84,7 @@ struct CharDriverState { void *handler_opaque; void (*chr_close)(struct CharDriverState *chr); void (*chr_disconnect)(struct CharDriverState *chr); + void (*chr_disconnect_n)(struct CharDriverState *chr, uint64_t id); void (*chr_accept_input)(struct CharDriverState *chr); void (*chr_set_echo)(struct CharDriverState *chr, bool echo); void (*chr_set_fe_open)(struct CharDriverState *chr, int fe_open); @@ -91,7 +99,10 @@ struct CharDriverState { int explicit_be_open; int avail_connections; int is_mux; - guint fd_in_tag; + guint fd_in_tag[MAX_CLIENTS]; + uint64_t max_connections; + unsigned long *conn_bitmap; + uint64_t conn_id; QemuOpts *opts; bool replay; QTAILQ_ENTRY(CharDriverState) next; @@ -281,6 +292,20 @@ int qemu_chr_fe_write(CharDriverState *s, const uint8_t *buf, int len); int qemu_chr_fe_write_all(CharDriverState *s, const uint8_t *buf, int len); /** + * @qemu_chr_fe_write_all_n: + * + * Write data to the selected character backend from the front end. + * + * @id the connection id of the character backend + * @buf the data + * @len the number of bytes to send + * + * Returns: the number of bytes consumed + */ +int qemu_chr_fe_write_all_n(CharDriverState *s, uint64_t id, + const uint8_t *buf, int len); + +/** * @qemu_chr_fe_read_all: * * Read data to a buffer from the back end. @@ -293,6 +318,20 @@ int qemu_chr_fe_write_all(CharDriverState *s, const uint8_t *buf, int len); int qemu_chr_fe_read_all(CharDriverState *s, uint8_t *buf, int len); /** + * @qemu_chr_fe_read_all_n: + * + * Read data to a buffer from the selected back end. + * + * @id the connection id + * @buf the data buffer + * @len the number of bytes to read + * + * Returns: the number of bytes read + */ +int qemu_chr_fe_read_all_n(CharDriverState *s, uint64_t id, + uint8_t *buf, int len); + +/** * @qemu_chr_fe_ioctl: * * Issue a device specific ioctl to a backend. This function is thread-safe. @@ -331,6 +370,19 @@ int qemu_chr_fe_get_msgfd(CharDriverState *s); */ int qemu_chr_fe_get_msgfds(CharDriverState *s, int *fds, int num); + +/** + * @qemu_chr_fe_get_msgfds_n: + * + * The multi-client version of @qemu_chr_fe_get_msgfds. + * + * Returns: -1 if fd passing isn't supported or there are no pending file + * descriptors. If file descriptors are returned, subsequent calls to + * this function will return -1 until a client sends a new set of file + * descriptors. + */ +int qemu_chr_fe_get_msgfds_n(CharDriverState *s, uint64_t id, int *fds, int num); + /** * @qemu_chr_fe_set_msgfds: * @@ -345,6 +397,16 @@ int qemu_chr_fe_get_msgfds(CharDriverState *s, int *fds, int num); int qemu_chr_fe_set_msgfds(CharDriverState *s, int *fds, int num); /** + * @qemu_chr_fe_set_msgfds_n: + * + * The multi-client version of @qemu_chr_fe_set_msgfds. + * + * Returns: -1 if fd passing isn't supported. + */ +int qemu_chr_fe_set_msgfds_n(CharDriverState *s, uint64_t id, int *fds, int num); + + +/** * @qemu_chr_fe_claim: * * Claim a backend before using it, should be called before calling diff --git a/qapi-schema.json b/qapi-schema.json index 5658723..9bb5d7d 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -3327,7 +3327,8 @@ '*wait' : 'bool', '*nodelay' : 'bool', '*telnet' : 'bool', - '*reconnect' : 'int' }, + '*reconnect' : 'int' , + '*connections' : 'uint64' }, 'base': 'ChardevCommon' } ## diff --git a/qemu-char.c b/qemu-char.c index 5f82ebb..dfad6d1 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -265,6 +265,35 @@ static int qemu_chr_fe_write_buffer(CharDriverState *s, const uint8_t *buf, int return res; } +static int qemu_chr_fe_write_buffer_n(CharDriverState *s, uint64_t id, + const uint8_t *buf, int len, int *offset) +{ + int res = 0; + *offset = 0; + + qemu_mutex_lock(&s->chr_write_lock); + while (*offset < len) { + retry: + res = s->chr_write_n(s, id, buf + *offset, len - *offset); + if (res < 0 && errno == EAGAIN) { + g_usleep(100); + goto retry; + } + + if (res <= 0) { + break; + } + + *offset += res; + } + if (*offset > 0) { + qemu_chr_fe_write_log(s, buf, *offset); + } + qemu_mutex_unlock(&s->chr_write_lock); + + return res; +} + int qemu_chr_fe_write(CharDriverState *s, const uint8_t *buf, int len) { int ret; @@ -317,6 +346,31 @@ int qemu_chr_fe_write_all(CharDriverState *s, const uint8_t *buf, int len) return offset; } +int qemu_chr_fe_write_all_n(CharDriverState *s, uint64_t id, + const uint8_t *buf, int len) +{ + int offset; + int res; + + if (s->replay && replay_mode == REPLAY_MODE_PLAY) { + replay_char_write_event_load(&res, &offset); + assert(offset <= len); + qemu_chr_fe_write_buffer_n(s, id, buf, offset, &offset); + return res; + } + + res = qemu_chr_fe_write_buffer_n(s, id, buf, len, &offset); + + if (s->replay && replay_mode == REPLAY_MODE_RECORD) { + replay_char_write_event_save(res, offset); + } + + if (res < 0) { + return res; + } + return offset; +} + int qemu_chr_fe_read_all(CharDriverState *s, uint8_t *buf, int len) { int offset = 0, counter = 10; @@ -325,7 +379,7 @@ int qemu_chr_fe_read_all(CharDriverState *s, uint8_t *buf, int len) if (!s->chr_sync_read) { return 0; } - + if (s->replay && replay_mode == REPLAY_MODE_PLAY) { return replay_char_read_all_load(buf); } @@ -362,6 +416,52 @@ int qemu_chr_fe_read_all(CharDriverState *s, uint8_t *buf, int len) return offset; } +int qemu_chr_fe_read_all_n(CharDriverState *s, uint64_t id, + uint8_t *buf, int len) +{ + int offset = 0, counter = 10; + int res; + + if (!s->chr_sync_read_n) { + return 0; + } + + if (s->replay && replay_mode == REPLAY_MODE_PLAY) { + return replay_char_read_all_load(buf); + } + + while (offset < len) { + retry: + res = s->chr_sync_read_n(s, id, buf + offset, len - offset); + if (res == -1 && errno == EAGAIN) { + g_usleep(100); + goto retry; + } + + if (res == 0) { + break; + } + + if (res < 0) { + if (s->replay && replay_mode == REPLAY_MODE_RECORD) { + replay_char_read_all_save_error(res); + } + return res; + } + + offset += res; + + if (!counter--) { + break; + } + } + + if (s->replay && replay_mode == REPLAY_MODE_RECORD) { + replay_char_read_all_save_buf(buf, offset); + } + return offset; +} + int qemu_chr_fe_ioctl(CharDriverState *s, int cmd, void *arg) { int res; @@ -417,11 +517,23 @@ int qemu_chr_fe_get_msgfds(CharDriverState *s, int *fds, int len) return s->get_msgfds ? s->get_msgfds(s, fds, len) : -1; } +int qemu_chr_fe_get_msgfds_n(CharDriverState *s, + uint64_t id, int *fds, int len) +{ + return s->get_msgfds_n ? s->get_msgfds_n(s, id, fds, len) : -1; +} + int qemu_chr_fe_set_msgfds(CharDriverState *s, int *fds, int num) { return s->set_msgfds ? s->set_msgfds(s, fds, num) : -1; } +int qemu_chr_fe_set_msgfds_n(CharDriverState *s, + uint64_t id, int *fds, int num) +{ + return s->set_msgfds_n ? s->set_msgfds_n(s, id, fds, num) : -1; +} + int qemu_chr_add_client(CharDriverState *s, int fd) { return s->chr_add_client ? s->chr_add_client(s, fd) : -1; @@ -951,12 +1063,19 @@ static void io_remove_watch_poll(guint tag) static void remove_fd_in_watch(CharDriverState *chr) { - if (chr->fd_in_tag) { - io_remove_watch_poll(chr->fd_in_tag); - chr->fd_in_tag = 0; + if (chr->fd_in_tag[0]) { + io_remove_watch_poll(chr->fd_in_tag[0]); + chr->fd_in_tag[0] = 0; } } +static void remove_fd_in_watch_n(CharDriverState *chr, uint64_t id) +{ + if (chr->fd_in_tag[id]) { + io_remove_watch_poll(chr->fd_in_tag[id]); + chr->fd_in_tag[id] = 0; + } +} static int io_channel_send_full(QIOChannel *ioc, const void *buf, size_t len, @@ -1063,7 +1182,7 @@ static void fd_chr_update_read_handler(CharDriverState *chr) remove_fd_in_watch(chr); if (s->ioc_in) { - chr->fd_in_tag = io_add_watch_poll(s->ioc_in, + chr->fd_in_tag[0] = io_add_watch_poll(s->ioc_in, fd_chr_read_poll, fd_chr_read, chr); } @@ -1410,8 +1529,8 @@ static void pty_chr_state(CharDriverState *chr, int connected) s->connected = 1; s->open_tag = g_idle_add(qemu_chr_be_generic_open_func, chr); } - if (!chr->fd_in_tag) { - chr->fd_in_tag = io_add_watch_poll(s->ioc, + if (!chr->fd_in_tag[0]) { + chr->fd_in_tag[0] = io_add_watch_poll(s->ioc, pty_chr_read_poll, pty_chr_read, chr); } @@ -2558,7 +2677,7 @@ static void udp_chr_update_read_handler(CharDriverState *chr) remove_fd_in_watch(chr); if (s->ioc) { - chr->fd_in_tag = io_add_watch_poll(s->ioc, + chr->fd_in_tag[0] = io_add_watch_poll(s->ioc, udp_chr_read_poll, udp_chr_read, chr); } @@ -2605,20 +2724,21 @@ static CharDriverState *qemu_chr_open_udp(QIOChannelSocket *sioc, /* TCP Net console */ typedef struct { - QIOChannel *ioc; /* Client I/O channel */ - QIOChannelSocket *sioc; /* Client master channel */ + QIOChannel *ioc[MAX_CLIENTS]; /* Client I/O channels */ + QIOChannelSocket *sioc[MAX_CLIENTS]; /* Client master channels */ QIOChannelSocket *listen_ioc; guint listen_tag; QCryptoTLSCreds *tls_creds; - int connected; + int connected[MAX_CLIENTS]; int max_size; int do_telnetopt; int do_nodelay; int is_unix; - int *read_msgfds; - size_t read_msgfds_num; - int *write_msgfds; - size_t write_msgfds_num; + int *read_msgfds[MAX_CLIENTS]; + size_t read_msgfds_num[MAX_CLIENTS]; + int *write_msgfds[MAX_CLIENTS]; + size_t write_msgfds_num[MAX_CLIENTS]; + uint64_t connections; SocketAddress *addr; bool is_listen; @@ -2634,7 +2754,7 @@ static gboolean socket_reconnect_timeout(gpointer opaque); static void qemu_chr_socket_restart_timer(CharDriverState *chr) { TCPCharDriver *s = chr->opaque; - assert(s->connected == 0); + assert(s->connected[0] == 0); s->reconnect_timer = g_timeout_add_seconds(s->reconnect_time, socket_reconnect_timeout, chr); } @@ -2660,16 +2780,16 @@ static gboolean tcp_chr_accept(QIOChannel *chan, static int tcp_chr_write(CharDriverState *chr, const uint8_t *buf, int len) { TCPCharDriver *s = chr->opaque; - if (s->connected) { - int ret = io_channel_send_full(s->ioc, buf, len, - s->write_msgfds, - s->write_msgfds_num); + if (s->connected[0]) { + int ret = io_channel_send_full(s->ioc[0], buf, len, + s->write_msgfds[0], + s->write_msgfds_num[0]); /* free the written msgfds, no matter what */ - if (s->write_msgfds_num) { - g_free(s->write_msgfds); - s->write_msgfds = 0; - s->write_msgfds_num = 0; + if (s->write_msgfds_num[0]) { + g_free(s->write_msgfds[0]); + s->write_msgfds[0] = 0; + s->write_msgfds_num[0] = 0; } return ret; @@ -2679,11 +2799,41 @@ static int tcp_chr_write(CharDriverState *chr, const uint8_t *buf, int len) } } +/* Called with chr_write_lock held. */ +static int tcp_chr_write_n(CharDriverState *chr, uint64_t id, + const uint8_t *buf, int len) +{ + TCPCharDriver *s = chr->opaque; + if (s->connected[id]) { + int ret = io_channel_send_full(s->ioc[id], buf, len, + s->write_msgfds[id], + s->write_msgfds_num[id]); + + /* free the written msgfds, no matter what */ + if (s->write_msgfds_num[id]) { + g_free(s->write_msgfds[id]); + s->write_msgfds[id] = 0; + s->write_msgfds_num[id] = 0; + } + + return ret; + } else { + /* XXX: indicate an error ? */ + return len; + } +} + static int tcp_chr_read_poll(void *opaque) { CharDriverState *chr = opaque; TCPCharDriver *s = chr->opaque; - if (!s->connected) + uint64_t id; + + for (id = 0; id < s->connections; id++) { + if (s->connected[id]) + break; + } + if (id == s->connections) return 0; s->max_size = qemu_chr_be_can_write(chr); return s->max_size; @@ -2742,54 +2892,107 @@ static void tcp_chr_process_IAC_bytes(CharDriverState *chr, static int tcp_get_msgfds(CharDriverState *chr, int *fds, int num) { TCPCharDriver *s = chr->opaque; - int to_copy = (s->read_msgfds_num < num) ? s->read_msgfds_num : num; + int to_copy = (s->read_msgfds_num[0] < num) ? s->read_msgfds_num[0] : num; assert(num <= TCP_MAX_FDS); if (to_copy) { int i; - memcpy(fds, s->read_msgfds, to_copy * sizeof(int)); + memcpy(fds, s->read_msgfds[0], to_copy * sizeof(int)); /* Close unused fds */ - for (i = to_copy; i < s->read_msgfds_num; i++) { - close(s->read_msgfds[i]); + for (i = to_copy; i < s->read_msgfds_num[0]; i++) { + close(s->read_msgfds[0][i]); } - g_free(s->read_msgfds); - s->read_msgfds = 0; - s->read_msgfds_num = 0; + g_free(s->read_msgfds[0]); + s->read_msgfds[0] = 0; + s->read_msgfds_num[0] = 0; } return to_copy; } +static int tcp_get_msgfds_n(CharDriverState *chr, uint64_t id, + int *fds, int num) +{ + TCPCharDriver *s = chr->opaque; + int to_copy = (s->read_msgfds_num[id] < num) ? s->read_msgfds_num[id] : num; + + assert(num <= TCP_MAX_FDS); + + if (to_copy) { + int i; + + memcpy(fds, s->read_msgfds[id], to_copy * sizeof(int)); + + /* Close unused fds */ + for (i = to_copy; i < s->read_msgfds_num[id]; i++) { + close(s->read_msgfds[id][i]); + } + + g_free(s->read_msgfds[id]); + s->read_msgfds[id] = 0; + s->read_msgfds_num[id] = 0; + } + + return to_copy; +} + static int tcp_set_msgfds(CharDriverState *chr, int *fds, int num) { TCPCharDriver *s = chr->opaque; /* clear old pending fd array */ - g_free(s->write_msgfds); - s->write_msgfds = NULL; - s->write_msgfds_num = 0; + g_free(s->write_msgfds[0]); + s->write_msgfds[0] = NULL; + s->write_msgfds_num[0] = 0; - if (!s->connected || - !qio_channel_has_feature(s->ioc, + if (!s->connected[0] || + !qio_channel_has_feature(s->ioc[0], QIO_CHANNEL_FEATURE_FD_PASS)) { return -1; } if (num) { - s->write_msgfds = g_new(int, num); - memcpy(s->write_msgfds, fds, num * sizeof(int)); + s->write_msgfds[0] = g_new(int, num); + memcpy(s->write_msgfds[0], fds, num * sizeof(int)); } - s->write_msgfds_num = num; + s->write_msgfds_num[0] = num; return 0; } -static ssize_t tcp_chr_recv(CharDriverState *chr, char *buf, size_t len) +static int tcp_set_msgfds_n(CharDriverState *chr, uint64_t id, + int *fds, int num) +{ + TCPCharDriver *s = chr->opaque; + + /* clear old pending fd array */ + g_free(s->write_msgfds[id]); + s->write_msgfds[id] = NULL; + s->write_msgfds_num[id] = 0; + + if (!s->connected[id] || + !qio_channel_has_feature(s->ioc[id], + QIO_CHANNEL_FEATURE_FD_PASS)) { + return -1; + } + + if (num) { + s->write_msgfds[id] = g_new(int, num); + memcpy(s->write_msgfds[id], fds, num * sizeof(int)); + } + + s->write_msgfds_num[id] = num; + + return 0; +} + +static ssize_t tcp_chr_recv(CharDriverState *chr, uint64_t id, + char *buf, size_t len) { TCPCharDriver *s = chr->opaque; struct iovec iov = { .iov_base = buf, .iov_len = len }; @@ -2798,12 +3001,12 @@ static ssize_t tcp_chr_recv(CharDriverState *chr, char *buf, size_t len) int *msgfds = NULL; size_t msgfds_num = 0; - if (qio_channel_has_feature(s->ioc, QIO_CHANNEL_FEATURE_FD_PASS)) { - ret = qio_channel_readv_full(s->ioc, &iov, 1, + if (qio_channel_has_feature(s->ioc[id], QIO_CHANNEL_FEATURE_FD_PASS)) { + ret = qio_channel_readv_full(s->ioc[id], &iov, 1, &msgfds, &msgfds_num, NULL); } else { - ret = qio_channel_readv_full(s->ioc, &iov, 1, + ret = qio_channel_readv_full(s->ioc[id], &iov, 1, NULL, NULL, NULL); } @@ -2817,20 +3020,20 @@ static ssize_t tcp_chr_recv(CharDriverState *chr, char *buf, size_t len) if (msgfds_num) { /* close and clean read_msgfds */ - for (i = 0; i < s->read_msgfds_num; i++) { - close(s->read_msgfds[i]); + for (i = 0; i < s->read_msgfds_num[id]; i++) { + close(s->read_msgfds[id][i]); } - if (s->read_msgfds_num) { - g_free(s->read_msgfds); + if (s->read_msgfds_num[id]) { + g_free(s->read_msgfds[id]); } - s->read_msgfds = msgfds; - s->read_msgfds_num = msgfds_num; + s->read_msgfds[id] = msgfds; + s->read_msgfds_num[id] = msgfds_num; } - for (i = 0; i < s->read_msgfds_num; i++) { - int fd = s->read_msgfds[i]; + for (i = 0; i < s->read_msgfds_num[id]; i++) { + int fd = s->read_msgfds[id][i]; if (fd < 0) { continue; } @@ -2849,47 +3052,47 @@ static ssize_t tcp_chr_recv(CharDriverState *chr, char *buf, size_t len) static GSource *tcp_chr_add_watch(CharDriverState *chr, GIOCondition cond) { TCPCharDriver *s = chr->opaque; - return qio_channel_create_watch(s->ioc, cond); + return qio_channel_create_watch(s->ioc[0], cond); } -static void tcp_chr_free_connection(CharDriverState *chr) +static void tcp_chr_free_connection(CharDriverState *chr, uint64_t id) { TCPCharDriver *s = chr->opaque; int i; - if (!s->connected) { + if (!s->connected[id]) { return; } - if (s->read_msgfds_num) { - for (i = 0; i < s->read_msgfds_num; i++) { - close(s->read_msgfds[i]); + if (s->read_msgfds_num[id]) { + for (i = 0; i < s->read_msgfds_num[id]; i++) { + close(s->read_msgfds[id][i]); } - g_free(s->read_msgfds); - s->read_msgfds = NULL; - s->read_msgfds_num = 0; + g_free(s->read_msgfds[id]); + s->read_msgfds[id] = NULL; + s->read_msgfds_num[id] = 0; } - tcp_set_msgfds(chr, NULL, 0); - remove_fd_in_watch(chr); - object_unref(OBJECT(s->sioc)); - s->sioc = NULL; - object_unref(OBJECT(s->ioc)); - s->ioc = NULL; + tcp_set_msgfds_n(chr, id, NULL, 0); + remove_fd_in_watch_n(chr, id); + object_unref(OBJECT(s->sioc[id])); + s->sioc[id] = NULL; + object_unref(OBJECT(s->ioc[id])); + s->ioc[id] = NULL; g_free(chr->filename); chr->filename = NULL; - s->connected = 0; + s->connected[id] = 0; } -static void tcp_chr_disconnect(CharDriverState *chr) +static void tcp_chr_disconnect_n(CharDriverState *chr, uint64_t id) { TCPCharDriver *s = chr->opaque; - if (!s->connected) { + if (!s->connected[id]) { return; } - tcp_chr_free_connection(chr); + tcp_chr_free_connection(chr, id); if (s->listen_ioc) { s->listen_tag = qio_channel_add_watch( @@ -2903,23 +3106,34 @@ static void tcp_chr_disconnect(CharDriverState *chr) } } +static void tcp_chr_disconnect(CharDriverState *chr) +{ + tcp_chr_disconnect_n(chr, 0); +} + static gboolean tcp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque) { CharDriverState *chr = opaque; TCPCharDriver *s = chr->opaque; uint8_t buf[READ_BUF_LEN]; int len, size; + uint64_t id; - if (!s->connected || s->max_size <= 0) { + for (id = 0; id < s->connections; id++) { + if (s->ioc[id] == chan) + break; + } + + if ((id == s->connections) || !s->connected[id] || s->max_size <= 0) { return TRUE; } len = sizeof(buf); if (len > s->max_size) len = s->max_size; - size = tcp_chr_recv(chr, (void *)buf, len); + size = tcp_chr_recv(chr, id, (void *)buf, len); if (size == 0 || size == -1) { /* connection closed */ - tcp_chr_disconnect(chr); + tcp_chr_disconnect_n(chr, id); } else if (size > 0) { if (s->do_telnetopt) tcp_chr_process_IAC_bytes(chr, s, buf, &size); @@ -2935,33 +3149,52 @@ static int tcp_chr_sync_read(CharDriverState *chr, const uint8_t *buf, int len) TCPCharDriver *s = chr->opaque; int size; - if (!s->connected) { + if (!s->connected[0]) { + return 0; + } + + size = tcp_chr_recv(chr, 0, (void *) buf, len); + if (size == 0) { + /* connection closed */ + tcp_chr_disconnect_n(chr, 0); + } + + return size; +} + +static int tcp_chr_sync_read_n(CharDriverState *chr, uint64_t id, + const uint8_t *buf, int len) +{ + TCPCharDriver *s = chr->opaque; + int size; + + if (!s->connected[id]) { return 0; } - size = tcp_chr_recv(chr, (void *) buf, len); + size = tcp_chr_recv(chr, id, (void *) buf, len); if (size == 0) { /* connection closed */ - tcp_chr_disconnect(chr); + tcp_chr_disconnect_n(chr, id); } return size; } -static void tcp_chr_connect(void *opaque) +static void tcp_chr_connect(void *opaque, uint64_t id) { CharDriverState *chr = opaque; TCPCharDriver *s = chr->opaque; g_free(chr->filename); chr->filename = sockaddr_to_str( - &s->sioc->localAddr, s->sioc->localAddrLen, - &s->sioc->remoteAddr, s->sioc->remoteAddrLen, + &s->sioc[id]->localAddr, s->sioc[id]->localAddrLen, + &s->sioc[id]->remoteAddr, s->sioc[id]->remoteAddrLen, s->is_listen, s->is_telnet); - s->connected = 1; - if (s->ioc) { - chr->fd_in_tag = io_add_watch_poll(s->ioc, + s->connected[id] = 1; + if (s->ioc[id]) { + chr->fd_in_tag[id] = io_add_watch_poll(s->ioc[id], tcp_chr_read_poll, tcp_chr_read, chr); } @@ -2971,16 +3204,18 @@ static void tcp_chr_connect(void *opaque) static void tcp_chr_update_read_handler(CharDriverState *chr) { TCPCharDriver *s = chr->opaque; + uint64_t id; - if (!s->connected) { - return; - } + for (id = 0; id < s->connections; id++) { + if (!s->connected[id]) + continue; - remove_fd_in_watch(chr); - if (s->ioc) { - chr->fd_in_tag = io_add_watch_poll(s->ioc, - tcp_chr_read_poll, - tcp_chr_read, chr); + remove_fd_in_watch_n(chr, id); + if (s->ioc[id]) { + chr->fd_in_tag[id] = io_add_watch_poll(s->ioc[id], + tcp_chr_read_poll, + tcp_chr_read, chr); + } } } @@ -3002,14 +3237,14 @@ static gboolean tcp_chr_telnet_init_io(QIOChannel *ioc, if (ret == QIO_CHANNEL_ERR_BLOCK) { ret = 0; } else { - tcp_chr_disconnect(init->chr); + tcp_chr_disconnect_n(init->chr, 0); return FALSE; } } init->buflen -= ret; if (init->buflen == 0) { - tcp_chr_connect(init->chr); + tcp_chr_connect(init->chr, 0); return FALSE; } @@ -3018,7 +3253,7 @@ static gboolean tcp_chr_telnet_init_io(QIOChannel *ioc, return TRUE; } -static void tcp_chr_telnet_init(CharDriverState *chr) +static void tcp_chr_telnet_init(CharDriverState *chr, uint64_t id) { TCPCharDriver *s = chr->opaque; TCPCharDriverTelnetInit *init = @@ -3045,7 +3280,7 @@ static void tcp_chr_telnet_init(CharDriverState *chr) #undef IACSET qio_channel_add_watch( - s->ioc, G_IO_OUT, + s->ioc[id], G_IO_OUT, tcp_chr_telnet_init_io, init, NULL); } @@ -3059,18 +3294,18 @@ static void tcp_chr_tls_handshake(Object *source, TCPCharDriver *s = chr->opaque; if (err) { - tcp_chr_disconnect(chr); + tcp_chr_disconnect_n(chr, 0); } else { if (s->do_telnetopt) { - tcp_chr_telnet_init(chr); + tcp_chr_telnet_init(chr, 0); } else { - tcp_chr_connect(chr); + tcp_chr_connect(chr, 0); } } } -static void tcp_chr_tls_init(CharDriverState *chr) +static void tcp_chr_tls_init(CharDriverState *chr, uint64_t id) { TCPCharDriver *s = chr->opaque; QIOChannelTLS *tioc; @@ -3078,21 +3313,21 @@ static void tcp_chr_tls_init(CharDriverState *chr) if (s->is_listen) { tioc = qio_channel_tls_new_server( - s->ioc, s->tls_creds, + s->ioc[id], s->tls_creds, NULL, /* XXX Use an ACL */ &err); } else { tioc = qio_channel_tls_new_client( - s->ioc, s->tls_creds, + s->ioc[id], s->tls_creds, s->addr->u.inet.data->host, &err); } if (tioc == NULL) { error_free(err); - tcp_chr_disconnect(chr); + tcp_chr_disconnect_n(chr, id); } - object_unref(OBJECT(s->ioc)); - s->ioc = QIO_CHANNEL(tioc); + object_unref(OBJECT(s->ioc[id])); + s->ioc[id] = QIO_CHANNEL(tioc); qio_channel_tls_handshake(tioc, tcp_chr_tls_handshake, @@ -3100,36 +3335,52 @@ static void tcp_chr_tls_init(CharDriverState *chr) NULL); } +static int find_avail_ioc(TCPCharDriver *s, uint64_t *id) +{ + uint64_t i; + + for(i = 0; i < MAX_CLIENTS; i++) { + if (s->ioc[i] == NULL) { + *id = i; + return 0; + } + } + return -1; +} static int tcp_chr_new_client(CharDriverState *chr, QIOChannelSocket *sioc) { TCPCharDriver *s = chr->opaque; - if (s->ioc != NULL) { - return -1; - } + uint64_t id; - s->ioc = QIO_CHANNEL(sioc); + if(find_avail_ioc(s, &id) < 0) + return -1; + + s->ioc[id] = QIO_CHANNEL(sioc); object_ref(OBJECT(sioc)); - s->sioc = sioc; + s->sioc[id] = sioc; object_ref(OBJECT(sioc)); + if(chr->conn_bitmap != NULL) + set_bit(id, chr->conn_bitmap); - qio_channel_set_blocking(s->ioc, false, NULL); + qio_channel_set_blocking(s->ioc[id], false, NULL); if (s->do_nodelay) { - qio_channel_set_delay(s->ioc, false); + qio_channel_set_delay(s->ioc[id], false); } +/* if (s->listen_tag) { g_source_remove(s->listen_tag); s->listen_tag = 0; } - +*/ if (s->tls_creds) { - tcp_chr_tls_init(chr); + tcp_chr_tls_init(chr, id); } else { if (s->do_telnetopt) { - tcp_chr_telnet_init(chr); + tcp_chr_telnet_init(chr, id); } else { - tcp_chr_connect(chr); + tcp_chr_connect(chr, id); } } @@ -3178,7 +3429,7 @@ static int tcp_chr_wait_connected(CharDriverState *chr, Error **errp) /* It can't wait on s->connected, since it is set asynchronously * in TLS and telnet cases, only wait for an accepted socket */ - while (!s->ioc) { + while (!s->ioc[0]) { if (s->is_listen) { fprintf(stderr, "QEMU waiting for connection on: %s\n", chr->filename); @@ -3211,9 +3462,11 @@ int qemu_chr_wait_connected(CharDriverState *chr, Error **errp) static void tcp_chr_close(CharDriverState *chr) { TCPCharDriver *s = chr->opaque; + uint64_t id; - tcp_chr_free_connection(chr); - + for (id = 0; id < s->connections; id++) { + tcp_chr_free_connection(chr, id); + } if (s->reconnect_timer) { g_source_remove(s->reconnect_timer); s->reconnect_timer = 0; @@ -3721,6 +3974,7 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend, bool is_telnet = qemu_opt_get_bool(opts, "telnet", false); bool do_nodelay = !qemu_opt_get_bool(opts, "delay", true); int64_t reconnect = qemu_opt_get_number(opts, "reconnect", 0); + uint64_t connections = qemu_opt_get_number(opts, "connections", 1); const char *path = qemu_opt_get(opts, "path"); const char *host = qemu_opt_get(opts, "host"); const char *port = qemu_opt_get(opts, "port"); @@ -3758,6 +4012,8 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend, sock->has_reconnect = true; sock->reconnect = reconnect; sock->tls_creds = g_strdup(tls_creds); + sock->has_connections = true; + sock->connections = connections; addr = g_new0(SocketAddress, 1); if (path) { @@ -4241,6 +4497,9 @@ QemuOptsList qemu_chardev_opts = { },{ .name = "logappend", .type = QEMU_OPT_BOOL, + },{ + .name = "connections", + .type = QEMU_OPT_NUMBER, }, { /* end of list */ } }, @@ -4413,6 +4672,7 @@ static CharDriverState *qmp_chardev_open_socket(const char *id, bool is_telnet = sock->has_telnet ? sock->telnet : false; bool is_waitconnect = sock->has_wait ? sock->wait : false; int64_t reconnect = sock->has_reconnect ? sock->reconnect : 0; + uint64_t connections = sock->has_connections ? sock->connections : 1; ChardevCommon *common = qapi_ChardevSocket_base(sock); QIOChannelSocket *sioc = NULL; @@ -4426,6 +4686,7 @@ static CharDriverState *qmp_chardev_open_socket(const char *id, s->is_listen = is_listen; s->is_telnet = is_telnet; s->do_nodelay = do_nodelay; + s->connections = connections; if (sock->tls_creds) { Object *creds; creds = object_resolve_path_component( @@ -4461,6 +4722,15 @@ static CharDriverState *qmp_chardev_open_socket(const char *id, s->addr = QAPI_CLONE(SocketAddress, sock->addr); + if (sock->connections > 1) { + chr->conn_bitmap = bitmap_new(sock->connections); + chr->max_connections = sock->connections; + chr->chr_write_n = tcp_chr_write_n; + chr->chr_sync_read_n = tcp_chr_sync_read_n; + chr->get_msgfds_n = tcp_get_msgfds_n; + chr->set_msgfds_n = tcp_set_msgfds_n; + chr->chr_disconnect_n = tcp_chr_disconnect_n; + } chr->opaque = s; chr->chr_wait_connected = tcp_chr_wait_connected; chr->chr_write = tcp_chr_write; @@ -4478,10 +4748,12 @@ static CharDriverState *qmp_chardev_open_socket(const char *id, chr->filename = SocketAddress_to_str("disconnected:", addr, is_listen, is_telnet); + chr->conn_id = ANONYMOUS_CLIENT; if (is_listen) { if (is_telnet) { s->do_telnetopt = 1; } + chr->conn_id = 0; } else if (reconnect > 0) { s->reconnect_time = reconnect; } @@ -4502,11 +4774,9 @@ static CharDriverState *qmp_chardev_open_socket(const char *id, qemu_chr_wait_connected(chr, errp) < 0) { goto error; } - if (!s->ioc) { - s->listen_tag = qio_channel_add_watch( + s->listen_tag = qio_channel_add_watch( QIO_CHANNEL(s->listen_ioc), G_IO_IN, tcp_chr_accept, chr, NULL); - } } else if (qemu_chr_wait_connected(chr, errp) < 0) { goto error; } -- 2.7.4 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [Qemu-devel] [RFC PATCH 1/4] qemu-char: add the "1-server-N-client" support 2016-11-10 2:47 ` [Qemu-devel] [RFC PATCH 1/4] qemu-char: add the "1-server-N-client" support Wei Wang @ 2016-11-10 11:38 ` Marc-André Lureau 2016-11-11 8:28 ` Wei Wang 0 siblings, 1 reply; 9+ messages in thread From: Marc-André Lureau @ 2016-11-10 11:38 UTC (permalink / raw) To: Wei Wang, mst, stefanha, pbonzini, qemu-devel, virtio-dev Hi On Thu, Nov 10, 2016 at 6:47 AM Wei Wang <wei.w.wang@intel.com> wrote: > This patch enables a qemu server socket to be connected by multiple > client sockets. > > Thanks for sharing this early version of the series, I hope some early feedback will help you. I'll be waiting for a more complete implementation for detailed review. Is this patch necessary as a first step? I would rather start with a vhost-pci 1-1 Master-Slave series. Keep 1-n for a following improvement. This would also probably post-pone the discussion regarding connection-id, or uuid. In short, I think it would help if you can break your proposal in smaller independant steps. Signed-off-by: Wei Wang <wei.w.wang@intel.com> > --- > include/sysemu/char.h | 64 ++++++- > qapi-schema.json | 3 +- > qemu-char.c | 512 > ++++++++++++++++++++++++++++++++++++++------------ > 3 files changed, 456 insertions(+), 123 deletions(-) > > diff --git a/include/sysemu/char.h b/include/sysemu/char.h > index ee7e554..ff5dda6 100644 > --- a/include/sysemu/char.h > +++ b/include/sysemu/char.h > @@ -58,17 +58,24 @@ struct ParallelIOArg { > > typedef void IOEventHandler(void *opaque, int event); > > +#define MAX_CLIENTS 256 > +#define ANONYMOUS_CLIENT (~((uint64_t)0)) > 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_write_n)(struct CharDriverState *s, uint64_t id, const > uint8_t *buf, int len); > int (*chr_sync_read)(struct CharDriverState *s, > const uint8_t *buf, int len); > + int (*chr_sync_read_n)(struct CharDriverState *s, uint64_t id, > + const uint8_t *buf, int len); > GSource *(*chr_add_watch)(struct CharDriverState *s, GIOCondition > cond); > void (*chr_update_read_handler)(struct CharDriverState *s); > int (*chr_ioctl)(struct CharDriverState *s, int cmd, void *arg); > int (*get_msgfds)(struct CharDriverState *s, int* fds, int num); > + int (*get_msgfds_n)(struct CharDriverState *s, uint64_t id, int* fds, > int num); > int (*set_msgfds)(struct CharDriverState *s, int *fds, int num); > + int (*set_msgfds_n)(struct CharDriverState *s, uint64_t id, int *fds, > int num); > int (*chr_add_client)(struct CharDriverState *chr, int fd); > int (*chr_wait_connected)(struct CharDriverState *chr, Error **errp); > IOEventHandler *chr_event; > @@ -77,6 +84,7 @@ struct CharDriverState { > void *handler_opaque; > void (*chr_close)(struct CharDriverState *chr); > void (*chr_disconnect)(struct CharDriverState *chr); > + void (*chr_disconnect_n)(struct CharDriverState *chr, uint64_t id); > void (*chr_accept_input)(struct CharDriverState *chr); > void (*chr_set_echo)(struct CharDriverState *chr, bool echo); > void (*chr_set_fe_open)(struct CharDriverState *chr, int fe_open); > @@ -91,7 +99,10 @@ struct CharDriverState { > int explicit_be_open; > int avail_connections; > int is_mux; > - guint fd_in_tag; > + guint fd_in_tag[MAX_CLIENTS]; > + uint64_t max_connections; > + unsigned long *conn_bitmap; > + uint64_t conn_id; > QemuOpts *opts; > bool replay; > QTAILQ_ENTRY(CharDriverState) next; > @@ -281,6 +292,20 @@ int qemu_chr_fe_write(CharDriverState *s, const > uint8_t *buf, int len); > int qemu_chr_fe_write_all(CharDriverState *s, const uint8_t *buf, int > len); > > /** > + * @qemu_chr_fe_write_all_n: > + * > + * Write data to the selected character backend from the front end. > + * > + * @id the connection id of the character backend > + * @buf the data > + * @len the number of bytes to send > + * > + * Returns: the number of bytes consumed > + */ > +int qemu_chr_fe_write_all_n(CharDriverState *s, uint64_t id, > + const uint8_t *buf, int len); > + > +/** > * @qemu_chr_fe_read_all: > * > * Read data to a buffer from the back end. > @@ -293,6 +318,20 @@ int qemu_chr_fe_write_all(CharDriverState *s, const > uint8_t *buf, int len); > int qemu_chr_fe_read_all(CharDriverState *s, uint8_t *buf, int len); > > /** > + * @qemu_chr_fe_read_all_n: > + * > + * Read data to a buffer from the selected back end. > + * > + * @id the connection id > + * @buf the data buffer > + * @len the number of bytes to read > + * > + * Returns: the number of bytes read > + */ > +int qemu_chr_fe_read_all_n(CharDriverState *s, uint64_t id, > + uint8_t *buf, int len); > + > +/** > * @qemu_chr_fe_ioctl: > * > * Issue a device specific ioctl to a backend. This function is > thread-safe. > @@ -331,6 +370,19 @@ int qemu_chr_fe_get_msgfd(CharDriverState *s); > */ > int qemu_chr_fe_get_msgfds(CharDriverState *s, int *fds, int num); > > + > +/** > + * @qemu_chr_fe_get_msgfds_n: > + * > + * The multi-client version of @qemu_chr_fe_get_msgfds. > + * > + * Returns: -1 if fd passing isn't supported or there are no pending file > + * descriptors. If file descriptors are returned, subsequent > calls to > + * this function will return -1 until a client sends a new set > of file > + * descriptors. > + */ > +int qemu_chr_fe_get_msgfds_n(CharDriverState *s, uint64_t id, int *fds, > int num); > + > /** > * @qemu_chr_fe_set_msgfds: > * > @@ -345,6 +397,16 @@ int qemu_chr_fe_get_msgfds(CharDriverState *s, int > *fds, int num); > int qemu_chr_fe_set_msgfds(CharDriverState *s, int *fds, int num); > > /** > + * @qemu_chr_fe_set_msgfds_n: > + * > + * The multi-client version of @qemu_chr_fe_set_msgfds. > + * > + * Returns: -1 if fd passing isn't supported. > + */ > +int qemu_chr_fe_set_msgfds_n(CharDriverState *s, uint64_t id, int *fds, > int num); > + > + > +/** > * @qemu_chr_fe_claim: > * > * Claim a backend before using it, should be called before calling > diff --git a/qapi-schema.json b/qapi-schema.json > index 5658723..9bb5d7d 100644 > --- a/qapi-schema.json > +++ b/qapi-schema.json > @@ -3327,7 +3327,8 @@ > '*wait' : 'bool', > '*nodelay' : 'bool', > '*telnet' : 'bool', > - '*reconnect' : 'int' }, > + '*reconnect' : 'int' , > + '*connections' : 'uint64' }, > 'base': 'ChardevCommon' } > > ## > diff --git a/qemu-char.c b/qemu-char.c > index 5f82ebb..dfad6d1 100644 > --- a/qemu-char.c > +++ b/qemu-char.c > @@ -265,6 +265,35 @@ static int qemu_chr_fe_write_buffer(CharDriverState > *s, const uint8_t *buf, int > return res; > } > > +static int qemu_chr_fe_write_buffer_n(CharDriverState *s, uint64_t id, > + const uint8_t *buf, int len, int > *offset) > +{ > + int res = 0; > + *offset = 0; > + > + qemu_mutex_lock(&s->chr_write_lock); > + while (*offset < len) { > + retry: > + res = s->chr_write_n(s, id, buf + *offset, len - *offset); > + if (res < 0 && errno == EAGAIN) { > + g_usleep(100); > + goto retry; > + } > + > + if (res <= 0) { > + break; > + } > + > + *offset += res; > + } > + if (*offset > 0) { > + qemu_chr_fe_write_log(s, buf, *offset); > + } > + qemu_mutex_unlock(&s->chr_write_lock); > + > + return res; > +} > + > int qemu_chr_fe_write(CharDriverState *s, const uint8_t *buf, int len) > { > int ret; > @@ -317,6 +346,31 @@ int qemu_chr_fe_write_all(CharDriverState *s, const > uint8_t *buf, int len) > return offset; > } > > +int qemu_chr_fe_write_all_n(CharDriverState *s, uint64_t id, > + const uint8_t *buf, int len) > +{ > + int offset; > + int res; > + > + if (s->replay && replay_mode == REPLAY_MODE_PLAY) { > + replay_char_write_event_load(&res, &offset); > + assert(offset <= len); > + qemu_chr_fe_write_buffer_n(s, id, buf, offset, &offset); > + return res; > + } > + > + res = qemu_chr_fe_write_buffer_n(s, id, buf, len, &offset); > + > + if (s->replay && replay_mode == REPLAY_MODE_RECORD) { > + replay_char_write_event_save(res, offset); > + } > + > + if (res < 0) { > + return res; > + } > + return offset; > +} > + > int qemu_chr_fe_read_all(CharDriverState *s, uint8_t *buf, int len) > { > int offset = 0, counter = 10; > @@ -325,7 +379,7 @@ int qemu_chr_fe_read_all(CharDriverState *s, uint8_t > *buf, int len) > if (!s->chr_sync_read) { > return 0; > } > - > + > if (s->replay && replay_mode == REPLAY_MODE_PLAY) { > return replay_char_read_all_load(buf); > } > @@ -362,6 +416,52 @@ int qemu_chr_fe_read_all(CharDriverState *s, uint8_t > *buf, int len) > return offset; > } > > +int qemu_chr_fe_read_all_n(CharDriverState *s, uint64_t id, > + uint8_t *buf, int len) > +{ > + int offset = 0, counter = 10; > + int res; > + > + if (!s->chr_sync_read_n) { > + return 0; > + } > + > + if (s->replay && replay_mode == REPLAY_MODE_PLAY) { > + return replay_char_read_all_load(buf); > + } > + > + while (offset < len) { > + retry: > + res = s->chr_sync_read_n(s, id, buf + offset, len - offset); > + if (res == -1 && errno == EAGAIN) { > + g_usleep(100); > + goto retry; > + } > + > + if (res == 0) { > + break; > + } > + > + if (res < 0) { > + if (s->replay && replay_mode == REPLAY_MODE_RECORD) { > + replay_char_read_all_save_error(res); > + } > + return res; > + } > + > + offset += res; > + > + if (!counter--) { > + break; > + } > + } > + > + if (s->replay && replay_mode == REPLAY_MODE_RECORD) { > + replay_char_read_all_save_buf(buf, offset); > + } > + return offset; > +} > + > int qemu_chr_fe_ioctl(CharDriverState *s, int cmd, void *arg) > { > int res; > @@ -417,11 +517,23 @@ int qemu_chr_fe_get_msgfds(CharDriverState *s, int > *fds, int len) > return s->get_msgfds ? s->get_msgfds(s, fds, len) : -1; > } > > +int qemu_chr_fe_get_msgfds_n(CharDriverState *s, > + uint64_t id, int *fds, int len) > +{ > + return s->get_msgfds_n ? s->get_msgfds_n(s, id, fds, len) : -1; > +} > + > int qemu_chr_fe_set_msgfds(CharDriverState *s, int *fds, int num) > { > return s->set_msgfds ? s->set_msgfds(s, fds, num) : -1; > } > > +int qemu_chr_fe_set_msgfds_n(CharDriverState *s, > + uint64_t id, int *fds, int num) > +{ > + return s->set_msgfds_n ? s->set_msgfds_n(s, id, fds, num) : -1; > +} > + > int qemu_chr_add_client(CharDriverState *s, int fd) > { > return s->chr_add_client ? s->chr_add_client(s, fd) : -1; > @@ -951,12 +1063,19 @@ static void io_remove_watch_poll(guint tag) > > static void remove_fd_in_watch(CharDriverState *chr) > { > - if (chr->fd_in_tag) { > - io_remove_watch_poll(chr->fd_in_tag); > - chr->fd_in_tag = 0; > + if (chr->fd_in_tag[0]) { > + io_remove_watch_poll(chr->fd_in_tag[0]); > + chr->fd_in_tag[0] = 0; > } > } > > +static void remove_fd_in_watch_n(CharDriverState *chr, uint64_t id) > +{ > + if (chr->fd_in_tag[id]) { > + io_remove_watch_poll(chr->fd_in_tag[id]); > + chr->fd_in_tag[id] = 0; > + } > +} > > static int io_channel_send_full(QIOChannel *ioc, > const void *buf, size_t len, > @@ -1063,7 +1182,7 @@ static void > fd_chr_update_read_handler(CharDriverState *chr) > > remove_fd_in_watch(chr); > if (s->ioc_in) { > - chr->fd_in_tag = io_add_watch_poll(s->ioc_in, > + chr->fd_in_tag[0] = io_add_watch_poll(s->ioc_in, > fd_chr_read_poll, > fd_chr_read, chr); > } > @@ -1410,8 +1529,8 @@ static void pty_chr_state(CharDriverState *chr, int > connected) > s->connected = 1; > s->open_tag = g_idle_add(qemu_chr_be_generic_open_func, chr); > } > - if (!chr->fd_in_tag) { > - chr->fd_in_tag = io_add_watch_poll(s->ioc, > + if (!chr->fd_in_tag[0]) { > + chr->fd_in_tag[0] = io_add_watch_poll(s->ioc, > pty_chr_read_poll, > pty_chr_read, chr); > } > @@ -2558,7 +2677,7 @@ static void > udp_chr_update_read_handler(CharDriverState *chr) > > remove_fd_in_watch(chr); > if (s->ioc) { > - chr->fd_in_tag = io_add_watch_poll(s->ioc, > + chr->fd_in_tag[0] = io_add_watch_poll(s->ioc, > udp_chr_read_poll, > udp_chr_read, chr); > } > @@ -2605,20 +2724,21 @@ static CharDriverState > *qemu_chr_open_udp(QIOChannelSocket *sioc, > /* TCP Net console */ > > typedef struct { > - QIOChannel *ioc; /* Client I/O channel */ > - QIOChannelSocket *sioc; /* Client master channel */ > + QIOChannel *ioc[MAX_CLIENTS]; /* Client I/O channels */ > + QIOChannelSocket *sioc[MAX_CLIENTS]; /* Client master channels */ > QIOChannelSocket *listen_ioc; > guint listen_tag; > QCryptoTLSCreds *tls_creds; > - int connected; > + int connected[MAX_CLIENTS]; > int max_size; > int do_telnetopt; > int do_nodelay; > int is_unix; > - int *read_msgfds; > - size_t read_msgfds_num; > - int *write_msgfds; > - size_t write_msgfds_num; > + int *read_msgfds[MAX_CLIENTS]; > + size_t read_msgfds_num[MAX_CLIENTS]; > + int *write_msgfds[MAX_CLIENTS]; > + size_t write_msgfds_num[MAX_CLIENTS]; > + uint64_t connections; > > SocketAddress *addr; > bool is_listen; > @@ -2634,7 +2754,7 @@ static gboolean socket_reconnect_timeout(gpointer > opaque); > static void qemu_chr_socket_restart_timer(CharDriverState *chr) > { > TCPCharDriver *s = chr->opaque; > - assert(s->connected == 0); > + assert(s->connected[0] == 0); > s->reconnect_timer = g_timeout_add_seconds(s->reconnect_time, > socket_reconnect_timeout, > chr); > } > @@ -2660,16 +2780,16 @@ static gboolean tcp_chr_accept(QIOChannel *chan, > static int tcp_chr_write(CharDriverState *chr, const uint8_t *buf, int > len) > { > TCPCharDriver *s = chr->opaque; > - if (s->connected) { > - int ret = io_channel_send_full(s->ioc, buf, len, > - s->write_msgfds, > - s->write_msgfds_num); > + if (s->connected[0]) { > + int ret = io_channel_send_full(s->ioc[0], buf, len, > + s->write_msgfds[0], > + s->write_msgfds_num[0]); > > /* free the written msgfds, no matter what */ > - if (s->write_msgfds_num) { > - g_free(s->write_msgfds); > - s->write_msgfds = 0; > - s->write_msgfds_num = 0; > + if (s->write_msgfds_num[0]) { > + g_free(s->write_msgfds[0]); > + s->write_msgfds[0] = 0; > + s->write_msgfds_num[0] = 0; > } > > return ret; > @@ -2679,11 +2799,41 @@ static int tcp_chr_write(CharDriverState *chr, > const uint8_t *buf, int len) > } > } > > +/* Called with chr_write_lock held. */ > +static int tcp_chr_write_n(CharDriverState *chr, uint64_t id, > + const uint8_t *buf, int len) > +{ > + TCPCharDriver *s = chr->opaque; > + if (s->connected[id]) { > + int ret = io_channel_send_full(s->ioc[id], buf, len, > + s->write_msgfds[id], > + s->write_msgfds_num[id]); > + > + /* free the written msgfds, no matter what */ > + if (s->write_msgfds_num[id]) { > + g_free(s->write_msgfds[id]); > + s->write_msgfds[id] = 0; > + s->write_msgfds_num[id] = 0; > + } > + > + return ret; > + } else { > + /* XXX: indicate an error ? */ > + return len; > + } > +} > + > static int tcp_chr_read_poll(void *opaque) > { > CharDriverState *chr = opaque; > TCPCharDriver *s = chr->opaque; > - if (!s->connected) > + uint64_t id; > + > + for (id = 0; id < s->connections; id++) { > + if (s->connected[id]) > + break; > + } > + if (id == s->connections) > return 0; > s->max_size = qemu_chr_be_can_write(chr); > return s->max_size; > @@ -2742,54 +2892,107 @@ static void > tcp_chr_process_IAC_bytes(CharDriverState *chr, > static int tcp_get_msgfds(CharDriverState *chr, int *fds, int num) > { > TCPCharDriver *s = chr->opaque; > - int to_copy = (s->read_msgfds_num < num) ? s->read_msgfds_num : num; > + int to_copy = (s->read_msgfds_num[0] < num) ? s->read_msgfds_num[0] : > num; > > assert(num <= TCP_MAX_FDS); > > if (to_copy) { > int i; > > - memcpy(fds, s->read_msgfds, to_copy * sizeof(int)); > + memcpy(fds, s->read_msgfds[0], to_copy * sizeof(int)); > > /* Close unused fds */ > - for (i = to_copy; i < s->read_msgfds_num; i++) { > - close(s->read_msgfds[i]); > + for (i = to_copy; i < s->read_msgfds_num[0]; i++) { > + close(s->read_msgfds[0][i]); > } > > - g_free(s->read_msgfds); > - s->read_msgfds = 0; > - s->read_msgfds_num = 0; > + g_free(s->read_msgfds[0]); > + s->read_msgfds[0] = 0; > + s->read_msgfds_num[0] = 0; > } > > return to_copy; > } > > +static int tcp_get_msgfds_n(CharDriverState *chr, uint64_t id, > + int *fds, int num) > +{ > + TCPCharDriver *s = chr->opaque; > + int to_copy = (s->read_msgfds_num[id] < num) ? s->read_msgfds_num[id] > : num; > + > + assert(num <= TCP_MAX_FDS); > + > + if (to_copy) { > + int i; > + > + memcpy(fds, s->read_msgfds[id], to_copy * sizeof(int)); > + > + /* Close unused fds */ > + for (i = to_copy; i < s->read_msgfds_num[id]; i++) { > + close(s->read_msgfds[id][i]); > + } > + > + g_free(s->read_msgfds[id]); > + s->read_msgfds[id] = 0; > + s->read_msgfds_num[id] = 0; > + } > + > + return to_copy; > +} > + > static int tcp_set_msgfds(CharDriverState *chr, int *fds, int num) > { > TCPCharDriver *s = chr->opaque; > > /* clear old pending fd array */ > - g_free(s->write_msgfds); > - s->write_msgfds = NULL; > - s->write_msgfds_num = 0; > + g_free(s->write_msgfds[0]); > + s->write_msgfds[0] = NULL; > + s->write_msgfds_num[0] = 0; > > - if (!s->connected || > - !qio_channel_has_feature(s->ioc, > + if (!s->connected[0] || > + !qio_channel_has_feature(s->ioc[0], > QIO_CHANNEL_FEATURE_FD_PASS)) { > return -1; > } > > if (num) { > - s->write_msgfds = g_new(int, num); > - memcpy(s->write_msgfds, fds, num * sizeof(int)); > + s->write_msgfds[0] = g_new(int, num); > + memcpy(s->write_msgfds[0], fds, num * sizeof(int)); > } > > - s->write_msgfds_num = num; > + s->write_msgfds_num[0] = num; > > return 0; > } > > -static ssize_t tcp_chr_recv(CharDriverState *chr, char *buf, size_t len) > +static int tcp_set_msgfds_n(CharDriverState *chr, uint64_t id, > + int *fds, int num) > +{ > + TCPCharDriver *s = chr->opaque; > + > + /* clear old pending fd array */ > + g_free(s->write_msgfds[id]); > + s->write_msgfds[id] = NULL; > + s->write_msgfds_num[id] = 0; > + > + if (!s->connected[id] || > + !qio_channel_has_feature(s->ioc[id], > + QIO_CHANNEL_FEATURE_FD_PASS)) { > + return -1; > + } > + > + if (num) { > + s->write_msgfds[id] = g_new(int, num); > + memcpy(s->write_msgfds[id], fds, num * sizeof(int)); > + } > + > + s->write_msgfds_num[id] = num; > + > + return 0; > +} > + > +static ssize_t tcp_chr_recv(CharDriverState *chr, uint64_t id, > + char *buf, size_t len) > { > TCPCharDriver *s = chr->opaque; > struct iovec iov = { .iov_base = buf, .iov_len = len }; > @@ -2798,12 +3001,12 @@ static ssize_t tcp_chr_recv(CharDriverState *chr, > char *buf, size_t len) > int *msgfds = NULL; > size_t msgfds_num = 0; > > - if (qio_channel_has_feature(s->ioc, QIO_CHANNEL_FEATURE_FD_PASS)) { > - ret = qio_channel_readv_full(s->ioc, &iov, 1, > + if (qio_channel_has_feature(s->ioc[id], QIO_CHANNEL_FEATURE_FD_PASS)) > { > + ret = qio_channel_readv_full(s->ioc[id], &iov, 1, > &msgfds, &msgfds_num, > NULL); > } else { > - ret = qio_channel_readv_full(s->ioc, &iov, 1, > + ret = qio_channel_readv_full(s->ioc[id], &iov, 1, > NULL, NULL, > NULL); > } > @@ -2817,20 +3020,20 @@ static ssize_t tcp_chr_recv(CharDriverState *chr, > char *buf, size_t len) > > if (msgfds_num) { > /* close and clean read_msgfds */ > - for (i = 0; i < s->read_msgfds_num; i++) { > - close(s->read_msgfds[i]); > + for (i = 0; i < s->read_msgfds_num[id]; i++) { > + close(s->read_msgfds[id][i]); > } > > - if (s->read_msgfds_num) { > - g_free(s->read_msgfds); > + if (s->read_msgfds_num[id]) { > + g_free(s->read_msgfds[id]); > } > > - s->read_msgfds = msgfds; > - s->read_msgfds_num = msgfds_num; > + s->read_msgfds[id] = msgfds; > + s->read_msgfds_num[id] = msgfds_num; > } > > - for (i = 0; i < s->read_msgfds_num; i++) { > - int fd = s->read_msgfds[i]; > + for (i = 0; i < s->read_msgfds_num[id]; i++) { > + int fd = s->read_msgfds[id][i]; > if (fd < 0) { > continue; > } > @@ -2849,47 +3052,47 @@ static ssize_t tcp_chr_recv(CharDriverState *chr, > char *buf, size_t len) > static GSource *tcp_chr_add_watch(CharDriverState *chr, GIOCondition cond) > { > TCPCharDriver *s = chr->opaque; > - return qio_channel_create_watch(s->ioc, cond); > + return qio_channel_create_watch(s->ioc[0], cond); > } > > -static void tcp_chr_free_connection(CharDriverState *chr) > +static void tcp_chr_free_connection(CharDriverState *chr, uint64_t id) > { > TCPCharDriver *s = chr->opaque; > int i; > > - if (!s->connected) { > + if (!s->connected[id]) { > return; > } > > - if (s->read_msgfds_num) { > - for (i = 0; i < s->read_msgfds_num; i++) { > - close(s->read_msgfds[i]); > + if (s->read_msgfds_num[id]) { > + for (i = 0; i < s->read_msgfds_num[id]; i++) { > + close(s->read_msgfds[id][i]); > } > - g_free(s->read_msgfds); > - s->read_msgfds = NULL; > - s->read_msgfds_num = 0; > + g_free(s->read_msgfds[id]); > + s->read_msgfds[id] = NULL; > + s->read_msgfds_num[id] = 0; > } > > - tcp_set_msgfds(chr, NULL, 0); > - remove_fd_in_watch(chr); > - object_unref(OBJECT(s->sioc)); > - s->sioc = NULL; > - object_unref(OBJECT(s->ioc)); > - s->ioc = NULL; > + tcp_set_msgfds_n(chr, id, NULL, 0); > + remove_fd_in_watch_n(chr, id); > + object_unref(OBJECT(s->sioc[id])); > + s->sioc[id] = NULL; > + object_unref(OBJECT(s->ioc[id])); > + s->ioc[id] = NULL; > g_free(chr->filename); > chr->filename = NULL; > - s->connected = 0; > + s->connected[id] = 0; > } > > -static void tcp_chr_disconnect(CharDriverState *chr) > +static void tcp_chr_disconnect_n(CharDriverState *chr, uint64_t id) > { > TCPCharDriver *s = chr->opaque; > > - if (!s->connected) { > + if (!s->connected[id]) { > return; > } > > - tcp_chr_free_connection(chr); > + tcp_chr_free_connection(chr, id); > > if (s->listen_ioc) { > s->listen_tag = qio_channel_add_watch( > @@ -2903,23 +3106,34 @@ static void tcp_chr_disconnect(CharDriverState > *chr) > } > } > > +static void tcp_chr_disconnect(CharDriverState *chr) > +{ > + tcp_chr_disconnect_n(chr, 0); > +} > + > static gboolean tcp_chr_read(QIOChannel *chan, GIOCondition cond, void > *opaque) > { > CharDriverState *chr = opaque; > TCPCharDriver *s = chr->opaque; > uint8_t buf[READ_BUF_LEN]; > int len, size; > + uint64_t id; > > - if (!s->connected || s->max_size <= 0) { > + for (id = 0; id < s->connections; id++) { > + if (s->ioc[id] == chan) > + break; > + } > + > + if ((id == s->connections) || !s->connected[id] || s->max_size <= 0) { > return TRUE; > } > len = sizeof(buf); > if (len > s->max_size) > len = s->max_size; > - size = tcp_chr_recv(chr, (void *)buf, len); > + size = tcp_chr_recv(chr, id, (void *)buf, len); > if (size == 0 || size == -1) { > /* connection closed */ > - tcp_chr_disconnect(chr); > + tcp_chr_disconnect_n(chr, id); > } else if (size > 0) { > if (s->do_telnetopt) > tcp_chr_process_IAC_bytes(chr, s, buf, &size); > @@ -2935,33 +3149,52 @@ static int tcp_chr_sync_read(CharDriverState *chr, > const uint8_t *buf, int len) > TCPCharDriver *s = chr->opaque; > int size; > > - if (!s->connected) { > + if (!s->connected[0]) { > + return 0; > + } > + > + size = tcp_chr_recv(chr, 0, (void *) buf, len); > + if (size == 0) { > + /* connection closed */ > + tcp_chr_disconnect_n(chr, 0); > + } > + > + return size; > +} > + > +static int tcp_chr_sync_read_n(CharDriverState *chr, uint64_t id, > + const uint8_t *buf, int len) > +{ > + TCPCharDriver *s = chr->opaque; > + int size; > + > + if (!s->connected[id]) { > return 0; > } > > - size = tcp_chr_recv(chr, (void *) buf, len); > + size = tcp_chr_recv(chr, id, (void *) buf, len); > if (size == 0) { > /* connection closed */ > - tcp_chr_disconnect(chr); > + tcp_chr_disconnect_n(chr, id); > } > > return size; > } > > -static void tcp_chr_connect(void *opaque) > +static void tcp_chr_connect(void *opaque, uint64_t id) > { > CharDriverState *chr = opaque; > TCPCharDriver *s = chr->opaque; > > g_free(chr->filename); > chr->filename = sockaddr_to_str( > - &s->sioc->localAddr, s->sioc->localAddrLen, > - &s->sioc->remoteAddr, s->sioc->remoteAddrLen, > + &s->sioc[id]->localAddr, s->sioc[id]->localAddrLen, > + &s->sioc[id]->remoteAddr, s->sioc[id]->remoteAddrLen, > s->is_listen, s->is_telnet); > > - s->connected = 1; > - if (s->ioc) { > - chr->fd_in_tag = io_add_watch_poll(s->ioc, > + s->connected[id] = 1; > + if (s->ioc[id]) { > + chr->fd_in_tag[id] = io_add_watch_poll(s->ioc[id], > tcp_chr_read_poll, > tcp_chr_read, chr); > } > @@ -2971,16 +3204,18 @@ static void tcp_chr_connect(void *opaque) > static void tcp_chr_update_read_handler(CharDriverState *chr) > { > TCPCharDriver *s = chr->opaque; > + uint64_t id; > > - if (!s->connected) { > - return; > - } > + for (id = 0; id < s->connections; id++) { > + if (!s->connected[id]) > + continue; > > - remove_fd_in_watch(chr); > - if (s->ioc) { > - chr->fd_in_tag = io_add_watch_poll(s->ioc, > - tcp_chr_read_poll, > - tcp_chr_read, chr); > + remove_fd_in_watch_n(chr, id); > + if (s->ioc[id]) { > + chr->fd_in_tag[id] = io_add_watch_poll(s->ioc[id], > + tcp_chr_read_poll, > + tcp_chr_read, chr); > + } > } > } > > @@ -3002,14 +3237,14 @@ static gboolean tcp_chr_telnet_init_io(QIOChannel > *ioc, > if (ret == QIO_CHANNEL_ERR_BLOCK) { > ret = 0; > } else { > - tcp_chr_disconnect(init->chr); > + tcp_chr_disconnect_n(init->chr, 0); > return FALSE; > } > } > init->buflen -= ret; > > if (init->buflen == 0) { > - tcp_chr_connect(init->chr); > + tcp_chr_connect(init->chr, 0); > return FALSE; > } > > @@ -3018,7 +3253,7 @@ static gboolean tcp_chr_telnet_init_io(QIOChannel > *ioc, > return TRUE; > } > > -static void tcp_chr_telnet_init(CharDriverState *chr) > +static void tcp_chr_telnet_init(CharDriverState *chr, uint64_t id) > { > TCPCharDriver *s = chr->opaque; > TCPCharDriverTelnetInit *init = > @@ -3045,7 +3280,7 @@ static void tcp_chr_telnet_init(CharDriverState *chr) > #undef IACSET > > qio_channel_add_watch( > - s->ioc, G_IO_OUT, > + s->ioc[id], G_IO_OUT, > tcp_chr_telnet_init_io, > init, NULL); > } > @@ -3059,18 +3294,18 @@ static void tcp_chr_tls_handshake(Object *source, > TCPCharDriver *s = chr->opaque; > > if (err) { > - tcp_chr_disconnect(chr); > + tcp_chr_disconnect_n(chr, 0); > } else { > if (s->do_telnetopt) { > - tcp_chr_telnet_init(chr); > + tcp_chr_telnet_init(chr, 0); > } else { > - tcp_chr_connect(chr); > + tcp_chr_connect(chr, 0); > } > } > } > > > -static void tcp_chr_tls_init(CharDriverState *chr) > +static void tcp_chr_tls_init(CharDriverState *chr, uint64_t id) > { > TCPCharDriver *s = chr->opaque; > QIOChannelTLS *tioc; > @@ -3078,21 +3313,21 @@ static void tcp_chr_tls_init(CharDriverState *chr) > > if (s->is_listen) { > tioc = qio_channel_tls_new_server( > - s->ioc, s->tls_creds, > + s->ioc[id], s->tls_creds, > NULL, /* XXX Use an ACL */ > &err); > } else { > tioc = qio_channel_tls_new_client( > - s->ioc, s->tls_creds, > + s->ioc[id], s->tls_creds, > s->addr->u.inet.data->host, > &err); > } > if (tioc == NULL) { > error_free(err); > - tcp_chr_disconnect(chr); > + tcp_chr_disconnect_n(chr, id); > } > - object_unref(OBJECT(s->ioc)); > - s->ioc = QIO_CHANNEL(tioc); > + object_unref(OBJECT(s->ioc[id])); > + s->ioc[id] = QIO_CHANNEL(tioc); > > qio_channel_tls_handshake(tioc, > tcp_chr_tls_handshake, > @@ -3100,36 +3335,52 @@ static void tcp_chr_tls_init(CharDriverState *chr) > NULL); > } > > +static int find_avail_ioc(TCPCharDriver *s, uint64_t *id) > +{ > + uint64_t i; > + > + for(i = 0; i < MAX_CLIENTS; i++) { > + if (s->ioc[i] == NULL) { > + *id = i; > + return 0; > + } > + } > + return -1; > +} > > static int tcp_chr_new_client(CharDriverState *chr, QIOChannelSocket > *sioc) > { > TCPCharDriver *s = chr->opaque; > - if (s->ioc != NULL) { > - return -1; > - } > + uint64_t id; > > - s->ioc = QIO_CHANNEL(sioc); > + if(find_avail_ioc(s, &id) < 0) > + return -1; > + > + s->ioc[id] = QIO_CHANNEL(sioc); > object_ref(OBJECT(sioc)); > - s->sioc = sioc; > + s->sioc[id] = sioc; > object_ref(OBJECT(sioc)); > + if(chr->conn_bitmap != NULL) > + set_bit(id, chr->conn_bitmap); > > - qio_channel_set_blocking(s->ioc, false, NULL); > + qio_channel_set_blocking(s->ioc[id], false, NULL); > > if (s->do_nodelay) { > - qio_channel_set_delay(s->ioc, false); > + qio_channel_set_delay(s->ioc[id], false); > } > +/* > if (s->listen_tag) { > g_source_remove(s->listen_tag); > s->listen_tag = 0; > } > - > +*/ > if (s->tls_creds) { > - tcp_chr_tls_init(chr); > + tcp_chr_tls_init(chr, id); > } else { > if (s->do_telnetopt) { > - tcp_chr_telnet_init(chr); > + tcp_chr_telnet_init(chr, id); > } else { > - tcp_chr_connect(chr); > + tcp_chr_connect(chr, id); > } > } > > @@ -3178,7 +3429,7 @@ static int tcp_chr_wait_connected(CharDriverState > *chr, Error **errp) > > /* It can't wait on s->connected, since it is set asynchronously > * in TLS and telnet cases, only wait for an accepted socket */ > - while (!s->ioc) { > + while (!s->ioc[0]) { > if (s->is_listen) { > fprintf(stderr, "QEMU waiting for connection on: %s\n", > chr->filename); > @@ -3211,9 +3462,11 @@ int qemu_chr_wait_connected(CharDriverState *chr, > Error **errp) > static void tcp_chr_close(CharDriverState *chr) > { > TCPCharDriver *s = chr->opaque; > + uint64_t id; > > - tcp_chr_free_connection(chr); > - > + for (id = 0; id < s->connections; id++) { > + tcp_chr_free_connection(chr, id); > + } > if (s->reconnect_timer) { > g_source_remove(s->reconnect_timer); > s->reconnect_timer = 0; > @@ -3721,6 +3974,7 @@ static void qemu_chr_parse_socket(QemuOpts *opts, > ChardevBackend *backend, > bool is_telnet = qemu_opt_get_bool(opts, "telnet", false); > bool do_nodelay = !qemu_opt_get_bool(opts, "delay", true); > int64_t reconnect = qemu_opt_get_number(opts, "reconnect", 0); > + uint64_t connections = qemu_opt_get_number(opts, "connections", 1); > const char *path = qemu_opt_get(opts, "path"); > const char *host = qemu_opt_get(opts, "host"); > const char *port = qemu_opt_get(opts, "port"); > @@ -3758,6 +4012,8 @@ static void qemu_chr_parse_socket(QemuOpts *opts, > ChardevBackend *backend, > sock->has_reconnect = true; > sock->reconnect = reconnect; > sock->tls_creds = g_strdup(tls_creds); > + sock->has_connections = true; > + sock->connections = connections; > > addr = g_new0(SocketAddress, 1); > if (path) { > @@ -4241,6 +4497,9 @@ QemuOptsList qemu_chardev_opts = { > },{ > .name = "logappend", > .type = QEMU_OPT_BOOL, > + },{ > + .name = "connections", > + .type = QEMU_OPT_NUMBER, > }, > { /* end of list */ } > }, > @@ -4413,6 +4672,7 @@ static CharDriverState > *qmp_chardev_open_socket(const char *id, > bool is_telnet = sock->has_telnet ? sock->telnet : false; > bool is_waitconnect = sock->has_wait ? sock->wait : false; > int64_t reconnect = sock->has_reconnect ? sock->reconnect : 0; > + uint64_t connections = sock->has_connections ? sock->connections : 1; > ChardevCommon *common = qapi_ChardevSocket_base(sock); > QIOChannelSocket *sioc = NULL; > > @@ -4426,6 +4686,7 @@ static CharDriverState > *qmp_chardev_open_socket(const char *id, > s->is_listen = is_listen; > s->is_telnet = is_telnet; > s->do_nodelay = do_nodelay; > + s->connections = connections; > if (sock->tls_creds) { > Object *creds; > creds = object_resolve_path_component( > @@ -4461,6 +4722,15 @@ static CharDriverState > *qmp_chardev_open_socket(const char *id, > > s->addr = QAPI_CLONE(SocketAddress, sock->addr); > > + if (sock->connections > 1) { > + chr->conn_bitmap = bitmap_new(sock->connections); > + chr->max_connections = sock->connections; > + chr->chr_write_n = tcp_chr_write_n; > + chr->chr_sync_read_n = tcp_chr_sync_read_n; > + chr->get_msgfds_n = tcp_get_msgfds_n; > + chr->set_msgfds_n = tcp_set_msgfds_n; > + chr->chr_disconnect_n = tcp_chr_disconnect_n; > + } > chr->opaque = s; > chr->chr_wait_connected = tcp_chr_wait_connected; > chr->chr_write = tcp_chr_write; > @@ -4478,10 +4748,12 @@ static CharDriverState > *qmp_chardev_open_socket(const char *id, > chr->filename = SocketAddress_to_str("disconnected:", > addr, is_listen, is_telnet); > > + chr->conn_id = ANONYMOUS_CLIENT; > if (is_listen) { > if (is_telnet) { > s->do_telnetopt = 1; > } > + chr->conn_id = 0; > } else if (reconnect > 0) { > s->reconnect_time = reconnect; > } > @@ -4502,11 +4774,9 @@ static CharDriverState > *qmp_chardev_open_socket(const char *id, > qemu_chr_wait_connected(chr, errp) < 0) { > goto error; > } > - if (!s->ioc) { > - s->listen_tag = qio_channel_add_watch( > + s->listen_tag = qio_channel_add_watch( > QIO_CHANNEL(s->listen_ioc), G_IO_IN, > tcp_chr_accept, chr, NULL); > - } > } else if (qemu_chr_wait_connected(chr, errp) < 0) { > goto error; > } > -- > 2.7.4 > > -- Marc-André Lureau ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [Qemu-devel] [RFC PATCH 1/4] qemu-char: add the "1-server-N-client" support 2016-11-10 11:38 ` Marc-André Lureau @ 2016-11-11 8:28 ` Wei Wang 0 siblings, 0 replies; 9+ messages in thread From: Wei Wang @ 2016-11-11 8:28 UTC (permalink / raw) To: Marc-André Lureau; +Cc: mst, stefanha, pbonzini, qemu-devel, virtio-dev On 11/10/2016 07:38 PM, Marc-André Lureau wrote: > Hi > > On Thu, Nov 10, 2016 at 6:47 AM Wei Wang <wei.w.wang@intel.com > <mailto:wei.w.wang@intel.com>> wrote: > > This patch enables a qemu server socket to be connected by multiple > client sockets. > > Thanks for sharing this early version of the series, I hope some early > feedback will help you. I'll be waiting for a more complete > implementation for detailed review. > > Is this patch necessary as a first step? I would rather start with a > vhost-pci 1-1 Master-Slave series. Keep 1-n for a following > improvement. This would also probably post-pone the discussion > regarding connection-id, or uuid. > > In short, I think it would help if you can break your proposal in > smaller independant steps. > OK, we can leave this QEMU socket patch to the 2nd step. So, I think we can have a two-step plan: Step1: 1-1 QEMU socket based vhost-pci-net design Each QEMU socket manages (create/hotplug/destruction) only 1 vhost-pci-net device. To create more vhost-pci-net devices, the slave VM needs to have more server sockets created at booting time. Step2: 1-server-N-client based vhost-pci design A single QEMU server socket manages all the vhost-pci devices (may also in different device types). Best, Wei ^ permalink raw reply [flat|nested] 9+ messages in thread
* [Qemu-devel] [RFC PATCH 2/4] vhost-user: add the vhost-user extension to support the vhost-pci based inter-vm communication 2016-11-10 2:47 [Qemu-devel] [RFC PATCH 0/4] *** Half-cooked vhost-pci implementation patch *** Wei Wang 2016-11-10 2:47 ` [Qemu-devel] [RFC PATCH 1/4] qemu-char: add the "1-server-N-client" support Wei Wang @ 2016-11-10 2:47 ` Wei Wang 2016-11-10 11:36 ` Marc-André Lureau 2016-11-10 2:47 ` [Qemu-devel] [RFC PATCH 3/4] vhost-pci-net device support Wei Wang 2016-11-10 2:47 ` [Qemu-devel] [RFC PATCH 4/4] vhost-user: extend the vhost-user Master (client) part to support vhost-pci Wei Wang 3 siblings, 1 reply; 9+ messages in thread From: Wei Wang @ 2016-11-10 2:47 UTC (permalink / raw) To: marcandre.lureau, mst, stefanha, pbonzini, qemu-devel, virtio-dev Cc: Wei Wang This is the slave part of vhost-user implemented in QEMU, with an extension to support vhost-pci. Signed-off-by: Wei Wang <wei.w.wang@intel.com> --- hw/virtio/Makefile.objs | 1 + hw/virtio/vhost-pci-server.c | 469 +++++++++++++++++++++++++++++++++++ hw/virtio/vhost-user.c | 86 +------ include/hw/virtio/vhost-pci-server.h | 45 ++++ include/hw/virtio/vhost-user.h | 110 ++++++++ include/sysemu/sysemu.h | 1 + qemu-options.hx | 4 + vl.c | 26 ++ 8 files changed, 657 insertions(+), 85 deletions(-) create mode 100644 hw/virtio/vhost-pci-server.c create mode 100644 include/hw/virtio/vhost-pci-server.h create mode 100644 include/hw/virtio/vhost-user.h diff --git a/hw/virtio/Makefile.objs b/hw/virtio/Makefile.objs index 3e2b175..e44feb8 100644 --- a/hw/virtio/Makefile.objs +++ b/hw/virtio/Makefile.objs @@ -2,6 +2,7 @@ common-obj-y += virtio-rng.o common-obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o common-obj-y += virtio-bus.o common-obj-y += virtio-mmio.o +common-obj-y += vhost-pci-server.o obj-y += virtio.o virtio-balloon.o obj-$(CONFIG_LINUX) += vhost.o vhost-backend.o vhost-user.o diff --git a/hw/virtio/vhost-pci-server.c b/hw/virtio/vhost-pci-server.c new file mode 100644 index 0000000..6ce8516 --- /dev/null +++ b/hw/virtio/vhost-pci-server.c @@ -0,0 +1,469 @@ +/* + * Vhost-pci server + * + * Copyright Intel Corp. 2016 + * + * Authors: + * Wei Wang <wei.w.wang@intel.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include <qemu/osdep.h> +#include <qemu/thread.h> +#include <qemu/main-loop.h> +#include <qemu/bitops.h> +#include <qemu/bitmap.h> +#include <qemu/sockets.h> +#include <linux/virtio_net.h> +#include "sysemu/char.h" +#include "qapi/error.h" +#include "hw/virtio/vhost-pci-server.h" +#include "qemu/option.h" +#include "monitor/qdev.h" +#include "hw/virtio/vhost-user.h" +#include "hw/qdev.h" + +#define VHOST_PCI_FEATURE_BITS (1ULL << VIRTIO_F_VERSION_1) + +#define VHOST_PCI_NET_FEATURE_BITS (1ULL << VIRTIO_NET_F_MRG_RXBUF) | \ + (1ULL << VIRTIO_NET_F_CTRL_VQ) | \ + (1ULL << VIRTIO_NET_F_MQ) + +#define VHOST_USER_SET_PEER_CONNECTION_OFF 0 +#define VHOST_USER_SET_PEER_CONNECTION_ON 1 +#define VHOST_USER_SET_PEER_CONNECTION_INIT 2 + +VhostPCIServer *vp_server; + +QemuOptsList qemu_vhost_pci_server_opts = { + .name = "vhost-pci-server", + .implied_opt_name = "chardev", + .head = QTAILQ_HEAD_INITIALIZER(qemu_vhost_pci_server_opts.head), + .desc = { + /* + * no elements => accept any + * sanity checking will happen later + * when setting device properties + */ + { /* end of list */ } + }, +}; + +static int vhost_pci_server_write(CharDriverState *chr, VhostUserMsg *msg) +{ + int size = msg->size + VHOST_USER_HDR_SIZE; + + if (!msg) + return 0; + + msg->flags &= ~VHOST_USER_VERSION_MASK; + msg->flags |= VHOST_USER_VERSION; + + return qemu_chr_fe_write_all_n(chr, msg->conn_id, + (const uint8_t *)msg, size) == size ? 0 : -1; +} + +PeerConnectionTable *vp_server_find_table_ent(const char *dev_id) +{ + int i; + PeerConnectionTable *ent; + uint64_t max_connections = vp_server->chr->max_connections; + + for (i = 0; i < max_connections; i++) { + ent = &vp_server->peer_table[i]; + if (!strcmp(dev_id, ent->dev_id)) + return ent; + } + return NULL; +} + +static void vhost_pci_init_peer_table(uint64_t id) +{ + PeerConnectionTable *ent = &vp_server->peer_table[id]; + + ent->peer_feature_bits |= 1ULL << VHOST_USER_F_PROTOCOL_FEATURES; + QLIST_INIT(&ent->vq_list); + ent->vq_num = 0; +} + +static int vhost_pci_get_conn_id(CharDriverState *chr, VhostUserMsg *msg) +{ + unsigned long *conn_bitmap = chr->conn_bitmap; + unsigned long *old_conn_bitmap = vp_server->old_conn_bitmap; + uint64_t nbits = chr->max_connections; + uint64_t id; + int r; + + bitmap_xor(old_conn_bitmap, old_conn_bitmap, conn_bitmap, (long)nbits); + + for (id = find_first_bit(old_conn_bitmap, nbits); id < nbits; + id = find_next_bit(old_conn_bitmap, nbits, id + 1)) { + vhost_pci_init_peer_table(id); + msg->conn_id = id; + msg->payload.u64 = id; + msg->size = sizeof(msg->payload.u64); + msg->flags |= VHOST_USER_REPLY_MASK; + r = vhost_pci_server_write(chr, msg); + } + bitmap_copy(old_conn_bitmap, conn_bitmap, (long)nbits); + + return r; +} + +static int vhost_pci_get_peer_features(CharDriverState *chr, VhostUserMsg *msg) +{ + PeerConnectionTable *ent = &vp_server->peer_table[msg->conn_id]; + msg->payload.u64 = ent->peer_feature_bits; + msg->size = sizeof(msg->payload.u64); + msg->flags |= VHOST_USER_REPLY_MASK; + return vhost_pci_server_write(chr, msg); +} + +static int vhost_pci_get_queue_num(CharDriverState *chr, VhostUserMsg *msg) +{ + PeerConnectionTable *ent = &vp_server->peer_table[msg->conn_id]; + switch (ent->virtio_id) { + case VIRTIO_ID_NET: + msg->payload.u64 = VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX; + break; + default: + printf("%s: device type not supported yet..\n", __func__); + } + msg->size = sizeof(msg->payload.u64); + msg->flags |= VHOST_USER_REPLY_MASK; + return vhost_pci_server_write(chr, msg); +} + +static int vhost_pci_get_protocol_features(CharDriverState *chr, VhostUserMsg *msg) +{ + msg->payload.u64 = VHOST_USER_PROTOCOL_FEATURES; + msg->size = sizeof(msg->payload.u64); + msg->flags |= VHOST_USER_REPLY_MASK; + return vhost_pci_server_write(chr, msg); +} + +static void vhost_pci_set_protocol_features(VhostUserMsg *msg) +{ + vp_server->protocol_features = msg->payload.u64; +} + +static int vhost_pci_device_create(uint64_t conn_id) +{ + PeerConnectionTable *ent = &vp_server->peer_table[conn_id]; + Error *local_err = NULL; + QemuOpts *opts; + DeviceState *dev; + char params[50]; + + switch (ent->virtio_id) { + case VIRTIO_ID_NET: + sprintf(params, "driver=vhost-pci-net-pci,id=vhost-pci-%ld", conn_id); + sprintf(ent->dev_id, "vhost-pci-%ld", conn_id); + break; + default: + printf("%s: device type not supported yet..\n", __func__); + } + + opts = qemu_opts_parse_noisily(qemu_find_opts("device"), params, true); + dev = qdev_device_add(opts, &local_err); + if (!dev) { + qemu_opts_del(opts); + return -1; + } + object_unref(OBJECT(dev)); + return 0; +} + +static void vhost_pci_set_device_info(VhostUserMsg *msg) +{ + PeerConnectionTable *ent = &vp_server->peer_table[msg->conn_id]; + DeviceInfo *info = &msg->payload.dev_info; + + memcpy(ent->uuid, info->uuid, sizeof(uuid_t)); + ent->virtio_id = info->virtio_id; + switch (ent->virtio_id) { + case VIRTIO_ID_NET: + ent->peer_feature_bits |= (VHOST_PCI_FEATURE_BITS | VHOST_PCI_NET_FEATURE_BITS); + break; + default: + printf("%s: device type not supported yet..\n", __func__); + } +} + +static void vhost_pci_set_peer_feature_bits(VhostUserMsg *msg) +{ + PeerConnectionTable *ent = &vp_server->peer_table[msg->conn_id]; + + ent->peer_feature_bits = msg->payload.u64; +} + +static uint64_t vhost_pci_peer_mem_size_get(VhostUserMemory *peer_mem) +{ + int i; + uint64_t total_size; + uint32_t nregions = peer_mem->nregions; + VhostUserMemoryRegion *regions = peer_mem->regions; + + for (i = 0; i < nregions; i++) { + total_size += regions[i].memory_size; + } + + return total_size; +} + +static int vhost_pci_set_mem_table(uint64_t conn_id, VhostUserMemory *peer_mem, int *fds) +{ + int i; + void *mr_qva; + PeerConnectionTable *ent = &vp_server->peer_table[conn_id]; + uint32_t nregions = peer_mem->nregions; + VhostUserMemoryRegion *peer_mr = peer_mem->regions; + MemoryRegion *bar_mr = g_malloc(sizeof(MemoryRegion)); + MemoryRegion *mr = g_malloc(nregions * sizeof(MemoryRegion)); + uint64_t bar_size = 2 * vhost_pci_peer_mem_size_get(peer_mem); + uint64_t bar_map_offset = 0; + + bar_size = pow2ceil(bar_size); + memory_region_init(bar_mr, NULL, "Peer Memory", bar_size); + + for (i = 0; i < nregions; i++) { + mr_qva = mmap(NULL, peer_mr[i].memory_size + peer_mr[i].mmap_offset, + PROT_READ | PROT_READ, MAP_SHARED, fds[i], 0); + if (mr_qva == MAP_FAILED) { + printf("%s called: map failed \n", __func__); + return -1; + } + mr_qva += peer_mr[i].mmap_offset; + memory_region_init_ram_ptr(&mr[i], NULL, "Peer Memory", peer_mr[i].memory_size, mr_qva); + memory_region_add_subregion(bar_mr, bar_map_offset, &mr[i]); + bar_map_offset += peer_mr[i].memory_size; + } + ent->bar_mr = bar_mr; + ent->bar_map_offset = bar_map_offset; + + return 0; +} + +static void vhost_pci_alloc_peer_vring_info(uint64_t conn_id) +{ + PeerConnectionTable *ent = &vp_server->peer_table[conn_id]; + PeerVirtqInfo *virtq_info = g_malloc0(sizeof(PeerVirtqInfo)); + QLIST_INSERT_HEAD(&ent->vq_list, virtq_info, node); + ent->vq_num++; +} + +static void vhost_pci_set_vring_num(VhostUserMsg *msg) +{ + PeerConnectionTable *ent = &vp_server->peer_table[msg->conn_id]; + PeerVirtqInfo *virtq_info = QLIST_FIRST(&ent->vq_list); + + virtq_info->vring_num = msg->payload.u64; +} + +static void vhost_pci_set_vring_base(VhostUserMsg *msg) +{ + PeerConnectionTable *ent = &vp_server->peer_table[msg->conn_id]; + PeerVirtqInfo *virtq_info = QLIST_FIRST(&ent->vq_list); + + virtq_info->last_avail_idx = msg->payload.u64; +} + +static void vhost_pci_set_vring_addr(VhostUserMsg *msg) +{ + PeerConnectionTable *ent = &vp_server->peer_table[msg->conn_id]; + PeerVirtqInfo *virtq_info = QLIST_FIRST(&ent->vq_list); + memcpy(&virtq_info->addr, &msg->payload.addr, + sizeof(struct vhost_vring_addr)); +} + +static void vhost_pci_set_vring_kick(uint64_t conn_id, int fd) +{ + PeerConnectionTable *ent = &vp_server->peer_table[conn_id]; + PeerVirtqInfo *virtq_info = QLIST_FIRST(&ent->vq_list); + if (!virtq_info) + virtq_info->kickfd = fd; +} + +static void vhost_pci_set_vring_call(uint64_t conn_id, int fd) +{ + PeerConnectionTable *ent = &vp_server->peer_table[conn_id]; + PeerVirtqInfo *virtq_info = QLIST_FIRST(&ent->vq_list); + if (virtq_info) + virtq_info->callfd = fd; +} + +static void vhost_pci_set_peer_connection(VhostUserMsg *msg) +{ + uint64_t cmd = msg->payload.u64; + uint64_t conn_id = msg->conn_id; + + switch (cmd) { + case VHOST_USER_SET_PEER_CONNECTION_INIT: + vhost_pci_device_create(conn_id); + break; + default: + printf("%s called: cmd %lu not supported yet \n", __func__, cmd); + } +} + +static void vhost_pci_server_read(void *opaque, const uint8_t *buf, int size) +{ + VhostUserMsg msg; + uint8_t *p = (uint8_t *) &msg; + CharDriverState *chr = (CharDriverState *)opaque; + int fds[8], fd_num; + + if (size != VHOST_USER_HDR_SIZE) { + printf("Wrong message size received %d\n", size); + return; + } + memcpy(p, buf, VHOST_USER_HDR_SIZE); + + if (msg.size) { + p += VHOST_USER_HDR_SIZE; + size = qemu_chr_fe_read_all_n(chr, msg.conn_id, p, msg.size); + if (size != msg.size) { + printf("Wrong message size received %d != %d\n", + size, msg.size); + return; + } + } + + if (msg.request > VHOST_USER_MAX) + printf("vhost read incorrect msg \n"); + + switch(msg.request) { + case VHOST_USER_GET_CONN_ID: + vhost_pci_get_conn_id(chr, &msg); + break; + case VHOST_USER_GET_FEATURES: + vhost_pci_get_peer_features(chr, &msg); + break; + case VHOST_USER_GET_PROTOCOL_FEATURES: + vhost_pci_get_protocol_features(chr, &msg); + break; + case VHOST_USER_SET_PROTOCOL_FEATURES: + vhost_pci_set_protocol_features(&msg); + break; + case VHOST_USER_SET_DEV_INFO: + vhost_pci_set_device_info(&msg); + break; + case VHOST_USER_GET_QUEUE_NUM: + vhost_pci_get_queue_num(chr, &msg); + break; + case VHOST_USER_SET_OWNER: + break; + case VHOST_USER_SET_FEATURES: + vhost_pci_set_peer_feature_bits(&msg); + break; + case VHOST_USER_SET_VRING_NUM: + vhost_pci_alloc_peer_vring_info(msg.conn_id); + vhost_pci_set_vring_num(&msg); + break; + case VHOST_USER_SET_VRING_BASE: + vhost_pci_set_vring_base(&msg); + break; + case VHOST_USER_SET_VRING_ADDR: + vhost_pci_set_vring_addr(&msg); + break; + case VHOST_USER_SET_VRING_KICK: + /* consume the fd */ + qemu_chr_fe_get_msgfds_n(chr, msg.conn_id, fds, 1); + printf("VHOST_USER_SET_VRING_KICK called:..kickfd = %d\n", fds[0]); + vhost_pci_set_vring_kick(msg.conn_id, fds[0]); + /* + * This is a non-blocking eventfd. + * The receive function forces it to be blocking, + * so revert it back to non-blocking. + */ + qemu_set_nonblock(fds[0]); + break; + case VHOST_USER_SET_VRING_CALL: + /* consume the fd */ + qemu_chr_fe_get_msgfds_n(chr, msg.conn_id, fds, 1); + vhost_pci_set_vring_call(msg.conn_id, fds[0]); + /* + * This is a non-blocking eventfd. + * The receive function forces it to be blocking, + * so revert it back to non-blocking. + */ + qemu_set_nonblock(fds[0]); + break; + case VHOST_USER_SET_MEM_TABLE: + fd_num = qemu_chr_fe_get_msgfds_n(chr, msg.conn_id, + fds, sizeof(fds) / sizeof(int)); + printf("VHOST_USER_SET_MEM_TABLE: fd = %d \n", fd_num); + vhost_pci_set_mem_table(msg.conn_id, &msg.payload.memory, fds); + break; + case VHOST_USER_SET_PEER_CONNECTION: + vhost_pci_set_peer_connection(&msg); + break; + default: + printf("default called..msg->request = %d \n", msg.request); + break; + } +} + +static int vhost_pci_server_can_read(void *opaque) +{ + return VHOST_USER_HDR_SIZE; +} + +static void vhost_pci_server_event(void *opaque, int event) +{ + switch (event) { + case CHR_EVENT_OPENED: + printf("vhost_pci_server_event called.. \n"); + break; + case CHR_EVENT_CLOSED: + printf("vhost_pci_server_event called: event close..\n"); + break; + } +} + +static CharDriverState *vhost_pci_server_parse_chardev(const char *id) +{ + CharDriverState *chr = qemu_chr_find(id); + if (chr == NULL) { + printf("chardev \"%s\" not found", id); + return NULL; + } + + qemu_chr_fe_claim_no_fail(chr); + + return chr; +} + +int vhost_pci_server_init(QemuOpts *opts) +{ + CharDriverState *chr; + const char *chardev_id = qemu_opt_get(opts, "chardev"); + uint64_t max_connections; + + vp_server = (VhostPCIServer *)malloc(sizeof(VhostPCIServer)); + + chr = vhost_pci_server_parse_chardev(chardev_id); + if (!chr) { + return -1; + } + max_connections = chr->max_connections; + + qemu_chr_add_handlers(chr, vhost_pci_server_can_read, vhost_pci_server_read, vhost_pci_server_event, chr); + + vp_server->chr = chr; + + vp_server->peer_table = (PeerConnectionTable *)g_malloc0(max_connections * sizeof(PeerConnectionTable)); + + vp_server->old_conn_bitmap = bitmap_new(max_connections); + + return 0; +} + +int vhost_pci_server_cleanup(void) +{ + free(vp_server); + printf("vhost_pci_server_cleanup called.. \n"); + return 0; +} diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index b57454a..bce5181 100644 --- a/hw/virtio/vhost-user.c +++ b/hw/virtio/vhost-user.c @@ -13,6 +13,7 @@ #include "hw/virtio/vhost.h" #include "hw/virtio/vhost-backend.h" #include "hw/virtio/virtio-net.h" +#include "hw/virtio/vhost-user.h" #include "sysemu/char.h" #include "sysemu/kvm.h" #include "qemu/error-report.h" @@ -24,91 +25,6 @@ #include <sys/un.h> #include <linux/vhost.h> -#define VHOST_MEMORY_MAX_NREGIONS 8 -#define VHOST_USER_F_PROTOCOL_FEATURES 30 - -enum VhostUserProtocolFeature { - VHOST_USER_PROTOCOL_F_MQ = 0, - VHOST_USER_PROTOCOL_F_LOG_SHMFD = 1, - VHOST_USER_PROTOCOL_F_RARP = 2, - VHOST_USER_PROTOCOL_F_REPLY_ACK = 3, - - VHOST_USER_PROTOCOL_F_MAX -}; - -#define VHOST_USER_PROTOCOL_FEATURE_MASK ((1 << VHOST_USER_PROTOCOL_F_MAX) - 1) - -typedef enum VhostUserRequest { - VHOST_USER_NONE = 0, - VHOST_USER_GET_FEATURES = 1, - VHOST_USER_SET_FEATURES = 2, - VHOST_USER_SET_OWNER = 3, - VHOST_USER_RESET_OWNER = 4, - VHOST_USER_SET_MEM_TABLE = 5, - VHOST_USER_SET_LOG_BASE = 6, - VHOST_USER_SET_LOG_FD = 7, - VHOST_USER_SET_VRING_NUM = 8, - VHOST_USER_SET_VRING_ADDR = 9, - VHOST_USER_SET_VRING_BASE = 10, - VHOST_USER_GET_VRING_BASE = 11, - VHOST_USER_SET_VRING_KICK = 12, - VHOST_USER_SET_VRING_CALL = 13, - VHOST_USER_SET_VRING_ERR = 14, - VHOST_USER_GET_PROTOCOL_FEATURES = 15, - VHOST_USER_SET_PROTOCOL_FEATURES = 16, - VHOST_USER_GET_QUEUE_NUM = 17, - VHOST_USER_SET_VRING_ENABLE = 18, - VHOST_USER_SEND_RARP = 19, - VHOST_USER_MAX -} VhostUserRequest; - -typedef struct VhostUserMemoryRegion { - uint64_t guest_phys_addr; - uint64_t memory_size; - uint64_t userspace_addr; - uint64_t mmap_offset; -} VhostUserMemoryRegion; - -typedef struct VhostUserMemory { - uint32_t nregions; - uint32_t padding; - VhostUserMemoryRegion regions[VHOST_MEMORY_MAX_NREGIONS]; -} VhostUserMemory; - -typedef struct VhostUserLog { - uint64_t mmap_size; - uint64_t mmap_offset; -} VhostUserLog; - -typedef struct VhostUserMsg { - VhostUserRequest request; - -#define VHOST_USER_VERSION_MASK (0x3) -#define VHOST_USER_REPLY_MASK (0x1<<2) -#define VHOST_USER_NEED_REPLY_MASK (0x1 << 3) - uint32_t flags; - uint32_t size; /* the following payload size */ - union { -#define VHOST_USER_VRING_IDX_MASK (0xff) -#define VHOST_USER_VRING_NOFD_MASK (0x1<<8) - uint64_t u64; - struct vhost_vring_state state; - struct vhost_vring_addr addr; - VhostUserMemory memory; - VhostUserLog log; - } payload; -} QEMU_PACKED VhostUserMsg; - -static VhostUserMsg m __attribute__ ((unused)); -#define VHOST_USER_HDR_SIZE (sizeof(m.request) \ - + sizeof(m.flags) \ - + sizeof(m.size)) - -#define VHOST_USER_PAYLOAD_SIZE (sizeof(m) - VHOST_USER_HDR_SIZE) - -/* The version of the protocol we support */ -#define VHOST_USER_VERSION (0x1) - static bool ioeventfd_enabled(void) { return kvm_enabled() && kvm_eventfds_enabled(); diff --git a/include/hw/virtio/vhost-pci-server.h b/include/hw/virtio/vhost-pci-server.h new file mode 100644 index 0000000..c9c4a69 --- /dev/null +++ b/include/hw/virtio/vhost-pci-server.h @@ -0,0 +1,45 @@ +#ifndef QEMU_VHOST_PCI_SERVER_H +#define QEMU_VHOST_PCI_SERVER_H + +#include <uuid/uuid.h> +#include <linux/vhost.h> + +typedef struct PeerVirtqInfo { + int kickfd; + int callfd; + uint32_t vring_num; + uint16_t last_avail_idx; + struct vhost_vring_addr addr; + QLIST_ENTRY(PeerVirtqInfo) node; +} PeerVirtqInfo; + +typedef struct PeerConnectionTable { + char dev_id[30]; + uuid_t uuid; + uint16_t virtio_id; + uint32_t bar_id; + MemoryRegion *bar_mr; + uint64_t bar_map_offset; + uint64_t peer_feature_bits; + void *opaque; + uint16_t vq_num; + QLIST_HEAD(, PeerVirtqInfo) vq_list; +} PeerConnectionTable; + +typedef struct VhostPCIServer { + CharDriverState *chr; + uint64_t protocol_features; + unsigned long *old_conn_bitmap; + /* a table indexed by the peer connection id */ + PeerConnectionTable *peer_table; +} VhostPCIServer; + +extern VhostPCIServer *vp_server; + +extern int vhost_pci_server_init(QemuOpts *opts); + +extern int vhost_pci_server_cleanup(void); + +extern PeerConnectionTable *vp_server_find_table_ent(const char *dev_id); + +#endif diff --git a/include/hw/virtio/vhost-user.h b/include/hw/virtio/vhost-user.h new file mode 100644 index 0000000..794a8d8 --- /dev/null +++ b/include/hw/virtio/vhost-user.h @@ -0,0 +1,110 @@ +#ifndef VHOST_USER_H +#define VHOST_USER_H + +#include <linux/vhost.h> +#include <uuid/uuid.h> + +#define VHOST_MEMORY_MAX_NREGIONS 8 +#define VHOST_USER_F_PROTOCOL_FEATURES 30 + +enum VhostUserProtocolFeature { + VHOST_USER_PROTOCOL_F_MQ = 0, + VHOST_USER_PROTOCOL_F_LOG_SHMFD = 1, + VHOST_USER_PROTOCOL_F_RARP = 2, + VHOST_USER_PROTOCOL_F_REPLY_ACK = 3, + VHOST_USER_PROTOCOL_F_VHOST_PCI =4, + + VHOST_USER_PROTOCOL_F_MAX +}; + +#define VHOST_USER_PROTOCOL_FEATURE_MASK ((1 << VHOST_USER_PROTOCOL_F_MAX) - 1) + +#define VHOST_USER_PROTOCOL_FEATURES ((1ULL << VHOST_USER_PROTOCOL_F_MQ) | \ + (1ULL << VHOST_USER_PROTOCOL_F_LOG_SHMFD) | \ + (1ULL << VHOST_USER_PROTOCOL_F_RARP)) | \ + (1ULL << VHOST_USER_PROTOCOL_F_VHOST_PCI) + +typedef enum VhostUserRequest { + VHOST_USER_NONE = 0, + VHOST_USER_GET_FEATURES = 1, + VHOST_USER_SET_FEATURES = 2, + VHOST_USER_SET_OWNER = 3, + VHOST_USER_RESET_OWNER = 4, + VHOST_USER_SET_MEM_TABLE = 5, + VHOST_USER_SET_LOG_BASE = 6, + VHOST_USER_SET_LOG_FD = 7, + VHOST_USER_SET_VRING_NUM = 8, + VHOST_USER_SET_VRING_ADDR = 9, + VHOST_USER_SET_VRING_BASE = 10, + VHOST_USER_GET_VRING_BASE = 11, + VHOST_USER_SET_VRING_KICK = 12, + VHOST_USER_SET_VRING_CALL = 13, + VHOST_USER_SET_VRING_ERR = 14, + VHOST_USER_GET_PROTOCOL_FEATURES = 15, + VHOST_USER_SET_PROTOCOL_FEATURES = 16, + VHOST_USER_GET_QUEUE_NUM = 17, + VHOST_USER_SET_VRING_ENABLE = 18, + VHOST_USER_SEND_RARP = 19, + VHOST_USER_GET_CONN_ID = 20, + VHOST_USER_SET_DEV_INFO = 21, + VHOST_USER_SET_PEER_CONNECTION = 22, + VHOST_USER_MAX +} VhostUserRequest; + +typedef struct VhostUserMemoryRegion { + uint64_t guest_phys_addr; + uint64_t memory_size; + uint64_t userspace_addr; + uint64_t mmap_offset; +} VhostUserMemoryRegion; + +typedef struct VhostUserMemory { + uint32_t nregions; + uint32_t padding; + VhostUserMemoryRegion regions[VHOST_MEMORY_MAX_NREGIONS]; +} VhostUserMemory; + +typedef struct VhostUserLog { + uint64_t mmap_size; + uint64_t mmap_offset; +} VhostUserLog; + +typedef struct DeviceInfo { + uuid_t uuid; + uint16_t virtio_id; +} DeviceInfo; + +typedef struct VhostUserMsg { + VhostUserRequest request; + +#define VHOST_USER_VERSION_MASK (0x3) +#define VHOST_USER_REPLY_MASK (0x1<<2) +#define VHOST_USER_NEED_REPLY_MASK (0x1 << 3) + uint32_t flags; + uint32_t size; /* the following payload size */ + uint64_t conn_id; + union { +#define VHOST_USER_VRING_IDX_MASK (0xff) +#define VHOST_USER_VRING_NOFD_MASK (0x1<<8) + uint64_t u64; + struct vhost_vring_state state; + struct vhost_vring_addr addr; + VhostUserMemory memory; + VhostUserLog log; + DeviceInfo dev_info; + } payload; +} QEMU_PACKED VhostUserMsg; + +static VhostUserMsg m __attribute__ ((unused)); +#define VHOST_USER_HDR_SIZE (sizeof(m.request) \ + + sizeof(m.flags) \ + + sizeof(m.size)) \ + + sizeof(m.conn_id) + +#define VHOST_USER_PAYLOAD_SIZE (sizeof(m) - VHOST_USER_HDR_SIZE) + +/* The version of the protocol we support */ +#define VHOST_USER_VERSION (0x2) + +#endif + diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h index ee7c760..7f8b25c 100644 --- a/include/sysemu/sysemu.h +++ b/include/sysemu/sysemu.h @@ -244,5 +244,6 @@ extern QemuOptsList qemu_netdev_opts; extern QemuOptsList qemu_net_opts; extern QemuOptsList qemu_global_opts; extern QemuOptsList qemu_mon_opts; +extern QemuOptsList qemu_vhost_pci_server_opts; #endif diff --git a/qemu-options.hx b/qemu-options.hx index a71aaf8..1fdb820 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -3968,6 +3968,10 @@ contents of @code{iv.b64} to the second secret ETEXI +DEF("vhost-pci-server", HAS_ARG, QEMU_OPTION_vhost_pci_server, + "-vhost-pci-server socket,chrdev={id}\n" + " creates a vhost-pci-server", + QEMU_ARCH_I386) HXCOMM This is the last statement. Insert new options before this line! STEXI diff --git a/vl.c b/vl.c index b3c80d5..c1f038d 100644 --- a/vl.c +++ b/vl.c @@ -121,6 +121,7 @@ int main(int argc, char **argv) #include "crypto/init.h" #include "sysemu/replay.h" #include "qapi/qmp/qerror.h" +#include "hw/virtio/vhost-pci-server.h" #define MAX_VIRTIO_CONSOLES 1 #define MAX_SCLP_CONSOLES 1 @@ -178,6 +179,7 @@ bool boot_strict; uint8_t *boot_splash_filedata; size_t boot_splash_filedata_size; uint8_t qemu_extra_params_fw[2]; +bool vhost_pci_server_enabled; int icount_align_option; @@ -2980,6 +2982,7 @@ int main(int argc, char **argv, char **envp) qemu_add_drive_opts(&qemu_drive_opts); qemu_add_opts(&qemu_chardev_opts); qemu_add_opts(&qemu_device_opts); + qemu_add_opts(&qemu_vhost_pci_server_opts); qemu_add_opts(&qemu_netdev_opts); qemu_add_opts(&qemu_net_opts); qemu_add_opts(&qemu_rtc_opts); @@ -3970,6 +3973,13 @@ int main(int argc, char **argv, char **envp) exit(1); } break; + case QEMU_OPTION_vhost_pci_server: + vhost_pci_server_enabled = true; + opts = qemu_opts_parse_noisily(qemu_find_opts("vhost-pci-server"), optarg, false); + if (!opts) { + exit(1); + } + break; default: os_parse_cmd_args(popt->index, optarg); } @@ -4479,6 +4489,16 @@ int main(int argc, char **argv, char **envp) exit(1); } + /* check if the vhost-pci-server is enabled */ + if (vhost_pci_server_enabled) { + int ret; + ret = vhost_pci_server_init(qemu_opts_find( + qemu_find_opts("vhost-pci-server"), + NULL)); + if (ret < 0) + exit(1); + } + /* init USB devices */ if (machine_usb(current_machine)) { if (foreach_device_config(DEV_USB, usb_parse) < 0) @@ -4607,6 +4627,12 @@ int main(int argc, char **argv, char **envp) bdrv_close_all(); pause_all_vcpus(); res_free(); + if (vhost_pci_server_enabled) { + int ret; + ret = vhost_pci_server_cleanup(); + if (ret < 0) + exit(1); + } #ifdef CONFIG_TPM tpm_cleanup(); #endif -- 2.7.4 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [Qemu-devel] [RFC PATCH 2/4] vhost-user: add the vhost-user extension to support the vhost-pci based inter-vm communication 2016-11-10 2:47 ` [Qemu-devel] [RFC PATCH 2/4] vhost-user: add the vhost-user extension to support the vhost-pci based inter-vm communication Wei Wang @ 2016-11-10 11:36 ` Marc-André Lureau 2016-11-11 8:28 ` Wei Wang 0 siblings, 1 reply; 9+ messages in thread From: Marc-André Lureau @ 2016-11-10 11:36 UTC (permalink / raw) To: Wei Wang, mst, stefanha, pbonzini, qemu-devel, virtio-dev Hi On Thu, Nov 10, 2016 at 6:47 AM Wei Wang <wei.w.wang@intel.com> wrote: > This is the slave part of vhost-user implemented in QEMU, with an extension > to support vhost-pci. > Instead of implementing "another vhost-user slave", it would be worth investigating using libvhost-user instead ( https://lists.gnu.org/archive/html/qemu-devel/2016-10/msg03990.html). This is just a suggestion, it is quite fine for vhost-pci to have its own smaller/specific vhost-user slave implementation (without virtio rings handlings etc). (libvhost-user is also very young, not yet in qemu, we should be able shape it for vhost-pci needs) > Signed-off-by: Wei Wang <wei.w.wang@intel.com> > --- > hw/virtio/Makefile.objs | 1 + > hw/virtio/vhost-pci-server.c | 469 > +++++++++++++++++++++++++++++++++++ > hw/virtio/vhost-user.c | 86 +------ > include/hw/virtio/vhost-pci-server.h | 45 ++++ > include/hw/virtio/vhost-user.h | 110 ++++++++ > include/sysemu/sysemu.h | 1 + > qemu-options.hx | 4 + > vl.c | 26 ++ > 8 files changed, 657 insertions(+), 85 deletions(-) > create mode 100644 hw/virtio/vhost-pci-server.c > create mode 100644 include/hw/virtio/vhost-pci-server.h > create mode 100644 include/hw/virtio/vhost-user.h > > diff --git a/hw/virtio/Makefile.objs b/hw/virtio/Makefile.objs > index 3e2b175..e44feb8 100644 > --- a/hw/virtio/Makefile.objs > +++ b/hw/virtio/Makefile.objs > @@ -2,6 +2,7 @@ common-obj-y += virtio-rng.o > common-obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o > common-obj-y += virtio-bus.o > common-obj-y += virtio-mmio.o > +common-obj-y += vhost-pci-server.o > > obj-y += virtio.o virtio-balloon.o > obj-$(CONFIG_LINUX) += vhost.o vhost-backend.o vhost-user.o > diff --git a/hw/virtio/vhost-pci-server.c b/hw/virtio/vhost-pci-server.c > new file mode 100644 > index 0000000..6ce8516 > --- /dev/null > +++ b/hw/virtio/vhost-pci-server.c > @@ -0,0 +1,469 @@ > +/* > + * Vhost-pci server > + * > + * Copyright Intel Corp. 2016 > + * > + * Authors: > + * Wei Wang <wei.w.wang@intel.com> > + * > + * This work is licensed under the terms of the GNU GPL, version 2 or > later. > + * See the COPYING file in the top-level directory. > + */ > + > +#include <qemu/osdep.h> > +#include <qemu/thread.h> > +#include <qemu/main-loop.h> > +#include <qemu/bitops.h> > +#include <qemu/bitmap.h> > +#include <qemu/sockets.h> > +#include <linux/virtio_net.h> > +#include "sysemu/char.h" > +#include "qapi/error.h" > +#include "hw/virtio/vhost-pci-server.h" > +#include "qemu/option.h" > +#include "monitor/qdev.h" > +#include "hw/virtio/vhost-user.h" > +#include "hw/qdev.h" > + > +#define VHOST_PCI_FEATURE_BITS (1ULL << VIRTIO_F_VERSION_1) > + > +#define VHOST_PCI_NET_FEATURE_BITS (1ULL << VIRTIO_NET_F_MRG_RXBUF) | \ > + (1ULL << VIRTIO_NET_F_CTRL_VQ) | \ > + (1ULL << VIRTIO_NET_F_MQ) > + > +#define VHOST_USER_SET_PEER_CONNECTION_OFF 0 > +#define VHOST_USER_SET_PEER_CONNECTION_ON 1 > +#define VHOST_USER_SET_PEER_CONNECTION_INIT 2 > + > +VhostPCIServer *vp_server; > + > +QemuOptsList qemu_vhost_pci_server_opts = { > + .name = "vhost-pci-server", > + .implied_opt_name = "chardev", > + .head = QTAILQ_HEAD_INITIALIZER(qemu_vhost_pci_server_opts.head), > + .desc = { > + /* > + * no elements => accept any > + * sanity checking will happen later > + * when setting device properties > + */ > + { /* end of list */ } > + }, > +}; > + > +static int vhost_pci_server_write(CharDriverState *chr, VhostUserMsg *msg) > +{ > + int size = msg->size + VHOST_USER_HDR_SIZE; > + > + if (!msg) > + return 0; > + > + msg->flags &= ~VHOST_USER_VERSION_MASK; > + msg->flags |= VHOST_USER_VERSION; > + > + return qemu_chr_fe_write_all_n(chr, msg->conn_id, > + (const uint8_t *)msg, size) == size ? 0 > : -1; > +} > + > +PeerConnectionTable *vp_server_find_table_ent(const char *dev_id) > +{ > + int i; > + PeerConnectionTable *ent; > + uint64_t max_connections = vp_server->chr->max_connections; > + > + for (i = 0; i < max_connections; i++) { > + ent = &vp_server->peer_table[i]; > + if (!strcmp(dev_id, ent->dev_id)) > + return ent; > + } > + return NULL; > +} > + > +static void vhost_pci_init_peer_table(uint64_t id) > +{ > + PeerConnectionTable *ent = &vp_server->peer_table[id]; > + > + ent->peer_feature_bits |= 1ULL << VHOST_USER_F_PROTOCOL_FEATURES; > + QLIST_INIT(&ent->vq_list); > + ent->vq_num = 0; > +} > + > +static int vhost_pci_get_conn_id(CharDriverState *chr, VhostUserMsg *msg) > +{ > + unsigned long *conn_bitmap = chr->conn_bitmap; > + unsigned long *old_conn_bitmap = vp_server->old_conn_bitmap; > + uint64_t nbits = chr->max_connections; > + uint64_t id; > + int r; > + > + bitmap_xor(old_conn_bitmap, old_conn_bitmap, conn_bitmap, > (long)nbits); > + > + for (id = find_first_bit(old_conn_bitmap, nbits); id < nbits; > + id = find_next_bit(old_conn_bitmap, nbits, id + 1)) { > + vhost_pci_init_peer_table(id); > + msg->conn_id = id; > + msg->payload.u64 = id; > + msg->size = sizeof(msg->payload.u64); > + msg->flags |= VHOST_USER_REPLY_MASK; > + r = vhost_pci_server_write(chr, msg); > + } > + bitmap_copy(old_conn_bitmap, conn_bitmap, (long)nbits); > + > + return r; > +} > + > +static int vhost_pci_get_peer_features(CharDriverState *chr, VhostUserMsg > *msg) > +{ > + PeerConnectionTable *ent = &vp_server->peer_table[msg->conn_id]; > + msg->payload.u64 = ent->peer_feature_bits; > + msg->size = sizeof(msg->payload.u64); > + msg->flags |= VHOST_USER_REPLY_MASK; > + return vhost_pci_server_write(chr, msg); > +} > + > +static int vhost_pci_get_queue_num(CharDriverState *chr, VhostUserMsg > *msg) > +{ > + PeerConnectionTable *ent = &vp_server->peer_table[msg->conn_id]; > + switch (ent->virtio_id) { > + case VIRTIO_ID_NET: > + msg->payload.u64 = VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX; > + break; > + default: > + printf("%s: device type not supported yet..\n", __func__); > + } > + msg->size = sizeof(msg->payload.u64); > + msg->flags |= VHOST_USER_REPLY_MASK; > + return vhost_pci_server_write(chr, msg); > +} > + > +static int vhost_pci_get_protocol_features(CharDriverState *chr, > VhostUserMsg *msg) > +{ > + msg->payload.u64 = VHOST_USER_PROTOCOL_FEATURES; > + msg->size = sizeof(msg->payload.u64); > + msg->flags |= VHOST_USER_REPLY_MASK; > + return vhost_pci_server_write(chr, msg); > +} > + > +static void vhost_pci_set_protocol_features(VhostUserMsg *msg) > +{ > + vp_server->protocol_features = msg->payload.u64; > +} > + > +static int vhost_pci_device_create(uint64_t conn_id) > +{ > + PeerConnectionTable *ent = &vp_server->peer_table[conn_id]; > + Error *local_err = NULL; > + QemuOpts *opts; > + DeviceState *dev; > + char params[50]; > + > + switch (ent->virtio_id) { > + case VIRTIO_ID_NET: > + sprintf(params, "driver=vhost-pci-net-pci,id=vhost-pci-%ld", > conn_id); > + sprintf(ent->dev_id, "vhost-pci-%ld", conn_id); > + break; > + default: > + printf("%s: device type not supported yet..\n", __func__); > + } > + > + opts = qemu_opts_parse_noisily(qemu_find_opts("device"), params, > true); > + dev = qdev_device_add(opts, &local_err); > + if (!dev) { > + qemu_opts_del(opts); > + return -1; > + } > + object_unref(OBJECT(dev)); > + return 0; > +} > + > +static void vhost_pci_set_device_info(VhostUserMsg *msg) > +{ > + PeerConnectionTable *ent = &vp_server->peer_table[msg->conn_id]; > + DeviceInfo *info = &msg->payload.dev_info; > + > + memcpy(ent->uuid, info->uuid, sizeof(uuid_t)); > + ent->virtio_id = info->virtio_id; > + switch (ent->virtio_id) { > + case VIRTIO_ID_NET: > + ent->peer_feature_bits |= (VHOST_PCI_FEATURE_BITS | > VHOST_PCI_NET_FEATURE_BITS); > + break; > + default: > + printf("%s: device type not supported yet..\n", __func__); > + } > +} > + > +static void vhost_pci_set_peer_feature_bits(VhostUserMsg *msg) > +{ > + PeerConnectionTable *ent = &vp_server->peer_table[msg->conn_id]; > + > + ent->peer_feature_bits = msg->payload.u64; > +} > + > +static uint64_t vhost_pci_peer_mem_size_get(VhostUserMemory *peer_mem) > +{ > + int i; > + uint64_t total_size; > + uint32_t nregions = peer_mem->nregions; > + VhostUserMemoryRegion *regions = peer_mem->regions; > + > + for (i = 0; i < nregions; i++) { > + total_size += regions[i].memory_size; > + } > + > + return total_size; > +} > + > +static int vhost_pci_set_mem_table(uint64_t conn_id, VhostUserMemory > *peer_mem, int *fds) > +{ > + int i; > + void *mr_qva; > + PeerConnectionTable *ent = &vp_server->peer_table[conn_id]; > + uint32_t nregions = peer_mem->nregions; > + VhostUserMemoryRegion *peer_mr = peer_mem->regions; > + MemoryRegion *bar_mr = g_malloc(sizeof(MemoryRegion)); > + MemoryRegion *mr = g_malloc(nregions * sizeof(MemoryRegion)); > + uint64_t bar_size = 2 * vhost_pci_peer_mem_size_get(peer_mem); > + uint64_t bar_map_offset = 0; > + > + bar_size = pow2ceil(bar_size); > + memory_region_init(bar_mr, NULL, "Peer Memory", bar_size); > + > + for (i = 0; i < nregions; i++) { > + mr_qva = mmap(NULL, peer_mr[i].memory_size + > peer_mr[i].mmap_offset, > + PROT_READ | PROT_READ, MAP_SHARED, fds[i], 0); > + if (mr_qva == MAP_FAILED) { > + printf("%s called: map failed \n", __func__); > + return -1; > + } > + mr_qva += peer_mr[i].mmap_offset; > + memory_region_init_ram_ptr(&mr[i], NULL, "Peer Memory", > peer_mr[i].memory_size, mr_qva); > + memory_region_add_subregion(bar_mr, bar_map_offset, &mr[i]); > + bar_map_offset += peer_mr[i].memory_size; > + } > + ent->bar_mr = bar_mr; > + ent->bar_map_offset = bar_map_offset; > + > + return 0; > +} > + > +static void vhost_pci_alloc_peer_vring_info(uint64_t conn_id) > +{ > + PeerConnectionTable *ent = &vp_server->peer_table[conn_id]; > + PeerVirtqInfo *virtq_info = g_malloc0(sizeof(PeerVirtqInfo)); > + QLIST_INSERT_HEAD(&ent->vq_list, virtq_info, node); > + ent->vq_num++; > +} > + > +static void vhost_pci_set_vring_num(VhostUserMsg *msg) > +{ > + PeerConnectionTable *ent = &vp_server->peer_table[msg->conn_id]; > + PeerVirtqInfo *virtq_info = QLIST_FIRST(&ent->vq_list); > + > + virtq_info->vring_num = msg->payload.u64; > +} > + > +static void vhost_pci_set_vring_base(VhostUserMsg *msg) > +{ > + PeerConnectionTable *ent = &vp_server->peer_table[msg->conn_id]; > + PeerVirtqInfo *virtq_info = QLIST_FIRST(&ent->vq_list); > + > + virtq_info->last_avail_idx = msg->payload.u64; > +} > + > +static void vhost_pci_set_vring_addr(VhostUserMsg *msg) > +{ > + PeerConnectionTable *ent = &vp_server->peer_table[msg->conn_id]; > + PeerVirtqInfo *virtq_info = QLIST_FIRST(&ent->vq_list); > + memcpy(&virtq_info->addr, &msg->payload.addr, > + sizeof(struct vhost_vring_addr)); > +} > + > +static void vhost_pci_set_vring_kick(uint64_t conn_id, int fd) > +{ > + PeerConnectionTable *ent = &vp_server->peer_table[conn_id]; > + PeerVirtqInfo *virtq_info = QLIST_FIRST(&ent->vq_list); > + if (!virtq_info) > + virtq_info->kickfd = fd; > +} > + > +static void vhost_pci_set_vring_call(uint64_t conn_id, int fd) > +{ > + PeerConnectionTable *ent = &vp_server->peer_table[conn_id]; > + PeerVirtqInfo *virtq_info = QLIST_FIRST(&ent->vq_list); > + if (virtq_info) > + virtq_info->callfd = fd; > +} > + > +static void vhost_pci_set_peer_connection(VhostUserMsg *msg) > +{ > + uint64_t cmd = msg->payload.u64; > + uint64_t conn_id = msg->conn_id; > + > + switch (cmd) { > + case VHOST_USER_SET_PEER_CONNECTION_INIT: > + vhost_pci_device_create(conn_id); > + break; > + default: > + printf("%s called: cmd %lu not supported yet \n", __func__, cmd); > + } > +} > + > +static void vhost_pci_server_read(void *opaque, const uint8_t *buf, int > size) > +{ > + VhostUserMsg msg; > + uint8_t *p = (uint8_t *) &msg; > + CharDriverState *chr = (CharDriverState *)opaque; > + int fds[8], fd_num; > + > + if (size != VHOST_USER_HDR_SIZE) { > + printf("Wrong message size received %d\n", size); > + return; > + } > + memcpy(p, buf, VHOST_USER_HDR_SIZE); > + > + if (msg.size) { > + p += VHOST_USER_HDR_SIZE; > + size = qemu_chr_fe_read_all_n(chr, msg.conn_id, p, msg.size); > + if (size != msg.size) { > + printf("Wrong message size received %d != %d\n", > + size, msg.size); > + return; > + } > + } > + > + if (msg.request > VHOST_USER_MAX) > + printf("vhost read incorrect msg \n"); > + > + switch(msg.request) { > + case VHOST_USER_GET_CONN_ID: > + vhost_pci_get_conn_id(chr, &msg); > + break; > + case VHOST_USER_GET_FEATURES: > + vhost_pci_get_peer_features(chr, &msg); > + break; > + case VHOST_USER_GET_PROTOCOL_FEATURES: > + vhost_pci_get_protocol_features(chr, &msg); > + break; > + case VHOST_USER_SET_PROTOCOL_FEATURES: > + vhost_pci_set_protocol_features(&msg); > + break; > + case VHOST_USER_SET_DEV_INFO: > + vhost_pci_set_device_info(&msg); > + break; > + case VHOST_USER_GET_QUEUE_NUM: > + vhost_pci_get_queue_num(chr, &msg); > + break; > + case VHOST_USER_SET_OWNER: > + break; > + case VHOST_USER_SET_FEATURES: > + vhost_pci_set_peer_feature_bits(&msg); > + break; > + case VHOST_USER_SET_VRING_NUM: > + vhost_pci_alloc_peer_vring_info(msg.conn_id); > + vhost_pci_set_vring_num(&msg); > + break; > + case VHOST_USER_SET_VRING_BASE: > + vhost_pci_set_vring_base(&msg); > + break; > + case VHOST_USER_SET_VRING_ADDR: > + vhost_pci_set_vring_addr(&msg); > + break; > + case VHOST_USER_SET_VRING_KICK: > + /* consume the fd */ > + qemu_chr_fe_get_msgfds_n(chr, msg.conn_id, fds, 1); > + printf("VHOST_USER_SET_VRING_KICK called:..kickfd = %d\n", > fds[0]); > + vhost_pci_set_vring_kick(msg.conn_id, fds[0]); > + /* > + * This is a non-blocking eventfd. > + * The receive function forces it to be blocking, > + * so revert it back to non-blocking. > + */ > + qemu_set_nonblock(fds[0]); > + break; > + case VHOST_USER_SET_VRING_CALL: > + /* consume the fd */ > + qemu_chr_fe_get_msgfds_n(chr, msg.conn_id, fds, 1); > + vhost_pci_set_vring_call(msg.conn_id, fds[0]); > + /* > + * This is a non-blocking eventfd. > + * The receive function forces it to be blocking, > + * so revert it back to non-blocking. > + */ > + qemu_set_nonblock(fds[0]); > + break; > + case VHOST_USER_SET_MEM_TABLE: > + fd_num = qemu_chr_fe_get_msgfds_n(chr, msg.conn_id, > + fds, sizeof(fds) / sizeof(int)); > + printf("VHOST_USER_SET_MEM_TABLE: fd = %d \n", fd_num); > + vhost_pci_set_mem_table(msg.conn_id, &msg.payload.memory, fds); > + break; > + case VHOST_USER_SET_PEER_CONNECTION: > + vhost_pci_set_peer_connection(&msg); > + break; > + default: > + printf("default called..msg->request = %d \n", msg.request); > + break; > + } > +} > + > +static int vhost_pci_server_can_read(void *opaque) > +{ > + return VHOST_USER_HDR_SIZE; > +} > + > +static void vhost_pci_server_event(void *opaque, int event) > +{ > + switch (event) { > + case CHR_EVENT_OPENED: > + printf("vhost_pci_server_event called.. \n"); > + break; > + case CHR_EVENT_CLOSED: > + printf("vhost_pci_server_event called: event close..\n"); > + break; > + } > +} > + > +static CharDriverState *vhost_pci_server_parse_chardev(const char *id) > +{ > + CharDriverState *chr = qemu_chr_find(id); > + if (chr == NULL) { > + printf("chardev \"%s\" not found", id); > + return NULL; > + } > + > + qemu_chr_fe_claim_no_fail(chr); > + > + return chr; > +} > + > +int vhost_pci_server_init(QemuOpts *opts) > +{ > + CharDriverState *chr; > + const char *chardev_id = qemu_opt_get(opts, "chardev"); > + uint64_t max_connections; > + > + vp_server = (VhostPCIServer *)malloc(sizeof(VhostPCIServer)); > + > + chr = vhost_pci_server_parse_chardev(chardev_id); > + if (!chr) { > + return -1; > + } > + max_connections = chr->max_connections; > + > + qemu_chr_add_handlers(chr, vhost_pci_server_can_read, > vhost_pci_server_read, vhost_pci_server_event, chr); > + > + vp_server->chr = chr; > + > + vp_server->peer_table = (PeerConnectionTable > *)g_malloc0(max_connections * sizeof(PeerConnectionTable)); > + > + vp_server->old_conn_bitmap = bitmap_new(max_connections); > + > + return 0; > +} > + > +int vhost_pci_server_cleanup(void) > +{ > + free(vp_server); > + printf("vhost_pci_server_cleanup called.. \n"); > + return 0; > +} > diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c > index b57454a..bce5181 100644 > --- a/hw/virtio/vhost-user.c > +++ b/hw/virtio/vhost-user.c > @@ -13,6 +13,7 @@ > #include "hw/virtio/vhost.h" > #include "hw/virtio/vhost-backend.h" > #include "hw/virtio/virtio-net.h" > +#include "hw/virtio/vhost-user.h" > #include "sysemu/char.h" > #include "sysemu/kvm.h" > #include "qemu/error-report.h" > @@ -24,91 +25,6 @@ > #include <sys/un.h> > #include <linux/vhost.h> > > -#define VHOST_MEMORY_MAX_NREGIONS 8 > -#define VHOST_USER_F_PROTOCOL_FEATURES 30 > - > -enum VhostUserProtocolFeature { > - VHOST_USER_PROTOCOL_F_MQ = 0, > - VHOST_USER_PROTOCOL_F_LOG_SHMFD = 1, > - VHOST_USER_PROTOCOL_F_RARP = 2, > - VHOST_USER_PROTOCOL_F_REPLY_ACK = 3, > - > - VHOST_USER_PROTOCOL_F_MAX > -}; > - > -#define VHOST_USER_PROTOCOL_FEATURE_MASK ((1 << > VHOST_USER_PROTOCOL_F_MAX) - 1) > - > -typedef enum VhostUserRequest { > - VHOST_USER_NONE = 0, > - VHOST_USER_GET_FEATURES = 1, > - VHOST_USER_SET_FEATURES = 2, > - VHOST_USER_SET_OWNER = 3, > - VHOST_USER_RESET_OWNER = 4, > - VHOST_USER_SET_MEM_TABLE = 5, > - VHOST_USER_SET_LOG_BASE = 6, > - VHOST_USER_SET_LOG_FD = 7, > - VHOST_USER_SET_VRING_NUM = 8, > - VHOST_USER_SET_VRING_ADDR = 9, > - VHOST_USER_SET_VRING_BASE = 10, > - VHOST_USER_GET_VRING_BASE = 11, > - VHOST_USER_SET_VRING_KICK = 12, > - VHOST_USER_SET_VRING_CALL = 13, > - VHOST_USER_SET_VRING_ERR = 14, > - VHOST_USER_GET_PROTOCOL_FEATURES = 15, > - VHOST_USER_SET_PROTOCOL_FEATURES = 16, > - VHOST_USER_GET_QUEUE_NUM = 17, > - VHOST_USER_SET_VRING_ENABLE = 18, > - VHOST_USER_SEND_RARP = 19, > - VHOST_USER_MAX > -} VhostUserRequest; > - > -typedef struct VhostUserMemoryRegion { > - uint64_t guest_phys_addr; > - uint64_t memory_size; > - uint64_t userspace_addr; > - uint64_t mmap_offset; > -} VhostUserMemoryRegion; > - > -typedef struct VhostUserMemory { > - uint32_t nregions; > - uint32_t padding; > - VhostUserMemoryRegion regions[VHOST_MEMORY_MAX_NREGIONS]; > -} VhostUserMemory; > - > -typedef struct VhostUserLog { > - uint64_t mmap_size; > - uint64_t mmap_offset; > -} VhostUserLog; > - > -typedef struct VhostUserMsg { > - VhostUserRequest request; > - > -#define VHOST_USER_VERSION_MASK (0x3) > -#define VHOST_USER_REPLY_MASK (0x1<<2) > -#define VHOST_USER_NEED_REPLY_MASK (0x1 << 3) > - uint32_t flags; > - uint32_t size; /* the following payload size */ > - union { > -#define VHOST_USER_VRING_IDX_MASK (0xff) > -#define VHOST_USER_VRING_NOFD_MASK (0x1<<8) > - uint64_t u64; > - struct vhost_vring_state state; > - struct vhost_vring_addr addr; > - VhostUserMemory memory; > - VhostUserLog log; > - } payload; > -} QEMU_PACKED VhostUserMsg; > - > -static VhostUserMsg m __attribute__ ((unused)); > -#define VHOST_USER_HDR_SIZE (sizeof(m.request) \ > - + sizeof(m.flags) \ > - + sizeof(m.size)) > - > -#define VHOST_USER_PAYLOAD_SIZE (sizeof(m) - VHOST_USER_HDR_SIZE) > - > -/* The version of the protocol we support */ > -#define VHOST_USER_VERSION (0x1) > - > static bool ioeventfd_enabled(void) > { > return kvm_enabled() && kvm_eventfds_enabled(); > diff --git a/include/hw/virtio/vhost-pci-server.h > b/include/hw/virtio/vhost-pci-server.h > new file mode 100644 > index 0000000..c9c4a69 > --- /dev/null > +++ b/include/hw/virtio/vhost-pci-server.h > @@ -0,0 +1,45 @@ > +#ifndef QEMU_VHOST_PCI_SERVER_H > +#define QEMU_VHOST_PCI_SERVER_H > + > +#include <uuid/uuid.h> > +#include <linux/vhost.h> > + > +typedef struct PeerVirtqInfo { > + int kickfd; > + int callfd; > + uint32_t vring_num; > + uint16_t last_avail_idx; > + struct vhost_vring_addr addr; > + QLIST_ENTRY(PeerVirtqInfo) node; > +} PeerVirtqInfo; > + > +typedef struct PeerConnectionTable { > + char dev_id[30]; > + uuid_t uuid; > + uint16_t virtio_id; > + uint32_t bar_id; > + MemoryRegion *bar_mr; > + uint64_t bar_map_offset; > + uint64_t peer_feature_bits; > + void *opaque; > + uint16_t vq_num; > + QLIST_HEAD(, PeerVirtqInfo) vq_list; > +} PeerConnectionTable; > + > +typedef struct VhostPCIServer { > + CharDriverState *chr; > + uint64_t protocol_features; > + unsigned long *old_conn_bitmap; > + /* a table indexed by the peer connection id */ > + PeerConnectionTable *peer_table; > +} VhostPCIServer; > + > +extern VhostPCIServer *vp_server; > + > +extern int vhost_pci_server_init(QemuOpts *opts); > + > +extern int vhost_pci_server_cleanup(void); > + > +extern PeerConnectionTable *vp_server_find_table_ent(const char *dev_id); > + > +#endif > diff --git a/include/hw/virtio/vhost-user.h > b/include/hw/virtio/vhost-user.h > new file mode 100644 > index 0000000..794a8d8 > --- /dev/null > +++ b/include/hw/virtio/vhost-user.h > @@ -0,0 +1,110 @@ > +#ifndef VHOST_USER_H > +#define VHOST_USER_H > + > +#include <linux/vhost.h> > +#include <uuid/uuid.h> > + > +#define VHOST_MEMORY_MAX_NREGIONS 8 > +#define VHOST_USER_F_PROTOCOL_FEATURES 30 > + > +enum VhostUserProtocolFeature { > + VHOST_USER_PROTOCOL_F_MQ = 0, > + VHOST_USER_PROTOCOL_F_LOG_SHMFD = 1, > + VHOST_USER_PROTOCOL_F_RARP = 2, > + VHOST_USER_PROTOCOL_F_REPLY_ACK = 3, > + VHOST_USER_PROTOCOL_F_VHOST_PCI =4, > + > + VHOST_USER_PROTOCOL_F_MAX > +}; > + > +#define VHOST_USER_PROTOCOL_FEATURE_MASK ((1 << > VHOST_USER_PROTOCOL_F_MAX) - 1) > + > +#define VHOST_USER_PROTOCOL_FEATURES ((1ULL << VHOST_USER_PROTOCOL_F_MQ) > | \ > + (1ULL << > VHOST_USER_PROTOCOL_F_LOG_SHMFD) | \ > + (1ULL << > VHOST_USER_PROTOCOL_F_RARP)) | \ > + (1ULL << > VHOST_USER_PROTOCOL_F_VHOST_PCI) > + > +typedef enum VhostUserRequest { > + VHOST_USER_NONE = 0, > + VHOST_USER_GET_FEATURES = 1, > + VHOST_USER_SET_FEATURES = 2, > + VHOST_USER_SET_OWNER = 3, > + VHOST_USER_RESET_OWNER = 4, > + VHOST_USER_SET_MEM_TABLE = 5, > + VHOST_USER_SET_LOG_BASE = 6, > + VHOST_USER_SET_LOG_FD = 7, > + VHOST_USER_SET_VRING_NUM = 8, > + VHOST_USER_SET_VRING_ADDR = 9, > + VHOST_USER_SET_VRING_BASE = 10, > + VHOST_USER_GET_VRING_BASE = 11, > + VHOST_USER_SET_VRING_KICK = 12, > + VHOST_USER_SET_VRING_CALL = 13, > + VHOST_USER_SET_VRING_ERR = 14, > + VHOST_USER_GET_PROTOCOL_FEATURES = 15, > + VHOST_USER_SET_PROTOCOL_FEATURES = 16, > + VHOST_USER_GET_QUEUE_NUM = 17, > + VHOST_USER_SET_VRING_ENABLE = 18, > + VHOST_USER_SEND_RARP = 19, > + VHOST_USER_GET_CONN_ID = 20, > + VHOST_USER_SET_DEV_INFO = 21, > + VHOST_USER_SET_PEER_CONNECTION = 22, > + VHOST_USER_MAX > +} VhostUserRequest; > + > +typedef struct VhostUserMemoryRegion { > + uint64_t guest_phys_addr; > + uint64_t memory_size; > + uint64_t userspace_addr; > + uint64_t mmap_offset; > +} VhostUserMemoryRegion; > + > +typedef struct VhostUserMemory { > + uint32_t nregions; > + uint32_t padding; > + VhostUserMemoryRegion regions[VHOST_MEMORY_MAX_NREGIONS]; > +} VhostUserMemory; > + > +typedef struct VhostUserLog { > + uint64_t mmap_size; > + uint64_t mmap_offset; > +} VhostUserLog; > + > +typedef struct DeviceInfo { > + uuid_t uuid; > + uint16_t virtio_id; > +} DeviceInfo; > + > +typedef struct VhostUserMsg { > + VhostUserRequest request; > + > +#define VHOST_USER_VERSION_MASK (0x3) > +#define VHOST_USER_REPLY_MASK (0x1<<2) > +#define VHOST_USER_NEED_REPLY_MASK (0x1 << 3) > + uint32_t flags; > + uint32_t size; /* the following payload size */ > + uint64_t conn_id; > + union { > +#define VHOST_USER_VRING_IDX_MASK (0xff) > +#define VHOST_USER_VRING_NOFD_MASK (0x1<<8) > + uint64_t u64; > + struct vhost_vring_state state; > + struct vhost_vring_addr addr; > + VhostUserMemory memory; > + VhostUserLog log; > + DeviceInfo dev_info; > + } payload; > +} QEMU_PACKED VhostUserMsg; > + > +static VhostUserMsg m __attribute__ ((unused)); > +#define VHOST_USER_HDR_SIZE (sizeof(m.request) \ > + + sizeof(m.flags) \ > + + sizeof(m.size)) \ > + + sizeof(m.conn_id) > + > +#define VHOST_USER_PAYLOAD_SIZE (sizeof(m) - VHOST_USER_HDR_SIZE) > + > +/* The version of the protocol we support */ > +#define VHOST_USER_VERSION (0x2) > + > +#endif > + > diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h > index ee7c760..7f8b25c 100644 > --- a/include/sysemu/sysemu.h > +++ b/include/sysemu/sysemu.h > @@ -244,5 +244,6 @@ extern QemuOptsList qemu_netdev_opts; > extern QemuOptsList qemu_net_opts; > extern QemuOptsList qemu_global_opts; > extern QemuOptsList qemu_mon_opts; > +extern QemuOptsList qemu_vhost_pci_server_opts; > > #endif > diff --git a/qemu-options.hx b/qemu-options.hx > index a71aaf8..1fdb820 100644 > --- a/qemu-options.hx > +++ b/qemu-options.hx > @@ -3968,6 +3968,10 @@ contents of @code{iv.b64} to the second secret > > ETEXI > > +DEF("vhost-pci-server", HAS_ARG, QEMU_OPTION_vhost_pci_server, > + "-vhost-pci-server socket,chrdev={id}\n" > + " creates a vhost-pci-server", > + QEMU_ARCH_I386) > > HXCOMM This is the last statement. Insert new options before this line! > STEXI > diff --git a/vl.c b/vl.c > index b3c80d5..c1f038d 100644 > --- a/vl.c > +++ b/vl.c > @@ -121,6 +121,7 @@ int main(int argc, char **argv) > #include "crypto/init.h" > #include "sysemu/replay.h" > #include "qapi/qmp/qerror.h" > +#include "hw/virtio/vhost-pci-server.h" > > #define MAX_VIRTIO_CONSOLES 1 > #define MAX_SCLP_CONSOLES 1 > @@ -178,6 +179,7 @@ bool boot_strict; > uint8_t *boot_splash_filedata; > size_t boot_splash_filedata_size; > uint8_t qemu_extra_params_fw[2]; > +bool vhost_pci_server_enabled; > > int icount_align_option; > > @@ -2980,6 +2982,7 @@ int main(int argc, char **argv, char **envp) > qemu_add_drive_opts(&qemu_drive_opts); > qemu_add_opts(&qemu_chardev_opts); > qemu_add_opts(&qemu_device_opts); > + qemu_add_opts(&qemu_vhost_pci_server_opts); > qemu_add_opts(&qemu_netdev_opts); > qemu_add_opts(&qemu_net_opts); > qemu_add_opts(&qemu_rtc_opts); > @@ -3970,6 +3973,13 @@ int main(int argc, char **argv, char **envp) > exit(1); > } > break; > + case QEMU_OPTION_vhost_pci_server: > + vhost_pci_server_enabled = true; > + opts = > qemu_opts_parse_noisily(qemu_find_opts("vhost-pci-server"), optarg, false); > + if (!opts) { > + exit(1); > + } > + break; > default: > os_parse_cmd_args(popt->index, optarg); > } > @@ -4479,6 +4489,16 @@ int main(int argc, char **argv, char **envp) > exit(1); > } > > + /* check if the vhost-pci-server is enabled */ > + if (vhost_pci_server_enabled) { > + int ret; > + ret = vhost_pci_server_init(qemu_opts_find( > + qemu_find_opts("vhost-pci-server"), > + NULL)); > + if (ret < 0) > + exit(1); > + } > + > /* init USB devices */ > if (machine_usb(current_machine)) { > if (foreach_device_config(DEV_USB, usb_parse) < 0) > @@ -4607,6 +4627,12 @@ int main(int argc, char **argv, char **envp) > bdrv_close_all(); > pause_all_vcpus(); > res_free(); > + if (vhost_pci_server_enabled) { > + int ret; > + ret = vhost_pci_server_cleanup(); > + if (ret < 0) > + exit(1); > + } > #ifdef CONFIG_TPM > tpm_cleanup(); > #endif > -- > 2.7.4 > > -- Marc-André Lureau ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [Qemu-devel] [RFC PATCH 2/4] vhost-user: add the vhost-user extension to support the vhost-pci based inter-vm communication 2016-11-10 11:36 ` Marc-André Lureau @ 2016-11-11 8:28 ` Wei Wang 0 siblings, 0 replies; 9+ messages in thread From: Wei Wang @ 2016-11-11 8:28 UTC (permalink / raw) To: Marc-André Lureau; +Cc: mst, stefanha, pbonzini, qemu-devel, virtio-dev On 11/10/2016 07:36 PM, Marc-André Lureau wrote: > Hi > > On Thu, Nov 10, 2016 at 6:47 AM Wei Wang <wei.w.wang@intel.com > <mailto:wei.w.wang@intel.com>> wrote: > > This is the slave part of vhost-user implemented in QEMU, with an > extension > to support vhost-pci. > > > Instead of implementing "another vhost-user slave", it would be worth > investigating using libvhost-user instead > (https://lists.gnu.org/archive/html/qemu-devel/2016-10/msg03990.html). > This is just a suggestion, it is quite fine for vhost-pci to have its > own smaller/specific vhost-user slave implementation (without virtio > rings handlings etc). (libvhost-user is also very young, not yet in > qemu, we should be able shape it for vhost-pci needs) Thanks for the suggestion. It looks possible. I will need to investigate more about it. libvhost-user is still at an initial stage and not in QEMU yet, I think we can probably have vhost-pci as an independent slave for now - this will not complicate libvhost-user when it's in the initial stage (it might not be good to make it a quite complex design at the beginning), and on the other side, the vhost-pci based slave implementation draft is already there. We can merge them if it is necessary when they both are stable in QEMU. What do you think? Best, Wei ^ permalink raw reply [flat|nested] 9+ messages in thread
* [Qemu-devel] [RFC PATCH 3/4] vhost-pci-net device support 2016-11-10 2:47 [Qemu-devel] [RFC PATCH 0/4] *** Half-cooked vhost-pci implementation patch *** Wei Wang 2016-11-10 2:47 ` [Qemu-devel] [RFC PATCH 1/4] qemu-char: add the "1-server-N-client" support Wei Wang 2016-11-10 2:47 ` [Qemu-devel] [RFC PATCH 2/4] vhost-user: add the vhost-user extension to support the vhost-pci based inter-vm communication Wei Wang @ 2016-11-10 2:47 ` Wei Wang 2016-11-10 2:47 ` [Qemu-devel] [RFC PATCH 4/4] vhost-user: extend the vhost-user Master (client) part to support vhost-pci Wei Wang 3 siblings, 0 replies; 9+ messages in thread From: Wei Wang @ 2016-11-10 2:47 UTC (permalink / raw) To: marcandre.lureau, mst, stefanha, pbonzini, qemu-devel, virtio-dev Cc: Wei Wang Signed-off-by: Wei Wang <wei.w.wang@intel.com> --- hw/net/Makefile.objs | 2 +- hw/net/vhost-pci-net.c | 142 +++++++++++++++++++++++++ hw/virtio/virtio-pci.c | 67 ++++++++++++ hw/virtio/virtio-pci.h | 14 +++ include/hw/pci/pci.h | 1 + include/hw/virtio/vhost-pci-net.h | 39 +++++++ include/standard-headers/linux/vhost_pci_net.h | 45 ++++++++ include/standard-headers/linux/virtio_ids.h | 1 + 8 files changed, 310 insertions(+), 1 deletion(-) create mode 100644 hw/net/vhost-pci-net.c create mode 100644 include/hw/virtio/vhost-pci-net.h create mode 100644 include/standard-headers/linux/vhost_pci_net.h diff --git a/hw/net/Makefile.objs b/hw/net/Makefile.objs index 610ed3e..71d6d2e 100644 --- a/hw/net/Makefile.objs +++ b/hw/net/Makefile.objs @@ -33,7 +33,7 @@ obj-$(CONFIG_MILKYMIST) += milkymist-minimac2.o obj-$(CONFIG_PSERIES) += spapr_llan.o obj-$(CONFIG_XILINX_ETHLITE) += xilinx_ethlite.o -obj-$(CONFIG_VIRTIO) += virtio-net.o +obj-$(CONFIG_VIRTIO) += virtio-net.o vhost-pci-net.o obj-y += vhost_net.o obj-$(CONFIG_ETSEC) += fsl_etsec/etsec.o fsl_etsec/registers.o \ diff --git a/hw/net/vhost-pci-net.c b/hw/net/vhost-pci-net.c new file mode 100644 index 0000000..3830caa --- /dev/null +++ b/hw/net/vhost-pci-net.c @@ -0,0 +1,142 @@ +/* + * vhost-pci-net support + * + * Copyright Intel, Inc. 2016 + * + * Authors: + * Wei Wang <wei.w.wang@intel.com> + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + * Contributions after 2012-01-13 are licensed under the terms of the + * GNU GPL, version 2 or (at your option) any later version. + */ + +#include "qemu/osdep.h" +#include "qemu/iov.h" +#include "hw/virtio/virtio.h" +#include "net/net.h" +#include "net/checksum.h" +#include "net/tap.h" +#include "qemu/error-report.h" +#include "qemu/timer.h" +#include "hw/virtio/virtio-net.h" +//#include "net/vhost_net.h" +#include "hw/virtio/virtio-bus.h" +#include "qapi/qmp/qjson.h" +#include "qapi-event.h" +#include "hw/virtio/virtio-access.h" +#include "hw/virtio/vhost-pci-net.h" + +void vhost_pci_net_set_max_rxqs(VhostPCINet *vpnet, uint16_t num) +{ + vpnet->max_rxq_num = num; +} + +static void vpnet_handle_input(VirtIODevice *vdev, VirtQueue *vq) +{ +} + +static void vpnet_handle_output(VirtIODevice *vdev, VirtQueue *vq) +{ +} + +static void vhost_pci_net_device_realize(DeviceState *dev, Error **errp) +{ + VirtIODevice *vdev = VIRTIO_DEVICE(dev); + VhostPCINet *vpnet = VHOST_PCI_NET(vdev); + int i; + + virtio_init(vdev, "vhost-pci-net", VIRTIO_ID_VHOST_PCI_NET, vpnet->config_size); + + /* control quque: host to guest */ + vpnet->cvq_rx = virtio_add_queue(vdev, 32, vpnet_handle_input); + /* control quque: guest to host */ + vpnet->cvq_tx = virtio_add_queue(vdev, 32, vpnet_handle_output); + + vpnet->rxqs = g_malloc0(sizeof(VirtQueue *) * vpnet->max_rxq_num); + for (i = 0; i < vpnet->max_rxq_num; i++) { + vpnet->rxqs[i] = virtio_add_queue(vdev, 256, vpnet_handle_output); + } +} + +static void vhost_pci_net_device_unrealize(DeviceState *dev, Error **errp) +{ +} + +static void vhost_pci_net_get_config(VirtIODevice *vdev, uint8_t *config) +{ +} + +static void vhost_pci_net_set_config(VirtIODevice *vdev, const uint8_t *config) +{ +} + +void vhost_pci_net_init_device_features(VhostPCINet *vpnet, uint64_t features) +{ + vpnet->device_features = features; +} + +static uint64_t vhost_pci_net_get_features(VirtIODevice *vdev, uint64_t features, Error **errp) +{ + VhostPCINet *vpnet = VHOST_PCI_NET(vdev); + + return vpnet->device_features; +} + +static void vhost_pci_net_set_features(VirtIODevice *vdev, uint64_t features) +{ +} + +static void vhost_pci_net_instance_init(Object *obj) +{ + VhostPCINet *vpnet = VHOST_PCI_NET(obj); + + /* + * The default config_size is sizeof(struct vhost_pci_net_config). + * Can be overriden with vhost_pci_net_set_config_size. + */ + vpnet->config_size = sizeof(struct vhost_pci_net_config); +} + +static Property vhost_pci_net_properties[] = { + DEFINE_PROP_END_OF_LIST(), +}; + +static void vhost_pci_net_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); + + dc->props = vhost_pci_net_properties; + set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); + vdc->realize = vhost_pci_net_device_realize; + vdc->unrealize = vhost_pci_net_device_unrealize; + vdc->get_config = vhost_pci_net_get_config; + vdc->set_config = vhost_pci_net_set_config; + vdc->get_features = vhost_pci_net_get_features; + vdc->set_features = vhost_pci_net_set_features; +// vdc->bad_features = vhost_pci_net_bad_features; +// vdc->reset = vhost_pci_net_reset; +// vdc->set_status = vhost_pci_net_set_status; +// vdc->guest_notifier_mask = vhost_pci_net_guest_notifier_mask; +// vdc->guest_notifier_pending = vhost_pci_net_guest_notifier_pending; +// vdc->load = vhost_pci_net_load_device; +// vdc->save = vhost_pci_net_save_device; +} + +static const TypeInfo vhost_pci_net_info = { + .name = TYPE_VHOST_PCI_NET, + .parent = TYPE_VIRTIO_DEVICE, + .instance_size = sizeof(VhostPCINet), + .instance_init = vhost_pci_net_instance_init, + .class_init = vhost_pci_net_class_init, +}; + +static void virtio_register_types(void) +{ + type_register_static(&vhost_pci_net_info); +} + +type_init(virtio_register_types) diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index 755f921..c4f6c72 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -25,6 +25,7 @@ #include "hw/virtio/virtio-scsi.h" #include "hw/virtio/virtio-balloon.h" #include "hw/virtio/virtio-input.h" +#include "hw/virtio/vhost-pci-net.h" #include "hw/pci/pci.h" #include "qapi/error.h" #include "qemu/error-report.h" @@ -2416,6 +2417,71 @@ static const TypeInfo virtio_host_pci_info = { }; #endif +/* vhost-pci-net */ + +static Property vhost_pci_net_pci_properties[] = { + DEFINE_PROP_END_OF_LIST(), +}; + +static void vhost_pci_net_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) +{ + DeviceState *qdev = DEVICE(vpci_dev); + VhostPCINetPCI *dev = VHOST_PCI_NET_PCI(vpci_dev); + DeviceState *vdev = DEVICE(&dev->vdev); + PeerConnectionTable *ent; + +// virtio_net_set_netclient_name(&dev->vdev, qdev->id, +// object_get_typename(OBJECT(qdev))); + qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus)); + + ent = vp_server_find_table_ent(qdev->id); + if (ent == NULL) + printf("%s called: no entry found \n", __func__); + + /* Sanity Check */ + if (ent->virtio_id != VIRTIO_ID_NET) + printf("%s called: device type doesn't match \n", __func__); + ent->bar_id = 2; + pci_register_bar(&vpci_dev->pci_dev, ent->bar_id, + PCI_BASE_ADDRESS_SPACE_MEMORY | + PCI_BASE_ADDRESS_MEM_PREFETCH | + PCI_BASE_ADDRESS_MEM_TYPE_64, + ent->bar_mr); + vhost_pci_net_set_max_rxqs(&dev->vdev, ent->vq_num / 2); + vhost_pci_net_init_device_features(&dev->vdev, ent->peer_feature_bits); + object_property_set_bool(OBJECT(vdev), true, "realized", errp); +} + +static void vhost_pci_net_pci_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + VirtioPCIClass *vpciklass = VIRTIO_PCI_CLASS(klass); + + k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; + k->device_id = PCI_DEVICE_ID_VHOST_PCI_NET; + k->class_id = PCI_CLASS_NETWORK_ETHERNET; + set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); + dc->props = vhost_pci_net_pci_properties; + vpciklass->realize = vhost_pci_net_pci_realize; +} + +static void vhost_pci_net_pci_instance_init(Object *obj) +{ + VhostPCINetPCI *dev = VHOST_PCI_NET_PCI(obj); + + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), + TYPE_VHOST_PCI_NET); +} + +static const TypeInfo vhost_pci_net_pci_info = { + .name = TYPE_VHOST_PCI_NET_PCI, + .parent = TYPE_VIRTIO_PCI, + .instance_size = sizeof(VhostPCINetPCI), + .instance_init = vhost_pci_net_pci_instance_init, + .class_init = vhost_pci_net_pci_class_init, +}; + /* virtio-pci-bus */ static void virtio_pci_bus_new(VirtioBusState *bus, size_t bus_size, @@ -2482,6 +2548,7 @@ static void virtio_pci_register_types(void) type_register_static(&virtio_balloon_pci_info); type_register_static(&virtio_serial_pci_info); type_register_static(&virtio_net_pci_info); + type_register_static(&vhost_pci_net_pci_info); #ifdef CONFIG_VHOST_SCSI type_register_static(&vhost_scsi_pci_info); #endif diff --git a/hw/virtio/virtio-pci.h b/hw/virtio/virtio-pci.h index 25fbf8a..0531b3f 100644 --- a/hw/virtio/virtio-pci.h +++ b/hw/virtio/virtio-pci.h @@ -25,6 +25,7 @@ #include "hw/virtio/virtio-bus.h" #include "hw/virtio/virtio-input.h" #include "hw/virtio/virtio-gpu.h" +#include "hw/virtio/vhost-pci-net.h" #ifdef CONFIG_VIRTFS #include "hw/9pfs/virtio-9p.h" #endif @@ -44,6 +45,7 @@ typedef struct VirtIOInputPCI VirtIOInputPCI; typedef struct VirtIOInputHIDPCI VirtIOInputHIDPCI; typedef struct VirtIOInputHostPCI VirtIOInputHostPCI; typedef struct VirtIOGPUPCI VirtIOGPUPCI; +typedef struct VhostPCINetPCI VhostPCINetPCI; /* virtio-pci-bus */ @@ -247,6 +249,18 @@ struct VirtIONetPCI { }; /* + * vhost-pci-net-pci: This extends VirtioPCIProxy. + */ +#define TYPE_VHOST_PCI_NET_PCI "vhost-pci-net-pci" +#define VHOST_PCI_NET_PCI(obj) \ + OBJECT_CHECK(VhostPCINetPCI, (obj), TYPE_VHOST_PCI_NET_PCI) + +struct VhostPCINetPCI { + VirtIOPCIProxy parent_obj; + VhostPCINet vdev; +}; + +/* * virtio-9p-pci: This extends VirtioPCIProxy. */ diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index 929ec2f..d25f031 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -79,6 +79,7 @@ #define PCI_DEVICE_ID_VIRTIO_SCSI 0x1004 #define PCI_DEVICE_ID_VIRTIO_RNG 0x1005 #define PCI_DEVICE_ID_VIRTIO_9P 0x1009 +#define PCI_DEVICE_ID_VHOST_PCI_NET 0x1023 #define PCI_VENDOR_ID_REDHAT 0x1b36 #define PCI_DEVICE_ID_REDHAT_BRIDGE 0x0001 diff --git a/include/hw/virtio/vhost-pci-net.h b/include/hw/virtio/vhost-pci-net.h new file mode 100644 index 0000000..b5f59ee --- /dev/null +++ b/include/hw/virtio/vhost-pci-net.h @@ -0,0 +1,39 @@ +/* + * Virtio Network Device + * + * Copyright Intel, Corp. 2016 + * + * Authors: + * Wei Wang <wei.w.wang@intel.com> + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ + +#ifndef _QEMU_VHOST_PCI_NET_H +#define _QEMU_VHOST_PCI_NET_H + +#include "standard-headers/linux/vhost_pci_net.h" +#include "hw/virtio/virtio.h" +#include "hw/virtio/vhost-pci-server.h" + +#define TYPE_VHOST_PCI_NET "vhost-pci-net-device" +#define VHOST_PCI_NET(obj) \ + OBJECT_CHECK(VhostPCINet, (obj), TYPE_VHOST_PCI_NET) + +typedef struct VhostPCINet { + VirtIODevice parent_obj; + + VirtQueue *cvq_rx, *cvq_tx; + VirtQueue **rxqs; + uint64_t device_features; + size_t config_size; + uint16_t max_rxq_num; +} VhostPCINet; + +void vhost_pci_net_set_max_rxqs(VhostPCINet *dev, uint16_t num); + +void vhost_pci_net_init_device_features(VhostPCINet *vpnet, uint64_t features); + +#endif diff --git a/include/standard-headers/linux/vhost_pci_net.h b/include/standard-headers/linux/vhost_pci_net.h new file mode 100644 index 0000000..8a92231 --- /dev/null +++ b/include/standard-headers/linux/vhost_pci_net.h @@ -0,0 +1,45 @@ +#ifndef _LINUX_VHOST_PCI_NET_H +#define _LINUX_VHOST_PCI_NET_H + +/* This header is BSD licensed so anyone can use the definitions to implement + * compatible drivers/servers. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Intel nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Intel OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. */ +#include "standard-headers/linux/types.h" +#include "standard-headers/linux/virtio_ids.h" +#include "standard-headers/linux/virtio_config.h" +#include "standard-headers/linux/virtio_types.h" +#include "standard-headers/linux/if_ether.h" + +struct vhost_pci_net_config { + /* Maximum number of receive queues; + * Should be equal to the number of the + * transmit queues of the peer device. + * Legal values are between 1 and 0x8000 + */ + uint16_t rq_num; +} QEMU_PACKED; + + +#endif diff --git a/include/standard-headers/linux/virtio_ids.h b/include/standard-headers/linux/virtio_ids.h index 77925f5..48f896d 100644 --- a/include/standard-headers/linux/virtio_ids.h +++ b/include/standard-headers/linux/virtio_ids.h @@ -41,5 +41,6 @@ #define VIRTIO_ID_CAIF 12 /* Virtio caif */ #define VIRTIO_ID_GPU 16 /* virtio GPU */ #define VIRTIO_ID_INPUT 18 /* virtio input */ +#define VIRTIO_ID_VHOST_PCI_NET 0x23 /* vhost-pci-net */ #endif /* _LINUX_VIRTIO_IDS_H */ -- 2.7.4 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* [Qemu-devel] [RFC PATCH 4/4] vhost-user: extend the vhost-user Master (client) part to support vhost-pci 2016-11-10 2:47 [Qemu-devel] [RFC PATCH 0/4] *** Half-cooked vhost-pci implementation patch *** Wei Wang ` (2 preceding siblings ...) 2016-11-10 2:47 ` [Qemu-devel] [RFC PATCH 3/4] vhost-pci-net device support Wei Wang @ 2016-11-10 2:47 ` Wei Wang 3 siblings, 0 replies; 9+ messages in thread From: Wei Wang @ 2016-11-10 2:47 UTC (permalink / raw) To: marcandre.lureau, mst, stefanha, pbonzini, qemu-devel, virtio-dev Cc: Wei Wang Signed-off-by: Wei Wang <wei.w.wang@intel.com> --- hw/net/vhost_net.c | 20 ++++++++++++ hw/virtio/vhost-user.c | 66 +++++++++++++++++++++++++++++++++++++++ include/hw/virtio/vhost-backend.h | 3 ++ include/net/vhost_net.h | 5 +++ 4 files changed, 94 insertions(+) diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c index f2d49ad..100349b 100644 --- a/hw/net/vhost_net.c +++ b/hw/net/vhost_net.c @@ -337,6 +337,9 @@ int vhost_net_start(VirtIODevice *dev, NetClientState *ncs, } } + vhost_set_peer_connection(ncs[i-1].peer, + VHOST_USER_SET_PEER_CONNECTION_INIT); + return 0; err_start: @@ -435,6 +438,18 @@ int vhost_set_vring_enable(NetClientState *nc, int enable) return 0; } +int vhost_set_peer_connection(NetClientState *nc, uint64_t cmd) +{ + VHostNetState *net = get_vhost_net(nc); + const VhostOps *vhost_ops = net->dev.vhost_ops; + + if (vhost_ops && vhost_ops->vhost_set_peer_connection) { + return vhost_ops->vhost_set_peer_connection(&net->dev, cmd); + } + + return 0; +} + #else uint64_t vhost_net_get_max_queues(VHostNetState *net) { @@ -501,4 +516,9 @@ int vhost_set_vring_enable(NetClientState *nc, int enable) { return 0; } + +int vhost_set_peer_connection(NetClientState *nc, uint64_t cmd) +{ + return 0; +} #endif diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index bce5181..6edf7f2 100644 --- a/hw/virtio/vhost-user.c +++ b/hw/virtio/vhost-user.c @@ -124,6 +124,8 @@ static int vhost_user_write(struct vhost_dev *dev, VhostUserMsg *msg, return 0; } + msg->conn_id = chr->conn_id; + if (qemu_chr_fe_set_msgfds(chr, fds, fd_num) < 0) { error_report("Failed to set msg fds."); return -1; @@ -313,6 +315,22 @@ static int vhost_user_set_vring_enable(struct vhost_dev *dev, int enable) return 0; } +static int vhost_user_set_peer_connection(struct vhost_dev *dev, uint64_t cmd) +{ + VhostUserMsg msg = { + .request = VHOST_USER_SET_PEER_CONNECTION, + .flags = VHOST_USER_VERSION, + .payload.u64 = cmd, + .size = sizeof(msg.payload.u64), + }; + + if (vhost_user_write(dev, &msg, NULL, 0) < 0) { + return -1; + } + + return 0; +} + static int vhost_user_get_vring_base(struct vhost_dev *dev, struct vhost_vring_state *ring) { @@ -448,6 +466,28 @@ static int vhost_user_get_u64(struct vhost_dev *dev, int request, uint64_t *u64) return 0; } +static bool vhost_user_need_conn_id(struct vhost_dev *dev) +{ + CharDriverState *chr = dev->opaque; + + if(chr->conn_id == ANONYMOUS_CLIENT) + return 1; + else + return 0; +} + +static int vhost_user_get_conn_id(struct vhost_dev *dev) +{ + int ret; + uint64_t conn_id; + CharDriverState *chr = dev->opaque; + + ret = vhost_user_get_u64(dev, VHOST_USER_GET_CONN_ID, &conn_id); + if (!ret) + chr->conn_id = conn_id; + return ret; +} + static int vhost_user_get_features(struct vhost_dev *dev, uint64_t *features) { return vhost_user_get_u64(dev, VHOST_USER_GET_FEATURES, features); @@ -481,6 +521,18 @@ static int vhost_user_reset_device(struct vhost_dev *dev) return 0; } +static int vhost_user_set_dev_info(struct vhost_dev *dev, uint16_t virtio_id) +{ + VhostUserMsg msg = { + .request = VHOST_USER_SET_DEV_INFO, + .flags = VHOST_USER_VERSION, + .payload.dev_info.virtio_id = virtio_id, + .size = sizeof(msg.payload.dev_info), + }; + + return vhost_user_write(dev, &msg, NULL, 0); +} + static int vhost_user_init(struct vhost_dev *dev, void *opaque) { uint64_t features; @@ -489,6 +541,12 @@ static int vhost_user_init(struct vhost_dev *dev, void *opaque) assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_USER); dev->opaque = opaque; + if (vhost_user_need_conn_id(dev)) { + err = vhost_user_get_conn_id(dev); + if (err < 0) { + return err; + } + } err = vhost_user_get_features(dev, &features); if (err < 0) { @@ -510,6 +568,13 @@ static int vhost_user_init(struct vhost_dev *dev, void *opaque) return err; } + if (dev->protocol_features & (1ULL << VHOST_USER_PROTOCOL_F_VHOST_PCI)) { + err = vhost_user_set_dev_info(dev, VIRTIO_ID_NET); + if (err < 0) { + return err; + } + } + /* query the max queues we support if backend supports Multiple Queue */ if (dev->protocol_features & (1ULL << VHOST_USER_PROTOCOL_F_MQ)) { err = vhost_user_get_u64(dev, VHOST_USER_GET_QUEUE_NUM, @@ -621,6 +686,7 @@ const VhostOps user_ops = { .vhost_reset_device = vhost_user_reset_device, .vhost_get_vq_index = vhost_user_get_vq_index, .vhost_set_vring_enable = vhost_user_set_vring_enable, + .vhost_set_peer_connection = vhost_user_set_peer_connection, .vhost_requires_shm_log = vhost_user_requires_shm_log, .vhost_migration_done = vhost_user_migration_done, .vhost_backend_can_merge = vhost_user_can_merge, diff --git a/include/hw/virtio/vhost-backend.h b/include/hw/virtio/vhost-backend.h index cf7f0b5..955dea6 100644 --- a/include/hw/virtio/vhost-backend.h +++ b/include/hw/virtio/vhost-backend.h @@ -67,6 +67,8 @@ typedef int (*vhost_reset_device_op)(struct vhost_dev *dev); typedef int (*vhost_get_vq_index_op)(struct vhost_dev *dev, int idx); typedef int (*vhost_set_vring_enable_op)(struct vhost_dev *dev, int enable); +typedef int (*vhost_set_peer_connection_op)(struct vhost_dev *dev, + uint64_t cmd); typedef bool (*vhost_requires_shm_log_op)(struct vhost_dev *dev); typedef int (*vhost_migration_done_op)(struct vhost_dev *dev, char *mac_addr); @@ -99,6 +101,7 @@ typedef struct VhostOps { vhost_reset_device_op vhost_reset_device; vhost_get_vq_index_op vhost_get_vq_index; vhost_set_vring_enable_op vhost_set_vring_enable; + vhost_set_peer_connection_op vhost_set_peer_connection; vhost_requires_shm_log_op vhost_requires_shm_log; vhost_migration_done_op vhost_migration_done; vhost_backend_can_merge_op vhost_backend_can_merge; diff --git a/include/net/vhost_net.h b/include/net/vhost_net.h index 5a08eff..baa5a34 100644 --- a/include/net/vhost_net.h +++ b/include/net/vhost_net.h @@ -33,6 +33,11 @@ VHostNetState *get_vhost_net(NetClientState *nc); int vhost_set_vring_enable(NetClientState * nc, int enable); +#define VHOST_USER_SET_PEER_CONNECTION_OFF 0 +#define VHOST_USER_SET_PEER_CONNECTION_ON 1 +#define VHOST_USER_SET_PEER_CONNECTION_INIT 2 +int vhost_set_peer_connection(NetClientState *nc, uint64_t cmd); + uint64_t vhost_net_get_acked_features(VHostNetState *net); #endif -- 2.7.4 ^ permalink raw reply related [flat|nested] 9+ messages in thread
end of thread, other threads:[~2016-11-11 8:28 UTC | newest] Thread overview: 9+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2016-11-10 2:47 [Qemu-devel] [RFC PATCH 0/4] *** Half-cooked vhost-pci implementation patch *** Wei Wang 2016-11-10 2:47 ` [Qemu-devel] [RFC PATCH 1/4] qemu-char: add the "1-server-N-client" support Wei Wang 2016-11-10 11:38 ` Marc-André Lureau 2016-11-11 8:28 ` Wei Wang 2016-11-10 2:47 ` [Qemu-devel] [RFC PATCH 2/4] vhost-user: add the vhost-user extension to support the vhost-pci based inter-vm communication Wei Wang 2016-11-10 11:36 ` Marc-André Lureau 2016-11-11 8:28 ` Wei Wang 2016-11-10 2:47 ` [Qemu-devel] [RFC PATCH 3/4] vhost-pci-net device support Wei Wang 2016-11-10 2:47 ` [Qemu-devel] [RFC PATCH 4/4] vhost-user: extend the vhost-user Master (client) part to support vhost-pci Wei Wang
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).