-
Notifications
You must be signed in to change notification settings - Fork 136
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
introduce mcache, move lwip allocations to mcache #69
Changes from all commits
2874d03
821212b
45fdc2b
e91d4b7
3098451
8d95b96
bf9bcca
248fdde
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
/* multi-cache heap | ||
|
||
This is essentially a wrapper heap for a set of caches of varying | ||
object sizes. Object sizes are specified on heap creation. Allocations | ||
are made from the cache of the smallest object size equal to or greater | ||
than the alloc size. | ||
*/ | ||
|
||
//#define MCACHE_DEBUG | ||
|
||
#include <runtime.h> | ||
|
||
typedef struct mcache { | ||
struct heap h; | ||
heap parent; | ||
heap meta; | ||
vector caches; | ||
} *mcache; | ||
|
||
u64 mcache_alloc(heap h, bytes b) | ||
{ | ||
mcache m = (mcache)h; | ||
heap o; | ||
#ifdef MCACHE_DEBUG | ||
console("mcache_alloc: heap "); | ||
print_u64(u64_from_pointer(h)); | ||
console(", size "); | ||
print_u64(b); | ||
console(": "); | ||
#endif | ||
/* Could become a binary search if search set is large... */ | ||
vector_foreach(m->caches, o) { | ||
if (o && b <= o->pagesize) { | ||
u64 a = allocate_u64(o, o->pagesize); | ||
if (a != INVALID_PHYSICAL) | ||
h->allocated += o->pagesize; | ||
#ifdef MCACHE_DEBUG | ||
console("obj size "); | ||
print_u64(o->pagesize); | ||
console(", addr "); | ||
print_u64(a); | ||
console("\n"); | ||
#endif | ||
return a; | ||
} | ||
} | ||
#ifdef MCACHE_DEBUG | ||
console("no matching cache; fail\n"); | ||
#endif | ||
return INVALID_PHYSICAL; | ||
} | ||
|
||
void mcache_dealloc(heap h, u64 a, bytes b) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. its worth revisiting whether or not deallocate should take the size. at some point I got tired of having to keep track of it behind the interface when in (almost) all cases, the caller has a really good idea. i think that probably still true - but given that a large consumer that we don't want to mess with has normal malloc/free, maybe not the most practical There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The only issue I can think of in this case is whether h->allocated should represent actual bytes allocated by the heap or bytes requested / freed by the caller. If the former is true, then I don't see what we'd do with a dealloc size other than a sanity check (which at this point is one-sided since we don't relate the found cache heap to the vector and thus don't know the next largest size, so we just make sure that it's not larger than the given cache size...it's all kind of pointless anyway if we can verify that the object is indeed owned by the leaf cache...) |
||
{ | ||
#ifdef MCACHE_DEBUG | ||
console("mcache_dealloc: heap "); | ||
print_u64(u64_from_pointer(h)); | ||
console(", addr "); | ||
print_u64(a); | ||
console(", size "); | ||
print_u64(b); | ||
console("\n"); | ||
#endif | ||
mcache m = (mcache)h; | ||
heap o = objcache_from_object(a, m->parent->pagesize); | ||
if (o == INVALID_ADDRESS) { | ||
msg_err("mcache %p: can't find cache for object %P, size %d; leaking\n", | ||
m, a, b); | ||
return; | ||
} | ||
|
||
/* We don't really need the size, but if we're given a valid one, | ||
make some attempt to verify it. */ | ||
if (b != -1ull && b > o->pagesize) { | ||
msg_err("dealloc size (%d) exceeds found cache size (%d); leaking\n", | ||
b, o->pagesize); | ||
return; | ||
} | ||
assert(h->allocated >= o->pagesize); | ||
h->allocated -= o->pagesize; | ||
deallocate(o, a, o->pagesize); | ||
} | ||
|
||
void destroy_mcache(heap h) | ||
{ | ||
mcache m = (mcache)h; | ||
heap o; | ||
vector_foreach(m->caches, o) { | ||
if (o) | ||
o->destroy(o); | ||
} | ||
deallocate(m->meta, m, sizeof(struct mcache)); | ||
} | ||
|
||
heap allocate_mcache(heap meta, heap parent, int min_order, int max_order) | ||
{ | ||
mcache m = allocate(meta, sizeof(struct mcache)); | ||
if (m == INVALID_ADDRESS) | ||
return INVALID_ADDRESS; | ||
|
||
#ifdef MCACHE_DEBUG | ||
console("allocate_mcache: heap at "); | ||
print_u64(u64_from_pointer(m)); | ||
console("\n"); | ||
#endif | ||
|
||
m->h.alloc = mcache_alloc; | ||
m->h.dealloc = mcache_dealloc; | ||
m->h.destroy = destroy_mcache; | ||
m->h.pagesize = 1 << min_order; /* default to smallest obj size */ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. random note...i'm not sure having a non-integral order (i'm getting used to that, for some reason never used that term before). ever makes sense. so maybe pagesize should always be log2 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would simplify things in some cases, though take away the possibility of caching exact sizes of objects when not a non-integral order (wow, it is fun to write!). |
||
m->h.allocated = 0; | ||
m->meta = meta; | ||
m->parent = parent; | ||
m->caches = allocate_vector(meta, 1); | ||
|
||
for(int i=0, order = min_order; order <= max_order; i++, order++) { | ||
u64 obj_size = 1 << order; | ||
heap h = allocate_objcache(meta, parent, obj_size); | ||
#ifdef MCACHE_DEBUG | ||
console(" - cache size "); | ||
print_u64(obj_size); | ||
console(": "); | ||
print_u64(u64_from_pointer(h)); | ||
console("\n"); | ||
#endif | ||
if (h == INVALID_ADDRESS) { | ||
console("failed to allocate mcache\n"); | ||
destroy_mcache((heap)m); | ||
return INVALID_ADDRESS; | ||
} | ||
vector_set(m->caches, i, h); | ||
} | ||
return (heap)m; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is there something specific to the leaves that require them to be caches? or it is any set of heap where the objects fall into the same size bucket (2^n)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The only requirements are that:
So the only thing that really makes this specific to the objcache is the call to objcache_from_object(). I suppose this could be made into a call in the abstract heap type (heap_from_pointer()?), but I don't know what other heaps would support this.
Also, at this point, objcache (and thus mcache) can accept any size object, not just 2^n.