Skip to content
2 changes: 2 additions & 0 deletions include/vfio-user.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ struct vfio_user_dma_map {
uint32_t argsz;
#define VFIO_USER_F_DMA_REGION_READ (1 << 0)
#define VFIO_USER_F_DMA_REGION_WRITE (1 << 1)
#define VFIO_USER_F_DMA_REGION_MMAP (1 << 2)
Comment thread
jlevon marked this conversation as resolved.
#define VFIO_USER_F_DMA_REGION_FILE_IO (1 << 3)
uint32_t flags;
uint64_t offset;
uint64_t addr;
Expand Down
48 changes: 23 additions & 25 deletions lib/dma.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ dma_sg_size(void)

bool
dma_sg_is_mappable(const dma_sg_t *sg) {
return sg->region->info.vaddr != NULL;
return sg->region->access_mode == REGION_ACCESS_MODE_MMAP;
}

static inline ssize_t
Expand Down Expand Up @@ -132,11 +132,6 @@ MOCK_DEFINE(dma_controller_unmap_region)(dma_controller_t *dma,
region->fd, region->info.mapping.iov_base,
iov_end(&region->info.mapping));
}

assert(region->fd != -1);

err = fd_cache_put(&region->fd);
assert(err == 0);
}

/* FIXME not thread safe */
Expand All @@ -148,6 +143,7 @@ MOCK_DEFINE(dma_controller_remove_region)(dma_controller_t *dma,
{
dma_memory_region_t *region;
btree_iter_t iter;
int err;

assert(dma != NULL);

Expand All @@ -170,12 +166,12 @@ MOCK_DEFINE(dma_controller_remove_region)(dma_controller_t *dma,

if (region->info.vaddr != NULL) {
dma_controller_unmap_region(dma, region);
} else {
assert(region->fd == -1);
}

btree_iter_remove(&iter);
dma_controller_increment_regions_generation(dma);
err = fd_cache_put(&region->fd);
Comment thread
mnissler-rivos marked this conversation as resolved.
Outdated
assert(err == 0);
free(region);

return 0;
Expand All @@ -188,6 +184,7 @@ dma_controller_remove_all_regions(dma_controller_t *dma,
{
dma_memory_region_t *region = NULL;
btree_iter_t iter;
int err;

assert(dma != NULL);

Expand All @@ -208,10 +205,10 @@ dma_controller_remove_all_regions(dma_controller_t *dma,

if (region->info.vaddr != NULL) {
Comment thread
mnissler-rivos marked this conversation as resolved.
Outdated
dma_controller_unmap_region(dma, region);
} else {
assert(region->fd == -1);
}

err = fd_cache_put(&region->fd);
assert(err == 0);
free(region);
}
}
Expand Down Expand Up @@ -261,7 +258,7 @@ dma_map_region(dma_controller_t *dma, dma_memory_region_t *region)
static int
dirty_page_logging_start_on_region(dma_memory_region_t *region, size_t pgsize)
{
assert(region->fd != -1);
assert(region->access_mode != REGION_ACCESS_MODE_MSG);

ssize_t size = get_bitmap_size(region->info.iova.iov_len, pgsize);
if (size < 0) {
Expand All @@ -278,7 +275,8 @@ dirty_page_logging_start_on_region(dma_memory_region_t *region, size_t pgsize)
dma_memory_region_t *
MOCK_DEFINE(dma_controller_add_region)(dma_controller_t *dma,
vfu_dma_addr_t dma_addr, uint64_t size,
int fd, off_t offset, uint32_t prot)
int fd, off_t offset, uint32_t prot,
enum region_access_mode access_mode)
{
dma_memory_region_t *existing = NULL;
dma_memory_region_t *region = NULL;
Expand Down Expand Up @@ -342,7 +340,7 @@ MOCK_DEFINE(dma_controller_add_region)(dma_controller_t *dma,
return ERROR_PTR(EINVAL);
}

if (fd != -1) {
if (access_mode != REGION_ACCESS_MODE_MSG) {
page_size = fd_get_blocksize(fd);
Comment thread
mnissler-rivos marked this conversation as resolved.
Outdated
if (page_size < 0) {
vfu_log(dma->vfu_ctx, LOG_ERR, "bad page size %d", page_size);
Expand All @@ -369,10 +367,11 @@ MOCK_DEFINE(dma_controller_add_region)(dma_controller_t *dma,
region->info.iova.iov_len = size;
region->info.page_size = page_size;
region->info.prot = prot;
region->access_mode = access_mode;
region->offset = offset;
region->fd = fd;

if (fd != -1) {
if (access_mode != REGION_ACCESS_MODE_MSG) {
/*
* TODO introduce a function that tells whether dirty page logging is
* enabled
Expand All @@ -387,15 +386,14 @@ MOCK_DEFINE(dma_controller_add_region)(dma_controller_t *dma,
}
}

ret = dma_map_region(dma, region);
if (access_mode == REGION_ACCESS_MODE_MMAP) {
ret = dma_map_region(dma, region);

if (ret != 0) {
vfu_log(dma->vfu_ctx, LOG_ERR,
"failed to memory map DMA region %s: %m", rstr);
goto rollback;
} else {
/* Ownership of the fd is now with the region. */
fd = -1;
if (ret != 0) {
vfu_log(dma->vfu_ctx, LOG_ERR,
"failed to memory map DMA region %s: %m", rstr);
goto rollback;
}
}
}

Expand Down Expand Up @@ -503,7 +501,7 @@ dma_controller_dirty_page_logging_start(dma_controller_t *dma, size_t pgsize)
for (btree_iter_init(&dma->regions, 0, &iter);
(region = btree_iter_get(&iter, NULL)) != NULL;
btree_iter_next(&iter)) {
if (region->fd == -1) {
if (region->access_mode == REGION_ACCESS_MODE_MSG) {
continue;
}

Expand Down Expand Up @@ -766,8 +764,8 @@ dma_controller_dirty_page_get(dma_controller_t *dma, vfu_dma_addr_t addr,

region = sg.region;

if (region->fd == -1) {
vfu_log(dma->vfu_ctx, LOG_ERR, "region [%p-%p] is not mapped",
if (region->access_mode == REGION_ACCESS_MODE_MSG) {
vfu_log(dma->vfu_ctx, LOG_ERR, "region [%p-%p] isn't accessed directly",
region->info.iova.iov_base, iov_end(&region->info.iova));
return ERROR_INT(EINVAL);
}
Expand Down
18 changes: 15 additions & 3 deletions lib/dma.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,20 @@

struct vfu_ctx;

/* Designates how to access a DMA target region */
enum region_access_mode {
/* Use VFIO_USER_DMA_{READ,WRITE} messages on socket */
REGION_ACCESS_MODE_MSG,
/* mmap() provided file descriptor */
REGION_ACCESS_MODE_MMAP,
/* pread()/pwrite() on provided file descriptor */
REGION_ACCESS_MODE_FILE_IO,
};

typedef struct {
vfu_dma_info_t info;
int fd; // File descriptor to mmap
enum region_access_mode access_mode;
int fd; // File descriptor for direct access
off_t offset; // File offset
uint8_t *dirty_bitmap; // Dirty page bitmap
} dma_memory_region_t;
Expand Down Expand Up @@ -127,7 +138,8 @@ dma_controller_destroy(dma_controller_t *dma);
*/
MOCK_DECLARE(dma_memory_region_t *, dma_controller_add_region,
dma_controller_t *dma, vfu_dma_addr_t dma_addr, uint64_t size,
int fd, off_t offset, uint32_t prot);
int fd, off_t offset, uint32_t prot,
enum region_access_mode access_mode);

MOCK_DECLARE(int, dma_controller_remove_region, dma_controller_t *dma,
vfu_dma_addr_t dma_addr, size_t size,
Expand Down Expand Up @@ -293,7 +305,7 @@ dma_sgl_get(dma_controller_t *dma, dma_sg_t *sgl, struct iovec *iov, size_t cnt)
return ERROR_INT(EINVAL);
}

if (sg->region->info.vaddr == NULL) {
if (sg->region->access_mode != REGION_ACCESS_MODE_MMAP) {
return ERROR_INT(EFAULT);
}

Expand Down
66 changes: 59 additions & 7 deletions lib/libvfio-user.c
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,7 @@ int
handle_dma_map(vfu_ctx_t *vfu_ctx, vfu_msg_t *msg,
struct vfio_user_dma_map *dma_map)
{
enum region_access_mode access_mode;
dma_memory_region_t *region;
char rstr[1024];
int fd = -1;
Expand All @@ -699,6 +700,14 @@ handle_dma_map(vfu_ctx_t *vfu_ctx, vfu_msg_t *msg,

vfu_log(vfu_ctx, LOG_DEBUG, "adding DMA region %s", rstr);

if (msg->in.nr_fds > 0) {
fd = consume_fd(msg->in.fds, msg->in.nr_fds, 0);
if (fd < 0) {
vfu_log(vfu_ctx, LOG_ERR, "failed to add DMA region %s: %m", rstr);
return -1;
}
}

if (dma_map->flags & VFIO_USER_F_DMA_REGION_READ) {
prot |= PROT_READ;
dma_map->flags &= ~VFIO_USER_F_DMA_REGION_READ;
Expand All @@ -709,22 +718,31 @@ handle_dma_map(vfu_ctx_t *vfu_ctx, vfu_msg_t *msg,
dma_map->flags &= ~VFIO_USER_F_DMA_REGION_WRITE;
}

/* If not specified, default to mmap()-ed access if an fd is provided. */
access_mode = fd != -1 ? REGION_ACCESS_MODE_MMAP : REGION_ACCESS_MODE_MSG;
Comment thread
mnissler-rivos marked this conversation as resolved.
Outdated
if (dma_map->flags & VFIO_USER_F_DMA_REGION_MMAP) {
access_mode = REGION_ACCESS_MODE_MMAP;
dma_map->flags &= ~VFIO_USER_F_DMA_REGION_MMAP;
} else if (dma_map->flags & VFIO_USER_F_DMA_REGION_FILE_IO) {
access_mode = REGION_ACCESS_MODE_FILE_IO;
dma_map->flags &= ~VFIO_USER_F_DMA_REGION_FILE_IO;
}

if (dma_map->flags != 0) {
vfu_log(vfu_ctx, LOG_ERR, "bad flags=%#x", dma_map->flags);
close_safely(&fd);
return ERROR_INT(EINVAL);
}

if (msg->in.nr_fds > 0) {
fd = consume_fd(msg->in.fds, msg->in.nr_fds, 0);
if (fd < 0) {
vfu_log(vfu_ctx, LOG_ERR, "failed to add DMA region %s: %m", rstr);
return -1;
}
if (access_mode != REGION_ACCESS_MODE_MSG && fd == -1) {
Comment thread
mnissler-rivos marked this conversation as resolved.
Outdated
vfu_log(vfu_ctx, LOG_ERR, "file descriptor required for mode=%u",
access_mode);
return ERROR_INT(EINVAL);
}

region = dma_controller_add_region(
vfu_ctx->dma, (vfu_dma_addr_t)(uintptr_t)dma_map->addr, dma_map->size,
fd, dma_map->offset, prot);
fd, dma_map->offset, prot, access_mode);
if (region == NULL) {
vfu_log(vfu_ctx, LOG_ERR, "failed to add DMA region %s: %m", rstr);
close_safely(&fd);
Expand Down Expand Up @@ -2395,6 +2413,40 @@ vfu_dma_transfer(vfu_ctx_t *vfu_ctx, enum vfio_user_command cmd,
return ERROR_INT(EPERM);
}

if (sg->region->access_mode == REGION_ACCESS_MODE_MMAP) {
Comment thread
mnissler-rivos marked this conversation as resolved.
Outdated
void *target, *src, *dst;
assert(sg->region->info.vaddr != NULL);
target = sg->region->info.vaddr + sg->offset;
src = cmd == VFIO_USER_DMA_READ ? target : data;
dst = cmd == VFIO_USER_DMA_READ ? data : target;
memcpy(dst, src, sg->length);
return 0;
}

if (sg->region->access_mode == REGION_ACCESS_MODE_FILE_IO) {
size_t length, offset;
assert(sg->region->fd != -1);
length = sg->length;
offset = sg->offset + sg->region->offset;
while (length > 0) {
ssize_t ret;
if (cmd == VFIO_USER_DMA_READ) {
ret = pread(sg->region->fd, data, length, offset);
} else {
ret = pwrite(sg->region->fd, data, length, offset);
}
if (ret <= 0) {
return ERROR_INT(EIO);
}
data += ret;
offset += ret;
length -= ret;
}
return 0;
}

assert(sg->region->access_mode == REGION_ACCESS_MODE_MSG);

rlen = sizeof(struct vfio_user_dma_region_access) +
MIN(sg->length, vfu_ctx->client_max_data_xfer_size);

Expand Down
9 changes: 5 additions & 4 deletions test/mocks.c
Original file line number Diff line number Diff line change
Expand Up @@ -112,13 +112,13 @@ unpatch_all(void)
}

dma_memory_region_t *
dma_controller_add_region(dma_controller_t *dma, void *dma_addr,
uint64_t size, int fd, off_t offset,
uint32_t prot)
dma_controller_add_region(dma_controller_t *dma, void *dma_addr, uint64_t size,
int fd, off_t offset, uint32_t prot,
enum region_access_mode access_mode)
{
if (!is_patched("dma_controller_add_region")) {
return __real_dma_controller_add_region(dma, dma_addr, size, fd, offset,
prot);
prot, access_mode);
}

check_expected_ptr(dma);
Expand All @@ -127,6 +127,7 @@ dma_controller_add_region(dma_controller_t *dma, void *dma_addr,
check_expected(fd);
check_expected(offset);
check_expected(prot);
check_expected(access_mode);
errno = mock();
return mock_ptr_type(dma_memory_region_t *);
}
Expand Down
Loading
Loading