Commit graph

7 commits

Author SHA1 Message Date
Johan Hedberg
47a28a9612 mempool: Remove unnecessary call to get_pool()
The pointer that get_pool() returns is already stored in the 'p'
variable.

Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
2018-01-12 08:05:08 -05:00
Johan Hedberg
1a8a8d9019 mempool: Don't store redundant information for k_malloc/k_free
We don't need to store the full k_mem_block, rather just the
k_mem_block_id. In effect, this saves 4 bytes of memory per allocated
memory chunk. Also take advantage of the newly introduced
k_mem_pool_free_id API here.

Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
2018-01-12 08:05:08 -05:00
Johan Hedberg
7d887cb615 mempool: Add k_mem_pool_free_id API
The k_mem_pool_free API has no use for the full k_mem_block struct. In
particular, it only needs the k_mem_block_id. Introduce a new API
which takes only this essential struct. This paves the way to
simplify & improve the k_malloc/k_free implementation a bit.

Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
2018-01-12 08:05:08 -05:00
Andrew Boie
a79c69823f mempool: add assertion for calloc bounds overflow
Signed-off-by: Andrew Boie <andrew.p.boie@intel.com>
2017-11-14 12:50:10 -08:00
Andrew Boie
7f95e83361 mempool: add k_calloc()
This uses the kernel heap to implement traditional calloc()
semantics.

Signed-off-by: Andrew Boie <andrew.p.boie@intel.com>
2017-11-13 09:50:15 -08:00
Andy Ross
4c63af8434 mem_pool: Don't check level_empty() before breaking a block
This test was just wrong.  If the current thread did not race with any
others during the allocation process, then the result will be false
because it was detected so earlier in the function.  If we did race,
then sure: it might be true now if someone snuck in and freed a block.
But so what?  We already have the block we want to break.  The
behavior in the code as written was to early-exit from the break loop,
returning a buffer that was larger than the one requested (though
otherwise benign -- we wouldn't leak, just waste memory).  No idea
what I was thinking.

Thanks to Du Quanwen for the diagnosis.

Signed-off-by: Andy Ross <andrew.j.ross@intel.com>
2017-07-31 09:14:59 -07:00
Andy Ross
73cb9586ce k_mem_pool: Complete rework
This patch amounts to a mostly complete rewrite of the k_mem_pool
allocator, which had been the source of historical complaints vs. the
one easily available in newlib.  The basic design of the allocator is
unchanged (it's still a 4-way buddy allocator), but the implementation
has made different choices throughout.  Major changes:

Space efficiency: The old implementation required ~2.66 bytes per
"smallest block" in overhead, plus 16 bytes per log4 "level" of the
allocation tree, plus a global tracking struct of 32 bytes and a very
surprising 12 byte overhead (in struct k_mem_block) per active
allocation on top of the returned data pointer.  This new allocator
uses a simple bit array as the only per-block storage and places the
free list into the freed blocks themselves, requiring only ~1.33 bits
per smallest block, 12 bytes per level, 32 byte globally and only 4
bytes of per-allocation bookeeping.  And it puts more of the generated
tree into BSS, slightly reducing binary sizes for non-trivial pool
sizes (even as the code size itself has increased a tiny bit).

IRQ safe: atomic operations on the store have been cut down to be at
most "4 bit sets and dlist operations" (i.e. a few dozen
instructions), reducing latency significantly and allowing us to lock
against interrupts cleanly from all APIs.  Allocations and frees can
be done from ISRs now without limitation (well, obviously you can't
sleep, so "timeout" must be K_NO_WAIT).

Deterministic performance: there is no more "defragmentation" step
that must be manually managed.  Block coalescing is done synchronously
at free time and takes constant time (strictly log4(num_levels)), as
the detection of four free "partner bits" is just a simple shift and
mask operation.

Cleaner behavior with odd sizes.  The old code assumed that the
specified maximum size would be a power of four multiple of the
minimum size, making use of non-standard buffer sizes problematic.
This implementation re-aligns the sub-blocks at each level and can
handle situations wehre alignment restrictions mean fewer than 4x will
be available.  If you want precise layout control, you can still
specify the sizes rigorously.  It just doesn't break if you don't.

More portable: the original implementation made use of GNU assembler
macros embedded inline within C __asm__ statements.  Not all
toolchains are actually backed by a GNU assembler even when the
support the GNU assembly syntax.  This is pure C, albeit with some
hairy macros to expand the compile-time-computed values.

Related changes that had to be rolled into this patch for bisectability:

* The new allocator has a firm minimum block size of 8 bytes (to store
  the dlist_node_t).  It will "work" with smaller requested min_size
  values, but obviously makes no firm promises about layout or how
  many will be available.  Unfortunately many of the tests were
  written with very small 4-byte minimum sizes and to assume exactly
  how many they could allocate.  Bump the sizes to match the allocator
  minimum.

* The mbox and pipes API made use of the internals of k_mem_block and
  had to be ported to the new scheme.  Blocks no longer store a
  backpointer to the pool that allocated them (it's an integer ID in a
  bitfield) , so if you want to "nullify" them you have to use the
  data pointer.

* test_mbox_api had a bug were it was prematurely freeing k_mem_blocks
  that it sent through the mailbox.  This worked in the old allocator
  because the memory wouldn't be touched when freed, but now we stuff
  list pointers in there and the bug was exposed.

* Remove test_mpool_options: the options (related to defragmentation
  behavior) tested no longer exist.

Signed-off-by: Andy Ross <andrew.j.ross@intel.com>
2017-05-13 14:39:41 -04:00