* [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
* [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
* [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
* 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 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 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
* 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
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).