Skip to content

Commit

Permalink
mcache: allow use of arbitrary sizes for malloc-style interface
Browse files Browse the repository at this point in the history
This modifies the mcache to allow using a malloc/free-style interface (where
the allocation size is not specified on a free()) when allocation sizes exceed
the largest objcache size. Such use would previously result in an error when
attempting to free such large allocations. To accomplish this, the mcache now
maintains a mapping of large allocation addresses to their corresponding
sizes. When a deallocate with an unspecified (-1ull) size occurs, a table
lookup/removal is made to check if the address corresponds to such a
"fallback" allocation and, if so, recover the allocation size.

The cost of using this fallback table is an insertion when making a
fallback (large) allocation and a table lookup/removal when deallocating a
fallback allocation of a known size, or on any deallocation (free) of an
unknown size. This should not impact the vast majority of mcache uses, for
which 1) the allocation size falls within the domain of the contained
objcaches and 2) the size parameter is valid on deallocation.
  • Loading branch information
wjhun committed Jun 30, 2023
1 parent 6ecf74e commit 743c056
Showing 1 changed file with 71 additions and 9 deletions.
80 changes: 71 additions & 9 deletions src/runtime/heap/mcache.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ typedef struct mcache {
u64 allocated;
u64 parent_threshold;
tuple mgmt;
table fallbacks;
} *mcache;

u64 mcache_alloc(heap h, bytes b)
Expand All @@ -41,19 +42,29 @@ u64 mcache_alloc(heap h, bytes b)
rputs(": ");
#endif
if (b > m->parent_threshold) {
if (!m->fallbacks) {
m->fallbacks = allocate_table(m->meta, identity_key, pointer_equal);
if (m->fallbacks == INVALID_ADDRESS) {
rputs("mcache_alloc: failed to allocate fallbacks table\n");
return INVALID_PHYSICAL;
}
}
u64 size = pad(b, m->parent->pagesize);
u64 a = allocate_u64(m->parent, size);
if (a != INVALID_PHYSICAL) {
m->allocated += size;
table_set(m->fallbacks, pointer_from_u64(a), pointer_from_u64(b));
}
#ifdef MCACHE_DEBUG
rputs("fallback to parent, size ");
print_u64(size);
rputs(", addr ");
print_u64(a);
rputs("\n");
rputs("fallback to parent, size ");
print_u64(b);
rputs(", padded to ");
print_u64(size);
rputs(", addr ");
print_u64(a);
rputs("\n");
#endif
return a;
}
return a;
}

/* Could become a binary search if search set is large... */
Expand Down Expand Up @@ -102,15 +113,52 @@ void mcache_dealloc(heap h, u64 a, bytes b)
#endif

mcache m = (mcache)h;
u64 size = 0;

/* The fallback table tracks allocations that fall back to the parent
heap. This allows use of a "malloc-style" interface to the mcache in
which the allocation size is not specified on a deallocate. The cost of
this is a table insertion when making a fallback allocation and a table
lookup/removal when deallocating a fallback allocation of a known size,
or on any deallocation (free) of an unknown size. */
if (b != -1ull && b > m->parent_threshold) {
u64 size = pad(b, m->parent->pagesize);
if (!m->fallbacks) {
rputs("mcache_dealloc: fallbacks table not allocated\n");
return;
}
size = u64_from_pointer(table_remove(m->fallbacks, pointer_from_u64(a)));
if (!size) {
rputs("mcache_dealloc: address ");
print_u64(a);
rputs(" (size ");
print_u64(b);
rputs(") not found in fallback table\n");
return;
}
if (size != b) {
rputs("mcache_dealloc: address ");
print_u64(a);
rputs(" (given size ");
print_u64(b);
rputs(") does not match alloc size (");
print_u64(size);
rputs("\n");
}
size = pad(size, m->parent->pagesize);
}
if (b == -1ull && m->fallbacks) {
size = u64_from_pointer(table_remove(m->fallbacks, pointer_from_u64(a)));
if (size > 0)
size = pad(size, m->parent->pagesize);
}

if (size) {
#ifdef MCACHE_DEBUG
rputs("dealloc size ");
print_u64(b);
rputs(", pagesize ");
print_u64(m->parent->pagesize);
rputs(", parent alloc, padded size ");
rputs(", parent alloc size ");
print_u64(size);
rputs("\n");
#endif
Expand Down Expand Up @@ -178,6 +226,19 @@ void destroy_mcache(heap h)
if (o)
o->destroy(o);
}
if (m->fallbacks) {
table_foreach(m->fallbacks, p, size) {
#ifdef MCACHE_DEBUG
rputs(" dealloc fallback @ ");
print_u64(u64_from_pointer(p));
rputs(", size ");
print_u64(u64_from_pointer(size));
rputs("\n");
#endif
deallocate(m->parent, p, u64_from_pointer(size));
}
deallocate_table(m->fallbacks);
}
deallocate(m->meta, m, sizeof(struct mcache));
}

Expand Down Expand Up @@ -291,6 +352,7 @@ heap allocate_mcache(heap meta, heap parent, int min_order, int max_order, bytes
m->allocated = 0;
m->parent_threshold = U64_FROM_BIT(max_order);
m->mgmt = 0;
m->fallbacks = 0;

for(int i = 0, order = min_order; order <= max_order; i++, order++) {
u64 obj_size = U64_FROM_BIT(order);
Expand Down

0 comments on commit 743c056

Please sign in to comment.