static bool talloc_memlimit_check(struct talloc_memlimit *limit, size_t size);
static bool talloc_memlimit_update(struct talloc_memlimit *limit,
size_t old_size, size_t new_size);
+static void talloc_memlimit_grow(struct talloc_memlimit *limit,
+ size_t size);
+static void talloc_memlimit_shrink(struct talloc_memlimit *limit,
+ size_t size);
+static void talloc_memlimit_update_on_free(struct talloc_chunk *tc);
typedef int (*talloc_destructor_t)(void *);
* if 'limit' is set it means all *new* children of the context will
* be limited to a total aggregate size ox max_size for memory
* allocations.
- * cur_size is used to kep track of the current use
+ * cur_size is used to keep track of the current use
*/
struct talloc_memlimit *limit;
*/
pool->hdr.c.pool = tc_pool_first_chunk(pool);
tc_invalidate_pool(pool);
- } else if (unlikely(pool->hdr.object_count == 0)) {
+ return;
+ }
+
+ if (unlikely(pool->hdr.object_count == 0)) {
/*
* we mark the freed memory with where we called the free
* from. This means on a double free error we can report where
TC_INVALIDATE_FULL_CHUNK(&pool->hdr.c);
free(pool);
- } else if (pool->hdr.c.pool == next_tc) {
+ return;
+ }
+
+ if (pool->hdr.c.pool == next_tc) {
/*
* if pool->pool still points to end of
* 'tc' (which is stored in the 'next_tc' variable),
* we can reclaim the memory of 'tc'.
*/
pool->hdr.c.pool = tc;
+ return;
}
+
+ /*
+ * Do nothing. The memory is just "wasted", waiting for the pool
+ * itself to be freed.
+ */
}
static inline void _talloc_free_children_internal(struct talloc_chunk *tc,
}
pool->hdr.object_count--;
- if (unlikely(pool->hdr.object_count == 0)) {
- TC_INVALIDATE_FULL_CHUNK(tc);
- free(tc);
+
+ if (likely(pool->hdr.object_count != 0)) {
+ return 0;
}
- } else if (tc->flags & TALLOC_FLAG_POOLMEM) {
- _talloc_free_poolmem(tc, location);
- } else {
+
TC_INVALIDATE_FULL_CHUNK(tc);
free(tc);
+ return 0;
}
+
+ if (tc->flags & TALLOC_FLAG_POOLMEM) {
+ _talloc_free_poolmem(tc, location);
+ return 0;
+ }
+
+ TC_INVALIDATE_FULL_CHUNK(tc);
+ free(tc);
return 0;
}
break;
case TOTAL_MEM_LIMIT:
if (likely(tc->name != TALLOC_MAGIC_REFERENCE)) {
- total = tc->size + TC_HDR_SIZE;
+ /*
+ * Don't count memory allocated from a pool
+ * when calculating limits. Only count the
+ * pool itself.
+ */
+ if (!(tc->flags & TALLOC_FLAG_POOLMEM)) {
+ total = tc->size + TC_HDR_SIZE;
+ }
}
break;
}
for (l = limit; l != NULL; l = l->upper) {
if (l->max_size != 0 &&
((l->max_size <= l->cur_size) ||
- (l->max_size - l->cur_size < TC_HDR_SIZE+size))) {
+ (l->max_size - l->cur_size < size))) {
return false;
}
}
return true;
}
+/*
+ Update memory limits when freeing a talloc_chunk.
+*/
+static void talloc_memlimit_update_on_free(struct talloc_chunk *tc)
+{
+ if (!tc->limit) {
+ return;
+ }
+
+ /*
+ * Pool entries don't count. Only the pools
+ * themselves are counted as part of the memory
+ * limits.
+ */
+ if (tc->flags & TALLOC_FLAG_POOLMEM) {
+ return;
+ }
+
+ /*
+ * If we are part of a memory limited context hierarchy
+ * we need to subtract the memory used from the counters
+ */
+
+ talloc_memlimit_shrink(tc->limit, tc->size+TC_HDR_SIZE);
+
+ if (tc->limit->parent == tc) {
+ free(tc->limit);
+ }
+
+ tc->limit = NULL;
+}
+
+/*
+ Increase memory limit accounting after a malloc/realloc.
+*/
+static void talloc_memlimit_grow(struct talloc_memlimit *limit,
+ size_t size)
+{
+ struct talloc_memlimit *l;
+
+ for (l = limit; l != NULL; l = l->upper) {
+ size_t new_cur_size = l->cur_size + size;
+ if (new_cur_size < l->cur_size) {
+ talloc_abort("logic error in talloc_memlimit_grow\n");
+ return;
+ }
+ l->cur_size = new_cur_size;
+ }
+}
+
+/*
+ Decrease memory limit accounting after a free/realloc.
+*/
+static void talloc_memlimit_shrink(struct talloc_memlimit *limit,
+ size_t size)
+{
+ struct talloc_memlimit *l;
+
+ for (l = limit; l != NULL; l = l->upper) {
+ if (l->cur_size < size) {
+ talloc_abort("logic error in talloc_memlimit_shrink\n");
+ return;
+ }
+ l->cur_size = l->cur_size - size;
+ }
+}
+
static bool talloc_memlimit_update(struct talloc_memlimit *limit,
size_t old_size, size_t new_size)
{