Commit 14b393ee authored by Kent Overstreet's avatar Kent Overstreet Committed by Kent Overstreet

bcachefs: Subvolumes, snapshots

This patch adds subvolume.c - support for the subvolumes and snapshots
btrees and related data types and on disk data structures. The next
patches will start hooking up this new code to existing code.
Signed-off-by: default avatarKent Overstreet <kent.overstreet@gmail.com>
parent 8948fc8f
...@@ -50,6 +50,7 @@ bcachefs-y := \ ...@@ -50,6 +50,7 @@ bcachefs-y := \
replicas.o \ replicas.o \
siphash.o \ siphash.o \
six.o \ six.o \
subvolume.o \
super.o \ super.o \
super-io.o \ super-io.o \
sysfs.o \ sysfs.o \
......
...@@ -381,6 +381,8 @@ enum gc_phase { ...@@ -381,6 +381,8 @@ enum gc_phase {
GC_PHASE_BTREE_alloc, GC_PHASE_BTREE_alloc,
GC_PHASE_BTREE_quotas, GC_PHASE_BTREE_quotas,
GC_PHASE_BTREE_reflink, GC_PHASE_BTREE_reflink,
GC_PHASE_BTREE_subvolumes,
GC_PHASE_BTREE_snapshots,
GC_PHASE_PENDING_DELETE, GC_PHASE_PENDING_DELETE,
}; };
...@@ -564,6 +566,21 @@ struct btree_path_buf { ...@@ -564,6 +566,21 @@ struct btree_path_buf {
#define REPLICAS_DELTA_LIST_MAX (1U << 16) #define REPLICAS_DELTA_LIST_MAX (1U << 16)
struct snapshot_t {
u32 parent;
u32 children[2];
u32 subvol; /* Nonzero only if a subvolume points to this node: */
u32 equiv;
};
typedef struct {
u32 subvol;
u64 inum;
} subvol_inum;
#define BCACHEFS_ROOT_SUBVOL_INUM \
((subvol_inum) { BCACHEFS_ROOT_SUBVOL, BCACHEFS_ROOT_INO })
struct bch_fs { struct bch_fs {
struct closure cl; struct closure cl;
...@@ -635,6 +652,12 @@ struct bch_fs { ...@@ -635,6 +652,12 @@ struct bch_fs {
struct closure sb_write; struct closure sb_write;
struct mutex sb_lock; struct mutex sb_lock;
/* snapshot.c: */
GENRADIX(struct snapshot_t) snapshots;
struct bch_snapshot_table __rcu *snapshot_table;
struct mutex snapshot_table_lock;
struct work_struct snapshot_delete_work;
/* BTREE CACHE */ /* BTREE CACHE */
struct bio_set btree_bio; struct bio_set btree_bio;
struct workqueue_struct *io_complete_wq; struct workqueue_struct *io_complete_wq;
......
...@@ -346,7 +346,9 @@ static inline void bkey_init(struct bkey *k) ...@@ -346,7 +346,9 @@ static inline void bkey_init(struct bkey *k)
x(inline_data, 17) \ x(inline_data, 17) \
x(btree_ptr_v2, 18) \ x(btree_ptr_v2, 18) \
x(indirect_inline_data, 19) \ x(indirect_inline_data, 19) \
x(alloc_v2, 20) x(alloc_v2, 20) \
x(subvolume, 21) \
x(snapshot, 22)
enum bch_bkey_type { enum bch_bkey_type {
#define x(name, nr) KEY_TYPE_##name = nr, #define x(name, nr) KEY_TYPE_##name = nr,
...@@ -690,6 +692,10 @@ struct bch_inode_generation { ...@@ -690,6 +692,10 @@ struct bch_inode_generation {
__le32 pad; __le32 pad;
} __attribute__((packed, aligned(8))); } __attribute__((packed, aligned(8)));
/*
* bi_subvol and bi_parent_subvol are only set for subvolume roots:
*/
#define BCH_INODE_FIELDS() \ #define BCH_INODE_FIELDS() \
x(bi_atime, 96) \ x(bi_atime, 96) \
x(bi_ctime, 96) \ x(bi_ctime, 96) \
...@@ -713,7 +719,9 @@ struct bch_inode_generation { ...@@ -713,7 +719,9 @@ struct bch_inode_generation {
x(bi_erasure_code, 16) \ x(bi_erasure_code, 16) \
x(bi_fields_set, 16) \ x(bi_fields_set, 16) \
x(bi_dir, 64) \ x(bi_dir, 64) \
x(bi_dir_offset, 64) x(bi_dir_offset, 64) \
x(bi_subvol, 32) \
x(bi_parent_subvol, 32)
/* subset of BCH_INODE_FIELDS */ /* subset of BCH_INODE_FIELDS */
#define BCH_INODE_OPTS() \ #define BCH_INODE_OPTS() \
...@@ -796,6 +804,9 @@ struct bch_dirent { ...@@ -796,6 +804,9 @@ struct bch_dirent {
__u8 d_name[]; __u8 d_name[];
} __attribute__((packed, aligned(8))); } __attribute__((packed, aligned(8)));
#define DT_SUBVOL 16
#define BCH_DT_MAX 17
#define BCH_NAME_MAX (U8_MAX * sizeof(u64) - \ #define BCH_NAME_MAX (U8_MAX * sizeof(u64) - \
sizeof(struct bkey) - \ sizeof(struct bkey) - \
offsetof(struct bch_dirent, d_name)) offsetof(struct bch_dirent, d_name))
...@@ -932,6 +943,42 @@ struct bch_inline_data { ...@@ -932,6 +943,42 @@ struct bch_inline_data {
u8 data[0]; u8 data[0];
}; };
/* Subvolumes: */
#define SUBVOL_POS_MIN POS(0, 1)
#define SUBVOL_POS_MAX POS(0, S32_MAX)
#define BCACHEFS_ROOT_SUBVOL 1
struct bch_subvolume {
struct bch_val v;
__le32 flags;
__le32 snapshot;
__le64 inode;
};
LE32_BITMASK(BCH_SUBVOLUME_RO, struct bch_subvolume, flags, 0, 1)
/*
* We need to know whether a subvolume is a snapshot so we can know whether we
* can delete it (or whether it should just be rm -rf'd)
*/
LE32_BITMASK(BCH_SUBVOLUME_SNAP, struct bch_subvolume, flags, 1, 2)
/* Snapshots */
struct bch_snapshot {
struct bch_val v;
__le32 flags;
__le32 parent;
__le32 children[2];
__le32 subvol;
__le32 pad;
};
LE32_BITMASK(BCH_SNAPSHOT_DELETED, struct bch_snapshot, flags, 0, 1)
/* True if a subvolume points to this snapshot node: */
LE32_BITMASK(BCH_SNAPSHOT_SUBVOL, struct bch_snapshot, flags, 1, 2)
/* Optional/variable size superblock sections: */ /* Optional/variable size superblock sections: */
struct bch_sb_field { struct bch_sb_field {
...@@ -1702,7 +1749,9 @@ LE32_BITMASK(JSET_NO_FLUSH, struct jset, flags, 5, 6); ...@@ -1702,7 +1749,9 @@ LE32_BITMASK(JSET_NO_FLUSH, struct jset, flags, 5, 6);
x(alloc, 4) \ x(alloc, 4) \
x(quotas, 5) \ x(quotas, 5) \
x(stripes, 6) \ x(stripes, 6) \
x(reflink, 7) x(reflink, 7) \
x(subvolumes, 8) \
x(snapshots, 9)
enum btree_id { enum btree_id {
#define x(kwd, val) BTREE_ID_##kwd = val, #define x(kwd, val) BTREE_ID_##kwd = val,
......
...@@ -78,6 +78,9 @@ struct bch_ioctl_incremental { ...@@ -78,6 +78,9 @@ struct bch_ioctl_incremental {
#define BCH_IOCTL_DISK_RESIZE _IOW(0xbc, 14, struct bch_ioctl_disk_resize) #define BCH_IOCTL_DISK_RESIZE _IOW(0xbc, 14, struct bch_ioctl_disk_resize)
#define BCH_IOCTL_DISK_RESIZE_JOURNAL _IOW(0xbc,15, struct bch_ioctl_disk_resize_journal) #define BCH_IOCTL_DISK_RESIZE_JOURNAL _IOW(0xbc,15, struct bch_ioctl_disk_resize_journal)
#define BCH_IOCTL_SUBVOLUME_CREATE _IOW(0xbc, 16, struct bch_ioctl_subvolume)
#define BCH_IOCTL_SUBVOLUME_DESTROY _IOW(0xbc, 17, struct bch_ioctl_subvolume)
/* ioctl below act on a particular file, not the filesystem as a whole: */ /* ioctl below act on a particular file, not the filesystem as a whole: */
#define BCHFS_IOC_REINHERIT_ATTRS _IOR(0xbc, 64, const char __user *) #define BCHFS_IOC_REINHERIT_ATTRS _IOR(0xbc, 64, const char __user *)
...@@ -349,4 +352,16 @@ struct bch_ioctl_disk_resize_journal { ...@@ -349,4 +352,16 @@ struct bch_ioctl_disk_resize_journal {
__u64 nbuckets; __u64 nbuckets;
}; };
struct bch_ioctl_subvolume {
__u32 flags;
__u32 dirfd;
__u16 mode;
__u16 pad[3];
__u64 dst_ptr;
__u64 src_ptr;
};
#define BCH_SUBVOL_SNAPSHOT_CREATE (1U << 0)
#define BCH_SUBVOL_SNAPSHOT_RO (1U << 1)
#endif /* _BCACHEFS_IOCTL_H */ #endif /* _BCACHEFS_IOCTL_H */
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "inode.h" #include "inode.h"
#include "quota.h" #include "quota.h"
#include "reflink.h" #include "reflink.h"
#include "subvolume.h"
#include "xattr.h" #include "xattr.h"
const char * const bch2_bkey_types[] = { const char * const bch2_bkey_types[] = {
...@@ -126,6 +127,10 @@ static unsigned bch2_key_types_allowed[] = { ...@@ -126,6 +127,10 @@ static unsigned bch2_key_types_allowed[] = {
[BKEY_TYPE_reflink] = [BKEY_TYPE_reflink] =
(1U << KEY_TYPE_reflink_v)| (1U << KEY_TYPE_reflink_v)|
(1U << KEY_TYPE_indirect_inline_data), (1U << KEY_TYPE_indirect_inline_data),
[BKEY_TYPE_subvolumes] =
(1U << KEY_TYPE_subvolume),
[BKEY_TYPE_snapshots] =
(1U << KEY_TYPE_snapshot),
[BKEY_TYPE_btree] = [BKEY_TYPE_btree] =
(1U << KEY_TYPE_btree_ptr)| (1U << KEY_TYPE_btree_ptr)|
(1U << KEY_TYPE_btree_ptr_v2), (1U << KEY_TYPE_btree_ptr_v2),
......
...@@ -164,6 +164,11 @@ btree_key_cache_create(struct btree_key_cache *c, ...@@ -164,6 +164,11 @@ btree_key_cache_create(struct btree_key_cache *c,
was_new = false; was_new = false;
} }
if (btree_id == BTREE_ID_subvolumes)
six_lock_pcpu_alloc(&ck->c.lock);
else
six_lock_pcpu_free(&ck->c.lock);
ck->c.level = 0; ck->c.level = 0;
ck->c.btree_id = btree_id; ck->c.btree_id = btree_id;
ck->key.btree_id = btree_id; ck->key.btree_id = btree_id;
......
...@@ -606,7 +606,8 @@ static inline bool btree_node_is_extents(struct btree *b) ...@@ -606,7 +606,8 @@ static inline bool btree_node_is_extents(struct btree *b)
#define BTREE_NODE_TYPE_HAS_MEM_TRIGGERS \ #define BTREE_NODE_TYPE_HAS_MEM_TRIGGERS \
((1U << BKEY_TYPE_alloc)| \ ((1U << BKEY_TYPE_alloc)| \
(1U << BKEY_TYPE_stripes)) (1U << BKEY_TYPE_stripes)| \
(1U << BKEY_TYPE_snapshots))
#define BTREE_NODE_TYPE_HAS_TRIGGERS \ #define BTREE_NODE_TYPE_HAS_TRIGGERS \
(BTREE_NODE_TYPE_HAS_TRANS_TRIGGERS| \ (BTREE_NODE_TYPE_HAS_TRANS_TRIGGERS| \
...@@ -653,7 +654,8 @@ enum btree_update_flags { ...@@ -653,7 +654,8 @@ enum btree_update_flags {
#define BTREE_TRIGGER_WANTS_OLD_AND_NEW \ #define BTREE_TRIGGER_WANTS_OLD_AND_NEW \
((1U << KEY_TYPE_stripe)| \ ((1U << KEY_TYPE_stripe)| \
(1U << KEY_TYPE_inode)) (1U << KEY_TYPE_inode)| \
(1U << KEY_TYPE_snapshot))
static inline bool btree_node_type_needs_gc(enum btree_node_type type) static inline bool btree_node_type_needs_gc(enum btree_node_type type)
{ {
...@@ -670,11 +672,6 @@ struct btree_root { ...@@ -670,11 +672,6 @@ struct btree_root {
s8 error; s8 error;
}; };
/*
* Optional hook that will be called just prior to a btree node update, when
* we're holding the write lock and we know what key is about to be overwritten:
*/
enum btree_insert_ret { enum btree_insert_ret {
BTREE_INSERT_OK, BTREE_INSERT_OK,
/* leaf node needs to be split */ /* leaf node needs to be split */
...@@ -695,8 +692,4 @@ enum btree_node_sibling { ...@@ -695,8 +692,4 @@ enum btree_node_sibling {
btree_next_sib, btree_next_sib,
}; };
typedef struct btree_nr_keys (*sort_fix_overlapping_fn)(struct bset *,
struct btree *,
struct btree_node_iter *);
#endif /* _BCACHEFS_BTREE_TYPES_H */ #endif /* _BCACHEFS_BTREE_TYPES_H */
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include "journal.h" #include "journal.h"
#include "journal_reclaim.h" #include "journal_reclaim.h"
#include "keylist.h" #include "keylist.h"
#include "subvolume.h"
#include "replicas.h" #include "replicas.h"
#include "trace.h" #include "trace.h"
...@@ -245,6 +246,11 @@ static inline void btree_insert_entry_checks(struct btree_trans *trans, ...@@ -245,6 +246,11 @@ static inline void btree_insert_entry_checks(struct btree_trans *trans,
BUG_ON(i->cached != i->path->cached); BUG_ON(i->cached != i->path->cached);
BUG_ON(i->level != i->path->level); BUG_ON(i->level != i->path->level);
BUG_ON(i->btree_id != i->path->btree_id); BUG_ON(i->btree_id != i->path->btree_id);
EBUG_ON(!i->level &&
!(i->flags & BTREE_UPDATE_INTERNAL_SNAPSHOT_NODE) &&
test_bit(JOURNAL_REPLAY_DONE, &trans->c->journal.flags) &&
i->k->k.p.snapshot &&
bch2_snapshot_internal_node(trans->c, i->k->k.p.snapshot));
} }
static noinline int static noinline int
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include "movinggc.h" #include "movinggc.h"
#include "reflink.h" #include "reflink.h"
#include "replicas.h" #include "replicas.h"
#include "subvolume.h"
#include "trace.h" #include "trace.h"
#include <linux/preempt.h> #include <linux/preempt.h>
...@@ -1204,6 +1205,8 @@ static int bch2_mark_key_locked(struct bch_fs *c, ...@@ -1204,6 +1205,8 @@ static int bch2_mark_key_locked(struct bch_fs *c,
return bch2_mark_reservation(c, old, new, journal_seq, flags); return bch2_mark_reservation(c, old, new, journal_seq, flags);
case KEY_TYPE_reflink_p: case KEY_TYPE_reflink_p:
return bch2_mark_reflink_p(c, old, new, journal_seq, flags); return bch2_mark_reflink_p(c, old, new, journal_seq, flags);
case KEY_TYPE_snapshot:
return bch2_mark_snapshot(c, old, new, journal_seq, flags);
default: default:
return 0; return 0;
} }
......
...@@ -99,7 +99,8 @@ const char *bch2_dirent_invalid(const struct bch_fs *c, struct bkey_s_c k) ...@@ -99,7 +99,8 @@ const char *bch2_dirent_invalid(const struct bch_fs *c, struct bkey_s_c k)
if (memchr(d.v->d_name, '/', len)) if (memchr(d.v->d_name, '/', len))
return "invalid name"; return "invalid name";
if (le64_to_cpu(d.v->d_inum) == d.k->p.inode) if (d.v->d_type != DT_SUBVOL &&
le64_to_cpu(d.v->d_inum) == d.k->p.inode)
return "dirent points to own directory"; return "dirent points to own directory";
return NULL; return NULL;
...@@ -113,7 +114,7 @@ void bch2_dirent_to_text(struct printbuf *out, struct bch_fs *c, ...@@ -113,7 +114,7 @@ void bch2_dirent_to_text(struct printbuf *out, struct bch_fs *c,
bch_scnmemcpy(out, d.v->d_name, bch_scnmemcpy(out, d.v->d_name,
bch2_dirent_name_bytes(d)); bch2_dirent_name_bytes(d));
pr_buf(out, " -> %llu type %s", d.v->d_inum, pr_buf(out, " -> %llu type %s", d.v->d_inum,
d.v->d_type < DT_MAX d.v->d_type < BCH_DT_MAX
? bch2_d_types[d.v->d_type] ? bch2_d_types[d.v->d_type]
: "(bad d_type)"); : "(bad d_type)");
} }
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "fsck.h" #include "fsck.h"
#include "inode.h" #include "inode.h"
#include "keylist.h" #include "keylist.h"
#include "subvolume.h"
#include "super.h" #include "super.h"
#include "xattr.h" #include "xattr.h"
...@@ -1410,7 +1411,8 @@ int bch2_fsck_full(struct bch_fs *c) ...@@ -1410,7 +1411,8 @@ int bch2_fsck_full(struct bch_fs *c)
{ {
struct bch_inode_unpacked root_inode; struct bch_inode_unpacked root_inode;
return check_inodes(c, true) ?: return bch2_fs_snapshots_check(c) ?:
check_inodes(c, true) ?:
check_extents(c) ?: check_extents(c) ?:
check_dirents(c) ?: check_dirents(c) ?:
check_xattrs(c) ?: check_xattrs(c) ?:
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include "extents.h" #include "extents.h"
#include "inode.h" #include "inode.h"
#include "str_hash.h" #include "str_hash.h"
#include "subvolume.h"
#include "varint.h" #include "varint.h"
#include <linux/random.h> #include <linux/random.h>
...@@ -340,8 +341,8 @@ int bch2_inode_write(struct btree_trans *trans, ...@@ -340,8 +341,8 @@ int bch2_inode_write(struct btree_trans *trans,
const char *bch2_inode_invalid(const struct bch_fs *c, struct bkey_s_c k) const char *bch2_inode_invalid(const struct bch_fs *c, struct bkey_s_c k)
{ {
struct bkey_s_c_inode inode = bkey_s_c_to_inode(k); struct bkey_s_c_inode inode = bkey_s_c_to_inode(k);
struct bch_inode_unpacked unpacked; struct bch_inode_unpacked unpacked;
if (k.k->p.inode) if (k.k->p.inode)
return "nonzero k.p.inode"; return "nonzero k.p.inode";
...@@ -368,6 +369,9 @@ const char *bch2_inode_invalid(const struct bch_fs *c, struct bkey_s_c k) ...@@ -368,6 +369,9 @@ const char *bch2_inode_invalid(const struct bch_fs *c, struct bkey_s_c k)
unpacked.bi_nlink != 0) unpacked.bi_nlink != 0)
return "flagged as unlinked but bi_nlink != 0"; return "flagged as unlinked but bi_nlink != 0";
if (unpacked.bi_subvol && !S_ISDIR(unpacked.bi_mode))
return "subvolume root but not a directory";
return NULL; return NULL;
} }
...@@ -635,6 +639,13 @@ int bch2_inode_rm(struct bch_fs *c, u64 inode_nr, bool cached) ...@@ -635,6 +639,13 @@ int bch2_inode_rm(struct bch_fs *c, u64 inode_nr, bool cached)
bch2_inode_unpack(bkey_s_c_to_inode(k), &inode_u); bch2_inode_unpack(bkey_s_c_to_inode(k), &inode_u);
/* Subvolume root? */
if (inode_u.bi_subvol) {
ret = bch2_subvolume_delete(&trans, inode_u.bi_subvol, -1);
if (ret)
goto err;
}
bkey_inode_generation_init(&delete.k_i); bkey_inode_generation_init(&delete.k_i);
delete.k.p = iter.pos; delete.k.p = iter.pos;
delete.v.bi_generation = cpu_to_le32(inode_u.bi_generation + 1); delete.v.bi_generation = cpu_to_le32(inode_u.bi_generation + 1);
......
...@@ -63,7 +63,7 @@ const char * const bch2_member_states[] = { ...@@ -63,7 +63,7 @@ const char * const bch2_member_states[] = {
#undef x #undef x
const char * const bch2_d_types[DT_MAX] = { const char * const bch2_d_types[BCH_DT_MAX] = {
[DT_UNKNOWN] = "unknown", [DT_UNKNOWN] = "unknown",
[DT_FIFO] = "fifo", [DT_FIFO] = "fifo",
[DT_CHR] = "chr", [DT_CHR] = "chr",
...@@ -73,6 +73,7 @@ const char * const bch2_d_types[DT_MAX] = { ...@@ -73,6 +73,7 @@ const char * const bch2_d_types[DT_MAX] = {
[DT_LNK] = "lnk", [DT_LNK] = "lnk",
[DT_SOCK] = "sock", [DT_SOCK] = "sock",
[DT_WHT] = "whiteout", [DT_WHT] = "whiteout",
[DT_SUBVOL] = "subvol",
}; };
void bch2_opts_apply(struct bch_opts *dst, struct bch_opts src) void bch2_opts_apply(struct bch_opts *dst, struct bch_opts src)
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include "quota.h" #include "quota.h"
#include "recovery.h" #include "recovery.h"
#include "replicas.h" #include "replicas.h"
#include "subvolume.h"
#include "super-io.h" #include "super-io.h"
#include <linux/sort.h> #include <linux/sort.h>
...@@ -961,6 +962,81 @@ static int read_btree_roots(struct bch_fs *c) ...@@ -961,6 +962,81 @@ static int read_btree_roots(struct bch_fs *c)
return ret; return ret;
} }
static int bch2_fs_initialize_subvolumes(struct bch_fs *c)
{
struct bkey_i_snapshot root_snapshot;
struct bkey_i_subvolume root_volume;
int ret;
bkey_snapshot_init(&root_snapshot.k_i);
root_snapshot.k.p.offset = U32_MAX;
root_snapshot.v.flags = 0;
root_snapshot.v.parent = 0;
root_snapshot.v.subvol = BCACHEFS_ROOT_SUBVOL;
root_snapshot.v.pad = 0;
SET_BCH_SNAPSHOT_SUBVOL(&root_snapshot.v, true);
ret = bch2_btree_insert(c, BTREE_ID_snapshots,
&root_snapshot.k_i,
NULL, NULL, 0);
if (ret)
return ret;
bkey_subvolume_init(&root_volume.k_i);
root_volume.k.p.offset = BCACHEFS_ROOT_SUBVOL;
root_volume.v.flags = 0;
root_volume.v.snapshot = cpu_to_le32(U32_MAX);
root_volume.v.inode = cpu_to_le64(BCACHEFS_ROOT_INO);
ret = bch2_btree_insert(c, BTREE_ID_subvolumes,
&root_volume.k_i,
NULL, NULL, 0);
if (ret)
return ret;
return 0;
}
static int bch2_fs_upgrade_for_subvolumes(struct btree_trans *trans)
{
struct bch_fs *c = trans->c;
struct btree_iter iter;
struct bkey_s_c k;
struct bch_inode_unpacked inode;
struct bkey_inode_buf *packed;
int ret;
bch2_trans_iter_init(trans, &iter, BTREE_ID_inodes,
POS(0, BCACHEFS_ROOT_INO), 0);
k = bch2_btree_iter_peek_slot(&iter);
ret = bkey_err(k);
if (ret)
goto err;
if (k.k->type != KEY_TYPE_inode) {
bch_err(c, "root inode not found");
ret = -ENOENT;
goto err;
}
ret = bch2_inode_unpack(bkey_s_c_to_inode(k), &inode);
BUG_ON(ret);
inode.bi_subvol = BCACHEFS_ROOT_SUBVOL;
packed = bch2_trans_kmalloc(trans, sizeof(*packed));
ret = PTR_ERR_OR_ZERO(packed);
if (ret)
goto err;
bch2_inode_pack(c, packed, &inode);
ret = bch2_trans_update(trans, &iter, &packed->inode.k_i, 0);
err:
bch2_trans_iter_exit(trans, &iter);
return ret;
}
int bch2_fs_recovery(struct bch_fs *c) int bch2_fs_recovery(struct bch_fs *c)
{ {
const char *err = "cannot allocate memory"; const char *err = "cannot allocate memory";
...@@ -1017,11 +1093,12 @@ int bch2_fs_recovery(struct bch_fs *c) ...@@ -1017,11 +1093,12 @@ int bch2_fs_recovery(struct bch_fs *c)
c->opts.version_upgrade = true; c->opts.version_upgrade = true;
c->opts.fsck = true; c->opts.fsck = true;
c->opts.fix_errors = FSCK_OPT_YES; c->opts.fix_errors = FSCK_OPT_YES;
} } else if (c->sb.version < bcachefs_metadata_version_btree_ptr_sectors_written) {
if (c->sb.version < bcachefs_metadata_version_btree_ptr_sectors_written) {
bch_info(c, "version prior to btree_ptr_sectors_written, upgrade required"); bch_info(c, "version prior to btree_ptr_sectors_written, upgrade required");
c->opts.version_upgrade = true; c->opts.version_upgrade = true;
} else if (c->sb.version < bcachefs_metadata_version_snapshot) {
bch_info(c, "filesystem version is prior to snapshot field - upgrading");
c->opts.version_upgrade = true;
} }
ret = bch2_blacklist_table_initialize(c); ret = bch2_blacklist_table_initialize(c);
...@@ -1190,6 +1267,29 @@ int bch2_fs_recovery(struct bch_fs *c) ...@@ -1190,6 +1267,29 @@ int bch2_fs_recovery(struct bch_fs *c)
bch_verbose(c, "alloc write done"); bch_verbose(c, "alloc write done");
} }
if (c->sb.version < bcachefs_metadata_version_snapshot) {
err = "error creating root snapshot node";
ret = bch2_fs_initialize_subvolumes(c);
if (ret)
goto err;
}
bch_verbose(c, "reading snapshots table");
err = "error reading snapshots table";
ret = bch2_fs_snapshots_start(c);
if (ret)
goto err;
bch_verbose(c, "reading snapshots done");
if (c->sb.version < bcachefs_metadata_version_snapshot) {
/* set bi_subvol on root inode */
err = "error upgrade root inode for subvolumes";
ret = bch2_trans_do(c, NULL, NULL, BTREE_INSERT_LAZY_RW,
bch2_fs_upgrade_for_subvolumes(&trans));
if (ret)
goto err;
}
if (c->opts.fsck) { if (c->opts.fsck) {
bch_info(c, "starting fsck"); bch_info(c, "starting fsck");
err = "error in fsck"; err = "error in fsck";
...@@ -1350,9 +1450,22 @@ int bch2_fs_initialize(struct bch_fs *c) ...@@ -1350,9 +1450,22 @@ int bch2_fs_initialize(struct bch_fs *c)
} }
} }
err = "error creating root snapshot node";
ret = bch2_fs_initialize_subvolumes(c);
if (ret)
goto err;
bch_verbose(c, "reading snapshots table");
err = "error reading snapshots table";
ret = bch2_fs_snapshots_start(c);
if (ret)
goto err;
bch_verbose(c, "reading snapshots done");
bch2_inode_init(c, &root_inode, 0, 0, bch2_inode_init(c, &root_inode, 0, 0,
S_IFDIR|S_IRWXU|S_IRUGO|S_IXUGO, 0, NULL); S_IFDIR|S_IRWXU|S_IRUGO|S_IXUGO, 0, NULL);
root_inode.bi_inum = BCACHEFS_ROOT_INO; root_inode.bi_inum = BCACHEFS_ROOT_INO;
root_inode.bi_subvol = BCACHEFS_ROOT_SUBVOL;
bch2_inode_pack(c, &packed_inode, &root_inode); bch2_inode_pack(c, &packed_inode, &root_inode);
packed_inode.inode.k.p.snapshot = U32_MAX; packed_inode.inode.k.p.snapshot = U32_MAX;
......
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _BCACHEFS_SUBVOLUME_H
#define _BCACHEFS_SUBVOLUME_H
void bch2_snapshot_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c);
const char *bch2_snapshot_invalid(const struct bch_fs *, struct bkey_s_c);
#define bch2_bkey_ops_snapshot (struct bkey_ops) { \
.key_invalid = bch2_snapshot_invalid, \
.val_to_text = bch2_snapshot_to_text, \
}
int bch2_mark_snapshot(struct bch_fs *, struct bkey_s_c,
struct bkey_s_c, u64, unsigned);
static inline struct snapshot_t *snapshot_t(struct bch_fs *c, u32 id)
{
return genradix_ptr(&c->snapshots, U32_MAX - id);
}
static inline u32 bch2_snapshot_parent(struct bch_fs *c, u32 id)
{
return snapshot_t(c, id)->parent;
}
static inline u32 bch2_snapshot_internal_node(struct bch_fs *c, u32 id)
{
struct snapshot_t *s = snapshot_t(c, id);
return s->children[0] || s->children[1];
}
static inline u32 bch2_snapshot_sibling(struct bch_fs *c, u32 id)
{
struct snapshot_t *s;
u32 parent = bch2_snapshot_parent(c, id);
if (!parent)
return 0;
s = snapshot_t(c, bch2_snapshot_parent(c, id));
if (id == s->children[0])
return s->children[1];
if (id == s->children[1])
return s->children[0];
return 0;
}
static inline bool bch2_snapshot_is_ancestor(struct bch_fs *c, u32 id, u32 ancestor)
{
while (id && id < ancestor)
id = bch2_snapshot_parent(c, id);
return id == ancestor;
}
int bch2_fs_snapshots_check(struct bch_fs *);
void bch2_fs_snapshots_exit(struct bch_fs *);
int bch2_fs_snapshots_start(struct bch_fs *);
const char *bch2_subvolume_invalid(const struct bch_fs *, struct bkey_s_c);
void bch2_subvolume_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c);
#define bch2_bkey_ops_subvolume (struct bkey_ops) { \
.key_invalid = bch2_subvolume_invalid, \
.val_to_text = bch2_subvolume_to_text, \
}
int bch2_subvolume_get_snapshot(struct btree_trans *, u32, u32 *);
int bch2_subvolume_delete(struct btree_trans *, u32, int);
int bch2_subvolume_create(struct btree_trans *, u64, u32,
u32 *, u32 *, bool);
int bch2_fs_subvolumes_init(struct bch_fs *);
#endif /* _BCACHEFS_SUBVOLUME_H */
...@@ -39,6 +39,7 @@ ...@@ -39,6 +39,7 @@
#include "rebalance.h" #include "rebalance.h"
#include "recovery.h" #include "recovery.h"
#include "replicas.h" #include "replicas.h"
#include "subvolume.h"
#include "super.h" #include "super.h"
#include "super-io.h" #include "super-io.h"
#include "sysfs.h" #include "sysfs.h"
...@@ -475,6 +476,7 @@ static void __bch2_fs_free(struct bch_fs *c) ...@@ -475,6 +476,7 @@ static void __bch2_fs_free(struct bch_fs *c)
for (i = 0; i < BCH_TIME_STAT_NR; i++) for (i = 0; i < BCH_TIME_STAT_NR; i++)
bch2_time_stats_exit(&c->times[i]); bch2_time_stats_exit(&c->times[i]);
bch2_fs_snapshots_exit(c);
bch2_fs_quota_exit(c); bch2_fs_quota_exit(c);
bch2_fs_fsio_exit(c); bch2_fs_fsio_exit(c);
bch2_fs_ec_exit(c); bch2_fs_ec_exit(c);
...@@ -694,6 +696,7 @@ static struct bch_fs *bch2_fs_alloc(struct bch_sb *sb, struct bch_opts opts) ...@@ -694,6 +696,7 @@ static struct bch_fs *bch2_fs_alloc(struct bch_sb *sb, struct bch_opts opts)
mutex_init(&c->usage_scratch_lock); mutex_init(&c->usage_scratch_lock);
mutex_init(&c->bio_bounce_pages_lock); mutex_init(&c->bio_bounce_pages_lock);
mutex_init(&c->snapshot_table_lock);
spin_lock_init(&c->btree_write_error_lock); spin_lock_init(&c->btree_write_error_lock);
...@@ -797,6 +800,7 @@ static struct bch_fs *bch2_fs_alloc(struct bch_sb *sb, struct bch_opts opts) ...@@ -797,6 +800,7 @@ static struct bch_fs *bch2_fs_alloc(struct bch_sb *sb, struct bch_opts opts)
bch2_fs_btree_key_cache_init(&c->btree_key_cache) || bch2_fs_btree_key_cache_init(&c->btree_key_cache) ||
bch2_fs_btree_iter_init(c) || bch2_fs_btree_iter_init(c) ||
bch2_fs_btree_interior_update_init(c) || bch2_fs_btree_interior_update_init(c) ||
bch2_fs_subvolumes_init(c) ||
bch2_fs_io_init(c) || bch2_fs_io_init(c) ||
bch2_fs_encryption_init(c) || bch2_fs_encryption_init(c) ||
bch2_fs_compress_init(c) || bch2_fs_compress_init(c) ||
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment