3737#include <linux/khugepaged.h>
3838#include <linux/hugetlb.h>
3939#include <linux/frontswap.h>
40+ #include <linux/fs_parser.h>
4041
4142#include <asm/tlbflush.h> /* for arch/microblaze update_mmu_cache() */
4243
@@ -3363,90 +3364,129 @@ static const struct export_operations shmem_export_ops = {
33633364 .fh_to_dentry = shmem_fh_to_dentry ,
33643365};
33653366
3366- static int shmem_parse_one (struct shmem_options * ctx , char * opt , char * value )
3367+ enum shmem_param {
3368+ Opt_gid ,
3369+ Opt_huge ,
3370+ Opt_mode ,
3371+ Opt_mpol ,
3372+ Opt_nr_blocks ,
3373+ Opt_nr_inodes ,
3374+ Opt_size ,
3375+ Opt_uid ,
3376+ };
3377+
3378+ static const struct fs_parameter_spec shmem_param_specs [] = {
3379+ fsparam_u32 ("gid" , Opt_gid ),
3380+ fsparam_enum ("huge" , Opt_huge ),
3381+ fsparam_u32oct ("mode" , Opt_mode ),
3382+ fsparam_string ("mpol" , Opt_mpol ),
3383+ fsparam_string ("nr_blocks" , Opt_nr_blocks ),
3384+ fsparam_string ("nr_inodes" , Opt_nr_inodes ),
3385+ fsparam_string ("size" , Opt_size ),
3386+ fsparam_u32 ("uid" , Opt_uid ),
3387+ {}
3388+ };
3389+
3390+ static const struct fs_parameter_enum shmem_param_enums [] = {
3391+ { Opt_huge , "never" , SHMEM_HUGE_NEVER },
3392+ { Opt_huge , "always" , SHMEM_HUGE_ALWAYS },
3393+ { Opt_huge , "within_size" , SHMEM_HUGE_WITHIN_SIZE },
3394+ { Opt_huge , "advise" , SHMEM_HUGE_ADVISE },
3395+ {}
3396+ };
3397+
3398+ const struct fs_parameter_description shmem_fs_parameters = {
3399+ .name = "tmpfs" ,
3400+ .specs = shmem_param_specs ,
3401+ .enums = shmem_param_enums ,
3402+ };
3403+
3404+ static int shmem_parse_one (struct shmem_options * ctx ,
3405+ struct fs_parameter * param )
33673406{
3407+ struct fs_context * fc = NULL ;
3408+ struct fs_parse_result result ;
3409+ unsigned long long size ;
33683410 char * rest ;
3369- uid_t uid ;
3370- gid_t gid ;
3411+ int opt ;
3412+
3413+ opt = fs_parse (fc , & shmem_fs_parameters , param , & result );
3414+ if (opt < 0 ) {
3415+ if (opt == - ENOPARAM )
3416+ return invalf (fc , "tmpfs: Unknown parameter '%s'" ,
3417+ param -> key );
3418+ return opt ;
3419+ }
33713420
3372- if (! strcmp ( opt , "size" ) ) {
3373- unsigned long long size ;
3374- size = memparse (value , & rest );
3421+ switch ( opt ) {
3422+ case Opt_size :
3423+ size = memparse (param -> string , & rest );
33753424 if (* rest == '%' ) {
33763425 size <<= PAGE_SHIFT ;
33773426 size *= totalram_pages ();
33783427 do_div (size , 100 );
33793428 rest ++ ;
33803429 }
33813430 if (* rest )
3382- goto bad_val ;
3431+ goto bad_value ;
33833432 ctx -> blocks = DIV_ROUND_UP (size , PAGE_SIZE );
33843433 ctx -> seen |= SHMEM_SEEN_BLOCKS ;
3385- } else if (!strcmp (opt , "nr_blocks" )) {
3386- ctx -> blocks = memparse (value , & rest );
3434+ break ;
3435+ case Opt_nr_blocks :
3436+ ctx -> blocks = memparse (param -> string , & rest );
33873437 if (* rest )
3388- goto bad_val ;
3438+ goto bad_value ;
33893439 ctx -> seen |= SHMEM_SEEN_BLOCKS ;
3390- } else if (!strcmp (opt , "nr_inodes" )) {
3391- ctx -> inodes = memparse (value , & rest );
3440+ break ;
3441+ case Opt_nr_inodes :
3442+ ctx -> inodes = memparse (param -> string , & rest );
33923443 if (* rest )
3393- goto bad_val ;
3444+ goto bad_value ;
33943445 ctx -> seen |= SHMEM_SEEN_INODES ;
3395- } else if (!strcmp (opt , "mode" )) {
3396- ctx -> mode = simple_strtoul (value , & rest , 8 ) & 07777 ;
3397- if (* rest )
3398- goto bad_val ;
3399- } else if (!strcmp (opt , "uid" )) {
3400- uid = simple_strtoul (value , & rest , 0 );
3401- if (* rest )
3402- goto bad_val ;
3403- ctx -> uid = make_kuid (current_user_ns (), uid );
3446+ break ;
3447+ case Opt_mode :
3448+ ctx -> mode = result .uint_32 & 07777 ;
3449+ break ;
3450+ case Opt_uid :
3451+ ctx -> uid = make_kuid (current_user_ns (), result .uint_32 );
34043452 if (!uid_valid (ctx -> uid ))
3405- goto bad_val ;
3406- } else if (!strcmp (opt , "gid" )) {
3407- gid = simple_strtoul (value , & rest , 0 );
3408- if (* rest )
3409- goto bad_val ;
3410- ctx -> gid = make_kgid (current_user_ns (), gid );
3453+ goto bad_value ;
3454+ break ;
3455+ case Opt_gid :
3456+ ctx -> gid = make_kgid (current_user_ns (), result .uint_32 );
34113457 if (!gid_valid (ctx -> gid ))
3412- goto bad_val ;
3413- #ifdef CONFIG_TRANSPARENT_HUGE_PAGECACHE
3414- } else if (!strcmp (opt , "huge" )) {
3415- int huge ;
3416- huge = shmem_parse_huge (value );
3417- if (huge < 0 )
3418- goto bad_val ;
3419- if (!has_transparent_hugepage () &&
3420- huge != SHMEM_HUGE_NEVER )
3421- goto bad_val ;
3422- ctx -> huge = huge ;
3458+ goto bad_value ;
3459+ break ;
3460+ case Opt_huge :
3461+ ctx -> huge = result .uint_32 ;
3462+ if (ctx -> huge != SHMEM_HUGE_NEVER &&
3463+ !(IS_ENABLED (CONFIG_TRANSPARENT_HUGE_PAGECACHE ) &&
3464+ has_transparent_hugepage ()))
3465+ goto unsupported_parameter ;
34233466 ctx -> seen |= SHMEM_SEEN_HUGE ;
3424- #endif
3425- #ifdef CONFIG_NUMA
3426- } else if (!strcmp (opt , "mpol" )) {
3427- mpol_put (ctx -> mpol );
3428- ctx -> mpol = NULL ;
3429- if (mpol_parse_str (value , & ctx -> mpol ))
3430- goto bad_val ;
3431- #endif
3432- } else {
3433- pr_err ("tmpfs: Bad mount option %s\n" , opt );
3434- return - EINVAL ;
3467+ break ;
3468+ case Opt_mpol :
3469+ if (IS_ENABLED (CONFIG_NUMA )) {
3470+ mpol_put (ctx -> mpol );
3471+ ctx -> mpol = NULL ;
3472+ if (mpol_parse_str (param -> string , & ctx -> mpol ))
3473+ goto bad_value ;
3474+ break ;
3475+ }
3476+ goto unsupported_parameter ;
34353477 }
34363478 return 0 ;
34373479
3438- bad_val :
3439- pr_err ( "tmpfs: Bad value '%s' for mount option '%s'\n" ,
3440- value , opt );
3441- return - EINVAL ;
3480+ unsupported_parameter :
3481+ return invalf ( fc , "tmpfs: Unsupported parameter '%s'" , param -> key );
3482+ bad_value :
3483+ return invalf ( fc , "tmpfs: Bad value for '%s'" , param -> key ) ;
34423484}
34433485
34443486static int shmem_parse_options (char * options , struct shmem_options * ctx )
34453487{
3446- char * this_char , * value ;
3447-
34483488 while (options != NULL ) {
3449- this_char = options ;
3489+ char * this_char = options ;
34503490 for (;;) {
34513491 /*
34523492 * NUL-terminate this option: unfortunately,
@@ -3462,17 +3502,26 @@ static int shmem_parse_options(char *options, struct shmem_options *ctx)
34623502 break ;
34633503 }
34643504 }
3465- if (!* this_char )
3466- continue ;
3467- if ((value = strchr (this_char ,'=' )) != NULL ) {
3468- * value ++ = 0 ;
3469- } else {
3470- pr_err ("tmpfs: No value for mount option '%s'\n" ,
3471- this_char );
3472- goto error ;
3505+ if (* this_char ) {
3506+ char * value = strchr (this_char ,'=' );
3507+ struct fs_parameter param = {
3508+ .key = this_char ,
3509+ .type = fs_value_is_string ,
3510+ };
3511+ int err ;
3512+
3513+ if (value ) {
3514+ * value ++ = '\0' ;
3515+ param .size = strlen (value );
3516+ param .string = kstrdup (value , GFP_KERNEL );
3517+ if (!param .string )
3518+ goto error ;
3519+ }
3520+ err = shmem_parse_one (ctx , & param );
3521+ kfree (param .string );
3522+ if (err )
3523+ goto error ;
34733524 }
3474- if (shmem_parse_one (ctx , this_char , value ) < 0 )
3475- goto error ;
34763525 }
34773526 return 0 ;
34783527
0 commit comments