Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/common/mem.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we directly use POBJ_FLAG_TX_NO_ABORT to be consistent with above POBJ_FLAG_NO_FLUSH and POBJ_FLAG_ZERO?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, will switch to POBJ_FLAG_TX_NO_ABORT for consistency with POBJ_FLAG_NO_FLUSH/POBJ_FLAG_ZERO. Thanks.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we directly use POBJ_FLAG_TX_NO_ABORT to be consistent with above POBJ_FLAG_NO_FLUSH and POBJ_FLAG_ZERO?

return umem_id2off(umm, pmemobj_tx_xalloc(size, type_num, pflags));
}

Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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);
Expand Down
53 changes: 53 additions & 0 deletions src/common/tests/umem_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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},
Expand Down
4 changes: 4 additions & 0 deletions src/include/daos/mem.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 7 additions & 1 deletion src/vos/vos_dtx.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Loading