DAOS-18690 vos: fix engine abort in DTX commit blob fallback on ENOSPC#18612
DAOS-18690 vos: fix engine abort in DTX commit blob fallback on ENOSPC#18612hgichon wants to merge 1 commit into
Conversation
|
Ticket title is 'Aurora daos_user: SCM single target ran out of space (min:0 B) and not able to finish GC. ' |
f395915 to
8e83c8f
Compare
When the pool is physically full, vos_dtx_extend_cmt_table() fails to
allocate a new commit blob and falls back to vos_dtx_reuse_cmt_blob()
inside the same transaction. The failing allocation is a plain
(non-NO_ABORT) alloc inside the regular object modification transaction
started by vos_tx_begin(), whose failure behavior is left at the
default, so the allocator aborts the whole TX on failure. The fallback
then calls umem_tx_add_ptr() on the aborted TX, which is a fatal usage
error in the allocator:
CRIT pmdk tx.c:1403 pmemobj_tx_add_range_direct() called in invalid stage 3
*** Process (daos_engine) received signal 6 (Aborted) ***
vos_dtx_commit_internal <- vos_dtx_prepared <- vos_tx_end
<- vos_obj_punch <- ds_obj_punch_handler
The reuse fallback introduced by daos-stack#18141 is only exercised in tests via
the DAOS_DTX_NOSPACE_NOREFRESH fail-check, which skips the allocation
entirely and therefore never reproduces the TX abort of a real ENOSPC.
Fix: add a UMEM_FLAG_NO_ABORT alloc flag (1 << 3, distinct from
UMEM_XADD_NO_SNAPSHOT), map it to POBJ_XALLOC_NO_ABORT /
DAV_XALLOC_NO_ABORT in the pmem/dav/dav_v2 backends, and use it for
the commit blob allocation so the transaction survives the failure and
the reuse fallback can run as designed.
Add a umem regression test (UMEM010): a failed NO_ABORT allocation must
leave the TX alive so a subsequent alloc/tx_add_ptr/commit succeeds.
Verified on a 400 MB tmpfs-SCM pool: fill the pool to ENOSPC, then run
a create/unlink storm. Before the fix the engine aborts within seconds
with the backtrace above; after the fix it stays up and the operations
fail cleanly with -DER_NOSPACE.
Signed-off-by: hgichon <hgichon@gmail.com>
8e83c8f to
6c4dd32
Compare
|
Note on backport scope: the Jira bot above shows DAOS-18690 is So this fix should ride along with the #18141 backport to 2.8.1 (and any other target branches), not just land on master. Happy to prepare the 2.8-based patch as well if useful. |
| if (flags & UMEM_FLAG_NO_FLUSH) | ||
| pflags |= POBJ_FLAG_NO_FLUSH; | ||
| if (flags & UMEM_FLAG_NO_ABORT) | ||
| pflags |= POBJ_XALLOC_NO_ABORT; |
There was a problem hiding this comment.
Can we directly use POBJ_FLAG_TX_NO_ABORT to be consistent with above POBJ_FLAG_NO_FLUSH and POBJ_FLAG_ZERO?
There was a problem hiding this comment.
Sure, will switch to POBJ_FLAG_TX_NO_ABORT for consistency with POBJ_FLAG_NO_FLUSH/POBJ_FLAG_ZERO. Thanks.
| if (flags & UMEM_FLAG_NO_FLUSH) | ||
| pflags |= POBJ_FLAG_NO_FLUSH; | ||
| if (flags & UMEM_FLAG_NO_ABORT) | ||
| pflags |= POBJ_XALLOC_NO_ABORT; |
There was a problem hiding this comment.
Can we directly use POBJ_FLAG_TX_NO_ABORT to be consistent with above POBJ_FLAG_NO_FLUSH and POBJ_FLAG_ZERO?
| struct umem_instance *umm = &pool->vp_umm; | ||
| int rc; | ||
|
|
||
| rc = umem_tx_add_ptr(umm, ptr, size); |
There was a problem hiding this comment.
Just realize that we also need to use umem_tx_xadd_ptr() with NO_ABORT flags instead of directly call umem_tx_add_ptr(). Otherwise, we will hit similar SIGABRT when DER_NOSPACE happened. Would you please to enhance you patch for that? Thanks!
There was a problem hiding this comment.
Agreed. I'll route the flag through the add path too: extend pmem_tx_xadd / bmem_tx_xadd(_v2) to map UMEM_FLAG_NO_ABORT -> POBJ_XADD_NO_ABORT / DAV_XADD_NO_ABORT, and call umem_tx_xadd_ptr(..., UMEM_FLAG_NO_ABORT) here so both backends are covered. (On DAV dav_tx_add_range already returns the error instead of aborting, so this mainly closes the PMEM abort while keeping the two backends consistent.)
|
Another relative simple solution can be like the followings, then we do not need |
|
@Nasf-Fan re: the So my plan is to keep the per-alloc/-add |
Currently,
@hgichon, how do you think? The patch for above changes maybe not small, many of them are for reverting the work from commit |
|
Thanks @Nasf-Fan — that plan sounds right. Since most of it is unwinding the A couple of things that might save you time, feel free to lift from this PR:
Shall I close this PR in favor of yours, or would you prefer to push onto this branch ( |
@hgichon , I merged your fixes into the new PR (#18671) against the latest master, and add you as |
Summary
Regression introduced by PR #18141 (DAOS-18690 "vos: handle DTX commit under space pressure", commit b36dd7b, 2026-05-14) — master only. Release branches (2.6.x/2.8) are not affected: their
vos_dtx_commit_internal()returns-DER_NOSPACEdirectly on blob allocation failure without touching the aborted TX.When the pool is physically full (ENOSPC), extending the DTX committed table during a punch/update commit crashes the whole engine with SIGABRT.
vos_dtx_extend_cmt_table()allocates a new commit blob withumem_zalloc(). On allocation failure it falls back tovos_dtx_reuse_cmt_blob()inside the same transaction. However, all three allocator backends (pmemobj, dav, dav_v2) abort the transaction automatically when a tx alloc fails (no*_NO_ABORTflag is passed by the umem wrappers). The fallback then callsumem_tx_add_ptr()/dbtree_delete()on the aborted TX, which is a fatal usage error in the allocator:Backtrace (master, commit ~f6b827e3e):
Why CI doesn't catch it
The reuse-blob fallback is exercised in tests via the
DAOS_DTX_NOSPACE_NOREFRESHfail-check, which skips the allocation entirely — the TX stays alive, so the fallback works. A real ENOSPC allocation failure aborts the TX first, which the fail-check path never reproduces.Reproduction
vos_dtx_extend_cmt_table()while the pool is full.invalid stage 3+ SIGABRT). A pool-full production system can be taken down by any client issuing unlinks.Suggested fix (verified)
Add a
UMEM_FLAG_NO_ABORTalloc flag, map it toPOBJ_XALLOC_NO_ABORT/DAV_XALLOC_NO_ABORTin the pmem/dav/dav_v2 backends, and use it for the commit-blob allocation invos_dtx_extend_cmt_table()so the TX survives the failure and the reuse fallback can run as designed:src/include/daos/mem.h:#define UMEM_FLAG_NO_ABORT (((uint64_t)1) << 2)src/common/mem.c:pmem_tx_alloc()→POBJ_XALLOC_NO_ABORT,bmem_tx_alloc()/bmem_tx_alloc_v2()→DAV_XALLOC_NO_ABORTsrc/vos/vos_dtx.cvos_dtx_extend_cmt_table():umem_zalloc(umm, DTX_CMT_BLOB_SIZE)→umem_alloc_verb(umm, UMEM_FLAG_ZERO | UMEM_FLAG_NO_ABORT, DTX_CMT_BLOB_SIZE, UMEM_DEFAULT_MBKT_ID)With this patch the same reproduction survives: unlink storms on a full pool return
-DER_NOSPACEcleanly and the engine stays up (verified on tmpfs SCM/pmemobj backend; dav paths compile-verified only).Note: filed as a PR because GitHub issues are disabled on this repo; happy to file a Jira ticket as well if preferred.
Update (2026-07-04):
UMEM_FLAG_NO_ABORTmoved to1 << 3to avoid sharing a bit withUMEM_XADD_NO_SNAPSHOT(1 << 2) in the same flags namespace.UMEM_FLAG_NO_ABORTallocation must leave the TX alive so a subsequent alloc/umem_tx_add_ptr()/commit succeeds — this covers the TX-abort path that theDAOS_DTX_NOSPACE_NOREFRESHfail-check cannot reproduce. Passes 10/10 locally (pmem backend on tmpfs needsPMEMOBJ_CONF=sds.at_create=0).vos_tx_begin()object transaction, whose failure behavior is left at the default (abort) rather thanTX_FAILURE_RETURN.