--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -1627,11 +1627,13 @@ static int cleaner_kthread(void *arg)
                int again = 0;

                if (!(root->fs_info->sb->s_flags & MS_RDONLY) &&
+                   down_read_trylock(&root->fs_info->sb->s_umount) &&
                    mutex_trylock(&root->fs_info->cleaner_mutex)) {
                        btrfs_run_delayed_iputs(root);
                        again = btrfs_clean_one_deleted_snapshot(root);
                        mutex_unlock(&root->fs_info->cleaner_mutex);
                        btrfs_run_defrag_inodes(root->fs_info);
+                       up_read(&root->fs_info->sb->s_umount);
                }

                if (!try_to_freeze() && !again) {
---

Seems that also checking for btrfs_fs_closing != 0 would help here.

And to the second part, no dead_roots is not supposed to be empty.

> > @@ -1783,31 +1783,50 @@ cleanup_transaction:
> >  }
> >
> >  /*
> > - * interface function to delete all the snapshots we have scheduled for deletion
> > + * return < 0 if error
> > + * 0 if there are no more dead_roots at the time of call
> > + * 1 there are more to be processed, call me again
> > + *
> > + * The return value indicates there are certainly more snapshots to delete, but
> > + * if there comes a new one during processing, it may return 0. We don't mind,
> > + * because btrfs_commit_super will poke cleaner thread and it will process it a
> > + * few seconds later.
> >   */
> > -int btrfs_clean_old_snapshots(struct btrfs_root *root)
> > +int btrfs_clean_one_deleted_snapshot(struct btrfs_root *root)
> >  {
> > -       LIST_HEAD(list);
> > +       int ret;
> > +       int run_again = 1;
> >         struct btrfs_fs_info *fs_info = root->fs_info;
> >
> > +       if (root->fs_info->sb->s_flags & MS_RDONLY) {
> > +               pr_debug(G "btrfs: cleaner called for RO fs!\n");
> > +               return 0;
> > +       }
> > +
> >         spin_lock(&fs_info->trans_lock);
> > -       list_splice_init(&fs_info->dead_roots, &list);
> > +       if (list_empty(&fs_info->dead_roots)) {
> > +               spin_unlock(&fs_info->trans_lock);
> > +               return 0;
> > +       }
> > +       root = list_first_entry(&fs_info->dead_roots,
> > +                       struct btrfs_root, root_list);
> > +       list_del(&root->root_list);
> >         spin_unlock(&fs_info->trans_lock);
> >
> > -       while (!list_empty(&list)) {
> > -               int ret;
> > -
> > -               root = list_entry(list.next, struct btrfs_root, root_list);
> > -               list_del(&root->root_list);
> > +       pr_debug("btrfs: cleaner removing %llu\n",
> > +                       (unsigned long long)root->objectid);
> >
> > -               btrfs_kill_all_delayed_nodes(root);
> > +       btrfs_kill_all_delayed_nodes(root);
> >
> > -               if (btrfs_header_backref_rev(root->node) <
> > -                   BTRFS_MIXED_BACKREF_REV)
> > -                       ret = btrfs_drop_snapshot(root, NULL, 0, 0);
> > -               else
> > -                       ret =btrfs_drop_snapshot(root, NULL, 1, 0);
> > -               BUG_ON(ret < 0);
> > -       }
> > -       return 0;
> > +       if (btrfs_header_backref_rev(root->node) <
> > +                       BTRFS_MIXED_BACKREF_REV)
> > +               ret = btrfs_drop_snapshot(root, NULL, 0, 0);
> > +       else
> > +               ret = btrfs_drop_snapshot(root, NULL, 1, 0);
> > +       /*
> > +        * If we encounter a transaction abort during snapshot cleaning, we
> > +        * don't want to crash here
> > +        */
> > +       BUG_ON(ret < 0 && (ret != -EAGAIN || ret != -EROFS));
> > +       return run_again || ret == -EAGAIN;
> Can you tell me when btrfs_drop_snapshot is supposed to return EAGAIN?

That's another inconsistency of this patch, sorry.

--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -6976,6 +6976,12 @@ int btrfs_drop_snapshot(struct btrfs_root *root,
        wc->reada_count = BTRFS_NODEPTRS_PER_BLOCK(root);

        while (1) {
+               if (btrfs_fs_closing(root->fs_info)) {
+                       printk(KERN_DEBUG "btrfs: drop snapshot early exit\n");
+                       err = -EAGAIN;
+                       goto out_end_trans;
+               }
+
                ret = walk_down_tree(trans, root, path, wc);
                if (ret < 0) {
                        err = ret;
