Skip to content

Commit 95d21eb

Browse files
kakrakreijack
andcommitted
btrfs: change the DEV_ITEM 'type' field via sysfs
v2: Adds a check to prevent modification while the file system is still mounting. v3: Accept only valid allocation hint types Todo: - Transactions should not be triggered from sysfw writes, see: https://lore.kernel.org/linux-btrfs/20251213200920.1808679-1-kai@kaishome.de/ Link: #36 (comment) Reported-by: Eli Venter <eli@genedx.com> Co-authored-by: Goffredo Baroncelli <kreijack@inwind.it> Signed-off-by: Kai Krakow <kai@kaishome.de>
1 parent 1692c5a commit 95d21eb

3 files changed

Lines changed: 83 additions & 3 deletions

File tree

‎fs/btrfs/sysfs.c‎

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2141,6 +2141,22 @@ static ssize_t btrfs_devinfo_error_stats_show(struct kobject *kobj,
21412141
BTRFS_ATTR(devid, error_stats, btrfs_devinfo_error_stats_show);
21422142

21432143
#ifdef CONFIG_BTRFS_ALLOCATOR_HINTS
2144+
static bool btrfs_dev_allocation_hint_valid(u64 type)
2145+
{
2146+
if (type & ~((1ULL << BTRFS_DEV_ALLOCATION_MASK_BIT_COUNT) - 1))
2147+
return false;
2148+
2149+
switch (type) {
2150+
case BTRFS_DEV_ALLOCATION_PREFERRED_DATA:
2151+
case BTRFS_DEV_ALLOCATION_PREFERRED_METADATA:
2152+
case BTRFS_DEV_ALLOCATION_METADATA_ONLY:
2153+
case BTRFS_DEV_ALLOCATION_DATA_ONLY:
2154+
return true;
2155+
default:
2156+
return false;
2157+
}
2158+
}
2159+
21442160
static ssize_t btrfs_devinfo_type_show(struct kobject *kobj,
21452161
struct kobj_attribute *a, char *buf)
21462162
{
@@ -2149,7 +2165,69 @@ static ssize_t btrfs_devinfo_type_show(struct kobject *kobj,
21492165

21502166
return scnprintf(buf, PAGE_SIZE, "0x%016llx\n", device->type);
21512167
}
2152-
BTRFS_ATTR(devid, type, btrfs_devinfo_type_show);
2168+
2169+
static ssize_t btrfs_devinfo_type_store(struct kobject *kobj,
2170+
struct kobj_attribute *a,
2171+
const char *buf, size_t len)
2172+
{
2173+
struct btrfs_fs_info *fs_info;
2174+
struct btrfs_root *root;
2175+
struct btrfs_device *device;
2176+
int ret;
2177+
struct btrfs_trans_handle *trans;
2178+
2179+
u64 type, prev_type;
2180+
2181+
device = container_of(kobj, struct btrfs_device, devid_kobj);
2182+
fs_info = device->fs_info;
2183+
if (!fs_info)
2184+
return -EPERM;
2185+
2186+
/*
2187+
* Changing the type field requires starting a transaction which will cause a NULL dereference in
2188+
* __reserve_bytes if the file system is not fully open. Thus, return EBUSY if the file system is not fully
2189+
* initialized.
2190+
*/
2191+
if (!test_bit(BTRFS_FS_OPEN, &fs_info->flags))
2192+
return -EBUSY;
2193+
2194+
root = fs_info->chunk_root;
2195+
if (sb_rdonly(fs_info->sb))
2196+
return -EROFS;
2197+
2198+
ret = kstrtou64(buf, 0, &type);
2199+
if (ret < 0)
2200+
return -EINVAL;
2201+
2202+
/* for now, only allow defined allocation hint values */
2203+
if (!btrfs_dev_allocation_hint_valid(type))
2204+
return -EINVAL;
2205+
2206+
trans = btrfs_start_transaction(root, 1);
2207+
if (IS_ERR(trans))
2208+
return PTR_ERR(trans);
2209+
2210+
prev_type = device->type;
2211+
device->type = type;
2212+
2213+
ret = btrfs_update_device(trans, device);
2214+
2215+
if (ret < 0) {
2216+
btrfs_abort_transaction(trans, ret);
2217+
btrfs_end_transaction(trans);
2218+
goto abort;
2219+
}
2220+
2221+
ret = btrfs_commit_transaction(trans);
2222+
if (ret < 0)
2223+
goto abort;
2224+
2225+
return len;
2226+
abort:
2227+
device->type = prev_type;
2228+
return ret;
2229+
}
2230+
BTRFS_ATTR_RW(devid, type, btrfs_devinfo_type_show, btrfs_devinfo_type_store);
21532231
#endif /* CONFIG_BTRFS_ALLOCATOR_HINTS */
21542232

21552233
/*

‎fs/btrfs/volumes.c‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2942,8 +2942,8 @@ int btrfs_init_new_device(struct btrfs_fs_info *fs_info, const char *device_path
29422942
return ret;
29432943
}
29442944

2945-
static noinline int btrfs_update_device(struct btrfs_trans_handle *trans,
2946-
struct btrfs_device *device)
2945+
noinline int btrfs_update_device(struct btrfs_trans_handle *trans,
2946+
struct btrfs_device *device)
29472947
{
29482948
int ret;
29492949
struct btrfs_path *path;

‎fs/btrfs/volumes.h‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -890,6 +890,8 @@ int btrfs_bg_type_to_factor(u64 flags);
890890
const char *btrfs_bg_type_to_raid_name(u64 flags);
891891
int btrfs_verify_dev_extents(struct btrfs_fs_info *fs_info);
892892
bool btrfs_repair_one_zone(struct btrfs_fs_info *fs_info, u64 logical);
893+
int btrfs_update_device(struct btrfs_trans_handle *trans,
894+
struct btrfs_device *device);
893895

894896
bool btrfs_pinned_by_swapfile(struct btrfs_fs_info *fs_info, void *ptr);
895897
const u8 *btrfs_sb_fsid_ptr(const struct btrfs_super_block *sb);

0 commit comments

Comments
 (0)