]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/mmap-cache.c
sd-dns: simplify headers
[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
41 bool keep_always;
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
LP
85#define WINDOWS_MIN 64
86#define WINDOW_SIZE (8ULL*1024ULL*1024ULL)
16e9f408 87
f8019684
LP
88MMapCache* mmap_cache_new(void) {
89 MMapCache *m;
16e9f408 90
f8019684
LP
91 m = new0(MMapCache, 1);
92 if (!m)
93 return NULL;
16e9f408 94
f8019684
LP
95 m->n_ref = 1;
96 return m;
16e9f408
LP
97}
98
f8019684 99MMapCache* mmap_cache_ref(MMapCache *m) {
16e9f408 100 assert(m);
f8019684 101 assert(m->n_ref > 0);
16e9f408 102
f8019684
LP
103 m->n_ref ++;
104 return m;
105}
f65425cb 106
f8019684
LP
107static void window_unlink(Window *w) {
108 Context *c;
f65425cb 109
f8019684 110 assert(w);
16e9f408 111
f8019684
LP
112 if (w->ptr)
113 munmap(w->ptr, w->size);
16e9f408 114
f8019684 115 if (w->fd)
71fda00f 116 LIST_REMOVE(by_fd, w->fd->windows, w);
16e9f408 117
f8019684
LP
118 if (w->in_unused) {
119 if (w->cache->last_unused == w)
120 w->cache->last_unused = w->unused_prev;
16e9f408 121
71fda00f 122 LIST_REMOVE(unused, w->cache->unused, w);
f65425cb 123 }
16e9f408 124
f8019684
LP
125 LIST_FOREACH(by_window, c, w->contexts) {
126 assert(c->window == w);
127 c->window = NULL;
f65425cb 128 }
16e9f408
LP
129}
130
f8019684
LP
131static void window_free(Window *w) {
132 assert(w);
f65425cb 133
f8019684 134 window_unlink(w);
89de6947 135 w->cache->n_windows--;
f8019684
LP
136 free(w);
137}
f65425cb 138
44a6b1b6 139_pure_ static bool window_matches(Window *w, int fd, int prot, uint64_t offset, size_t size) {
f8019684
LP
140 assert(w);
141 assert(fd >= 0);
142 assert(size > 0);
16e9f408 143
f8019684
LP
144 return
145 w->fd &&
146 fd == w->fd->fd &&
147 prot == w->prot &&
148 offset >= w->offset &&
149 offset + size <= w->offset + w->size;
16e9f408
LP
150}
151
f8019684
LP
152static Window *window_add(MMapCache *m) {
153 Window *w;
16e9f408
LP
154
155 assert(m);
16e9f408 156
f8019684 157 if (!m->last_unused || m->n_windows <= WINDOWS_MIN) {
f65425cb 158
f8019684
LP
159 /* Allocate a new window */
160 w = new0(Window, 1);
161 if (!w)
162 return NULL;
89de6947 163 m->n_windows++;
f65425cb 164 } else {
16e9f408 165
f8019684
LP
166 /* Reuse an existing one */
167 w = m->last_unused;
168 window_unlink(w);
169 zero(*w);
f65425cb 170 }
f8019684
LP
171
172 w->cache = m;
173 return w;
16e9f408
LP
174}
175
f8019684
LP
176static void context_detach_window(Context *c) {
177 Window *w;
16e9f408 178
f8019684 179 assert(c);
16e9f408 180
f8019684 181 if (!c->window)
16e9f408
LP
182 return;
183
f8019684
LP
184 w = c->window;
185 c->window = NULL;
71fda00f 186 LIST_REMOVE(by_window, w->contexts, c);
16e9f408 187
e18021f7 188 if (!w->contexts && !w->keep_always) {
f8019684 189 /* Not used anymore? */
71fda00f 190 LIST_PREPEND(unused, c->cache->unused, w);
f8019684
LP
191 if (!c->cache->last_unused)
192 c->cache->last_unused = w;
16e9f408 193
f8019684
LP
194 w->in_unused = true;
195 }
16e9f408
LP
196}
197
f8019684
LP
198static void context_attach_window(Context *c, Window *w) {
199 assert(c);
200 assert(w);
16e9f408 201
f8019684 202 if (c->window == w)
16e9f408
LP
203 return;
204
f8019684 205 context_detach_window(c);
16e9f408 206
e18021f7 207 if (w->in_unused) {
f8019684 208 /* Used again? */
71fda00f 209 LIST_REMOVE(unused, c->cache->unused, w);
a2ab7ee6
CG
210 if (c->cache->last_unused == w)
211 c->cache->last_unused = w->unused_prev;
16e9f408 212
f8019684
LP
213 w->in_unused = false;
214 }
f65425cb 215
f8019684 216 c->window = w;
71fda00f 217 LIST_PREPEND(by_window, w->contexts, c);
16e9f408
LP
218}
219
f8019684
LP
220static Context *context_add(MMapCache *m, unsigned id) {
221 Context *c;
222 int r;
16e9f408
LP
223
224 assert(m);
225
f8019684
LP
226 c = hashmap_get(m->contexts, UINT_TO_PTR(id + 1));
227 if (c)
228 return c;
229
230 r = hashmap_ensure_allocated(&m->contexts, trivial_hash_func, trivial_compare_func);
231 if (r < 0)
232 return NULL;
233
234 c = new0(Context, 1);
235 if (!c)
236 return NULL;
16e9f408 237
f8019684
LP
238 c->cache = m;
239 c->id = id;
16e9f408 240
f8019684
LP
241 r = hashmap_put(m->contexts, UINT_TO_PTR(id + 1), c);
242 if (r < 0) {
243 free(c);
244 return NULL;
16e9f408
LP
245 }
246
f8019684 247 return c;
16e9f408
LP
248}
249
f8019684
LP
250static void context_free(Context *c) {
251 assert(c);
16e9f408 252
f8019684 253 context_detach_window(c);
16e9f408 254
f8019684
LP
255 if (c->cache)
256 assert_se(hashmap_remove(c->cache->contexts, UINT_TO_PTR(c->id + 1)));
16e9f408 257
f8019684
LP
258 free(c);
259}
260
261static void fd_free(FileDescriptor *f) {
262 assert(f);
263
264 while (f->windows)
265 window_free(f->windows);
266
267 if (f->cache)
268 assert_se(hashmap_remove(f->cache->fds, INT_TO_PTR(f->fd + 1)));
269
270 free(f);
271}
272
273static FileDescriptor* fd_add(MMapCache *m, int fd) {
274 FileDescriptor *f;
275 int r;
276
277 assert(m);
278 assert(fd >= 0);
279
280 f = hashmap_get(m->fds, INT_TO_PTR(fd + 1));
281 if (f)
282 return f;
283
284 r = hashmap_ensure_allocated(&m->fds, trivial_hash_func, trivial_compare_func);
285 if (r < 0)
16e9f408 286 return NULL;
16e9f408 287
f8019684
LP
288 f = new0(FileDescriptor, 1);
289 if (!f)
16e9f408 290 return NULL;
16e9f408 291
f8019684
LP
292 f->cache = m;
293 f->fd = fd;
294
295 r = hashmap_put(m->fds, UINT_TO_PTR(fd + 1), f);
296 if (r < 0) {
297 free(f);
16e9f408
LP
298 return NULL;
299 }
300
f8019684 301 return f;
16e9f408
LP
302}
303
f8019684
LP
304static void mmap_cache_free(MMapCache *m) {
305 Context *c;
306 FileDescriptor *f;
307
16e9f408 308 assert(m);
16e9f408 309
f8019684
LP
310 while ((c = hashmap_first(m->contexts)))
311 context_free(c);
312
8e6d9397
GM
313 hashmap_free(m->contexts);
314
f8019684
LP
315 while ((f = hashmap_first(m->fds)))
316 fd_free(f);
317
8e6d9397
GM
318 hashmap_free(m->fds);
319
f8019684
LP
320 while (m->unused)
321 window_free(m->unused);
322
323 free(m);
16e9f408
LP
324}
325
326MMapCache* mmap_cache_unref(MMapCache *m) {
327 assert(m);
328 assert(m->n_ref > 0);
329
f8019684
LP
330 m->n_ref --;
331 if (m->n_ref == 0)
16e9f408 332 mmap_cache_free(m);
16e9f408
LP
333
334 return NULL;
335}
336
f8019684
LP
337static int make_room(MMapCache *m) {
338 assert(m);
339
340 if (!m->last_unused)
341 return 0;
342
343 window_free(m->last_unused);
344 return 1;
345}
346
347static int try_context(
348 MMapCache *m,
349 int fd,
350 int prot,
351 unsigned context,
352 bool keep_always,
353 uint64_t offset,
354 size_t size,
355 void **ret) {
356
357 Context *c;
f65425cb 358
16e9f408 359 assert(m);
f8019684
LP
360 assert(m->n_ref > 0);
361 assert(fd >= 0);
362 assert(size > 0);
363 assert(ret);
16e9f408 364
f8019684
LP
365 c = hashmap_get(m->contexts, UINT_TO_PTR(context+1));
366 if (!c)
16e9f408 367 return 0;
16e9f408 368
f8019684 369 assert(c->id == context);
16e9f408 370
f8019684
LP
371 if (!c->window)
372 return 0;
f65425cb 373
f8019684 374 if (!window_matches(c->window, fd, prot, offset, size)) {
f65425cb 375
f8019684
LP
376 /* Drop the reference to the window, since it's unnecessary now */
377 context_detach_window(c);
378 return 0;
f65425cb
LP
379 }
380
f8019684 381 c->window->keep_always = c->window->keep_always || keep_always;
16e9f408 382
f8019684
LP
383 *ret = (uint8_t*) c->window->ptr + (offset - c->window->offset);
384 return 1;
16e9f408
LP
385}
386
f8019684
LP
387static int find_mmap(
388 MMapCache *m,
389 int fd,
390 int prot,
391 unsigned context,
392 bool keep_always,
393 uint64_t offset,
394 size_t size,
395 void **ret) {
396
397 FileDescriptor *f;
398 Window *w;
399 Context *c;
16e9f408
LP
400
401 assert(m);
f8019684
LP
402 assert(m->n_ref > 0);
403 assert(fd >= 0);
404 assert(size > 0);
405 assert(ret);
16e9f408 406
f8019684
LP
407 f = hashmap_get(m->fds, INT_TO_PTR(fd + 1));
408 if (!f)
409 return 0;
16e9f408 410
f8019684 411 assert(f->fd == fd);
16e9f408 412
f8019684
LP
413 LIST_FOREACH(by_fd, w, f->windows)
414 if (window_matches(w, fd, prot, offset, size))
415 break;
16e9f408 416
f8019684
LP
417 if (!w)
418 return 0;
419
420 c = context_add(m, context);
421 if (!c)
422 return -ENOMEM;
423
424 context_attach_window(c, w);
425 w->keep_always = w->keep_always || keep_always;
16e9f408 426
f8019684
LP
427 *ret = (uint8_t*) w->ptr + (offset - w->offset);
428 return 1;
16e9f408
LP
429}
430
f8019684 431static int add_mmap(
16e9f408
LP
432 MMapCache *m,
433 int fd,
16e9f408
LP
434 int prot,
435 unsigned context,
fcde2389 436 bool keep_always,
16e9f408 437 uint64_t offset,
f8019684 438 size_t size,
fcde2389 439 struct stat *st,
16e9f408
LP
440 void **ret) {
441
16e9f408 442 uint64_t woffset, wsize;
f8019684
LP
443 Context *c;
444 FileDescriptor *f;
445 Window *w;
446 void *d;
16e9f408
LP
447 int r;
448
449 assert(m);
f8019684 450 assert(m->n_ref > 0);
16e9f408 451 assert(fd >= 0);
16e9f408
LP
452 assert(size > 0);
453 assert(ret);
454
455 woffset = offset & ~((uint64_t) page_size() - 1ULL);
456 wsize = size + (offset - woffset);
457 wsize = PAGE_ALIGN(wsize);
458
459 if (wsize < WINDOW_SIZE) {
460 uint64_t delta;
461
beec0085 462 delta = PAGE_ALIGN((WINDOW_SIZE - wsize) / 2);
16e9f408
LP
463
464 if (delta > offset)
465 woffset = 0;
466 else
467 woffset -= delta;
468
469 wsize = WINDOW_SIZE;
470 }
471
fcde2389
LP
472 if (st) {
473 /* Memory maps that are larger then the files
c5315881 474 underneath have undefined behavior. Hence, clamp
fcde2389
LP
475 things to the file size if we know it */
476
477 if (woffset >= (uint64_t) st->st_size)
478 return -EADDRNOTAVAIL;
479
480 if (woffset + wsize > (uint64_t) st->st_size)
481 wsize = PAGE_ALIGN(st->st_size - woffset);
482 }
483
16e9f408
LP
484 for (;;) {
485 d = mmap(NULL, wsize, prot, MAP_SHARED, fd, woffset);
486 if (d != MAP_FAILED)
487 break;
488 if (errno != ENOMEM)
489 return -errno;
490
f8019684 491 r = make_room(m);
16e9f408
LP
492 if (r < 0)
493 return r;
494 if (r == 0)
495 return -ENOMEM;
496 }
497
f8019684
LP
498 c = context_add(m, context);
499 if (!c)
500 return -ENOMEM;
16e9f408 501
f8019684
LP
502 f = fd_add(m, fd);
503 if (!f)
504 return -ENOMEM;
16e9f408 505
f8019684
LP
506 w = window_add(m);
507 if (!w)
508 return -ENOMEM;
16e9f408 509
f8019684
LP
510 w->keep_always = keep_always;
511 w->ptr = d;
512 w->offset = woffset;
513 w->prot = prot;
514 w->size = wsize;
515 w->fd = f;
16e9f408 516
71fda00f 517 LIST_PREPEND(by_fd, f->windows, w);
16e9f408 518
f8019684
LP
519 context_detach_window(c);
520 c->window = w;
71fda00f 521 LIST_PREPEND(by_window, w->contexts, c);
16e9f408 522
f8019684 523 *ret = (uint8_t*) w->ptr + (offset - w->offset);
16e9f408
LP
524 return 1;
525}
526
527int mmap_cache_get(
528 MMapCache *m,
529 int fd,
530 int prot,
531 unsigned context,
fcde2389 532 bool keep_always,
16e9f408 533 uint64_t offset,
f8019684 534 size_t size,
fcde2389 535 struct stat *st,
16e9f408
LP
536 void **ret) {
537
16e9f408
LP
538 int r;
539
540 assert(m);
f8019684 541 assert(m->n_ref > 0);
16e9f408 542 assert(fd >= 0);
16e9f408
LP
543 assert(size > 0);
544 assert(ret);
545
f8019684
LP
546 /* Check whether the current context is the right one already */
547 r = try_context(m, fd, prot, context, keep_always, offset, size, ret);
bf807d4d
LP
548 if (r != 0) {
549 m->n_hit ++;
16e9f408 550 return r;
bf807d4d 551 }
16e9f408 552
f8019684
LP
553 /* Search for a matching mmap */
554 r = find_mmap(m, fd, prot, context, keep_always, offset, size, ret);
bf807d4d
LP
555 if (r != 0) {
556 m->n_hit ++;
16e9f408 557 return r;
bf807d4d
LP
558 }
559
560 m->n_missed++;
16e9f408 561
f8019684
LP
562 /* Create a new mmap */
563 return add_mmap(m, fd, prot, context, keep_always, offset, size, st, ret);
16e9f408
LP
564}
565
566void mmap_cache_close_fd(MMapCache *m, int fd) {
f8019684 567 FileDescriptor *f;
16e9f408
LP
568
569 assert(m);
f8019684 570 assert(fd >= 0);
16e9f408 571
f8019684
LP
572 f = hashmap_get(m->fds, INT_TO_PTR(fd + 1));
573 if (!f)
16e9f408 574 return;
16e9f408 575
f8019684
LP
576 fd_free(f);
577}
16e9f408 578
f8019684
LP
579void mmap_cache_close_context(MMapCache *m, unsigned context) {
580 Context *c;
16e9f408 581
f8019684 582 assert(m);
16e9f408 583
f8019684
LP
584 c = hashmap_get(m->contexts, UINT_TO_PTR(context + 1));
585 if (!c)
586 return;
16e9f408 587
f8019684 588 context_free(c);
16e9f408 589}
bf807d4d
LP
590
591unsigned mmap_cache_get_hit(MMapCache *m) {
592 assert(m);
593
594 return m->n_hit;
595}
596
597unsigned mmap_cache_get_missed(MMapCache *m) {
598 assert(m);
599
600 return m->n_missed;
601}