* be replayed:
  */
 static int journal_entry_add(struct bch_fs *c, struct bch_dev *ca,
+                            struct bch_extent_ptr entry_ptr,
                             struct journal_list *jlist, struct jset *j,
                             bool bad)
 {
-       struct journal_replay *i, *pos;
-       struct bch_devs_list devs = { .nr = 0 };
+       struct journal_replay *i, *pos, *dup = NULL;
+       struct bch_extent_ptr *ptr;
        struct list_head *where;
        size_t bytes = vstruct_bytes(j);
        u64 last_seq = 0;
-       int ret;
+       int ret = JOURNAL_ENTRY_ADD_OK;
 
        list_for_each_entry_reverse(i, jlist->head, list) {
                if (!JSET_NO_FLUSH(&i->j)) {
 
        where = jlist->head;
 add:
-       i = where->next != jlist->head
+       dup = where->next != jlist->head
                ? container_of(where->next, struct journal_replay, list)
                : NULL;
 
+       if (dup && le64_to_cpu(j->seq) != le64_to_cpu(dup->j.seq))
+               dup = NULL;
+
        /*
         * Duplicate journal entries? If so we want the one that didn't have a
         * checksum error:
         */
-       if (i && le64_to_cpu(j->seq) == le64_to_cpu(i->j.seq)) {
-               if (i->bad) {
-                       devs = i->devs;
-                       __journal_replay_free(i);
+       if (dup) {
+               if (dup->bad) {
+                       /* we'll replace @dup: */
                } else if (bad) {
+                       i = dup;
                        goto found;
                } else {
-                       fsck_err_on(bytes != vstruct_bytes(&i->j) ||
-                                   memcmp(j, &i->j, bytes), c,
+                       fsck_err_on(bytes != vstruct_bytes(&dup->j) ||
+                                   memcmp(j, &dup->j, bytes), c,
                                    "found duplicate but non identical journal entries (seq %llu)",
                                    le64_to_cpu(j->seq));
+                       i = dup;
                        goto found;
                }
-
        }
 
        i = kvpmalloc(offsetof(struct journal_replay, j) + bytes, GFP_KERNEL);
                goto out;
        }
 
-       list_add(&i->list, where);
-       i->devs = devs;
-       i->bad  = bad;
-       i->ignore = false;
+       i->nr_ptrs       = 0;
+       i->bad          = bad;
+       i->ignore       = false;
        unsafe_memcpy(&i->j, j, bytes, "embedded variable length struct");
+
+       if (dup) {
+               i->nr_ptrs = dup->nr_ptrs;
+               memcpy(i->ptrs, dup->ptrs, sizeof(dup->ptrs));
+               __journal_replay_free(dup);
+       }
+
+       list_add(&i->list, where);
 found:
-       if (!bch2_dev_list_has_dev(i->devs, ca->dev_idx))
-               bch2_dev_list_add_dev(&i->devs, ca->dev_idx);
-       else
-               fsck_err_on(1, c, "duplicate journal entries on same device");
-       ret = JOURNAL_ENTRY_ADD_OK;
+       for (ptr = i->ptrs; ptr < i->ptrs + i->nr_ptrs; ptr++) {
+               if (ptr->dev == ca->dev_idx) {
+                       bch_err(c, "duplicate journal entry %llu on same device",
+                               le64_to_cpu(i->j.seq));
+                       goto out;
+               }
+       }
+
+       if (i->nr_ptrs >= ARRAY_SIZE(i->ptrs)) {
+               bch_err(c, "found too many copies of journal entry %llu",
+                       le64_to_cpu(i->j.seq));
+               goto out;
+       }
+
+       i->ptrs[i->nr_ptrs++] = entry_ptr;
 out:
 fsck_err:
        return ret;
                ja->bucket_seq[bucket] = le64_to_cpu(j->seq);
 
                mutex_lock(&jlist->lock);
-               ret = journal_entry_add(c, ca, jlist, j, ret != 0);
+               ret = journal_entry_add(c, ca, (struct bch_extent_ptr) {
+                                       .dev = ca->dev_idx,
+                                       .offset = offset,
+                                       }, jlist, j, ret != 0);
                mutex_unlock(&jlist->lock);
 
                switch (ret) {
        goto out;
 }
 
+static void bch2_journal_ptrs_to_text(struct printbuf *out, struct bch_fs *c,
+                                     struct journal_replay *j)
+{
+       unsigned i;
+
+       for (i = 0; i < j->nr_ptrs; i++) {
+               struct bch_dev *ca = c->devs[j->ptrs[i].dev];
+
+               if (i)
+                       pr_buf(out, " ");
+               pr_buf(out, "%u:%llu (offset %llu)",
+                      j->ptrs[i].dev,
+                      (u64) j->ptrs[i].offset,
+                      (u64) j->ptrs[i].offset % ca->mi.bucket_size);
+       }
+}
+
 int bch2_journal_read(struct bch_fs *c, struct list_head *list,
                      u64 *blacklist_seq, u64 *start_seq)
 {
 
                while (seq < le64_to_cpu(i->j.seq)) {
                        u64 missing_start, missing_end;
+                       char buf1[200], buf2[200];
 
                        while (seq < le64_to_cpu(i->j.seq) &&
                               bch2_journal_seq_is_blacklisted(c, seq, false))
                               !bch2_journal_seq_is_blacklisted(c, seq, false))
                                seq++;
 
+                       if (i->list.prev != list) {
+                               struct printbuf out = PBUF(buf1);
+                               struct journal_replay *p = list_prev_entry(i, list);
+
+                               bch2_journal_ptrs_to_text(&out, c, p);
+                               pr_buf(&out, " size %llu", vstruct_sectors(&p->j, c->block_bits));
+                       } else
+                               sprintf(buf1, "(none)");
+                       bch2_journal_ptrs_to_text(&PBUF(buf2), c, i);
+
                        missing_end = seq - 1;
-                       fsck_err(c, "journal entries %llu-%llu missing! (replaying %llu-%llu)",
+                       fsck_err(c, "journal entries %llu-%llu missing! (replaying %llu-%llu)\n"
+                                "  prev at %s\n"
+                                "  next at %s",
                                 missing_start, missing_end,
-                                last_seq, *blacklist_seq - 1);
+                                last_seq, *blacklist_seq - 1,
+                                buf1, buf2);
                }
 
                seq++;
        list_for_each_entry(i, list, list) {
                struct jset_entry *entry;
                struct bkey_i *k, *_n;
-               struct bch_replicas_padded replicas;
+               struct bch_replicas_padded replicas = {
+                       .e.data_type = BCH_DATA_journal,
+                       .e.nr_required = 1,
+               };
+               unsigned ptr;
                char buf[80];
 
                if (i->ignore)
                if (ret)
                        goto fsck_err;
 
+               for (ptr = 0; ptr < i->nr_ptrs; ptr++)
+                       replicas.e.devs[replicas.e.nr_devs++] = i->ptrs[ptr].dev;
+
                /*
                 * If we're mounting in degraded mode - if we didn't read all
                 * the devices - this is wrong:
                 */
 
-               bch2_devlist_to_replicas(&replicas.e, BCH_DATA_journal, i->devs);
-
                if (!degraded &&
                    (test_bit(BCH_FS_REBUILD_REPLICAS, &c->flags) ||
                     fsck_err_on(!bch2_replicas_marked(c, &replicas.e), c,