} while ((v = cmpxchg(&dst->ei_journal_seq, old, journal_seq)) != old);
}
-static inline int ptrcmp(void *l, void *r)
-{
- return (l > r) - (l < r);
-}
-
-#define __bch2_lock_inodes(_lock, ...) \
-do { \
- struct bch_inode_info *a[] = { NULL, __VA_ARGS__ }; \
- unsigned i; \
- \
- bubble_sort(&a[1], ARRAY_SIZE(a) - 1 , ptrcmp); \
- \
- for (i = ARRAY_SIZE(a) - 1; a[i]; --i) \
- if (a[i] != a[i - 1]) { \
- if (_lock) \
- mutex_lock_nested(&a[i]->ei_update_lock, i);\
- else \
- mutex_unlock(&a[i]->ei_update_lock); \
- } \
-} while (0)
-
-#define bch2_lock_inodes(...) __bch2_lock_inodes(true, __VA_ARGS__)
-#define bch2_unlock_inodes(...) __bch2_lock_inodes(false, __VA_ARGS__)
-
/*
* I_SIZE_DIRTY requires special handling:
*
inode->v.i_ctime = bch2_time_to_timespec(c, bi->bi_ctime);
inode->ei_inode = *bi;
- inode->ei_qid = bch_qid(bi);
bch2_inode_flags_to_vfs(inode);
}
inode_set_fn set,
void *p)
{
- struct btree_iter *iter;
+ struct bch_fs *c = trans->c;
+ struct btree_iter *iter = NULL;
struct bkey_inode_buf *inode_p;
int ret;
lockdep_assert_held(&inode->ei_update_lock);
- iter = bch2_trans_get_iter(trans, BTREE_ID_INODES,
- POS(inode->v.i_ino, 0),
- BTREE_ITER_SLOTS|BTREE_ITER_INTENT);
- if (IS_ERR(iter))
- return PTR_ERR(iter);
-
- /* The btree node lock is our lock on the inode: */
- ret = bch2_btree_iter_traverse(iter);
- if (ret)
- return ret;
+ if (c->opts.new_inode_updates) {
+ /* XXX: Don't do this with btree locks held */
+ if (!inode->ei_inode_update)
+ inode->ei_inode_update =
+ bch2_deferred_update_alloc(c, BTREE_ID_INODES, 64);
+ } else {
+ iter = bch2_trans_get_iter(trans, BTREE_ID_INODES,
+ POS(inode->v.i_ino, 0),
+ BTREE_ITER_SLOTS|BTREE_ITER_INTENT);
+ if (IS_ERR(iter))
+ return PTR_ERR(iter);
+
+ /* The btree node lock is our lock on the inode: */
+ ret = bch2_btree_iter_traverse(iter);
+ if (ret)
+ return ret;
+ }
*inode_u = inode->ei_inode;
return PTR_ERR(inode_p);
bch2_inode_pack(inode_p, inode_u);
- bch2_trans_update(trans, BTREE_INSERT_ENTRY(iter, &inode_p->inode.k_i));
+
+ if (!inode->ei_inode_update)
+ bch2_trans_update(trans,
+ BTREE_INSERT_ENTRY(iter, &inode_p->inode.k_i));
+ else
+ bch2_trans_update(trans,
+ BTREE_INSERT_DEFERRED(inode->ei_inode_update,
+ &inode_p->inode.k_i));
+
return 0;
}
struct bch_inode_unpacked inode_u;
int ret;
- bch2_trans_init(&trans, c);
+ bch2_trans_init(&trans, c, 0, 0);
retry:
bch2_trans_begin(&trans);
return ret < 0 ? ret : 0;
}
-static struct inode *bch2_vfs_inode_get(struct bch_fs *c, u64 inum)
+int bch2_fs_quota_transfer(struct bch_fs *c,
+ struct bch_inode_info *inode,
+ struct bch_qid new_qid,
+ unsigned qtypes,
+ enum quota_acct_mode mode)
+{
+ unsigned i;
+ int ret;
+
+ qtypes &= enabled_qtypes(c);
+
+ for (i = 0; i < QTYP_NR; i++)
+ if (new_qid.q[i] == inode->ei_qid.q[i])
+ qtypes &= ~(1U << i);
+
+ if (!qtypes)
+ return 0;
+
+ mutex_lock(&inode->ei_quota_lock);
+
+ ret = bch2_quota_transfer(c, qtypes, new_qid,
+ inode->ei_qid,
+ inode->v.i_blocks +
+ inode->ei_quota_reserved,
+ mode);
+ if (!ret)
+ for (i = 0; i < QTYP_NR; i++)
+ if (qtypes & (1 << i))
+ inode->ei_qid.q[i] = new_qid.q[i];
+
+ mutex_unlock(&inode->ei_quota_lock);
+
+ return ret;
+}
+
+int bch2_reinherit_attrs_fn(struct bch_inode_info *inode,
+ struct bch_inode_unpacked *bi,
+ void *p)
+{
+ struct bch_inode_info *dir = p;
+ u64 src, dst;
+ unsigned id;
+ int ret = 1;
+
+ for (id = 0; id < Inode_opt_nr; id++) {
+ if (bi->bi_fields_set & (1 << id))
+ continue;
+
+ src = bch2_inode_opt_get(&dir->ei_inode, id);
+ dst = bch2_inode_opt_get(bi, id);
+
+ if (src == dst)
+ continue;
+
+ bch2_inode_opt_set(bi, id, src);
+ ret = 0;
+ }
+
+ return ret;
+}
+
+struct inode *bch2_vfs_inode_get(struct bch_fs *c, u64 inum)
{
struct bch_inode_unpacked inode_u;
struct bch_inode_info *inode;
bch2_inode_init(c, &inode_u, 0, 0, 0, rdev, &dir->ei_inode);
bch2_inode_init_owner(&inode_u, &dir->v, mode);
- inode_u.bi_project = dir->ei_qid.q[QTYP_PRJ];
-
hash_info = bch2_hash_info_init(c, &inode_u);
if (tmpfile)
inode_u.bi_flags |= BCH_INODE_UNLINKED;
- ret = bch2_quota_acct(c, bch_qid(&inode_u), Q_INO, 1, BCH_QUOTA_PREALLOC);
+ ret = bch2_quota_acct(c, bch_qid(&inode_u), Q_INO, 1,
+ KEY_TYPE_QUOTA_PREALLOC);
if (ret)
return ERR_PTR(ret);
if (!tmpfile)
mutex_lock(&dir->ei_update_lock);
- bch2_trans_init(&trans, c);
+ bch2_trans_init(&trans, c, 8, 1024);
retry:
bch2_trans_begin(&trans);
if (unlikely(ret))
goto err_trans;
- atomic_long_inc(&c->nr_inodes);
-
if (!tmpfile) {
bch2_inode_update_after_write(c, dir, &dir_u,
ATTR_MTIME|ATTR_CTIME);
- journal_seq_copy(dir, inode->ei_journal_seq);
+ journal_seq_copy(dir, journal_seq);
mutex_unlock(&dir->ei_update_lock);
}
make_bad_inode(&inode->v);
iput(&inode->v);
err:
- bch2_quota_acct(c, bch_qid(&inode_u), Q_INO, -1, BCH_QUOTA_WARN);
+ bch2_quota_acct(c, bch_qid(&inode_u), Q_INO, -1, KEY_TYPE_QUOTA_WARN);
inode = ERR_PTR(ret);
goto out;
}
int ret;
mutex_lock(&inode->ei_update_lock);
- bch2_trans_init(&trans, c);
+ bch2_trans_init(&trans, c, 4, 1024);
retry:
bch2_trans_begin(&trans);
int ret;
bch2_lock_inodes(dir, inode);
- bch2_trans_init(&trans, c);
+ bch2_trans_init(&trans, c, 4, 1024);
retry:
bch2_trans_begin(&trans);
void *p)
{
struct rename_info *info = p;
+ int ret;
if (inode == info->src_dir) {
bi->bi_nlink -= S_ISDIR(info->src_inode->v.i_mode);
S_ISDIR(info->dst_inode->v.i_mode);
}
+ if (inode == info->src_inode) {
+ ret = bch2_reinherit_attrs_fn(inode, bi, info->dst_dir);
+
+ BUG_ON(!ret && S_ISDIR(info->src_inode->v.i_mode));
+ }
+
+ if (inode == info->dst_inode &&
+ info->mode == BCH_RENAME_EXCHANGE) {
+ ret = bch2_reinherit_attrs_fn(inode, bi, info->src_dir);
+
+ BUG_ON(!ret && S_ISDIR(info->dst_inode->v.i_mode));
+ }
+
if (inode == info->dst_inode &&
info->mode == BCH_RENAME_OVERWRITE) {
BUG_ON(bi->bi_nlink &&
return ret;
}
+ bch2_trans_init(&trans, c, 8, 2048);
+
bch2_lock_inodes(i.src_dir,
i.dst_dir,
i.src_inode,
i.dst_inode);
- bch2_trans_init(&trans, c);
+ if (S_ISDIR(i.src_inode->v.i_mode) &&
+ inode_attrs_changing(i.dst_dir, i.src_inode)) {
+ ret = -EXDEV;
+ goto err;
+ }
+
+ if (i.mode == BCH_RENAME_EXCHANGE &&
+ S_ISDIR(i.dst_inode->v.i_mode) &&
+ inode_attrs_changing(i.src_dir, i.dst_inode)) {
+ ret = -EXDEV;
+ goto err;
+ }
+
+ if (inode_attr_changing(i.dst_dir, i.src_inode, Inode_opt_project)) {
+ ret = bch2_fs_quota_transfer(c, i.src_inode,
+ i.dst_dir->ei_qid,
+ 1 << QTYP_PRJ,
+ KEY_TYPE_QUOTA_PREALLOC);
+ if (ret)
+ goto err;
+ }
+
+ if (i.mode == BCH_RENAME_EXCHANGE &&
+ inode_attr_changing(i.src_dir, i.dst_inode, Inode_opt_project)) {
+ ret = bch2_fs_quota_transfer(c, i.dst_inode,
+ i.src_dir->ei_qid,
+ 1 << QTYP_PRJ,
+ KEY_TYPE_QUOTA_PREALLOC);
+ if (ret)
+ goto err;
+ }
+
retry:
bch2_trans_begin(&trans);
i.now = bch2_current_time(c);
ATTR_CTIME);
err:
bch2_trans_exit(&trans);
+
+ bch2_fs_quota_transfer(c, i.src_inode,
+ bch_qid(&i.src_inode->ei_inode),
+ 1 << QTYP_PRJ,
+ KEY_TYPE_QUOTA_NOCHECK);
+ if (i.dst_inode)
+ bch2_fs_quota_transfer(c, i.dst_inode,
+ bch_qid(&i.dst_inode->ei_inode),
+ 1 << QTYP_PRJ,
+ KEY_TYPE_QUOTA_NOCHECK);
+
bch2_unlock_inodes(i.src_dir,
i.dst_dir,
i.src_inode,
static int bch2_setattr_nonsize(struct bch_inode_info *inode, struct iattr *iattr)
{
struct bch_fs *c = inode->v.i_sb->s_fs_info;
- struct bch_qid qid = inode->ei_qid;
+ struct bch_qid qid;
struct btree_trans trans;
struct bch_inode_unpacked inode_u;
struct posix_acl *acl = NULL;
- unsigned qtypes = 0;
int ret;
mutex_lock(&inode->ei_update_lock);
- if (c->opts.usrquota &&
- (iattr->ia_valid & ATTR_UID) &&
- !uid_eq(iattr->ia_uid, inode->v.i_uid)) {
- qid.q[QTYP_USR] = from_kuid(&init_user_ns, iattr->ia_uid),
- qtypes |= 1 << QTYP_USR;
- }
+ qid = inode->ei_qid;
- if (c->opts.grpquota &&
- (iattr->ia_valid & ATTR_GID) &&
- !gid_eq(iattr->ia_gid, inode->v.i_gid)) {
+ if (iattr->ia_valid & ATTR_UID)
+ qid.q[QTYP_USR] = from_kuid(&init_user_ns, iattr->ia_uid);
+
+ if (iattr->ia_valid & ATTR_GID)
qid.q[QTYP_GRP] = from_kgid(&init_user_ns, iattr->ia_gid);
- qtypes |= 1 << QTYP_GRP;
- }
- if (qtypes) {
- ret = bch2_quota_transfer(c, qtypes, qid, inode->ei_qid,
- inode->v.i_blocks +
- inode->ei_quota_reserved);
- if (ret)
- goto err;
- }
+ ret = bch2_fs_quota_transfer(c, inode, qid, ~0,
+ KEY_TYPE_QUOTA_PREALLOC);
+ if (ret)
+ goto err;
- bch2_trans_init(&trans, c);
+ bch2_trans_init(&trans, c, 0, 0);
retry:
bch2_trans_begin(&trans);
kfree(acl);
{
if (bkey_extent_is_data(&k->k)) {
struct bkey_s_c_extent e = bkey_i_to_s_c_extent(k);
- const struct bch_extent_ptr *ptr;
- struct bch_extent_crc_unpacked crc;
+ const union bch_extent_entry *entry;
+ struct extent_ptr_decoded p;
int ret;
- extent_for_each_ptr_crc(e, ptr, crc) {
+ extent_for_each_ptr_decode(e, p, entry) {
int flags2 = 0;
- u64 offset = ptr->offset;
+ u64 offset = p.ptr.offset;
- if (crc.compression_type)
+ if (p.crc.compression_type)
flags2 |= FIEMAP_EXTENT_ENCODED;
else
- offset += crc.offset;
+ offset += p.crc.offset;
if ((offset & (PAGE_SECTORS - 1)) ||
(e.k->size & (PAGE_SECTORS - 1)))
flags2 |= FIEMAP_EXTENT_NOT_ALIGNED;
ret = fiemap_fill_next_extent(info,
- bkey_start_offset(e.k) << 9,
- offset << 9,
- e.k->size << 9, flags|flags2);
+ bkey_start_offset(e.k) << 9,
+ offset << 9,
+ e.k->size << 9, flags|flags2);
if (ret)
return ret;
}
return 0;
- } else if (k->k.type == BCH_RESERVATION) {
+ } else if (k->k.type == KEY_TYPE_reservation) {
return fiemap_fill_next_extent(info,
bkey_start_offset(&k->k) << 9,
0, k->k.size << 9,
{
struct bch_fs *c = vinode->i_sb->s_fs_info;
struct bch_inode_info *ei = to_bch_ei(vinode);
- struct btree_iter iter;
+ struct btree_trans trans;
+ struct btree_iter *iter;
struct bkey_s_c k;
BKEY_PADDED(k) tmp;
bool have_extent = false;
if (start + len < start)
return -EINVAL;
- for_each_btree_key(&iter, c, BTREE_ID_EXTENTS,
- POS(ei->v.i_ino, start >> 9), 0, k)
+ bch2_trans_init(&trans, c, 0, 0);
+
+ for_each_btree_key(&trans, iter, BTREE_ID_EXTENTS,
+ POS(ei->v.i_ino, start >> 9), 0, k, ret)
if (bkey_extent_is_data(k.k) ||
- k.k->type == BCH_RESERVATION) {
+ k.k->type == KEY_TYPE_reservation) {
if (bkey_cmp(bkey_start_pos(k.k),
POS(ei->v.i_ino, (start + len) >> 9)) >= 0)
break;
if (have_extent) {
ret = bch2_fill_extent(info, &tmp.k, 0);
if (ret)
- goto out;
+ break;
}
bkey_reassemble(&tmp.k, k);
have_extent = true;
}
- if (have_extent)
+ if (!ret && have_extent)
ret = bch2_fill_extent(info, &tmp.k, FIEMAP_EXTENT_LAST);
-out:
- bch2_btree_iter_unlock(&iter);
+
+ ret = bch2_trans_exit(&trans) ?: ret;
return ret < 0 ? ret : 0;
}
inode->ei_journal_seq = 0;
inode->ei_quota_reserved = 0;
inode->ei_str_hash = bch2_hash_info_init(c, bi);
+ inode->ei_qid = bch_qid(bi);
inode->v.i_mapping->a_ops = &bch_address_space_operations;
inode_init_once(&inode->v);
mutex_init(&inode->ei_update_lock);
mutex_init(&inode->ei_quota_lock);
+ inode->ei_inode_update = NULL;
inode->ei_journal_seq = 0;
return &inode->v;
BUG_ON(!is_bad_inode(&inode->v) && inode->ei_quota_reserved);
+ if (inode->ei_inode_update)
+ bch2_deferred_update_free(c, inode->ei_inode_update);
+ inode->ei_inode_update = NULL;
+
if (!inode->v.i_nlink && !is_bad_inode(&inode->v)) {
bch2_quota_acct(c, inode->ei_qid, Q_SPC, -((s64) inode->v.i_blocks),
- BCH_QUOTA_WARN);
+ KEY_TYPE_QUOTA_WARN);
bch2_quota_acct(c, inode->ei_qid, Q_INO, -1,
- BCH_QUOTA_WARN);
+ KEY_TYPE_QUOTA_WARN);
bch2_inode_rm(c, inode->v.i_ino);
-
- WARN_ONCE(atomic_long_dec_return(&c->nr_inodes) < 0,
- "nr_inodes < 0");
}
}
{
struct super_block *sb = dentry->d_sb;
struct bch_fs *c = sb->s_fs_info;
- struct bch_fs_usage usage = bch2_fs_usage_read(c);
- u64 hidden_metadata = usage.buckets[BCH_DATA_SB] +
- usage.buckets[BCH_DATA_JOURNAL];
+ struct bch_fs_usage_short usage = bch2_fs_usage_read_short(c);
unsigned shift = sb->s_blocksize_bits - 9;
u64 fsid;
buf->f_type = BCACHEFS_STATFS_MAGIC;
buf->f_bsize = sb->s_blocksize;
- buf->f_blocks = (c->capacity - hidden_metadata) >> shift;
- buf->f_bfree = (c->capacity - bch2_fs_sectors_used(c, usage)) >> shift;
+ buf->f_blocks = usage.capacity >> shift;
+ buf->f_bfree = (usage.capacity - usage.used) >> shift;
buf->f_bavail = buf->f_bfree;
- buf->f_files = atomic_long_read(&c->nr_inodes);
+ buf->f_files = usage.nr_inodes;
buf->f_ffree = U64_MAX;
fsid = le64_to_cpup((void *) c->sb.user_uuid.b) ^
*/
c1 = bch2_path_to_fs(devs[0]);
- if (!c1)
+ if (IS_ERR(c1))
return c;
for (i = 1; i < nr_devs; i++) {
mutex_lock(&c->state_lock);
- if (!bch2_fs_running(c)) {
+ if (!test_bit(BCH_FS_STARTED, &c->flags)) {
mutex_unlock(&c->state_lock);
closure_put(&c->cl);
pr_err("err mounting %s: incomplete filesystem", dev_name);
struct bch_opts opts = bch2_opts_empty();
int ret;
- opt_set(opts, read_only, (*flags & MS_RDONLY) != 0);
+ opt_set(opts, read_only, (*flags & SB_RDONLY) != 0);
ret = bch2_parse_mount_opts(&opts, data);
if (ret)
return ret;
if (opts.read_only != c->opts.read_only) {
- const char *err = NULL;
-
mutex_lock(&c->state_lock);
if (opts.read_only) {
bch2_fs_read_only(c);
- sb->s_flags |= MS_RDONLY;
+ sb->s_flags |= SB_RDONLY;
} else {
- err = bch2_fs_read_write(c);
- if (err) {
- bch_err(c, "error going rw: %s", err);
+ ret = bch2_fs_read_write(c);
+ if (ret) {
+ bch_err(c, "error going rw: %i", ret);
+ mutex_unlock(&c->state_lock);
return -EINVAL;
}
- sb->s_flags &= ~MS_RDONLY;
+ sb->s_flags &= ~SB_RDONLY;
}
c->opts.read_only = opts.read_only;
const struct bch_option *opt = &bch2_opt_table[i];
u64 v = bch2_opt_get_by_id(&c->opts, i);
- if (opt->mode < OPT_MOUNT)
+ if (!(opt->mode & OPT_MOUNT))
continue;
if (v == bch2_opt_get_by_id(&bch2_opts_default, i))
continue;
- bch2_opt_to_text(c, buf, sizeof(buf), opt, v,
+ bch2_opt_to_text(&PBUF(buf), c, opt, v,
OPT_SHOW_MOUNT_STYLE);
seq_putc(seq, ',');
seq_puts(seq, buf);
unsigned i;
int ret;
- opt_set(opts, read_only, (flags & MS_RDONLY) != 0);
+ opt_set(opts, read_only, (flags & SB_RDONLY) != 0);
ret = bch2_parse_mount_opts(&opts, data);
if (ret)
if (IS_ERR(c))
return ERR_CAST(c);
- sb = sget(fs_type, bch2_test_super, bch2_set_super, flags|MS_NOSEC, c);
+ sb = sget(fs_type, bch2_test_super, bch2_set_super, flags|SB_NOSEC, c);
if (IS_ERR(sb)) {
closure_put(&c->cl);
return ERR_CAST(sb);
if (sb->s_root) {
closure_put(&c->cl);
- if ((flags ^ sb->s_flags) & MS_RDONLY) {
+ if ((flags ^ sb->s_flags) & SB_RDONLY) {
ret = -EBUSY;
goto err_put_super;
}
#ifdef CONFIG_BCACHEFS_POSIX_ACL
if (c->opts.acl)
- sb->s_flags |= MS_POSIXACL;
+ sb->s_flags |= SB_POSIXACL;
#endif
vinode = bch2_vfs_inode_get(c, BCACHEFS_ROOT_INO);
if (IS_ERR(vinode)) {
+ bch_err(c, "error mounting: error getting root inode %i",
+ (int) PTR_ERR(vinode));
ret = PTR_ERR(vinode);
goto err_put_super;
}
sb->s_root = d_make_root(vinode);
if (!sb->s_root) {
+ bch_err(c, "error mounting: error allocating root dentry");
ret = -ENOMEM;
goto err_put_super;
}
- sb->s_flags |= MS_ACTIVE;
+ sb->s_flags |= SB_ACTIVE;
out:
return dget(sb->s_root);