]> git.sesse.net Git - bcachefs-tools-debian/blobdiff - libbcachefs/journal.c
Add a subcommand for resizing the journal
[bcachefs-tools-debian] / libbcachefs / journal.c
index 0a4538b3dc604e206ba5057d17a5517fc35e53af..1b3f249bc3e4211019996ac38538b02fcbaa6534 100644 (file)
 
 #include <trace/events/bcachefs.h>
 
+static u64 last_unwritten_seq(struct journal *j)
+{
+       union journal_res_state s = READ_ONCE(j->reservations);
+
+       lockdep_assert_held(&j->lock);
+
+       return journal_cur_seq(j) - s.prev_buf_unwritten;
+}
+
+static inline bool journal_seq_unwritten(struct journal *j, u64 seq)
+{
+       return seq >= last_unwritten_seq(j);
+}
+
 static bool __journal_entry_is_open(union journal_res_state state)
 {
        return state.cur_entry_offset < JOURNAL_ENTRY_CLOSED_VAL;
@@ -28,6 +42,22 @@ static bool journal_entry_is_open(struct journal *j)
        return __journal_entry_is_open(j->reservations);
 }
 
+static inline struct journal_buf *
+journal_seq_to_buf(struct journal *j, u64 seq)
+{
+       struct journal_buf *buf = NULL;
+
+       EBUG_ON(seq > journal_cur_seq(j));
+       EBUG_ON(seq == journal_cur_seq(j) &&
+               j->reservations.cur_entry_offset == JOURNAL_ENTRY_CLOSED_VAL);
+
+       if (journal_seq_unwritten(j, seq)) {
+               buf = j->buf + (seq & 1);
+               EBUG_ON(le64_to_cpu(buf->data->seq) != seq);
+       }
+       return buf;
+}
+
 static void journal_pin_new_entry(struct journal *j, int count)
 {
        struct journal_entry_pin_list *p;
@@ -49,6 +79,8 @@ static void bch2_journal_buf_init(struct journal *j)
 {
        struct journal_buf *buf = journal_cur_buf(j);
 
+       bkey_extent_init(&buf->key);
+
        memset(buf->has_inode, 0, sizeof(buf->has_inode));
 
        memset(buf->data, 0, sizeof(*buf->data));
@@ -70,6 +102,7 @@ void bch2_journal_halt(struct journal *j)
        } while ((v = atomic64_cmpxchg(&j->reservations.counter,
                                       old.v, new.v)) != old.v);
 
+       j->err_seq = journal_cur_seq(j);
        journal_wake(j);
        closure_wake_up(&journal_cur_buf(j)->wait);
 }
@@ -137,8 +170,6 @@ static bool __journal_entry_close(struct journal *j)
        BUG_ON(sectors > buf->sectors);
        buf->sectors = sectors;
 
-       bkey_extent_init(&buf->key);
-
        /*
         * We have to set last_seq here, _before_ opening a new journal entry:
         *
@@ -160,11 +191,6 @@ static bool __journal_entry_close(struct journal *j)
         */
        buf->data->last_seq     = cpu_to_le64(journal_last_seq(j));
 
-       if (journal_entry_empty(buf->data))
-               clear_bit(JOURNAL_NOT_EMPTY, &j->flags);
-       else
-               set_bit(JOURNAL_NOT_EMPTY, &j->flags);
-
        journal_pin_new_entry(j, 1);
 
        bch2_journal_buf_init(j);
@@ -305,6 +331,19 @@ u64 bch2_inode_journal_seq(struct journal *j, u64 inode)
        return seq;
 }
 
+void bch2_journal_set_has_inum(struct journal *j, u64 inode, u64 seq)
+{
+       size_t h = hash_64(inode, ilog2(sizeof(j->buf[0].has_inode) * 8));
+       struct journal_buf *buf;
+
+       spin_lock(&j->lock);
+
+       if ((buf = journal_seq_to_buf(j, seq)))
+               set_bit(h, buf->has_inode);
+
+       spin_unlock(&j->lock);
+}
+
 static int __journal_res_get(struct journal *j, struct journal_res *res,
                             unsigned flags)
 {
@@ -376,8 +415,17 @@ unlock:
                goto retry;
 
        if (ret == -ENOSPC) {
-               WARN_ONCE(!can_discard && (flags & JOURNAL_RES_GET_RESERVED),
-                         "JOURNAL_RES_GET_RESERVED set but journal full");
+               if (WARN_ONCE(!can_discard && (flags & JOURNAL_RES_GET_RESERVED),
+                             "JOURNAL_RES_GET_RESERVED set but journal full")) {
+                       char *buf;
+
+                       buf = kmalloc(4096, GFP_NOFS);
+                       if (buf) {
+                               bch2_journal_debug_to_text(&PBUF(buf), j);
+                               pr_err("\n%s", buf);
+                               kfree(buf);
+                       }
+               }
 
                /*
                 * Journal is full - can't rely on reclaim from work item due to
@@ -428,9 +476,10 @@ int bch2_journal_res_get_slowpath(struct journal *j, struct journal_res *res,
 
 static bool journal_preres_available(struct journal *j,
                                     struct journal_preres *res,
-                                    unsigned new_u64s)
+                                    unsigned new_u64s,
+                                    unsigned flags)
 {
-       bool ret = bch2_journal_preres_get_fast(j, res, new_u64s);
+       bool ret = bch2_journal_preres_get_fast(j, res, new_u64s, flags);
 
        if (!ret)
                bch2_journal_reclaim_work(&j->reclaim_work.work);
@@ -440,13 +489,14 @@ static bool journal_preres_available(struct journal *j,
 
 int __bch2_journal_preres_get(struct journal *j,
                              struct journal_preres *res,
-                             unsigned new_u64s)
+                             unsigned new_u64s,
+                             unsigned flags)
 {
        int ret;
 
        closure_wait_event(&j->preres_wait,
                   (ret = bch2_journal_error(j)) ||
-                  journal_preres_available(j, res, new_u64s));
+                  journal_preres_available(j, res, new_u64s, flags));
        return ret;
 }
 
@@ -486,146 +536,28 @@ out:
 
 /* journal flushing: */
 
-u64 bch2_journal_last_unwritten_seq(struct journal *j)
-{
-       u64 seq;
-
-       spin_lock(&j->lock);
-       seq = journal_cur_seq(j);
-       if (j->reservations.prev_buf_unwritten)
-               seq--;
-       spin_unlock(&j->lock);
-
-       return seq;
-}
-
-/**
- * bch2_journal_open_seq_async - try to open a new journal entry if @seq isn't
- * open yet, or wait if we cannot
- *
- * used by the btree interior update machinery, when it needs to write a new
- * btree root - every journal entry contains the roots of all the btrees, so it
- * doesn't need to bother with getting a journal reservation
- */
-int bch2_journal_open_seq_async(struct journal *j, u64 seq, struct closure *cl)
-{
-       struct bch_fs *c = container_of(j, struct bch_fs, journal);
-       int ret;
-
-       spin_lock(&j->lock);
-
-       /*
-        * Can't try to open more than one sequence number ahead:
-        */
-       BUG_ON(journal_cur_seq(j) < seq && !journal_entry_is_open(j));
-
-       if (journal_cur_seq(j) > seq ||
-           journal_entry_is_open(j)) {
-               spin_unlock(&j->lock);
-               return 0;
-       }
-
-       if (journal_cur_seq(j) < seq &&
-           !__journal_entry_close(j)) {
-               /* haven't finished writing out the previous one: */
-               trace_journal_entry_full(c);
-               ret = -EAGAIN;
-       } else {
-               BUG_ON(journal_cur_seq(j) != seq);
-
-               ret = journal_entry_open(j);
-       }
-
-       if ((ret == -EAGAIN || ret == -ENOSPC) &&
-           !j->res_get_blocked_start)
-               j->res_get_blocked_start = local_clock() ?: 1;
-
-       if (ret == -EAGAIN || ret == -ENOSPC)
-               closure_wait(&j->async_wait, cl);
-
-       spin_unlock(&j->lock);
-
-       if (ret == -ENOSPC) {
-               trace_journal_full(c);
-               bch2_journal_reclaim_work(&j->reclaim_work.work);
-               ret = -EAGAIN;
-       }
-
-       return ret;
-}
-
-static int journal_seq_error(struct journal *j, u64 seq)
-{
-       union journal_res_state state = READ_ONCE(j->reservations);
-
-       if (seq == journal_cur_seq(j))
-               return bch2_journal_error(j);
-
-       if (seq + 1 == journal_cur_seq(j) &&
-           !state.prev_buf_unwritten &&
-           seq > j->seq_ondisk)
-               return -EIO;
-
-       return 0;
-}
-
-static inline struct journal_buf *
-journal_seq_to_buf(struct journal *j, u64 seq)
-{
-       /* seq should be for a journal entry that has been opened: */
-       BUG_ON(seq > journal_cur_seq(j));
-       BUG_ON(seq == journal_cur_seq(j) &&
-              j->reservations.cur_entry_offset == JOURNAL_ENTRY_CLOSED_VAL);
-
-       if (seq == journal_cur_seq(j))
-               return journal_cur_buf(j);
-       if (seq + 1 == journal_cur_seq(j) &&
-           j->reservations.prev_buf_unwritten)
-               return journal_prev_buf(j);
-       return NULL;
-}
-
-/**
- * bch2_journal_wait_on_seq - wait for a journal entry to be written
- *
- * does _not_ cause @seq to be written immediately - if there is no other
- * activity to cause the relevant journal entry to be filled up or flushed it
- * can wait for an arbitrary amount of time (up to @j->write_delay_ms, which is
- * configurable).
- */
-void bch2_journal_wait_on_seq(struct journal *j, u64 seq,
-                             struct closure *parent)
-{
-       struct journal_buf *buf;
-
-       spin_lock(&j->lock);
-
-       if ((buf = journal_seq_to_buf(j, seq))) {
-               if (!closure_wait(&buf->wait, parent))
-                       BUG();
-
-               if (seq == journal_cur_seq(j)) {
-                       smp_mb();
-                       if (bch2_journal_error(j))
-                               closure_wake_up(&buf->wait);
-               }
-       }
-
-       spin_unlock(&j->lock);
-}
-
 /**
  * bch2_journal_flush_seq_async - wait for a journal entry to be written
  *
  * like bch2_journal_wait_on_seq, except that it triggers a write immediately if
  * necessary
  */
-void bch2_journal_flush_seq_async(struct journal *j, u64 seq,
+int bch2_journal_flush_seq_async(struct journal *j, u64 seq,
                                  struct closure *parent)
 {
        struct journal_buf *buf;
+       int ret = 0;
 
        spin_lock(&j->lock);
+       if (seq <= j->err_seq) {
+               ret = -EIO;
+               goto out;
+       }
+
+       if (seq <= j->seq_ondisk) {
+               ret = 1;
+               goto out;
+       }
 
        if (parent &&
            (buf = journal_seq_to_buf(j, seq)))
@@ -634,20 +566,8 @@ void bch2_journal_flush_seq_async(struct journal *j, u64 seq,
 
        if (seq == journal_cur_seq(j))
                __journal_entry_close(j);
+out:
        spin_unlock(&j->lock);
-}
-
-static int journal_seq_flushed(struct journal *j, u64 seq)
-{
-       int ret;
-
-       spin_lock(&j->lock);
-       ret = seq <= j->seq_ondisk ? 1 : journal_seq_error(j, seq);
-
-       if (seq == journal_cur_seq(j))
-               __journal_entry_close(j);
-       spin_unlock(&j->lock);
-
        return ret;
 }
 
@@ -656,28 +576,13 @@ int bch2_journal_flush_seq(struct journal *j, u64 seq)
        u64 start_time = local_clock();
        int ret, ret2;
 
-       ret = wait_event_killable(j->wait, (ret2 = journal_seq_flushed(j, seq)));
+       ret = wait_event_killable(j->wait, (ret2 = bch2_journal_flush_seq_async(j, seq, NULL)));
 
        bch2_time_stats_update(j->flush_seq_time, start_time);
 
        return ret ?: ret2 < 0 ? ret2 : 0;
 }
 
-/**
- * bch2_journal_meta_async - force a journal entry to be written
- */
-void bch2_journal_meta_async(struct journal *j, struct closure *parent)
-{
-       struct journal_res res;
-
-       memset(&res, 0, sizeof(res));
-
-       bch2_journal_res_get(j, &res, jset_u64s(0), 0);
-       bch2_journal_res_put(j, &res);
-
-       bch2_journal_flush_seq_async(j, res.seq, parent);
-}
-
 int bch2_journal_meta(struct journal *j)
 {
        struct journal_res res;
@@ -780,7 +685,7 @@ static int __bch2_set_nr_journal_buckets(struct bch_dev *ca, unsigned nr,
                goto err;
 
        journal_buckets = bch2_sb_resize_journal(&ca->disk_sb,
-                                                nr + sizeof(*journal_buckets) / sizeof(u64));
+                                       nr + sizeof(*journal_buckets) / sizeof(u64));
        if (!journal_buckets)
                goto err;
 
@@ -826,6 +731,12 @@ static int __bch2_set_nr_journal_buckets(struct bch_dev *ca, unsigned nr,
                        spin_lock(&c->journal.lock);
                }
 
+               /*
+                * XXX
+                * For resize at runtime, we should be writing the new
+                * superblock before inserting into the journal array
+                */
+
                pos = ja->nr ? (ja->cur_idx + 1) % ja->nr : 0;
                __array_insert_item(ja->buckets,                ja->nr, pos);
                __array_insert_item(ja->bucket_seq,             ja->nr, pos);
@@ -845,7 +756,7 @@ static int __bch2_set_nr_journal_buckets(struct bch_dev *ca, unsigned nr,
                if (pos <= ja->cur_idx)
                        ja->cur_idx = (ja->cur_idx + 1) % ja->nr;
 
-               bch2_mark_metadata_bucket(c, ca, bucket, BCH_DATA_JOURNAL,
+               bch2_mark_metadata_bucket(c, ca, bucket, BCH_DATA_journal,
                                          ca->mi.bucket_size,
                                          gc_phase(GC_PHASE_SB),
                                          0);
@@ -861,6 +772,8 @@ static int __bch2_set_nr_journal_buckets(struct bch_dev *ca, unsigned nr,
 
        ret = 0;
 err:
+       bch2_sb_resize_journal(&ca->disk_sb,
+               ja->nr + sizeof(*journal_buckets) / sizeof(u64));
        kfree(new_bucket_seq);
        kfree(new_buckets);
 
@@ -959,21 +872,21 @@ void bch2_dev_journal_stop(struct journal *j, struct bch_dev *ca)
 
 void bch2_fs_journal_stop(struct journal *j)
 {
-       struct bch_fs *c = container_of(j, struct bch_fs, journal);
-
        bch2_journal_flush_all_pins(j);
 
        wait_event(j->wait, journal_entry_close(j));
 
-       /* do we need to write another journal entry? */
-       if (test_bit(JOURNAL_NOT_EMPTY, &j->flags) ||
-           c->btree_roots_dirty)
-               bch2_journal_meta(j);
+       /*
+        * Always write a new journal entry, to make sure the clock hands are up
+        * to date (and match the superblock)
+        */
+       bch2_journal_meta(j);
 
        journal_quiesce(j);
 
        BUG_ON(!bch2_journal_error(j) &&
-              test_bit(JOURNAL_NOT_EMPTY, &j->flags));
+              (journal_entry_is_open(j) ||
+               j->last_empty_seq + 1 != journal_cur_seq(j)));
 
        cancel_delayed_work_sync(&j->write_work);
        cancel_delayed_work_sync(&j->reclaim_work);
@@ -988,9 +901,8 @@ int bch2_fs_journal_start(struct journal *j, u64 cur_seq,
        u64 last_seq = cur_seq, nr, seq;
 
        if (!list_empty(journal_entries))
-               last_seq = le64_to_cpu(list_first_entry(journal_entries,
-                                                       struct journal_replay,
-                                                       list)->j.seq);
+               last_seq = le64_to_cpu(list_last_entry(journal_entries,
+                               struct journal_replay, list)->j.last_seq);
 
        nr = cur_seq - last_seq;
 
@@ -1019,8 +931,10 @@ int bch2_fs_journal_start(struct journal *j, u64 cur_seq,
 
        list_for_each_entry(i, journal_entries, list) {
                seq = le64_to_cpu(i->j.seq);
+               BUG_ON(seq >= cur_seq);
 
-               BUG_ON(seq < last_seq || seq >= cur_seq);
+               if (seq < last_seq)
+                       continue;
 
                journal_seq_pin(j, seq)->devs = i->devs;
        }
@@ -1030,6 +944,9 @@ int bch2_fs_journal_start(struct journal *j, u64 cur_seq,
        set_bit(JOURNAL_STARTED, &j->flags);
 
        journal_pin_new_entry(j, 1);
+
+       j->reservations.idx = journal_cur_seq(j);
+
        bch2_journal_buf_init(j);
 
        c->last_bucket_seq_cleanup = journal_cur_seq(j);
@@ -1135,9 +1052,8 @@ out:
 
 /* debug: */
 
-ssize_t bch2_journal_print_debug(struct journal *j, char *buf)
+void bch2_journal_debug_to_text(struct printbuf *out, struct journal *j)
 {
-       struct printbuf out = _PBUF(buf, PAGE_SIZE);
        struct bch_fs *c = container_of(j, struct bch_fs, journal);
        union journal_res_state s;
        struct bch_dev *ca;
@@ -1147,7 +1063,7 @@ ssize_t bch2_journal_print_debug(struct journal *j, char *buf)
        spin_lock(&j->lock);
        s = READ_ONCE(j->reservations);
 
-       pr_buf(&out,
+       pr_buf(out,
               "active journal entries:\t%llu\n"
               "seq:\t\t\t%llu\n"
               "last_seq:\t\t%llu\n"
@@ -1165,44 +1081,44 @@ ssize_t bch2_journal_print_debug(struct journal *j, char *buf)
 
        switch (s.cur_entry_offset) {
        case JOURNAL_ENTRY_ERROR_VAL:
-               pr_buf(&out, "error\n");
+               pr_buf(out, "error\n");
                break;
        case JOURNAL_ENTRY_CLOSED_VAL:
-               pr_buf(&out, "closed\n");
+               pr_buf(out, "closed\n");
                break;
        default:
-               pr_buf(&out, "%u/%u\n",
+               pr_buf(out, "%u/%u\n",
                       s.cur_entry_offset,
                       j->cur_entry_u64s);
                break;
        }
 
-       pr_buf(&out,
+       pr_buf(out,
               "current entry refs:\t%u\n"
               "prev entry unwritten:\t",
               journal_state_count(s, s.idx));
 
        if (s.prev_buf_unwritten)
-               pr_buf(&out, "yes, ref %u sectors %u\n",
+               pr_buf(out, "yes, ref %u sectors %u\n",
                       journal_state_count(s, !s.idx),
                       journal_prev_buf(j)->sectors);
        else
-               pr_buf(&out, "no\n");
+               pr_buf(out, "no\n");
 
-       pr_buf(&out,
+       pr_buf(out,
               "need write:\t\t%i\n"
               "replay done:\t\t%i\n",
               test_bit(JOURNAL_NEED_WRITE,     &j->flags),
               test_bit(JOURNAL_REPLAY_DONE,    &j->flags));
 
        for_each_member_device_rcu(ca, c, iter,
-                                  &c->rw_devs[BCH_DATA_JOURNAL]) {
+                                  &c->rw_devs[BCH_DATA_journal]) {
                struct journal_device *ja = &ca->journal;
 
                if (!ja->nr)
                        continue;
 
-               pr_buf(&out,
+               pr_buf(out,
                       "dev %u:\n"
                       "\tnr\t\t%u\n"
                       "\tavailable\t%u:%u\n"
@@ -1221,34 +1137,29 @@ ssize_t bch2_journal_print_debug(struct journal *j, char *buf)
 
        spin_unlock(&j->lock);
        rcu_read_unlock();
-
-       return out.pos - buf;
 }
 
-ssize_t bch2_journal_print_pins(struct journal *j, char *buf)
+void bch2_journal_pins_to_text(struct printbuf *out, struct journal *j)
 {
-       struct printbuf out = _PBUF(buf, PAGE_SIZE);
        struct journal_entry_pin_list *pin_list;
        struct journal_entry_pin *pin;
        u64 i;
 
        spin_lock(&j->lock);
        fifo_for_each_entry_ptr(pin_list, &j->pin, i) {
-               pr_buf(&out, "%llu: count %u\n",
+               pr_buf(out, "%llu: count %u\n",
                       i, atomic_read(&pin_list->count));
 
                list_for_each_entry(pin, &pin_list->list, list)
-                       pr_buf(&out, "\t%p %pf\n",
+                       pr_buf(out, "\t%px %ps\n",
                               pin, pin->flush);
 
                if (!list_empty(&pin_list->flushed))
-                       pr_buf(&out, "flushed:\n");
+                       pr_buf(out, "flushed:\n");
 
                list_for_each_entry(pin, &pin_list->flushed, list)
-                       pr_buf(&out, "\t%p %pf\n",
+                       pr_buf(out, "\t%px %ps\n",
                               pin, pin->flush);
        }
        spin_unlock(&j->lock);
-
-       return out.pos - buf;
 }