mmap-cache.c revision 8e6d9397b550f5617fc9231e3a275348cda23c89
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering This file is part of systemd.
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering Copyright 2012 Lennart Poettering
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering systemd is free software; you can redistribute it and/or modify it
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering under the terms of the GNU Lesser General Public License as published by
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering the Free Software Foundation; either version 2.1 of the License, or
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering (at your option) any later version.
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering systemd is distributed in the hope that it will be useful, but
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering Lesser General Public License for more details.
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering You should have received a copy of the GNU Lesser General Public License
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
d4205751d4643c272059a3728045929dd0e5e800Lennart Poetteringtypedef struct FileDescriptor FileDescriptor;
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering#define WINDOW_SIZE (8ULL*1024ULL*1024ULL)
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering LIST_REMOVE(Window, by_fd, w->fd->windows, w);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering LIST_REMOVE(Window, unused, w->cache->unused, w);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering LIST_FOREACH(by_window, c, w->contexts) {
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering_pure_ static bool window_matches(Window *w, int fd, int prot, uint64_t offset, size_t size) {
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering if (!m->last_unused || m->n_windows <= WINDOWS_MIN) {
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering /* Allocate a new window */
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering /* Reuse an existing one */
d4205751d4643c272059a3728045929dd0e5e800Lennart Poetteringstatic void context_detach_window(Context *c) {
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering LIST_REMOVE(Context, by_window, w->contexts, c);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering /* Not used anymore? */
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering LIST_PREPEND(Window, unused, c->cache->unused, w);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poetteringstatic void context_attach_window(Context *c, Window *w) {
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering /* Used again? */
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering LIST_REMOVE(Window, unused, c->cache->unused, w);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering LIST_PREPEND(Context, by_window, w->contexts, c);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poetteringstatic Context *context_add(MMapCache *m, unsigned id) {
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering c = hashmap_get(m->contexts, UINT_TO_PTR(id + 1));
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering r = hashmap_ensure_allocated(&m->contexts, trivial_hash_func, trivial_compare_func);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering r = hashmap_put(m->contexts, UINT_TO_PTR(id + 1), c);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering assert_se(hashmap_remove(c->cache->contexts, UINT_TO_PTR(c->id + 1)));
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering assert_se(hashmap_remove(f->cache->fds, INT_TO_PTR(f->fd + 1)));
d4205751d4643c272059a3728045929dd0e5e800Lennart Poetteringstatic FileDescriptor* fd_add(MMapCache *m, int fd) {
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering f = hashmap_get(m->fds, INT_TO_PTR(fd + 1));
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering r = hashmap_ensure_allocated(&m->fds, trivial_hash_func, trivial_compare_func);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering r = hashmap_put(m->fds, UINT_TO_PTR(fd + 1), f);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poetteringstatic void mmap_cache_free(MMapCache *m) {
d4205751d4643c272059a3728045929dd0e5e800Lennart PoetteringMMapCache* mmap_cache_unref(MMapCache *m) {
80343dc19a9bcd703275ad2ad88f43e5310559d6Zbigniew Jędrzejewski-Szmek c = hashmap_get(m->contexts, UINT_TO_PTR(context+1));
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering if (!window_matches(c->window, fd, prot, offset, size)) {
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering /* Drop the reference to the window, since it's unnecessary now */
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering c->window->keep_always = c->window->keep_always || keep_always;
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering *ret = (uint8_t*) c->window->ptr + (offset - c->window->offset);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering f = hashmap_get(m->fds, INT_TO_PTR(fd + 1));
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering if (window_matches(w, fd, prot, offset, size))
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering w->keep_always = w->keep_always || keep_always;
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering *ret = (uint8_t*) w->ptr + (offset - w->offset);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering woffset = offset & ~((uint64_t) page_size() - 1ULL);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering delta = PAGE_ALIGN((WINDOW_SIZE - wsize) / 2);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering /* Memory maps that are larger then the files
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering underneath have undefined behavior. Hence, clamp
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering things to the file size if we know it */
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering if (woffset + wsize > (uint64_t) st->st_size)
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering wsize = PAGE_ALIGN(st->st_size - woffset);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering d = mmap(NULL, wsize, prot, MAP_SHARED, fd, woffset);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering LIST_PREPEND(Window, by_fd, f->windows, w);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering LIST_PREPEND(Context, by_window, w->contexts, c);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering *ret = (uint8_t*) w->ptr + (offset - w->offset);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering /* Check whether the current context is the right one already */
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering r = try_context(m, fd, prot, context, keep_always, offset, size, ret);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering /* Search for a matching mmap */
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering r = find_mmap(m, fd, prot, context, keep_always, offset, size, ret);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering /* Create a new mmap */
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering return add_mmap(m, fd, prot, context, keep_always, offset, size, st, ret);
d4205751d4643c272059a3728045929dd0e5e800Lennart Poetteringvoid mmap_cache_close_fd(MMapCache *m, int fd) {
d4205751d4643c272059a3728045929dd0e5e800Lennart Poettering f = hashmap_get(m->fds, INT_TO_PTR(fd + 1));
d4205751d4643c272059a3728045929dd0e5e800Lennart Poetteringvoid mmap_cache_close_context(MMapCache *m, unsigned context) {