Repository navigation
Add explicit asynchronous submit/poll offload API - #34
Merged
Merged
Conversation
jsydir
approved these changes
Sep 25, 2026
jsydir
left a comment
Contributor
There was a problem hiding this comment.
It makes sense to add an optional parameter to the dto_submit_* functions to pass in the max number of retries to allow users to selectively override the default value
dto_submit_common is not made externally visible. It should be renamed to async_submit_common (or some other name that does not start with dto_ ) to conform with the naming convention that non-transparent externally visible functions are named dto_*
libdto so far only accelerated memcpy/memmove/memset/memcmp transparently via LD_PRELOAD. This adds a public dto.h with an explicit submit/poll API so applications can overlap DSA data movement with their own CPU work: dto_submit_memcpy / dto_submit_memset / dto_submit_memcpy_crc / dto_submit_crc -> dto_async_poll -> dto_async_crc_val dto_batch_op_new / dto_submit_batch_copy / dto_batch_poll (up to 64 copies) The operation state is caller-allocated (opaque dto_async_op) so it can be polled from any thread. Every submit takes a flags argument: DTO_SUBMIT_CC directs output toward the CPU cache when the device supports it, and DTO_SUBMIT_BOF makes the device block on page faults instead of aborting (requires a work queue configured with block-on-fault). Without BOF a faulting operation surfaces as DTO_ASYNC_FAILED and the caller redoes it on the CPU; submission returns DTO_ASYNC_FALLBACK when DSA is unavailable or the request is out of range, with nothing copied. CRC results follow the raw CRC32C convention (seed 0, no final inversion), matching _mm_crc32. The memset entry point takes (dest, c, n) like the C library, replacing the earlier page-oriented helper so the offload API is uniform across operations. Adds seven functional tests (copy, memset, crc, copy+crc, no-BOF fault contract, argument validation, batch) that pass both on DSA hardware and on the CPU-fallback path.
Bring the asynchronous API work from the two development branches into one interface, keeping this PR's conventions: every submit takes a flags argument (DTO_SUBMIT_CC, DTO_SUBMIT_BOF), DTO_ASYNC_FALLBACK means nothing was done, and explicit submits are not gated by DTO_MIN_BYTES. From ch-async-ops (Cloud Hypervisor): - dto_submit_memfill, dto_submit_compare, dto_submit_compval and dto_submit_transl_fetch, with dto_async_result, dto_async_bytes_completed and dto_async_status. - The dto_batch builder: dto_batch_create (up to DTO_BATCH_MAX = 1024 operations), add_memmove/memfill/dualcast/crc/memcpy_crc/compare/ compval, submit, poll and per-operation status, result, bytes_completed and crc. A single operation goes out as a plain descriptor. It replaces dto_batch_op; dto_submit_batch_copy remains as a convenience on top of it. From cachelib-navy: - Seeded CRC32C in the iSCSI presentation, dto_submit_memcpy_crc32c, dto_submit_crc32c and dto_async_crc32c_val, and the synchronous dto_crc32c_with_seed (gated by the new DTO_CRC_MIN_BYTES, default DTO_MIN_BYTES). - dto_async_wait and dto_batch_wait, which wait with the configured DTO_WAIT_METHOD and are no-ops for ops that were never submitted, and dto_wait_blocks (0 until a blocking wait method exists). dto_async_crc_val and dto_batch_crc now return the raw CRC32C (the SSE4.2 crc32 loop from 0, no final inversion), which both branches and their users' CPU fallbacks use, instead of standard CRC32C; the seeded calls provide the standard form. dto_batch_poll redoes every operation the device did not complete on the CPU before returning DONE, results and CRCs included, so DONE means every result is valid; dto_batch_status still reports the device's status. A move the device stopped partway is resumed from where it stopped, in the direction it was copying, because with overlapping buffers the device has already overwritten part of the source. One poller repairs; concurrent pollers see PENDING until it is done. DTO_SUBMIT_BOF is passed to the device as given. dto.h now spells out what that means for each work queue configuration: with block-on-fault enabled the device waits for the fault; without it a BOF submit fails with DSA_COMP_INVALID_FLAGS; and without the flag a page fault is a partial completion, which for an overlapping move has to be finished from where the device stopped rather than redone. Build with DTO_NO_LIBC_INTERPOSE to leave out the memcpy, memset, memmove and memcmp interposers (libdto_explicit). Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Mv7xKtQPkGoXQUvvuuhAEz
libdto_explicit is dto.c built with DTO_NO_LIBC_INTERPOSE, for programs that call the dto_* API directly and must not have every large libc copy in the process sent to DSA (Cloud Hypervisor links it). CMake builds and installs it by default (DTO_BUILD_EXPLICIT) and exports DTO::dto_explicit; the Makefile gains a libdto_explicit target. Also drop the duplicate include(GNUInstallDirs). Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Mv7xKtQPkGoXQUvvuuhAEz
Test every submit and batch operation against CPU results: raw and seeded CRCs (including chaining and the CRC32C check value), compare and compval offsets, memfill patterns, translation fetch, waits on unsubmitted ops, batch limits, the single-descriptor path, and the CPU repair of a batch whose operations hit unmapped pages, including overlapping moves the device left half done in both directions and two threads polling the same batch. test_async_bof checks the block-on-fault contract on whatever the work queue is configured with: a BOF copy into untouched pages completes on a block-on-fault queue and is rejected (invalid flags) on one without; without the flag the copy stops at the first untouched page with the touched part done; and batch operations follow the same rules. The tests accepted DTO_ASYNC_FAILED and FALLBACK everywhere, so they passed while every DTO_SUBMIT_BOF operation failed on a work queue without block-on-fault. With DTO_TEST_EXPECT_DSA set they now require operations on faulted-in buffers to be submitted and completed by the device, and the general tests no longer request block-on-fault, so they hold on any work queue configuration. The suite also runs against libdto_explicit (functional_explicit). Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Mv7xKtQPkGoXQUvvuuhAEz
Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Mv7xKtQPkGoXQUvvuuhAEz
byrnedj
force-pushed
the
async-api-upstream
branch
from
October 21, 2026 16:53
80d602f to
de1f694
Compare
This was referenced Oct 21, 2026
byrnedj
added a commit
to byrnedj/DTO
that referenced
this pull request
Oct 27, 2026
Resolves conflicts with the explicit asynchronous API (intel#34), which added to the same spots in CMakeLists.txt, Makefile, README.md and TESTING.md. Both sides are kept: the libdto_explicit target, test and docs first, then the dtoctl ones. The Makefile's install target becomes "install: dtoctl install-man" with the libdto_explicit target above it.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This adds a public
dto.hwith an explicit submit/poll API, so applications can overlap DSA work with their own CPU work. It unifies the API work from thecachelib-navyandch-async-opsdevelopment branches into one interface, and addslibdto_explicit: the same library without the libc interposers.Single operations (
dto_async_op, caller-allocated, can be polled from any thread):dto_submit_memcpy,dto_submit_memset,dto_submit_memfill(8-byte pattern).dto_submit_crcanddto_submit_memcpy_crcreturn the raw CRC32C: the SSE4.2crc32loop from 0, no final inversion.dto_submit_crc32canddto_submit_memcpy_crc32ctake a seed in the iSCSI presentation: seed 0 gives standard CRC32C, and passing a previous result chains.dto_crc32c_with_seedis a synchronous call that always returns the right value, using the CPU belowDTO_CRC_MIN_BYTES.dto_submit_compare,dto_submit_compval(against an 8-byte pattern).dto_submit_transl_fetch(DSA 2.0).dto_async_poll;dto_async_wait, which waits withDTO_WAIT_METHODand is a no-op for ops that were never submitted; and the accessors (crc_val,crc32c_val,result,bytes_completed,status).Batches (
dto_batch, up to 1024 operations in one DSA batch descriptor; a single operation goes out as a plain descriptor):dto_batch_create, thendto_batch_add_{memmove,memfill,dualcast,crc,memcpy_crc,compare,compval}, thendto_batch_submit, thendto_batch_wait/dto_batch_poll, with per-operation status, result, bytes completed and CRC.dto_batch_pollredoes on the CPU every operation the device did not finish, results included, soDONEmeans every result is valid;dto_batch_statusstill reports what the device did.dto_submit_batch_copyis kept as a convenience on top of the builder.Every submit takes
flags:DTO_SUBMIT_CCsteers output toward the CPU cache.DTO_SUBMIT_BOFasks the device to block on page faults, and is passed to it as given:DTO_SUBMIT_BOFDTO_ASYNC_FAILED, status0x11(invalid flags)DTO_ASYNC_FAILED, status0x03: partial completion (dto_async_bytes_completed)dto.hdocuments how to finish an overlapping move from where the device stopped. Nothing in the library requests block-on-fault on its own (dto_crc32c_with_seeddoesn't), so the default calls work on either configuration.DTO_ASYNC_FALLBACKmeans nothing was done, and explicit submits are not gated byDTO_MIN_BYTES.Migrating from the development branches:
ch-async-ops):flagsargument todto_submit_{crc,compare,compval,transl_fetch}anddto_batch_add_{compare,crc}.cache_controlbecomesflags;1is stillDTO_SUBMIT_CC.DTO_SUBMIT_BOF, which used to be implicit. It needs a work queue with block-on-fault, as the branch'saccelConfig.shconfigures.dto_async_crc_valnow returnsuint32_t.dto_batch_pollno longer returnsFAILED.dto_batch_op_new/freebecomesdto_batch_create/destroy.dto_submit_batch(members)becomesdto_batch_add_*calls.dto_batch_member_done/dto_batch_crc_valbecomedto_batch_status/dto_batch_crc.cache_controlbecomesflags.dto_thread_active(admission control) are left for follow-up PRs, sodto_wait_blocks()returns 0 for now.dto_memcpy_async,dto_crc,dto_memset_pages,dto_batch_copy, ...) are not part of this API.Testing (GNR, Xeon 6787P, one DSA, shared WQ
dsa0/wq0.0):FAILED/FALLBACKeverywhere, so they could not tell a working device from one rejecting every operation (as the device does with everyDTO_SUBMIT_BOFop on a work queue without block-on-fault, which is what those tests passed). WithDTO_TEST_EXPECT_DSA=1, offload is now required.test_async_bofchecks the table above against whichever setting the work queue has.libdtoandlibdto_explicit, and on the CPU-fallback path.ctest -R sanityA/B againstperf_baseline: every cell within ±1.1%, no regressions.cachelib-navyandch-async-opsfor every op and size, up to 64 GB/s; for comparison, CPU memcpy reaches 14.8 GB/s and CPU CRC32C 8.5 GB/s.ch-async-ops's own 4 KiB batch-compare benchmark gives 131 ns/page for batches of 256 or more on both branches, against 320 ns/page for CPUmemcmp.get_wq()(an atomic increment), which the development branches replace with a per-thread WQ.accelConfig.shconfigures it): every explicit op and batch fails with status0x11(invalid flags), because they set block-on-fault on every descriptor. Here it is set only when the caller asks for it.