It's a preparation to add Btrfs CPU affinity support. First, create a new struct named `btrfs_cpu_set` to contain CPU affinity information. Then, create helpers to allocate, parse, and free them. Signed-off-by: Ammar Faizi <ammarfaizi2@xxxxxxxxxxx> --- fs/btrfs/fs.c | 97 +++++++++++++++++++++++++++++++++++++++++++++++++++ fs/btrfs/fs.h | 7 ++++ 2 files changed, 104 insertions(+) diff --git a/fs/btrfs/fs.c b/fs/btrfs/fs.c index 31c1648bc0b46922..283d153d4491289c 100644 --- a/fs/btrfs/fs.c +++ b/fs/btrfs/fs.c @@ -96,3 +96,100 @@ void __btrfs_clear_fs_compat_ro(struct btrfs_fs_info *fs_info, u64 flag, set_bit(BTRFS_FS_FEATURE_CHANGED, &fs_info->flags); } } + +/* + * The user can't use the taskset pattern because ',' is used as + * the mount option delimiter. They can use the same taskset pattern, + * but replace the ',' with '.' and we will replace it back to + * ',', so the cpulist_parse() can recognize it. + * + * For example, in taskset cmd, they do: + * taskset -c 1,4,7 /bin/ls + * + * The equivalent CPU mask for the btrfs mount option will be: + * wq_cpu_set=1.4.7 + * + * Mark these as __cold to avoid the code bloat from overoptimizing + * the loop. + */ +__cold static void cpulist_dot_to_comma(char *set) +{ + while (*set) { + if (*set == '.') + *set = ','; + set++; + } +} + +__cold static void cpulist_comma_to_dot(char *set) +{ + while (*set) { + if (*set == ',') + *set = '.'; + set++; + } +} + +void btrfs_destroy_cpu_set(struct btrfs_cpu_set *cpu_set) +{ + if (!cpu_set) + return; + + free_cpumask_var(cpu_set->mask); + kfree(cpu_set->mask_str); + kfree(cpu_set); +} + +/* + * Only called from btrfs_parse_cpu_set(). + */ +static struct btrfs_cpu_set *btrfs_alloc_cpu_set(void) +{ + struct btrfs_cpu_set *cpu_set; + + cpu_set = kmalloc(sizeof(*cpu_set), GFP_KERNEL); + if (!cpu_set) + return NULL; + + if (!alloc_cpumask_var(&cpu_set->mask, GFP_KERNEL)) { + kfree(cpu_set); + return NULL; + } + + cpu_set->mask_str = NULL; + return cpu_set; +} + +int btrfs_parse_cpu_set(struct btrfs_cpu_set **cpu_set_p, const char *mask_str) +{ + struct btrfs_cpu_set *cpu_set; + int ret; + + cpu_set = btrfs_alloc_cpu_set(); + if (!cpu_set) + return -ENOMEM; + + cpu_set->mask_str = kstrdup(mask_str, GFP_KERNEL); + if (!cpu_set->mask_str) { + ret = -ENOMEM; + goto out_fail; + } + + cpulist_dot_to_comma(cpu_set->mask_str); + ret = cpulist_parse(cpu_set->mask_str, cpu_set->mask); + if (ret) + goto out_fail; + + if (cpumask_empty(cpu_set->mask)) { + ret = -EINVAL; + goto out_fail; + } + + cpulist_comma_to_dot(cpu_set->mask_str); + *cpu_set_p = cpu_set; + return 0; + +out_fail: + btrfs_destroy_cpu_set(cpu_set); + return ret; +} diff --git a/fs/btrfs/fs.h b/fs/btrfs/fs.h index 6de61367b6686197..cbad856df197ccfd 100644 --- a/fs/btrfs/fs.h +++ b/fs/btrfs/fs.h @@ -356,6 +356,11 @@ struct btrfs_commit_stats { u64 total_commit_dur; }; +struct btrfs_cpu_set { + cpumask_var_t mask; + char *mask_str; +}; + struct btrfs_fs_info { u8 chunk_tree_uuid[BTRFS_UUID_SIZE]; unsigned long flags; @@ -876,6 +881,8 @@ void btrfs_exclop_start_unlock(struct btrfs_fs_info *fs_info); void btrfs_exclop_finish(struct btrfs_fs_info *fs_info); void btrfs_exclop_balance(struct btrfs_fs_info *fs_info, enum btrfs_exclusive_operation op); +int btrfs_parse_cpu_set(struct btrfs_cpu_set **cpu_set_p, const char *mask_str); +void btrfs_destroy_cpu_set(struct btrfs_cpu_set *cpu_set); /* Compatibility and incompatibility defines */ void __btrfs_set_fs_incompat(struct btrfs_fs_info *fs_info, u64 flag, -- Ammar Faizi