diff options
author | Thanos Makatos <thanos.makatos@nutanix.com> | 2020-09-29 11:35:29 -0400 |
---|---|---|
committer | Thanos Makatos <thanos.makatos@nutanix.com> | 2020-09-29 11:35:29 -0400 |
commit | 2ea402b215f89a29a8e5163749456588856cf9c9 (patch) | |
tree | f91a035f0f8c357e308a38533883576c171fc9db | |
parent | 2ac55250a44832bd9e0b585e62e1e4217dda71be (diff) | |
download | libvfio-user-2ea402b215f89a29a8e5163749456588856cf9c9.zip libvfio-user-2ea402b215f89a29a8e5163749456588856cf9c9.tar.gz libvfio-user-2ea402b215f89a29a8e5163749456588856cf9c9.tar.bz2 |
implement VFIO_USER_REGION_READ/VFIO_USER_REGION_WRITE
Signed-off-by: Thanos Makatos <thanos.makatos@nutanix.com>
-rw-r--r-- | lib/libmuser.c | 87 | ||||
-rw-r--r-- | samples/client.c | 71 | ||||
-rw-r--r-- | samples/server.c | 53 |
3 files changed, 180 insertions, 31 deletions
diff --git a/lib/libmuser.c b/lib/libmuser.c index ffd605d..8806d98 100644 --- a/lib/libmuser.c +++ b/lib/libmuser.c @@ -1539,7 +1539,8 @@ lm_access(lm_ctx_t *lm_ctx, char *buf, size_t count, loff_t *ppos, } static inline int -muser_access(lm_ctx_t *lm_ctx, struct muser_cmd *cmd, bool is_write) +muser_access(lm_ctx_t *lm_ctx, struct muser_cmd *cmd, bool is_write, + void **data) { char *rwbuf; int err; @@ -1554,7 +1555,7 @@ muser_access(lm_ctx_t *lm_ctx, struct muser_cmd *cmd, bool is_write) } lm_log(lm_ctx, LM_DBG, "%s %#lx-%#lx\n", is_write ? "W" : "R", cmd->rw.pos, - cmd->rw.pos + cmd->rw.count); + cmd->rw.pos + cmd->rw.count - 1); /* copy data to be written from kernel to user space */ if (is_write) { @@ -1594,18 +1595,23 @@ muser_access(lm_ctx_t *lm_ctx, struct muser_cmd *cmd, bool is_write) is_write); if (!is_write && ret >= 0) { ret += count; - err = post_read(lm_ctx, rwbuf, ret); + if (data != NULL) { + *data = rwbuf; + return ret; + } else { + err = post_read(lm_ctx, rwbuf, ret); #ifdef LM_VERBOSE_LOGGING - if (err == ret) { - dump_buffer("buffer read", rwbuf, ret); - } + if (err == ret) { + dump_buffer("buffer read", rwbuf, ret); + } #endif + } } out: free(rwbuf); - return err; + return ret; } static int @@ -1849,6 +1855,59 @@ handle_dma_map_or_unmap(lm_ctx_t *lm_ctx, struct vfio_user_header *hdr, bool map return 0; } +static int +handle_region_access(lm_ctx_t *lm_ctx, struct vfio_user_header *hdr, + void **data, int *len) +{ + struct vfio_user_region_access region_access; + struct muser_cmd muser_cmd = {}; + int ret; + + assert(lm_ctx != NULL); + assert(hdr != NULL); + assert(data != NULL); + + /* + * TODO if muser_access doesn't need to handle the kernel case, then we can + * avoid having to do an additional read/recv inside muser_access (one recv + * for struct region_access and another for the write data) by doing a + * single recvmsg here with an iovec where the first element of the array + * will be struct vfio_user_region_access and the second a buffer if it's a + * write. The size of the write buffer is: + * hdr->msg_size - sizeof *hdr - sizeof region_access, + * and should be equal to region_access.count. + */ + + hdr->msg_size -= sizeof *hdr; + if (hdr->msg_size < sizeof region_access) { + return -EINVAL; + } + + ret = recv(lm_ctx->conn_fd, ®ion_access, sizeof region_access, 0); + if (ret == -1) { + return -errno; + } + if (ret != sizeof region_access) { + return -EINVAL; + } + if (region_access.region >= LM_DEV_NUM_REGS || region_access.count <= 0 ) { + return -EINVAL; + } + muser_cmd.rw.count = region_access.count; + muser_cmd.rw.pos = region_to_offset(region_access.region) + region_access.offset; + + ret = muser_access(lm_ctx, &muser_cmd, hdr->cmd == VFIO_USER_REGION_WRITE, + data); + if (ret != (int)region_access.count) { + assert(false); /* FIXME */ + } + assert(muser_cmd.err == 0); + + *len = region_access.count; + + return 0; +} + /* * FIXME return value is messed up, sometimes we return -1 and set errno while * other times we return -errno. Fix. @@ -1864,7 +1923,8 @@ process_request(lm_ctx_t *lm_ctx) struct vfio_irq_info irq_info; struct vfio_device_info dev_info; void *data = NULL; - int len; + bool free_data = false; + int len; assert(lm_ctx != NULL); @@ -1894,7 +1954,7 @@ process_request(lm_ctx_t *lm_ctx) } if (ret < (int)sizeof hdr) { - lm_log(lm_ctx, LM_ERR, "short header read"); + lm_log(lm_ctx, LM_ERR, "short header read %d", ret); return -EINVAL; } @@ -1932,6 +1992,11 @@ process_request(lm_ctx_t *lm_ctx) case VFIO_USER_DEVICE_SET_IRQS: ret = handle_device_set_irqs(lm_ctx, &hdr, fds, nr_fds); break; + case VFIO_USER_REGION_READ: + case VFIO_USER_REGION_WRITE: + ret = handle_region_access(lm_ctx, &hdr, &data, &len); + free_data = true; + break; default: lm_log(lm_ctx, LM_ERR, "bad command %d", hdr.cmd); return -EINVAL; @@ -1947,7 +2012,9 @@ process_request(lm_ctx_t *lm_ctx) lm_log(lm_ctx, LM_ERR, "failed to complete command: %s\n", strerror(-ret)); } - + if (free_data) { + free(data); + } return ret; } diff --git a/samples/client.c b/samples/client.c index 91ab102..1a336373 100644 --- a/samples/client.c +++ b/samples/client.c @@ -36,6 +36,8 @@ #include <errno.h> #include <sys/mman.h> #include <sys/eventfd.h> +#include <time.h> +#include <err.h> #include "../lib/muser.h" #include "../lib/muser_priv.h" @@ -189,6 +191,46 @@ configure_irqs(int sock) return 0; } +static int +access_bar0(int sock) +{ + struct { + struct vfio_user_region_access region_access; + time_t t; + } __attribute__((packed)) data = { + .region_access = { + .region = LM_DEV_BAR0_REG_IDX, + .count = sizeof(data.t) + }, + .t = time(NULL) + }; + uint16_t msg_id = 1; + int ret = send_recv_vfio_user_msg(sock, msg_id, VFIO_USER_REGION_WRITE, + &data, sizeof data, NULL, 0, NULL, NULL, 0); + if (ret < 0) { + fprintf(stderr, "failed to write to BAR0: %s\n", strerror(-ret)); + return ret; + } + + printf("wrote to BAR0: %ld\n", data.t); + + sleep(2); + + msg_id++; + + ret = send_recv_vfio_user_msg(sock, msg_id, VFIO_USER_REGION_READ, + &data.region_access, sizeof data.region_access, + NULL, 0, NULL, &data.t, sizeof data.t); + if (ret < 0) { + fprintf(stderr, "failed to read from BAR0: %s\n", strerror(-ret)); + return ret; + } + + printf("read from BAR0: %ld\n", data.t); + + return 0; +} + int main(int argc, char *argv[]) { int ret, sock; @@ -205,7 +247,7 @@ int main(int argc, char *argv[]) if (argc != 2) { fprintf(stderr, "usage: %s /path/to/socket\n", argv[0]); - return -1; + exit(EXIT_FAILURE); } if ((sock = init_sock(argv[1])) < 0) { @@ -238,13 +280,11 @@ int main(int argc, char *argv[]) nr_dma_regions = server_max_fds << 1; if ((fp = tmpfile()) == NULL) { - perror("failed to create DMA file"); - return -1; + err(EXIT_FAILURE, "failed to create DMA file"); } if ((ret = ftruncate(fileno(fp), nr_dma_regions * sysconf(_SC_PAGESIZE))) == -1) { - perror("failed to truncate file"); - return -1; + err(EXIT_FAILURE,"failed to truncate file"); } dma_regions = alloca(sizeof *dma_regions * nr_dma_regions); @@ -296,6 +336,27 @@ int main(int argc, char *argv[]) exit(EXIT_FAILURE); } + /* + * XXX VFIO_USER_REGION_READ and VFIO_USER_REGION_WRITE + * + * BAR0 in the server does not support memory mapping so it must be accessed + * via explicit messages. + */ + ret = access_bar0(sock); + if (ret < 0) { + fprintf(stderr, "failed to access BAR0: %s\n", strerror(-ret)); + exit(EXIT_FAILURE); + } + + /* + * FIXME now that region read/write works, change the server implementation + * to trigger an interrupt after N seconds, where N is the value written to + * BAR0 by the client. + */ + + /* BAR1 can be memory mapped and read directly */ + /* TODO implement the following: write a value in BAR1, a server timer will increase it every second (SIGALARM) */ + return 0; } diff --git a/samples/server.c b/samples/server.c index 6ebb65d..deb986b 100644 --- a/samples/server.c +++ b/samples/server.c @@ -41,6 +41,11 @@ #include "../lib/muser.h" +struct server_data { + time_t bar0; + uint8_t *bar1; +}; + static void _log(void *pvt, lm_log_lvl_t lvl __attribute__((unused)), char const *msg) { @@ -52,29 +57,35 @@ ssize_t bar0_access(void *pvt, char * const buf, size_t count, loff_t offset, const bool is_write) { - time_t t = time(NULL); + struct server_data *server_data = pvt; if (count != sizeof(time_t) || offset != 0) { errno = EINVAL; return -1; } - memcpy(buf, &t, count); + if (is_write) { + memcpy(&server_data->bar0, buf, count); + } else { + time_t delta = time(NULL) - server_data->bar0; + memcpy(buf, &delta, count); + } return count; } -lm_ctx_t *lm_ctx; +ssize_t +bar1_access(void *pvt, char * const buf, size_t count, loff_t offset, + const bool is_write) +{ + assert(false); +} bool irq_triggered = false; static void _sa_handler(int signum) { int _errno = errno; if (signum == SIGUSR1) { - /* - * FIXME not async-signal-safe becasue lm_irq_trigger prints to the log - */ - lm_irq_trigger(lm_ctx, 0); irq_triggered = true; } errno = _errno; @@ -92,7 +103,8 @@ int main(int argc, char *argv[]) bool trans_sock = false, verbose = false; char opt; struct sigaction act = {.sa_handler = _sa_handler}; - lm_ctx_t **_lm_ctx; + struct server_data server_data; + lm_ctx_t *lm_ctx; while ((opt = getopt(argc, argv, "v")) != -1) { switch (opt) { @@ -100,8 +112,7 @@ int main(int argc, char *argv[]) verbose = true; break; default: /* '?' */ - fprintf(stderr, "Usage: %s [-d] <IOMMU group>\n", argv[0]); - exit(EXIT_FAILURE); + err(EXIT_FAILURE, "Usage: %s [-d] <IOMMU group>\n", argv[0]); } } @@ -109,6 +120,11 @@ int main(int argc, char *argv[]) err(EXIT_FAILURE, "missing MUSER device UUID"); } + server_data.bar1 = malloc(sysconf(_SC_PAGESIZE)); + if (server_data.bar1 == NULL) { + err(EXIT_FAILURE, "BAR1"); + } + lm_dev_info_t dev_info = { .trans = LM_TRANS_SOCK, .log = verbose ? _log : NULL, @@ -119,16 +135,21 @@ int main(int argc, char *argv[]) .size = sizeof(time_t), .fn = &bar0_access }, + .reg_info[LM_DEV_BAR1_REG_IDX] = { + .flags = LM_REG_FLAG_RW, + .size = sysconf(_SC_PAGESIZE), + .fn = &bar1_access + }, .irq_count[LM_DEV_INTX_IRQ_IDX] = 1, }, .uuid = argv[optind], .unmap_dma = unmap_dma, - .pvt = &_lm_ctx + .pvt = &server_data }; sigemptyset(&act.sa_mask); if (sigaction(SIGUSR1, &act, NULL) == -1) { - fprintf(stderr, "warning: failed to register signal handler: %m\n"); + err(EXIT_FAILURE, "warning: failed to register signal handler: %m\n"); } lm_ctx = lm_ctx_create(&dev_info); @@ -136,16 +157,16 @@ int main(int argc, char *argv[]) if (errno == EINTR) { goto out; } - fprintf(stderr, "failed to initialize device emulation: %m\n"); - return -1; + err(EXIT_FAILURE, "failed to initialize device emulation: %m\n"); } - _lm_ctx = &lm_ctx; + do { ret = lm_ctx_drive(lm_ctx); if (ret == -EINTR) { if (irq_triggered) { - ret = 0; irq_triggered = false; + lm_irq_trigger(lm_ctx, 0); + ret = 0; } } } while (ret == 0); |