]> git.sesse.net Git - bcachefs-tools-debian/blobdiff - libbcachefs/subvolume.c
Update bcachefs sources to f638850417 bcachefs: bch2_trans_log_msg()
[bcachefs-tools-debian] / libbcachefs / subvolume.c
index d1c111050c35cdf2601e011f504b46175ef58c2d..cdb89ba216cca3a5f109a97cb6eedfa2c8f4c2fd 100644 (file)
@@ -4,6 +4,7 @@
 #include "btree_key_cache.h"
 #include "btree_update.h"
 #include "error.h"
+#include "fs.h"
 #include "subvolume.h"
 
 /* Snapshot tree: */
@@ -60,10 +61,11 @@ const char *bch2_snapshot_invalid(const struct bch_fs *c, struct bkey_s_c k)
        return NULL;
 }
 
-int bch2_mark_snapshot(struct bch_fs *c,
+int bch2_mark_snapshot(struct btree_trans *trans,
                       struct bkey_s_c old, struct bkey_s_c new,
-                      u64 journal_seq, unsigned flags)
+                      unsigned flags)
 {
+       struct bch_fs *c = trans->c;
        struct snapshot_t *t;
 
        t = genradix_ptr_alloc(&c->snapshots,
@@ -137,7 +139,7 @@ static int bch2_snapshots_set_equiv(struct btree_trans *trans)
        for_each_btree_key(trans, iter, BTREE_ID_snapshots,
                           POS_MIN, 0, k, ret) {
                u32 id = k.k->p.offset, child[2];
-               unsigned nr_live = 0, live_idx;
+               unsigned nr_live = 0, live_idx = 0;
 
                if (k.k->type != KEY_TYPE_snapshot)
                        continue;
@@ -149,7 +151,7 @@ static int bch2_snapshots_set_equiv(struct btree_trans *trans)
                for (i = 0; i < 2; i++) {
                        ret = snapshot_live(trans, child[i]);
                        if (ret < 0)
-                               break;
+                               goto err;
 
                        if (ret)
                                live_idx = i;
@@ -160,6 +162,7 @@ static int bch2_snapshots_set_equiv(struct btree_trans *trans)
                        ? snapshot_t(c, child[live_idx])->equiv
                        : id;
        }
+err:
        bch2_trans_iter_exit(trans, &iter);
 
        if (ret)
@@ -307,7 +310,7 @@ int bch2_fs_snapshots_start(struct bch_fs *c)
                if (BCH_SNAPSHOT_DELETED(bkey_s_c_to_snapshot(k).v))
                        have_deleted = true;
 
-               ret = bch2_mark_snapshot(c, bkey_s_c_null, k, 0, 0);
+               ret = bch2_mark_snapshot(&trans, bkey_s_c_null, k, 0);
                if (ret)
                        break;
        }
@@ -454,10 +457,10 @@ err:
        return ret;
 }
 
-static int bch2_snapshot_node_create(struct btree_trans *trans, u32 parent,
-                                    u32 *new_snapids,
-                                    u32 *snapshot_subvols,
-                                    unsigned nr_snapids)
+int bch2_snapshot_node_create(struct btree_trans *trans, u32 parent,
+                             u32 *new_snapids,
+                             u32 *snapshot_subvols,
+                             unsigned nr_snapids)
 {
        struct btree_iter iter;
        struct bkey_i_snapshot *n;
@@ -486,7 +489,7 @@ static int bch2_snapshot_node_create(struct btree_trans *trans, u32 parent,
                n = bch2_trans_kmalloc(trans, sizeof(*n));
                ret = PTR_ERR_OR_ZERO(n);
                if (ret)
-                       return ret;
+                       goto err;
 
                bkey_snapshot_init(&n->k_i);
                n->k.p          = iter.pos;
@@ -496,11 +499,10 @@ static int bch2_snapshot_node_create(struct btree_trans *trans, u32 parent,
                n->v.pad        = 0;
                SET_BCH_SNAPSHOT_SUBVOL(&n->v, true);
 
-               bch2_trans_update(trans, &iter, &n->k_i, 0);
-
-               ret = bch2_mark_snapshot(trans->c, bkey_s_c_null, bkey_i_to_s_c(&n->k_i), 0, 0);
+               ret   = bch2_trans_update(trans, &iter, &n->k_i, 0) ?:
+                       bch2_mark_snapshot(trans, bkey_s_c_null, bkey_i_to_s_c(&n->k_i), 0);
                if (ret)
-                       break;
+                       goto err;
 
                new_snapids[i]  = iter.pos.offset;
        }
@@ -521,7 +523,7 @@ static int bch2_snapshot_node_create(struct btree_trans *trans, u32 parent,
                n = bch2_trans_kmalloc(trans, sizeof(*n));
                ret = PTR_ERR_OR_ZERO(n);
                if (ret)
-                       return ret;
+                       goto err;
 
                bkey_reassemble(&n->k_i, k);
 
@@ -534,60 +536,30 @@ static int bch2_snapshot_node_create(struct btree_trans *trans, u32 parent,
                n->v.children[0] = cpu_to_le32(new_snapids[0]);
                n->v.children[1] = cpu_to_le32(new_snapids[1]);
                SET_BCH_SNAPSHOT_SUBVOL(&n->v, false);
-               bch2_trans_update(trans, &iter, &n->k_i, 0);
+               ret = bch2_trans_update(trans, &iter, &n->k_i, 0);
+               if (ret)
+                       goto err;
        }
 err:
        bch2_trans_iter_exit(trans, &iter);
        return ret;
 }
 
-/* List of snapshot IDs that are being deleted: */
-struct snapshot_id_list {
-       u32             nr;
-       u32             size;
-       u32             *d;
-};
-
-static bool snapshot_list_has_id(struct snapshot_id_list *s, u32 id)
-{
-       unsigned i;
-
-       for (i = 0; i < s->nr; i++)
-               if (id == s->d[i])
-                       return true;
-       return false;
-}
-
-static int snapshot_id_add(struct snapshot_id_list *s, u32 id)
+static int snapshot_id_add(snapshot_id_list *s, u32 id)
 {
        BUG_ON(snapshot_list_has_id(s, id));
 
-       if (s->nr == s->size) {
-               size_t new_size = max(8U, s->size * 2);
-               void *n = krealloc(s->d,
-                                  new_size * sizeof(s->d[0]),
-                                  GFP_KERNEL);
-               if (!n) {
-                       pr_err("error allocating snapshot ID list");
-                       return -ENOMEM;
-               }
-
-               s->d    = n;
-               s->size = new_size;
-       };
-
-       s->d[s->nr++] = id;
-       return 0;
+       return darray_push(*s, id);
 }
 
 static int bch2_snapshot_delete_keys_btree(struct btree_trans *trans,
-                                          struct snapshot_id_list *deleted,
+                                          snapshot_id_list *deleted,
                                           enum btree_id btree_id)
 {
        struct bch_fs *c = trans->c;
        struct btree_iter iter;
        struct bkey_s_c k;
-       struct snapshot_id_list equiv_seen = { 0 };
+       snapshot_id_list equiv_seen = { 0 };
        struct bpos last_pos = POS_MIN;
        int ret = 0;
 
@@ -634,7 +606,7 @@ static int bch2_snapshot_delete_keys_btree(struct btree_trans *trans,
        }
        bch2_trans_iter_exit(trans, &iter);
 
-       kfree(equiv_seen.d);
+       darray_exit(equiv_seen);
 
        return ret;
 }
@@ -646,7 +618,7 @@ static void bch2_delete_dead_snapshots_work(struct work_struct *work)
        struct btree_iter iter;
        struct bkey_s_c k;
        struct bkey_s_c_snapshot snap;
-       struct snapshot_id_list deleted = { 0 };
+       snapshot_id_list deleted = { 0 };
        u32 i, id, children[2];
        int ret = 0;
 
@@ -726,15 +698,15 @@ static void bch2_delete_dead_snapshots_work(struct work_struct *work)
 
        for (i = 0; i < deleted.nr; i++) {
                ret = __bch2_trans_do(&trans, NULL, NULL, 0,
-                       bch2_snapshot_node_delete(&trans, deleted.d[i]));
+                       bch2_snapshot_node_delete(&trans, deleted.data[i]));
                if (ret) {
                        bch_err(c, "error deleting snapshot %u: %i",
-                               deleted.d[i], ret);
+                               deleted.data[i], ret);
                        goto err;
                }
        }
 err:
-       kfree(deleted.d);
+       darray_exit(deleted);
        bch2_trans_exit(&trans);
        percpu_ref_put(&c->writes);
 }
@@ -804,6 +776,15 @@ int bch2_subvolume_get(struct btree_trans *trans, unsigned subvol,
        return ret;
 }
 
+int bch2_snapshot_get_subvol(struct btree_trans *trans, u32 snapshot,
+                            struct bch_subvolume *subvol)
+{
+       struct bch_snapshot snap;
+
+       return  snapshot_lookup(trans, snapshot, &snap) ?:
+               bch2_subvolume_get(trans, le32_to_cpu(snap.subvol), true, 0, subvol);
+}
+
 int bch2_subvolume_get_snapshot(struct btree_trans *trans, u32 subvol,
                                u32 *snapid)
 {
@@ -819,9 +800,11 @@ int bch2_subvolume_get_snapshot(struct btree_trans *trans, u32 subvol,
        return ret;
 }
 
-/* XXX: mark snapshot id for deletion, walk btree and delete: */
-int bch2_subvolume_delete(struct btree_trans *trans, u32 subvolid,
-                         int deleting_snapshot)
+/*
+ * Delete subvolume, mark snapshot ID as deleted, queue up snapshot
+ * deletion/cleanup:
+ */
+int bch2_subvolume_delete(struct btree_trans *trans, u32 subvolid)
 {
        struct btree_iter iter;
        struct bkey_s_c k;
@@ -849,12 +832,6 @@ int bch2_subvolume_delete(struct btree_trans *trans, u32 subvolid,
        subvol = bkey_s_c_to_subvolume(k);
        snapid = le32_to_cpu(subvol.v->snapshot);
 
-       if (deleting_snapshot >= 0 &&
-           deleting_snapshot != BCH_SUBVOLUME_SNAP(subvol.v)) {
-               ret = -ENOENT;
-               goto err;
-       }
-
        delete = bch2_trans_kmalloc(trans, sizeof(*delete));
        ret = PTR_ERR_OR_ZERO(delete);
        if (ret)
@@ -880,12 +857,123 @@ err:
        return ret;
 }
 
+void bch2_subvolume_wait_for_pagecache_and_delete(struct work_struct *work)
+{
+       struct bch_fs *c = container_of(work, struct bch_fs,
+                               snapshot_wait_for_pagecache_and_delete_work);
+       snapshot_id_list s;
+       u32 *id;
+       int ret = 0;
+
+       while (!ret) {
+               mutex_lock(&c->snapshots_unlinked_lock);
+               s = c->snapshots_unlinked;
+               darray_init(c->snapshots_unlinked);
+               mutex_unlock(&c->snapshots_unlinked_lock);
+
+               if (!s.nr)
+                       break;
+
+               bch2_evict_subvolume_inodes(c, &s);
+
+               for (id = s.data; id < s.data + s.nr; id++) {
+                       ret = bch2_trans_do(c, NULL, NULL, BTREE_INSERT_NOFAIL,
+                                     bch2_subvolume_delete(&trans, *id));
+                       if (ret) {
+                               bch_err(c, "error %i deleting subvolume %u", ret, *id);
+                               break;
+                       }
+               }
+
+               darray_exit(s);
+       }
+
+       percpu_ref_put(&c->writes);
+}
+
+struct subvolume_unlink_hook {
+       struct btree_trans_commit_hook  h;
+       u32                             subvol;
+};
+
+int bch2_subvolume_wait_for_pagecache_and_delete_hook(struct btree_trans *trans,
+                                                     struct btree_trans_commit_hook *_h)
+{
+       struct subvolume_unlink_hook *h = container_of(_h, struct subvolume_unlink_hook, h);
+       struct bch_fs *c = trans->c;
+       int ret = 0;
+
+       mutex_lock(&c->snapshots_unlinked_lock);
+       if (!snapshot_list_has_id(&c->snapshots_unlinked, h->subvol))
+               ret = snapshot_id_add(&c->snapshots_unlinked, h->subvol);
+       mutex_unlock(&c->snapshots_unlinked_lock);
+
+       if (ret)
+               return ret;
+
+       if (unlikely(!percpu_ref_tryget(&c->writes)))
+               return -EROFS;
+
+       if (!queue_work(system_long_wq, &c->snapshot_wait_for_pagecache_and_delete_work))
+               percpu_ref_put(&c->writes);
+       return 0;
+}
+
+int bch2_subvolume_unlink(struct btree_trans *trans, u32 subvolid)
+{
+       struct btree_iter iter;
+       struct bkey_s_c k;
+       struct bkey_i_subvolume *n;
+       struct subvolume_unlink_hook *h;
+       int ret = 0;
+
+       bch2_trans_iter_init(trans, &iter, BTREE_ID_subvolumes,
+                            POS(0, subvolid),
+                            BTREE_ITER_CACHED|
+                            BTREE_ITER_INTENT);
+       k = bch2_btree_iter_peek_slot(&iter);
+       ret = bkey_err(k);
+       if (ret)
+               goto err;
+
+       if (k.k->type != KEY_TYPE_subvolume) {
+               bch2_fs_inconsistent(trans->c, "missing subvolume %u", subvolid);
+               ret = -EIO;
+               goto err;
+       }
+
+       n = bch2_trans_kmalloc(trans, sizeof(*n));
+       ret = PTR_ERR_OR_ZERO(n);
+       if (ret)
+               goto err;
+
+       bkey_reassemble(&n->k_i, k);
+       SET_BCH_SUBVOLUME_UNLINKED(&n->v, true);
+
+       ret = bch2_trans_update(trans, &iter, &n->k_i, 0);
+       if (ret)
+               goto err;
+
+       h = bch2_trans_kmalloc(trans, sizeof(*h));
+       ret = PTR_ERR_OR_ZERO(h);
+       if (ret)
+               goto err;
+
+       h->h.fn         = bch2_subvolume_wait_for_pagecache_and_delete_hook;
+       h->subvol       = subvolid;
+       bch2_trans_commit_hook(trans, &h->h);
+err:
+       bch2_trans_iter_exit(trans, &iter);
+       return ret;
+}
+
 int bch2_subvolume_create(struct btree_trans *trans, u64 inode,
                          u32 src_subvolid,
                          u32 *new_subvolid,
                          u32 *new_snapshotid,
                          bool ro)
 {
+       struct bch_fs *c = trans->c;
        struct btree_iter dst_iter, src_iter = (struct btree_iter) { NULL };
        struct bkey_i_subvolume *new_subvol = NULL;
        struct bkey_i_subvolume *src_subvol = NULL;
@@ -897,7 +985,13 @@ int bch2_subvolume_create(struct btree_trans *trans, u64 inode,
                           BTREE_ITER_SLOTS|BTREE_ITER_INTENT, k, ret) {
                if (bkey_cmp(k.k->p, SUBVOL_POS_MAX) > 0)
                        break;
-               if (bkey_deleted(k.k))
+
+               /*
+                * bch2_subvolume_delete() doesn't flush the btree key cache -
+                * ideally it would but that's tricky
+                */
+               if (bkey_deleted(k.k) &&
+                   !bch2_btree_key_cache_find(c, BTREE_ID_subvolumes, dst_iter.pos))
                        goto found_slot;
        }
 
@@ -925,7 +1019,7 @@ found_slot:
                        goto err;
 
                if (k.k->type != KEY_TYPE_subvolume) {
-                       bch_err(trans->c, "subvolume %u not found", src_subvolid);
+                       bch_err(c, "subvolume %u not found", src_subvolid);
                        ret = -ENOENT;
                        goto err;
                }
@@ -942,7 +1036,9 @@ found_slot:
 
        if (src_subvolid) {
                src_subvol->v.snapshot = cpu_to_le32(new_nodes[1]);
-               bch2_trans_update(trans, &src_iter, &src_subvol->k_i, 0);
+               ret = bch2_trans_update(trans, &src_iter, &src_subvol->k_i, 0);
+               if (ret)
+                       goto err;
        }
 
        new_subvol = bch2_trans_kmalloc(trans, sizeof(*new_subvol));
@@ -957,7 +1053,9 @@ found_slot:
        SET_BCH_SUBVOLUME_RO(&new_subvol->v, ro);
        SET_BCH_SUBVOLUME_SNAP(&new_subvol->v, src_subvolid != 0);
        new_subvol->k.p         = dst_iter.pos;
-       bch2_trans_update(trans, &dst_iter, &new_subvol->k_i, 0);
+       ret = bch2_trans_update(trans, &dst_iter, &new_subvol->k_i, 0);
+       if (ret)
+               goto err;
 
        *new_subvolid   = new_subvol->k.p.offset;
        *new_snapshotid = new_nodes[0];
@@ -970,5 +1068,8 @@ err:
 int bch2_fs_subvolumes_init(struct bch_fs *c)
 {
        INIT_WORK(&c->snapshot_delete_work, bch2_delete_dead_snapshots_work);
+       INIT_WORK(&c->snapshot_wait_for_pagecache_and_delete_work,
+                 bch2_subvolume_wait_for_pagecache_and_delete);
+       mutex_init(&c->snapshots_unlinked_lock);
        return 0;
 }