Skip to content

Commit 3bb17a2

Browse files
josefbacikkdave
authored andcommitted
btrfs: add get_tree callback for new mount API
This is the actual mounting callback for the new mount API. Implement this using our current fill super as a guideline, making the appropriate adjustments for the new mount API. Our old mount operation had two fs_types, one to handle the actual opening, and the one that we called to handle the actual opening and then did the subvol lookup for returning the actual root dentry. This is mirrored here, but simply with different behaviors for ->get_tree. We use the existence of ->s_fs_info to tell which part we're in. The initial call allocates the fs_info, then call mount_fc() with a duplicated fc to do the actual open_ctree part. Then we take that vfsmount and use it to look up our subvolume that we're mounting and return that as our s_root. This idea was taken from Christians attempt to convert us to the new mount API [1]. In btrfs_get_tree_super() the mount device is scanned and opened in one go under uuid_mutex we expect that all related devices have been already scanned, either by mount or from the outside. A device forget can be called on some of the devices as the whole context is not protected but it's an unlikely event, though it's a minor behaviour change. References: https://lore.kernel.org/all/[email protected]/ Reviewed-by: Christian Brauner <[email protected]> Reviewed-by: Johannes Thumshirn <[email protected]> Signed-off-by: Josef Bacik <[email protected]> Reviewed-by: David Sterba <[email protected]> [ add note about device scanning ] Signed-off-by: David Sterba <[email protected]>
1 parent eddb1a4 commit 3bb17a2

File tree

1 file changed

+204
-4
lines changed

1 file changed

+204
-4
lines changed

fs/btrfs/super.c

Lines changed: 204 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ struct btrfs_fs_context {
9898
unsigned long mount_opt;
9999
unsigned long compress_type:4;
100100
unsigned int compress_level;
101+
refcount_t refs;
101102
};
102103

103104
enum {
@@ -2797,6 +2798,180 @@ static int btrfs_statfs(struct dentry *dentry, struct kstatfs *buf)
27972798
return 0;
27982799
}
27992800

2801+
static int btrfs_fc_test_super(struct super_block *sb, struct fs_context *fc)
2802+
{
2803+
struct btrfs_fs_info *p = fc->s_fs_info;
2804+
struct btrfs_fs_info *fs_info = btrfs_sb(sb);
2805+
2806+
return fs_info->fs_devices == p->fs_devices;
2807+
}
2808+
2809+
static int btrfs_get_tree_super(struct fs_context *fc)
2810+
{
2811+
struct btrfs_fs_info *fs_info = fc->s_fs_info;
2812+
struct btrfs_fs_context *ctx = fc->fs_private;
2813+
struct btrfs_fs_devices *fs_devices = NULL;
2814+
struct block_device *bdev;
2815+
struct btrfs_device *device;
2816+
struct super_block *sb;
2817+
blk_mode_t mode = sb_open_mode(fc->sb_flags);
2818+
int ret;
2819+
2820+
btrfs_ctx_to_info(fs_info, ctx);
2821+
mutex_lock(&uuid_mutex);
2822+
2823+
/*
2824+
* With 'true' passed to btrfs_scan_one_device() (mount time) we expect
2825+
* either a valid device or an error.
2826+
*/
2827+
device = btrfs_scan_one_device(fc->source, mode, true);
2828+
ASSERT(device != NULL);
2829+
if (IS_ERR(device)) {
2830+
mutex_unlock(&uuid_mutex);
2831+
return PTR_ERR(device);
2832+
}
2833+
2834+
fs_devices = device->fs_devices;
2835+
fs_info->fs_devices = fs_devices;
2836+
2837+
ret = btrfs_open_devices(fs_devices, mode, &btrfs_fs_type);
2838+
mutex_unlock(&uuid_mutex);
2839+
if (ret)
2840+
return ret;
2841+
2842+
if (!(fc->sb_flags & SB_RDONLY) && fs_devices->rw_devices == 0) {
2843+
ret = -EACCES;
2844+
goto error;
2845+
}
2846+
2847+
bdev = fs_devices->latest_dev->bdev;
2848+
2849+
/*
2850+
* From now on the error handling is not straightforward.
2851+
*
2852+
* If successful, this will transfer the fs_info into the super block,
2853+
* and fc->s_fs_info will be NULL. However if there's an existing
2854+
* super, we'll still have fc->s_fs_info populated. If we error
2855+
* completely out it'll be cleaned up when we drop the fs_context,
2856+
* otherwise it's tied to the lifetime of the super_block.
2857+
*/
2858+
sb = sget_fc(fc, btrfs_fc_test_super, set_anon_super_fc);
2859+
if (IS_ERR(sb)) {
2860+
ret = PTR_ERR(sb);
2861+
goto error;
2862+
}
2863+
2864+
if (sb->s_root) {
2865+
btrfs_close_devices(fs_devices);
2866+
if ((fc->sb_flags ^ sb->s_flags) & SB_RDONLY)
2867+
ret = -EBUSY;
2868+
} else {
2869+
snprintf(sb->s_id, sizeof(sb->s_id), "%pg", bdev);
2870+
shrinker_debugfs_rename(sb->s_shrink, "sb-btrfs:%s", sb->s_id);
2871+
btrfs_sb(sb)->bdev_holder = &btrfs_fs_type;
2872+
ret = btrfs_fill_super(sb, fs_devices, NULL);
2873+
}
2874+
2875+
if (ret) {
2876+
deactivate_locked_super(sb);
2877+
return ret;
2878+
}
2879+
2880+
fc->root = dget(sb->s_root);
2881+
return 0;
2882+
2883+
error:
2884+
btrfs_close_devices(fs_devices);
2885+
return ret;
2886+
}
2887+
2888+
static int btrfs_get_tree_subvol(struct fs_context *fc)
2889+
{
2890+
struct btrfs_fs_info *fs_info = NULL;
2891+
struct btrfs_fs_context *ctx = fc->fs_private;
2892+
struct fs_context *dup_fc;
2893+
struct dentry *dentry;
2894+
struct vfsmount *mnt;
2895+
2896+
/*
2897+
* Setup a dummy root and fs_info for test/set super. This is because
2898+
* we don't actually fill this stuff out until open_ctree, but we need
2899+
* then open_ctree will properly initialize the file system specific
2900+
* settings later. btrfs_init_fs_info initializes the static elements
2901+
* of the fs_info (locks and such) to make cleanup easier if we find a
2902+
* superblock with our given fs_devices later on at sget() time.
2903+
*/
2904+
fs_info = kvzalloc(sizeof(struct btrfs_fs_info), GFP_KERNEL);
2905+
if (!fs_info)
2906+
return -ENOMEM;
2907+
2908+
fs_info->super_copy = kzalloc(BTRFS_SUPER_INFO_SIZE, GFP_KERNEL);
2909+
fs_info->super_for_commit = kzalloc(BTRFS_SUPER_INFO_SIZE, GFP_KERNEL);
2910+
if (!fs_info->super_copy || !fs_info->super_for_commit) {
2911+
btrfs_free_fs_info(fs_info);
2912+
return -ENOMEM;
2913+
}
2914+
btrfs_init_fs_info(fs_info);
2915+
2916+
dup_fc = vfs_dup_fs_context(fc);
2917+
if (IS_ERR(dup_fc)) {
2918+
btrfs_free_fs_info(fs_info);
2919+
return PTR_ERR(dup_fc);
2920+
}
2921+
2922+
/*
2923+
* When we do the sget_fc this gets transferred to the sb, so we only
2924+
* need to set it on the dup_fc as this is what creates the super block.
2925+
*/
2926+
dup_fc->s_fs_info = fs_info;
2927+
2928+
/*
2929+
* We'll do the security settings in our btrfs_get_tree_super() mount
2930+
* loop, they were duplicated into dup_fc, we can drop the originals
2931+
* here.
2932+
*/
2933+
security_free_mnt_opts(&fc->security);
2934+
fc->security = NULL;
2935+
2936+
mnt = fc_mount(dup_fc);
2937+
put_fs_context(dup_fc);
2938+
if (IS_ERR(mnt))
2939+
return PTR_ERR(mnt);
2940+
2941+
/*
2942+
* This free's ->subvol_name, because if it isn't set we have to
2943+
* allocate a buffer to hold the subvol_name, so we just drop our
2944+
* reference to it here.
2945+
*/
2946+
dentry = mount_subvol(ctx->subvol_name, ctx->subvol_objectid, mnt);
2947+
ctx->subvol_name = NULL;
2948+
if (IS_ERR(dentry))
2949+
return PTR_ERR(dentry);
2950+
2951+
fc->root = dentry;
2952+
return 0;
2953+
}
2954+
2955+
static int btrfs_get_tree(struct fs_context *fc)
2956+
{
2957+
/*
2958+
* Since we use mount_subtree to mount the default/specified subvol, we
2959+
* have to do mounts in two steps.
2960+
*
2961+
* First pass through we call btrfs_get_tree_subvol(), this is just a
2962+
* wrapper around fc_mount() to call back into here again, and this time
2963+
* we'll call btrfs_get_tree_super(). This will do the open_ctree() and
2964+
* everything to open the devices and file system. Then we return back
2965+
* with a fully constructed vfsmount in btrfs_get_tree_subvol(), and
2966+
* from there we can do our mount_subvol() call, which will lookup
2967+
* whichever subvol we're mounting and setup this fc with the
2968+
* appropriate dentry for the subvol.
2969+
*/
2970+
if (fc->s_fs_info)
2971+
return btrfs_get_tree_super(fc);
2972+
return btrfs_get_tree_subvol(fc);
2973+
}
2974+
28002975
static void btrfs_kill_super(struct super_block *sb)
28012976
{
28022977
struct btrfs_fs_info *fs_info = btrfs_sb(sb);
@@ -2807,17 +2982,41 @@ static void btrfs_kill_super(struct super_block *sb)
28072982
static void btrfs_free_fs_context(struct fs_context *fc)
28082983
{
28092984
struct btrfs_fs_context *ctx = fc->fs_private;
2985+
struct btrfs_fs_info *fs_info = fc->s_fs_info;
28102986

2811-
if (!ctx)
2812-
return;
2987+
if (fs_info)
2988+
btrfs_free_fs_info(fs_info);
2989+
2990+
if (ctx && refcount_dec_and_test(&ctx->refs)) {
2991+
kfree(ctx->subvol_name);
2992+
kfree(ctx);
2993+
}
2994+
}
28132995

2814-
kfree(ctx->subvol_name);
2815-
kfree(ctx);
2996+
static int btrfs_dup_fs_context(struct fs_context *fc, struct fs_context *src_fc)
2997+
{
2998+
struct btrfs_fs_context *ctx = src_fc->fs_private;
2999+
3000+
/*
3001+
* Give a ref to our ctx to this dup, as we want to keep it around for
3002+
* our original fc so we can have the subvolume name or objectid.
3003+
*
3004+
* We unset ->source in the original fc because the dup needs it for
3005+
* mounting, and then once we free the dup it'll free ->source, so we
3006+
* need to make sure we're only pointing to it in one fc.
3007+
*/
3008+
refcount_inc(&ctx->refs);
3009+
fc->fs_private = ctx;
3010+
fc->source = src_fc->source;
3011+
src_fc->source = NULL;
3012+
return 0;
28163013
}
28173014

28183015
static const struct fs_context_operations btrfs_fs_context_ops = {
28193016
.parse_param = btrfs_parse_param,
28203017
.reconfigure = btrfs_reconfigure,
3018+
.get_tree = btrfs_get_tree,
3019+
.dup = btrfs_dup_fs_context,
28213020
.free = btrfs_free_fs_context,
28223021
};
28233022

@@ -2829,6 +3028,7 @@ static int __maybe_unused btrfs_init_fs_context(struct fs_context *fc)
28293028
if (!ctx)
28303029
return -ENOMEM;
28313030

3031+
refcount_set(&ctx->refs, 1);
28323032
fc->fs_private = ctx;
28333033
fc->ops = &btrfs_fs_context_ops;
28343034

0 commit comments

Comments
 (0)