]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
BUG/MINOR: pools: always flush pools about to be destroyed
authorWilly Tarreau <w@1wt.eu>
Wed, 9 Feb 2022 15:19:24 +0000 (16:19 +0100)
committerWilly Tarreau <w@1wt.eu>
Mon, 14 Feb 2022 19:10:43 +0000 (20:10 +0100)
When destroying a pool (e.g. at exit or when resizing buffers), it's
important to try to free all their local objects otherwise we can leave
some in the cache. This is particularly visible when changing "bufsize",
because "show pools" will then show two "trash" pools, one of which
contains a single object in cache (which is fortunately not reachable).
In all cases this happens while single-threaded so that's easy to do,
we just have to do it on the current thread.

The easiest way to do this is to pass an extra argument to function
pool_evict_from_local_cache() to force a full flush instead of a
partial one.

This can probably be backported to about all branches where this
applies, but at least 2.4 needs it.

include/haproxy/pool.h
src/pool.c

index 4392a2e974e94b2dad17f71650cc880dfa27369e..de41fa1fe905aee63127a5cf046b9b1af42c05c8 100644 (file)
@@ -130,7 +130,7 @@ void __pool_free(struct pool_head *pool, void *ptr);
 extern THREAD_LOCAL size_t pool_cache_bytes;   /* total cache size */
 extern THREAD_LOCAL size_t pool_cache_count;   /* #cache objects   */
 
-void pool_evict_from_local_cache(struct pool_head *pool);
+void pool_evict_from_local_cache(struct pool_head *pool, int full);
 void pool_evict_from_local_caches(void);
 void pool_put_to_cache(struct pool_head *pool, void *ptr, const void *caller);
 
index 04677ec9abca690b9f38d4ef80ea83f9360354a9..70a80410d7b23322139fff96a22b5536b591f122 100644 (file)
@@ -364,15 +364,16 @@ static void pool_evict_last_items(struct pool_head *pool, struct pool_cache_head
  * objects is no more than 16+1/8 of the total number of locally cached objects
  * or the total size of the local cache is no more than 75% of its maximum (i.e.
  * we don't want a single cache to use all the cache for itself). For this, the
- * list is scanned in reverse.
+ * list is scanned in reverse. If <full> is non-null, all objects are evicted.
  */
-void pool_evict_from_local_cache(struct pool_head *pool)
+void pool_evict_from_local_cache(struct pool_head *pool, int full)
 {
        struct pool_cache_head *ph = &pool->cache[tid];
 
-       while (ph->count >= CONFIG_HAP_POOL_CLUSTER_SIZE &&
-              ph->count >= 16 + pool_cache_count / 8 &&
-              pool_cache_bytes > CONFIG_HAP_POOL_CACHE_SIZE * 3 / 4) {
+       while ((ph->count && full) ||
+              (ph->count >= CONFIG_HAP_POOL_CLUSTER_SIZE &&
+               ph->count >= 16 + pool_cache_count / 8 &&
+               pool_cache_bytes > CONFIG_HAP_POOL_CACHE_SIZE * 3 / 4)) {
                pool_evict_last_items(pool, ph, CONFIG_HAP_POOL_CLUSTER_SIZE);
        }
 }
@@ -418,7 +419,7 @@ void pool_put_to_cache(struct pool_head *pool, void *ptr, const void *caller)
 
        if (unlikely(pool_cache_bytes > CONFIG_HAP_POOL_CACHE_SIZE * 3 / 4)) {
                if (ph->count >= 16 + pool_cache_count / 8 + CONFIG_HAP_POOL_CLUSTER_SIZE)
-                       pool_evict_from_local_cache(pool);
+                       pool_evict_from_local_cache(pool, 0);
                if (pool_cache_bytes > CONFIG_HAP_POOL_CACHE_SIZE)
                        pool_evict_from_local_caches();
        }
@@ -715,6 +716,9 @@ void pool_free_area_uaf(void *area, size_t size)
 void *pool_destroy(struct pool_head *pool)
 {
        if (pool) {
+#ifdef CONFIG_HAP_POOLS
+               pool_evict_from_local_cache(pool, 1);
+#endif
                pool_flush(pool);
                if (pool->used)
                        return pool;