diff --git a/headers/private/kernel/slab/Base.h b/headers/private/kernel/slab/Base.h index 1631a4035c..48ef6b3b49 100644 --- a/headers/private/kernel/slab/Base.h +++ b/headers/private/kernel/slab/Base.h @@ -11,7 +11,12 @@ #include +#include #include + +#include +#include +#include #include #ifdef __cplusplus @@ -32,14 +37,15 @@ typedef void (*base_cache_destructor)(void *cookie, void *object); /* base Slab implementation, opaque to the backend used. * - * NOTE: the caller is responsible for the Cache's locking. */ + * NOTE: the caller is responsible for the Cache's locking. + * Cache<> below handles it as well. */ typedef struct base_cache { char name[32]; size_t object_size; size_t cache_color_cycle; struct list empty, partial, full; - size_t empty_count; + size_t empty_count, pressure; base_cache_constructor constructor; base_cache_destructor destructor; void *cookie; @@ -56,11 +62,16 @@ typedef struct cache_slab { struct list_link link; } cache_slab; +// TODO add reclaim method to base_cache to be called under severe memory +// pressure so the slab owner can free as much buffers as possible. + void base_cache_init(base_cache *cache, const char *name, size_t object_size, size_t alignment, base_cache_constructor constructor, base_cache_destructor destructor, void *cookie); void base_cache_destroy(base_cache *cache, void (*return_slab)(base_cache *, cache_slab *)); +void base_cache_low_memory(base_cache *cache, int32 level, + void (*return_slab)(base_cache *, cache_slab *)); cache_object_link *base_cache_allocate_object(base_cache *cache); cache_object_link *base_cache_allocate_object_with_new_slab(base_cache *cache, @@ -83,6 +94,7 @@ typedef std::pair CacheObjectInfo; template class Cache : protected base_cache { public: + typedef Cache ThisCache; typedef base_cache_constructor Constructor; typedef base_cache_destructor Destructor; @@ -90,17 +102,29 @@ public: Constructor constructor, Destructor destructor, void *cookie) : fStrategy(this) { - base_cache_init(this, name, objectSize, alignment, constructor, - destructor, cookie); + if (benaphore_init(&fLock, name) >= B_OK) { + base_cache_init(this, name, objectSize, alignment, constructor, + destructor, cookie); + register_low_memory_handler(_LowMemory, this, 0); + } } ~Cache() { - base_cache_destroy(this, _ReturnSlab); + if (fLock.sem >= B_OK) { + benaphore_lock(&fLock); + unregister_low_memory_handler(_LowMemory, this); + base_cache_destroy(this, _ReturnSlab); + benaphore_destroy(&fLock); + } } + status_t InitCheck() const { return fLock.sem; } + void *AllocateObject(uint32_t flags) { + BenaphoreLocker _(fLock); + cache_object_link *link = base_cache_allocate_object(this); // if the cache is returning NULL it is because it ran out of slabs @@ -118,6 +142,8 @@ public: void ReturnObject(void *object) { + BenaphoreLocker _(fLock); + CacheObjectInfo location = fStrategy.ObjectInformation(object); if (base_cache_return_object(this, location.first, location.second)) @@ -127,9 +153,22 @@ public: private: static void _ReturnSlab(base_cache *self, cache_slab *slab) { - ((Cache *)self)->fStrategy.ReturnSlab(slab); + // Already locked, ~Cache() -> base_cache_destroy -> _ReturnSlab + ((ThisCache *)self)->fStrategy.ReturnSlab(slab); } + static void _LowMemory(void *_self, int32 level) + { + if (level == B_NO_LOW_MEMORY) + return; + + ThisCache *self = (ThisCache *)_self; + + BenaphoreLocker _(self->fLock); + base_cache_low_memory(self, level, _ReturnSlab); + } + + benaphore fLock; Strategy fStrategy; }; diff --git a/headers/private/kernel/slab/MergedStrategy.h b/headers/private/kernel/slab/MergedStrategy.h index 33cd84c289..3993f9e3b3 100644 --- a/headers/private/kernel/slab/MergedStrategy.h +++ b/headers/private/kernel/slab/MergedStrategy.h @@ -9,7 +9,6 @@ #ifndef _SLAB_MERGED_STRATEGY_H_ #define _SLAB_MERGED_STRATEGY_H_ -#include #include diff --git a/src/add-ons/kernel/network/stack/net_buffer.cpp b/src/add-ons/kernel/network/stack/net_buffer.cpp index 9bbd8c32dc..a3659b1945 100644 --- a/src/add-ons/kernel/network/stack/net_buffer.cpp +++ b/src/add-ons/kernel/network/stack/net_buffer.cpp @@ -73,7 +73,6 @@ typedef Cache DataNodeCache; static NetBufferCache *sNetBufferCache; static DataNodeCache *sDataNodeCache; -static benaphore sCachesLock; static status_t append_data(net_buffer *buffer, const void *data, size_t size); @@ -103,7 +102,6 @@ dump_buffer(net_buffer *_buffer) static inline data_header * allocate_data_header() { - BenaphoreLocker _(sCachesLock); return (data_header *)sDataNodeCache->AllocateObject(CACHE_DONT_SLEEP); } @@ -111,7 +109,6 @@ allocate_data_header() static inline net_buffer_private * allocate_net_buffer() { - BenaphoreLocker _(sCachesLock); return (net_buffer_private *)sNetBufferCache->AllocateObject( CACHE_DONT_SLEEP); } @@ -120,7 +117,6 @@ allocate_net_buffer() static inline void free_data_header(data_header *header) { - BenaphoreLocker _(sCachesLock); sDataNodeCache->ReturnObject(header); } @@ -128,7 +124,6 @@ free_data_header(data_header *header) static inline void free_net_buffer(net_buffer_private *buffer) { - BenaphoreLocker _(sCachesLock); sNetBufferCache->ReturnObject(buffer); } @@ -1185,25 +1180,31 @@ init_net_buffers() // TODO improve our code a bit so we can add constructors // and keep around half-constructed buffers in the slab - status_t status = benaphore_init(&sCachesLock, "net buffer cache lock"); - if (status < B_OK) - return status; - sNetBufferCache = new (std::nothrow) NetBufferCache("net buffer cache", sizeof(net_buffer_private), 8, NULL, NULL, NULL); - if (sNetBufferCache == NULL) { - benaphore_destroy(&sCachesLock); + if (sNetBufferCache == NULL) return B_NO_MEMORY; + + status_t status = sNetBufferCache->InitCheck(); + if (status < B_OK) { + delete sNetBufferCache; + return status; } sDataNodeCache = new (std::nothrow) DataNodeCache("data node cache", BUFFER_SIZE, 0, NULL, NULL, NULL); if (sDataNodeCache == NULL) { - benaphore_destroy(&sCachesLock); delete sNetBufferCache; return B_NO_MEMORY; } + status = sDataNodeCache->InitCheck(); + if (status < B_OK) { + delete sDataNodeCache; + delete sNetBufferCache; + return status; + } + return B_OK; } @@ -1211,13 +1212,9 @@ init_net_buffers() status_t uninit_net_buffers() { - benaphore_lock(&sCachesLock); - delete sNetBufferCache; delete sDataNodeCache; - benaphore_destroy(&sCachesLock); - return B_OK; } diff --git a/src/system/kernel/slab/Slab.cpp b/src/system/kernel/slab/Slab.cpp index 7a404c4fcb..a479a0cb66 100644 --- a/src/system/kernel/slab/Slab.cpp +++ b/src/system/kernel/slab/Slab.cpp @@ -107,6 +107,9 @@ base_cache_init(base_cache *cache, const char *name, size_t objectSize, cache->empty_count = 0; + // pressure is increased whenever we need a slab and don't have one + cache->pressure = 0; + cache->constructor = constructor; cache->destructor = destructor; cache->cookie = cookie; @@ -132,14 +135,54 @@ base_cache_destroy(base_cache *cache, } +void +base_cache_low_memory(base_cache *cache, int32 level, + void (*return_slab)(base_cache *, cache_slab *)) +{ + size_t minimumAllowed; + + // only thing we can do right now is free up empty slabs + + switch (level) { + case B_LOW_MEMORY_NOTE: + minimumAllowed = cache->pressure / 2 + 1; + break; + + case B_LOW_MEMORY_WARNING: + cache->pressure /= 2; + minimumAllowed = 0; + break; + + default: + cache->pressure = 0; + minimumAllowed = 0; + break; + } + + if (cache->empty_count <= minimumAllowed) + return; + + TRACE_CACHE(cache, "cache: memory pressure, will release down to %lu.", + minimumAllowed); + + while (cache->empty_count > minimumAllowed) { + cache_slab *slab = (cache_slab *)list_remove_head_item(&cache->empty); + return_slab(cache, slab); + cache->empty_count--; + } +} + + cache_object_link * base_cache_allocate_object(base_cache *cache) { cache_slab *slab; if (list_is_empty(&cache->partial)) { - if (list_is_empty(&cache->empty)) + if (list_is_empty(&cache->empty)) { + cache->pressure++; return NULL; + } cache->empty_count--; slab = (cache_slab *)list_remove_head_item(&cache->empty); @@ -184,7 +227,7 @@ base_cache_return_object(base_cache *cache, cache_slab *slab, if (slab->count == slab->size) { list_remove_item(&cache->partial, slab); - if (cache->empty_count > 2) + if (cache->empty_count >= cache->pressure) return 1; cache->empty_count++;