* Improve testing of memcpy including adding perf test.
* Change remaining_bytes to be branch free.
Use reciprocal division followed by multiply to remove a branch.
* Post large deallocations to original thread
This change sets all large allocations to be owned by the originating
thread. This means they will be messaged back to the original thread
before they can be reused.
The following reason for making this change:
* This will improve producer/consumer apps involving large allocations.
* It enables the implementation of a more complex chunk allocator that
reassembles chunks.
* It addresses an issue with compartmentalisation where the handling of
large allocations can result in meta-data ownership changing.
TSAN complained that there was a race that after some thoughts appears
to be due to this exchange needing to be an `acquire`.
I still wonder if the data dependence that is threaded through the
exchange induces enough order.
If there is only one slab remaining, then we probabalisticly allocator a
new one. If a slab is barely in use, then this could cause us to
effectively double the number of slabs in use.
This commit checks if the remaining slab has enough remaining elements
to provide randomisation.
* Add default for getting chunk allocator state
Makes the API same between the two configurations.
* Reduce address space usage for Open Enclave
* Fix OE Pal concept
* Add support for Pal not to provide time.
The lazy return of pages to the OS uses a simple time
based heuristic. This enables a PAL to not support time,
and return the memory to a central pool immediately.
* Update src/backend/backend.h
Co-authored-by: Amaury Chamayou <amaury@xargs.fr>
Co-authored-by: Amaury Chamayou <amaury@xargs.fr>
The primary aim for this refactor is to use a representation for
sizeclasses that uniformly covers both large and small. This allows
certain operations such as alloc_size and external_pointer to be
uniformly implemented.
The additional types make clear which kind of sizeclass is in use.
This also tidies up the code for sizeclass based divisible by and
modulus.
It fixes a bug in rust_realloc that didn't correctly determine a realloc
was required for large classes.
With the new snmalloc2 changes it seems the larger window is leading to
more fragmentation and harming performance. Reducing size still
provides good batching, improves memory overhead.
This adds a way to periodically pool the PAL to see if any timers have
expired. Timers can be used to periodically provide callbacks to the
rest of snmalloc.
Consuming available slabs in LIFO order makes predicting address reuse harder
but appears to have performance implications. Condition this on CHECK_CLIENT
and instead use FIFO order on !CHECK_CLIENT builds.
On systems with larger than 16KiB page size, we have chunks
that divide a page. This seems a little strange, and if we
want to disable the pages backing a chunk, this is not possible.
This change ensures the chunk is always at least a single page.
This preserves the chunk pointer through the use of a chunk as a slab. It does
grow the structure by one pointer, but on non-CHERI it is still padded to 64
bytes, even with CHECK_CLIENT guards in place:
0: MetaCommon chunk pointer
8: next pointer
16: builder head[0]
24: builder head[1]
32: builder tail[0]
40: builder tail[1]
48: builder length[0] (uint16_t)
50: builder length[1] (uint16_t)
52: padding (4 bytes)
56: needed (uint16_t)
58: sleeping (bool)
(Sadly, on CHERI, even without CHECK_CLIENT guards and with no padding, there
are now four pointers in the structure -- chunk, next, head, tail -- plus five
extra bytes. We will likely wish to explore encoding the head and tail offsets
relative to the chunk pointer.)
This lets us remove the "subversive amplification" in dealloc() in favor of just
preserving the chunk pointer. Speaking of, be sure to assign that in all the
right places, and ASSERT that we've got it right.
The use of void* had let an overzealous unsafe_ptr() leak a pointer with address
space control to the client (in LocalAllocator::alloc_not_small, specifically).
Correct this to call capptr_chunk_is_alloc() (to capture our intent) and
capptr_to_user_address_control() (to do the bounding) and defer the conversion
to void* until the very periphery of the allocator, using capptr_reveal()
(again, to capture intent).
Avoid computing bits::next_pow2_bits(1 << n). Even if the compiler can see
through enough of the algebra, it's surely more direct to just use n.
While here, slightly expand documentation about what's going on with the
"sizeclass" encoded into MetaEntry-s.
capptr_rebound was only ever going to be used for external_pointer, which now
operates entirely using pointer_offset. So instead, just make external_pointer
use capptr::AllocWild<void>, capptr_from_client, and a new capptr_reveal_wild.
There is no such thing as "struct Slab" any more.
We use alignof(RemoteAllocator) below, so we already require the complete type
definition at this point.
The free list builder in a checked build will only validate entries when
they are removed. This commit adds a validate method, so they can be
checked during teardown. This means that programs that leak memory
will still fail if the free list has become corrupt.
This avoids repeated double-tapping domestication of the same pointer in
!QueueHeadsAreTame builds, by keeping the current "front" pointer to the queue
in trusted locations (stack, register) rather than storing it back to possibly
client-accessible memory.
Motivated by renaming `FreeObject::{Head,Queue,AtomicQueue}Ptr` to
`freelist::...Ptr`, in fact go further, moving `FreeObject` itself to
`freelist::Object` and `FreeListBuilder` to `freelist::Builder` and
`FreeListIter` to `freelist::Iter`
Now that explicit annotations have gotten us through the refactoring, it's time
for the scaffolding to disappear. src/mem/freelist.h is left generic for any
future machinations, but `FreeObject::T<>`, the several `FreeObject::...Ptr<>`s,
`FreeListIter<>`, and `FreeListBuilder<>` are given default parameters and all
uses are shortened to use defaults where possible.
Without this it complains that LocalCache's constructor can't be constexpr
because small_fast_free_lists isn't initialized. It's not clear to me why it
didn't mind before, and nobody else seems to mind now, but this shouldn't break
anyone else, either.