chiark / gitweb /
mmap: resize arrays dynamically
[elogind.git] / src / journal / mmap-cache.c
index 68dbe7015bce8cb05c253c3f8fb5b426ce1c8c74..69efb20adea08f95e39098da596eb199b4e25fa8 100644 (file)
 #include "mmap-cache.h"
 
 #define WINDOW_SIZE (8ULL*1024ULL*1024ULL)
-#define WINDOWS_MAX 32
+
+#define DEFAULT_WINDOWS_MAX 64
+#define DEFAULT_FDS_MAX 32
+#define DEFAULT_CONTEXTS_MAX 32
 
 typedef struct Window {
         int fd;
@@ -68,6 +71,8 @@ struct MMapCache {
         FileDescriptor *by_fd;
 };
 
+static int mmap_cache_peek_fd_index(MMapCache *m, int fd, unsigned *fd_index);
+
 static void mmap_cache_window_unmap(MMapCache *m, unsigned w) {
         Window *v;
 
@@ -89,6 +94,13 @@ static void mmap_cache_window_add_lru(MMapCache *m, unsigned w) {
         assert(w < m->n_windows);
 
         v = m->windows + w;
+        assert(v->n_ref == 0);
+
+        if (m->lru_last != (unsigned) -1) {
+                assert(m->windows[m->lru_last].lru_next == (unsigned) -1);
+                m->windows[m->lru_last].lru_next = w;
+        }
+
         v->lru_prev = m->lru_last;
         v->lru_next = (unsigned) -1;
 
@@ -105,15 +117,21 @@ static void mmap_cache_window_remove_lru(MMapCache *m, unsigned w) {
 
         v = m->windows + w;
 
-        if (v->lru_prev == (unsigned) -1)
+        if (v->lru_prev == (unsigned) -1) {
+                assert(m->lru_first == w);
                 m->lru_first = v->lru_next;
-        else
+        } else {
+                assert(m->windows[v->lru_prev].lru_next == w);
                 m->windows[v->lru_prev].lru_next = v->lru_next;
+        }
 
-        if (v->lru_next == (unsigned) -1)
+        if (v->lru_next == (unsigned) -1) {
+                assert(m->lru_last == w);
                 m->lru_last = v->lru_prev;
-        else
+        } else {
+                assert(m->windows[v->lru_next].lru_prev == w);
                 m->windows[v->lru_next].lru_prev = v->lru_prev;
+        }
 }
 
 static void mmap_cache_fd_add(MMapCache *m, unsigned fd_index, unsigned w) {
@@ -123,6 +141,13 @@ static void mmap_cache_fd_add(MMapCache *m, unsigned fd_index, unsigned w) {
         assert(fd_index < m->n_fds);
 
         v = m->windows + w;
+        assert(m->by_fd[fd_index].fd == v->fd);
+
+        if (m->by_fd[fd_index].windows != (unsigned) -1) {
+                assert(m->windows[m->by_fd[fd_index].windows].by_fd_prev == (unsigned) -1);
+                m->windows[m->by_fd[fd_index].windows].by_fd_prev = w;
+        }
+
         v->by_fd_next = m->by_fd[fd_index].windows;
         v->by_fd_prev = (unsigned) -1;
 
@@ -136,13 +161,22 @@ static void mmap_cache_fd_remove(MMapCache *m, unsigned fd_index, unsigned w) {
         assert(fd_index < m->n_fds);
 
         v = m->windows + w;
-        if (v->by_fd_prev == (unsigned) -1)
+        assert(m->by_fd[fd_index].fd == v->fd);
+        assert(v->by_fd_next == (unsigned) -1 || m->windows[v->by_fd_next].fd == v->fd);
+        assert(v->by_fd_prev == (unsigned) -1 || m->windows[v->by_fd_prev].fd == v->fd);
+
+        if (v->by_fd_prev == (unsigned) -1) {
+                assert(m->by_fd[fd_index].windows == w);
                 m->by_fd[fd_index].windows = v->by_fd_next;
-        else
+        } else {
+                assert(m->windows[v->by_fd_prev].by_fd_next == w);
                 m->windows[v->by_fd_prev].by_fd_next = v->by_fd_next;
+        }
 
-        if (v->by_fd_next != (unsigned) -1)
+        if (v->by_fd_next != (unsigned) -1) {
+                assert(m->windows[v->by_fd_next].by_fd_prev == w);
                 m->windows[v->by_fd_next].by_fd_prev = v->by_fd_prev;
+        }
 }
 
 static void mmap_cache_context_unset(MMapCache *m, unsigned c) {
@@ -182,6 +216,7 @@ static void mmap_cache_context_set(MMapCache *m, unsigned c, unsigned w) {
 
         v = m->windows + w;
         v->n_ref ++;
+
         if (v->n_ref == 1)
                 mmap_cache_window_remove_lru(m, w);
 }
@@ -204,19 +239,16 @@ static void mmap_cache_free(MMapCache *m) {
         free(m);
 }
 
-MMapCache* mmap_cache_new(unsigned contexts_max, unsigned fds_max) {
+MMapCache* mmap_cache_new(void) {
         MMapCache *m;
 
-        assert(contexts_max > 0);
-        assert(fds_max > 0);
-
         m = new0(MMapCache, 1);
         if (!m)
                 return NULL;
 
-        m->contexts_max = contexts_max;
-        m->fds_max = fds_max;
-        m->windows_max = MAX(m->contexts_max, WINDOWS_MAX);
+        m->contexts_max = DEFAULT_CONTEXTS_MAX;
+        m->fds_max = DEFAULT_FDS_MAX;
+        m->windows_max = DEFAULT_WINDOWS_MAX;
         m->n_ref = 1;
         m->lru_first = (unsigned) -1;
         m->lru_last = (unsigned) -1;
@@ -232,7 +264,6 @@ MMapCache* mmap_cache_new(unsigned contexts_max, unsigned fds_max) {
                 mmap_cache_free(m);
                 return NULL;
         }
-
         memset(m->by_context, -1, m->contexts_max * sizeof(unsigned));
 
         m->by_fd = new(FileDescriptor, m->fds_max);
@@ -265,6 +296,9 @@ MMapCache* mmap_cache_unref(MMapCache *m) {
 }
 
 static int mmap_cache_allocate_window(MMapCache *m, unsigned *w) {
+        Window *v;
+        unsigned fd_index;
+
         assert(m);
         assert(w);
 
@@ -277,7 +311,16 @@ static int mmap_cache_allocate_window(MMapCache *m, unsigned *w) {
                 return -E2BIG;
 
         *w = m->lru_first;
+        v = m->windows + *w;
+        assert(v->n_ref == 0);
+
         mmap_cache_window_unmap(m, *w);
+
+        if (v->fd >= 0) {
+                assert_se(mmap_cache_peek_fd_index(m, v->fd, &fd_index) > 0);
+                mmap_cache_fd_remove(m, fd_index, *w);
+        }
+
         mmap_cache_window_remove_lru(m, *w);
 
         return 0;
@@ -334,7 +377,7 @@ static int mmap_cache_put(
         if (wsize < WINDOW_SIZE) {
                 uint64_t delta;
 
-                delta = (WINDOW_SIZE - wsize) / 2;
+                delta = PAGE_ALIGN((WINDOW_SIZE - wsize) / 2);
 
                 if (delta > offset)
                         woffset = 0;
@@ -371,8 +414,7 @@ static int mmap_cache_put(
         v->size = wsize;
 
         v->n_ref = 0;
-        v->lru_prev = v->lru_next = (unsigned) -1;
-
+        mmap_cache_window_add_lru(m, w);
         mmap_cache_fd_add(m, fd_index, w);
         mmap_cache_context_set(m, context, w);
 
@@ -391,28 +433,58 @@ static int fd_cmp(const void *_a, const void *_b) {
         return 0;
 }
 
+static int mmap_cache_peek_fd_index(MMapCache *m, int fd, unsigned *fd_index) {
+        FileDescriptor *j;
+        unsigned r;
+
+        assert(m);
+        assert(fd >= 0);
+        assert(fd_index);
+
+        for (r = 0; r < m->n_fds; r++)
+                assert(m->by_fd[r].windows == (unsigned) -1 ||
+                       m->windows[m->by_fd[r].windows].fd == m->by_fd[r].fd);
+
+        j = bsearch(&fd, m->by_fd, m->n_fds, sizeof(FileDescriptor), fd_cmp);
+        if (!j)
+                return 0;
+
+        *fd_index = (unsigned) (j - m->by_fd);
+        return 1;
+}
+
 static int mmap_cache_get_fd_index(MMapCache *m, int fd, unsigned *fd_index) {
         FileDescriptor *j;
+        int r;
 
         assert(m);
         assert(fd >= 0);
         assert(fd_index);
 
-        j = bsearch(&fd, m->by_fd, m->n_fds, sizeof(m->by_fd[0]), fd_cmp);
-        if (!j) {
-                if (m->n_fds >= m->fds_max)
-                        return -E2BIG;
+        r = mmap_cache_peek_fd_index(m, fd, fd_index);
+        if (r != 0)
+                return r;
+
+        if (m->n_fds >= m->fds_max) {
+                unsigned k;
+                FileDescriptor *n;
 
-                j = m->by_fd + m->n_fds ++;
-                j->fd = fd;
-                j->windows = (unsigned) -1;
+                k = m->n_fds * 2;
+                n = realloc(m->by_fd, sizeof(FileDescriptor) * k);
+                if (!n)
+                        return -ENOMEM;
 
-                qsort(m->by_fd, m->n_fds, sizeof(m->by_fd[0]), fd_cmp);
-                j = bsearch(&fd, m->by_fd, m->n_fds, sizeof(m->by_fd[0]), fd_cmp);
+                m->fds_max = k;
+                m->by_fd = n;
         }
 
-        *fd_index = (unsigned) (j - m->by_fd);
-        return 0;
+        j = m->by_fd + m->n_fds ++;
+        j->fd = fd;
+        j->windows = (unsigned) -1;
+
+        qsort(m->by_fd, m->n_fds, sizeof(FileDescriptor), fd_cmp);
+
+        return mmap_cache_peek_fd_index(m, fd, fd_index);
 }
 
 static bool mmap_cache_test_window(
@@ -467,6 +539,7 @@ static int mmap_cache_current(
 
 static int mmap_cache_find(
                 MMapCache *m,
+                int fd,
                 unsigned fd_index,
                 unsigned context,
                 uint64_t offset,
@@ -477,6 +550,7 @@ static int mmap_cache_find(
         unsigned w;
 
         assert(m);
+        assert(fd >= 0);
         assert(fd_index < m->n_fds);
         assert(context < m->contexts_max);
         assert(size > 0);
@@ -484,10 +558,13 @@ static int mmap_cache_find(
 
         w = m->by_fd[fd_index].windows;
         while (w != (unsigned) -1) {
+                v = m->windows + w;
+                assert(v->fd == fd);
+
                 if (mmap_cache_test_window(m, w, offset, size))
                         break;
 
-                w = m->windows[w].by_fd_next;
+                w = v->by_fd_next;
         }
 
         if (w == (unsigned) -1)
@@ -495,7 +572,6 @@ static int mmap_cache_find(
 
         mmap_cache_context_set(m, context, w);
 
-        v = m->windows + w;
         *ret = (uint8_t*) v->ptr + (offset - v->offset);
         return 1;
 }
@@ -514,23 +590,50 @@ int mmap_cache_get(
 
         assert(m);
         assert(fd >= 0);
-        assert(context < m->contexts_max);
         assert(size > 0);
         assert(ret);
 
+        if (context >= m->contexts_max) {
+                unsigned k, *n;
+                Window *w;
+
+                /* Increase the number of contexts if necessary, and
+                 * make sure we have twice the number of windows */
+
+                k = context * 2;
+                n = realloc(m->by_context, sizeof(unsigned) * k);
+                if (!n)
+                        return -ENOMEM;
+                memset(n + m->contexts_max, -1, (k - m->contexts_max) * sizeof(unsigned));
+                m->contexts_max = k;
+                m->by_context = n;
+
+                k = MAX(m->windows_max, m->contexts_max*2);
+                w = realloc(m->windows, sizeof(Window) * k);
+                if (!w)
+                        return -ENOMEM;
+
+                m->windows_max = k;
+                m->windows = w;
+        }
+
         /* Maybe the current pointer for this context is already the
          * right one? */
         r = mmap_cache_current(m, fd, context, offset, size, ret);
         if (r != 0)
                 return r;
 
+        /* Hmm, drop the reference to the current one, since it wasn't
+         * good enough */
+        mmap_cache_context_unset(m, context);
+
         /* OK, let's find the chain for this FD */
         r = mmap_cache_get_fd_index(m, fd, &fd_index);
         if (r < 0)
                 return r;
 
         /* And let's look through the available mmaps */
-        r = mmap_cache_find(m, fd_index, context, offset, size, ret);
+        r = mmap_cache_find(m, fd, fd_index, context, offset, size, ret);
         if (r != 0)
                 return r;
 
@@ -539,16 +642,15 @@ int mmap_cache_get(
 }
 
 void mmap_cache_close_fd(MMapCache *m, int fd) {
-        FileDescriptor *j;
         unsigned fd_index, c, w;
+        int r;
 
         assert(m);
         assert(fd > 0);
 
-        j = bsearch(&fd, m->by_fd, m->n_fds, sizeof(m->by_fd[0]), fd_cmp);
-        if (!j)
+        r = mmap_cache_peek_fd_index(m, fd, &fd_index);
+        if (r <= 0)
                 return;
-        fd_index = (unsigned) (j - m->by_fd);
 
         for (c = 0; c < m->contexts_max; c++) {
                 w = m->by_context[c];
@@ -561,9 +663,14 @@ void mmap_cache_close_fd(MMapCache *m, int fd) {
 
         w = m->by_fd[fd_index].windows;
         while (w != (unsigned) -1) {
+                Window *v;
+
+                v = m->windows + w;
+                assert(v->fd == fd);
 
-                mmap_cache_fd_remove(m, fd_index, w);
                 mmap_cache_window_unmap(m, w);
+                mmap_cache_fd_remove(m, fd_index, w);
+                v->fd = -1;
 
                 w = m->by_fd[fd_index].windows;
         }
@@ -572,6 +679,51 @@ void mmap_cache_close_fd(MMapCache *m, int fd) {
         m->n_fds --;
 }
 
+void mmap_cache_close_fd_range(MMapCache *m, int fd, uint64_t p) {
+        unsigned fd_index, c, w;
+        int r;
+
+        assert(m);
+        assert(fd > 0);
+
+        /* This drops all windows that include space right of the
+         * specified offset. This is useful to ensure that after the
+         * file size is extended we drop our mappings of the end and
+         * create it anew, since otherwise it is undefined whether
+         * mapping will continue to work as intended. */
+
+        r = mmap_cache_peek_fd_index(m, fd, &fd_index);
+        if (r <= 0)
+                return;
+
+        for (c = 0; c < m->contexts_max; c++) {
+                w = m->by_context[c];
+
+                if (w != (unsigned) -1 && m->windows[w].fd == fd)
+                        mmap_cache_context_unset(m, c);
+        }
+
+        w = m->by_fd[fd_index].windows;
+        while (w != (unsigned) -1) {
+                Window *v;
+
+                v = m->windows + w;
+                assert(v->fd == fd);
+                assert(v->by_fd_next == (unsigned) -1 ||
+                       m->windows[v->by_fd_next].fd == fd);
+
+                if (v->offset + v->size > p) {
+
+                        mmap_cache_window_unmap(m, w);
+                        mmap_cache_fd_remove(m, fd_index, w);
+                        v->fd = -1;
+
+                        w = m->by_fd[fd_index].windows;
+                } else
+                        w = v->by_fd_next;
+        }
+}
+
 void mmap_cache_close_context(MMapCache *m, unsigned context) {
         mmap_cache_context_unset(m, context);
 }