]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal/mmap-cache.c
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
) {
235 memset(m
->by_context
, -1, m
->contexts_max
* sizeof(unsigned));
237 m
->by_fd
= new(FileDescriptor
, m
->fds_max
);
246 MMapCache
* mmap_cache_ref(MMapCache
*m
) {
248 assert(m
->n_ref
> 0);
254 MMapCache
* mmap_cache_unref(MMapCache
*m
) {
256 assert(m
->n_ref
> 0);
266 static int mmap_cache_allocate_window(MMapCache
*m
, unsigned *w
) {
270 if (m
->n_windows
< m
->windows_max
) {
271 *w
= m
->n_windows
++;
275 if (m
->lru_first
== (unsigned) -1)
279 mmap_cache_window_unmap(m
, *w
);
280 mmap_cache_window_remove_lru(m
, *w
);
285 static int mmap_cache_make_room(MMapCache
*m
) {
291 while (w
!= (unsigned) -1) {
297 mmap_cache_window_unmap(m
, w
);
307 static int mmap_cache_put(
320 uint64_t woffset
, wsize
;
325 assert(context
< m
->contexts_max
);
329 woffset
= offset
& ~((uint64_t) page_size() - 1ULL);
330 wsize
= size
+ (offset
- woffset
);
331 wsize
= PAGE_ALIGN(wsize
);
333 if (wsize
< WINDOW_SIZE
) {
336 delta
= PAGE_ALIGN((WINDOW_SIZE
- wsize
) / 2);
347 d
= mmap(NULL
, wsize
, prot
, MAP_SHARED
, fd
, woffset
);
353 r
= mmap_cache_make_room(m
);
360 r
= mmap_cache_allocate_window(m
, &w
);
373 v
->lru_prev
= v
->lru_next
= (unsigned) -1;
375 mmap_cache_fd_add(m
, fd_index
, w
);
376 mmap_cache_context_set(m
, context
, w
);
378 *ret
= (uint8_t*) d
+ (offset
- woffset
);
382 static int fd_cmp(const void *_a
, const void *_b
) {
383 const FileDescriptor
*a
= _a
, *b
= _b
;
393 static int mmap_cache_get_fd_index(MMapCache
*m
, int fd
, unsigned *fd_index
) {
400 j
= bsearch(&fd
, m
->by_fd
, m
->n_fds
, sizeof(m
->by_fd
[0]), fd_cmp
);
402 if (m
->n_fds
>= m
->fds_max
)
405 j
= m
->by_fd
+ m
->n_fds
++;
407 j
->windows
= (unsigned) -1;
409 qsort(m
->by_fd
, m
->n_fds
, sizeof(m
->by_fd
[0]), fd_cmp
);
410 j
= bsearch(&fd
, m
->by_fd
, m
->n_fds
, sizeof(m
->by_fd
[0]), fd_cmp
);
413 *fd_index
= (unsigned) (j
- m
->by_fd
);
417 static bool mmap_cache_test_window(
425 assert(w
< m
->n_windows
);
430 return offset
>= v
->offset
&&
431 offset
+ size
<= v
->offset
+ v
->size
;
434 static int mmap_cache_current(
447 assert(context
< m
->contexts_max
);
451 if (m
->by_context
[context
] == (unsigned) -1)
454 w
= m
->by_context
[context
];
460 if (!mmap_cache_test_window(m
, w
, offset
, size
))
463 *ret
= (uint8_t*) v
->ptr
+ (offset
- v
->offset
);
467 static int mmap_cache_find(
479 assert(fd_index
< m
->n_fds
);
480 assert(context
< m
->contexts_max
);
484 w
= m
->by_fd
[fd_index
].windows
;
485 while (w
!= (unsigned) -1) {
486 if (mmap_cache_test_window(m
, w
, offset
, size
))
489 w
= m
->windows
[w
].by_fd_next
;
492 if (w
== (unsigned) -1)
495 mmap_cache_context_set(m
, context
, w
);
498 *ret
= (uint8_t*) v
->ptr
+ (offset
- v
->offset
);
516 assert(context
< m
->contexts_max
);
520 /* Maybe the current pointer for this context is already the
522 r
= mmap_cache_current(m
, fd
, context
, offset
, size
, ret
);
526 /* OK, let's find the chain for this FD */
527 r
= mmap_cache_get_fd_index(m
, fd
, &fd_index
);
531 /* And let's look through the available mmaps */
532 r
= mmap_cache_find(m
, fd_index
, context
, offset
, size
, ret
);
536 /* Not found? Then, let's add it */
537 return mmap_cache_put(m
, fd
, fd_index
, prot
, context
, offset
, size
, ret
);
540 void mmap_cache_close_fd(MMapCache
*m
, int fd
) {
542 unsigned fd_index
, c
, w
;
547 j
= bsearch(&fd
, m
->by_fd
, m
->n_fds
, sizeof(m
->by_fd
[0]), fd_cmp
);
550 fd_index
= (unsigned) (j
- m
->by_fd
);
552 for (c
= 0; c
< m
->contexts_max
; c
++) {
553 w
= m
->by_context
[c
];
554 if (w
== (unsigned) -1)
557 if (m
->windows
[w
].fd
== fd
)
558 mmap_cache_context_unset(m
, c
);
561 w
= m
->by_fd
[fd_index
].windows
;
562 while (w
!= (unsigned) -1) {
564 mmap_cache_fd_remove(m
, fd_index
, w
);
565 mmap_cache_window_unmap(m
, w
);
567 w
= m
->by_fd
[fd_index
].windows
;
570 memmove(m
->by_fd
+ fd_index
, m
->by_fd
+ fd_index
+ 1, (m
->n_fds
- (fd_index
+ 1)) * sizeof(FileDescriptor
));
574 void mmap_cache_close_context(MMapCache
*m
, unsigned context
) {
575 mmap_cache_context_unset(m
, context
);