mirror of
http://git.haproxy.org/git/haproxy.git/
synced 2025-01-20 04:30:46 +00:00
ed891fda52
Till now the local pool caches were implemented only when lockless pools were in use. This was mainly due to the difficulties to disentangle the code parts. However the locked pools would further benefit from the local cache, and having this would reduce the variants in the code. This patch does just this. It adds a new debug macro DEBUG_NO_LOCAL_POOLS to forcefully disable local pool caches, and makes sure that the high level functions are now strictly the same between locked and lockless (pool_alloc(), pool_alloc_dirty(), pool_free(), pool_get_first()). The pool index calculation was moved inside the CONFIG_HAP_LOCAL_POOLS guards. This allowed to move them out of the giant #ifdef and to significantly reduce the code duplication. A quick perf test shows that with locked pools the performance increases by roughly 10% on 8 threads and gets closer to the lockless one.
221 lines
6.4 KiB
C
221 lines
6.4 KiB
C
/*
|
|
* include/common/buffer.h
|
|
* Buffer management definitions, macros and inline functions.
|
|
*
|
|
* Copyright (C) 2000-2012 Willy Tarreau - w@1wt.eu
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation, version 2.1
|
|
* exclusively.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#ifndef _COMMON_BUFFER_H
|
|
#define _COMMON_BUFFER_H
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <haproxy/api.h>
|
|
#include <haproxy/buf.h>
|
|
#include <common/chunk.h>
|
|
#include <import/ist.h>
|
|
#include <haproxy/istbuf.h>
|
|
#include <common/memory.h>
|
|
|
|
#include <proto/activity.h>
|
|
|
|
/* an element of the <buffer_wq> list. It represents an object that need to
|
|
* acquire a buffer to continue its process. */
|
|
struct buffer_wait {
|
|
void *target; /* The waiting object that should be woken up */
|
|
int (*wakeup_cb)(void *); /* The function used to wake up the <target>, passed as argument */
|
|
struct mt_list list; /* Next element in the <buffer_wq> list */
|
|
};
|
|
|
|
extern struct pool_head *pool_head_buffer;
|
|
extern struct mt_list buffer_wq;
|
|
__decl_thread(extern HA_SPINLOCK_T buffer_wq_lock);
|
|
|
|
int init_buffer();
|
|
void buffer_dump(FILE *o, struct buffer *b, int from, int to);
|
|
|
|
/*****************************************************************/
|
|
/* These functions are used to compute various buffer area sizes */
|
|
/*****************************************************************/
|
|
|
|
/* Return 1 if the buffer has less than 1/4 of its capacity free, otherwise 0 */
|
|
static inline int buffer_almost_full(const struct buffer *buf)
|
|
{
|
|
if (b_is_null(buf))
|
|
return 0;
|
|
|
|
return b_almost_full(buf);
|
|
}
|
|
|
|
/**************************************************/
|
|
/* Functions below are used for buffer allocation */
|
|
/**************************************************/
|
|
|
|
/* Allocates a buffer and assigns it to *buf. If no memory is available,
|
|
* ((char *)1) is assigned instead with a zero size. No control is made to
|
|
* check if *buf already pointed to another buffer. The allocated buffer is
|
|
* returned, or NULL in case no memory is available.
|
|
*/
|
|
static inline struct buffer *b_alloc(struct buffer *buf)
|
|
{
|
|
char *area;
|
|
|
|
*buf = BUF_WANTED;
|
|
area = pool_alloc_dirty(pool_head_buffer);
|
|
if (unlikely(!area)) {
|
|
activity[tid].buf_wait++;
|
|
return NULL;
|
|
}
|
|
|
|
buf->area = area;
|
|
buf->size = pool_head_buffer->size;
|
|
return buf;
|
|
}
|
|
|
|
/* Allocates a buffer and assigns it to *buf. If no memory is available,
|
|
* ((char *)1) is assigned instead with a zero size. No control is made to
|
|
* check if *buf already pointed to another buffer. The allocated buffer is
|
|
* returned, or NULL in case no memory is available. The difference with
|
|
* b_alloc() is that this function only picks from the pool and never calls
|
|
* malloc(), so it can fail even if some memory is available.
|
|
*/
|
|
static inline struct buffer *b_alloc_fast(struct buffer *buf)
|
|
{
|
|
char *area;
|
|
|
|
*buf = BUF_WANTED;
|
|
area = pool_get_first(pool_head_buffer);
|
|
if (unlikely(!area))
|
|
return NULL;
|
|
|
|
buf->area = area;
|
|
buf->size = pool_head_buffer->size;
|
|
return buf;
|
|
}
|
|
|
|
/* Releases buffer <buf> (no check of emptiness). The buffer's head is marked
|
|
* empty.
|
|
*/
|
|
static inline void __b_free(struct buffer *buf)
|
|
{
|
|
char *area = buf->area;
|
|
|
|
/* let's first clear the area to save an occasional "show sess all"
|
|
* glancing over our shoulder from getting a dangling pointer.
|
|
*/
|
|
*buf = BUF_NULL;
|
|
__ha_barrier_store();
|
|
pool_free(pool_head_buffer, area);
|
|
}
|
|
|
|
/* Releases buffer <buf> if allocated, and marks it empty. */
|
|
static inline void b_free(struct buffer *buf)
|
|
{
|
|
if (buf->size)
|
|
__b_free(buf);
|
|
}
|
|
|
|
/* Ensures that <buf> is allocated. If an allocation is needed, it ensures that
|
|
* there are still at least <margin> buffers available in the pool after this
|
|
* allocation so that we don't leave the pool in a condition where a session or
|
|
* a response buffer could not be allocated anymore, resulting in a deadlock.
|
|
* This means that we sometimes need to try to allocate extra entries even if
|
|
* only one buffer is needed.
|
|
*
|
|
* We need to lock the pool here to be sure to have <margin> buffers available
|
|
* after the allocation, regardless how many threads that doing it in the same
|
|
* time. So, we use internal and lockless memory functions (prefixed with '__').
|
|
*/
|
|
static inline struct buffer *b_alloc_margin(struct buffer *buf, int margin)
|
|
{
|
|
char *area;
|
|
ssize_t idx __maybe_unused;
|
|
unsigned int cached;
|
|
|
|
if (buf->size)
|
|
return buf;
|
|
|
|
cached = 0;
|
|
#ifdef CONFIG_HAP_LOCAL_POOLS
|
|
idx = pool_get_index(pool_head_buffer);
|
|
if (idx >= 0)
|
|
cached = pool_cache[tid][idx].count;
|
|
#endif
|
|
|
|
*buf = BUF_WANTED;
|
|
|
|
#ifndef CONFIG_HAP_LOCKLESS_POOLS
|
|
HA_SPIN_LOCK(POOL_LOCK, &pool_head_buffer->lock);
|
|
#endif
|
|
|
|
/* fast path */
|
|
if ((pool_head_buffer->allocated - pool_head_buffer->used + cached) > margin) {
|
|
area = __pool_get_first(pool_head_buffer);
|
|
if (likely(area)) {
|
|
#ifndef CONFIG_HAP_LOCKLESS_POOLS
|
|
HA_SPIN_UNLOCK(POOL_LOCK, &pool_head_buffer->lock);
|
|
#endif
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
/* slow path, uses malloc() */
|
|
area = __pool_refill_alloc(pool_head_buffer, margin);
|
|
|
|
#ifndef CONFIG_HAP_LOCKLESS_POOLS
|
|
HA_SPIN_UNLOCK(POOL_LOCK, &pool_head_buffer->lock);
|
|
#endif
|
|
|
|
if (unlikely(!area)) {
|
|
activity[tid].buf_wait++;
|
|
return NULL;
|
|
}
|
|
|
|
done:
|
|
buf->area = area;
|
|
buf->size = pool_head_buffer->size;
|
|
return buf;
|
|
}
|
|
|
|
|
|
/* Offer a buffer currently belonging to target <from> to whoever needs one.
|
|
* Any pointer is valid for <from>, including NULL. Its purpose is to avoid
|
|
* passing a buffer to oneself in case of failed allocations (e.g. need two
|
|
* buffers, get one, fail, release it and wake up self again). In case of
|
|
* normal buffer release where it is expected that the caller is not waiting
|
|
* for a buffer, NULL is fine.
|
|
*/
|
|
void __offer_buffer(void *from, unsigned int threshold);
|
|
|
|
static inline void offer_buffers(void *from, unsigned int threshold)
|
|
{
|
|
if (!MT_LIST_ISEMPTY(&buffer_wq))
|
|
__offer_buffer(from, threshold);
|
|
}
|
|
|
|
|
|
#endif /* _COMMON_BUFFER_H */
|
|
|
|
/*
|
|
* Local variables:
|
|
* c-indent-level: 8
|
|
* c-basic-offset: 8
|
|
* End:
|
|
*/
|