summary refs log tree commit diff
path: root/pkgs/os-specific
diff options
context:
space:
mode:
authorEelco Dolstra <eelco.dolstra@logicblox.com>2015-09-08 14:15:48 +0200
committerEelco Dolstra <eelco.dolstra@logicblox.com>2015-09-08 14:25:55 +0200
commitc34953ed245c3691cc66943bfff46a264eeb1b3b (patch)
treeb423d363f35f5deb28d13d0c233a297fdf7d9f83 /pkgs/os-specific
parent747e0149cc1ca95a669071557d4cd1d3f8866554 (diff)
downloadnixlib-c34953ed245c3691cc66943bfff46a264eeb1b3b.tar
nixlib-c34953ed245c3691cc66943bfff46a264eeb1b3b.tar.gz
nixlib-c34953ed245c3691cc66943bfff46a264eeb1b3b.tar.bz2
nixlib-c34953ed245c3691cc66943bfff46a264eeb1b3b.tar.lz
nixlib-c34953ed245c3691cc66943bfff46a264eeb1b3b.tar.xz
nixlib-c34953ed245c3691cc66943bfff46a264eeb1b3b.tar.zst
nixlib-c34953ed245c3691cc66943bfff46a264eeb1b3b.zip
systemd: Backport some journalctl performance improvements
Before:

$ time journalctl > /dev/null

real    6m12.470s
user    5m51.439s
sys     0m19.265s

After:

real    0m40.067s
user    0m37.717s
sys     0m2.383s

Before:

$ time journalctl --since '2015-08-01' _TRANSPORT=kernel

real    1m9.817s
user    0m13.318s
sys     0m56.626s

After:

real    0m0.689s
user    0m0.521s
sys     0m0.221s
Diffstat (limited to 'pkgs/os-specific')
-rw-r--r--pkgs/os-specific/linux/systemd/fixes.patch1361
1 files changed, 1358 insertions, 3 deletions
diff --git a/pkgs/os-specific/linux/systemd/fixes.patch b/pkgs/os-specific/linux/systemd/fixes.patch
index c1c768dbacb9..89a40f93b890 100644
--- a/pkgs/os-specific/linux/systemd/fixes.patch
+++ b/pkgs/os-specific/linux/systemd/fixes.patch
@@ -220,7 +220,7 @@ index 25c4a0b..e1f2d6d 100644
                          flags |= SHOW_MASKED;
                  else if (strneq ("equivalent", word, l))
 diff --git a/src/fsck/fsck.c b/src/fsck/fsck.c
-index 70a5918..1926e52 100644
+index 70a5918..a5661e8 100644
 --- a/src/fsck/fsck.c
 +++ b/src/fsck/fsck.c
 @@ -315,8 +315,7 @@ int main(int argc, char *argv[]) {
@@ -272,11 +272,231 @@ index e487369..ff4e9c9 100644
 -        return r < 0 ? EXIT_FAILURE : r;
 +        return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
  }
+diff --git a/src/journal-remote/journal-remote-parse.c b/src/journal-remote/journal-remote-parse.c
+index 7dd8878..70a9a13 100644
+--- a/src/journal-remote/journal-remote-parse.c
++++ b/src/journal-remote/journal-remote-parse.c
+@@ -344,22 +344,25 @@ int process_data(RemoteSource *source) {
+                    LLLLLLLL0011223344...\n
+                 */
+                 sep = memchr(line, '=', n);
+-                if (sep)
++                if (sep) {
+                         /* chomp newline */
+                         n--;
+-                else
++
++                        r = iovw_put(&source->iovw, line, n);
++                        if (r < 0)
++                                return r;
++                } else {
+                         /* replace \n with = */
+                         line[n-1] = '=';
+-                log_trace("Received: %.*s", (int) n, line);
+ 
+-                r = iovw_put(&source->iovw, line, n);
+-                if (r < 0) {
+-                        log_error("Failed to put line in iovect");
+-                        return r;
++                        source->field_len = n;
++                        source->state = STATE_DATA_START;
++
++                        /* we cannot put the field in iovec until we have all data */
+                 }
+ 
+-                if (!sep)
+-                        source->state = STATE_DATA_START;
++                log_trace("Received: %.*s (%s)", (int) n, line, sep ? "text" : "binary");
++
+                 return 0; /* continue */
+         }
+ 
+@@ -382,6 +385,7 @@ int process_data(RemoteSource *source) {
+ 
+         case STATE_DATA: {
+                 void *data;
++                char *field;
+ 
+                 assert(source->data_size > 0);
+ 
+@@ -396,11 +400,12 @@ int process_data(RemoteSource *source) {
+ 
+                 assert(data);
+ 
+-                r = iovw_put(&source->iovw, data, source->data_size);
+-                if (r < 0) {
+-                        log_error("failed to put binary buffer in iovect");
++                field = (char*) data - sizeof(uint64_t) - source->field_len;
++                memmove(field + sizeof(uint64_t), field, source->field_len);
++
++                r = iovw_put(&source->iovw, field + sizeof(uint64_t), source->field_len + source->data_size);
++                if (r < 0)
+                         return r;
+-                }
+ 
+                 source->state = STATE_DATA_FINISH;
+ 
+diff --git a/src/journal-remote/journal-remote-parse.h b/src/journal-remote/journal-remote-parse.h
+index 8499f4e..22db550 100644
+--- a/src/journal-remote/journal-remote-parse.h
++++ b/src/journal-remote/journal-remote-parse.h
+@@ -42,7 +42,9 @@ typedef struct RemoteSource {
+         size_t offset;     /* offset to the beginning of live data in the buffer */
+         size_t scanned;    /* number of bytes since the beginning of data without a newline */
+         size_t filled;     /* total number of bytes in the buffer */
+-        size_t data_size;  /* size of the binary data chunk being processed */
++
++        size_t field_len;  /* used for binary fields: the field name length */
++        size_t data_size;  /* and the size of the binary data chunk being processed */
+ 
+         struct iovec_wrapper iovw;
+ 
+diff --git a/src/journal/journal-authenticate.c b/src/journal/journal-authenticate.c
+index 5ab1982..1f980ee 100644
+--- a/src/journal/journal-authenticate.c
++++ b/src/journal/journal-authenticate.c
+@@ -229,7 +229,7 @@ int journal_file_maybe_append_tag(JournalFile *f, uint64_t realtime) {
+         return 0;
+ }
+ 
+-int journal_file_hmac_put_object(JournalFile *f, int type, Object *o, uint64_t p) {
++int journal_file_hmac_put_object(JournalFile *f, ObjectType type, Object *o, uint64_t p) {
+         int r;
+ 
+         assert(f);
+@@ -246,7 +246,7 @@ int journal_file_hmac_put_object(JournalFile *f, int type, Object *o, uint64_t p
+                 if (r < 0)
+                         return r;
+         } else {
+-                if (type >= 0 && o->object.type != type)
++                if (type > OBJECT_UNUSED && o->object.type != type)
+                         return -EBADMSG;
+         }
+ 
+diff --git a/src/journal/journal-authenticate.h b/src/journal/journal-authenticate.h
+index 0aaf836..565fe84 100644
+--- a/src/journal/journal-authenticate.h
++++ b/src/journal/journal-authenticate.h
+@@ -33,7 +33,7 @@ int journal_file_append_first_tag(JournalFile *f);
+ int journal_file_hmac_setup(JournalFile *f);
+ int journal_file_hmac_start(JournalFile *f);
+ int journal_file_hmac_put_header(JournalFile *f);
+-int journal_file_hmac_put_object(JournalFile *f, int type, Object *o, uint64_t p);
++int journal_file_hmac_put_object(JournalFile *f, ObjectType type, Object *o, uint64_t p);
+ 
+ int journal_file_fss_load(JournalFile *f);
+ int journal_file_parse_verification_key(JournalFile *f, const char *key);
+diff --git a/src/journal/journal-def.h b/src/journal/journal-def.h
+index e55fa19..ab089cb 100644
+--- a/src/journal/journal-def.h
++++ b/src/journal/journal-def.h
+@@ -52,8 +52,8 @@ typedef struct HashItem HashItem;
+ typedef struct FSSHeader FSSHeader;
+ 
+ /* Object types */
+-enum {
+-        OBJECT_UNUSED,
++typedef enum ObjectType {
++        OBJECT_UNUSED, /* also serves as "any type" or "additional context" */
+         OBJECT_DATA,
+         OBJECT_FIELD,
+         OBJECT_ENTRY,
+@@ -62,7 +62,7 @@ enum {
+         OBJECT_ENTRY_ARRAY,
+         OBJECT_TAG,
+         _OBJECT_TYPE_MAX
+-};
++} ObjectType;
+ 
+ /* Object flags */
+ enum {
 diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c
-index 8a2c0fc..9de3ddd 100644
+index 8a2c0fc..c55a4dc 100644
 --- a/src/journal/journal-file.c
 +++ b/src/journal/journal-file.c
-@@ -1657,7 +1657,7 @@ static int generic_array_bisect(
+@@ -374,7 +374,13 @@ static int journal_file_allocate(JournalFile *f, uint64_t offset, uint64_t size)
+         return 0;
+ }
+ 
+-static int journal_file_move_to(JournalFile *f, int context, bool keep_always, uint64_t offset, uint64_t size, void **ret) {
++static unsigned type_to_context(ObjectType type) {
++        /* One context for each type, plus one catch-all for the rest */
++        assert_cc(_OBJECT_TYPE_MAX <= MMAP_CACHE_MAX_CONTEXTS);
++        return type > OBJECT_UNUSED && type < _OBJECT_TYPE_MAX ? type : 0;
++}
++
++static int journal_file_move_to(JournalFile *f, ObjectType type, bool keep_always, uint64_t offset, uint64_t size, void **ret) {
+         assert(f);
+         assert(ret);
+ 
+@@ -391,7 +397,7 @@ static int journal_file_move_to(JournalFile *f, int context, bool keep_always, u
+                         return -EADDRNOTAVAIL;
+         }
+ 
+-        return mmap_cache_get(f->mmap, f->fd, f->prot, context, keep_always, offset, size, &f->last_stat, ret, NULL);
++        return mmap_cache_get(f->mmap, f->fd, f->prot, type_to_context(type), keep_always, offset, size, &f->last_stat, ret);
+ }
+ 
+ static uint64_t minimum_header_size(Object *o) {
+@@ -412,7 +418,7 @@ static uint64_t minimum_header_size(Object *o) {
+         return table[o->object.type];
+ }
+ 
+-int journal_file_move_to_object(JournalFile *f, int type, uint64_t offset, Object **ret) {
++int journal_file_move_to_object(JournalFile *f, ObjectType type, uint64_t offset, Object **ret) {
+         int r;
+         void *t;
+         Object *o;
+@@ -425,7 +431,7 @@ int journal_file_move_to_object(JournalFile *f, int type, uint64_t offset, Objec
+         if (!VALID64(offset))
+                 return -EFAULT;
+ 
+-        r = journal_file_move_to(f, type_to_context(type), false, offset, sizeof(ObjectHeader), &t);
++        r = journal_file_move_to(f, type, false, offset, sizeof(ObjectHeader), &t);
+         if (r < 0)
+                 return r;
+ 
+@@ -441,11 +447,11 @@ int journal_file_move_to_object(JournalFile *f, int type, uint64_t offset, Objec
+         if (s < minimum_header_size(o))
+                 return -EBADMSG;
+ 
+-        if (type > 0 && o->object.type != type)
++        if (type > OBJECT_UNUSED && o->object.type != type)
+                 return -EBADMSG;
+ 
+         if (s > sizeof(ObjectHeader)) {
+-                r = journal_file_move_to(f, o->object.type, false, offset, s, &t);
++                r = journal_file_move_to(f, type, false, offset, s, &t);
+                 if (r < 0)
+                         return r;
+ 
+@@ -482,14 +488,14 @@ static uint64_t journal_file_entry_seqnum(JournalFile *f, uint64_t *seqnum) {
+         return r;
+ }
+ 
+-int journal_file_append_object(JournalFile *f, int type, uint64_t size, Object **ret, uint64_t *offset) {
++int journal_file_append_object(JournalFile *f, ObjectType type, uint64_t size, Object **ret, uint64_t *offset) {
+         int r;
+         uint64_t p;
+         Object *tail, *o;
+         void *t;
+ 
+         assert(f);
+-        assert(type > 0 && type < _OBJECT_TYPE_MAX);
++        assert(type > OBJECT_UNUSED && type < _OBJECT_TYPE_MAX);
+         assert(size >= sizeof(ObjectHeader));
+         assert(offset);
+         assert(ret);
+@@ -502,7 +508,7 @@ int journal_file_append_object(JournalFile *f, int type, uint64_t size, Object *
+         if (p == 0)
+                 p = le64toh(f->header->header_size);
+         else {
+-                r = journal_file_move_to_object(f, -1, p, &tail);
++                r = journal_file_move_to_object(f, OBJECT_UNUSED, p, &tail);
+                 if (r < 0)
+                         return r;
+ 
+@@ -1657,7 +1663,7 @@ static int generic_array_bisect(
                          }
                  }
  
@@ -285,6 +505,369 @@ index 8a2c0fc..9de3ddd 100644
                          if (direction == DIRECTION_UP) {
                                  i = n;
                                  subtract_one = true;
+@@ -1793,23 +1799,6 @@ _pure_ static int test_object_offset(JournalFile *f, uint64_t p, uint64_t needle
+                 return TEST_RIGHT;
+ }
+ 
+-int journal_file_move_to_entry_by_offset(
+-                JournalFile *f,
+-                uint64_t p,
+-                direction_t direction,
+-                Object **ret,
+-                uint64_t *offset) {
+-
+-        return generic_array_bisect(f,
+-                                    le64toh(f->header->entry_array_offset),
+-                                    le64toh(f->header->n_entries),
+-                                    p,
+-                                    test_object_offset,
+-                                    direction,
+-                                    ret, offset, NULL);
+-}
+-
+-
+ static int test_object_seqnum(JournalFile *f, uint64_t p, uint64_t needle) {
+         Object *o;
+         int r;
+@@ -1939,9 +1928,81 @@ int journal_file_move_to_entry_by_monotonic(
+                                              ret, offset, NULL);
+ }
+ 
++void journal_file_reset_location(JournalFile *f) {
++        f->location_type = LOCATION_HEAD;
++        f->current_offset = 0;
++        f->current_seqnum = 0;
++        f->current_realtime = 0;
++        f->current_monotonic = 0;
++        zero(f->current_boot_id);
++        f->current_xor_hash = 0;
++}
++
++void journal_file_save_location(JournalFile *f, Object *o, uint64_t offset) {
++        f->location_type = LOCATION_SEEK;
++        f->current_offset = offset;
++        f->current_seqnum = le64toh(o->entry.seqnum);
++        f->current_realtime = le64toh(o->entry.realtime);
++        f->current_monotonic = le64toh(o->entry.monotonic);
++        f->current_boot_id = o->entry.boot_id;
++        f->current_xor_hash = le64toh(o->entry.xor_hash);
++}
++
++int journal_file_compare_locations(JournalFile *af, JournalFile *bf) {
++        assert(af);
++        assert(bf);
++        assert(af->location_type == LOCATION_SEEK);
++        assert(bf->location_type == LOCATION_SEEK);
++
++        /* If contents and timestamps match, these entries are
++         * identical, even if the seqnum does not match */
++        if (sd_id128_equal(af->current_boot_id, bf->current_boot_id) &&
++            af->current_monotonic == bf->current_monotonic &&
++            af->current_realtime == bf->current_realtime &&
++            af->current_xor_hash == bf->current_xor_hash)
++                return 0;
++
++        if (sd_id128_equal(af->header->seqnum_id, bf->header->seqnum_id)) {
++
++                /* If this is from the same seqnum source, compare
++                 * seqnums */
++                if (af->current_seqnum < bf->current_seqnum)
++                        return -1;
++                if (af->current_seqnum > bf->current_seqnum)
++                        return 1;
++
++                /* Wow! This is weird, different data but the same
++                 * seqnums? Something is borked, but let's make the
++                 * best of it and compare by time. */
++        }
++
++        if (sd_id128_equal(af->current_boot_id, bf->current_boot_id)) {
++
++                /* If the boot id matches, compare monotonic time */
++                if (af->current_monotonic < bf->current_monotonic)
++                        return -1;
++                if (af->current_monotonic > bf->current_monotonic)
++                        return 1;
++        }
++
++        /* Otherwise, compare UTC time */
++        if (af->current_realtime < bf->current_realtime)
++                return -1;
++        if (af->current_realtime > bf->current_realtime)
++                return 1;
++
++        /* Finally, compare by contents */
++        if (af->current_xor_hash < bf->current_xor_hash)
++                return -1;
++        if (af->current_xor_hash > bf->current_xor_hash)
++                return 1;
++
++        return 0;
++}
++
+ int journal_file_next_entry(
+                 JournalFile *f,
+-                Object *o, uint64_t p,
++                uint64_t p,
+                 direction_t direction,
+                 Object **ret, uint64_t *offset) {
+ 
+@@ -1949,18 +2010,14 @@ int journal_file_next_entry(
+         int r;
+ 
+         assert(f);
+-        assert(p > 0 || !o);
+ 
+         n = le64toh(f->header->n_entries);
+         if (n <= 0)
+                 return 0;
+ 
+-        if (!o)
++        if (p == 0)
+                 i = direction == DIRECTION_DOWN ? 0 : n - 1;
+         else {
+-                if (o->object.type != OBJECT_ENTRY)
+-                        return -EINVAL;
+-
+                 r = generic_array_bisect(f,
+                                          le64toh(f->header->entry_array_offset),
+                                          le64toh(f->header->n_entries),
+@@ -2006,55 +2063,6 @@ int journal_file_next_entry(
+         return 1;
+ }
+ 
+-int journal_file_skip_entry(
+-                JournalFile *f,
+-                Object *o, uint64_t p,
+-                int64_t skip,
+-                Object **ret, uint64_t *offset) {
+-
+-        uint64_t i, n;
+-        int r;
+-
+-        assert(f);
+-        assert(o);
+-        assert(p > 0);
+-
+-        if (o->object.type != OBJECT_ENTRY)
+-                return -EINVAL;
+-
+-        r = generic_array_bisect(f,
+-                                 le64toh(f->header->entry_array_offset),
+-                                 le64toh(f->header->n_entries),
+-                                 p,
+-                                 test_object_offset,
+-                                 DIRECTION_DOWN,
+-                                 NULL, NULL,
+-                                 &i);
+-        if (r <= 0)
+-                return r;
+-
+-        /* Calculate new index */
+-        if (skip < 0) {
+-                if ((uint64_t) -skip >= i)
+-                        i = 0;
+-                else
+-                        i = i - (uint64_t) -skip;
+-        } else
+-                i  += (uint64_t) skip;
+-
+-        n = le64toh(f->header->n_entries);
+-        if (n <= 0)
+-                return -EBADMSG;
+-
+-        if (i >= n)
+-                i = n-1;
+-
+-        return generic_array_get(f,
+-                                 le64toh(f->header->entry_array_offset),
+-                                 i,
+-                                 ret, offset);
+-}
+-
+ int journal_file_next_entry_for_data(
+                 JournalFile *f,
+                 Object *o, uint64_t p,
+@@ -2289,7 +2297,7 @@ void journal_file_dump(JournalFile *f) {
+ 
+         p = le64toh(f->header->header_size);
+         while (p != 0) {
+-                r = journal_file_move_to_object(f, -1, p, &o);
++                r = journal_file_move_to_object(f, OBJECT_UNUSED, p, &o);
+                 if (r < 0)
+                         goto fail;
+ 
+diff --git a/src/journal/journal-file.h b/src/journal/journal-file.h
+index 211e121..ca17c97 100644
+--- a/src/journal/journal-file.h
++++ b/src/journal/journal-file.h
+@@ -48,6 +48,20 @@ typedef enum direction {
+         DIRECTION_DOWN
+ } direction_t;
+ 
++typedef enum LocationType {
++        /* The first and last entries, resp. */
++        LOCATION_HEAD,
++        LOCATION_TAIL,
++
++        /* We already read the entry we currently point to, and the
++         * next one to read should probably not be this one again. */
++        LOCATION_DISCRETE,
++
++        /* We should seek to the precise location specified, and
++         * return it, as we haven't read it yet. */
++        LOCATION_SEEK
++} LocationType;
++
+ typedef struct JournalFile {
+         int fd;
+ 
+@@ -63,6 +77,8 @@ typedef struct JournalFile {
+         bool tail_entry_monotonic_valid:1;
+ 
+         direction_t last_direction;
++        LocationType location_type;
++        uint64_t last_n_entries;
+ 
+         char *path;
+         struct stat last_stat;
+@@ -72,6 +88,11 @@ typedef struct JournalFile {
+         HashItem *field_hash_table;
+ 
+         uint64_t current_offset;
++        uint64_t current_seqnum;
++        uint64_t current_realtime;
++        uint64_t current_monotonic;
++        sd_id128_t current_boot_id;
++        uint64_t current_xor_hash;
+ 
+         JournalMetrics metrics;
+         MMapCache *mmap;
+@@ -160,13 +181,13 @@ static inline bool VALID_EPOCH(uint64_t u) {
+ #define JOURNAL_HEADER_COMPRESSED_LZ4(h) \
+         (!!(le32toh((h)->incompatible_flags) & HEADER_INCOMPATIBLE_COMPRESSED_LZ4))
+ 
+-int journal_file_move_to_object(JournalFile *f, int type, uint64_t offset, Object **ret);
++int journal_file_move_to_object(JournalFile *f, ObjectType type, uint64_t offset, Object **ret);
+ 
+ uint64_t journal_file_entry_n_items(Object *o) _pure_;
+ uint64_t journal_file_entry_array_n_items(Object *o) _pure_;
+ uint64_t journal_file_hash_table_n_items(Object *o) _pure_;
+ 
+-int journal_file_append_object(JournalFile *f, int type, uint64_t size, Object **ret, uint64_t *offset);
++int journal_file_append_object(JournalFile *f, ObjectType type, uint64_t size, Object **ret, uint64_t *offset);
+ int journal_file_append_entry(JournalFile *f, const dual_timestamp *ts, const struct iovec iovec[], unsigned n_iovec, uint64_t *seqno, Object **ret, uint64_t *offset);
+ 
+ int journal_file_find_data_object(JournalFile *f, const void *data, uint64_t size, Object **ret, uint64_t *offset);
+@@ -175,12 +196,13 @@ int journal_file_find_data_object_with_hash(JournalFile *f, const void *data, ui
+ int journal_file_find_field_object(JournalFile *f, const void *field, uint64_t size, Object **ret, uint64_t *offset);
+ int journal_file_find_field_object_with_hash(JournalFile *f, const void *field, uint64_t size, uint64_t hash, Object **ret, uint64_t *offset);
+ 
+-int journal_file_next_entry(JournalFile *f, Object *o, uint64_t p, direction_t direction, Object **ret, uint64_t *offset);
+-int journal_file_skip_entry(JournalFile *f, Object *o, uint64_t p, int64_t skip, Object **ret, uint64_t *offset);
++void journal_file_reset_location(JournalFile *f);
++void journal_file_save_location(JournalFile *f, Object *o, uint64_t offset);
++int journal_file_compare_locations(JournalFile *af, JournalFile *bf);
++int journal_file_next_entry(JournalFile *f, uint64_t p, direction_t direction, Object **ret, uint64_t *offset);
+ 
+ int journal_file_next_entry_for_data(JournalFile *f, Object *o, uint64_t p, uint64_t data_offset, direction_t direction, Object **ret, uint64_t *offset);
+ 
+-int journal_file_move_to_entry_by_offset(JournalFile *f, uint64_t seqnum, direction_t direction, Object **ret, uint64_t *offset);
+ int journal_file_move_to_entry_by_seqnum(JournalFile *f, uint64_t seqnum, direction_t direction, Object **ret, uint64_t *offset);
+ int journal_file_move_to_entry_by_realtime(JournalFile *f, uint64_t realtime, direction_t direction, Object **ret, uint64_t *offset);
+ int journal_file_move_to_entry_by_monotonic(JournalFile *f, sd_id128_t boot_id, uint64_t monotonic, direction_t direction, Object **ret, uint64_t *offset);
+@@ -205,21 +227,3 @@ int journal_file_get_cutoff_realtime_usec(JournalFile *f, usec_t *from, usec_t *
+ int journal_file_get_cutoff_monotonic_usec(JournalFile *f, sd_id128_t boot, usec_t *from, usec_t *to);
+ 
+ bool journal_file_rotate_suggested(JournalFile *f, usec_t max_file_usec);
+-
+-
+-static unsigned type_to_context(int type) {
+-        /* One context for each type, plus one catch-all for the rest */
+-        return type > 0 && type < _OBJECT_TYPE_MAX ? type : 0;
+-}
+-
+-static inline int journal_file_object_keep(JournalFile *f, Object *o, uint64_t offset, void **release_cookie) {
+-        unsigned context = type_to_context(o->object.type);
+-        uint64_t s = le64toh(o->object.size);
+-
+-        return mmap_cache_get(f->mmap, f->fd, f->prot, context, true,
+-                              offset, s, &f->last_stat, NULL, release_cookie);
+-}
+-
+-static inline int journal_file_object_release(JournalFile *f, void *release_cookie) {
+-        return mmap_cache_release(f->mmap, f->fd, release_cookie);
+-}
+diff --git a/src/journal/journal-internal.h b/src/journal/journal-internal.h
+index 70847db..e99050c 100644
+--- a/src/journal/journal-internal.h
++++ b/src/journal/journal-internal.h
+@@ -57,20 +57,6 @@ struct Match {
+         LIST_HEAD(Match, matches);
+ };
+ 
+-typedef enum LocationType {
+-        /* The first and last entries, resp. */
+-        LOCATION_HEAD,
+-        LOCATION_TAIL,
+-
+-        /* We already read the entry we currently point to, and the
+-         * next one to read should probably not be this one again. */
+-        LOCATION_DISCRETE,
+-
+-        /* We should seek to the precise location specified, and
+-         * return it, as we haven't read it yet. */
+-        LOCATION_SEEK
+-} LocationType;
+-
+ struct Location {
+         LocationType type;
+ 
+diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c
+index f74adcb..5baa22d 100644
+--- a/src/journal/journal-verify.c
++++ b/src/journal/journal-verify.c
+@@ -368,7 +368,7 @@ static int contains_uint64(MMapCache *m, int fd, uint64_t n, uint64_t p) {
+ 
+                 c = (a + b) / 2;
+ 
+-                r = mmap_cache_get(m, fd, PROT_READ|PROT_WRITE, 0, false, c * sizeof(uint64_t), sizeof(uint64_t), NULL, (void **) &z, NULL);
++                r = mmap_cache_get(m, fd, PROT_READ|PROT_WRITE, 0, false, c * sizeof(uint64_t), sizeof(uint64_t), NULL, (void **) &z);
+                 if (r < 0)
+                         return r;
+ 
+@@ -865,7 +865,7 @@ int journal_file_verify(
+                 if (show_progress)
+                         draw_progress(0x7FFF * p / le64toh(f->header->tail_object_offset), &last_usec);
+ 
+-                r = journal_file_move_to_object(f, -1, p, &o);
++                r = journal_file_move_to_object(f, OBJECT_UNUSED, p, &o);
+                 if (r < 0) {
+                         error(p, "invalid object");
+                         goto fail;
+@@ -1085,11 +1085,11 @@ int journal_file_verify(
+                                         q = last_tag;
+ 
+                                 while (q <= p) {
+-                                        r = journal_file_move_to_object(f, -1, q, &o);
++                                        r = journal_file_move_to_object(f, OBJECT_UNUSED, q, &o);
+                                         if (r < 0)
+                                                 goto fail;
+ 
+-                                        r = journal_file_hmac_put_object(f, -1, o, q);
++                                        r = journal_file_hmac_put_object(f, OBJECT_UNUSED, o, q);
+                                         if (r < 0)
+                                                 goto fail;
+ 
+@@ -1097,7 +1097,7 @@ int journal_file_verify(
+                                 }
+ 
+                                 /* Position might have changed, let's reposition things */
+-                                r = journal_file_move_to_object(f, -1, p, &o);
++                                r = journal_file_move_to_object(f, OBJECT_UNUSED, p, &o);
+                                 if (r < 0)
+                                         goto fail;
+ 
 diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c
 index f50faf4..03579fd 100644
 --- a/src/journal/journalctl.c
@@ -298,6 +881,21 @@ index f50faf4..03579fd 100644
                  arg_lines = 10;
  
          if (!!arg_directory + !!arg_file + !!arg_machine > 1) {
+diff --git a/src/journal/journald-native.c b/src/journal/journald-native.c
+index a635202..655e2dd 100644
+--- a/src/journal/journald-native.c
++++ b/src/journal/journald-native.c
+@@ -132,8 +132,8 @@ void server_process_native_message(
+ 
+                 /* A property follows */
+ 
+-                /* n received properties, +1 for _TRANSPORT */
+-                if (!GREEDY_REALLOC(iovec, m, n + 1 + N_IOVEC_META_FIELDS + !!object_pid * N_IOVEC_OBJECT_FIELDS)) {
++                /* n existing properties, 1 new, +1 for _TRANSPORT */
++                if (!GREEDY_REALLOC(iovec, m, n + 2 + N_IOVEC_META_FIELDS + N_IOVEC_OBJECT_FIELDS)) {
+                         log_oom();
+                         break;
+                 }
 diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c
 index 12735c4..08b143b 100644
 --- a/src/journal/journald-server.c
@@ -310,6 +908,739 @@ index 12735c4..08b143b 100644
  
          if (s->mmap)
                  mmap_cache_unref(s->mmap);
+diff --git a/src/journal/mmap-cache.c b/src/journal/mmap-cache.c
+index b7db6f1..f6f669d 100644
+--- a/src/journal/mmap-cache.c
++++ b/src/journal/mmap-cache.c
+@@ -38,7 +38,7 @@ typedef struct FileDescriptor FileDescriptor;
+ struct Window {
+         MMapCache *cache;
+ 
+-        unsigned keep_always;
++        bool keep_always;
+         bool in_unused;
+ 
+         int prot;
+@@ -76,7 +76,7 @@ struct MMapCache {
+ 
+ 
+         Hashmap *fds;
+-        Hashmap *contexts;
++        Context *contexts[MMAP_CACHE_MAX_CONTEXTS];
+ 
+         LIST_HEAD(Window, unused);
+         Window *last_unused;
+@@ -185,7 +185,7 @@ static void context_detach_window(Context *c) {
+         c->window = NULL;
+         LIST_REMOVE(by_window, w->contexts, c);
+ 
+-        if (!w->contexts && w->keep_always == 0) {
++        if (!w->contexts && !w->keep_always) {
+                 /* Not used anymore? */
+                 LIST_PREPEND(unused, c->cache->unused, w);
+                 if (!c->cache->last_unused)
+@@ -219,18 +219,13 @@ static void context_attach_window(Context *c, Window *w) {
+ 
+ static Context *context_add(MMapCache *m, unsigned id) {
+         Context *c;
+-        int r;
+ 
+         assert(m);
+ 
+-        c = hashmap_get(m->contexts, UINT_TO_PTR(id + 1));
++        c = m->contexts[id];
+         if (c)
+                 return c;
+ 
+-        r = hashmap_ensure_allocated(&m->contexts, NULL);
+-        if (r < 0)
+-                return NULL;
+-
+         c = new0(Context, 1);
+         if (!c)
+                 return NULL;
+@@ -238,11 +233,8 @@ static Context *context_add(MMapCache *m, unsigned id) {
+         c->cache = m;
+         c->id = id;
+ 
+-        r = hashmap_put(m->contexts, UINT_TO_PTR(id + 1), c);
+-        if (r < 0) {
+-                free(c);
+-                return NULL;
+-        }
++        assert(!m->contexts[id]);
++        m->contexts[id] = c;
+ 
+         return c;
+ }
+@@ -252,8 +244,10 @@ static void context_free(Context *c) {
+ 
+         context_detach_window(c);
+ 
+-        if (c->cache)
+-                assert_se(hashmap_remove(c->cache->contexts, UINT_TO_PTR(c->id + 1)));
++        if (c->cache) {
++                assert(c->cache->contexts[c->id] == c);
++                c->cache->contexts[c->id] = NULL;
++        }
+ 
+         free(c);
+ }
+@@ -302,15 +296,14 @@ static FileDescriptor* fd_add(MMapCache *m, int fd) {
+ }
+ 
+ static void mmap_cache_free(MMapCache *m) {
+-        Context *c;
+         FileDescriptor *f;
++        int i;
+ 
+         assert(m);
+ 
+-        while ((c = hashmap_first(m->contexts)))
+-                context_free(c);
+-
+-        hashmap_free(m->contexts);
++        for (i = 0; i < MMAP_CACHE_MAX_CONTEXTS; i++)
++                if (m->contexts[i])
++                        context_free(m->contexts[i]);
+ 
+         while ((f = hashmap_first(m->fds)))
+                 fd_free(f);
+@@ -352,8 +345,7 @@ static int try_context(
+                 bool keep_always,
+                 uint64_t offset,
+                 size_t size,
+-                void **ret,
+-                void **release_cookie) {
++                void **ret) {
+ 
+         Context *c;
+ 
+@@ -361,8 +353,9 @@ static int try_context(
+         assert(m->n_ref > 0);
+         assert(fd >= 0);
+         assert(size > 0);
++        assert(ret);
+ 
+-        c = hashmap_get(m->contexts, UINT_TO_PTR(context+1));
++        c = m->contexts[context];
+         if (!c)
+                 return 0;
+ 
+@@ -378,12 +371,9 @@ static int try_context(
+                 return 0;
+         }
+ 
+-        c->window->keep_always += keep_always;
++        c->window->keep_always |= keep_always;
+ 
+-        if (ret)
+-                *ret = (uint8_t*) c->window->ptr + (offset - c->window->offset);
+-        if (keep_always && release_cookie)
+-                *release_cookie = c->window;
++        *ret = (uint8_t*) c->window->ptr + (offset - c->window->offset);
+         return 1;
+ }
+ 
+@@ -395,8 +385,7 @@ static int find_mmap(
+                 bool keep_always,
+                 uint64_t offset,
+                 size_t size,
+-                void **ret,
+-                void **release_cookie) {
++                void **ret) {
+ 
+         FileDescriptor *f;
+         Window *w;
+@@ -427,10 +416,7 @@ static int find_mmap(
+         context_attach_window(c, w);
+         w->keep_always += keep_always;
+ 
+-        if (ret)
+-                *ret = (uint8_t*) w->ptr + (offset - w->offset);
+-        if (keep_always && release_cookie)
+-                *release_cookie = c->window;
++        *ret = (uint8_t*) w->ptr + (offset - w->offset);
+         return 1;
+ }
+ 
+@@ -443,8 +429,7 @@ static int add_mmap(
+                 uint64_t offset,
+                 size_t size,
+                 struct stat *st,
+-                void **ret,
+-                void **release_cookie) {
++                void **ret) {
+ 
+         uint64_t woffset, wsize;
+         Context *c;
+@@ -457,6 +442,7 @@ static int add_mmap(
+         assert(m->n_ref > 0);
+         assert(fd >= 0);
+         assert(size > 0);
++        assert(ret);
+ 
+         woffset = offset & ~((uint64_t) page_size() - 1ULL);
+         wsize = size + (offset - woffset);
+@@ -526,10 +512,7 @@ static int add_mmap(
+         c->window = w;
+         LIST_PREPEND(by_window, w->contexts, c);
+ 
+-        if (ret)
+-                *ret = (uint8_t*) w->ptr + (offset - w->offset);
+-        if (keep_always && release_cookie)
+-                *release_cookie = c->window;
++        *ret = (uint8_t*) w->ptr + (offset - w->offset);
+         return 1;
+ 
+ outofmem:
+@@ -546,8 +529,7 @@ int mmap_cache_get(
+                 uint64_t offset,
+                 size_t size,
+                 struct stat *st,
+-                void **ret,
+-                void **release_cookie) {
++                void **ret) {
+ 
+         int r;
+ 
+@@ -555,16 +537,18 @@ int mmap_cache_get(
+         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, release_cookie);
++        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, release_cookie);
++        r = find_mmap(m, fd, prot, context, keep_always, offset, size, ret);
+         if (r != 0) {
+                 m->n_hit ++;
+                 return r;
+@@ -573,39 +557,7 @@ int mmap_cache_get(
+         m->n_missed++;
+ 
+         /* Create a new mmap */
+-        return add_mmap(m, fd, prot, context, keep_always, offset, size, st, ret, release_cookie);
+-}
+-
+-int mmap_cache_release(
+-                MMapCache *m,
+-                int fd,
+-                void *release_cookie) {
+-
+-        FileDescriptor *f;
+-        Window *w;
+-
+-        assert(m);
+-        assert(m->n_ref > 0);
+-        assert(fd >= 0);
+-
+-        f = hashmap_get(m->fds, INT_TO_PTR(fd + 1));
+-        if (!f)
+-                return -EBADF;
+-
+-        assert(f->fd == fd);
+-
+-        LIST_FOREACH(by_fd, w, f->windows)
+-                if (w == release_cookie)
+-                        break;
+-
+-        if (!w)
+-                return -ENOENT;
+-
+-        if (w->keep_always == 0)
+-                return -ENOLCK;
+-
+-        w->keep_always -= 1;
+-        return 0;
++        return add_mmap(m, fd, prot, context, keep_always, offset, size, st, ret);
+ }
+ 
+ void mmap_cache_close_fd(MMapCache *m, int fd) {
+@@ -621,18 +573,6 @@ void mmap_cache_close_fd(MMapCache *m, int fd) {
+         fd_free(f);
+ }
+ 
+-void mmap_cache_close_context(MMapCache *m, unsigned context) {
+-        Context *c;
+-
+-        assert(m);
+-
+-        c = hashmap_get(m->contexts, UINT_TO_PTR(context + 1));
+-        if (!c)
+-                return;
+-
+-        context_free(c);
+-}
+-
+ unsigned mmap_cache_get_hit(MMapCache *m) {
+         assert(m);
+ 
+diff --git a/src/journal/mmap-cache.h b/src/journal/mmap-cache.h
+index 76e5316..fe2c83d 100644
+--- a/src/journal/mmap-cache.h
++++ b/src/journal/mmap-cache.h
+@@ -25,6 +25,8 @@
+ #include <stdbool.h>
+ #include <sys/stat.h>
+ 
++#define MMAP_CACHE_MAX_CONTEXTS 8
++
+ typedef struct MMapCache MMapCache;
+ 
+ MMapCache* mmap_cache_new(void);
+@@ -40,14 +42,8 @@ int mmap_cache_get(
+         uint64_t offset,
+         size_t size,
+         struct stat *st,
+-        void **ret,
+-        void **release_cookie);
+-int mmap_cache_release(
+-        MMapCache *m,
+-        int fd,
+-        void *release_cookie);
++        void **ret);
+ void mmap_cache_close_fd(MMapCache *m, int fd);
+-void mmap_cache_close_context(MMapCache *m, unsigned context);
+ 
+ unsigned mmap_cache_get_hit(MMapCache *m);
+ unsigned mmap_cache_get_missed(MMapCache *m);
+diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c
+index cf21c4d..cb7fc32 100644
+--- a/src/journal/sd-journal.c
++++ b/src/journal/sd-journal.c
+@@ -87,7 +87,7 @@ static void detach_location(sd_journal *j) {
+         j->current_field = 0;
+ 
+         ORDERED_HASHMAP_FOREACH(f, j->files, i)
+-                f->current_offset = 0;
++                journal_file_reset_location(f);
+ }
+ 
+ static void reset_location(sd_journal *j) {
+@@ -114,20 +114,19 @@ static void init_location(Location *l, LocationType type, JournalFile *f, Object
+         l->seqnum_set = l->realtime_set = l->monotonic_set = l->xor_hash_set = true;
+ }
+ 
+-static void set_location(sd_journal *j, LocationType type, JournalFile *f, Object *o,
+-                         direction_t direction, uint64_t offset) {
++static void set_location(sd_journal *j, JournalFile *f, Object *o) {
+         assert(j);
+-        assert(type == LOCATION_DISCRETE || type == LOCATION_SEEK);
+         assert(f);
+         assert(o);
+ 
+-        init_location(&j->current_location, type, f, o);
++        init_location(&j->current_location, LOCATION_DISCRETE, f, o);
+ 
+         j->current_file = f;
+         j->current_field = 0;
+ 
+-        f->last_direction = direction;
+-        f->current_offset = offset;
++        /* Let f know its candidate entry was picked. */
++        assert(f->location_type == LOCATION_SEEK);
++        f->location_type = LOCATION_DISCRETE;
+ }
+ 
+ static int match_is_valid(const void *data, size_t size) {
+@@ -413,144 +412,51 @@ _public_ void sd_journal_flush_matches(sd_journal *j) {
+         detach_location(j);
+ }
+ 
+-static int compare_entry_order(JournalFile *af, Object *_ao,
+-                               JournalFile *bf, uint64_t bp) {
+-
+-        uint64_t a, b;
+-        Object *ao, *bo;
+-        int r;
+-
+-        assert(af);
+-        assert(bf);
+-        assert(_ao);
+-
+-        /* The mmap cache might invalidate the object from the first
+-         * file if we look at the one from the second file. Hence
+-         * temporarily copy the header of the first one, and look at
+-         * that only. */
+-        ao = alloca(offsetof(EntryObject, items));
+-        memcpy(ao, _ao, offsetof(EntryObject, items));
+-
+-        r = journal_file_move_to_object(bf, OBJECT_ENTRY, bp, &bo);
+-        if (r < 0)
+-                return strcmp(af->path, bf->path);
+-
+-        /* We operate on two different files here, hence we can access
+-         * two objects at the same time, which we normally can't.
+-         *
+-         * If contents and timestamps match, these entries are
+-         * identical, even if the seqnum does not match */
+-
+-        if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id) &&
+-            ao->entry.monotonic == bo->entry.monotonic &&
+-            ao->entry.realtime == bo->entry.realtime &&
+-            ao->entry.xor_hash == bo->entry.xor_hash)
+-                return 0;
+-
+-        if (sd_id128_equal(af->header->seqnum_id, bf->header->seqnum_id)) {
+-
+-                /* If this is from the same seqnum source, compare
+-                 * seqnums */
+-                a = le64toh(ao->entry.seqnum);
+-                b = le64toh(bo->entry.seqnum);
+-
+-                if (a < b)
+-                        return -1;
+-                if (a > b)
+-                        return 1;
+-
+-                /* Wow! This is weird, different data but the same
+-                 * seqnums? Something is borked, but let's make the
+-                 * best of it and compare by time. */
+-        }
+-
+-        if (sd_id128_equal(ao->entry.boot_id, bo->entry.boot_id)) {
+-
+-                /* If the boot id matches, compare monotonic time */
+-                a = le64toh(ao->entry.monotonic);
+-                b = le64toh(bo->entry.monotonic);
+-
+-                if (a < b)
+-                        return -1;
+-                if (a > b)
+-                        return 1;
+-        }
+-
+-        /* Otherwise, compare UTC time */
+-        a = le64toh(ao->entry.realtime);
+-        b = le64toh(bo->entry.realtime);
+-
+-        if (a < b)
+-                return -1;
+-        if (a > b)
+-                return 1;
+-
+-        /* Finally, compare by contents */
+-        a = le64toh(ao->entry.xor_hash);
+-        b = le64toh(bo->entry.xor_hash);
+-
+-        if (a < b)
+-                return -1;
+-        if (a > b)
+-                return 1;
+-
+-        return 0;
+-}
+-
+-_pure_ static int compare_with_location(JournalFile *af, Object *ao, Location *l) {
+-        uint64_t a;
+-
+-        assert(af);
+-        assert(ao);
++_pure_ static int compare_with_location(JournalFile *f, Location *l) {
++        assert(f);
+         assert(l);
++        assert(f->location_type == LOCATION_SEEK);
+         assert(l->type == LOCATION_DISCRETE || l->type == LOCATION_SEEK);
+ 
+         if (l->monotonic_set &&
+-            sd_id128_equal(ao->entry.boot_id, l->boot_id) &&
++            sd_id128_equal(f->current_boot_id, l->boot_id) &&
+             l->realtime_set &&
+-            le64toh(ao->entry.realtime) == l->realtime &&
++            f->current_realtime == l->realtime &&
+             l->xor_hash_set &&
+-            le64toh(ao->entry.xor_hash) == l->xor_hash)
++            f->current_xor_hash == l->xor_hash)
+                 return 0;
+ 
+         if (l->seqnum_set &&
+-            sd_id128_equal(af->header->seqnum_id, l->seqnum_id)) {
+-
+-                a = le64toh(ao->entry.seqnum);
++            sd_id128_equal(f->header->seqnum_id, l->seqnum_id)) {
+ 
+-                if (a < l->seqnum)
++                if (f->current_seqnum < l->seqnum)
+                         return -1;
+-                if (a > l->seqnum)
++                if (f->current_seqnum > l->seqnum)
+                         return 1;
+         }
+ 
+         if (l->monotonic_set &&
+-            sd_id128_equal(ao->entry.boot_id, l->boot_id)) {
++            sd_id128_equal(f->current_boot_id, l->boot_id)) {
+ 
+-                a = le64toh(ao->entry.monotonic);
+-
+-                if (a < l->monotonic)
++                if (f->current_monotonic < l->monotonic)
+                         return -1;
+-                if (a > l->monotonic)
++                if (f->current_monotonic > l->monotonic)
+                         return 1;
+         }
+ 
+         if (l->realtime_set) {
+ 
+-                a = le64toh(ao->entry.realtime);
+-
+-                if (a < l->realtime)
++                if (f->current_realtime < l->realtime)
+                         return -1;
+-                if (a > l->realtime)
++                if (f->current_realtime > l->realtime)
+                         return 1;
+         }
+ 
+         if (l->xor_hash_set) {
+-                a = le64toh(ao->entry.xor_hash);
+ 
+-                if (a < l->xor_hash)
++                if (f->current_xor_hash < l->xor_hash)
+                         return -1;
+-                if (a > l->xor_hash)
++                if (f->current_xor_hash > l->xor_hash)
+                         return 1;
+         }
+ 
+@@ -766,9 +672,9 @@ static int find_location_with_matches(
+                 /* No matches is simple */
+ 
+                 if (j->current_location.type == LOCATION_HEAD)
+-                        return journal_file_next_entry(f, NULL, 0, DIRECTION_DOWN, ret, offset);
++                        return journal_file_next_entry(f, 0, DIRECTION_DOWN, ret, offset);
+                 if (j->current_location.type == LOCATION_TAIL)
+-                        return journal_file_next_entry(f, NULL, 0, DIRECTION_UP, ret, offset);
++                        return journal_file_next_entry(f, 0, DIRECTION_UP, ret, offset);
+                 if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id))
+                         return journal_file_move_to_entry_by_seqnum(f, j->current_location.seqnum, direction, ret, offset);
+                 if (j->current_location.monotonic_set) {
+@@ -779,7 +685,7 @@ static int find_location_with_matches(
+                 if (j->current_location.realtime_set)
+                         return journal_file_move_to_entry_by_realtime(f, j->current_location.realtime, direction, ret, offset);
+ 
+-                return journal_file_next_entry(f, NULL, 0, direction, ret, offset);
++                return journal_file_next_entry(f, 0, direction, ret, offset);
+         } else
+                 return find_location_for_match(j, j->level0, f, direction, ret, offset);
+ }
+@@ -791,49 +697,61 @@ static int next_with_matches(
+                 Object **ret,
+                 uint64_t *offset) {
+ 
+-        Object *c;
+-        uint64_t cp;
+-
+         assert(j);
+         assert(f);
+         assert(ret);
+         assert(offset);
+ 
+-        c = *ret;
+-        cp = *offset;
+-
+         /* No matches is easy. We simple advance the file
+          * pointer by one. */
+         if (!j->level0)
+-                return journal_file_next_entry(f, c, cp, direction, ret, offset);
++                return journal_file_next_entry(f, f->current_offset, direction, ret, offset);
+ 
+         /* If we have a match then we look for the next matching entry
+          * with an offset at least one step larger */
+-        return next_for_match(j, j->level0, f, direction == DIRECTION_DOWN ? cp+1 : cp-1, direction, ret, offset);
++        return next_for_match(j, j->level0, f,
++                              direction == DIRECTION_DOWN ? f->current_offset + 1
++                                                          : f->current_offset - 1,
++                              direction, ret, offset);
+ }
+ 
+-static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direction, Object **ret, uint64_t *offset) {
++static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direction) {
+         Object *c;
+-        uint64_t cp;
++        uint64_t cp, n_entries;
+         int r;
+ 
+         assert(j);
+         assert(f);
+ 
+-        if (f->last_direction == direction && f->current_offset > 0) {
+-                cp = f->current_offset;
++        n_entries = le64toh(f->header->n_entries);
+ 
+-                r = journal_file_move_to_object(f, OBJECT_ENTRY, cp, &c);
+-                if (r < 0)
+-                        return r;
++        /* If we hit EOF before, we don't need to look into this file again
++         * unless direction changed or new entries appeared. */
++        if (f->last_direction == direction && f->location_type == LOCATION_TAIL &&
++            n_entries == f->last_n_entries)
++                return 0;
+ 
+-                r = next_with_matches(j, f, direction, &c, &cp);
+-                if (r <= 0)
+-                        return r;
++        f->last_n_entries = n_entries;
++
++        if (f->last_direction == direction && f->current_offset > 0) {
++                /* LOCATION_SEEK here means we did the work in a previous
++                 * iteration and the current location already points to a
++                 * candidate entry. */
++                if (f->location_type != LOCATION_SEEK) {
++                        r = next_with_matches(j, f, direction, &c, &cp);
++                        if (r <= 0)
++                                return r;
++
++                        journal_file_save_location(f, c, cp);
++                }
+         } else {
++                f->last_direction = direction;
++
+                 r = find_location_with_matches(j, f, direction, &c, &cp);
+                 if (r <= 0)
+                         return r;
++
++                journal_file_save_location(f, c, cp);
+         }
+ 
+         /* OK, we found the spot, now let's advance until an entry
+@@ -848,30 +766,25 @@ static int next_beyond_location(sd_journal *j, JournalFile *f, direction_t direc
+                 if (j->current_location.type == LOCATION_DISCRETE) {
+                         int k;
+ 
+-                        k = compare_with_location(f, c, &j->current_location);
++                        k = compare_with_location(f, &j->current_location);
+ 
+                         found = direction == DIRECTION_DOWN ? k > 0 : k < 0;
+                 } else
+                         found = true;
+ 
+-                if (found) {
+-                        if (ret)
+-                                *ret = c;
+-                        if (offset)
+-                                *offset = cp;
++                if (found)
+                         return 1;
+-                }
+ 
+                 r = next_with_matches(j, f, direction, &c, &cp);
+                 if (r <= 0)
+                         return r;
++
++                journal_file_save_location(f, c, cp);
+         }
+ }
+ 
+ static int real_journal_next(sd_journal *j, direction_t direction) {
+         JournalFile *f, *new_file = NULL;
+-        uint64_t new_offset = 0;
+-        uint64_t p = 0;
+         Iterator i;
+         Object *o;
+         int r;
+@@ -882,38 +795,38 @@ static int real_journal_next(sd_journal *j, direction_t direction) {
+         ORDERED_HASHMAP_FOREACH(f, j->files, i) {
+                 bool found;
+ 
+-                r = next_beyond_location(j, f, direction, &o, &p);
++                r = next_beyond_location(j, f, direction);
+                 if (r < 0) {
+                         log_debug("Can't iterate through %s, ignoring: %s", f->path, strerror(-r));
+                         remove_file_real(j, f);
+                         continue;
+-                } else if (r == 0)
++                } else if (r == 0) {
++                        f->location_type = LOCATION_TAIL;
+                         continue;
++                }
+ 
+                 if (!new_file)
+                         found = true;
+                 else {
+                         int k;
+ 
+-                        k = compare_entry_order(f, o, new_file, new_offset);
++                        k = journal_file_compare_locations(f, new_file);
+ 
+                         found = direction == DIRECTION_DOWN ? k < 0 : k > 0;
+                 }
+ 
+-                if (found) {
++                if (found)
+                         new_file = f;
+-                        new_offset = p;
+-                }
+         }
+ 
+         if (!new_file)
+                 return 0;
+ 
+-        r = journal_file_move_to_object(new_file, OBJECT_ENTRY, new_offset, &o);
++        r = journal_file_move_to_object(new_file, OBJECT_ENTRY, new_file->current_offset, &o);
+         if (r < 0)
+                 return r;
+ 
+-        set_location(j, LOCATION_DISCRETE, new_file, o, direction, new_offset);
++        set_location(j, new_file, o);
+ 
+         return 1;
+ }
+@@ -2526,7 +2439,6 @@ _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_
+                 size_t ol;
+                 bool found;
+                 int r;
+-                void *release_cookie;
+ 
+                 /* Proceed to next data object in the field's linked list */
+                 if (j->unique_offset == 0) {
+@@ -2552,10 +2464,10 @@ _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_
+                         continue;
+                 }
+ 
+-                /* We do not use the type context here, but 0 instead,
+-                 * so that we can look at this data object at the same
++                /* We do not use OBJECT_DATA context here, but OBJECT_UNUSED
++                 * instead, so that we can look at this data object at the same
+                  * time as one on another file */
+-                r = journal_file_move_to_object(j->unique_file, 0, j->unique_offset, &o);
++                r = journal_file_move_to_object(j->unique_file, OBJECT_UNUSED, j->unique_offset, &o);
+                 if (r < 0)
+                         return r;
+ 
+@@ -2567,10 +2479,6 @@ _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_
+                         return -EBADMSG;
+                 }
+ 
+-                r = journal_file_object_keep(j->unique_file, o, j->unique_offset, &release_cookie);
+-                if (r < 0)
+-                        return r;
+-
+                 r = return_data(j, j->unique_file, o, &odata, &ol);
+                 if (r < 0)
+                         return r;
+@@ -2615,10 +2523,6 @@ _public_ int sd_journal_enumerate_unique(sd_journal *j, const void **data, size_
+                                 found = true;
+                 }
+ 
+-                r = journal_file_object_release(j->unique_file, release_cookie);
+-                if (r < 0)
+-                        return r;
+-
+                 if (found)
+                         continue;
+ 
 diff --git a/src/libsystemd-network/network-internal.c b/src/libsystemd-network/network-internal.c
 index 372f3ed..d56ee51 100644
 --- a/src/libsystemd-network/network-internal.c
@@ -875,6 +2206,30 @@ index 28eaa6a..3866308 100644
          reboot(RB_ENABLE_CAD);
  
          switch (a) {
+diff --git a/src/systemd/sd-journal.h b/src/systemd/sd-journal.h
+index eb24372..00237a2 100644
+--- a/src/systemd/sd-journal.h
++++ b/src/systemd/sd-journal.h
+@@ -138,13 +138,15 @@ int sd_journal_reliable_fd(sd_journal *j);
+ int sd_journal_get_catalog(sd_journal *j, char **text);
+ int sd_journal_get_catalog_for_message_id(sd_id128_t id, char **text);
+ 
++/* the inverse condition avoids ambiguity of danling 'else' after the macro */
+ #define SD_JOURNAL_FOREACH(j)                                           \
+-        if (sd_journal_seek_head(j) >= 0)                               \
+-                while (sd_journal_next(j) > 0)
++        if (sd_journal_seek_head(j) < 0) { }                            \
++        else while (sd_journal_next(j) > 0)
+ 
++/* the inverse condition avoids ambiguity of danling 'else' after the macro */
+ #define SD_JOURNAL_FOREACH_BACKWARDS(j)                                 \
+-        if (sd_journal_seek_tail(j) >= 0)                               \
+-                while (sd_journal_previous(j) > 0)
++        if (sd_journal_seek_tail(j) < 0) { }                            \
++        else while (sd_journal_previous(j) > 0)
+ 
+ #define SD_JOURNAL_FOREACH_DATA(j, data, l)                             \
+         for (sd_journal_restart_data(j); sd_journal_enumerate_data((j), &(data), &(l)) > 0; )
 diff --git a/src/test/test-path-util.c b/src/test/test-path-util.c
 index 63d64b2..57264de 100644
 --- a/src/test/test-path-util.c