2017-03-17 09:18:50 +03:00
// SPDX-License-Identifier: GPL-2.0
# include <linux/kernel.h>
# include "bcachefs.h"
2018-11-13 02:30:55 +03:00
# include "compress.h"
2017-03-17 09:18:50 +03:00
# include "disk_groups.h"
# include "opts.h"
# include "super-io.h"
# include "util.h"
2021-02-21 03:47:58 +03:00
# define x(t, n) #t,
2017-03-17 09:18:50 +03:00
const char * const bch2_error_actions [ ] = {
2021-02-21 03:47:58 +03:00
BCH_ERROR_ACTIONS ( )
2017-03-17 09:18:50 +03:00
NULL
} ;
2019-12-29 04:17:06 +03:00
const char * const bch2_sb_features [ ] = {
BCH_SB_FEATURES ( )
NULL
} ;
2021-04-05 04:57:35 +03:00
const char * const bch2_sb_compat [ ] = {
BCH_SB_COMPAT ( )
NULL
} ;
2021-02-21 03:27:37 +03:00
const char * const bch2_btree_ids [ ] = {
BCH_BTREE_IDS ( )
NULL
} ;
2021-11-11 20:11:33 +03:00
const char * const bch2_csum_types [ ] = {
BCH_CSUM_TYPES ( )
NULL
} ;
2019-12-29 04:17:06 +03:00
const char * const bch2_csum_opts [ ] = {
2021-02-21 03:47:58 +03:00
BCH_CSUM_OPTS ( )
2017-03-17 09:18:50 +03:00
NULL
} ;
2021-11-11 20:11:33 +03:00
const char * const bch2_compression_types [ ] = {
BCH_COMPRESSION_TYPES ( )
NULL
} ;
2019-12-29 04:17:06 +03:00
const char * const bch2_compression_opts [ ] = {
BCH_COMPRESSION_OPTS ( )
2017-03-17 09:18:50 +03:00
NULL
} ;
const char * const bch2_str_hash_types [ ] = {
2021-11-11 20:11:33 +03:00
BCH_STR_HASH_TYPES ( )
NULL
} ;
const char * const bch2_str_hash_opts [ ] = {
2021-02-21 03:47:58 +03:00
BCH_STR_HASH_OPTS ( )
2017-03-17 09:18:50 +03:00
NULL
} ;
const char * const bch2_data_types [ ] = {
2020-07-10 01:28:11 +03:00
BCH_DATA_TYPES ( )
2017-03-17 09:18:50 +03:00
NULL
} ;
2021-02-21 03:47:58 +03:00
const char * const bch2_member_states [ ] = {
BCH_MEMBER_STATES ( )
2017-03-17 09:18:50 +03:00
NULL
} ;
2022-01-01 01:06:29 +03:00
const char * const bch2_jset_entry_types [ ] = {
BCH_JSET_ENTRY_TYPES ( )
NULL
} ;
const char * const bch2_fs_usage_types [ ] = {
BCH_FS_USAGE_TYPES ( )
NULL
} ;
2021-02-21 03:47:58 +03:00
# undef x
2017-03-17 09:18:50 +03:00
2021-03-16 07:42:25 +03:00
const char * const bch2_d_types [ BCH_DT_MAX ] = {
2021-07-06 05:18:07 +03:00
[ DT_UNKNOWN ] = " unknown " ,
[ DT_FIFO ] = " fifo " ,
[ DT_CHR ] = " chr " ,
[ DT_DIR ] = " dir " ,
[ DT_BLK ] = " blk " ,
[ DT_REG ] = " reg " ,
[ DT_LNK ] = " lnk " ,
[ DT_SOCK ] = " sock " ,
[ DT_WHT ] = " whiteout " ,
2021-03-16 07:42:25 +03:00
[ DT_SUBVOL ] = " subvol " ,
2021-07-06 05:18:07 +03:00
} ;
2017-03-17 09:18:50 +03:00
void bch2_opts_apply ( struct bch_opts * dst , struct bch_opts src )
{
2018-12-19 20:58:56 +03:00
# define x(_name, ...) \
2017-03-17 09:18:50 +03:00
if ( opt_defined ( src , _name ) ) \
opt_set ( * dst , _name , src . _name ) ;
BCH_OPTS ( )
2018-12-19 20:58:56 +03:00
# undef x
2017-03-17 09:18:50 +03:00
}
bool bch2_opt_defined_by_id ( const struct bch_opts * opts , enum bch_opt_id id )
{
switch ( id ) {
2018-12-19 20:58:56 +03:00
# define x(_name, ...) \
2017-03-17 09:18:50 +03:00
case Opt_ # # _name : \
return opt_defined ( * opts , _name ) ;
BCH_OPTS ( )
2018-12-19 20:58:56 +03:00
# undef x
2017-03-17 09:18:50 +03:00
default :
BUG ( ) ;
}
}
u64 bch2_opt_get_by_id ( const struct bch_opts * opts , enum bch_opt_id id )
{
switch ( id ) {
2018-12-19 20:58:56 +03:00
# define x(_name, ...) \
2017-03-17 09:18:50 +03:00
case Opt_ # # _name : \
return opts - > _name ;
BCH_OPTS ( )
2018-12-19 20:58:56 +03:00
# undef x
2017-03-17 09:18:50 +03:00
default :
BUG ( ) ;
}
}
void bch2_opt_set_by_id ( struct bch_opts * opts , enum bch_opt_id id , u64 v )
{
switch ( id ) {
2018-12-19 20:58:56 +03:00
# define x(_name, ...) \
2017-03-17 09:18:50 +03:00
case Opt_ # # _name : \
opt_set ( * opts , _name , v ) ; \
break ;
BCH_OPTS ( )
2018-12-19 20:58:56 +03:00
# undef x
2017-03-17 09:18:50 +03:00
default :
BUG ( ) ;
}
}
const struct bch_option bch2_opt_table [ ] = {
2021-12-14 22:24:41 +03:00
# define OPT_BOOL() .type = BCH_OPT_BOOL, .min = 0, .max = 2
# define OPT_UINT(_min, _max) .type = BCH_OPT_UINT, \
. min = _min , . max = _max
# define OPT_STR(_choices) .type = BCH_OPT_STR, \
. min = 0 , . max = ARRAY_SIZE ( _choices ) , \
. choices = _choices
2017-03-17 09:18:50 +03:00
# define OPT_FN(_fn) .type = BCH_OPT_FN, \
. parse = _fn # # _parse , \
2018-11-09 09:24:07 +03:00
. to_text = _fn # # _to_text
2017-03-17 09:18:50 +03:00
2021-12-14 22:24:41 +03:00
# define x(_name, _bits, _flags, _type, _sb_opt, _default, _hint, _help) \
2017-03-17 09:18:50 +03:00
[ Opt_ # # _name ] = { \
. attr = { \
. name = # _name , \
2021-12-14 22:24:41 +03:00
. mode = ( _flags ) & OPT_RUNTIME ? 0644 : 0444 , \
2017-03-17 09:18:50 +03:00
} , \
2021-12-14 22:24:41 +03:00
. flags = _flags , \
2018-12-19 20:58:56 +03:00
. hint = _hint , \
. help = _help , \
2021-12-14 22:24:41 +03:00
. get_sb = _sb_opt , \
2017-03-17 09:18:50 +03:00
. set_sb = SET_ # # _sb_opt , \
_type \
} ,
BCH_OPTS ( )
2018-12-19 20:58:56 +03:00
# undef x
2017-03-17 09:18:50 +03:00
} ;
int bch2_opt_lookup ( const char * name )
{
const struct bch_option * i ;
for ( i = bch2_opt_table ;
i < bch2_opt_table + ARRAY_SIZE ( bch2_opt_table ) ;
i + + )
if ( ! strcmp ( name , i - > attr . name ) )
return i - bch2_opt_table ;
return - 1 ;
}
struct synonym {
const char * s1 , * s2 ;
} ;
static const struct synonym bch_opt_synonyms [ ] = {
{ " quota " , " usrquota " } ,
} ;
static int bch2_mount_opt_lookup ( const char * name )
{
const struct synonym * i ;
for ( i = bch_opt_synonyms ;
i < bch_opt_synonyms + ARRAY_SIZE ( bch_opt_synonyms ) ;
i + + )
if ( ! strcmp ( name , i - > s1 ) )
name = i - > s2 ;
return bch2_opt_lookup ( name ) ;
}
2021-12-14 22:24:41 +03:00
static int bch2_opt_validate ( const struct bch_option * opt , const char * msg , u64 v )
{
if ( v < opt - > min ) {
if ( msg )
pr_err ( " invalid %s%s: too small (min %llu) " ,
msg , opt - > attr . name , opt - > min ) ;
return - ERANGE ;
}
if ( opt - > max & & v > = opt - > max ) {
if ( msg )
pr_err ( " invalid %s%s: too big (max %llu) " ,
msg , opt - > attr . name , opt - > max ) ;
return - ERANGE ;
}
if ( ( opt - > flags & OPT_SB_FIELD_SECTORS ) & & ( v & 511 ) ) {
if ( msg )
pr_err ( " invalid %s %s: not a multiple of 512 " ,
msg , opt - > attr . name ) ;
return - EINVAL ;
}
if ( ( opt - > flags & OPT_MUST_BE_POW_2 ) & & ! is_power_of_2 ( v ) ) {
if ( msg )
pr_err ( " invalid %s%s: must be a power of two " ,
msg , opt - > attr . name ) ;
return - EINVAL ;
}
return 0 ;
}
int bch2_opt_parse ( struct bch_fs * c , const char * msg ,
const struct bch_option * opt ,
2017-03-17 09:18:50 +03:00
const char * val , u64 * res )
{
ssize_t ret ;
switch ( opt - > type ) {
case BCH_OPT_BOOL :
ret = kstrtou64 ( val , 10 , res ) ;
if ( ret < 0 )
return ret ;
break ;
case BCH_OPT_UINT :
2021-12-14 22:24:41 +03:00
ret = opt - > flags & OPT_HUMAN_READABLE
? bch2_strtou64_h ( val , res )
: kstrtou64 ( val , 10 , res ) ;
2017-03-17 09:18:50 +03:00
if ( ret < 0 )
return ret ;
break ;
case BCH_OPT_STR :
ret = match_string ( opt - > choices , - 1 , val ) ;
if ( ret < 0 )
return ret ;
* res = ret ;
break ;
case BCH_OPT_FN :
if ( ! c )
2020-10-24 04:07:17 +03:00
return 0 ;
2017-03-17 09:18:50 +03:00
2021-12-14 22:24:41 +03:00
ret = opt - > parse ( c , val , res ) ;
if ( ret < 0 )
return ret ;
2017-03-17 09:18:50 +03:00
}
2021-12-14 22:24:41 +03:00
return bch2_opt_validate ( opt , msg , * res ) ;
2017-03-17 09:18:50 +03:00
}
2018-11-09 09:24:07 +03:00
void bch2_opt_to_text ( struct printbuf * out , struct bch_fs * c ,
const struct bch_option * opt , u64 v ,
unsigned flags )
2017-03-17 09:18:50 +03:00
{
if ( flags & OPT_SHOW_MOUNT_STYLE ) {
2018-11-09 09:24:07 +03:00
if ( opt - > type = = BCH_OPT_BOOL ) {
pr_buf ( out , " %s%s " ,
v ? " " : " no " ,
opt - > attr . name ) ;
return ;
}
pr_buf ( out , " %s= " , opt - > attr . name ) ;
2017-03-17 09:18:50 +03:00
}
switch ( opt - > type ) {
case BCH_OPT_BOOL :
case BCH_OPT_UINT :
2021-12-14 22:24:41 +03:00
if ( opt - > flags & OPT_HUMAN_READABLE )
bch2_hprint ( out , v ) ;
else
pr_buf ( out , " %lli " , v ) ;
2018-12-19 20:58:56 +03:00
break ;
2017-03-17 09:18:50 +03:00
case BCH_OPT_STR :
2018-11-09 09:24:07 +03:00
if ( flags & OPT_SHOW_FULL_LIST )
bch2_string_opt_to_text ( out , opt - > choices , v ) ;
else
pr_buf ( out , opt - > choices [ v ] ) ;
2017-03-17 09:18:50 +03:00
break ;
case BCH_OPT_FN :
2018-11-09 09:24:07 +03:00
opt - > to_text ( out , c , v ) ;
break ;
2017-03-17 09:18:50 +03:00
default :
BUG ( ) ;
}
}
2018-11-13 02:30:55 +03:00
int bch2_opt_check_may_set ( struct bch_fs * c , int id , u64 v )
{
int ret = 0 ;
switch ( id ) {
case Opt_compression :
case Opt_background_compression :
ret = bch2_check_set_has_compressed_data ( c , v ) ;
break ;
2018-11-01 22:13:19 +03:00
case Opt_erasure_code :
2019-11-29 21:47:42 +03:00
if ( v )
2019-12-29 04:17:06 +03:00
bch2_check_set_feature ( c , BCH_FEATURE_ec ) ;
2018-11-01 22:13:19 +03:00
break ;
2018-11-13 02:30:55 +03:00
}
return ret ;
}
2018-11-01 22:13:19 +03:00
int bch2_opts_check_may_set ( struct bch_fs * c )
{
unsigned i ;
int ret ;
for ( i = 0 ; i < bch2_opts_nr ; i + + ) {
ret = bch2_opt_check_may_set ( c , i ,
bch2_opt_get_by_id ( & c - > opts , i ) ) ;
if ( ret )
return ret ;
}
return 0 ;
}
2020-10-24 04:07:17 +03:00
int bch2_parse_mount_opts ( struct bch_fs * c , struct bch_opts * opts ,
char * options )
2017-03-17 09:18:50 +03:00
{
2021-05-10 01:52:23 +03:00
char * copied_opts , * copied_opts_start ;
2017-03-17 09:18:50 +03:00
char * opt , * name , * val ;
int ret , id ;
u64 v ;
2021-05-10 01:52:23 +03:00
if ( ! options )
return 0 ;
copied_opts = kstrdup ( options , GFP_KERNEL ) ;
if ( ! copied_opts )
return - 1 ;
copied_opts_start = copied_opts ;
while ( ( opt = strsep ( & copied_opts , " , " ) ) ! = NULL ) {
2017-03-17 09:18:50 +03:00
name = strsep ( & opt , " = " ) ;
val = opt ;
if ( val ) {
id = bch2_mount_opt_lookup ( name ) ;
if ( id < 0 )
goto bad_opt ;
2021-12-14 22:24:41 +03:00
ret = bch2_opt_parse ( c , " mount option " ,
& bch2_opt_table [ id ] , val , & v ) ;
2017-03-17 09:18:50 +03:00
if ( ret < 0 )
goto bad_val ;
} else {
id = bch2_mount_opt_lookup ( name ) ;
v = 1 ;
if ( id < 0 & &
! strncmp ( " no " , name , 2 ) ) {
id = bch2_mount_opt_lookup ( name + 2 ) ;
v = 0 ;
}
if ( id < 0 )
goto bad_opt ;
if ( bch2_opt_table [ id ] . type ! = BCH_OPT_BOOL )
goto no_val ;
}
2021-12-14 22:24:41 +03:00
if ( ! ( bch2_opt_table [ id ] . flags & OPT_MOUNT ) )
2017-03-17 09:18:50 +03:00
goto bad_opt ;
if ( id = = Opt_acl & &
! IS_ENABLED ( CONFIG_BCACHEFS_POSIX_ACL ) )
goto bad_opt ;
if ( ( id = = Opt_usrquota | |
id = = Opt_grpquota ) & &
! IS_ENABLED ( CONFIG_BCACHEFS_QUOTA ) )
goto bad_opt ;
bch2_opt_set_by_id ( opts , id , v ) ;
}
2021-05-10 01:52:23 +03:00
ret = 0 ;
goto out ;
2017-03-17 09:18:50 +03:00
bad_opt :
pr_err ( " Bad mount option %s " , name ) ;
2021-05-10 01:52:23 +03:00
ret = - 1 ;
goto out ;
2017-03-17 09:18:50 +03:00
bad_val :
pr_err ( " Invalid value %s for mount option %s " , val , name ) ;
2021-05-10 01:52:23 +03:00
ret = - 1 ;
goto out ;
2017-03-17 09:18:50 +03:00
no_val :
pr_err ( " Mount option %s requires a value " , name ) ;
2021-05-10 01:52:23 +03:00
ret = - 1 ;
goto out ;
out :
kfree ( copied_opts_start ) ;
return ret ;
2017-03-17 09:18:50 +03:00
}
2021-12-14 22:24:41 +03:00
/*
* Initial options from superblock - here we don ' t want any options undefined ,
* any options the superblock doesn ' t specify are set to 0 :
*/
int bch2_opts_from_sb ( struct bch_opts * opts , struct bch_sb * sb )
{
unsigned id ;
int ret ;
for ( id = 0 ; id < bch2_opts_nr ; id + + ) {
const struct bch_option * opt = bch2_opt_table + id ;
u64 v ;
if ( opt - > get_sb = = NO_SB_OPT )
continue ;
v = opt - > get_sb ( sb ) ;
if ( opt - > flags & OPT_SB_FIELD_ILOG2 )
v = 1ULL < < v ;
if ( opt - > flags & OPT_SB_FIELD_SECTORS )
v < < = 9 ;
ret = bch2_opt_validate ( opt , " superblock option " , v ) ;
if ( ret )
return ret ;
bch2_opt_set_by_id ( opts , id , v ) ;
}
return 0 ;
}
void __bch2_opt_set_sb ( struct bch_sb * sb , const struct bch_option * opt , u64 v )
{
if ( opt - > set_sb = = SET_NO_SB_OPT )
return ;
if ( opt - > flags & OPT_SB_FIELD_SECTORS )
v > > = 9 ;
if ( opt - > flags & OPT_SB_FIELD_ILOG2 )
v = ilog2 ( v ) ;
opt - > set_sb ( sb , v ) ;
}
void bch2_opt_set_sb ( struct bch_fs * c , const struct bch_option * opt , u64 v )
{
if ( opt - > set_sb = = SET_NO_SB_OPT )
return ;
mutex_lock ( & c - > sb_lock ) ;
__bch2_opt_set_sb ( c - > disk_sb . sb , opt , v ) ;
bch2_write_super ( c ) ;
mutex_unlock ( & c - > sb_lock ) ;
}
2017-03-17 09:18:50 +03:00
/* io opts: */
struct bch_io_opts bch2_opts_to_inode_opts ( struct bch_opts src )
{
struct bch_io_opts ret = { 0 } ;
2018-12-13 14:01:30 +03:00
# define x(_name, _bits) \
2017-03-17 09:18:50 +03:00
if ( opt_defined ( src , _name ) ) \
opt_set ( ret , _name , src . _name ) ;
BCH_INODE_OPTS ( )
2018-12-13 14:01:30 +03:00
# undef x
2017-03-17 09:18:50 +03:00
return ret ;
}
struct bch_opts bch2_inode_opts_to_opts ( struct bch_io_opts src )
{
struct bch_opts ret = { 0 } ;
2018-12-13 14:01:30 +03:00
# define x(_name, _bits) \
2017-03-17 09:18:50 +03:00
if ( opt_defined ( src , _name ) ) \
opt_set ( ret , _name , src . _name ) ;
BCH_INODE_OPTS ( )
2018-12-13 14:01:30 +03:00
# undef x
2017-03-17 09:18:50 +03:00
return ret ;
}
void bch2_io_opts_apply ( struct bch_io_opts * dst , struct bch_io_opts src )
{
2018-12-13 14:01:30 +03:00
# define x(_name, _bits) \
2017-03-17 09:18:50 +03:00
if ( opt_defined ( src , _name ) ) \
opt_set ( * dst , _name , src . _name ) ;
BCH_INODE_OPTS ( )
2018-12-13 14:01:30 +03:00
# undef x
2017-03-17 09:18:50 +03:00
}
bool bch2_opt_is_inode_opt ( enum bch_opt_id id )
{
static const enum bch_opt_id inode_opt_list [ ] = {
2018-12-13 14:01:30 +03:00
# define x(_name, _bits) Opt_##_name,
2017-03-17 09:18:50 +03:00
BCH_INODE_OPTS ( )
2018-12-13 14:01:30 +03:00
# undef x
2017-03-17 09:18:50 +03:00
} ;
unsigned i ;
for ( i = 0 ; i < ARRAY_SIZE ( inode_opt_list ) ; i + + )
if ( inode_opt_list [ i ] = = id )
return true ;
return false ;
}