spin_lock(&fs_info->super_lock);
features = get_features(fs_info, fa->feature_set); if (val)
features |= fa->feature_bit; else
features &= ~fa->feature_bit;
set_features(fs_info, fa->feature_set, features);
spin_unlock(&fs_info->super_lock);
/* * We don't want to do full transaction commit from inside sysfs
*/
set_bit(BTRFS_FS_NEED_TRANS_COMMIT, &fs_info->flags);
wake_up_process(fs_info->transaction_kthread);
BTRFS_FEAT_ATTR_INCOMPAT(default_subvol, DEFAULT_SUBVOL);
BTRFS_FEAT_ATTR_INCOMPAT(mixed_groups, MIXED_GROUPS);
BTRFS_FEAT_ATTR_INCOMPAT(compress_lzo, COMPRESS_LZO);
BTRFS_FEAT_ATTR_INCOMPAT(compress_zstd, COMPRESS_ZSTD);
BTRFS_FEAT_ATTR_INCOMPAT(extended_iref, EXTENDED_IREF);
BTRFS_FEAT_ATTR_INCOMPAT(raid56, RAID56);
BTRFS_FEAT_ATTR_INCOMPAT(skinny_metadata, SKINNY_METADATA);
BTRFS_FEAT_ATTR_INCOMPAT(no_holes, NO_HOLES);
BTRFS_FEAT_ATTR_INCOMPAT(metadata_uuid, METADATA_UUID);
BTRFS_FEAT_ATTR_COMPAT_RO(free_space_tree, FREE_SPACE_TREE);
BTRFS_FEAT_ATTR_COMPAT_RO(block_group_tree, BLOCK_GROUP_TREE);
BTRFS_FEAT_ATTR_INCOMPAT(raid1c34, RAID1C34);
BTRFS_FEAT_ATTR_INCOMPAT(simple_quota, SIMPLE_QUOTA); #ifdef CONFIG_BLK_DEV_ZONED
BTRFS_FEAT_ATTR_INCOMPAT(zoned, ZONED); #endif #ifdef CONFIG_BTRFS_EXPERIMENTAL /* Remove once support for extent tree v2 is feature complete */
BTRFS_FEAT_ATTR_INCOMPAT(extent_tree_v2, EXTENT_TREE_V2); /* Remove once support for raid stripe tree is feature complete. */
BTRFS_FEAT_ATTR_INCOMPAT(raid_stripe_tree, RAID_STRIPE_TREE); #endif #ifdef CONFIG_FS_VERITY
BTRFS_FEAT_ATTR_COMPAT_RO(verity, VERITY); #endif
/* * Features which depend on feature bits and may differ between each fs. * * /sys/fs/btrfs/features - all available features implemented by this version * /sys/fs/btrfs/UUID/features - features of the fs which are enabled or * can be changed on a mounted filesystem.
*/ staticstruct attribute *btrfs_supported_feature_attrs[] = {
BTRFS_FEAT_ATTR_PTR(default_subvol),
BTRFS_FEAT_ATTR_PTR(mixed_groups),
BTRFS_FEAT_ATTR_PTR(compress_lzo),
BTRFS_FEAT_ATTR_PTR(compress_zstd),
BTRFS_FEAT_ATTR_PTR(extended_iref),
BTRFS_FEAT_ATTR_PTR(raid56),
BTRFS_FEAT_ATTR_PTR(skinny_metadata),
BTRFS_FEAT_ATTR_PTR(no_holes),
BTRFS_FEAT_ATTR_PTR(metadata_uuid),
BTRFS_FEAT_ATTR_PTR(free_space_tree),
BTRFS_FEAT_ATTR_PTR(raid1c34),
BTRFS_FEAT_ATTR_PTR(block_group_tree),
BTRFS_FEAT_ATTR_PTR(simple_quota), #ifdef CONFIG_BLK_DEV_ZONED
BTRFS_FEAT_ATTR_PTR(zoned), #endif #ifdef CONFIG_BTRFS_EXPERIMENTAL
BTRFS_FEAT_ATTR_PTR(extent_tree_v2),
BTRFS_FEAT_ATTR_PTR(raid_stripe_tree), #endif #ifdef CONFIG_FS_VERITY
BTRFS_FEAT_ATTR_PTR(verity), #endif
NULL
};
static ssize_t supported_checksums_show(struct kobject *kobj, struct kobj_attribute *a, char *buf)
{
ssize_t ret = 0; int i;
for (i = 0; i < btrfs_get_num_csums(); i++) { /* * This "trick" only works as long as 'enum btrfs_csum_type' has * no holes in it
*/
ret += sysfs_emit_at(buf, ret, "%s%s", (i == 0 ? "" : " "),
btrfs_super_csum_name(i));
/* * Features which only depend on kernel version. * * These are listed in /sys/fs/btrfs/features along with * btrfs_supported_feature_attrs.
*/ staticstruct attribute *btrfs_supported_static_feature_attrs[] = {
BTRFS_ATTR_PTR(static_feature, acl),
BTRFS_ATTR_PTR(static_feature, rmdir_subvol),
BTRFS_ATTR_PTR(static_feature, supported_checksums),
BTRFS_ATTR_PTR(static_feature, send_stream_version),
BTRFS_ATTR_PTR(static_feature, supported_rescue_options),
BTRFS_ATTR_PTR(static_feature, supported_sectorsizes),
BTRFS_ATTR_PTR(static_feature, temp_fsid),
NULL
};
/* * Store new chunk size in space info. Can be called on a read-only filesystem. * * If the new chunk size value is larger than 10% of free space it is reduced * to match that limit. Alignment must be to 256M and the system chunk size * cannot be set.
*/ static ssize_t btrfs_chunk_size_store(struct kobject *kobj, struct kobj_attribute *a, constchar *buf, size_t len)
{ struct btrfs_space_info *space_info = to_space_info(kobj); struct btrfs_fs_info *fs_info = to_fs_info(get_btrfs_kobj(kobj)); char *retptr;
u64 val;
if (!capable(CAP_SYS_ADMIN)) return -EPERM;
if (!fs_info->fs_devices) return -EINVAL;
if (btrfs_is_zoned(fs_info)) return -EINVAL;
/* System block type must not be changed. */ if (space_info->flags & BTRFS_BLOCK_GROUP_SYSTEM) return -EPERM;
val = memparse(buf, &retptr); /* There could be trailing '\n', also catch any typos after the value */
retptr = skip_spaces(retptr); if (*retptr != 0 || val == 0) return -EINVAL;
val = min(val, BTRFS_MAX_DATA_CHUNK_SIZE);
/* Limit stripe size to 10% of available space. */
val = min(mult_perc(fs_info->fs_devices->total_rw_bytes, 10), val);
/* Must be multiple of 256M. */
val &= ~((u64)SZ_256M - 1);
/* Must be at least 256M. */ if (val < SZ_256M) return -EINVAL;
/* * This is unsafe to be called from sysfs context and may cause * unexpected problems.
*/
trans = btrfs_start_transaction(fs_info->tree_root, 0); if (IS_ERR(trans)) return PTR_ERR(trans);
ret = btrfs_force_chunk_alloc(trans, space_info->flags);
btrfs_end_transaction(trans);
/* * We don't want to do full transaction commit from inside sysfs
*/
set_bit(BTRFS_FS_NEED_TRANS_COMMIT, &fs_info->flags);
wake_up_process(fs_info->transaction_kthread);
#ifdef CONFIG_BTRFS_EXPERIMENTAL /* Separate value from input in policy:value format. */
value_str = strchr(param, ':'); if (value_str) { char *retptr;
*value_str = 0;
value_str++; if (!value_ret) return -EINVAL;
*value_ret = memparse(value_str, &retptr); /* There could be any trailing typos after the value. */
retptr = skip_spaces(retptr); if (*retptr != 0 || *value_ret <= 0) return -EINVAL;
} #endif
for (i = 0; i < BTRFS_NR_READ_POLICY; i++) { if (ret != 0)
ret += sysfs_emit_at(buf, ret, " ");
if (i == policy)
ret += sysfs_emit_at(buf, ret, "[");
ret += sysfs_emit_at(buf, ret, "%s", btrfs_read_policy_name[i]);
#ifdef CONFIG_BTRFS_EXPERIMENTAL if (i == BTRFS_READ_POLICY_RR)
ret += sysfs_emit_at(buf, ret, ":%u",
READ_ONCE(fs_devices->rr_min_contig_read));
if (i == BTRFS_READ_POLICY_DEVID)
ret += sysfs_emit_at(buf, ret, ":%llu",
READ_ONCE(fs_devices->read_devid)); #endif if (i == policy)
ret += sysfs_emit_at(buf, ret, "]");
}
index = btrfs_read_policy_to_enum(buf, &value); if (index < 0) return -EINVAL;
#ifdef CONFIG_BTRFS_EXPERIMENTAL /* If moving from RR then disable collecting fs stats. */ if (fs_devices->read_policy == BTRFS_READ_POLICY_RR && index != BTRFS_READ_POLICY_RR)
fs_devices->collect_fs_stats = false;
if (index == BTRFS_READ_POLICY_RR) { if (value != -1) { const u32 sectorsize = fs_devices->fs_info->sectorsize;
if (!IS_ALIGNED(value, sectorsize)) {
u64 temp_value = round_up(value, sectorsize);
btrfs_debug(fs_devices->fs_info, "read_policy: min contig read %lld should be multiple of sectorsize %u, rounded to %llu",
value, sectorsize, temp_value);
value = temp_value;
}
} else {
value = BTRFS_DEFAULT_RR_MIN_CONTIG_READ;
}
if (index != READ_ONCE(fs_devices->read_policy) ||
value != READ_ONCE(fs_devices->rr_min_contig_read)) {
WRITE_ONCE(fs_devices->read_policy, index);
WRITE_ONCE(fs_devices->rr_min_contig_read, value);
btrfs_info(fs_devices->fs_info, "read policy set to '%s:%lld'",
btrfs_read_policy_name[index], value);
}
fs_devices->collect_fs_stats = true;
return len;
}
if (index == BTRFS_READ_POLICY_DEVID) { if (value != -1) {
BTRFS_DEV_LOOKUP_ARGS(args);
/* Validate input devid. */
args.devid = value; if (btrfs_find_device(fs_devices, &args) == NULL) return -EINVAL;
} else { /* Set default devid to the devid of the latest device. */
value = fs_devices->latest_dev->devid;
}
if (index != READ_ONCE(fs_devices->read_policy) ||
value != READ_ONCE(fs_devices->read_devid)) {
WRITE_ONCE(fs_devices->read_policy, index);
WRITE_ONCE(fs_devices->read_devid, value);
btrfs_info(fs_devices->fs_info, "read policy set to '%s:%llu'",
btrfs_read_policy_name[index], value);
}
return len;
} #endif if (index != READ_ONCE(fs_devices->read_policy)) {
WRITE_ONCE(fs_devices->read_policy, index);
btrfs_info(fs_devices->fs_info, "read policy set to '%s'",
btrfs_read_policy_name[index]);
}
staticint addrm_unknown_feature_attrs(struct btrfs_fs_info *fs_info, bool add)
{ int set;
for (set = 0; set < FEAT_MAX; set++) { int i; struct attribute *attrs[2]; struct attribute_group agroup = {
.name = "features",
.attrs = attrs,
};
u64 features = get_features(fs_info, set);
features &= ~supported_feature_masks[set];
if (!features) continue;
attrs[1] = NULL; for (i = 0; i < NUM_FEATURE_BITS; i++) { struct btrfs_feature_attr *fa;
if (!(features & (1ULL << i))) continue;
fa = &btrfs_feature_attrs[set][i];
attrs[0] = &fa->kobj_attr.attr; if (add) { int ret;
ret = sysfs_merge_group(&fs_info->fs_devices->fsid_kobj,
&agroup); if (ret) return ret;
} else
sysfs_unmerge_group(&fs_info->fs_devices->fsid_kobj,
&agroup);
}
if (fs_devs->devices_kobj) {
kobject_del(fs_devs->devices_kobj);
kobject_put(fs_devs->devices_kobj);
fs_devs->devices_kobj = NULL;
}
if (fs_devs->fsid_kobj.state_initialized) {
kobject_del(&fs_devs->fsid_kobj);
kobject_put(&fs_devs->fsid_kobj);
wait_for_completion(&fs_devs->kobj_unregister);
}
}
/* when fs_devs is NULL it will remove all fsid kobject */ void btrfs_sysfs_remove_fsid(struct btrfs_fs_devices *fs_devs)
{ struct list_head *fs_uuids = btrfs_get_fs_uuids();
if (fs_devs) {
__btrfs_sysfs_remove_fsid(fs_devs); return;
}
for (set = 0; set < FEAT_MAX; set++) { for (i = 0; i < ARRAY_SIZE(btrfs_feature_attrs[set]); i++) { char *name = btrfs_unknown_feature_names[set][i];
fa = &btrfs_feature_attrs[set][i];
/* * Create a sysfs entry for a given block group type at path * /sys/fs/btrfs/UUID/allocation/data/TYPE
*/ void btrfs_sysfs_add_block_group_type(struct btrfs_block_group *cache)
{ struct btrfs_fs_info *fs_info = cache->fs_info; struct btrfs_space_info *space_info = cache->space_info; struct raid_kobject *rkobj; constint index = btrfs_bg_flags_to_raid_index(cache->flags); unsignedint nofs_flag; int ret;
/* * Setup a NOFS context because kobject_add(), deep in its call chain, * does GFP_KERNEL allocations, and we are often called in a context * where if reclaim is triggered we can deadlock (we are either holding * a transaction handle or some lock required for a transaction * commit).
*/
nofs_flag = memalloc_nofs_save();
rkobj = kzalloc(sizeof(*rkobj), GFP_NOFS); if (!rkobj) {
memalloc_nofs_restore(nofs_flag);
btrfs_warn(cache->fs_info, "couldn't alloc memory for raid level kobject"); return;
}
/* * We call this either on mount, or if we've created a block group for a * new index type while running (i.e. when restriping). The running * case is tricky because we could race with other threads, so we need * to have this check to make sure we didn't already init the kobject. * * We don't have to protect on the free side because it only happens on * unmount.
*/
spin_lock(&space_info->lock); if (space_info->block_group_kobjs[index]) {
spin_unlock(&space_info->lock);
kobject_put(&rkobj->kobj); return;
} else {
space_info->block_group_kobjs[index] = &rkobj->kobj;
}
spin_unlock(&space_info->lock);
ret = kobject_add(&rkobj->kobj, &space_info->kobj, "%s",
btrfs_bg_type_to_raid_name(rkobj->flags));
memalloc_nofs_restore(nofs_flag); if (ret) {
spin_lock(&space_info->lock);
space_info->block_group_kobjs[index] = NULL;
spin_unlock(&space_info->lock);
kobject_put(&rkobj->kobj);
btrfs_warn(fs_info, "failed to add kobject for block cache, ignoring"); return;
}
}
/* * Remove sysfs directories for all block group types of a given space info and * the space info as well
*/ void btrfs_sysfs_remove_space_info(struct btrfs_space_info *space_info)
{ int i;
for (i = 0; i < BTRFS_NR_RAID_TYPES; i++) { struct kobject *kobj;
switch (flags) { case BTRFS_BLOCK_GROUP_METADATA | BTRFS_BLOCK_GROUP_DATA: return"mixed"; case BTRFS_BLOCK_GROUP_METADATA: switch (space_info->subgroup_id) { case BTRFS_SUB_GROUP_PRIMARY: return"metadata"; case BTRFS_SUB_GROUP_TREELOG: return"metadata-treelog"; default:
WARN_ON_ONCE(1); return"metadata (unknown sub-group)";
} case BTRFS_BLOCK_GROUP_DATA: switch (space_info->subgroup_id) { case BTRFS_SUB_GROUP_PRIMARY: return"data"; case BTRFS_SUB_GROUP_DATA_RELOC: return"data-reloc"; default:
WARN_ON_ONCE(1); return"data (unknown sub-group)";
} case BTRFS_BLOCK_GROUP_SYSTEM:
ASSERT(space_info->subgroup_id == BTRFS_SUB_GROUP_PRIMARY); return"system"; default:
WARN_ON(1); return"invalid-combination";
}
}
/* * Create a sysfs entry for a space info type at path * /sys/fs/btrfs/UUID/allocation/TYPE
*/ int btrfs_sysfs_add_space_info_type(struct btrfs_fs_info *fs_info, struct btrfs_space_info *space_info)
{ int ret;
ret = kobject_init_and_add(&space_info->kobj, &space_info_ktype,
fs_info->space_info_kobj, "%s",
alloc_name(space_info)); if (ret) {
kobject_put(&space_info->kobj); return ret;
}
limit = memparse(buf, &endptr); /* There could be trailing '\n', also catch any typos after the value. */
endptr = skip_spaces(endptr); if (*endptr != 0) return -EINVAL;
WRITE_ONCE(device->scrub_speed_max, limit); return len;
}
BTRFS_ATTR_RW(devid, scrub_speed_max, btrfs_devinfo_scrub_speed_max_show,
btrfs_devinfo_scrub_speed_max_store);
if (!device->dev_stats_valid) return sysfs_emit(buf, "invalid\n");
/* * Print all at once so we get a snapshot of all values from the same * time. Keep them in sync and in order of definition of * btrfs_dev_stat_values.
*/ return sysfs_emit(buf, "write_errs %d\n" "read_errs %d\n" "flush_errs %d\n" "corruption_errs %d\n" "generation_errs %d\n",
btrfs_dev_stat_read(device, BTRFS_DEV_STAT_WRITE_ERRS),
btrfs_dev_stat_read(device, BTRFS_DEV_STAT_READ_ERRS),
btrfs_dev_stat_read(device, BTRFS_DEV_STAT_FLUSH_ERRS),
btrfs_dev_stat_read(device, BTRFS_DEV_STAT_CORRUPTION_ERRS),
btrfs_dev_stat_read(device, BTRFS_DEV_STAT_GENERATION_ERRS));
}
BTRFS_ATTR(devid, error_stats, btrfs_devinfo_error_stats_show);
int btrfs_sysfs_add_device(struct btrfs_device *device)
{ int ret; unsignedint nofs_flag; struct kobject *devices_kobj; struct kobject *devinfo_kobj;
/* * Make sure we use the fs_info::fs_devices to fetch the kobjects even * for the seed fs_devices
*/
devices_kobj = device->fs_info->fs_devices->devices_kobj;
devinfo_kobj = device->fs_info->fs_devices->devinfo_kobj;
ASSERT(devices_kobj);
ASSERT(devinfo_kobj);
nofs_flag = memalloc_nofs_save();
if (device->bdev) { struct kobject *disk_kobj = bdev_kobj(device->bdev);
ret = sysfs_create_link(devices_kobj, disk_kobj, disk_kobj->name); if (ret) {
btrfs_warn(device->fs_info, "creating sysfs device link for devid %llu failed: %d",
device->devid, ret); goto out;
}
}
init_completion(&device->kobj_unregister);
ret = kobject_init_and_add(&device->devid_kobj, &devid_ktype,
devinfo_kobj, "%llu", device->devid); if (ret) {
kobject_put(&device->devid_kobj);
btrfs_warn(device->fs_info, "devinfo init for devid %llu failed: %d",
device->devid, ret);
}
/* * Creates: * /sys/fs/btrfs/UUID * * Can be called by the device discovery thread.
*/ int btrfs_sysfs_add_fsid(struct btrfs_fs_devices *fs_devs)
{ int ret;
init_completion(&fs_devs->kobj_unregister);
fs_devs->fsid_kobj.kset = btrfs_kset;
ret = kobject_init_and_add(&fs_devs->fsid_kobj, &btrfs_ktype, NULL, "%pU", fs_devs->fsid); if (ret) {
kobject_put(&fs_devs->fsid_kobj); return ret;
}
fs_devs->devices_kobj = kobject_create_and_add("devices",
&fs_devs->fsid_kobj); if (!fs_devs->devices_kobj) {
btrfs_err(fs_devs->fs_info, "failed to init sysfs device interface");
btrfs_sysfs_remove_fsid(fs_devs); return -ENOMEM;
}
fs_devs->devinfo_kobj = kobject_create_and_add("devinfo",
&fs_devs->fsid_kobj); if (!fs_devs->devinfo_kobj) {
btrfs_err(fs_devs->fs_info, "failed to init sysfs devinfo kobject");
btrfs_sysfs_remove_fsid(fs_devs); return -ENOMEM;
}
return 0;
}
int btrfs_sysfs_add_mounted(struct btrfs_fs_info *fs_info)
{ int ret; struct btrfs_fs_devices *fs_devs = fs_info->fs_devices; struct kobject *fsid_kobj = &fs_devs->fsid_kobj;
ret = btrfs_sysfs_add_fs_devices(fs_devs); if (ret) return ret;
ret = sysfs_create_files(fsid_kobj, btrfs_attrs); if (ret) {
btrfs_sysfs_remove_fs_devices(fs_devs); return ret;
}
ret = sysfs_create_group(fsid_kobj, &btrfs_feature_attr_group); if (ret) goto failure;
#ifdef CONFIG_BTRFS_DEBUG
fs_info->debug_kobj = kobject_create_and_add("debug", fsid_kobj); if (!fs_info->debug_kobj) {
ret = -ENOMEM; goto failure;
}
ret = sysfs_create_files(fs_info->debug_kobj, btrfs_debug_mount_attrs); if (ret) goto failure; #endif
/* Discard directory */
fs_info->discard_kobj = kobject_create_and_add("discard", fsid_kobj); if (!fs_info->discard_kobj) {
ret = -ENOMEM; goto failure;
}
ret = sysfs_create_files(fs_info->discard_kobj, discard_attrs); if (ret) goto failure;
ret = addrm_unknown_feature_attrs(fs_info, true); if (ret) goto failure;
ret = sysfs_create_link(fsid_kobj, &fs_info->sb->s_bdi->dev->kobj, "bdi"); if (ret) goto failure;
fs_info->space_info_kobj = kobject_create_and_add("allocation",
fsid_kobj); if (!fs_info->space_info_kobj) {
ret = -ENOMEM; goto failure;
}
ret = sysfs_create_files(fs_info->space_info_kobj, allocation_attrs); if (ret) goto failure;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.