btrfs: preallocate anon block device at first phase of snapshot creation
[BUG] When the anonymous block device pool is exhausted, subvolume/snapshot creation fails with EMFILE (Too many files open). This has been reported by a user. The allocation happens in the second phase during transaction commit where it's only way out is to abort the transaction BTRFS: Transaction aborted (error -24) WARNING: CPU: 17 PID: 17041 at fs/btrfs/transaction.c:1576 create_pending_snapshot+0xbc4/0xd10 [btrfs] RIP: 0010:create_pending_snapshot+0xbc4/0xd10 [btrfs] Call Trace: create_pending_snapshots+0x82/0xa0 [btrfs] btrfs_commit_transaction+0x275/0x8c0 [btrfs] btrfs_mksubvol+0x4b9/0x500 [btrfs] btrfs_ioctl_snap_create_transid+0x174/0x180 [btrfs] btrfs_ioctl_snap_create_v2+0x11c/0x180 [btrfs] btrfs_ioctl+0x11a4/0x2da0 [btrfs] do_vfs_ioctl+0xa9/0x640 ksys_ioctl+0x67/0x90 __x64_sys_ioctl+0x1a/0x20 do_syscall_64+0x5a/0x110 entry_SYSCALL_64_after_hwframe+0x44/0xa9 ---[ end trace 33f2f83f3d5250e9 ]--- BTRFS: error (device sda1) in create_pending_snapshot:1576: errno=-24 unknown BTRFS info (device sda1): forced readonly BTRFS warning (device sda1): Skipping commit of aborted transaction. BTRFS: error (device sda1) in cleanup_transaction:1831: errno=-24 unknown [CAUSE] When the global anonymous block device pool is exhausted, the following call chain will fail, and lead to transaction abort: btrfs_ioctl_snap_create_v2() |- btrfs_ioctl_snap_create_transid() |- btrfs_mksubvol() |- btrfs_commit_transaction() |- create_pending_snapshot() |- btrfs_get_fs_root() |- btrfs_init_fs_root() |- get_anon_bdev() [FIX] Although we can't enlarge the anonymous block device pool, at least we can preallocate anon_dev for subvolume/snapshot in the first phase, outside of transaction context and exactly at the moment the user calls the creation ioctl. Reported-by: Greed Rong <greedrong@gmail.com> Link: https://lore.kernel.org/linux-btrfs/CA+UqX+NTrZ6boGnWHhSeZmEY5J76CTqmYjO2S+=tHJX7nb9DPw@mail.gmail.com/ CC: stable@vger.kernel.org # 4.4+ Signed-off-by: Qu Wenruo <wqu@suse.com> Reviewed-by: David Sterba <dsterba@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
@@ -566,6 +566,7 @@ static noinline int create_subvol(struct inode *dir,
|
||||
struct inode *inode;
|
||||
int ret;
|
||||
int err;
|
||||
dev_t anon_dev = 0;
|
||||
u64 objectid;
|
||||
u64 new_dirid = BTRFS_FIRST_FREE_OBJECTID;
|
||||
u64 index = 0;
|
||||
@@ -578,6 +579,10 @@ static noinline int create_subvol(struct inode *dir,
|
||||
if (ret)
|
||||
goto fail_free;
|
||||
|
||||
ret = get_anon_bdev(&anon_dev);
|
||||
if (ret < 0)
|
||||
goto fail_free;
|
||||
|
||||
/*
|
||||
* Don't create subvolume whose level is not zero. Or qgroup will be
|
||||
* screwed up since it assumes subvolume qgroup's level to be 0.
|
||||
@@ -660,12 +665,15 @@ static noinline int create_subvol(struct inode *dir,
|
||||
goto fail;
|
||||
|
||||
key.offset = (u64)-1;
|
||||
new_root = btrfs_get_fs_root(fs_info, objectid, true);
|
||||
new_root = btrfs_get_new_fs_root(fs_info, objectid, anon_dev);
|
||||
if (IS_ERR(new_root)) {
|
||||
free_anon_bdev(anon_dev);
|
||||
ret = PTR_ERR(new_root);
|
||||
btrfs_abort_transaction(trans, ret);
|
||||
goto fail;
|
||||
}
|
||||
/* Freeing will be done in btrfs_put_root() of new_root */
|
||||
anon_dev = 0;
|
||||
|
||||
btrfs_record_root_in_trans(trans, new_root);
|
||||
|
||||
@@ -735,6 +743,8 @@ fail:
|
||||
return ret;
|
||||
|
||||
fail_free:
|
||||
if (anon_dev)
|
||||
free_anon_bdev(anon_dev);
|
||||
kfree(root_item);
|
||||
return ret;
|
||||
}
|
||||
@@ -762,6 +772,9 @@ static int create_snapshot(struct btrfs_root *root, struct inode *dir,
|
||||
if (!pending_snapshot)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = get_anon_bdev(&pending_snapshot->anon_dev);
|
||||
if (ret < 0)
|
||||
goto free_pending;
|
||||
pending_snapshot->root_item = kzalloc(sizeof(struct btrfs_root_item),
|
||||
GFP_KERNEL);
|
||||
pending_snapshot->path = btrfs_alloc_path();
|
||||
@@ -823,10 +836,16 @@ static int create_snapshot(struct btrfs_root *root, struct inode *dir,
|
||||
|
||||
d_instantiate(dentry, inode);
|
||||
ret = 0;
|
||||
pending_snapshot->anon_dev = 0;
|
||||
fail:
|
||||
/* Prevent double freeing of anon_dev */
|
||||
if (ret && pending_snapshot->snap)
|
||||
pending_snapshot->snap->anon_dev = 0;
|
||||
btrfs_put_root(pending_snapshot->snap);
|
||||
btrfs_subvolume_release_metadata(fs_info, &pending_snapshot->block_rsv);
|
||||
free_pending:
|
||||
if (pending_snapshot->anon_dev)
|
||||
free_anon_bdev(pending_snapshot->anon_dev);
|
||||
kfree(pending_snapshot->root_item);
|
||||
btrfs_free_path(pending_snapshot->path);
|
||||
kfree(pending_snapshot);
|
||||
|
Reference in New Issue
Block a user