vinyl-cache/bin/vinyld/cache/cache_mempool.c
0
/*-
1
 * Copyright (c) 2011 Varnish Software AS
2
 * All rights reserved.
3
 *
4
 * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
5
 *
6
 * SPDX-License-Identifier: BSD-2-Clause
7
 *
8
 * Redistribution and use in source and binary forms, with or without
9
 * modification, are permitted provided that the following conditions
10
 * are met:
11
 * 1. Redistributions of source code must retain the above copyright
12
 *    notice, this list of conditions and the following disclaimer.
13
 * 2. Redistributions in binary form must reproduce the above copyright
14
 *    notice, this list of conditions and the following disclaimer in the
15
 *    documentation and/or other materials provided with the distribution.
16
 *
17
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
21
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27
 * SUCH DAMAGE.
28
 *
29
 * Generic memory pool
30
 */
31
32
#include "config.h"
33
34
#include "cache_int.h"
35
36
#include <stdio.h>
37
#include <stdlib.h>
38
39
#include "vtim.h"
40
41
#include "VSC_mempool.h"
42
43
struct memitem {
44
        unsigned                        magic;
45
#define MEMITEM_MAGIC                   0x42e55401
46
        unsigned                        size;
47
        VTAILQ_ENTRY(memitem)           list;
48
        vtim_real                       touched;        // XXX -> mono?
49
};
50
51
VTAILQ_HEAD(memhead_s, memitem);
52
53
struct mempool {
54
        unsigned                        magic;
55
#define MEMPOOL_MAGIC                   0x37a75a8d
56
        char                            name[12];
57
        struct memhead_s                list;
58
        struct memhead_s                surplus;
59
        struct lock                     mtx;
60
        volatile struct poolparam       *param;
61
        volatile unsigned               *cur_size;
62
        uint64_t                        live;
63
        struct vsc_seg                  *vsc_seg;
64
        struct VSC_mempool              *vsc;
65
        unsigned                        n_pool;
66
        pthread_t                       thread;
67
        vtim_real                       t_now;  // XXX -> mono?
68
        int                             self_destruct;
69
};
70
71
/*---------------------------------------------------------------------
72
 */
73
74
static struct memitem *
75 1053805
mpl_alloc(const struct mempool *mpl)
76
{
77
        unsigned tsz;
78
        struct memitem *mi;
79
80 1053805
        CHECK_OBJ_NOTNULL(mpl, MEMPOOL_MAGIC);
81 1053805
        tsz = *mpl->cur_size;
82 1053805
        mi = calloc(1, tsz);
83 1053805
        AN(mi);
84 1053805
        mi->magic = MEMITEM_MAGIC;
85 1053805
        mi->size = tsz;
86 1053805
        mpl->vsc->sz_wanted = tsz;
87 1053805
        mpl->vsc->sz_actual = tsz - sizeof *mi;
88 1053805
        return (mi);
89
}
90
91
/*---------------------------------------------------------------------
92
 * Pool-guard
93
 *   Attempt to keep number of free items in pool inside bounds with
94
 *   minimum locking activity, and keep an eye on items at the tail
95
 *   of the list not getting too old.
96
 */
97
98
static void *
99 618
mpl_guard(void *priv)
100
{
101
        struct mempool *mpl;
102 618
        struct memitem *mi = NULL;
103
        vtim_dur v_statevariable_(mpl_slp);
104 618
        vtim_real last = 0;
105
106 618
        CAST_OBJ_NOTNULL(mpl, priv, MEMPOOL_MAGIC);
107 618
        THR_SetName(mpl->name);
108 618
        THR_Init();
109 618
        mpl_slp = 0.15; // random
110 1147714
        while (1) {
111 1433761
                VTIM_sleep(mpl_slp);
112 1433761
                mpl_slp = 0.814;        // random
113 1433761
                mpl->t_now = VTIM_real();
114
115 1433761
                if (mi != NULL && (mpl->n_pool > mpl->param->max_pool ||
116 6
                    mi->size < *mpl->cur_size)) {
117 0
                        CHECK_OBJ(mi, MEMITEM_MAGIC);
118 0
                        FREE_OBJ(mi);
119 0
                }
120
121 1433761
                if (mi == NULL && mpl->n_pool < mpl->param->min_pool)
122 1048524
                        mi = mpl_alloc(mpl);
123
124 1433761
                if (mpl->n_pool < mpl->param->min_pool && mi != NULL) {
125
                        /* can do */
126 1433761
                } else if (mpl->n_pool > mpl->param->max_pool && mi == NULL) {
127
                        /* can do */
128 379976
                } else if (!VTAILQ_EMPTY(&mpl->surplus)) {
129
                        /* can do */
130 379473
                } else if (last + .1 * mpl->param->max_age < mpl->t_now) {
131
                        /* should do */
132 379452
                } else if (mpl->self_destruct) {
133
                        /* can do */
134 397
                } else {
135 285805
                        continue;       /* nothing to do */
136
                }
137
138 1147956
                mpl_slp = 0.314;        // random
139
140 1147956
                if (Lck_Trylock(&mpl->mtx))
141 242
                        continue;
142
143 1147738
                if (mpl->self_destruct) {
144 672
                        AZ(mpl->live);
145 7751
                        while (1) {
146 7751
                                if (mi == NULL) {
147 7706
                                        mi = VTAILQ_FIRST(&mpl->list);
148 7706
                                        if (mi != NULL) {
149 7064
                                                mpl->vsc->pool = --mpl->n_pool;
150 7064
                                                VTAILQ_REMOVE(&mpl->list,
151
                                                    mi, list);
152 7064
                                        }
153 7706
                                }
154 7751
                                if (mi == NULL) {
155 672
                                        mi = VTAILQ_FIRST(&mpl->surplus);
156 672
                                        if (mi != NULL)
157 0
                                                VTAILQ_REMOVE(&mpl->surplus,
158
                                                    mi, list);
159 672
                                }
160 7751
                                if (mi == NULL)
161 672
                                        break;
162 7079
                                CHECK_OBJ(mi, MEMITEM_MAGIC);
163 7079
                                FREE_OBJ(mi);
164
                        }
165 672
                        VSC_mempool_Destroy(&mpl->vsc_seg);
166 672
                        Lck_Unlock(&mpl->mtx);
167 672
                        Lck_Delete(&mpl->mtx);
168 672
                        FREE_OBJ(mpl);
169 672
                        break;
170
                }
171
172 2200612
                if (mpl->n_pool < mpl->param->min_pool &&
173 1053553
                    mi != NULL && mi->size >= *mpl->cur_size) {
174 1053547
                        CHECK_OBJ(mi, MEMITEM_MAGIC);
175 1053547
                        mpl->vsc->pool = ++mpl->n_pool;
176 1053547
                        mi->touched = mpl->t_now;
177 1053547
                        VTAILQ_INSERT_HEAD(&mpl->list, mi, list);
178 1053547
                        mi = NULL;
179 1053547
                        mpl_slp = .01;  // random
180
181 1053547
                }
182 1147092
                if (mpl->n_pool > mpl->param->max_pool && mi == NULL) {
183 495
                        mi = VTAILQ_FIRST(&mpl->list);
184 495
                        CHECK_OBJ_NOTNULL(mi, MEMITEM_MAGIC);
185 495
                        mpl->vsc->pool = --mpl->n_pool;
186 495
                        mpl->vsc->surplus++;
187 495
                        VTAILQ_REMOVE(&mpl->list, mi, list);
188 495
                        mpl_slp = .01;  // random
189 495
                }
190 1147096
                if (mi == NULL) {
191 1146684
                        mi = VTAILQ_FIRST(&mpl->surplus);
192 1146684
                        if (mi != NULL) {
193 21
                                CHECK_OBJ(mi, MEMITEM_MAGIC);
194 21
                                VTAILQ_REMOVE(&mpl->surplus, mi, list);
195 21
                                mpl_slp = .01;  // random
196 21
                        }
197 1146684
                }
198 1147096
                if (mi == NULL && mpl->n_pool > mpl->param->min_pool) {
199 14699
                        mi = VTAILQ_LAST(&mpl->list, memhead_s);
200 14699
                        CHECK_OBJ_NOTNULL(mi, MEMITEM_MAGIC);
201 14699
                        if (mi->touched + mpl->param->max_age < mpl->t_now) {
202 3555
                                mpl->vsc->pool = --mpl->n_pool;
203 3555
                                mpl->vsc->timeout++;
204 3555
                                VTAILQ_REMOVE(&mpl->list, mi, list);
205 3555
                                mpl_slp = .01;  // random
206 3555
                        } else {
207 11144
                                mi = NULL;
208 11144
                                last = mpl->t_now;
209
                        }
210 1147096
                } else if (mpl->n_pool <= mpl->param->min_pool) {
211 1132030
                        last = mpl->t_now;
212 1132030
                }
213
214 1147096
                Lck_Unlock(&mpl->mtx);
215
216 1147096
                if (mi != NULL) {
217 4109
                        CHECK_OBJ(mi, MEMITEM_MAGIC);
218 4109
                        FREE_OBJ(mi);
219 4109
                }
220
        }
221 672
        return (NULL);
222
}
223
224
/*---------------------------------------------------------------------
225
 * Create a new memory pool, and start the guard thread for it.
226
 */
227
228
struct mempool *
229 103848
MPL_New(const char *name,
230
    volatile struct poolparam *pp, volatile unsigned *cur_size)
231
{
232
        struct mempool *mpl;
233
234 103848
        ALLOC_OBJ(mpl, MEMPOOL_MAGIC);
235 103846
        AN(mpl);
236 103846
        bprintf(mpl->name, "MPL_%s", name);
237 103846
        mpl->param = pp;
238 103846
        mpl->cur_size = cur_size;
239 103846
        VTAILQ_INIT(&mpl->list);
240 103846
        VTAILQ_INIT(&mpl->surplus);
241 103846
        Lck_New(&mpl->mtx, lck_mempool);
242
        /* XXX: prealloc min_pool */
243 103846
        mpl->vsc = VSC_mempool_New(NULL, &mpl->vsc_seg, mpl->name + 4);
244 103846
        AN(mpl->vsc);
245 103846
        PTOK(pthread_create(&mpl->thread, NULL, mpl_guard, mpl));
246 103846
        PTOK(pthread_detach(mpl->thread));
247 103846
        return (mpl);
248
}
249
250
/*---------------------------------------------------------------------
251
 * Destroy a memory pool.  There must be no live items, and we cheat
252
 * and leave all the hard work to the guard thread.
253
 */
254
255
void
256 81900
MPL_Destroy(struct mempool **mpp)
257
{
258
        struct mempool *mpl;
259
260 81900
        TAKE_OBJ_NOTNULL(mpl, mpp, MEMPOOL_MAGIC);
261 81900
        Lck_Lock(&mpl->mtx);
262 81900
        AZ(mpl->live);
263 81900
        mpl->self_destruct = 1;
264 81900
        Lck_Unlock(&mpl->mtx);
265 81900
}
266
267
/*---------------------------------------------------------------------
268
 */
269
270
void *
271 169387
MPL_Get(struct mempool *mpl, unsigned *size)
272
{
273
        struct memitem *mi;
274
275 169387
        CHECK_OBJ_NOTNULL(mpl, MEMPOOL_MAGIC);
276 169387
        AN(size);
277
278 169387
        Lck_Lock(&mpl->mtx);
279
280 169387
        mpl->vsc->allocs++;
281 169387
        mpl->vsc->live = ++mpl->live;
282
283 169387
        do {
284 169407
                mi = VTAILQ_FIRST(&mpl->list);
285 169407
                if (mi == NULL) {
286 276
                        mpl->vsc->randry++;
287 276
                        break;
288
                }
289 169131
                mpl->vsc->pool = --mpl->n_pool;
290 169131
                CHECK_OBJ(mi, MEMITEM_MAGIC);
291 169131
                VTAILQ_REMOVE(&mpl->list, mi, list);
292 169131
                if (mi->size < *mpl->cur_size) {
293 21
                        mpl->vsc->toosmall++;
294 21
                        VTAILQ_INSERT_HEAD(&mpl->surplus, mi, list);
295 21
                        mi = NULL;
296 21
                } else {
297 169110
                        mpl->vsc->recycle++;
298
                }
299 169131
        } while (mi == NULL);
300
301 169395
        Lck_Unlock(&mpl->mtx);
302
303 169395
        if (mi == NULL)
304 276
                mi = mpl_alloc(mpl);
305 169395
        *size = mi->size - sizeof *mi;
306
307 169395
        CHECK_OBJ(mi, MEMITEM_MAGIC);
308
        /* Throw away sizeof info for FlexeLint: */
309 169395
        return ((void *)(uintptr_t)(mi + 1));
310
}
311
312
void
313 169034
MPL_Free(struct mempool *mpl, void *item)
314
{
315
        struct memitem *mi;
316
317 169034
        CHECK_OBJ_NOTNULL(mpl, MEMPOOL_MAGIC);
318 169034
        AN(item);
319
320 169034
        mi = (void*)((uintptr_t)item - sizeof(*mi));
321 169034
        CHECK_OBJ_NOTNULL(mi, MEMITEM_MAGIC);
322 169034
        memset(item, 0, mi->size - sizeof *mi);
323
324 169034
        Lck_Lock(&mpl->mtx);
325
326 169034
        mpl->vsc->frees++;
327 169034
        mpl->vsc->live = --mpl->live;
328
329 169034
        if (mi->size < *mpl->cur_size) {
330 0
                mpl->vsc->toosmall++;
331 0
                VTAILQ_INSERT_HEAD(&mpl->surplus, mi, list);
332 0
        } else {
333 169034
                mpl->vsc->pool = ++mpl->n_pool;
334 169034
                mi->touched = mpl->t_now;
335 169034
                VTAILQ_INSERT_HEAD(&mpl->list, mi, list);
336
        }
337
338 169034
        Lck_Unlock(&mpl->mtx);
339 169034
}
340
341
void
342 68208
MPL_AssertSane(const void *item)
343
{
344
        struct memitem *mi;
345 68208
        mi = (void*)((uintptr_t)item - sizeof(*mi));
346 68208
        CHECK_OBJ_NOTNULL(mi, MEMITEM_MAGIC);
347 68208
}