~ubuntu-branches/ubuntu/trusty/systemd/trusty

« back to all changes in this revision

Viewing changes to src/journal/mmap-cache.c

  • Committer: Package Import Robot
  • Author(s): Michael Biebl, Michael Biebl, Michael Stapelberg, Daniel Schaal, Ondrej Balaz
  • Date: 2013-09-12 00:13:11 UTC
  • mfrom: (1.1.11) (9.1.2 experimental)
  • mto: This revision was merged to the branch mainline in revision 53.
  • Revision ID: package-import@ubuntu.com-20130912001311-dz35it34wr2lbday
Tags: 204-3
[ Michael Biebl ]
* Upload to unstable.
* Use /bin/bash in debug-shell.service as Debian doesn't have /sbin/sushell.
* Only import net.ifaces cmdline property for network devices.
* Generate strict dependencies between the binary packages using a
  shlibs.local file and add an explicit versioned dependency on
  libsystemd-login0 to systemd to ensure packages are upgraded in sync.
  Closes: #719444
* Drop obsolete Replaces: libudev0 from udev package.
* Use correct paths for various binaries, like /sbin/quotaon, which are
  installed in / and not /usr in Debian.  Closes: #721347
* Don't install kernel-install(8) man page since we don't install the
  corresponding binary either.  Closes: #722180
* Cherry-pick upstream fixes to make switching runlevels and starting
  reboot via ctrl-alt-del more robust.
* Cherry-pick upstream fix to properly apply ACLs to Journal files.

[ Michael Stapelberg ]
* Make systemctl enable|disable call update-rc.d for SysV init scripts.
  Closes: #709780
* Don't mount /tmp as tmpfs by default and make it possible to enable this
  feature via "systemctl enable tmp.mount".

[ Daniel Schaal ]
* Add bug-script to systemd and udev.  Closes: #711245

[ Ondrej Balaz ]
* Recognize discard option in /etc/crypttab.

Show diffs side-by-side

added added

removed removed

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