aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/dma.h48
-rw-r--r--test/unit-tests.c60
2 files changed, 78 insertions, 30 deletions
diff --git a/lib/dma.h b/lib/dma.h
index 895e14f..190a016 100644
--- a/lib/dma.h
+++ b/lib/dma.h
@@ -229,35 +229,38 @@ dma_map_sg(dma_controller_t *dma, dma_sg_t *sg, struct iovec *iov,
int cnt)
{
dma_memory_region_t *region;
- int i;
assert(dma != NULL);
assert(sg != NULL);
assert(iov != NULL);
+ assert(cnt > 0);
- for (i = 0; i < cnt; i++) {
- if (sg[i].region >= dma->nregions) {
+ do {
+ if (sg->region >= dma->nregions) {
return ERROR_INT(EINVAL);
}
- region = &dma->regions[sg[i].region];
+ region = &dma->regions[sg->region];
if (region->info.vaddr == NULL) {
return ERROR_INT(EFAULT);
}
- if (sg[i].writeable) {
+ if (sg->writeable) {
if (dma->dirty_pgsize > 0) {
- _dma_mark_dirty(dma, region, &sg[i]);
+ _dma_mark_dirty(dma, region, sg);
}
- LIST_INSERT_HEAD(&dma->maps, &sg[i], entry);
+ LIST_INSERT_HEAD(&dma->maps, sg, entry);
}
vfu_log(dma->vfu_ctx, LOG_DEBUG, "map %p-%p",
- sg[i].dma_addr + sg[i].offset,
- sg[i].dma_addr + sg[i].offset + sg[i].length);
- iov[i].iov_base = region->info.vaddr + sg[i].offset;
- iov[i].iov_len = sg[i].length;
+ sg->dma_addr + sg->offset,
+ sg->dma_addr + sg->offset + sg->length);
+ iov->iov_base = region->info.vaddr + sg->offset;
+ iov->iov_len = sg->length;
region->refcnt++;
- }
+
+ sg++;
+ iov++;
+ } while (--cnt > 0);
return 0;
}
@@ -266,9 +269,13 @@ static inline void
dma_unmap_sg(dma_controller_t *dma, const dma_sg_t *sg,
UNUSED struct iovec *iov, int cnt)
{
- int i;
- for (i = 0; i < cnt; i++) {
+ assert(dma != NULL);
+ assert(sg != NULL);
+ assert(iov != NULL);
+ assert(cnt > 0);
+
+ do {
dma_memory_region_t *r;
/*
* FIXME this double loop will be removed if we replace the array with
@@ -276,20 +283,21 @@ dma_unmap_sg(dma_controller_t *dma, const dma_sg_t *sg,
*/
for (r = dma->regions;
r < dma->regions + dma->nregions &&
- r->info.iova.iov_base != sg[i].dma_addr;
+ r->info.iova.iov_base != sg->dma_addr;
r++);
if (r > dma->regions + dma->nregions) {
/* bad region */
continue;
}
- if (sg[i].writeable) {
- LIST_REMOVE(&sg[i], entry);
+ if (sg->writeable) {
+ LIST_REMOVE(sg, entry);
}
vfu_log(dma->vfu_ctx, LOG_DEBUG, "unmap %p-%p",
- sg[i].dma_addr + sg[i].offset,
- sg[i].dma_addr + sg[i].offset + sg[i].length);
+ sg->dma_addr + sg->offset,
+ sg->dma_addr + sg->offset + sg->length);
r->refcnt--;
- }
+ sg++;
+ } while (--cnt > 0);
}
int
diff --git a/test/unit-tests.c b/test/unit-tests.c
index bcfd348..b605728 100644
--- a/test/unit-tests.c
+++ b/test/unit-tests.c
@@ -360,28 +360,67 @@ test_dma_controller_remove_region_unmapped(void **state UNUSED)
static void
test_dma_map_sg(void **state UNUSED)
{
- dma_sg_t sg = { .region = 1 };
- struct iovec iovec = { 0 };
+ dma_sg_t sg[] = { {.region = 0}, {.region = 1}, {.region = 2} };
+ struct iovec iovec[] = { {0}, {0}, {0} };
/* bad region */
- assert_int_equal(-1, dma_map_sg(vfu_ctx.dma, &sg, &iovec, 1));
+ assert_int_equal(-1, dma_map_sg(vfu_ctx.dma, &sg[0], &iovec[0], 1));
assert_int_equal(EINVAL, errno);
vfu_ctx.dma->nregions = 1;
/* w/o fd */
- sg.region = 0;
- assert_int_equal(-1, dma_map_sg(vfu_ctx.dma, &sg, &iovec, 1));
+ sg[0].region = 0;
+ assert_int_equal(-1, dma_map_sg(vfu_ctx.dma, &sg[0], &iovec[0], 1));
assert_int_equal(EFAULT, errno);
/* w/ fd */
vfu_ctx.dma->regions[0].info.vaddr = (void *)0xdead0000;
- sg.offset = 0x0000beef;
- sg.length = 0xcafebabe;
- assert_int_equal(0, dma_map_sg(vfu_ctx.dma, &sg, &iovec, 1));
- assert_int_equal(0xdeadbeef, iovec.iov_base);
- assert_int_equal(0x00000000cafebabe, iovec.iov_len);
+ sg[0].offset = 0x0000beef;
+ sg[0].length = 0xcafebabe;
+ assert_int_equal(0, dma_map_sg(vfu_ctx.dma, &sg[0], &iovec[0], 1));
+ assert_int_equal(0xdeadbeef, iovec[0].iov_base);
+ assert_int_equal(0x00000000cafebabe, iovec[0].iov_len);
+
+ /* multiple sg entries */
+ vfu_ctx.dma->nregions = 3;
+ vfu_ctx.dma->regions[0].info.vaddr = (void *)0xc0000;
+ sg[0].offset = 0;
+ sg[0].length = 0x20000;
+ vfu_ctx.dma->regions[1].info.vaddr = (void *)0xe0000;
+ sg[1].length = 0x20000;
+ vfu_ctx.dma->regions[2].info.vaddr = (void *)0x100000;
+ sg[2].length = 0x10000;
+ assert_int_equal(0, dma_map_sg(vfu_ctx.dma, sg, iovec, 3));
+ assert_int_equal(0xc0000, iovec[0].iov_base);
+ assert_int_equal(0x20000, iovec[0].iov_len);
+ assert_int_equal(0xe0000, iovec[1].iov_base);
+ assert_int_equal(0x20000, iovec[1].iov_len);
+ assert_int_equal(0x100000, iovec[2].iov_base);
+ assert_int_equal(0x10000, iovec[2].iov_len);
+}
+
+static void
+test_dma_unmap_sg(void **state UNUSED)
+{
+ dma_sg_t sg[] = { {.region = 0}, {.region = 1}, {.region = 2} };
+ struct iovec iovec[] = { {0}, {0}, {0} };
+
+ vfu_ctx.dma->nregions = 3;
+ vfu_ctx.dma->regions[0].info.iova.iov_base = (void *)0xc0000;
+ vfu_ctx.dma->regions[0].refcnt = 1;
+ sg[0].dma_addr = (void *)0xc0000;
+ vfu_ctx.dma->regions[1].info.iova.iov_base = (void *)0xe0000;
+ vfu_ctx.dma->regions[1].refcnt = 1;
+ sg[1].dma_addr = (void *)0xe0000;
+ vfu_ctx.dma->regions[2].info.iova.iov_base = (void *)0x100000;
+ vfu_ctx.dma->regions[2].refcnt = 1;
+ sg[2].dma_addr = (void *)0x100000;
+ dma_unmap_sg(vfu_ctx.dma, sg, iovec, 3);
+ assert_int_equal(0, vfu_ctx.dma->regions[0].refcnt);
+ assert_int_equal(0, vfu_ctx.dma->regions[1].refcnt);
+ assert_int_equal(0, vfu_ctx.dma->regions[2].refcnt);
}
static void
@@ -749,6 +788,7 @@ main(void)
cmocka_unit_test_setup(test_dma_controller_remove_region_mapped, setup),
cmocka_unit_test_setup(test_dma_controller_remove_region_unmapped, setup),
cmocka_unit_test_setup(test_dma_map_sg, setup),
+ cmocka_unit_test_setup(test_dma_unmap_sg, setup),
cmocka_unit_test_setup(test_dma_addr_to_sg, setup),
cmocka_unit_test_setup(test_vfu_setup_device_dma, setup),
cmocka_unit_test_setup(test_migration_state_transitions, setup),