]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/mmap-cache.c
journal: remove journal_file_object_keep/release functions
[thirdparty/systemd.git] / src / journal / mmap-cache.c
CommitLineData
16e9f408
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2012 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
16e9f408
LP
22#include <errno.h>
23#include <stdlib.h>
f8019684 24#include <sys/mman.h>
16e9f408
LP
25#include <string.h>
26
f8019684
LP
27#include "hashmap.h"
28#include "list.h"
29#include "log.h"
16e9f408 30#include "util.h"
f8019684 31#include "macro.h"
16e9f408
LP
32#include "mmap-cache.h"
33
f8019684
LP
34typedef struct Window Window;
35typedef struct Context Context;
36typedef struct FileDescriptor FileDescriptor;
84168d80 37
f8019684
LP
38struct Window {
39 MMapCache *cache;
40
1b8951e5 41 bool keep_always;
f8019684 42 bool in_unused;
16e9f408 43
68667801 44 int prot;
16e9f408
LP
45 void *ptr;
46 uint64_t offset;
f8019684
LP
47 size_t size;
48
49 FileDescriptor *fd;
16e9f408 50
f8019684
LP
51 LIST_FIELDS(Window, by_fd);
52 LIST_FIELDS(Window, unused);
53
54 LIST_HEAD(Context, contexts);
55};
16e9f408 56
f8019684
LP
57struct Context {
58 MMapCache *cache;
59 unsigned id;
60 Window *window;
16e9f408 61
f8019684
LP
62 LIST_FIELDS(Context, by_window);
63};
64
65struct FileDescriptor {
66 MMapCache *cache;
16e9f408 67 int fd;
f8019684
LP
68 LIST_HEAD(Window, windows);
69};
16e9f408
LP
70
71struct MMapCache {
f8019684 72 int n_ref;
68667801 73 unsigned n_windows;
16e9f408 74
bf807d4d
LP
75 unsigned n_hit, n_missed;
76
77
f8019684
LP
78 Hashmap *fds;
79 Hashmap *contexts;
16e9f408 80
f8019684
LP
81 LIST_HEAD(Window, unused);
82 Window *last_unused;
16e9f408
LP
83};
84
f8019684 85#define WINDOWS_MIN 64
fad5a6c6
MS
86
87#ifdef ENABLE_DEBUG_MMAP_CACHE
88/* Tiny windows increase mmap activity and the chance of exposing unsafe use. */
89# define WINDOW_SIZE (page_size())
90#else
91# define WINDOW_SIZE (8ULL*1024ULL*1024ULL)
92#endif
16e9f408 93
f8019684
LP
94MMapCache* mmap_cache_new(void) {
95 MMapCache *m;
16e9f408 96
f8019684
LP
97 m = new0(MMapCache, 1);
98 if (!m)
99 return NULL;
16e9f408 100
f8019684
LP
101 m->n_ref = 1;
102 return m;
16e9f408
LP
103}
104
f8019684 105MMapCache* mmap_cache_ref(MMapCache *m) {
16e9f408 106 assert(m);
f8019684 107 assert(m->n_ref > 0);
16e9f408 108
f8019684
LP
109 m->n_ref ++;
110 return m;
111}
f65425cb 112
f8019684
LP
113static void window_unlink(Window *w) {
114 Context *c;
f65425cb 115
f8019684 116 assert(w);
16e9f408 117
f8019684
LP
118 if (w->ptr)
119 munmap(w->ptr, w->size);
16e9f408 120
f8019684 121 if (w->fd)
71fda00f 122 LIST_REMOVE(by_fd, w->fd->windows, w);
16e9f408 123
f8019684
LP
124 if (w->in_unused) {
125 if (w->cache->last_unused == w)
126 w->cache->last_unused = w->unused_prev;
16e9f408 127
71fda00f 128 LIST_REMOVE(unused, w->cache->unused, w);
f65425cb 129 }
16e9f408 130
f8019684
LP
131 LIST_FOREACH(by_window, c, w->contexts) {
132 assert(c->window == w);
133 c->window = NULL;
f65425cb 134 }
16e9f408
LP
135}
136
f8019684
LP
137static void window_free(Window *w) {
138 assert(w);
f65425cb 139
f8019684 140 window_unlink(w);
89de6947 141 w->cache->n_windows--;
f8019684
LP
142 free(w);
143}
f65425cb 144
44a6b1b6 145_pure_ static bool window_matches(Window *w, int fd, int prot, uint64_t offset, size_t size) {
f8019684
LP
146 assert(w);
147 assert(fd >= 0);
148 assert(size > 0);
16e9f408 149
f8019684
LP
150 return
151 w->fd &&
152 fd == w->fd->fd &&
153 prot == w->prot &&
154 offset >= w->offset &&
155 offset + size <= w->offset + w->size;
16e9f408
LP
156}
157
f8019684
LP
158static Window *window_add(MMapCache *m) {
159 Window *w;
16e9f408
LP
160
161 assert(m);
16e9f408 162
f8019684 163 if (!m->last_unused || m->n_windows <= WINDOWS_MIN) {
f65425cb 164
f8019684
LP
165 /* Allocate a new window */
166 w = new0(Window, 1);
167 if (!w)
168 return NULL;
89de6947 169 m->n_windows++;
f65425cb 170 } else {
16e9f408 171
f8019684
LP
172 /* Reuse an existing one */
173 w = m->last_unused;
174 window_unlink(w);
175 zero(*w);
f65425cb 176 }
f8019684
LP
177
178 w->cache = m;
179 return w;
16e9f408
LP
180}
181
f8019684
LP
182static void context_detach_window(Context *c) {
183 Window *w;
16e9f408 184
f8019684 185 assert(c);
16e9f408 186
f8019684 187 if (!c->window)
16e9f408
LP
188 return;
189
f8019684
LP
190 w = c->window;
191 c->window = NULL;
71fda00f 192 LIST_REMOVE(by_window, w->contexts, c);
16e9f408 193
1b8951e5 194 if (!w->contexts && !w->keep_always) {
f8019684 195 /* Not used anymore? */
fad5a6c6
MS
196#ifdef ENABLE_DEBUG_MMAP_CACHE
197 /* Unmap unused windows immediately to expose use-after-unmap
198 * by SIGSEGV. */
199 window_free(w);
200#else
71fda00f 201 LIST_PREPEND(unused, c->cache->unused, w);
f8019684
LP
202 if (!c->cache->last_unused)
203 c->cache->last_unused = w;
16e9f408 204
f8019684 205 w->in_unused = true;
fad5a6c6 206#endif
f8019684 207 }
16e9f408
LP
208}
209
f8019684
LP
210static void context_attach_window(Context *c, Window *w) {
211 assert(c);
212 assert(w);
16e9f408 213
f8019684 214 if (c->window == w)
16e9f408
LP
215 return;
216
f8019684 217 context_detach_window(c);
16e9f408 218
e18021f7 219 if (w->in_unused) {
f8019684 220 /* Used again? */
71fda00f 221 LIST_REMOVE(unused, c->cache->unused, w);
a2ab7ee6
CG
222 if (c->cache->last_unused == w)
223 c->cache->last_unused = w->unused_prev;
16e9f408 224
f8019684
LP
225 w->in_unused = false;
226 }
f65425cb 227
f8019684 228 c->window = w;
71fda00f 229 LIST_PREPEND(by_window, w->contexts, c);
16e9f408
LP
230}
231
f8019684
LP
232static Context *context_add(MMapCache *m, unsigned id) {
233 Context *c;
234 int r;
16e9f408
LP
235
236 assert(m);
237
f8019684
LP
238 c = hashmap_get(m->contexts, UINT_TO_PTR(id + 1));
239 if (c)
240 return c;
241
d5099efc 242 r = hashmap_ensure_allocated(&m->contexts, NULL);
f8019684
LP
243 if (r < 0)
244 return NULL;
245
246 c = new0(Context, 1);
247 if (!c)
248 return NULL;
16e9f408 249
f8019684
LP
250 c->cache = m;
251 c->id = id;
16e9f408 252
f8019684
LP
253 r = hashmap_put(m->contexts, UINT_TO_PTR(id + 1), c);
254 if (r < 0) {
255 free(c);
256 return NULL;
16e9f408
LP
257 }
258
f8019684 259 return c;
16e9f408
LP
260}
261
f8019684
LP
262static void context_free(Context *c) {
263 assert(c);
16e9f408 264
f8019684 265 context_detach_window(c);
16e9f408 266
f8019684
LP
267 if (c->cache)
268 assert_se(hashmap_remove(c->cache->contexts, UINT_TO_PTR(c->id + 1)));
16e9f408 269
f8019684
LP
270 free(c);
271}
272
273static void fd_free(FileDescriptor *f) {
274 assert(f);
275
276 while (f->windows)
277 window_free(f->windows);
278
279 if (f->cache)
280 assert_se(hashmap_remove(f->cache->fds, INT_TO_PTR(f->fd + 1)));
281
282 free(f);
283}
284
285static FileDescriptor* fd_add(MMapCache *m, int fd) {
286 FileDescriptor *f;
287 int r;
288
289 assert(m);
290 assert(fd >= 0);
291
292 f = hashmap_get(m->fds, INT_TO_PTR(fd + 1));
293 if (f)
294 return f;
295
d5099efc 296 r = hashmap_ensure_allocated(&m->fds, NULL);
f8019684 297 if (r < 0)
16e9f408 298 return NULL;
16e9f408 299
f8019684
LP
300 f = new0(FileDescriptor, 1);
301 if (!f)
16e9f408 302 return NULL;
16e9f408 303
f8019684
LP
304 f->cache = m;
305 f->fd = fd;
306
307 r = hashmap_put(m->fds, UINT_TO_PTR(fd + 1), f);
308 if (r < 0) {
309 free(f);
16e9f408
LP
310 return NULL;
311 }
312
f8019684 313 return f;
16e9f408
LP
314}
315
f8019684
LP
316static void mmap_cache_free(MMapCache *m) {
317 Context *c;
318 FileDescriptor *f;
319
16e9f408 320 assert(m);
16e9f408 321
f8019684
LP
322 while ((c = hashmap_first(m->contexts)))
323 context_free(c);
324
8e6d9397
GM
325 hashmap_free(m->contexts);
326
f8019684
LP
327 while ((f = hashmap_first(m->fds)))
328 fd_free(f);
329
8e6d9397
GM
330 hashmap_free(m->fds);
331
f8019684
LP
332 while (m->unused)
333 window_free(m->unused);
334
335 free(m);
16e9f408
LP
336}
337
338MMapCache* mmap_cache_unref(MMapCache *m) {
339 assert(m);
340 assert(m->n_ref > 0);
341
f8019684
LP
342 m->n_ref --;
343 if (m->n_ref == 0)
16e9f408 344 mmap_cache_free(m);
16e9f408
LP
345
346 return NULL;
347}
348
f8019684
LP
349static int make_room(MMapCache *m) {
350 assert(m);
351
352 if (!m->last_unused)
353 return 0;
354
355 window_free(m->last_unused);
356 return 1;
357}
358
359static int try_context(
360 MMapCache *m,
361 int fd,
362 int prot,
363 unsigned context,
364 bool keep_always,
365 uint64_t offset,
366 size_t size,
1b8951e5 367 void **ret) {
f8019684
LP
368
369 Context *c;
f65425cb 370
16e9f408 371 assert(m);
f8019684
LP
372 assert(m->n_ref > 0);
373 assert(fd >= 0);
374 assert(size > 0);
1b8951e5 375 assert(ret);
16e9f408 376
f8019684
LP
377 c = hashmap_get(m->contexts, UINT_TO_PTR(context+1));
378 if (!c)
16e9f408 379 return 0;
16e9f408 380
f8019684 381 assert(c->id == context);
16e9f408 382
f8019684
LP
383 if (!c->window)
384 return 0;
f65425cb 385
f8019684 386 if (!window_matches(c->window, fd, prot, offset, size)) {
f65425cb 387
f8019684
LP
388 /* Drop the reference to the window, since it's unnecessary now */
389 context_detach_window(c);
390 return 0;
f65425cb
LP
391 }
392
1b8951e5 393 c->window->keep_always |= keep_always;
16e9f408 394
1b8951e5 395 *ret = (uint8_t*) c->window->ptr + (offset - c->window->offset);
f8019684 396 return 1;
16e9f408
LP
397}
398
f8019684
LP
399static int find_mmap(
400 MMapCache *m,
401 int fd,
402 int prot,
403 unsigned context,
404 bool keep_always,
405 uint64_t offset,
406 size_t size,
1b8951e5 407 void **ret) {
f8019684
LP
408
409 FileDescriptor *f;
410 Window *w;
411 Context *c;
16e9f408
LP
412
413 assert(m);
f8019684
LP
414 assert(m->n_ref > 0);
415 assert(fd >= 0);
416 assert(size > 0);
16e9f408 417
f8019684
LP
418 f = hashmap_get(m->fds, INT_TO_PTR(fd + 1));
419 if (!f)
420 return 0;
16e9f408 421
f8019684 422 assert(f->fd == fd);
16e9f408 423
f8019684
LP
424 LIST_FOREACH(by_fd, w, f->windows)
425 if (window_matches(w, fd, prot, offset, size))
426 break;
16e9f408 427
f8019684
LP
428 if (!w)
429 return 0;
430
431 c = context_add(m, context);
432 if (!c)
433 return -ENOMEM;
434
435 context_attach_window(c, w);
ae97089d 436 w->keep_always += keep_always;
16e9f408 437
1b8951e5 438 *ret = (uint8_t*) w->ptr + (offset - w->offset);
f8019684 439 return 1;
16e9f408
LP
440}
441
f8019684 442static int add_mmap(
16e9f408
LP
443 MMapCache *m,
444 int fd,
16e9f408
LP
445 int prot,
446 unsigned context,
fcde2389 447 bool keep_always,
16e9f408 448 uint64_t offset,
f8019684 449 size_t size,
fcde2389 450 struct stat *st,
1b8951e5 451 void **ret) {
16e9f408 452
16e9f408 453 uint64_t woffset, wsize;
f8019684
LP
454 Context *c;
455 FileDescriptor *f;
456 Window *w;
457 void *d;
16e9f408
LP
458 int r;
459
460 assert(m);
f8019684 461 assert(m->n_ref > 0);
16e9f408 462 assert(fd >= 0);
16e9f408 463 assert(size > 0);
1b8951e5 464 assert(ret);
16e9f408
LP
465
466 woffset = offset & ~((uint64_t) page_size() - 1ULL);
467 wsize = size + (offset - woffset);
468 wsize = PAGE_ALIGN(wsize);
469
470 if (wsize < WINDOW_SIZE) {
471 uint64_t delta;
472
beec0085 473 delta = PAGE_ALIGN((WINDOW_SIZE - wsize) / 2);
16e9f408
LP
474
475 if (delta > offset)
476 woffset = 0;
477 else
478 woffset -= delta;
479
480 wsize = WINDOW_SIZE;
481 }
482
fcde2389
LP
483 if (st) {
484 /* Memory maps that are larger then the files
c5315881 485 underneath have undefined behavior. Hence, clamp
fcde2389
LP
486 things to the file size if we know it */
487
488 if (woffset >= (uint64_t) st->st_size)
489 return -EADDRNOTAVAIL;
490
491 if (woffset + wsize > (uint64_t) st->st_size)
492 wsize = PAGE_ALIGN(st->st_size - woffset);
493 }
494
16e9f408
LP
495 for (;;) {
496 d = mmap(NULL, wsize, prot, MAP_SHARED, fd, woffset);
497 if (d != MAP_FAILED)
498 break;
499 if (errno != ENOMEM)
500 return -errno;
501
f8019684 502 r = make_room(m);
16e9f408
LP
503 if (r < 0)
504 return r;
505 if (r == 0)
506 return -ENOMEM;
507 }
508
f8019684
LP
509 c = context_add(m, context);
510 if (!c)
b67ddc7b 511 goto outofmem;
16e9f408 512
f8019684
LP
513 f = fd_add(m, fd);
514 if (!f)
b67ddc7b 515 goto outofmem;
16e9f408 516
f8019684
LP
517 w = window_add(m);
518 if (!w)
b67ddc7b 519 goto outofmem;
16e9f408 520
f8019684
LP
521 w->keep_always = keep_always;
522 w->ptr = d;
523 w->offset = woffset;
524 w->prot = prot;
525 w->size = wsize;
526 w->fd = f;
16e9f408 527
71fda00f 528 LIST_PREPEND(by_fd, f->windows, w);
16e9f408 529
f8019684
LP
530 context_detach_window(c);
531 c->window = w;
71fda00f 532 LIST_PREPEND(by_window, w->contexts, c);
16e9f408 533
1b8951e5 534 *ret = (uint8_t*) w->ptr + (offset - w->offset);
16e9f408 535 return 1;
b67ddc7b
PDS
536
537outofmem:
538 munmap(d, wsize);
539 return -ENOMEM;
16e9f408
LP
540}
541
542int mmap_cache_get(
543 MMapCache *m,
544 int fd,
545 int prot,
546 unsigned context,
fcde2389 547 bool keep_always,
16e9f408 548 uint64_t offset,
f8019684 549 size_t size,
fcde2389 550 struct stat *st,
1b8951e5 551 void **ret) {
16e9f408 552
16e9f408
LP
553 int r;
554
555 assert(m);
f8019684 556 assert(m->n_ref > 0);
16e9f408 557 assert(fd >= 0);
16e9f408 558 assert(size > 0);
1b8951e5 559 assert(ret);
16e9f408 560
f8019684 561 /* Check whether the current context is the right one already */
1b8951e5 562 r = try_context(m, fd, prot, context, keep_always, offset, size, ret);
bf807d4d
LP
563 if (r != 0) {
564 m->n_hit ++;
16e9f408 565 return r;
bf807d4d 566 }
16e9f408 567
f8019684 568 /* Search for a matching mmap */
1b8951e5 569 r = find_mmap(m, fd, prot, context, keep_always, offset, size, ret);
bf807d4d
LP
570 if (r != 0) {
571 m->n_hit ++;
16e9f408 572 return r;
bf807d4d
LP
573 }
574
575 m->n_missed++;
16e9f408 576
f8019684 577 /* Create a new mmap */
1b8951e5 578 return add_mmap(m, fd, prot, context, keep_always, offset, size, st, ret);
ae97089d
ZJS
579}
580
16e9f408 581void mmap_cache_close_fd(MMapCache *m, int fd) {
f8019684 582 FileDescriptor *f;
16e9f408
LP
583
584 assert(m);
f8019684 585 assert(fd >= 0);
16e9f408 586
f8019684
LP
587 f = hashmap_get(m->fds, INT_TO_PTR(fd + 1));
588 if (!f)
16e9f408 589 return;
16e9f408 590
f8019684
LP
591 fd_free(f);
592}
16e9f408 593
f8019684
LP
594void mmap_cache_close_context(MMapCache *m, unsigned context) {
595 Context *c;
16e9f408 596
f8019684 597 assert(m);
16e9f408 598
f8019684
LP
599 c = hashmap_get(m->contexts, UINT_TO_PTR(context + 1));
600 if (!c)
601 return;
16e9f408 602
f8019684 603 context_free(c);
16e9f408 604}
bf807d4d
LP
605
606unsigned mmap_cache_get_hit(MMapCache *m) {
607 assert(m);
608
609 return m->n_hit;
610}
611
612unsigned mmap_cache_get_missed(MMapCache *m) {
613 assert(m);
614
615 return m->n_missed;
616}