return ret;
 }
 
-static void journal_flush_pins(struct journal *j, u64 seq_to_flush,
+/* returns true if we did work */
+static bool journal_flush_pins(struct journal *j, u64 seq_to_flush,
                               unsigned min_nr)
 {
        struct journal_entry_pin *pin;
+       bool ret = false;
        u64 seq;
 
        lockdep_assert_held(&j->reclaim_lock);
                BUG_ON(j->flush_in_progress != pin);
                j->flush_in_progress = NULL;
                wake_up(&j->pin_flush_wait);
+               ret = true;
        }
+
+       return ret;
 }
 
 /**
        mutex_unlock(&j->reclaim_lock);
 }
 
-static int journal_flush_done(struct journal *j, u64 seq_to_flush)
+static int journal_flush_done(struct journal *j, u64 seq_to_flush,
+                             bool *did_work)
 {
        int ret;
 
 
        mutex_lock(&j->reclaim_lock);
 
-       journal_flush_pins(j, seq_to_flush, 0);
+       *did_work = journal_flush_pins(j, seq_to_flush, 0);
 
        spin_lock(&j->lock);
        /*
        return ret;
 }
 
-void bch2_journal_flush_pins(struct journal *j, u64 seq_to_flush)
+bool bch2_journal_flush_pins(struct journal *j, u64 seq_to_flush)
 {
+       bool did_work = false;
+
        if (!test_bit(JOURNAL_STARTED, &j->flags))
-               return;
+               return false;
+
+       closure_wait_event(&j->async_wait,
+               journal_flush_done(j, seq_to_flush, &did_work));
 
-       closure_wait_event(&j->async_wait, journal_flush_done(j, seq_to_flush));
+       return did_work;
 }
 
 int bch2_journal_flush_device_pins(struct journal *j, int dev_idx)
 
 static void __bch2_fs_read_only(struct bch_fs *c)
 {
        struct bch_dev *ca;
-       bool wrote;
+       bool wrote = false;
        unsigned i, clean_passes = 0;
        int ret;
 
                goto nowrote_alloc;
 
        bch_verbose(c, "writing alloc info");
+       /*
+        * This should normally just be writing the bucket read/write clocks:
+        */
+       ret = bch2_stripes_write(c, BTREE_INSERT_NOCHECK_RW, &wrote) ?:
+               bch2_alloc_write(c, BTREE_INSERT_NOCHECK_RW, &wrote);
+       bch_verbose(c, "writing alloc info complete");
 
-       do {
-               wrote = false;
+       if (ret && !test_bit(BCH_FS_EMERGENCY_RO, &c->flags))
+               bch2_fs_inconsistent(c, "error writing out alloc info %i", ret);
 
-               ret = bch2_stripes_write(c, BTREE_INSERT_NOCHECK_RW, &wrote) ?:
-                       bch2_alloc_write(c, BTREE_INSERT_NOCHECK_RW, &wrote);
+       if (ret)
+               goto nowrote_alloc;
 
-               if (ret && !test_bit(BCH_FS_EMERGENCY_RO, &c->flags))
-                       bch2_fs_inconsistent(c, "error writing out alloc info %i", ret);
+       bch_verbose(c, "flushing journal and stopping allocators");
 
-               if (ret)
-                       goto nowrote_alloc;
+       bch2_journal_flush_all_pins(&c->journal);
+       set_bit(BCH_FS_ALLOCATOR_STOPPING, &c->flags);
 
-               for_each_member_device(ca, c, i)
-                       bch2_dev_allocator_quiesce(c, ca);
+       do {
+               clean_passes++;
 
-               bch2_journal_flush_all_pins(&c->journal);
+               if (bch2_journal_flush_all_pins(&c->journal))
+                       clean_passes = 0;
 
                /*
-                * We need to explicitly wait on btree interior updates to complete
-                * before stopping the journal, flushing all journal pins isn't
-                * sufficient, because in the BTREE_INTERIOR_UPDATING_ROOT case btree
-                * interior updates have to drop their journal pin before they're
-                * fully complete:
+                * In flight interior btree updates will generate more journal
+                * updates and btree updates (alloc btree):
                 */
-               closure_wait_event(&c->btree_interior_update_wait,
-                                  !bch2_btree_interior_updates_nr_pending(c));
+               if (bch2_btree_interior_updates_nr_pending(c)) {
+                       closure_wait_event(&c->btree_interior_update_wait,
+                                          !bch2_btree_interior_updates_nr_pending(c));
+                       clean_passes = 0;
+               }
                flush_work(&c->btree_interior_update_work);
 
-               clean_passes = wrote ? 0 : clean_passes + 1;
+               if (bch2_journal_flush_all_pins(&c->journal))
+                       clean_passes = 0;
        } while (clean_passes < 2);
+       bch_verbose(c, "flushing journal and stopping allocators complete");
 
-       bch_verbose(c, "writing alloc info complete");
        set_bit(BCH_FS_ALLOC_CLEAN, &c->flags);
 nowrote_alloc:
        closure_wait_event(&c->btree_interior_update_wait,
                bch2_dev_allocator_stop(ca);
 
        clear_bit(BCH_FS_ALLOCATOR_RUNNING, &c->flags);
+       clear_bit(BCH_FS_ALLOCATOR_STOPPING, &c->flags);
 
        bch2_fs_journal_stop(&c->journal);
 
-       /* XXX: mark super that alloc info is persistent */
-
        /*
         * the journal kicks off btree writes via reclaim - wait for in flight
         * writes after stopping journal: