diff options
-rw-r--r-- | include/libvfio-user.h | 1 | ||||
-rw-r--r-- | lib/dma.c | 8 | ||||
-rw-r--r-- | lib/dma.h | 23 | ||||
-rw-r--r-- | test/unit-tests.c | 26 |
4 files changed, 44 insertions, 14 deletions
diff --git a/include/libvfio-user.h b/include/libvfio-user.h index 89e1c85..a39b5f4 100644 --- a/include/libvfio-user.h +++ b/include/libvfio-user.h @@ -67,6 +67,7 @@ typedef struct { int region; /* TODO replace region and length with struct iovec */ int length; uint64_t offset; + bool mappable; } dma_sg_t; typedef struct vfu_ctx vfu_ctx_t; @@ -375,13 +375,7 @@ _dma_addr_sg_split(const dma_controller_t *dma, size_t region_len = MIN(region_end - dma_addr, len); if (cnt < max_sg) { - sg[cnt].dma_addr = region->dma_addr; - sg[cnt].region = idx; - sg[cnt].offset = dma_addr - region->dma_addr; - sg[cnt].length = region_len; - if (_dma_should_mark_dirty(dma, prot)) { - _dma_mark_dirty(dma, region, sg); - } + dma_init_sg(dma, sg, dma_addr, region_len, prot, idx); } cnt++; @@ -181,6 +181,21 @@ _dma_mark_dirty(const dma_controller_t *dma, const dma_memory_region_t *region, } } +static inline void +dma_init_sg(const dma_controller_t *dma, dma_sg_t *sg, dma_addr_t dma_addr, + uint32_t len, int prot, int region_index) +{ + const dma_memory_region_t *const region = &dma->regions[region_index]; + sg->dma_addr = region->dma_addr; + sg->region = region_index; + sg->offset = dma_addr - region->dma_addr; + sg->length = len; + if (_dma_should_mark_dirty(dma, prot)) { + _dma_mark_dirty(dma, region, sg); + } + sg->mappable = region->virt_addr != NULL; +} + /* Takes a linear dma address span and returns a sg list suitable for DMA. * A single linear dma address span may need to be split into multiple * scatter gather regions due to limitations of how memory can be mapped. @@ -207,13 +222,7 @@ dma_addr_to_sg(const dma_controller_t *dma, if (likely(max_sg > 0 && len > 0 && dma_addr >= region->dma_addr && dma_addr + len <= region_end && region_hint < dma->nregions)) { - sg->dma_addr = region->dma_addr; - sg->region = region_hint; - sg->offset = dma_addr - region->dma_addr; - sg->length = len; - if (_dma_should_mark_dirty(dma, prot)) { - _dma_mark_dirty(dma, region, sg); - } + dma_init_sg(dma, sg, dma_addr, len, prot, region_hint); return 1; } // Slow path: search through regions. diff --git a/test/unit-tests.c b/test/unit-tests.c index 6355843..9063038 100644 --- a/test/unit-tests.c +++ b/test/unit-tests.c @@ -681,6 +681,31 @@ test_dma_map_sg(void **state __attribute__((unused))) } static void +test_dma_addr_to_sg(void **state __attribute__((unused))) +{ + dma_controller_t *dma = alloca(sizeof(dma_controller_t) + sizeof(dma_memory_region_t)); + dma_sg_t sg; + dma_memory_region_t *r; + + dma->nregions = 1; + r = &dma->regions[0]; + r->dma_addr = 0x1000; + r->size = 0x4000; + r->virt_addr = (void*)0xdeadbeef; + + /* fast path, region hint hit */ + assert_int_equal(1, + dma_addr_to_sg(dma, 0x2000, 0x400, &sg, 1, PROT_NONE)); + assert_int_equal(r->dma_addr, sg.dma_addr); + assert_int_equal(0, sg.region); + assert_int_equal(0x2000 - r->dma_addr, sg.offset); + assert_int_equal(0x400, sg.length); + assert_true(sg.mappable); + + /* TODO test more scenarios */ +} + +static void test_vfu_setup_device_dma_cb(void **state __attribute__((unused))) { vfu_ctx_t vfu_ctx = { 0 }; @@ -722,6 +747,7 @@ int main(void) cmocka_unit_test_setup(test_setup_sparse_region, setup), cmocka_unit_test_setup(test_dma_map_return_value, setup), cmocka_unit_test_setup(test_dma_map_sg, setup), + cmocka_unit_test_setup(test_dma_addr_to_sg, setup), cmocka_unit_test_setup(test_vfu_setup_device_dma_cb, setup) }; |