aboutsummaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorJohn Levon <john.levon@nutanix.com>2022-05-30 09:41:32 +0100
committerGitHub <noreply@github.com>2022-05-30 09:41:32 +0100
commite036ac145acea1a5aa77879e978ac2fff909a657 (patch)
tree1f0837b4c79feb97aa642d4e505e3d64012896d7 /test
parent79e83e482d4eb0b7a07cfa207506d33edf05d04b (diff)
downloadlibvfio-user-e036ac145acea1a5aa77879e978ac2fff909a657.zip
libvfio-user-e036ac145acea1a5aa77879e978ac2fff909a657.tar.gz
libvfio-user-e036ac145acea1a5aa77879e978ac2fff909a657.tar.bz2
allow concurrent dirty bitmap get (#677)
Use atomic operations to allow concurrent bitmap updates with VFIO_IOMMU_DIRTY_PAGES_FLAG_GET_BITMAP operations. Dirtying clients can race against each other, so we must use atomic or when marking dirty: we do this byte-by-byte. When reading the dirty bitmap, we must be careful to not race and lose any set bits within the same byte. If we miss an update, we'll catch it the next time around, presuming that before the final pass we'll have quiesced all I/O. Signed-off-by: John Levon <john.levon@nutanix.com> Reviewed-by: Raphael Norwitz <raphael.norwitz@nutanix.com> Reviewed-by: Thanos Makatos <thanos.makatos@nutanix.com>
Diffstat (limited to 'test')
-rw-r--r--test/py/test_dirty_pages.py104
1 files changed, 74 insertions, 30 deletions
diff --git a/test/py/test_dirty_pages.py b/test/py/test_dirty_pages.py
index 6cf87fb..8b4e3dc 100644
--- a/test/py/test_dirty_pages.py
+++ b/test/py/test_dirty_pages.py
@@ -89,13 +89,13 @@ def test_dirty_pages_setup():
payload = vfio_user_dma_map(argsz=len(vfio_user_dma_map()),
flags=(VFIO_USER_F_DMA_REGION_READ | VFIO_USER_F_DMA_REGION_WRITE),
- offset=0, addr=0x10000, size=0x10000)
+ offset=0, addr=0x10000, size=0x20000)
msg(ctx, sock, VFIO_USER_DMA_MAP, payload, fds=[f.fileno()])
payload = vfio_user_dma_map(argsz=len(vfio_user_dma_map()),
flags=(VFIO_USER_F_DMA_REGION_READ | VFIO_USER_F_DMA_REGION_WRITE),
- offset=0, addr=0x20000, size=0x10000)
+ offset=0, addr=0x40000, size=0x10000)
msg(ctx, sock, VFIO_USER_DMA_MAP, payload)
@@ -247,7 +247,7 @@ def test_get_dirty_page_bitmap_unmapped():
dirty_pages = vfio_user_dirty_pages(argsz=argsz,
flags=VFIO_IOMMU_DIRTY_PAGES_FLAG_GET_BITMAP)
bitmap = vfio_user_bitmap(pgsize=0x1000, size=8)
- br = vfio_user_bitmap_range(iova=0x20000, size=0x10000, bitmap=bitmap)
+ br = vfio_user_bitmap_range(iova=0x40000, size=0x10000, bitmap=bitmap)
payload = bytes(dirty_pages) + bytes(br)
@@ -283,13 +283,7 @@ def test_dirty_pages_get_unmodified():
assert br.bitmap.size == 8
-def get_dirty_page_bitmap_result(resp):
- _, resp = vfio_user_dirty_pages.pop_from_buffer(resp)
- _, resp = vfio_user_bitmap_range.pop_from_buffer(resp)
- return struct.unpack("Q", resp)[0]
-
-
-def send_dirty_page_bitmap(busy=False):
+def get_dirty_page_bitmap():
argsz = len(vfio_user_dirty_pages()) + len(vfio_user_bitmap_range()) + 8
dirty_pages = vfio_user_dirty_pages(argsz=argsz,
@@ -299,18 +293,33 @@ def send_dirty_page_bitmap(busy=False):
payload = bytes(dirty_pages) + bytes(br)
- return msg(ctx, sock, VFIO_USER_DIRTY_PAGES, payload, busy=busy)
+ result = msg(ctx, sock, VFIO_USER_DIRTY_PAGES, payload)
+
+ _, result = vfio_user_dirty_pages.pop_from_buffer(result)
+ _, result = vfio_user_bitmap_range.pop_from_buffer(result)
+ assert(len(result) == 8)
-def get_dirty_page_bitmap():
- result = send_dirty_page_bitmap()
- return get_dirty_page_bitmap_result(result)
+ return struct.unpack("Q", result)[0]
sg3 = None
iovec3 = None
+def write_to_addr(ctx, addr, size, get_bitmap=True):
+ """Simulate a write to the given address and size."""
+ ret, sg = vfu_addr_to_sgl(ctx, dma_addr=addr, length=size)
+ assert ret == 1
+ iovec = iovec_t()
+ ret = vfu_sgl_get(ctx, sg, iovec)
+ assert ret == 0
+ vfu_sgl_put(ctx, sg, iovec)
+ if get_bitmap:
+ return get_dirty_page_bitmap()
+ return None
+
+
def test_dirty_pages_get_modified():
ret, sg1 = vfu_addr_to_sgl(ctx, dma_addr=0x10000, length=0x1000)
assert ret == 1
@@ -326,13 +335,15 @@ def test_dirty_pages_get_modified():
ret = vfu_sgl_get(ctx, sg2, iovec2)
assert ret == 0
+ # simple single bitmap entry map
ret, sg3 = vfu_addr_to_sgl(ctx, dma_addr=0x12000, length=0x1000)
assert ret == 1
iovec3 = iovec_t()
ret = vfu_sgl_get(ctx, sg3, iovec3)
assert ret == 0
- ret, sg4 = vfu_addr_to_sgl(ctx, dma_addr=0x14000, length=0x4000)
+ # write that spans bytes in bitmap
+ ret, sg4 = vfu_addr_to_sgl(ctx, dma_addr=0x16000, length=0x4000)
assert ret == 1
iovec4 = iovec_t()
ret = vfu_sgl_get(ctx, sg4, iovec4)
@@ -340,23 +351,65 @@ def test_dirty_pages_get_modified():
# not put yet, dirty bitmap should be zero
bitmap = get_dirty_page_bitmap()
- assert bitmap == 0b00000000
+ assert bitmap == 0b0000000000000000
# put SGLs, dirty bitmap should be updated
vfu_sgl_put(ctx, sg1, iovec1)
vfu_sgl_put(ctx, sg4, iovec4)
bitmap = get_dirty_page_bitmap()
- assert bitmap == 0b11110001
+ assert bitmap == 0b0000001111000001
# after another two puts, should just be one dirty page
vfu_sgl_put(ctx, sg2, iovec2)
vfu_sgl_put(ctx, sg3, iovec3)
bitmap = get_dirty_page_bitmap()
- assert bitmap == 0b00000100
+ assert bitmap == 0b0000000000000100
# and should now be clear
bitmap = get_dirty_page_bitmap()
- assert bitmap == 0b00000000
+ assert bitmap == 0b0000000000000000
+
+ #
+ # check various edge cases of bitmap values.
+ #
+
+ # very first bit
+ bitmap = write_to_addr(ctx, 0x10000, 0x1000)
+ assert bitmap == 0b0000000000000001
+
+ # top bit of first byte
+ bitmap = write_to_addr(ctx, 0x17000, 0x1000)
+ assert bitmap == 0b0000000010000000
+
+ # all bits except top one of first byte
+ bitmap = write_to_addr(ctx, 0x10000, 0x7000)
+ assert bitmap == 0b0000000001111111
+
+ # all bits of first byte
+ bitmap = write_to_addr(ctx, 0x10000, 0x8000)
+ assert bitmap == 0b0000000011111111
+
+ # all bits of first byte plus bottom bit of next
+ bitmap = write_to_addr(ctx, 0x10000, 0x9000)
+ assert bitmap == 0b0000000111111111
+
+ # straddle top/bottom bit
+ bitmap = write_to_addr(ctx, 0x17000, 0x2000)
+ assert bitmap == 0b0000000110000000
+
+ # top bit of second byte
+ bitmap = write_to_addr(ctx, 0x1f000, 0x1000)
+ assert bitmap == 0b1000000000000000
+
+ # top bit of third byte
+ bitmap = write_to_addr(ctx, 0x27000, 0x1000)
+ assert bitmap == 0b100000000000000000000000
+
+ # bits in third and first byte
+ write_to_addr(ctx, 0x26000, 0x1000, get_bitmap=False)
+ write_to_addr(ctx, 0x12000, 0x2000, get_bitmap=False)
+ bitmap = get_dirty_page_bitmap()
+ assert bitmap == 0b010000000000000000001100
def test_dirty_pages_stop():
@@ -399,17 +452,8 @@ def test_dirty_pages_bitmap_with_quiesce():
assert ret == 0
vfu_sgl_put(ctx, sg1, iovec1)
- send_dirty_page_bitmap(busy=True)
-
- ret = vfu_device_quiesced(ctx, 0)
- assert ret == 0
-
- # now should be able to get the reply
- result = get_reply(sock, expect=0)
- bitmap = get_dirty_page_bitmap_result(result)
- assert bitmap == 0b00000001
-
- quiesce_errno = 0
+ bitmap = get_dirty_page_bitmap()
+ assert bitmap == 0b0000000000000001
def test_dirty_pages_stop_with_quiesce():