mmap-cache.c revision 739731cdace09ff179fdd75ae0714da0d81e384d
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen/***
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen This file is part of systemd.
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen Copyright 2012 Lennart Poettering
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen systemd is free software; you can redistribute it and/or modify it
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen under the terms of the GNU Lesser General Public License as published by
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen the Free Software Foundation; either version 2.1 of the License, or
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen (at your option) any later version.
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen systemd is distributed in the hope that it will be useful, but
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen WITHOUT ANY WARRANTY; without even the implied warranty of
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen Lesser General Public License for more details.
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen You should have received a copy of the GNU Lesser General Public License
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen along with systemd; If not, see <http://www.gnu.org/licenses/>.
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen***/
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen#include <errno.h>
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen#include <stdlib.h>
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen#include <sys/mman.h>
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen#include "alloc-util.h"
07630cea1f3a845c09309f197ac7c4f11edd3b62Lennart Poettering#include "fd-util.h"
07630cea1f3a845c09309f197ac7c4f11edd3b62Lennart Poettering#include "hashmap.h"
07630cea1f3a845c09309f197ac7c4f11edd3b62Lennart Poettering#include "list.h"
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen#include "log.h"
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen#include "macro.h"
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen#include "mmap-cache.h"
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen#include "sigbus.h"
07630cea1f3a845c09309f197ac7c4f11edd3b62Lennart Poettering#include "util.h"
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersentypedef struct Window Window;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersentypedef struct Context Context;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersentypedef struct FileDescriptor FileDescriptor;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersenstruct Window {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen MMapCache *cache;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen bool invalidated:1;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen bool keep_always:1;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen bool in_unused:1;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen int prot;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen void *ptr;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen uint64_t offset;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen size_t size;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen FileDescriptor *fd;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen LIST_FIELDS(Window, by_fd);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen LIST_FIELDS(Window, unused);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen LIST_HEAD(Context, contexts);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen};
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersenstruct Context {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen MMapCache *cache;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen unsigned id;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen Window *window;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen LIST_FIELDS(Context, by_window);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen};
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersenstruct FileDescriptor {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen MMapCache *cache;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen int fd;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen bool sigbus;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen LIST_HEAD(Window, windows);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen};
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersenstruct MMapCache {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen int n_ref;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen unsigned n_windows;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen unsigned n_hit, n_missed;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen Hashmap *fds;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen Context *contexts[MMAP_CACHE_MAX_CONTEXTS];
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen LIST_HEAD(Window, unused);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen Window *last_unused;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen};
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen#define WINDOWS_MIN 64
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen#ifdef ENABLE_DEBUG_MMAP_CACHE
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen/* Tiny windows increase mmap activity and the chance of exposing unsafe use. */
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen# define WINDOW_SIZE (page_size())
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen#else
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen# define WINDOW_SIZE (8ULL*1024ULL*1024ULL)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen#endif
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom GundersenMMapCache* mmap_cache_new(void) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen MMapCache *m;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen m = new0(MMapCache, 1);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (!m)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen return NULL;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen m->n_ref = 1;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen return m;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen}
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom GundersenMMapCache* mmap_cache_ref(MMapCache *m) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert(m);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert(m->n_ref > 0);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen m->n_ref ++;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen return m;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen}
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersenstatic void window_unlink(Window *w) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen Context *c;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert(w);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (w->ptr)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen munmap(w->ptr, w->size);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (w->fd)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen LIST_REMOVE(by_fd, w->fd->windows, w);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (w->in_unused) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (w->cache->last_unused == w)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen w->cache->last_unused = w->unused_prev;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen LIST_REMOVE(unused, w->cache->unused, w);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen }
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen LIST_FOREACH(by_window, c, w->contexts) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert(c->window == w);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen c->window = NULL;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen }
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen}
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersenstatic void window_invalidate(Window *w) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert(w);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (w->invalidated)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen return;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen /* Replace the window with anonymous pages. This is useful
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen * when we hit a SIGBUS and want to make sure the file cannot
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen * trigger any further SIGBUS, possibly overrunning the sigbus
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen * queue. */
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert_se(mmap(w->ptr, w->size, w->prot, MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, -1, 0) == w->ptr);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen w->invalidated = true;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen}
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersenstatic void window_free(Window *w) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert(w);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen window_unlink(w);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen w->cache->n_windows--;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen free(w);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen}
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen_pure_ static bool window_matches(Window *w, int fd, int prot, uint64_t offset, size_t size) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert(w);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert(fd >= 0);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert(size > 0);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen return
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen w->fd &&
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen fd == w->fd->fd &&
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen prot == w->prot &&
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen offset >= w->offset &&
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen offset + size <= w->offset + w->size;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen}
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersenstatic Window *window_add(MMapCache *m) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen Window *w;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert(m);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (!m->last_unused || m->n_windows <= WINDOWS_MIN) {
2237aa02f3e2739a1ebe9c0bc224b5125f5eb292David Herrmann
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen /* Allocate a new window */
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen w = new0(Window, 1);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (!w)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen return NULL;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen m->n_windows++;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen } else {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen /* Reuse an existing one */
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen w = m->last_unused;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen window_unlink(w);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen zero(*w);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen }
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen w->cache = m;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen return w;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen}
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersenstatic void context_detach_window(Context *c) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen Window *w;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert(c);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (!c->window)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen return;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen w = c->window;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen c->window = NULL;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen LIST_REMOVE(by_window, w->contexts, c);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (!w->contexts && !w->keep_always) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen /* Not used anymore? */
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen#ifdef ENABLE_DEBUG_MMAP_CACHE
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen /* Unmap unused windows immediately to expose use-after-unmap
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen * by SIGSEGV. */
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen window_free(w);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen#else
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen LIST_PREPEND(unused, c->cache->unused, w);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (!c->cache->last_unused)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen c->cache->last_unused = w;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen w->in_unused = true;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen#endif
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen }
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen}
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersenstatic void context_attach_window(Context *c, Window *w) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert(c);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert(w);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (c->window == w)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen return;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen context_detach_window(c);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (w->in_unused) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen /* Used again? */
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen LIST_REMOVE(unused, c->cache->unused, w);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (c->cache->last_unused == w)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen c->cache->last_unused = w->unused_prev;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen w->in_unused = false;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen }
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen c->window = w;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen LIST_PREPEND(by_window, w->contexts, c);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen}
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersenstatic Context *context_add(MMapCache *m, unsigned id) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen Context *c;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert(m);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen c = m->contexts[id];
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (c)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen return c;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen c = new0(Context, 1);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (!c)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen return NULL;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen c->cache = m;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen c->id = id;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert(!m->contexts[id]);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen m->contexts[id] = c;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen return c;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen}
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersenstatic void context_free(Context *c) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert(c);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen context_detach_window(c);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (c->cache) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert(c->cache->contexts[c->id] == c);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen c->cache->contexts[c->id] = NULL;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen }
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen free(c);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen}
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersenstatic void fd_free(FileDescriptor *f) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert(f);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen while (f->windows)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen window_free(f->windows);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (f->cache)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert_se(hashmap_remove(f->cache->fds, FD_TO_PTR(f->fd)));
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen free(f);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen}
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersenstatic FileDescriptor* fd_add(MMapCache *m, int fd) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen FileDescriptor *f;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen int r;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert(m);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert(fd >= 0);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen f = hashmap_get(m->fds, FD_TO_PTR(fd));
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (f)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen return f;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen r = hashmap_ensure_allocated(&m->fds, NULL);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (r < 0)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen return NULL;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen f = new0(FileDescriptor, 1);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (!f)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen return NULL;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen f->cache = m;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen f->fd = fd;
2237aa02f3e2739a1ebe9c0bc224b5125f5eb292David Herrmann
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen r = hashmap_put(m->fds, FD_TO_PTR(fd), f);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (r < 0) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen free(f);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen return NULL;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen }
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen return f;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen}
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersenstatic void mmap_cache_free(MMapCache *m) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen FileDescriptor *f;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen int i;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert(m);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen for (i = 0; i < MMAP_CACHE_MAX_CONTEXTS; i++)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (m->contexts[i])
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen context_free(m->contexts[i]);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen while ((f = hashmap_first(m->fds)))
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen fd_free(f);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen hashmap_free(m->fds);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen while (m->unused)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen window_free(m->unused);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen free(m);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen}
2237aa02f3e2739a1ebe9c0bc224b5125f5eb292David Herrmann
e3dca0089b7b50e2ec21409d1292727921d06102Tom GundersenMMapCache* mmap_cache_unref(MMapCache *m) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (!m)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen return NULL;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert(m->n_ref > 0);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen m->n_ref --;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (m->n_ref == 0)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen mmap_cache_free(m);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen return NULL;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen}
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersenstatic int make_room(MMapCache *m) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert(m);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (!m->last_unused)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen return 0;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen window_free(m->last_unused);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen return 1;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen}
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersenstatic int try_context(
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen MMapCache *m,
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen int fd,
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen int prot,
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen unsigned context,
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen bool keep_always,
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen uint64_t offset,
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen size_t size,
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen void **ret) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen Context *c;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert(m);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert(m->n_ref > 0);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert(fd >= 0);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert(size > 0);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert(ret);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen c = m->contexts[context];
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (!c)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen return 0;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert(c->id == context);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (!c->window)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen return 0;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (!window_matches(c->window, fd, prot, offset, size)) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen /* Drop the reference to the window, since it's unnecessary now */
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen context_detach_window(c);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen return 0;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen }
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (c->window->fd->sigbus)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen return -EIO;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen c->window->keep_always = c->window->keep_always || keep_always;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen *ret = (uint8_t*) c->window->ptr + (offset - c->window->offset);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen return 1;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen}
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersenstatic int find_mmap(
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen MMapCache *m,
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen int fd,
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen int prot,
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen unsigned context,
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen bool keep_always,
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen uint64_t offset,
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen size_t size,
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen void **ret) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen FileDescriptor *f;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen Window *w;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen Context *c;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert(m);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert(m->n_ref > 0);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert(fd >= 0);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert(size > 0);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen f = hashmap_get(m->fds, FD_TO_PTR(fd));
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (!f)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen return 0;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert(f->fd == fd);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (f->sigbus)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen return -EIO;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen LIST_FOREACH(by_fd, w, f->windows)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (window_matches(w, fd, prot, offset, size))
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen break;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (!w)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen return 0;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen c = context_add(m, context);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (!c)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen return -ENOMEM;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen context_attach_window(c, w);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen w->keep_always = w->keep_always || keep_always;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen *ret = (uint8_t*) w->ptr + (offset - w->offset);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen return 1;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen}
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersenstatic int add_mmap(
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen MMapCache *m,
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen int fd,
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen int prot,
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen unsigned context,
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen bool keep_always,
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen uint64_t offset,
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen size_t size,
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen struct stat *st,
04c0136989b7eb896bfb0fb176e11233d69e1453Lennart Poettering void **ret) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen uint64_t woffset, wsize;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen Context *c;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen FileDescriptor *f;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen Window *w;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen void *d;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen int r;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert(m);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert(m->n_ref > 0);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert(fd >= 0);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert(size > 0);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen assert(ret);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen woffset = offset & ~((uint64_t) page_size() - 1ULL);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen wsize = size + (offset - woffset);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen wsize = PAGE_ALIGN(wsize);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (wsize < WINDOW_SIZE) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen uint64_t delta;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen delta = PAGE_ALIGN((WINDOW_SIZE - wsize) / 2);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (delta > offset)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen woffset = 0;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen else
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen woffset -= delta;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen wsize = WINDOW_SIZE;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen }
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (st) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen /* Memory maps that are larger then the files
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen underneath have undefined behavior. Hence, clamp
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen things to the file size if we know it */
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (woffset >= (uint64_t) st->st_size)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen return -EADDRNOTAVAIL;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (woffset + wsize > (uint64_t) st->st_size)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen wsize = PAGE_ALIGN(st->st_size - woffset);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen }
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen for (;;) {
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen d = mmap(NULL, wsize, prot, MAP_SHARED, fd, woffset);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (d != MAP_FAILED)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen break;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (errno != ENOMEM)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen return -errno;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen r = make_room(m);
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (r < 0)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen return r;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen if (r == 0)
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen return -ENOMEM;
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen }
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen
e3dca0089b7b50e2ec21409d1292727921d06102Tom Gundersen c = context_add(m, context);
if (!c)
goto outofmem;
f = fd_add(m, fd);
if (!f)
goto outofmem;
w = window_add(m);
if (!w)
goto outofmem;
w->keep_always = keep_always;
w->ptr = d;
w->offset = woffset;
w->prot = prot;
w->size = wsize;
w->fd = f;
LIST_PREPEND(by_fd, f->windows, w);
context_detach_window(c);
c->window = w;
LIST_PREPEND(by_window, w->contexts, c);
*ret = (uint8_t*) w->ptr + (offset - w->offset);
return 1;
outofmem:
munmap(d, wsize);
return -ENOMEM;
}
int mmap_cache_get(
MMapCache *m,
int fd,
int prot,
unsigned context,
bool keep_always,
uint64_t offset,
size_t size,
struct stat *st,
void **ret) {
int r;
assert(m);
assert(m->n_ref > 0);
assert(fd >= 0);
assert(size > 0);
assert(ret);
assert(context < MMAP_CACHE_MAX_CONTEXTS);
/* Check whether the current context is the right one already */
r = try_context(m, fd, prot, context, keep_always, offset, size, ret);
if (r != 0) {
m->n_hit ++;
return r;
}
/* Search for a matching mmap */
r = find_mmap(m, fd, prot, context, keep_always, offset, size, ret);
if (r != 0) {
m->n_hit ++;
return r;
}
m->n_missed++;
/* Create a new mmap */
return add_mmap(m, fd, prot, context, keep_always, offset, size, st, ret);
}
unsigned mmap_cache_get_hit(MMapCache *m) {
assert(m);
return m->n_hit;
}
unsigned mmap_cache_get_missed(MMapCache *m) {
assert(m);
return m->n_missed;
}
static void mmap_cache_process_sigbus(MMapCache *m) {
bool found = false;
FileDescriptor *f;
Iterator i;
int r;
assert(m);
/* Iterate through all triggered pages and mark their files as
* invalidated */
for (;;) {
bool ours;
void *addr;
r = sigbus_pop(&addr);
if (_likely_(r == 0))
break;
if (r < 0) {
log_error_errno(r, "SIGBUS handling failed: %m");
abort();
}
ours = false;
HASHMAP_FOREACH(f, m->fds, i) {
Window *w;
LIST_FOREACH(by_fd, w, f->windows) {
if ((uint8_t*) addr >= (uint8_t*) w->ptr &&
(uint8_t*) addr < (uint8_t*) w->ptr + w->size) {
found = ours = f->sigbus = true;
break;
}
}
if (ours)
break;
}
/* Didn't find a matching window, give up */
if (!ours) {
log_error("Unknown SIGBUS page, aborting.");
abort();
}
}
/* The list of triggered pages is now empty. Now, let's remap
* all windows of the triggered file to anonymous maps, so
* that no page of the file in question is triggered again, so
* that we can be sure not to hit the queue size limit. */
if (_likely_(!found))
return;
HASHMAP_FOREACH(f, m->fds, i) {
Window *w;
if (!f->sigbus)
continue;
LIST_FOREACH(by_fd, w, f->windows)
window_invalidate(w);
}
}
bool mmap_cache_got_sigbus(MMapCache *m, int fd) {
FileDescriptor *f;
assert(m);
assert(fd >= 0);
mmap_cache_process_sigbus(m);
f = hashmap_get(m->fds, FD_TO_PTR(fd));
if (!f)
return false;
return f->sigbus;
}
void mmap_cache_close_fd(MMapCache *m, int fd) {
FileDescriptor *f;
assert(m);
assert(fd >= 0);
/* Make sure that any queued SIGBUS are first dispatched, so
* that we don't end up with a SIGBUS entry we cannot relate
* to any existing memory map */
mmap_cache_process_sigbus(m);
f = hashmap_get(m->fds, FD_TO_PTR(fd));
if (!f)
return;
fd_free(f);
}