]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/mmap-cache.c
NEWS: Mention the new Personality= switch in unit files
[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
ae97089d 41 unsigned 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
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
ae97089d 188 if (!w->contexts && w->keep_always == 0) {
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);
16e9f408 363
f8019684
LP
364 c = hashmap_get(m->contexts, UINT_TO_PTR(context+1));
365 if (!c)
16e9f408 366 return 0;
16e9f408 367
f8019684 368 assert(c->id == context);
16e9f408 369
f8019684
LP
370 if (!c->window)
371 return 0;
f65425cb 372
f8019684 373 if (!window_matches(c->window, fd, prot, offset, size)) {
f65425cb 374
f8019684
LP
375 /* Drop the reference to the window, since it's unnecessary now */
376 context_detach_window(c);
377 return 0;
f65425cb
LP
378 }
379
ae97089d 380 c->window->keep_always += keep_always;
16e9f408 381
ae97089d
ZJS
382 if (ret)
383 *ret = (uint8_t*) c->window->ptr + (offset - c->window->offset);
f8019684 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);
16e9f408 405
f8019684
LP
406 f = hashmap_get(m->fds, INT_TO_PTR(fd + 1));
407 if (!f)
408 return 0;
16e9f408 409
f8019684 410 assert(f->fd == fd);
16e9f408 411
f8019684
LP
412 LIST_FOREACH(by_fd, w, f->windows)
413 if (window_matches(w, fd, prot, offset, size))
414 break;
16e9f408 415
f8019684
LP
416 if (!w)
417 return 0;
418
419 c = context_add(m, context);
420 if (!c)
421 return -ENOMEM;
422
423 context_attach_window(c, w);
ae97089d 424 w->keep_always += keep_always;
16e9f408 425
ae97089d
ZJS
426 if (ret)
427 *ret = (uint8_t*) w->ptr + (offset - w->offset);
f8019684 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 452 assert(size > 0);
16e9f408
LP
453
454 woffset = offset & ~((uint64_t) page_size() - 1ULL);
455 wsize = size + (offset - woffset);
456 wsize = PAGE_ALIGN(wsize);
457
458 if (wsize < WINDOW_SIZE) {
459 uint64_t delta;
460
beec0085 461 delta = PAGE_ALIGN((WINDOW_SIZE - wsize) / 2);
16e9f408
LP
462
463 if (delta > offset)
464 woffset = 0;
465 else
466 woffset -= delta;
467
468 wsize = WINDOW_SIZE;
469 }
470
fcde2389
LP
471 if (st) {
472 /* Memory maps that are larger then the files
c5315881 473 underneath have undefined behavior. Hence, clamp
fcde2389
LP
474 things to the file size if we know it */
475
476 if (woffset >= (uint64_t) st->st_size)
477 return -EADDRNOTAVAIL;
478
479 if (woffset + wsize > (uint64_t) st->st_size)
480 wsize = PAGE_ALIGN(st->st_size - woffset);
481 }
482
16e9f408
LP
483 for (;;) {
484 d = mmap(NULL, wsize, prot, MAP_SHARED, fd, woffset);
485 if (d != MAP_FAILED)
486 break;
487 if (errno != ENOMEM)
488 return -errno;
489
f8019684 490 r = make_room(m);
16e9f408
LP
491 if (r < 0)
492 return r;
493 if (r == 0)
494 return -ENOMEM;
495 }
496
f8019684
LP
497 c = context_add(m, context);
498 if (!c)
499 return -ENOMEM;
16e9f408 500
f8019684
LP
501 f = fd_add(m, fd);
502 if (!f)
503 return -ENOMEM;
16e9f408 504
f8019684
LP
505 w = window_add(m);
506 if (!w)
507 return -ENOMEM;
16e9f408 508
f8019684
LP
509 w->keep_always = keep_always;
510 w->ptr = d;
511 w->offset = woffset;
512 w->prot = prot;
513 w->size = wsize;
514 w->fd = f;
16e9f408 515
71fda00f 516 LIST_PREPEND(by_fd, f->windows, w);
16e9f408 517
f8019684
LP
518 context_detach_window(c);
519 c->window = w;
71fda00f 520 LIST_PREPEND(by_window, w->contexts, c);
16e9f408 521
ae97089d
ZJS
522 if (ret)
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 543 assert(size > 0);
16e9f408 544
f8019684
LP
545 /* Check whether the current context is the right one already */
546 r = try_context(m, fd, prot, context, keep_always, offset, size, ret);
bf807d4d
LP
547 if (r != 0) {
548 m->n_hit ++;
16e9f408 549 return r;
bf807d4d 550 }
16e9f408 551
f8019684
LP
552 /* Search for a matching mmap */
553 r = find_mmap(m, fd, prot, context, keep_always, offset, size, ret);
bf807d4d
LP
554 if (r != 0) {
555 m->n_hit ++;
16e9f408 556 return r;
bf807d4d
LP
557 }
558
559 m->n_missed++;
16e9f408 560
f8019684
LP
561 /* Create a new mmap */
562 return add_mmap(m, fd, prot, context, keep_always, offset, size, st, ret);
16e9f408
LP
563}
564
ae97089d
ZJS
565int mmap_cache_release(
566 MMapCache *m,
567 int fd,
568 int prot,
569 unsigned context,
570 uint64_t offset,
571 size_t size) {
572
573 FileDescriptor *f;
574 Window *w;
575
576 assert(m);
577 assert(m->n_ref > 0);
578 assert(fd >= 0);
579 assert(size > 0);
580
581 f = hashmap_get(m->fds, INT_TO_PTR(fd + 1));
582 if (!f)
583 return -EBADF;
584
585 assert(f->fd == fd);
586
587 LIST_FOREACH(by_fd, w, f->windows)
588 if (window_matches(w, fd, prot, offset, size))
589 break;
590
591 if (!w)
592 return -ENOENT;
593
594 if (w->keep_always == 0)
595 return -ENOLCK;
596
597 w->keep_always -= 1;
598 return 0;
599}
600
16e9f408 601void mmap_cache_close_fd(MMapCache *m, int fd) {
f8019684 602 FileDescriptor *f;
16e9f408
LP
603
604 assert(m);
f8019684 605 assert(fd >= 0);
16e9f408 606
f8019684
LP
607 f = hashmap_get(m->fds, INT_TO_PTR(fd + 1));
608 if (!f)
16e9f408 609 return;
16e9f408 610
f8019684
LP
611 fd_free(f);
612}
16e9f408 613
f8019684
LP
614void mmap_cache_close_context(MMapCache *m, unsigned context) {
615 Context *c;
16e9f408 616
f8019684 617 assert(m);
16e9f408 618
f8019684
LP
619 c = hashmap_get(m->contexts, UINT_TO_PTR(context + 1));
620 if (!c)
621 return;
16e9f408 622
f8019684 623 context_free(c);
16e9f408 624}
bf807d4d
LP
625
626unsigned mmap_cache_get_hit(MMapCache *m) {
627 assert(m);
628
629 return m->n_hit;
630}
631
632unsigned mmap_cache_get_missed(MMapCache *m) {
633 assert(m);
634
635 return m->n_missed;
636}