1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20: #include <assert.h>
21: #include <errno.h>
22: #include <error.h>
23: #include <fcntl.h>
24: #include <inttypes.h>
25: #include <libintl.h>
26: #include <limits.h>
27: #include <stdlib.h>
28: #include <string.h>
29: #include <unistd.h>
30: #include <sys/mman.h>
31: #include <sys/param.h>
32:
33: #include "dbg_log.h"
34: #include "nscd.h"
35:
36:
37: static int
38: sort_he (const void *p1, const void *p2)
39: {
40: struct hashentry *h1 = *(struct hashentry **) p1;
41: struct hashentry *h2 = *(struct hashentry **) p2;
42:
43: if (h1 < h2)
44: return -1;
45: if (h1 > h2)
46: return 1;
47: return 0;
48: }
49:
50:
51: static int
52: sort_he_data (const void *p1, const void *p2)
53: {
54: struct hashentry *h1 = *(struct hashentry **) p1;
55: struct hashentry *h2 = *(struct hashentry **) p2;
56:
57: if (h1->packet < h2->packet)
58: return -1;
59: if (h1->packet > h2->packet)
60: return 1;
61: return 0;
62: }
63:
64:
65:
66:
67: #define BITMAP_T uint8_t
68: #define BITS (CHAR_BIT * sizeof (BITMAP_T))
69: #define ALLBITS ((((BITMAP_T) 1) << BITS) - 1)
70: #define HIGHBIT (((BITMAP_T) 1) << (BITS - 1))
71:
72:
73: static void
74: markrange (BITMAP_T *mark, ref_t start, size_t len)
75: {
76:
77: start /= BLOCK_ALIGN;
78: len = (len + BLOCK_ALIGN_M1) / BLOCK_ALIGN;
79:
80: size_t elem = start / BITS;
81:
82: if (start % BITS != 0)
83: {
84: if (start % BITS + len <= BITS)
85: {
86:
87: mark[elem] |= (ALLBITS >> (BITS - len)) << (start % BITS);
88: return;
89: }
90:
91: mark[elem++] |= 0xff << (start % BITS);
92: len -= BITS - (start % BITS);
93: }
94:
95: while (len >= BITS)
96: {
97: mark[elem++] = ALLBITS;
98: len -= BITS;
99: }
100:
101: if (len > 0)
102: mark[elem] |= ALLBITS >> (BITS - len);
103: }
104:
105:
106: void
107: gc (struct database_dyn *db)
108: {
109:
110: pthread_rwlock_wrlock (&db->lock);
111:
112:
113: pthread_mutex_lock (&db->memlock);
114:
115:
116:
117:
118:
119: assert (db->head->first_free % BLOCK_ALIGN == 0);
120: BITMAP_T mark[(db->head->first_free / BLOCK_ALIGN + BITS - 1) / BITS];
121: memset (mark, '\0', sizeof (mark));
122:
123:
124:
125: struct hashentry *he[db->head->nentries];
126: struct hashentry *he_data[db->head->nentries];
127:
128: size_t cnt = 0;
129: for (size_t idx = 0; idx < db->head->module; ++idx)
130: {
131: ref_t *prevp = &db->head->array[idx];
132: ref_t run = *prevp;
133:
134: while (run != ENDREF)
135: {
136: assert (cnt < db->head->nentries);
137: he[cnt] = (struct hashentry *) (db->data + run);
138:
139: he[cnt]->prevp = prevp;
140: prevp = &he[cnt]->next;
141:
142:
143: markrange (mark, run, sizeof (struct hashentry));
144:
145:
146:
147: if (he[cnt]->first)
148: {
149: struct datahead *dh
150: = (struct datahead *) (db->data + he[cnt]->packet);
151: markrange (mark, he[cnt]->packet, dh->allocsize);
152: }
153:
154: run = he[cnt]->next;
155:
156: ++cnt;
157: }
158: }
159: assert (cnt == db->head->nentries);
160:
161:
162:
163:
164: memcpy (he_data, he, cnt * sizeof (struct hashentry *));
165: qsort (he_data, cnt, sizeof (struct hashentry *), sort_he_data);
166:
167:
168: qsort (he, cnt, sizeof (struct hashentry *), sort_he);
169:
170:
171: size_t high = sizeof (mark);
172: while (high > 0 && mark[high - 1] == 0)
173: --high;
174:
175:
176: if (high == 0)
177: {
178: db->head->first_free = 0;
179: goto out;
180: }
181:
182:
183: BITMAP_T mask = HIGHBIT;
184: ref_t highref = (high * BITS - 1) * BLOCK_ALIGN;
185: while ((mark[high - 1] & mask) == 0)
186: {
187: mask >>= 1;
188: highref -= BLOCK_ALIGN;
189: }
190:
191:
192:
193: size_t byte = 0;
194:
195: while (byte < high && mark[byte] == ALLBITS)
196: ++byte;
197:
198: if (byte == high
199: || (byte == high - 1 && (mark[byte] & ~(mask | (mask - 1))) == 0))
200:
201: goto out;
202:
203: mask = 1;
204: cnt = 0;
205: while ((mark[byte] & mask) != 0)
206: {
207: ++cnt;
208: mask <<= 1;
209: }
210: ref_t off_free = (byte * BITS + cnt) * BLOCK_ALIGN;
211: assert (off_free <= db->head->first_free);
212:
213: struct hashentry **next_hash = he;
214: struct hashentry **next_data = he_data;
215:
216:
217:
218: while (next_hash < &he[db->head->nentries]
219: && *next_hash < (struct hashentry *) (db->data + off_free))
220: ++next_hash;
221:
222: while (next_data < &he_data[db->head->nentries]
223: && (*next_data)->packet < off_free)
224: ++next_data;
225:
226:
227:
228:
229: ++db->head->gc_cycle;
230: assert ((db->head->gc_cycle & 1) == 1);
231:
232:
233:
234:
235: struct moveinfo
236: {
237: void *from;
238: void *to;
239: size_t size;
240: struct moveinfo *next;
241: } *moves = NULL;
242:
243: while (byte < high)
244: {
245:
246:
247:
248: if ((mark[byte] & ~(mask - 1)) == 0)
249: {
250:
251:
252: do
253: ++byte;
254: while (byte < high && mark[byte] == 0);
255:
256: if (byte == high)
257:
258: break;
259:
260: mask = 1;
261: cnt = 0;
262: }
263:
264: while ((mark[byte] & mask) == 0)
265: {
266: ++cnt;
267: mask <<= 1;
268: }
269:
270: ref_t off_alloc = (byte * BITS + cnt) * BLOCK_ALIGN;
271: assert (off_alloc <= db->head->first_free);
272:
273:
274: if ((mark[byte] & ~(mask - 1)) == (BITMAP_T) ~(mask - 1))
275: {
276:
277: do
278: ++byte;
279: while (byte < high && mark[byte] == ALLBITS);
280:
281: mask = 1;
282: cnt = 0;
283: }
284: if (byte < high)
285: {
286:
287: while ((mark[byte] & mask) != 0)
288: {
289: ++cnt;
290: mask <<= 1;
291: }
292: }
293:
294: ref_t off_allocend = (byte * BITS + cnt) * BLOCK_ALIGN;
295: assert (off_allocend <= db->head->first_free);
296:
297:
298:
299:
300: ref_t disp = off_alloc - off_free;
301:
302: struct moveinfo *new_move
303: = (struct moveinfo *) alloca (sizeof (*new_move));
304: new_move->from = db->data + off_alloc;
305: new_move->to = db->data + off_free;
306: new_move->size = off_allocend - off_alloc;
307:
308: if (moves == NULL)
309: moves = new_move->next = new_move;
310: else
311: {
312: new_move->next = moves->next;
313: moves = moves->next = new_move;
314: }
315:
316:
317: off_free += off_allocend - off_alloc;
318:
319: while (off_alloc < off_allocend)
320: {
321:
322:
323: if ((struct hashentry *) (db->data + off_alloc) == *next_hash)
324: {
325:
326: *(*next_hash++)->prevp -= disp;
327:
328: off_alloc += ((sizeof (struct hashentry) + BLOCK_ALIGN_M1)
329: & ~BLOCK_ALIGN_M1);
330: }
331: else
332: {
333: assert (next_data < &he_data[db->head->nentries]);
334: assert ((*next_data)->packet == off_alloc);
335:
336: struct datahead *dh = (struct datahead *) (db->data + off_alloc);
337: do
338: {
339: assert ((*next_data)->key >= (*next_data)->packet);
340: assert ((*next_data)->key + (*next_data)->len
341: <= (*next_data)->packet + dh->allocsize);
342:
343: (*next_data)->packet -= disp;
344: (*next_data)->key -= disp;
345: ++next_data;
346: }
347: while (next_data < &he_data[db->head->nentries]
348: && (*next_data)->packet == off_alloc);
349:
350: off_alloc += (dh->allocsize + BLOCK_ALIGN_M1) & ~BLOCK_ALIGN_M1;
351: }
352: }
353: assert (off_alloc == off_allocend);
354:
355: assert (off_alloc <= db->head->first_free);
356: if (off_alloc == db->head->first_free)
357:
358: break;
359: }
360: assert (next_hash == &he[db->head->nentries]);
361: assert (next_data == &he_data[db->head->nentries]);
362:
363:
364: if (moves != NULL)
365: {
366: struct moveinfo *runp = moves->next;
367: do
368: {
369: assert ((char *) runp->to >= db->data);
370: assert ((char *) runp->to + runp->size
371: <= db->data + db->head->first_free);
372: assert ((char *) runp->from >= db->data);
373: assert ((char *) runp->from + runp->size
374: <= db->data + db->head->first_free);
375:
376:
377: memmove (runp->to, runp->from, runp->size);
378: runp = runp->next;
379: }
380: while (runp != moves->next);
381:
382: if (__builtin_expect (debug_level >= 3, 0))
383: dbg_log (_("freed %zu bytes in %s cache"),
384: db->head->first_free
385: - ((char *) moves->to + moves->size - db->data),
386: dbnames[db - dbs]);
387:
388:
389:
390: db->head->first_free = (char *) moves->to + moves->size - db->data;
391:
392:
393: if (__builtin_expect (debug_level >= 3, 0))
394: {
395: for (size_t idx = 0; idx < db->head->module; ++idx)
396: {
397: ref_t run = db->head->array[idx];
398: size_t cnt = 0;
399:
400: while (run != ENDREF)
401: {
402: if (run + sizeof (struct hashentry) > db->head->first_free)
403: {
404: dbg_log ("entry %zu in hash bucket %zu out of bounds: "
405: "%" PRIu32 "+%zu > %zu\n",
406: cnt, idx, run, sizeof (struct hashentry),
407: (size_t) db->head->first_free);
408: break;
409: }
410:
411: struct hashentry *he = (struct hashentry *) (db->data + run);
412:
413: if (he->key + he->len > db->head->first_free)
414: dbg_log ("key of entry %zu in hash bucket %zu out of "
415: "bounds: %" PRIu32 "+%zu > %zu\n",
416: cnt, idx, he->key, (size_t) he->len,
417: (size_t) db->head->first_free);
418:
419: if (he->packet + sizeof (struct datahead)
420: > db->head->first_free)
421: dbg_log ("packet of entry %zu in hash bucket %zu out of "
422: "bounds: %" PRIu32 "+%zu > %zu\n",
423: cnt, idx, he->packet, sizeof (struct datahead),
424: (size_t) db->head->first_free);
425: else
426: {
427: struct datahead *dh = (struct datahead *) (db->data
428: + he->packet);
429: if (he->packet + dh->allocsize
430: > db->head->first_free)
431: dbg_log ("full key of entry %zu in hash bucket %zu "
432: "out of bounds: %" PRIu32 "+%zu > %zu",
433: cnt, idx, he->packet, (size_t) dh->allocsize,
434: (size_t) db->head->first_free);
435: }
436:
437: run = he->next;
438: ++cnt;
439: }
440: }
441: }
442: }
443:
444:
445: if (db->persistent)
446: msync (db->head, db->data + db->head->first_free - (char *) db->head,
447: MS_ASYNC);
448:
449:
450:
451: ++db->head->gc_cycle;
452: assert ((db->head->gc_cycle & 1) == 0);
453:
454:
455: out:
456: pthread_mutex_unlock (&db->memlock);
457: pthread_rwlock_unlock (&db->lock);
458: }
459:
460:
461: void *
462: mempool_alloc (struct database_dyn *db, size_t len)
463: {
464:
465:
466: if ((len & BLOCK_ALIGN_M1) != 0)
467: len += BLOCK_ALIGN - (len & BLOCK_ALIGN_M1);
468:
469: pthread_mutex_lock (&db->memlock);
470:
471: assert ((db->head->first_free & BLOCK_ALIGN_M1) == 0);
472:
473: bool tried_resize = false;
474: void *res;
475: retry:
476: res = db->data + db->head->first_free;
477:
478: if (__builtin_expect (db->head->first_free + len > db->head->data_size, 0))
479: {
480: if (! tried_resize)
481: {
482:
483: size_t oldtotal = (sizeof (struct database_pers_head)
484: + roundup (db->head->module * sizeof (ref_t), ALIGN)
485: + db->head->data_size);
486: size_t new_data_size = (db->head->data_size
487: + MAX (2 * len, db->head->data_size / 8));
488: size_t newtotal = (sizeof (struct database_pers_head)
489: + roundup (db->head->module * sizeof (ref_t), ALIGN)
490: + new_data_size);
491: if (newtotal > db->max_db_size)
492: {
493: new_data_size -= newtotal - db->max_db_size;
494: newtotal = db->max_db_size;
495: }
496:
497: if (db->mmap_used && newtotal > oldtotal
498:
499:
500: && TEMP_FAILURE_RETRY_VAL (posix_fallocate (db->wr_fd, oldtotal,
501: newtotal
502: - oldtotal)) == 0)
503: {
504: db->head->data_size = new_data_size;
505: tried_resize = true;
506: goto retry;
507: }
508: }
509:
510: if (! db->last_alloc_failed)
511: {
512: dbg_log (_("no more memory for database '%s'"), dbnames[db - dbs]);
513:
514: db->last_alloc_failed = true;
515: }
516:
517:
518: res = NULL;
519: }
520: else
521: {
522: db->head->first_free += len;
523:
524: db->last_alloc_failed = false;
525: }
526:
527: pthread_mutex_unlock (&db->memlock);
528:
529: return res;
530: }