diff --git a/src/common/mem.c b/src/common/mem.c index f64d744be72..be19df506bd 100644 --- a/src/common/mem.c +++ b/src/common/mem.c @@ -806,6 +806,8 @@ pmem_tx_alloc(struct umem_instance *umm, size_t size, uint64_t flags, unsigned i pflags |= POBJ_FLAG_ZERO; if (flags & UMEM_FLAG_NO_FLUSH) pflags |= POBJ_FLAG_NO_FLUSH; + if (flags & UMEM_FLAG_NO_ABORT) + pflags |= POBJ_XALLOC_NO_ABORT; return umem_id2off(umm, pmemobj_tx_xalloc(size, type_num, pflags)); } @@ -1244,6 +1246,8 @@ bmem_tx_alloc(struct umem_instance *umm, size_t size, uint64_t flags, unsigned i pflags |= DAV_FLAG_ZERO; if (flags & UMEM_FLAG_NO_FLUSH) pflags |= DAV_FLAG_NO_FLUSH; + if (flags & UMEM_FLAG_NO_ABORT) + pflags |= DAV_XALLOC_NO_ABORT; return dav_tx_xalloc(size, type_num, pflags); } @@ -1483,6 +1487,8 @@ bmem_tx_alloc_v2(struct umem_instance *umm, size_t size, uint64_t flags, unsigne pflags |= DAV_FLAG_ZERO; if (flags & UMEM_FLAG_NO_FLUSH) pflags |= DAV_FLAG_NO_FLUSH; + if (flags & UMEM_FLAG_NO_ABORT) + pflags |= DAV_XALLOC_NO_ABORT; if (mbkt_id != 0) pflags |= DAV_EZONE_ID(mbkt_id); return dav_tx_alloc_v2(size, type_num, pflags); diff --git a/src/common/tests/umem_test.c b/src/common/tests/umem_test.c index 6080843f51c..bd25f257eca 100644 --- a/src/common/tests/umem_test.c +++ b/src/common/tests/umem_test.c @@ -360,6 +360,58 @@ test_alloc(void **state) assert_int_equal(rc, 0); } +/* A failed allocation with UMEM_FLAG_NO_ABORT must leave the transaction + * alive so in-tx fallbacks (e.g. vos_dtx_reuse_cmt_blob) keep working; + * without the flag the failure aborts the TX and any further tx_add is a + * fatal usage error in the allocator. + */ +static void +test_alloc_no_abort(void **state) +{ + struct test_arg *arg = *state; + struct umem_instance *umm = utest_utx2umm(arg->ta_utx); + int *value; + umem_off_t umoff = 0; + umem_off_t huge; + int rc; + + rc = utest_tx_begin(arg->ta_utx); + if (rc != 0) + goto done; + + /* Larger than the pool: must fail, but must NOT abort the TX */ + huge = umem_alloc_verb(umm, UMEM_FLAG_ZERO | UMEM_FLAG_NO_ABORT, + POOL_SIZE, UMEM_DEFAULT_MBKT_ID); + if (!UMOFF_IS_NULL(huge)) { + print_message("huge allocation unexpectedly succeeded\n"); + rc = 1; + goto end; + } + + /* TX still alive: alloc + snapshot + write must all work */ + umoff = umem_zalloc(umm, 4); + if (UMOFF_IS_NULL(umoff)) { + print_message("small alloc failed after NO_ABORT failure\n"); + rc = 1; + goto end; + } + + value = umem_off2ptr(umm, umoff); + rc = umem_tx_add_ptr(umm, value, 4); + if (rc != 0) { + print_message("tx_add_ptr failed after NO_ABORT failure\n"); + rc = 1; + goto end; + } + *value = 42; + + rc = umem_free(umm, umoff); +end: + rc = utest_tx_end(arg->ta_utx, rc); +done: + assert_int_equal(rc, 0); +} + static int flush_prep(struct umem_store *store, struct umem_store_iod *iod, daos_handle_t *fh) { @@ -757,6 +809,7 @@ main(int argc, char **argv) {"UMEM002: Test null flags vmem", test_invalid_flags, setup_vmem, teardown_vmem}, {"UMEM003: Test alloc pmem", test_alloc, setup_pmem, teardown_pmem}, {"UMEM004: Test alloc vmem", test_alloc, setup_vmem, teardown_vmem}, + {"UMEM010: Test alloc NO_ABORT pmem", test_alloc_no_abort, setup_pmem, teardown_pmem}, {"UMEM005: Test page cache", test_page_cache, NULL, NULL}, {"UMEM006: Test page cache many pages", test_many_pages, NULL, NULL}, {"UMEM007: Test page cache many writes", test_many_writes, NULL, NULL}, diff --git a/src/include/daos/mem.h b/src/include/daos/mem.h index eb4bdbf3552..6b275afea68 100644 --- a/src/include/daos/mem.h +++ b/src/include/daos/mem.h @@ -681,6 +681,10 @@ struct umem_instance; #define UMEM_FLAG_ZERO (((uint64_t)1) << 0) #define UMEM_FLAG_NO_FLUSH (((uint64_t)1) << 1) #define UMEM_XADD_NO_SNAPSHOT (((uint64_t)1) << 2) +/* On allocation failure, return UMOFF_NULL without aborting the current + * transaction, so the caller can fall back to another strategy in-tx. + */ +#define UMEM_FLAG_NO_ABORT (((uint64_t)1) << 3) /* Macros associated with Memory buckets */ #define UMEM_DEFAULT_MBKT_ID 0 diff --git a/src/vos/vos_dtx.c b/src/vos/vos_dtx.c index a7e830bff7a..636810ee88e 100644 --- a/src/vos/vos_dtx.c +++ b/src/vos/vos_dtx.c @@ -1293,8 +1293,14 @@ vos_dtx_extend_cmt_table(struct vos_container *cont) umem_off_t dbd_off = UMOFF_NULL; int rc; + /* NO_ABORT: on ENOSPC the fallback below (vos_dtx_reuse_cmt_blob) + * keeps working inside the same transaction; a plain alloc failure + * would have aborted the tx and any further tx_add would be a fatal + * usage error in the allocator (engine abort). + */ if (!DAOS_FAIL_CHECK(DAOS_DTX_NOSPACE_NOREFRESH)) - dbd_off = umem_zalloc(umm, DTX_CMT_BLOB_SIZE); + dbd_off = umem_alloc_verb(umm, UMEM_FLAG_ZERO | UMEM_FLAG_NO_ABORT, + DTX_CMT_BLOB_SIZE, UMEM_DEFAULT_MBKT_ID); if (UMOFF_IS_NULL(dbd_off)) return vos_dtx_reuse_cmt_blob(cont);