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.
The spelling of inline assembler constraints on Morello would be
different, but Jessica Clarke points out that we should just be using
the builtin when available, so do that instead.
Co-authored-by: Jessica Clarke <jrtc27@jrtc27.com>
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.
Errno is not required to be 0 on return from malloc,
so don't bother trying to make it 0. Leads to false test failures where
libc calls have not reset it after a failure.
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.
The correct thing to do, of course, is to fix these upstream, but that requires
understanding exactly what's wrong, and that's harder than just not tickling the
bugs.
This adds a CHERI AAL and expands the FreeBSD PAL to cover CHERI. It updates a
comment in ds/address.h now that there is an example architecture that
differentiates uintptr_t and address_t.
This dates back to the much earlier design that required the use of an authority
map. Since the current CHERI design does not, go ahead and ask the platform
whenever underlying allocation requests are sufficiently aligned.
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).
- Grab a larger second allocation on the first allocator to dodge the sizeclass
of the prior alloc on that allocator *and* any implicit, bootstrapping slabs
that get opened (e.g., for remote queue message stubs).
- De-FAST_PATH the domestication function. No need to always inline it, here.
- Document things a little better
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.
We'll want user_address_control_type in some particular PALs, so it can't live
in pal.h.
While here, make the spelling be capptr::is_spatial_refinement.
- `check_result()` `abort()` on `null` and non-`nullptr` result. Otherwise it
just prints and doesn't end the test
- Don't call `realloc(, 0)`; this has never been consistent and the current C2x
draft (see §7.22.3.5 and N2464) finally just declares it to be undefined
behavior. POSIX (2017) tolerates our current behavior of freeing the
passed-in pointer and returning a new object.
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.
Instantiate two allocators and arrange for a message to get passed between them
by exploiting the existing slow-paths' handling of message queues. Count and
CHECK the number of domestication calls during this message passing. For a
little more excitement, pave over the forward pointer in the freelist::Object::T
that is the message and have the domestication callback patch the original value
back; should we somehow fail to invoke the domestication callback on that
address, this will induce a crash (WHP, on both CHECK_CLIENT and unchecked
builds).
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.