Merge branch 'pools' into merge-pools
This commit is contained in:
commit
315bff5183
4
Makefile
4
Makefile
@ -215,9 +215,9 @@ all: haproxy
|
||||
OBJS = src/haproxy.o src/list.o src/chtbl.o src/hashpjw.o src/base64.o \
|
||||
src/uri_auth.o src/standard.o src/buffers.o src/log.o src/task.o \
|
||||
src/time.o src/fd.o src/regex.o src/cfgparse.o src/server.o \
|
||||
src/checks.o src/queue.o src/capture.o src/client.o src/proxy.o \
|
||||
src/checks.o src/queue.o src/client.o src/proxy.o \
|
||||
src/proto_http.o src/stream_sock.o src/appsession.o src/backend.o \
|
||||
src/session.o src/hdr_idx.o src/ev_select.o src/acl.o
|
||||
src/session.o src/hdr_idx.o src/ev_select.o src/acl.o src/memory.o
|
||||
|
||||
haproxy: $(OBJS) $(OPT_OBJS)
|
||||
$(LD) $(LDFLAGS) -o $@ $^ $(LIBS)
|
||||
|
@ -85,10 +85,10 @@ LDFLAGS = -g
|
||||
OBJS = src/haproxy.o src/list.o src/chtbl.o src/hashpjw.o src/base64.o \
|
||||
src/uri_auth.o src/standard.o src/buffers.o src/log.o src/task.o \
|
||||
src/time.o src/fd.o src/regex.o src/cfgparse.o src/server.o \
|
||||
src/checks.o src/queue.o src/capture.o src/client.o src/proxy.o \
|
||||
src/checks.o src/queue.o src/client.o src/proxy.o \
|
||||
src/proto_http.o src/stream_sock.o src/appsession.o src/backend.o \
|
||||
src/session.o src/hdr_idx.o src/ev_select.o src/ev_poll.o \
|
||||
src/ev_kqueue.o src/acl.o
|
||||
src/ev_kqueue.o src/acl.o src/memory.o
|
||||
|
||||
all: haproxy
|
||||
|
||||
|
@ -85,9 +85,10 @@ LDFLAGS = -g -isysroot /Developer/SDKs/MacOSX10.4u.sdk -arch ppc -arch i386
|
||||
OBJS = src/haproxy.o src/list.o src/chtbl.o src/hashpjw.o src/base64.o \
|
||||
src/uri_auth.o src/standard.o src/buffers.o src/log.o src/task.o \
|
||||
src/time.o src/fd.o src/regex.o src/cfgparse.o src/server.o \
|
||||
src/checks.o src/queue.o src/capture.o src/client.o src/proxy.o \
|
||||
src/checks.o src/queue.o src/client.o src/proxy.o \
|
||||
src/proto_http.o src/stream_sock.o src/appsession.o src/backend.o \
|
||||
src/session.o src/hdr_idx.o src/ev_select.o src/ev_poll.o src/acl.o
|
||||
src/session.o src/hdr_idx.o src/ev_select.o src/ev_poll.o src/acl.o \
|
||||
src/memory.o
|
||||
|
||||
all: haproxy
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <common/config.h>
|
||||
#include <common/hashpjw.h>
|
||||
#include <common/list.h>
|
||||
#include <common/memory.h>
|
||||
|
||||
#include <types/task.h>
|
||||
|
||||
@ -20,14 +21,11 @@ typedef struct appsessions {
|
||||
unsigned long int request_count;
|
||||
} appsess;
|
||||
|
||||
#define sizeof_appsess sizeof(struct appsessions)
|
||||
extern void **pool_appsess;
|
||||
extern struct pool_head *pool2_appsess;
|
||||
|
||||
struct app_pool {
|
||||
void **sessid;
|
||||
void **serverid;
|
||||
int ses_waste, ses_use, ses_msize;
|
||||
int ser_waste, ser_use, ser_msize;
|
||||
struct pool_head *sessid;
|
||||
struct pool_head *serverid;
|
||||
};
|
||||
|
||||
extern struct app_pool apools;
|
||||
|
@ -2,7 +2,7 @@
|
||||
include/common/config.h
|
||||
This files contains most of the user-configurable settings.
|
||||
|
||||
Copyright (C) 2000-2006 Willy Tarreau - w@1wt.eu
|
||||
Copyright (C) 2000-2007 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
|
||||
@ -39,6 +39,27 @@
|
||||
# define CONFIG_HAP_MEM_OPTIM
|
||||
#endif /* CONFIG_HAP_NO_MEM_OPTIM */
|
||||
|
||||
/* CONFIG_HAP_MALLOC / CONFIG_HAP_CALLOC / CONFIG_HAP_FREE
|
||||
* This macro allows to replace the malloc function with another one.
|
||||
*/
|
||||
#ifdef CONFIG_HAP_MALLOC
|
||||
#define MALLOC CONFIG_HAP_MALLOC
|
||||
#else
|
||||
#define MALLOC malloc
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_HAP_CALLOC
|
||||
#define CALLOC CONFIG_HAP_CALLOC
|
||||
#else
|
||||
#define CALLOC calloc
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_HAP_FREE
|
||||
#define FREE CONFIG_HAP_FREE
|
||||
#else
|
||||
#define FREE free
|
||||
#endif
|
||||
|
||||
|
||||
/* CONFIG_HAP_INLINE_FD_SET
|
||||
* This makes use of inline FD_* macros instead of calling equivalent
|
||||
|
@ -2,7 +2,7 @@
|
||||
include/common/memory.h
|
||||
Memory management definitions..
|
||||
|
||||
Copyright (C) 2000-2006 Willy Tarreau - w@1wt.eu
|
||||
Copyright (C) 2000-2007 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
|
||||
@ -25,9 +25,8 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <common/config.h>
|
||||
#include <common/mini-clist.h>
|
||||
|
||||
#define sizeof_requri REQURI_LEN
|
||||
#define sizeof_capture CAPTURE_LEN
|
||||
/*
|
||||
* Returns a pointer to an area of <__len> bytes taken from the pool <pool> or
|
||||
* dynamically allocated. In the first case, <__pool> is updated to point to
|
||||
@ -112,6 +111,91 @@ static inline void pool_destroy(void **pool)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/******* pools version 2 ********/
|
||||
|
||||
#define MEM_F_SHARED 0x1
|
||||
|
||||
struct pool_head {
|
||||
void **free_list;
|
||||
struct list list; /* list of all known pools */
|
||||
unsigned int used; /* how many chunks are currently in use */
|
||||
unsigned int allocated; /* how many chunks have been allocated */
|
||||
unsigned int limit; /* hard limit on the number of chunks */
|
||||
unsigned int minavail; /* how many chunks are expected to be used */
|
||||
unsigned int size; /* chunk size */
|
||||
unsigned int flags; /* MEM_F_* */
|
||||
unsigned int users; /* number of pools sharing this zone */
|
||||
char name[12]; /* name of the pool */
|
||||
};
|
||||
|
||||
|
||||
/* Allocate a new entry for pool <pool>, and return it for immediate use.
|
||||
* NULL is returned if no memory is available for a new creation.
|
||||
*/
|
||||
void *pool_refill_alloc(struct pool_head *pool);
|
||||
|
||||
/* Try to find an existing shared pool with the same characteristics and
|
||||
* returns it, otherwise creates this one. NULL is returned if no memory
|
||||
* is available for a new creation.
|
||||
*/
|
||||
struct pool_head *create_pool(char *name, unsigned int size, unsigned int flags);
|
||||
|
||||
/* Dump statistics on pools usage.
|
||||
*/
|
||||
void dump_pools(void);
|
||||
|
||||
/*
|
||||
* This function frees whatever can be freed in pool <pool>.
|
||||
*/
|
||||
void pool_flush2(struct pool_head *pool);
|
||||
|
||||
/*
|
||||
* This function frees whatever can be freed in all pools, but respecting
|
||||
* the minimum thresholds imposed by owners.
|
||||
*/
|
||||
void pool_gc2();
|
||||
|
||||
/*
|
||||
* This function destroys a pull by freeing it completely.
|
||||
* This should be called only under extreme circumstances.
|
||||
*/
|
||||
void *pool_destroy2(struct pool_head *pool);
|
||||
|
||||
/*
|
||||
* Returns a pointer to type <type> taken from the
|
||||
* pool <pool_type> or dynamically allocated. In the
|
||||
* first case, <pool_type> is updated to point to the
|
||||
* next element in the list.
|
||||
*/
|
||||
#define pool_alloc2(pool) \
|
||||
({ \
|
||||
void *__p; \
|
||||
if ((__p = pool->free_list) == NULL) \
|
||||
__p = pool_refill_alloc(pool); \
|
||||
else { \
|
||||
pool->free_list = *(void **)pool->free_list; \
|
||||
pool->used++; \
|
||||
} \
|
||||
__p; \
|
||||
})
|
||||
|
||||
/*
|
||||
* Puts a memory area back to the corresponding pool.
|
||||
* Items are chained directly through a pointer that
|
||||
* is written in the beginning of the memory area, so
|
||||
* there's no need for any carrier cell. This implies
|
||||
* that each memory area is at least as big as one
|
||||
* pointer.
|
||||
*/
|
||||
#define pool_free2(pool, ptr) \
|
||||
({ \
|
||||
*(void **)ptr = (void *)pool->free_list; \
|
||||
pool->free_list = (void *)ptr; \
|
||||
pool->used--; \
|
||||
})
|
||||
|
||||
|
||||
#endif /* _COMMON_MEMORY_H */
|
||||
|
||||
/*
|
||||
|
@ -2,6 +2,8 @@
|
||||
* tree.h : tree manipulation macros and structures.
|
||||
* (C) 2002 - Willy Tarreau - willy@ant-computing.com
|
||||
*
|
||||
* 2007/05/13: adapted to mempools v2.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __TREE_H__
|
||||
@ -51,8 +53,7 @@ struct tree64 {
|
||||
struct tree64 *up; /* parent node. NULL = root */
|
||||
};
|
||||
|
||||
#define sizeof_tree64 (sizeof (struct tree64))
|
||||
extern void **pool_tree64;
|
||||
extern struct pool_head *pool2_tree64;
|
||||
|
||||
#define ULTREE_HEAD(l) struct ultree (l) = { .left=NULL, .right=NULL, .up=NULL, .low=0, .level=LONGBITS, .data=NULL }
|
||||
#define ULTREE_INIT(l) { (l)->data = (l)->left = (l)->right = NULL; }
|
||||
@ -96,7 +97,7 @@ inline static struct ulltree *__ulltree_insert(struct ulltree *root, unsigned lo
|
||||
|
||||
if (next == NULL) {
|
||||
/* we'll have to insert our node here */
|
||||
*branch = new = (struct ulltree *)pool_alloc(tree64);
|
||||
*branch = new = (struct ulltree *)pool_alloc2(pool2_tree64);
|
||||
ULLTREE_INIT(new);
|
||||
new->up = root;
|
||||
new->value = x;
|
||||
@ -111,7 +112,7 @@ inline static struct ulltree *__ulltree_insert(struct ulltree *root, unsigned lo
|
||||
/* ok, now we know that we must insert between both. */
|
||||
|
||||
/* the new interconnect node */
|
||||
*branch = node = (struct ulltree *)pool_alloc(tree64); /* was <next> */
|
||||
*branch = node = (struct ulltree *)pool_alloc2(pool2_tree64); /* was <next> */
|
||||
ULLTREE_INIT(node);
|
||||
node->up = root;
|
||||
next->up = node;
|
||||
@ -139,7 +140,7 @@ inline static struct ulltree *__ulltree_insert(struct ulltree *root, unsigned lo
|
||||
|
||||
/* the new leaf now */
|
||||
node->level = m; /* set the level to the lowest common bit */
|
||||
new = (struct ulltree *)pool_alloc(tree64);
|
||||
new = (struct ulltree *)pool_alloc2(pool2_tree64);
|
||||
ULLTREE_INIT(new);
|
||||
new->value = x;
|
||||
new->level = ffs;
|
||||
@ -186,7 +187,7 @@ inline static struct ultree *__ultree_insert(struct ultree *root, unsigned long
|
||||
|
||||
if (next == NULL) {
|
||||
/* we'll have to insert our node here */
|
||||
*branch = new = (struct ultree *)pool_alloc(tree64);
|
||||
*branch = new = (struct ultree *)pool_alloc2(pool2_tree64);
|
||||
ULTREE_INIT(new);
|
||||
new->up = root;
|
||||
new->low = x;
|
||||
@ -200,7 +201,7 @@ inline static struct ultree *__ultree_insert(struct ultree *root, unsigned long
|
||||
/* ok, now we know that we must insert between both. */
|
||||
|
||||
/* the new interconnect node */
|
||||
*branch = node = (struct ultree *)pool_alloc(tree64); /* was <next> */
|
||||
*branch = node = (struct ultree *)pool_alloc2(pool2_tree64); /* was <next> */
|
||||
ULTREE_INIT(node);
|
||||
node->up = root;
|
||||
next->up = node;
|
||||
@ -228,7 +229,7 @@ inline static struct ultree *__ultree_insert(struct ultree *root, unsigned long
|
||||
|
||||
/* the new leaf now */
|
||||
node->level = m; /* set the level to the lowest common bit */
|
||||
new = (struct ultree *)pool_alloc(tree64);
|
||||
new = (struct ultree *)pool_alloc2(pool2_tree64);
|
||||
ULTREE_INIT(new);
|
||||
new->low = x;
|
||||
new->level = ffs;
|
||||
@ -279,7 +280,7 @@ inline static struct ultree *__ul2tree_insert(struct ultree *root, unsigned long
|
||||
|
||||
if (next == NULL) {
|
||||
/* we'll have to insert our node here */
|
||||
*branch = new =(struct ultree *)pool_alloc(tree64);
|
||||
*branch = new =(struct ultree *)pool_alloc2(pool2_tree64);
|
||||
UL2TREE_INIT(new);
|
||||
new->up = root;
|
||||
new->high = h;
|
||||
@ -308,7 +309,7 @@ inline static struct ultree *__ul2tree_insert(struct ultree *root, unsigned long
|
||||
/* ok, now we know that we must insert between both. */
|
||||
|
||||
/* the new interconnect node */
|
||||
*branch = node = (struct ultree *)pool_alloc(tree64); /* was <next> */
|
||||
*branch = node = (struct ultree *)pool_alloc2(pool2_tree64); /* was <next> */
|
||||
UL2TREE_INIT(node);
|
||||
node->up = root;
|
||||
next->up = node;
|
||||
@ -352,7 +353,7 @@ inline static struct ultree *__ul2tree_insert(struct ultree *root, unsigned long
|
||||
|
||||
/* the new leaf now */
|
||||
node->level = m; /* set the level to the lowest common bit */
|
||||
new = (struct ultree *)pool_alloc(tree64);
|
||||
new = (struct ultree *)pool_alloc2(pool2_tree64);
|
||||
UL2TREE_INIT(new);
|
||||
new->high = h;
|
||||
new->low = l;
|
||||
@ -456,7 +457,7 @@ __right: \
|
||||
goto __end; /* nothing left, don't delete the root node */ \
|
||||
else { \
|
||||
typeof (__root) __old; \
|
||||
pool_free(tree64, __ptr); \
|
||||
pool_free2(pool2_tree64, __ptr); \
|
||||
__old = __ptr; \
|
||||
__ptr = __stack[__slen]; \
|
||||
if (__ptr->left == __old) { \
|
||||
@ -506,7 +507,7 @@ __right: \
|
||||
goto __end; /* nothing left, don't delete the root node */ \
|
||||
else { \
|
||||
typeof (__root) __old; \
|
||||
pool_free(__type, __ptr); \
|
||||
pool_free2(pool##__type, __ptr); \
|
||||
__old = __ptr; \
|
||||
__ptr = __stack[__slen]; \
|
||||
if (__ptr->left == __old) { \
|
||||
@ -561,7 +562,7 @@ __right: \
|
||||
goto __end; /* nothing left, don't delete the root node */ \
|
||||
else { \
|
||||
typeof (__root) __old; \
|
||||
pool_free(tree64, __ptr); \
|
||||
pool_free2(pool2_tree64, __ptr); \
|
||||
__old = __ptr; \
|
||||
__ptr = __stack[__slen]; \
|
||||
if (__ptr->left == __old) { \
|
||||
@ -617,7 +618,7 @@ __right: \
|
||||
goto __end; /* nothing left, don't delete the root node */ \
|
||||
else { \
|
||||
typeof (__root) __old; \
|
||||
pool_free(tree64, __ptr); \
|
||||
pool_free2(pool2_tree64, __ptr); \
|
||||
__old = __ptr; \
|
||||
__ptr = __stack[__slen]; \
|
||||
if (__ptr->left == __old) { \
|
||||
@ -671,7 +672,7 @@ inline static void *__tree_delete_only_one(void *firstnode) {
|
||||
*/
|
||||
down = node;
|
||||
node = node->up;
|
||||
pool_free(tree64, down);
|
||||
pool_free2(pool2_tree64, down);
|
||||
if (node->data || node->up == NULL)
|
||||
return node;
|
||||
/* now we're sure we were sharing this empty node with another branch, let's find it */
|
||||
@ -684,7 +685,7 @@ inline static void *__tree_delete_only_one(void *firstnode) {
|
||||
down->up = node->up;
|
||||
}
|
||||
/* free the last node */
|
||||
pool_free(tree64, node);
|
||||
pool_free2(pool2_tree64, node);
|
||||
return down->up;
|
||||
}
|
||||
|
||||
@ -716,7 +717,7 @@ inline static void *__tree_delete(void *firstnode) {
|
||||
uplink = &up->right;
|
||||
|
||||
*uplink = down; /* we relink the lower branch above us or simply cut it */
|
||||
pool_free(tree64, node);
|
||||
pool_free2(pool2_tree64, node);
|
||||
node = up;
|
||||
if (down)
|
||||
down->up = node;
|
||||
|
@ -22,11 +22,19 @@
|
||||
#ifndef _PROTO_BUFFERS_H
|
||||
#define _PROTO_BUFFERS_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <common/config.h>
|
||||
#include <common/memory.h>
|
||||
#include <types/buffers.h>
|
||||
|
||||
extern struct pool_head *pool2_buffer;
|
||||
|
||||
/* perform minimal intializations, report 0 in case of error, 1 if OK. */
|
||||
int init_buffer();
|
||||
|
||||
/* Initializes all fields in the buffer. The ->rlim field is initialized last
|
||||
* so that the compiler can optimize it away if changed immediately after the
|
||||
* call to this function.
|
||||
|
@ -27,10 +27,13 @@
|
||||
#include <syslog.h>
|
||||
|
||||
#include <common/config.h>
|
||||
#include <common/memory.h>
|
||||
#include <types/log.h>
|
||||
#include <types/proxy.h>
|
||||
#include <types/session.h>
|
||||
|
||||
extern struct pool_head *pool2_requri;
|
||||
|
||||
/*
|
||||
* Displays the message on stderr with the date and pid. Overrides the quiet
|
||||
* mode during startup.
|
||||
|
@ -32,6 +32,9 @@
|
||||
#include <types/server.h>
|
||||
#include <types/task.h>
|
||||
|
||||
extern struct pool_head *pool2_pendconn;
|
||||
|
||||
int init_pendconn();
|
||||
struct session *pendconn_get_next_sess(struct server *srv, struct proxy *px);
|
||||
struct pendconn *pendconn_add(struct session *sess);
|
||||
void pendconn_free(struct pendconn *p);
|
||||
|
@ -2,7 +2,7 @@
|
||||
include/proto/session.h
|
||||
This file defines everything related to sessions.
|
||||
|
||||
Copyright (C) 2000-2006 Willy Tarreau - w@1wt.eu
|
||||
Copyright (C) 2000-2007 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
|
||||
@ -23,10 +23,15 @@
|
||||
#define _PROTO_SESSION_H
|
||||
|
||||
#include <common/config.h>
|
||||
#include <common/memory.h>
|
||||
#include <types/session.h>
|
||||
|
||||
extern struct pool_head *pool2_session;
|
||||
|
||||
void session_free(struct session *s);
|
||||
|
||||
/* perform minimal intializations, report 0 in case of error, 1 if OK. */
|
||||
int init_session();
|
||||
|
||||
#endif /* _PROTO_SESSION_H */
|
||||
|
||||
|
@ -33,6 +33,10 @@
|
||||
#include <types/task.h>
|
||||
|
||||
extern void *run_queue;
|
||||
extern struct pool_head *pool2_task;
|
||||
|
||||
/* perform minimal intializations, report 0 in case of error, 1 if OK. */
|
||||
int init_task();
|
||||
|
||||
/* needed later */
|
||||
void *tree_delete(void *node);
|
||||
@ -97,7 +101,7 @@ static inline struct task *task_delete(struct task *t)
|
||||
*/
|
||||
static inline void task_free(struct task *t)
|
||||
{
|
||||
pool_free(task, t);
|
||||
pool_free2(pool2_task, t);
|
||||
}
|
||||
|
||||
/* inserts <task> into its assigned wait queue, where it may already be. In this case, it
|
||||
|
@ -73,9 +73,6 @@ struct buffer {
|
||||
char data[BUFSIZE];
|
||||
};
|
||||
|
||||
#define sizeof_buffer sizeof(struct buffer)
|
||||
extern void **pool_buffer;
|
||||
|
||||
|
||||
#endif /* _TYPES_BUFFERS_H */
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
include/types/capture.h
|
||||
This file defines everything related to captures.
|
||||
|
||||
Copyright (C) 2000-2006 Willy Tarreau - w@1wt.eu
|
||||
Copyright (C) 2000-2007 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
|
||||
@ -23,6 +23,7 @@
|
||||
#define _TYPES_CAPTURE_H
|
||||
|
||||
#include <common/config.h>
|
||||
#include <common/memory.h>
|
||||
|
||||
struct cap_hdr {
|
||||
struct cap_hdr *next;
|
||||
@ -30,10 +31,10 @@ struct cap_hdr {
|
||||
int namelen; /* length of the header name, to speed-up lookups */
|
||||
int len; /* capture length, not including terminal zero */
|
||||
int index; /* index in the output array */
|
||||
void *pool; /* pool of pre-allocated memory area of (len+1) bytes */
|
||||
struct pool_head *pool; /* pool of pre-allocated memory area of (len+1) bytes */
|
||||
};
|
||||
|
||||
extern void **pool_capture;
|
||||
extern struct pool_head *pool2_capture;
|
||||
|
||||
#endif /* _TYPES_CAPTURE_H */
|
||||
|
||||
|
@ -44,8 +44,6 @@
|
||||
#define LW_REQHDR 1024 /* request header(s) */
|
||||
#define LW_RSPHDR 2048 /* response header(s) */
|
||||
|
||||
extern void **pool_requri;
|
||||
|
||||
|
||||
#endif /* _TYPES_LOG_H */
|
||||
|
||||
|
@ -140,8 +140,9 @@ struct proxy {
|
||||
int nb_req_cap, nb_rsp_cap; /* # of headers to be captured */
|
||||
struct cap_hdr *req_cap; /* chained list of request headers to be captured */
|
||||
struct cap_hdr *rsp_cap; /* chained list of response headers to be captured */
|
||||
void *req_cap_pool, *rsp_cap_pool; /* pools of pre-allocated char ** used to build the sessions */
|
||||
void *hdr_idx_pool; /* pools of pre-allocated int* used for headers indexing */
|
||||
struct pool_head *req_cap_pool, /* pools of pre-allocated char ** used to build the sessions */
|
||||
*rsp_cap_pool;
|
||||
struct pool_head *hdr_idx_pool; /* pools of pre-allocated int* used for headers indexing */
|
||||
char *req_add[MAX_NEWHDR], *rsp_add[MAX_NEWHDR]; /* headers to be added */
|
||||
int grace; /* grace time after stop request */
|
||||
char *check_req; /* HTTP or SSL request to use for PR_O_HTTP_CHK|PR_O_SSL3_CHK */
|
||||
|
@ -34,10 +34,6 @@ struct pendconn {
|
||||
struct server *srv; /* the server we are waiting for */
|
||||
};
|
||||
|
||||
#define sizeof_pendconn sizeof(struct pendconn)
|
||||
extern void **pool_pendconn;
|
||||
|
||||
|
||||
#endif /* _TYPES_QUEUE_H */
|
||||
|
||||
/*
|
||||
|
@ -2,7 +2,7 @@
|
||||
include/types/session.h
|
||||
This file defines everything related to sessions.
|
||||
|
||||
Copyright (C) 2000-2006 Willy Tarreau - w@1wt.eu
|
||||
Copyright (C) 2000-2007 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
|
||||
@ -123,10 +123,6 @@ struct session {
|
||||
};
|
||||
|
||||
|
||||
#define sizeof_session sizeof(struct session)
|
||||
extern void **pool_session;
|
||||
|
||||
|
||||
#endif /* _TYPES_SESSION_H */
|
||||
|
||||
/*
|
||||
|
@ -42,9 +42,6 @@ struct task {
|
||||
void *context; /* the task's context */
|
||||
};
|
||||
|
||||
#define sizeof_task sizeof(struct task)
|
||||
extern void **pool_task;
|
||||
|
||||
#endif /* _TYPES_TASK_H */
|
||||
|
||||
/*
|
||||
|
@ -2,6 +2,7 @@
|
||||
* AppSession functions.
|
||||
*
|
||||
* Copyright 2004-2006 Alexander Lazic, Klaus Wagner
|
||||
* Copyright 2006-2007 Willy Tarreau
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -17,6 +18,7 @@
|
||||
#include <common/chtbl.h>
|
||||
#include <common/config.h>
|
||||
#include <common/list.h>
|
||||
#include <common/memory.h>
|
||||
#include <common/time.h>
|
||||
|
||||
#include <types/buffers.h>
|
||||
@ -27,7 +29,7 @@
|
||||
#include <proto/task.h>
|
||||
|
||||
|
||||
void **pool_appsess = NULL;
|
||||
struct pool_head *pool2_appsess;
|
||||
struct app_pool apools;
|
||||
int have_appsession;
|
||||
|
||||
@ -72,30 +74,40 @@ int appsession_init(void)
|
||||
struct proxy *p = proxy;
|
||||
|
||||
if (!initialized) {
|
||||
pool2_appsess = create_pool("appsess", sizeof(appsess), MEM_F_SHARED);
|
||||
if (pool2_appsess == NULL)
|
||||
return -1;
|
||||
|
||||
if (!appsession_task_init()) {
|
||||
int ser_msize, ses_msize;
|
||||
|
||||
apools.sessid = NULL;
|
||||
apools.serverid = NULL;
|
||||
apools.ser_waste = 0;
|
||||
apools.ser_use = 0;
|
||||
apools.ser_msize = sizeof(void *);
|
||||
apools.ses_waste = 0;
|
||||
apools.ses_use = 0;
|
||||
apools.ses_msize = sizeof(void *);
|
||||
|
||||
ser_msize = sizeof(void *);
|
||||
ses_msize = sizeof(void *);
|
||||
while (p) {
|
||||
s = p->srv;
|
||||
if (apools.ses_msize < p->appsession_len)
|
||||
apools.ses_msize = p->appsession_len;
|
||||
if (ses_msize < p->appsession_len)
|
||||
ses_msize = p->appsession_len;
|
||||
while (s) {
|
||||
idlen = strlen(s->id);
|
||||
if (apools.ser_msize < idlen)
|
||||
apools.ser_msize = idlen;
|
||||
if (ser_msize < idlen)
|
||||
ser_msize = idlen;
|
||||
s = s->next;
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
/* we use strings, so reserve space for '\0' */
|
||||
apools.ser_msize ++;
|
||||
apools.ses_msize ++;
|
||||
ser_msize ++;
|
||||
ses_msize ++;
|
||||
|
||||
apools.sessid = create_pool("sessid", ses_msize, MEM_F_SHARED);
|
||||
if (!apools.sessid)
|
||||
return -1;
|
||||
apools.serverid = create_pool("serverid", ser_msize, MEM_F_SHARED);
|
||||
if (!apools.serverid)
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "appsession_task_init failed\n");
|
||||
@ -111,15 +123,15 @@ int appsession_task_init(void)
|
||||
static int initialized = 0;
|
||||
struct task *t;
|
||||
if (!initialized) {
|
||||
if ((t = pool_alloc(task)) == NULL)
|
||||
if ((t = pool_alloc2(pool2_task)) == NULL)
|
||||
return -1;
|
||||
t->wq = NULL;
|
||||
t->qlist.p = NULL;
|
||||
t->state = TASK_IDLE;
|
||||
t->context = NULL;
|
||||
tv_ms_add(&t->expire, &now, TBLCHKINT);
|
||||
task_queue(t);
|
||||
t->process = appsession_refresh;
|
||||
task_queue(t);
|
||||
initialized ++;
|
||||
}
|
||||
return 0;
|
||||
@ -174,6 +186,7 @@ void appsession_refresh(struct task *t, struct timeval *next)
|
||||
p = p->next;
|
||||
}
|
||||
tv_ms_add(&t->expire, &now, TBLCHKINT); /* check expiration every 5 seconds */
|
||||
task_queue(t);
|
||||
*next = t->expire;
|
||||
} /* end appsession_refresh */
|
||||
|
||||
@ -196,12 +209,12 @@ void destroy(void *data) {
|
||||
temp1 = (appsess *)data;
|
||||
|
||||
if (temp1->sessid)
|
||||
pool_free_to(apools.sessid, temp1->sessid);
|
||||
pool_free2(apools.sessid, temp1->sessid);
|
||||
|
||||
if (temp1->serverid)
|
||||
pool_free_to(apools.serverid, temp1->serverid);
|
||||
pool_free2(apools.serverid, temp1->serverid);
|
||||
|
||||
pool_free(appsess, temp1);
|
||||
pool_free2(pool2_appsess, temp1);
|
||||
} /* end destroy */
|
||||
|
||||
void appsession_cleanup( void )
|
||||
|
@ -15,9 +15,19 @@
|
||||
#include <string.h>
|
||||
|
||||
#include <common/config.h>
|
||||
#include <common/memory.h>
|
||||
#include <proto/buffers.h>
|
||||
|
||||
void **pool_buffer = NULL;
|
||||
struct pool_head *pool2_buffer;
|
||||
|
||||
|
||||
/* perform minimal intializations, report 0 in case of error, 1 if OK. */
|
||||
int init_buffer()
|
||||
{
|
||||
pool2_buffer = create_pool("buffer", sizeof(struct buffer), MEM_F_SHARED);
|
||||
return pool2_buffer != NULL;
|
||||
}
|
||||
|
||||
|
||||
/* writes <len> bytes from message <msg> to buffer <buf>. Returns 0 in case of
|
||||
* success, or the number of bytes available otherwise.
|
||||
|
@ -1,26 +0,0 @@
|
||||
/*
|
||||
* Capture variables and functions.
|
||||
*
|
||||
* Copyright 2000-2006 Willy Tarreau <w@1wt.eu>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <common/config.h>
|
||||
#include <types/capture.h>
|
||||
|
||||
void **pool_capture = NULL;
|
||||
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* c-indent-level: 8
|
||||
* c-basic-offset: 8
|
||||
* End:
|
||||
*/
|
@ -824,6 +824,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args)
|
||||
hdr->name = strdup(args[3]);
|
||||
hdr->namelen = strlen(args[3]);
|
||||
hdr->len = atol(args[5]);
|
||||
hdr->pool = create_pool("caphdr", hdr->len + 1, MEM_F_SHARED);
|
||||
hdr->index = curproxy->nb_req_cap++;
|
||||
curproxy->req_cap = hdr;
|
||||
curproxy->to_log |= LW_REQHDR;
|
||||
@ -846,6 +847,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args)
|
||||
hdr->name = strdup(args[3]);
|
||||
hdr->namelen = strlen(args[3]);
|
||||
hdr->len = atol(args[5]);
|
||||
hdr->pool = create_pool("caphdr", hdr->len + 1, MEM_F_SHARED);
|
||||
hdr->index = curproxy->nb_rsp_cap++;
|
||||
curproxy->rsp_cap = hdr;
|
||||
curproxy->to_log |= LW_RSPHDR;
|
||||
@ -2401,6 +2403,20 @@ int readcfgfile(const char *file)
|
||||
memcpy(curproxy->check_req, sslv3_client_hello_pkt, sizeof(sslv3_client_hello_pkt));
|
||||
}
|
||||
|
||||
/* The small pools required for the capture lists */
|
||||
if (curproxy->nb_req_cap)
|
||||
curproxy->req_cap_pool = create_pool("ptrcap",
|
||||
curproxy->nb_req_cap * sizeof(char *),
|
||||
MEM_F_SHARED);
|
||||
if (curproxy->nb_rsp_cap)
|
||||
curproxy->rsp_cap_pool = create_pool("ptrcap",
|
||||
curproxy->nb_rsp_cap * sizeof(char *),
|
||||
MEM_F_SHARED);
|
||||
|
||||
curproxy->hdr_idx_pool = create_pool("hdr_idx",
|
||||
MAX_HTTP_HDR * sizeof(struct hdr_idx_elem),
|
||||
MEM_F_SHARED);
|
||||
|
||||
/* for backwards compatibility with "listen" instances, if
|
||||
* fullconn is not set but maxconn is set, then maxconn
|
||||
* is used.
|
||||
@ -2488,7 +2504,7 @@ int readcfgfile(const char *file)
|
||||
if (newsrv->maxconn > 0) {
|
||||
struct task *t;
|
||||
|
||||
if ((t = pool_alloc(task)) == NULL) {
|
||||
if ((t = pool_alloc2(pool2_task)) == NULL) {
|
||||
Alert("parsing [%s:%d] : out of memory.\n", file, linenum);
|
||||
return -1;
|
||||
}
|
||||
@ -2535,7 +2551,7 @@ int readcfgfile(const char *file)
|
||||
while (newsrv != NULL) {
|
||||
/* should this server be checked ? */
|
||||
if (newsrv->state & SRV_CHECKED) {
|
||||
if ((t = pool_alloc(task)) == NULL) {
|
||||
if ((t = pool_alloc2(pool2_task)) == NULL) {
|
||||
Alert("parsing [%s:%d] : out of memory.\n", file, linenum);
|
||||
return -1;
|
||||
}
|
||||
|
80
src/client.c
80
src/client.c
@ -41,6 +41,7 @@
|
||||
#include <proto/log.h>
|
||||
#include <proto/hdr_idx.h>
|
||||
#include <proto/proto_http.h>
|
||||
#include <proto/session.h>
|
||||
#include <proto/stream_sock.h>
|
||||
#include <proto/task.h>
|
||||
|
||||
@ -110,7 +111,7 @@ int event_accept(int fd) {
|
||||
}
|
||||
}
|
||||
|
||||
if ((s = pool_alloc(session)) == NULL) { /* disable this proxy for a while */
|
||||
if ((s = pool_alloc2(pool2_session)) == NULL) { /* disable this proxy for a while */
|
||||
Alert("out of memory in event_accept().\n");
|
||||
EV_FD_CLR(fd, DIR_RD);
|
||||
p->state = PR_STIDLE;
|
||||
@ -127,18 +128,18 @@ int event_accept(int fd) {
|
||||
(((struct sockaddr_in *)&addr)->sin_addr.s_addr & p->mon_mask.s_addr) == p->mon_net.s_addr) {
|
||||
if (p->mode == PR_MODE_TCP) {
|
||||
close(cfd);
|
||||
pool_free(session, s);
|
||||
pool_free2(pool2_session, s);
|
||||
continue;
|
||||
}
|
||||
s->flags |= SN_MONITOR;
|
||||
}
|
||||
|
||||
if ((t = pool_alloc(task)) == NULL) { /* disable this proxy for a while */
|
||||
if ((t = pool_alloc2(pool2_task)) == NULL) { /* disable this proxy for a while */
|
||||
Alert("out of memory in event_accept().\n");
|
||||
EV_FD_CLR(fd, DIR_RD);
|
||||
p->state = PR_STIDLE;
|
||||
close(cfd);
|
||||
pool_free(session, s);
|
||||
pool_free2(pool2_session, s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -146,8 +147,8 @@ int event_accept(int fd) {
|
||||
if (cfd >= global.maxsock) {
|
||||
Alert("accept(): not enough free sockets. Raise -n argument. Giving up.\n");
|
||||
close(cfd);
|
||||
pool_free(task, t);
|
||||
pool_free(session, s);
|
||||
pool_free2(pool2_task, t);
|
||||
pool_free2(pool2_session, s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -156,8 +157,8 @@ int event_accept(int fd) {
|
||||
(char *) &one, sizeof(one)) == -1)) {
|
||||
Alert("accept(): cannot set the socket in non blocking mode. Giving up\n");
|
||||
close(cfd);
|
||||
pool_free(task, t);
|
||||
pool_free(session, s);
|
||||
pool_free2(pool2_task, t);
|
||||
pool_free2(pool2_session, s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -229,15 +230,12 @@ int event_accept(int fd) {
|
||||
txn->req.som = txn->req.eoh = 0; /* relative to the buffer */
|
||||
txn->auth_hdr.len = -1;
|
||||
|
||||
txn->hdr_idx.size = MAX_HTTP_HDR;
|
||||
|
||||
if (p->nb_req_cap > 0) {
|
||||
if ((txn->req.cap =
|
||||
pool_alloc_from(p->req_cap_pool, p->nb_req_cap*sizeof(char *)))
|
||||
== NULL) { /* no memory */
|
||||
if ((txn->req.cap = pool_alloc2(p->req_cap_pool)) == NULL) {
|
||||
/* no memory */
|
||||
close(cfd); /* nothing can be done for this fd without memory */
|
||||
pool_free(task, t);
|
||||
pool_free(session, s);
|
||||
pool_free2(pool2_task, t);
|
||||
pool_free2(pool2_session, s);
|
||||
return 0;
|
||||
}
|
||||
memset(txn->req.cap, 0, p->nb_req_cap*sizeof(char *));
|
||||
@ -245,30 +243,30 @@ int event_accept(int fd) {
|
||||
|
||||
|
||||
if (p->nb_rsp_cap > 0) {
|
||||
if ((txn->rsp.cap =
|
||||
pool_alloc_from(p->rsp_cap_pool, p->nb_rsp_cap*sizeof(char *)))
|
||||
== NULL) { /* no memory */
|
||||
if ((txn->rsp.cap = pool_alloc2(p->rsp_cap_pool)) == NULL) {
|
||||
/* no memory */
|
||||
if (txn->req.cap != NULL)
|
||||
pool_free_to(p->req_cap_pool, txn->req.cap);
|
||||
pool_free2(p->req_cap_pool, txn->req.cap);
|
||||
close(cfd); /* nothing can be done for this fd without memory */
|
||||
pool_free(task, t);
|
||||
pool_free(session, s);
|
||||
pool_free2(pool2_task, t);
|
||||
pool_free2(pool2_session, s);
|
||||
return 0;
|
||||
}
|
||||
memset(txn->rsp.cap, 0, p->nb_rsp_cap*sizeof(char *));
|
||||
}
|
||||
|
||||
|
||||
if ((txn->hdr_idx.v =
|
||||
pool_alloc_from(p->hdr_idx_pool, txn->hdr_idx.size*sizeof(*txn->hdr_idx.v)))
|
||||
== NULL) { /* no memory */
|
||||
txn->hdr_idx.size = MAX_HTTP_HDR;
|
||||
|
||||
if ((txn->hdr_idx.v = pool_alloc2(p->hdr_idx_pool)) == NULL) {
|
||||
/* no memory */
|
||||
if (txn->rsp.cap != NULL)
|
||||
pool_free_to(p->rsp_cap_pool, txn->rsp.cap);
|
||||
pool_free2(p->rsp_cap_pool, txn->rsp.cap);
|
||||
if (txn->req.cap != NULL)
|
||||
pool_free_to(p->req_cap_pool, txn->req.cap);
|
||||
pool_free2(p->req_cap_pool, txn->req.cap);
|
||||
close(cfd); /* nothing can be done for this fd without memory */
|
||||
pool_free(task, t);
|
||||
pool_free(session, s);
|
||||
pool_free2(pool2_task, t);
|
||||
pool_free2(pool2_session, s);
|
||||
return 0;
|
||||
}
|
||||
hdr_idx_init(&txn->hdr_idx);
|
||||
@ -346,16 +344,16 @@ int event_accept(int fd) {
|
||||
write(1, trash, len);
|
||||
}
|
||||
|
||||
if ((s->req = pool_alloc(buffer)) == NULL) { /* no memory */
|
||||
if ((s->req = pool_alloc2(pool2_buffer)) == NULL) { /* no memory */
|
||||
if (txn->hdr_idx.v != NULL)
|
||||
pool_free_to(p->hdr_idx_pool, txn->hdr_idx.v);
|
||||
pool_free2(p->hdr_idx_pool, txn->hdr_idx.v);
|
||||
if (txn->rsp.cap != NULL)
|
||||
pool_free_to(p->rsp_cap_pool, txn->rsp.cap);
|
||||
pool_free2(p->rsp_cap_pool, txn->rsp.cap);
|
||||
if (txn->req.cap != NULL)
|
||||
pool_free_to(p->req_cap_pool, txn->req.cap);
|
||||
pool_free2(p->req_cap_pool, txn->req.cap);
|
||||
close(cfd); /* nothing can be done for this fd without memory */
|
||||
pool_free(task, t);
|
||||
pool_free(session, s);
|
||||
pool_free2(pool2_task, t);
|
||||
pool_free2(pool2_session, s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -368,17 +366,17 @@ int event_accept(int fd) {
|
||||
s->req->wto = s->be->srvtimeout;
|
||||
s->req->cto = s->be->srvtimeout;
|
||||
|
||||
if ((s->rep = pool_alloc(buffer)) == NULL) { /* no memory */
|
||||
pool_free(buffer, s->req);
|
||||
if ((s->rep = pool_alloc2(pool2_buffer)) == NULL) { /* no memory */
|
||||
pool_free2(pool2_buffer, s->req);
|
||||
if (txn->hdr_idx.v != NULL)
|
||||
pool_free_to(p->hdr_idx_pool, txn->hdr_idx.v);
|
||||
pool_free2(p->hdr_idx_pool, txn->hdr_idx.v);
|
||||
if (txn->rsp.cap != NULL)
|
||||
pool_free_to(p->rsp_cap_pool, txn->rsp.cap);
|
||||
pool_free2(p->rsp_cap_pool, txn->rsp.cap);
|
||||
if (txn->req.cap != NULL)
|
||||
pool_free_to(p->req_cap_pool, txn->req.cap);
|
||||
pool_free2(p->req_cap_pool, txn->req.cap);
|
||||
close(cfd); /* nothing can be done for this fd without memory */
|
||||
pool_free(task, t);
|
||||
pool_free(session, s);
|
||||
pool_free2(pool2_task, t);
|
||||
pool_free2(pool2_session, s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -87,6 +87,7 @@
|
||||
#include <proto/proxy.h>
|
||||
#include <proto/queue.h>
|
||||
#include <proto/server.h>
|
||||
#include <proto/session.h>
|
||||
#include <proto/stream_sock.h>
|
||||
#include <proto/task.h>
|
||||
|
||||
@ -202,6 +203,7 @@ void usage(char *name)
|
||||
void sig_soft_stop(int sig)
|
||||
{
|
||||
soft_stop();
|
||||
pool_gc2();
|
||||
signal(sig, SIG_IGN);
|
||||
}
|
||||
|
||||
@ -211,6 +213,7 @@ void sig_soft_stop(int sig)
|
||||
void sig_pause(int sig)
|
||||
{
|
||||
pause_proxies();
|
||||
pool_gc2();
|
||||
signal(sig, sig_pause);
|
||||
}
|
||||
|
||||
@ -292,6 +295,9 @@ void dump(int sig)
|
||||
);
|
||||
}
|
||||
#endif
|
||||
/* dump memory usage then free everything possible */
|
||||
dump_pools();
|
||||
pool_gc2();
|
||||
}
|
||||
|
||||
#ifdef DEBUG_MEMORY
|
||||
@ -314,6 +320,7 @@ void sig_int(int sig)
|
||||
0 GRACE time
|
||||
*/
|
||||
fast_stop();
|
||||
pool_gc2();
|
||||
/* If we are killed twice, we decide to die*/
|
||||
signal(sig, SIG_DFL);
|
||||
}
|
||||
@ -326,6 +333,7 @@ void sig_term(int sig)
|
||||
0 GRACE time
|
||||
*/
|
||||
fast_stop();
|
||||
pool_gc2();
|
||||
/* If we are killed twice, we decide to die*/
|
||||
signal(sig, SIG_DFL);
|
||||
}
|
||||
@ -370,6 +378,10 @@ void init(int argc, char **argv)
|
||||
localtime((time_t *)&now.tv_sec);
|
||||
start_date = now;
|
||||
|
||||
init_task();
|
||||
init_session();
|
||||
init_buffer();
|
||||
init_pendconn();
|
||||
init_proto_http();
|
||||
|
||||
cfg_polling_mechanism = POLL_USE_SELECT; /* select() is always available */
|
||||
@ -572,7 +584,7 @@ void init(int argc, char **argv)
|
||||
|
||||
void deinit(void)
|
||||
{
|
||||
struct proxy *p = proxy;
|
||||
struct proxy *p = proxy, *p0;
|
||||
struct cap_hdr *h,*h_next;
|
||||
struct server *s,*s_next;
|
||||
struct listener *l,*l_next;
|
||||
@ -608,7 +620,7 @@ void deinit(void)
|
||||
h_next = h->next;
|
||||
if (h->name)
|
||||
free(h->name);
|
||||
pool_destroy(h->pool);
|
||||
pool_destroy2(h->pool);
|
||||
free(h);
|
||||
h = h_next;
|
||||
}/* end while(h) */
|
||||
@ -619,7 +631,7 @@ void deinit(void)
|
||||
if (h->name)
|
||||
free(h->name);
|
||||
|
||||
pool_destroy(h->pool);
|
||||
pool_destroy2(h->pool);
|
||||
free(h);
|
||||
h = h_next;
|
||||
}/* end while(h) */
|
||||
@ -644,9 +656,11 @@ void deinit(void)
|
||||
l = l_next;
|
||||
}/* end while(l) */
|
||||
|
||||
pool_destroy((void **) p->req_cap_pool);
|
||||
pool_destroy((void **) p->rsp_cap_pool);
|
||||
pool_destroy2(p->req_cap_pool);
|
||||
pool_destroy2(p->rsp_cap_pool);
|
||||
p0 = p;
|
||||
p = p->next;
|
||||
free(p0);
|
||||
}/* end while(p) */
|
||||
|
||||
if (global.chroot) free(global.chroot);
|
||||
@ -654,16 +668,17 @@ void deinit(void)
|
||||
|
||||
if (fdtab) free(fdtab);
|
||||
|
||||
pool_destroy(pool_session);
|
||||
pool_destroy(pool_buffer);
|
||||
pool_destroy(pool_requri);
|
||||
pool_destroy(pool_task);
|
||||
pool_destroy(pool_capture);
|
||||
pool_destroy(pool_appsess);
|
||||
pool_destroy2(pool2_session);
|
||||
pool_destroy2(pool2_buffer);
|
||||
pool_destroy2(pool2_requri);
|
||||
pool_destroy2(pool2_task);
|
||||
pool_destroy2(pool2_capture);
|
||||
pool_destroy2(pool2_appsess);
|
||||
pool_destroy2(pool2_pendconn);
|
||||
|
||||
if (have_appsession) {
|
||||
pool_destroy(apools.serverid);
|
||||
pool_destroy(apools.sessid);
|
||||
pool_destroy2(apools.serverid);
|
||||
pool_destroy2(apools.sessid);
|
||||
}
|
||||
} /* end deinit() */
|
||||
|
||||
|
187
src/memory.c
Normal file
187
src/memory.c
Normal file
@ -0,0 +1,187 @@
|
||||
/*
|
||||
* Memory management functions.
|
||||
*
|
||||
* Copyright 2000-2007 Willy Tarreau <w@1wt.eu>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <common/config.h>
|
||||
#include <common/memory.h>
|
||||
#include <common/mini-clist.h>
|
||||
#include <common/standard.h>
|
||||
|
||||
#include <proto/log.h>
|
||||
|
||||
static struct list pools = LIST_HEAD_INIT(pools);
|
||||
|
||||
/* Try to find an existing shared pool with the same characteristics and
|
||||
* returns it, otherwise creates this one. NULL is returned if no memory
|
||||
* is available for a new creation.
|
||||
*/
|
||||
struct pool_head *create_pool(char *name, unsigned int size, unsigned int flags)
|
||||
{
|
||||
struct pool_head *pool;
|
||||
struct pool_head *entry;
|
||||
struct list *start;
|
||||
unsigned int align;
|
||||
|
||||
/* We need to store at least a (void *) in the chunks. Since we know
|
||||
* that the malloc() function will never return such a small size,
|
||||
* let's round the size up to something slightly bigger, in order to
|
||||
* ease merging of entries. Note that the rounding is a power of two.
|
||||
*/
|
||||
|
||||
align = 16;
|
||||
size = (size + align - 1) & -align;
|
||||
|
||||
start = &pools;
|
||||
pool = NULL;
|
||||
|
||||
list_for_each_entry(entry, &pools, list) {
|
||||
if (entry->size == size) {
|
||||
/* either we can share this place and we take it, or
|
||||
* we look for a sharable one or for the next position
|
||||
* before which we will insert a new one.
|
||||
*/
|
||||
if (flags & entry->flags & MEM_F_SHARED) {
|
||||
/* we can share this one */
|
||||
pool = entry;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (entry->size > size) {
|
||||
/* insert before this one */
|
||||
start = &entry->list;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!pool) {
|
||||
pool = CALLOC(1, sizeof(*pool));
|
||||
if (!pool)
|
||||
return NULL;
|
||||
if (name)
|
||||
strlcpy2(pool->name, name, sizeof(pool->name));
|
||||
pool->size = size;
|
||||
pool->flags = flags;
|
||||
LIST_ADDQ(start, &pool->list);
|
||||
}
|
||||
pool->users++;
|
||||
return pool;
|
||||
}
|
||||
|
||||
/* Allocate a new entry for pool <pool>, and return it for immediate use.
|
||||
* NULL is returned if no memory is available for a new creation. A call
|
||||
* to the garbage collector is performed before returning NULL.
|
||||
*/
|
||||
void *pool_refill_alloc(struct pool_head *pool)
|
||||
{
|
||||
void *ret;
|
||||
|
||||
if (pool->limit && (pool->allocated >= pool->limit))
|
||||
return NULL;
|
||||
ret = MALLOC(pool->size);
|
||||
if (!ret) {
|
||||
pool_gc2();
|
||||
ret = MALLOC(pool->size);
|
||||
if (!ret)
|
||||
return NULL;
|
||||
}
|
||||
pool->allocated++;
|
||||
pool->used++;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function frees whatever can be freed in pool <pool>.
|
||||
*/
|
||||
void pool_flush2(struct pool_head *pool)
|
||||
{
|
||||
void *temp, *next;
|
||||
if (!pool)
|
||||
return;
|
||||
|
||||
next = pool->free_list;
|
||||
while (next) {
|
||||
temp = next;
|
||||
next = *(void **)temp;
|
||||
pool->allocated--;
|
||||
FREE(temp);
|
||||
}
|
||||
pool->free_list = next;
|
||||
|
||||
/* here, we should have pool->allocate == pool->used */
|
||||
}
|
||||
|
||||
/*
|
||||
* This function frees whatever can be freed in all pools, but respecting
|
||||
* the minimum thresholds imposed by owners.
|
||||
*/
|
||||
void pool_gc2()
|
||||
{
|
||||
struct pool_head *entry;
|
||||
list_for_each_entry(entry, &pools, list) {
|
||||
void *temp, *next;
|
||||
//qfprintf(stderr, "Flushing pool %s\n", entry->name);
|
||||
next = entry->free_list;
|
||||
while (next &&
|
||||
entry->allocated > entry->minavail &&
|
||||
entry->allocated > entry->used) {
|
||||
temp = next;
|
||||
next = *(void **)temp;
|
||||
entry->allocated--;
|
||||
FREE(temp);
|
||||
}
|
||||
entry->free_list = next;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This function destroys a pull by freeing it completely.
|
||||
* This should be called only under extreme circumstances.
|
||||
* It always returns NULL, easing the clearing of the old pointer.
|
||||
*/
|
||||
void *pool_destroy2(struct pool_head *pool)
|
||||
{
|
||||
if (pool) {
|
||||
pool_flush2(pool);
|
||||
FREE(pool);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Dump statistics on pools usage.
|
||||
*/
|
||||
void dump_pools(void)
|
||||
{
|
||||
struct pool_head *entry;
|
||||
unsigned long allocated, used;
|
||||
int nbpools;
|
||||
|
||||
allocated = used = nbpools = 0;
|
||||
qfprintf(stderr, "Dumping pools usage.\n");
|
||||
list_for_each_entry(entry, &pools, list) {
|
||||
qfprintf(stderr, " - Pool %s (%d bytes) : %d allocated (%lu bytes), %d used, %d users%s\n",
|
||||
entry->name, entry->size, entry->allocated,
|
||||
entry->size * entry->allocated, entry->used,
|
||||
entry->users, (entry->flags & MEM_F_SHARED) ? " [SHARED]" : "");
|
||||
|
||||
allocated += entry->allocated * entry->size;
|
||||
used += entry->used * entry->size;
|
||||
nbpools++;
|
||||
}
|
||||
qfprintf(stderr, "Total: %d pools, %lu bytes allocated, %lu used.\n",
|
||||
nbpools, allocated, used);
|
||||
}
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* c-indent-level: 8
|
||||
* c-basic-offset: 8
|
||||
* End:
|
||||
*/
|
@ -243,6 +243,10 @@ void init_proto_http()
|
||||
FD_SET(*tmp, url_encode_map);
|
||||
tmp++;
|
||||
}
|
||||
|
||||
/* memory allocations */
|
||||
pool2_requri = create_pool("requri", REQURI_LEN, MEM_F_SHARED);
|
||||
pool2_capture = create_pool("capture", CAPTURE_LEN, MEM_F_SHARED);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -624,7 +628,8 @@ const char sess_cookie[4] = "NIDV"; /* No cookie, Invalid cookie, cookie fo
|
||||
const char sess_set_cookie[8] = "N1I3PD5R"; /* No set-cookie, unknown, Set-Cookie Inserted, unknown,
|
||||
Set-cookie seen and left unchanged (passive), Set-cookie Deleted,
|
||||
unknown, Set-cookie Rewritten */
|
||||
void **pool_requri = NULL;
|
||||
struct pool_head *pool2_requri;
|
||||
struct pool_head *pool2_capture;
|
||||
|
||||
/*
|
||||
* send a log for the session when we have enough info about it.
|
||||
@ -770,7 +775,7 @@ void capture_headers(char *som, struct hdr_idx *idx,
|
||||
(strncasecmp(sol, h->name, h->namelen) == 0)) {
|
||||
if (cap[h->index] == NULL)
|
||||
cap[h->index] =
|
||||
pool_alloc_from(h->pool, h->len + 1);
|
||||
pool_alloc2(h->pool);
|
||||
|
||||
if (cap[h->index] == NULL) {
|
||||
Alert("HTTP capture : out of memory.\n");
|
||||
@ -1592,7 +1597,7 @@ int process_cli(struct session *t)
|
||||
*/
|
||||
if (unlikely(t->logs.logwait & LW_REQ)) {
|
||||
/* we have a complete HTTP request that we must log */
|
||||
if ((txn->uri = pool_alloc(requri)) != NULL) {
|
||||
if ((txn->uri = pool_alloc2(pool2_requri)) != NULL) {
|
||||
int urilen = msg->sl.rq.l;
|
||||
|
||||
if (urilen >= REQURI_LEN)
|
||||
@ -4169,7 +4174,7 @@ void manage_client_side_cookies(struct session *t, struct buffer *req)
|
||||
memcmp(p1, t->fe->capture_name, t->fe->capture_namelen) == 0) {
|
||||
int log_len = p4 - p1;
|
||||
|
||||
if ((txn->cli_cookie = pool_alloc(capture)) == NULL) {
|
||||
if ((txn->cli_cookie = pool_alloc2(pool2_capture)) == NULL) {
|
||||
Alert("HTTP logging : out of memory.\n");
|
||||
} else {
|
||||
if (log_len > t->fe->capture_len)
|
||||
@ -4298,7 +4303,7 @@ void manage_client_side_cookies(struct session *t, struct buffer *req)
|
||||
|
||||
asession_temp = &local_asession;
|
||||
|
||||
if ((asession_temp->sessid = pool_alloc_from(apools.sessid, apools.ses_msize)) == NULL) {
|
||||
if ((asession_temp->sessid = pool_alloc2(apools.sessid)) == NULL) {
|
||||
Alert("Not enough memory process_cli():asession->sessid:malloc().\n");
|
||||
send_log(t->be, LOG_ALERT, "Not enough memory process_cli():asession->sessid:malloc().\n");
|
||||
return;
|
||||
@ -4310,9 +4315,9 @@ void manage_client_side_cookies(struct session *t, struct buffer *req)
|
||||
|
||||
/* only do insert, if lookup fails */
|
||||
if (chtbl_lookup(&(t->be->htbl_proxy), (void *) &asession_temp) != 0) {
|
||||
if ((asession_temp = pool_alloc(appsess)) == NULL) {
|
||||
if ((asession_temp = pool_alloc2(pool2_appsess)) == NULL) {
|
||||
/* free previously allocated memory */
|
||||
pool_free_to(apools.sessid, local_asession.sessid);
|
||||
pool_free2(apools.sessid, local_asession.sessid);
|
||||
Alert("Not enough memory process_cli():asession:calloc().\n");
|
||||
send_log(t->be, LOG_ALERT, "Not enough memory process_cli():asession:calloc().\n");
|
||||
return;
|
||||
@ -4323,7 +4328,7 @@ void manage_client_side_cookies(struct session *t, struct buffer *req)
|
||||
chtbl_insert(&(t->be->htbl_proxy), (void *) asession_temp);
|
||||
} else {
|
||||
/* free previously allocated memory */
|
||||
pool_free_to(apools.sessid, local_asession.sessid);
|
||||
pool_free2(apools.sessid, local_asession.sessid);
|
||||
}
|
||||
|
||||
if (asession_temp->serverid == NULL) {
|
||||
@ -4704,7 +4709,7 @@ void manage_server_side_cookies(struct session *t, struct buffer *rtr)
|
||||
memcmp(p1, t->be->capture_name, t->be->capture_namelen) == 0) {
|
||||
int log_len = p4 - p1;
|
||||
|
||||
if ((txn->srv_cookie = pool_alloc(capture)) == NULL) {
|
||||
if ((txn->srv_cookie = pool_alloc2(pool2_capture)) == NULL) {
|
||||
Alert("HTTP logging : out of memory.\n");
|
||||
}
|
||||
|
||||
@ -4771,7 +4776,7 @@ void manage_server_side_cookies(struct session *t, struct buffer *rtr)
|
||||
size_t server_id_len = strlen(t->srv->id) + 1;
|
||||
asession_temp = &local_asession;
|
||||
|
||||
if ((asession_temp->sessid = pool_alloc_from(apools.sessid, apools.ses_msize)) == NULL) {
|
||||
if ((asession_temp->sessid = pool_alloc2(apools.sessid)) == NULL) {
|
||||
Alert("Not enough Memory process_srv():asession->sessid:malloc().\n");
|
||||
send_log(t->be, LOG_ALERT, "Not enough Memory process_srv():asession->sessid:malloc().\n");
|
||||
return;
|
||||
@ -4782,7 +4787,7 @@ void manage_server_side_cookies(struct session *t, struct buffer *rtr)
|
||||
|
||||
/* only do insert, if lookup fails */
|
||||
if (chtbl_lookup(&(t->be->htbl_proxy), (void *) &asession_temp) != 0) {
|
||||
if ((asession_temp = pool_alloc(appsess)) == NULL) {
|
||||
if ((asession_temp = pool_alloc2(pool2_appsess)) == NULL) {
|
||||
Alert("Not enough Memory process_srv():asession:calloc().\n");
|
||||
send_log(t->be, LOG_ALERT, "Not enough Memory process_srv():asession:calloc().\n");
|
||||
return;
|
||||
@ -4793,11 +4798,11 @@ void manage_server_side_cookies(struct session *t, struct buffer *rtr)
|
||||
}/* end if (chtbl_lookup()) */
|
||||
else {
|
||||
/* free wasted memory */
|
||||
pool_free_to(apools.sessid, local_asession.sessid);
|
||||
pool_free2(apools.sessid, local_asession.sessid);
|
||||
} /* end else from if (chtbl_lookup()) */
|
||||
|
||||
if (asession_temp->serverid == NULL) {
|
||||
if ((asession_temp->serverid = pool_alloc_from(apools.serverid, apools.ser_msize)) == NULL) {
|
||||
if ((asession_temp->serverid = pool_alloc2(apools.serverid)) == NULL) {
|
||||
Alert("Not enough Memory process_srv():asession->sessid:malloc().\n");
|
||||
send_log(t->be, LOG_ALERT, "Not enough Memory process_srv():asession->sessid:malloc().\n");
|
||||
return;
|
||||
@ -4941,7 +4946,7 @@ void get_srv_from_appsession(struct session *t, const char *begin, int len)
|
||||
/* First try if we already have an appsession */
|
||||
asession_temp = &local_asession;
|
||||
|
||||
if ((asession_temp->sessid = pool_alloc_from(apools.sessid, apools.ses_msize)) == NULL) {
|
||||
if ((asession_temp->sessid = pool_alloc2(apools.sessid)) == NULL) {
|
||||
Alert("Not enough memory process_cli():asession_temp->sessid:calloc().\n");
|
||||
send_log(t->be, LOG_ALERT, "Not enough Memory process_cli():asession_temp->sessid:calloc().\n");
|
||||
return;
|
||||
@ -4954,9 +4959,9 @@ void get_srv_from_appsession(struct session *t, const char *begin, int len)
|
||||
|
||||
/* only do insert, if lookup fails */
|
||||
if (chtbl_lookup(&(t->be->htbl_proxy), (void *)&asession_temp)) {
|
||||
if ((asession_temp = pool_alloc(appsess)) == NULL) {
|
||||
if ((asession_temp = pool_alloc2(pool2_appsess)) == NULL) {
|
||||
/* free previously allocated memory */
|
||||
pool_free_to(apools.sessid, local_asession.sessid);
|
||||
pool_free2(apools.sessid, local_asession.sessid);
|
||||
Alert("Not enough memory process_cli():asession:calloc().\n");
|
||||
send_log(t->be, LOG_ALERT, "Not enough memory process_cli():asession:calloc().\n");
|
||||
return;
|
||||
@ -4967,7 +4972,7 @@ void get_srv_from_appsession(struct session *t, const char *begin, int len)
|
||||
}
|
||||
else {
|
||||
/* free previously allocated memory */
|
||||
pool_free_to(apools.sessid, local_asession.sessid);
|
||||
pool_free2(apools.sessid, local_asession.sessid);
|
||||
}
|
||||
|
||||
tv_add(&asession_temp->expire, &now, &t->be->appsession_timeout);
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include <common/defaults.h>
|
||||
#include <common/compat.h>
|
||||
#include <common/config.h>
|
||||
#include <common/memory.h>
|
||||
#include <common/time.h>
|
||||
|
||||
#include <types/global.h>
|
||||
@ -230,6 +231,8 @@ void maintain_proxies(struct timeval *next)
|
||||
listeners--;
|
||||
}
|
||||
p->state = PR_STSTOPPED;
|
||||
/* try to free more memory */
|
||||
pool_gc2();
|
||||
}
|
||||
else {
|
||||
tv_bound(next, &p->stop_time);
|
||||
|
14
src/queue.c
14
src/queue.c
@ -11,6 +11,7 @@
|
||||
*/
|
||||
|
||||
#include <common/config.h>
|
||||
#include <common/memory.h>
|
||||
#include <common/time.h>
|
||||
|
||||
#include <types/proxy.h>
|
||||
@ -21,7 +22,14 @@
|
||||
#include <proto/task.h>
|
||||
|
||||
|
||||
void **pool_pendconn = NULL;
|
||||
struct pool_head *pool2_pendconn;
|
||||
|
||||
/* perform minimal intializations, report 0 in case of error, 1 if OK. */
|
||||
int init_pendconn()
|
||||
{
|
||||
pool2_pendconn = create_pool("pendconn", sizeof(struct pendconn), MEM_F_SHARED);
|
||||
return pool2_pendconn != NULL;
|
||||
}
|
||||
|
||||
/* returns the effective dynamic maxconn for a server, considering the minconn
|
||||
* and the proxy's usage relative to its dynamic connections limit. It is
|
||||
@ -98,7 +106,7 @@ struct pendconn *pendconn_add(struct session *sess)
|
||||
{
|
||||
struct pendconn *p;
|
||||
|
||||
p = pool_alloc(pendconn);
|
||||
p = pool_alloc2(pool2_pendconn);
|
||||
if (!p)
|
||||
return NULL;
|
||||
|
||||
@ -136,7 +144,7 @@ void pendconn_free(struct pendconn *p)
|
||||
else
|
||||
p->sess->be->nbpend--;
|
||||
p->sess->be->totpend--;
|
||||
pool_free(pendconn, p);
|
||||
pool_free2(pool2_pendconn, p);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Server management functions.
|
||||
*
|
||||
* Copyright 2000-2006 Willy Tarreau <w@1wt.eu>
|
||||
* Copyright 2000-2007 Willy Tarreau <w@1wt.eu>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -21,12 +21,14 @@
|
||||
#include <types/proxy.h>
|
||||
#include <types/server.h>
|
||||
|
||||
#include <proto/buffers.h>
|
||||
#include <proto/hdr_idx.h>
|
||||
#include <proto/log.h>
|
||||
#include <proto/session.h>
|
||||
#include <proto/queue.h>
|
||||
|
||||
|
||||
void **pool_session = NULL;
|
||||
struct pool_head *pool2_session;
|
||||
|
||||
/*
|
||||
* frees the context associated to a session. It must have been removed first.
|
||||
@ -38,38 +40,46 @@ void session_free(struct session *s)
|
||||
if (s->pend_pos)
|
||||
pendconn_free(s->pend_pos);
|
||||
if (s->req)
|
||||
pool_free(buffer, s->req);
|
||||
pool_free2(pool2_buffer, s->req);
|
||||
if (s->rep)
|
||||
pool_free(buffer, s->rep);
|
||||
pool_free2(pool2_buffer, s->rep);
|
||||
|
||||
if (txn->hdr_idx.v != NULL)
|
||||
pool_free_to(s->fe->hdr_idx_pool, txn->hdr_idx.v);
|
||||
pool_free2(s->fe->hdr_idx_pool, txn->hdr_idx.v);
|
||||
|
||||
if (txn->rsp.cap != NULL) {
|
||||
struct cap_hdr *h;
|
||||
for (h = s->fe->rsp_cap; h; h = h->next) {
|
||||
if (txn->rsp.cap[h->index] != NULL)
|
||||
pool_free_to(h->pool, txn->rsp.cap[h->index]);
|
||||
pool_free2(h->pool, txn->rsp.cap[h->index]);
|
||||
}
|
||||
pool_free_to(s->fe->rsp_cap_pool, txn->rsp.cap);
|
||||
pool_free2(s->fe->rsp_cap_pool, txn->rsp.cap);
|
||||
}
|
||||
if (txn->req.cap != NULL) {
|
||||
struct cap_hdr *h;
|
||||
for (h = s->fe->req_cap; h; h = h->next) {
|
||||
if (txn->req.cap[h->index] != NULL)
|
||||
pool_free_to(h->pool, txn->req.cap[h->index]);
|
||||
pool_free2(h->pool, txn->req.cap[h->index]);
|
||||
}
|
||||
pool_free_to(s->fe->req_cap_pool, txn->req.cap);
|
||||
pool_free2(s->fe->req_cap_pool, txn->req.cap);
|
||||
}
|
||||
|
||||
if (txn->uri)
|
||||
pool_free(requri, txn->uri);
|
||||
pool_free2(pool2_requri, txn->uri);
|
||||
if (txn->cli_cookie)
|
||||
pool_free(capture, txn->cli_cookie);
|
||||
pool_free2(pool2_capture, txn->cli_cookie);
|
||||
if (txn->srv_cookie)
|
||||
pool_free(capture, txn->srv_cookie);
|
||||
pool_free2(pool2_capture, txn->srv_cookie);
|
||||
|
||||
pool_free(session, s);
|
||||
pool_free2(pool2_session, s);
|
||||
}
|
||||
|
||||
|
||||
/* perform minimal intializations, report 0 in case of error, 1 if OK. */
|
||||
int init_session()
|
||||
{
|
||||
pool2_session = create_pool("session", sizeof(struct session), MEM_F_SHARED);
|
||||
return pool2_session != NULL;
|
||||
}
|
||||
|
||||
|
||||
|
14
src/task.c
14
src/task.c
@ -11,6 +11,7 @@
|
||||
*/
|
||||
|
||||
#include <common/config.h>
|
||||
#include <common/memory.h>
|
||||
#include <common/mini-clist.h>
|
||||
#include <common/standard.h>
|
||||
#include <common/time.h>
|
||||
@ -23,15 +24,22 @@
|
||||
#include <import/bitops.h>
|
||||
#include <import/tree.h>
|
||||
|
||||
|
||||
void **pool_task= NULL;
|
||||
void **pool_tree64 = NULL;
|
||||
static struct ultree *stack[LLONGBITS];
|
||||
|
||||
struct pool_head *pool2_task, *pool2_tree64;
|
||||
|
||||
UL2TREE_HEAD(timer_wq);
|
||||
void *eternity_queue = NULL;
|
||||
void *run_queue = NULL;
|
||||
|
||||
/* perform minimal intializations, report 0 in case of error, 1 if OK. */
|
||||
int init_task()
|
||||
{
|
||||
pool2_task = create_pool("task", sizeof(struct task), MEM_F_SHARED);
|
||||
pool2_tree64 = create_pool("tree64", sizeof(struct tree64), MEM_F_SHARED);
|
||||
return pool2_task && pool2_tree64;
|
||||
}
|
||||
|
||||
struct ultree *ul2tree_insert(struct ultree *root, unsigned long h, unsigned long l)
|
||||
{
|
||||
return __ul2tree_insert(root, h, l);
|
||||
|
Loading…
x
Reference in New Issue
Block a user