1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2012 Lennart Poettering
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.
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.
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/>.
30 #include "mmap-cache.h"
32 #define WINDOW_SIZE (8ULL*1024ULL*1024ULL)
33 #define WINDOWS_MAX 32
35 typedef struct Window {
49 typedef struct FileDescriptor {
57 unsigned contexts_max;
64 unsigned lru_first, lru_last;
68 FileDescriptor *by_fd;
71 static void mmap_cache_window_unmap(MMapCache *m, unsigned w) {
75 assert(w < m->n_windows);
81 munmap(v->ptr, v->size);
85 static void mmap_cache_window_add_lru(MMapCache *m, unsigned w) {
89 assert(w < m->n_windows);
92 v->lru_prev = m->lru_last;
93 v->lru_next = (unsigned) -1;
96 if (m->lru_first == (unsigned) -1)
100 static void mmap_cache_window_remove_lru(MMapCache *m, unsigned w) {
104 assert(w < m->n_windows);
108 if (v->lru_prev == (unsigned) -1)
109 m->lru_first = v->lru_next;
111 m->windows[v->lru_prev].lru_next = v->lru_next;
113 if (v->lru_next == (unsigned) -1)
114 m->lru_last = v->lru_prev;
116 m->windows[v->lru_next].lru_prev = v->lru_prev;
119 static void mmap_cache_fd_add(MMapCache *m, unsigned fd_index, unsigned w) {
123 assert(fd_index < m->n_fds);
126 v->by_fd_next = m->by_fd[fd_index].windows;
127 v->by_fd_prev = (unsigned) -1;
129 m->by_fd[fd_index].windows = w;
132 static void mmap_cache_fd_remove(MMapCache *m, unsigned fd_index, unsigned w) {
136 assert(fd_index < m->n_fds);
139 if (v->by_fd_prev == (unsigned) -1)
140 m->by_fd[fd_index].windows = v->by_fd_next;
142 m->windows[v->by_fd_prev].by_fd_next = v->by_fd_next;
144 if (v->by_fd_next != (unsigned) -1)
145 m->windows[v->by_fd_next].by_fd_prev = v->by_fd_prev;
148 static void mmap_cache_context_unset(MMapCache *m, unsigned c) {
153 assert(c < m->contexts_max);
155 if (m->by_context[c] == (unsigned) -1)
158 w = m->by_context[c];
159 m->by_context[c] = (unsigned) -1;
162 assert(v->n_ref > 0);
166 mmap_cache_window_add_lru(m, w);
169 static void mmap_cache_context_set(MMapCache *m, unsigned c, unsigned w) {
173 assert(c < m->contexts_max);
174 assert(w < m->n_windows);
176 if (m->by_context[c] == w)
179 mmap_cache_context_unset(m, c);
181 m->by_context[c] = w;
186 mmap_cache_window_remove_lru(m, w);
189 static void mmap_cache_free(MMapCache *m) {
196 for (w = 0; w < m->n_windows; w++)
197 mmap_cache_window_unmap(m, w);
207 MMapCache* mmap_cache_new(unsigned contexts_max, unsigned fds_max) {
210 assert(contexts_max > 0);
213 m = new0(MMapCache, 1);
217 m->contexts_max = contexts_max;
218 m->fds_max = fds_max;
219 m->windows_max = MAX(m->contexts_max, WINDOWS_MAX);
221 m->lru_first = (unsigned) -1;
222 m->lru_last = (unsigned) -1;
224 m->windows = new(Window, m->windows_max);
230 m->by_context = new(unsigned, m->contexts_max);
231 if (!m->by_context) {
236 memset(m->by_context, -1, m->contexts_max * sizeof(unsigned));
238 m->by_fd = new(FileDescriptor, m->fds_max);
247 MMapCache* mmap_cache_ref(MMapCache *m) {
249 assert(m->n_ref > 0);
255 MMapCache* mmap_cache_unref(MMapCache *m) {
257 assert(m->n_ref > 0);
267 static int mmap_cache_allocate_window(MMapCache *m, unsigned *w) {
271 if (m->n_windows < m->windows_max) {
272 *w = m->n_windows ++;
276 if (m->lru_first == (unsigned) -1)
280 mmap_cache_window_unmap(m, *w);
281 mmap_cache_window_remove_lru(m, *w);
286 static int mmap_cache_make_room(MMapCache *m) {
292 while (w != (unsigned) -1) {
298 mmap_cache_window_unmap(m, w);
308 static int mmap_cache_put(
321 uint64_t woffset, wsize;
326 assert(context < m->contexts_max);
330 woffset = offset & ~((uint64_t) page_size() - 1ULL);
331 wsize = size + (offset - woffset);
332 wsize = PAGE_ALIGN(wsize);
334 if (wsize < WINDOW_SIZE) {
337 delta = (WINDOW_SIZE - wsize) / 2;
348 d = mmap(NULL, wsize, prot, MAP_SHARED, fd, woffset);
354 r = mmap_cache_make_room(m);
361 r = mmap_cache_allocate_window(m, &w);
374 v->lru_prev = v->lru_next = (unsigned) -1;
376 mmap_cache_fd_add(m, fd_index, w);
377 mmap_cache_context_set(m, context, w);
379 *ret = (uint8_t*) d + (offset - woffset);
383 static int fd_cmp(const void *_a, const void *_b) {
384 const FileDescriptor *a = _a, *b = _b;
394 static int mmap_cache_get_fd_index(MMapCache *m, int fd, unsigned *fd_index) {
401 j = bsearch(&fd, m->by_fd, m->n_fds, sizeof(m->by_fd[0]), fd_cmp);
403 if (m->n_fds >= m->fds_max)
406 j = m->by_fd + m->n_fds ++;
408 j->windows = (unsigned) -1;
410 qsort(m->by_fd, m->n_fds, sizeof(m->by_fd[0]), fd_cmp);
411 j = bsearch(&fd, m->by_fd, m->n_fds, sizeof(m->by_fd[0]), fd_cmp);
414 *fd_index = (unsigned) (j - m->by_fd);
418 static bool mmap_cache_test_window(
426 assert(w < m->n_windows);
431 return offset >= v->offset &&
432 offset + size <= v->offset + v->size;
435 static int mmap_cache_current(
448 assert(context < m->contexts_max);
452 if (m->by_context[context] == (unsigned) -1)
455 w = m->by_context[context];
461 if (!mmap_cache_test_window(m, w, offset, size))
464 *ret = (uint8_t*) v->ptr + (offset - v->offset);
468 static int mmap_cache_find(
480 assert(fd_index < m->n_fds);
481 assert(context < m->contexts_max);
485 w = m->by_fd[fd_index].windows;
486 while (w != (unsigned) -1) {
487 if (mmap_cache_test_window(m, w, offset, size))
490 w = m->windows[w].by_fd_next;
493 if (w == (unsigned) -1)
496 mmap_cache_context_set(m, context, w);
499 *ret = (uint8_t*) v->ptr + (offset - v->offset);
517 assert(context < m->contexts_max);
521 /* Maybe the current pointer for this context is already the
523 r = mmap_cache_current(m, fd, context, offset, size, ret);
527 /* OK, let's find the chain for this FD */
528 r = mmap_cache_get_fd_index(m, fd, &fd_index);
532 /* And let's look through the available mmaps */
533 r = mmap_cache_find(m, fd_index, context, offset, size, ret);
537 /* Not found? Then, let's add it */
538 return mmap_cache_put(m, fd, fd_index, prot, context, offset, size, ret);
541 void mmap_cache_close_fd(MMapCache *m, int fd) {
543 unsigned fd_index, c, w;
548 j = bsearch(&fd, m->by_fd, m->n_fds, sizeof(m->by_fd[0]), fd_cmp);
551 fd_index = (unsigned) (j - m->by_fd);
553 for (c = 0; c < m->contexts_max; c++) {
554 w = m->by_context[c];
555 if (w == (unsigned) -1)
558 if (m->windows[w].fd == fd)
559 mmap_cache_context_unset(m, c);
562 w = m->by_fd[fd_index].windows;
563 while (w != (unsigned) -1) {
565 mmap_cache_fd_remove(m, fd_index, w);
566 mmap_cache_window_unmap(m, w);
568 w = m->by_fd[fd_index].windows;
571 memmove(m->by_fd + fd_index, m->by_fd + fd_index + 1, (m->n_fds - (fd_index + 1)) * sizeof(FileDescriptor));
575 void mmap_cache_close_context(MMapCache *m, unsigned context) {
576 mmap_cache_context_unset(m, context);