2007-06-12 17:07:21 +04:00
/*
* Copyright ( C ) 2007 Oracle . All rights reserved .
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public
* License v2 as published by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* General Public License for more details .
*
* You should have received a copy of the GNU General Public
* License along with this program ; if not , write to the
* Free Software Foundation , Inc . , 59 Temple Place - Suite 330 ,
* Boston , MA 021110 - 1307 , USA .
*/
2007-08-29 23:47:34 +04:00
# include <linux/sched.h>
# include <linux/slab.h>
# include <linux/spinlock.h>
# include <linux/completion.h>
# include <linux/buffer_head.h>
# include <linux/kobject.h>
2013-11-01 21:07:00 +04:00
# include <linux/bug.h>
2013-11-01 21:07:05 +04:00
# include <linux/genhd.h>
2014-02-05 18:36:18 +04:00
# include <linux/debugfs.h>
2007-08-29 23:47:34 +04:00
2007-04-05 05:22:22 +04:00
# include "ctree.h"
# include "disk-io.h"
# include "transaction.h"
2013-11-01 21:06:57 +04:00
# include "sysfs.h"
2013-11-01 21:07:05 +04:00
# include "volumes.h"
2013-11-01 21:06:57 +04:00
2013-11-01 21:06:59 +04:00
static inline struct btrfs_fs_info * to_fs_info ( struct kobject * kobj ) ;
2015-03-10 01:38:29 +03:00
static inline struct btrfs_fs_devices * to_fs_devs ( struct kobject * kobj ) ;
2013-11-01 21:06:58 +04:00
2013-11-01 21:06:59 +04:00
static u64 get_features ( struct btrfs_fs_info * fs_info ,
enum btrfs_feature_set set )
2013-11-01 21:06:58 +04:00
{
2013-11-01 21:06:59 +04:00
struct btrfs_super_block * disk_super = fs_info - > super_copy ;
if ( set = = FEAT_COMPAT )
return btrfs_super_compat_flags ( disk_super ) ;
else if ( set = = FEAT_COMPAT_RO )
return btrfs_super_compat_ro_flags ( disk_super ) ;
else
return btrfs_super_incompat_flags ( disk_super ) ;
2013-11-01 21:06:58 +04:00
}
2013-11-01 21:07:01 +04:00
static void set_features ( struct btrfs_fs_info * fs_info ,
enum btrfs_feature_set set , u64 features )
{
struct btrfs_super_block * disk_super = fs_info - > super_copy ;
if ( set = = FEAT_COMPAT )
btrfs_set_super_compat_flags ( disk_super , features ) ;
else if ( set = = FEAT_COMPAT_RO )
btrfs_set_super_compat_ro_flags ( disk_super , features ) ;
else
btrfs_set_super_incompat_flags ( disk_super , features ) ;
}
static int can_modify_feature ( struct btrfs_feature_attr * fa )
{
int val = 0 ;
u64 set , clear ;
switch ( fa - > feature_set ) {
case FEAT_COMPAT :
set = BTRFS_FEATURE_COMPAT_SAFE_SET ;
clear = BTRFS_FEATURE_COMPAT_SAFE_CLEAR ;
break ;
case FEAT_COMPAT_RO :
set = BTRFS_FEATURE_COMPAT_RO_SAFE_SET ;
clear = BTRFS_FEATURE_COMPAT_RO_SAFE_CLEAR ;
break ;
case FEAT_INCOMPAT :
set = BTRFS_FEATURE_INCOMPAT_SAFE_SET ;
clear = BTRFS_FEATURE_INCOMPAT_SAFE_CLEAR ;
break ;
default :
2013-11-19 16:36:21 +04:00
printk ( KERN_WARNING " btrfs: sysfs: unknown feature set %d \n " ,
fa - > feature_set ) ;
return 0 ;
2013-11-01 21:07:01 +04:00
}
if ( set & fa - > feature_bit )
val | = 1 ;
if ( clear & fa - > feature_bit )
val | = 2 ;
return val ;
}
2013-11-01 21:06:59 +04:00
static ssize_t btrfs_feature_attr_show ( struct kobject * kobj ,
struct kobj_attribute * a , char * buf )
2013-11-01 21:06:58 +04:00
{
2013-11-01 21:06:59 +04:00
int val = 0 ;
2013-11-01 21:06:58 +04:00
struct btrfs_fs_info * fs_info = to_fs_info ( kobj ) ;
2013-11-01 21:07:01 +04:00
struct btrfs_feature_attr * fa = to_btrfs_feature_attr ( a ) ;
2013-11-01 21:06:59 +04:00
if ( fs_info ) {
u64 features = get_features ( fs_info , fa - > feature_set ) ;
if ( features & fa - > feature_bit )
val = 1 ;
2013-11-01 21:07:01 +04:00
} else
val = can_modify_feature ( fa ) ;
2013-11-01 21:06:59 +04:00
return snprintf ( buf , PAGE_SIZE , " %d \n " , val ) ;
2013-11-01 21:06:58 +04:00
}
2013-11-01 21:07:01 +04:00
static ssize_t btrfs_feature_attr_store ( struct kobject * kobj ,
struct kobj_attribute * a ,
const char * buf , size_t count )
{
struct btrfs_fs_info * fs_info ;
struct btrfs_feature_attr * fa = to_btrfs_feature_attr ( a ) ;
u64 features , set , clear ;
unsigned long val ;
int ret ;
fs_info = to_fs_info ( kobj ) ;
if ( ! fs_info )
return - EPERM ;
ret = kstrtoul ( skip_spaces ( buf ) , 0 , & val ) ;
if ( ret )
return ret ;
if ( fa - > feature_set = = FEAT_COMPAT ) {
set = BTRFS_FEATURE_COMPAT_SAFE_SET ;
clear = BTRFS_FEATURE_COMPAT_SAFE_CLEAR ;
} else if ( fa - > feature_set = = FEAT_COMPAT_RO ) {
set = BTRFS_FEATURE_COMPAT_RO_SAFE_SET ;
clear = BTRFS_FEATURE_COMPAT_RO_SAFE_CLEAR ;
} else {
set = BTRFS_FEATURE_INCOMPAT_SAFE_SET ;
clear = BTRFS_FEATURE_INCOMPAT_SAFE_CLEAR ;
}
features = get_features ( fs_info , fa - > feature_set ) ;
/* Nothing to do */
if ( ( val & & ( features & fa - > feature_bit ) ) | |
( ! val & & ! ( features & fa - > feature_bit ) ) )
return count ;
if ( ( val & & ! ( set & fa - > feature_bit ) ) | |
( ! val & & ! ( clear & fa - > feature_bit ) ) ) {
btrfs_info ( fs_info ,
" %sabling feature %s on mounted fs is not supported. " ,
val ? " En " : " Dis " , fa - > kobj_attr . attr . name ) ;
return - EPERM ;
}
btrfs_info ( fs_info , " %s %s feature flag " ,
val ? " Setting " : " Clearing " , fa - > kobj_attr . attr . name ) ;
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 ) ;
2014-11-12 16:22:21 +03:00
/*
* We don ' t want to do full transaction commit from inside sysfs
*/
btrfs_set_pending ( fs_info , COMMIT ) ;
wake_up_process ( fs_info - > transaction_kthread ) ;
2013-11-01 21:07:01 +04:00
return count ;
}
2013-11-01 21:06:59 +04:00
static umode_t btrfs_feature_visible ( struct kobject * kobj ,
struct attribute * attr , int unused )
2013-11-01 21:06:57 +04:00
{
2013-11-01 21:06:59 +04:00
struct btrfs_fs_info * fs_info = to_fs_info ( kobj ) ;
umode_t mode = attr - > mode ;
if ( fs_info ) {
struct btrfs_feature_attr * fa ;
u64 features ;
fa = attr_to_btrfs_feature_attr ( attr ) ;
features = get_features ( fs_info , fa - > feature_set ) ;
2013-11-01 21:07:01 +04:00
if ( can_modify_feature ( fa ) )
mode | = S_IWUSR ;
else if ( ! ( features & fa - > feature_bit ) )
2013-11-01 21:06:59 +04:00
mode = 0 ;
}
return mode ;
2013-11-01 21:06:57 +04:00
}
BTRFS_FEAT_ATTR_INCOMPAT ( mixed_backref , MIXED_BACKREF ) ;
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 ( big_metadata , BIG_METADATA ) ;
BTRFS_FEAT_ATTR_INCOMPAT ( extended_iref , EXTENDED_IREF ) ;
BTRFS_FEAT_ATTR_INCOMPAT ( raid56 , RAID56 ) ;
BTRFS_FEAT_ATTR_INCOMPAT ( skinny_metadata , SKINNY_METADATA ) ;
2014-01-21 21:56:09 +04:00
BTRFS_FEAT_ATTR_INCOMPAT ( no_holes , NO_HOLES ) ;
2013-11-01 21:06:57 +04:00
static struct attribute * btrfs_supported_feature_attrs [ ] = {
BTRFS_FEAT_ATTR_PTR ( mixed_backref ) ,
BTRFS_FEAT_ATTR_PTR ( default_subvol ) ,
BTRFS_FEAT_ATTR_PTR ( mixed_groups ) ,
BTRFS_FEAT_ATTR_PTR ( compress_lzo ) ,
BTRFS_FEAT_ATTR_PTR ( big_metadata ) ,
BTRFS_FEAT_ATTR_PTR ( extended_iref ) ,
BTRFS_FEAT_ATTR_PTR ( raid56 ) ,
BTRFS_FEAT_ATTR_PTR ( skinny_metadata ) ,
2014-01-21 21:56:09 +04:00
BTRFS_FEAT_ATTR_PTR ( no_holes ) ,
2013-11-01 21:06:57 +04:00
NULL
} ;
static const struct attribute_group btrfs_feature_attr_group = {
. name = " features " ,
2013-11-01 21:06:59 +04:00
. is_visible = btrfs_feature_visible ,
2013-11-01 21:06:57 +04:00
. attrs = btrfs_supported_feature_attrs ,
} ;
2007-08-29 23:47:34 +04:00
2013-11-01 21:07:04 +04:00
static ssize_t btrfs_show_u64 ( u64 * value_ptr , spinlock_t * lock , char * buf )
{
u64 val ;
if ( lock )
spin_lock ( lock ) ;
val = * value_ptr ;
if ( lock )
spin_unlock ( lock ) ;
return snprintf ( buf , PAGE_SIZE , " %llu \n " , val ) ;
}
static ssize_t global_rsv_size_show ( struct kobject * kobj ,
struct kobj_attribute * ka , char * buf )
{
struct btrfs_fs_info * fs_info = to_fs_info ( kobj - > parent ) ;
struct btrfs_block_rsv * block_rsv = & fs_info - > global_block_rsv ;
return btrfs_show_u64 ( & block_rsv - > size , & block_rsv - > lock , buf ) ;
}
2014-07-30 16:04:08 +04:00
BTRFS_ATTR ( global_rsv_size , global_rsv_size_show ) ;
2013-11-01 21:07:04 +04:00
static ssize_t global_rsv_reserved_show ( struct kobject * kobj ,
struct kobj_attribute * a , char * buf )
{
struct btrfs_fs_info * fs_info = to_fs_info ( kobj - > parent ) ;
struct btrfs_block_rsv * block_rsv = & fs_info - > global_block_rsv ;
return btrfs_show_u64 ( & block_rsv - > reserved , & block_rsv - > lock , buf ) ;
}
2014-07-30 16:04:08 +04:00
BTRFS_ATTR ( global_rsv_reserved , global_rsv_reserved_show ) ;
2013-11-01 21:07:04 +04:00
# define to_space_info(_kobj) container_of(_kobj, struct btrfs_space_info, kobj)
2014-05-27 20:59:57 +04:00
# define to_raid_kobj(_kobj) container_of(_kobj, struct raid_kobject, kobj)
2013-11-01 21:07:04 +04:00
static ssize_t raid_bytes_show ( struct kobject * kobj ,
struct kobj_attribute * attr , char * buf ) ;
BTRFS_RAID_ATTR ( total_bytes , raid_bytes_show ) ;
BTRFS_RAID_ATTR ( used_bytes , raid_bytes_show ) ;
static ssize_t raid_bytes_show ( struct kobject * kobj ,
struct kobj_attribute * attr , char * buf )
{
struct btrfs_space_info * sinfo = to_space_info ( kobj - > parent ) ;
struct btrfs_block_group_cache * block_group ;
2014-05-27 20:59:57 +04:00
int index = to_raid_kobj ( kobj ) - > raid_type ;
2013-11-01 21:07:04 +04:00
u64 val = 0 ;
down_read ( & sinfo - > groups_sem ) ;
list_for_each_entry ( block_group , & sinfo - > block_groups [ index ] , list ) {
if ( & attr - > attr = = BTRFS_RAID_ATTR_PTR ( total_bytes ) )
val + = block_group - > key . offset ;
else
val + = btrfs_block_group_used ( & block_group - > item ) ;
}
up_read ( & sinfo - > groups_sem ) ;
return snprintf ( buf , PAGE_SIZE , " %llu \n " , val ) ;
}
static struct attribute * raid_attributes [ ] = {
BTRFS_RAID_ATTR_PTR ( total_bytes ) ,
BTRFS_RAID_ATTR_PTR ( used_bytes ) ,
NULL
} ;
static void release_raid_kobj ( struct kobject * kobj )
{
2014-05-27 20:59:57 +04:00
kfree ( to_raid_kobj ( kobj ) ) ;
2013-11-01 21:07:04 +04:00
}
struct kobj_type btrfs_raid_ktype = {
. sysfs_ops = & kobj_sysfs_ops ,
. release = release_raid_kobj ,
. default_attrs = raid_attributes ,
} ;
# define SPACE_INFO_ATTR(field) \
static ssize_t btrfs_space_info_show_ # # field ( struct kobject * kobj , \
struct kobj_attribute * a , \
char * buf ) \
{ \
struct btrfs_space_info * sinfo = to_space_info ( kobj ) ; \
return btrfs_show_u64 ( & sinfo - > field , & sinfo - > lock , buf ) ; \
} \
2014-07-30 16:04:08 +04:00
BTRFS_ATTR ( field , btrfs_space_info_show_ # # field )
2013-11-01 21:07:04 +04:00
static ssize_t btrfs_space_info_show_total_bytes_pinned ( struct kobject * kobj ,
struct kobj_attribute * a ,
char * buf )
{
struct btrfs_space_info * sinfo = to_space_info ( kobj ) ;
s64 val = percpu_counter_sum ( & sinfo - > total_bytes_pinned ) ;
return snprintf ( buf , PAGE_SIZE , " %lld \n " , val ) ;
}
SPACE_INFO_ATTR ( flags ) ;
SPACE_INFO_ATTR ( total_bytes ) ;
SPACE_INFO_ATTR ( bytes_used ) ;
SPACE_INFO_ATTR ( bytes_pinned ) ;
SPACE_INFO_ATTR ( bytes_reserved ) ;
SPACE_INFO_ATTR ( bytes_may_use ) ;
SPACE_INFO_ATTR ( disk_used ) ;
SPACE_INFO_ATTR ( disk_total ) ;
2014-07-30 16:04:08 +04:00
BTRFS_ATTR ( total_bytes_pinned , btrfs_space_info_show_total_bytes_pinned ) ;
2013-11-01 21:07:04 +04:00
static struct attribute * space_info_attrs [ ] = {
BTRFS_ATTR_PTR ( flags ) ,
BTRFS_ATTR_PTR ( total_bytes ) ,
BTRFS_ATTR_PTR ( bytes_used ) ,
BTRFS_ATTR_PTR ( bytes_pinned ) ,
BTRFS_ATTR_PTR ( bytes_reserved ) ,
BTRFS_ATTR_PTR ( bytes_may_use ) ,
BTRFS_ATTR_PTR ( disk_used ) ,
BTRFS_ATTR_PTR ( disk_total ) ,
BTRFS_ATTR_PTR ( total_bytes_pinned ) ,
NULL ,
} ;
static void space_info_release ( struct kobject * kobj )
{
struct btrfs_space_info * sinfo = to_space_info ( kobj ) ;
percpu_counter_destroy ( & sinfo - > total_bytes_pinned ) ;
kfree ( sinfo ) ;
}
struct kobj_type space_info_ktype = {
. sysfs_ops = & kobj_sysfs_ops ,
. release = space_info_release ,
. default_attrs = space_info_attrs ,
} ;
static const struct attribute * allocation_attrs [ ] = {
BTRFS_ATTR_PTR ( global_rsv_reserved ) ,
BTRFS_ATTR_PTR ( global_rsv_size ) ,
NULL ,
} ;
2013-11-01 21:07:06 +04:00
static ssize_t btrfs_label_show ( struct kobject * kobj ,
struct kobj_attribute * a , char * buf )
{
struct btrfs_fs_info * fs_info = to_fs_info ( kobj ) ;
2014-07-01 12:00:07 +04:00
char * label = fs_info - > super_copy - > label ;
return snprintf ( buf , PAGE_SIZE , label [ 0 ] ? " %s \n " : " %s " , label ) ;
2013-11-01 21:07:06 +04:00
}
static ssize_t btrfs_label_store ( struct kobject * kobj ,
struct kobj_attribute * a ,
const char * buf , size_t len )
{
struct btrfs_fs_info * fs_info = to_fs_info ( kobj ) ;
2014-07-01 12:00:07 +04:00
size_t p_len ;
2013-11-01 21:07:06 +04:00
2014-07-30 16:04:10 +04:00
if ( fs_info - > sb - > s_flags & MS_RDONLY )
return - EROFS ;
2014-07-01 12:00:07 +04:00
/*
* p_len is the len until the first occurrence of either
* ' \n ' or ' \0 '
*/
p_len = strcspn ( buf , " \n " ) ;
if ( p_len > = BTRFS_LABEL_SIZE )
2013-11-01 21:07:06 +04:00
return - EINVAL ;
2014-05-30 21:29:05 +04:00
spin_lock ( & fs_info - > super_lock ) ;
2014-07-01 12:00:07 +04:00
memset ( fs_info - > super_copy - > label , 0 , BTRFS_LABEL_SIZE ) ;
memcpy ( fs_info - > super_copy - > label , buf , p_len ) ;
2014-05-30 21:29:05 +04:00
spin_unlock ( & fs_info - > super_lock ) ;
2013-11-01 21:07:06 +04:00
2014-05-30 21:29:05 +04:00
/*
* We don ' t want to do full transaction commit from inside sysfs
*/
btrfs_set_pending ( fs_info , COMMIT ) ;
wake_up_process ( fs_info - > transaction_kthread ) ;
2013-11-01 21:07:06 +04:00
2014-05-30 21:29:05 +04:00
return len ;
2013-11-01 21:07:06 +04:00
}
2014-07-30 16:04:09 +04:00
BTRFS_ATTR_RW ( label , btrfs_label_show , btrfs_label_store ) ;
2013-11-01 21:07:06 +04:00
2014-05-07 20:17:16 +04:00
static ssize_t btrfs_nodesize_show ( struct kobject * kobj ,
struct kobj_attribute * a , char * buf )
{
struct btrfs_fs_info * fs_info = to_fs_info ( kobj ) ;
return snprintf ( buf , PAGE_SIZE , " %u \n " , fs_info - > super_copy - > nodesize ) ;
}
2014-07-30 16:04:08 +04:00
BTRFS_ATTR ( nodesize , btrfs_nodesize_show ) ;
2014-05-07 20:17:16 +04:00
static ssize_t btrfs_sectorsize_show ( struct kobject * kobj ,
struct kobj_attribute * a , char * buf )
{
struct btrfs_fs_info * fs_info = to_fs_info ( kobj ) ;
return snprintf ( buf , PAGE_SIZE , " %u \n " , fs_info - > super_copy - > sectorsize ) ;
}
2014-07-30 16:04:08 +04:00
BTRFS_ATTR ( sectorsize , btrfs_sectorsize_show ) ;
2014-05-07 20:17:16 +04:00
static ssize_t btrfs_clone_alignment_show ( struct kobject * kobj ,
struct kobj_attribute * a , char * buf )
{
struct btrfs_fs_info * fs_info = to_fs_info ( kobj ) ;
return snprintf ( buf , PAGE_SIZE , " %u \n " , fs_info - > super_copy - > sectorsize ) ;
}
2014-07-30 16:04:08 +04:00
BTRFS_ATTR ( clone_alignment , btrfs_clone_alignment_show ) ;
2014-05-07 20:17:16 +04:00
2015-03-10 01:38:27 +03:00
static const struct attribute * btrfs_attrs [ ] = {
2013-11-01 21:07:06 +04:00
BTRFS_ATTR_PTR ( label ) ,
2014-05-07 20:17:16 +04:00
BTRFS_ATTR_PTR ( nodesize ) ,
BTRFS_ATTR_PTR ( sectorsize ) ,
BTRFS_ATTR_PTR ( clone_alignment ) ,
2013-11-01 21:07:06 +04:00
NULL ,
} ;
2013-11-01 21:06:59 +04:00
static void btrfs_release_super_kobj ( struct kobject * kobj )
{
2015-03-10 01:38:29 +03:00
struct btrfs_fs_devices * fs_devs = to_fs_devs ( kobj ) ;
2015-03-10 01:38:19 +03:00
2015-03-10 01:38:29 +03:00
memset ( & fs_devs - > super_kobj , 0 , sizeof ( struct kobject ) ) ;
complete ( & fs_devs - > kobj_unregister ) ;
2013-11-01 21:06:59 +04:00
}
static struct kobj_type btrfs_ktype = {
. sysfs_ops = & kobj_sysfs_ops ,
. release = btrfs_release_super_kobj ,
} ;
2015-03-10 01:38:29 +03:00
static inline struct btrfs_fs_devices * to_fs_devs ( struct kobject * kobj )
{
if ( kobj - > ktype ! = & btrfs_ktype )
return NULL ;
return container_of ( kobj , struct btrfs_fs_devices , super_kobj ) ;
}
2013-11-01 21:06:59 +04:00
static inline struct btrfs_fs_info * to_fs_info ( struct kobject * kobj )
{
if ( kobj - > ktype ! = & btrfs_ktype )
return NULL ;
2015-03-10 01:38:29 +03:00
return to_fs_devs ( kobj ) - > fs_info ;
2013-11-01 21:06:59 +04:00
}
2007-08-29 23:47:34 +04:00
2013-11-21 19:37:16 +04:00
# define NUM_FEATURE_BITS 64
static char btrfs_unknown_feature_names [ 3 ] [ NUM_FEATURE_BITS ] [ 13 ] ;
static struct btrfs_feature_attr btrfs_feature_attrs [ 3 ] [ NUM_FEATURE_BITS ] ;
2015-01-02 20:23:10 +03:00
static const u64 supported_feature_masks [ 3 ] = {
2013-11-21 19:37:16 +04:00
[ FEAT_COMPAT ] = BTRFS_FEATURE_COMPAT_SUPP ,
[ FEAT_COMPAT_RO ] = BTRFS_FEATURE_COMPAT_RO_SUPP ,
[ FEAT_INCOMPAT ] = BTRFS_FEATURE_INCOMPAT_SUPP ,
} ;
static int 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 ;
2015-03-10 01:38:29 +03:00
ret = sysfs_merge_group ( & fs_info - > fs_devices - > super_kobj ,
2013-11-21 19:37:16 +04:00
& agroup ) ;
if ( ret )
return ret ;
} else
2015-03-10 01:38:29 +03:00
sysfs_unmerge_group ( & fs_info - > fs_devices - > super_kobj ,
2013-11-21 19:37:16 +04:00
& agroup ) ;
}
}
return 0 ;
}
2015-03-10 01:38:32 +03:00
static void __btrfs_sysfs_remove_fsid ( struct btrfs_fs_devices * fs_devs )
2013-11-01 21:06:58 +04:00
{
2015-03-10 01:38:29 +03:00
if ( fs_devs - > device_dir_kobj ) {
kobject_del ( fs_devs - > device_dir_kobj ) ;
kobject_put ( fs_devs - > device_dir_kobj ) ;
fs_devs - > device_dir_kobj = NULL ;
2015-03-10 01:38:24 +03:00
}
2015-03-10 01:38:29 +03:00
kobject_del ( & fs_devs - > super_kobj ) ;
kobject_put ( & fs_devs - > super_kobj ) ;
wait_for_completion ( & fs_devs - > kobj_unregister ) ;
2013-11-01 21:06:58 +04:00
}
2015-03-10 01:38:32 +03:00
/* when fs_devs is NULL it will remove all fsid kobject */
static 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 ;
}
list_for_each_entry ( fs_devs , fs_uuids , list ) {
__btrfs_sysfs_remove_fsid ( fs_devs ) ;
}
}
2013-11-21 19:37:16 +04:00
void btrfs_sysfs_remove_one ( struct btrfs_fs_info * fs_info )
{
2015-03-10 01:38:31 +03:00
btrfs_reset_fs_info_ptr ( fs_info ) ;
2013-11-21 19:37:16 +04:00
if ( fs_info - > space_info_kobj ) {
sysfs_remove_files ( fs_info - > space_info_kobj , allocation_attrs ) ;
kobject_del ( fs_info - > space_info_kobj ) ;
kobject_put ( fs_info - > space_info_kobj ) ;
}
addrm_unknown_feature_attrs ( fs_info , false ) ;
2015-03-10 01:38:29 +03:00
sysfs_remove_group ( & fs_info - > fs_devices - > super_kobj , & btrfs_feature_attr_group ) ;
sysfs_remove_files ( & fs_info - > fs_devices - > super_kobj , btrfs_attrs ) ;
2015-03-10 01:38:34 +03:00
btrfs_kobj_rm_device ( fs_info - > fs_devices , NULL ) ;
2015-03-10 01:38:29 +03:00
btrfs_sysfs_remove_fsid ( fs_info - > fs_devices ) ;
2013-11-21 19:37:16 +04:00
}
2013-11-01 21:07:00 +04:00
const char * const btrfs_feature_set_names [ 3 ] = {
[ FEAT_COMPAT ] = " compat " ,
[ FEAT_COMPAT_RO ] = " compat_ro " ,
[ FEAT_INCOMPAT ] = " incompat " ,
} ;
2013-11-01 21:07:02 +04:00
char * btrfs_printable_features ( enum btrfs_feature_set set , u64 flags )
{
size_t bufsize = 4096 ; /* safe max, 64 names * 64 bytes */
int len = 0 ;
int i ;
char * str ;
str = kmalloc ( bufsize , GFP_KERNEL ) ;
if ( ! str )
return str ;
for ( i = 0 ; i < ARRAY_SIZE ( btrfs_feature_attrs [ set ] ) ; i + + ) {
const char * name ;
if ( ! ( flags & ( 1ULL < < i ) ) )
continue ;
name = btrfs_feature_attrs [ set ] [ i ] . kobj_attr . attr . name ;
len + = snprintf ( str + len , bufsize - len , " %s%s " ,
len ? " , " : " " , name ) ;
}
return str ;
}
2013-11-01 21:07:00 +04:00
static void init_feature_attrs ( void )
{
struct btrfs_feature_attr * fa ;
int set , i ;
BUILD_BUG_ON ( ARRAY_SIZE ( btrfs_unknown_feature_names ) ! =
ARRAY_SIZE ( btrfs_feature_attrs ) ) ;
BUILD_BUG_ON ( ARRAY_SIZE ( btrfs_unknown_feature_names [ 0 ] ) ! =
ARRAY_SIZE ( btrfs_feature_attrs [ 0 ] ) ) ;
2013-11-01 21:07:02 +04:00
memset ( btrfs_feature_attrs , 0 , sizeof ( btrfs_feature_attrs ) ) ;
memset ( btrfs_unknown_feature_names , 0 ,
sizeof ( btrfs_unknown_feature_names ) ) ;
2013-11-01 21:07:00 +04:00
for ( i = 0 ; btrfs_supported_feature_attrs [ i ] ; i + + ) {
struct btrfs_feature_attr * sfa ;
struct attribute * a = btrfs_supported_feature_attrs [ i ] ;
2013-11-01 21:07:02 +04:00
int bit ;
2013-11-01 21:07:00 +04:00
sfa = attr_to_btrfs_feature_attr ( a ) ;
2013-11-01 21:07:02 +04:00
bit = ilog2 ( sfa - > feature_bit ) ;
fa = & btrfs_feature_attrs [ sfa - > feature_set ] [ bit ] ;
2013-11-01 21:07:00 +04:00
fa - > kobj_attr . attr . name = sfa - > kobj_attr . attr . name ;
}
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 ] ;
if ( fa - > kobj_attr . attr . name )
continue ;
snprintf ( name , 13 , " %s:%u " ,
btrfs_feature_set_names [ set ] , i ) ;
fa - > kobj_attr . attr . name = name ;
fa - > kobj_attr . attr . mode = S_IRUGO ;
fa - > feature_set = set ;
fa - > feature_bit = 1ULL < < i ;
}
}
}
2015-03-10 01:38:21 +03:00
/* when one_device is NULL, it removes all device links */
2015-03-10 01:38:34 +03:00
int btrfs_kobj_rm_device ( struct btrfs_fs_devices * fs_devices ,
2014-06-03 07:36:00 +04:00
struct btrfs_device * one_device )
{
struct hd_struct * disk ;
struct kobject * disk_kobj ;
2015-03-10 01:38:34 +03:00
if ( ! fs_devices - > device_dir_kobj )
2014-06-03 07:36:00 +04:00
return - EINVAL ;
2014-07-29 15:09:39 +04:00
if ( one_device & & one_device - > bdev ) {
2014-06-03 07:36:00 +04:00
disk = one_device - > bdev - > bd_part ;
disk_kobj = & part_to_dev ( disk ) - > kobj ;
2015-03-10 01:38:34 +03:00
sysfs_remove_link ( fs_devices - > device_dir_kobj ,
2014-06-03 07:36:00 +04:00
disk_kobj - > name ) ;
}
2015-03-10 01:38:21 +03:00
if ( one_device )
return 0 ;
list_for_each_entry ( one_device ,
2015-03-10 01:38:34 +03:00
& fs_devices - > devices , dev_list ) {
2015-03-10 01:38:21 +03:00
if ( ! one_device - > bdev )
continue ;
disk = one_device - > bdev - > bd_part ;
disk_kobj = & part_to_dev ( disk ) - > kobj ;
2015-03-10 01:38:34 +03:00
sysfs_remove_link ( fs_devices - > device_dir_kobj ,
2015-03-10 01:38:21 +03:00
disk_kobj - > name ) ;
}
2014-06-03 07:36:00 +04:00
return 0 ;
}
2015-03-10 01:38:29 +03:00
int btrfs_sysfs_add_device ( struct btrfs_fs_devices * fs_devs )
2013-11-01 21:07:05 +04:00
{
2015-03-10 01:38:29 +03:00
if ( ! fs_devs - > device_dir_kobj )
fs_devs - > device_dir_kobj = kobject_create_and_add ( " devices " ,
& fs_devs - > super_kobj ) ;
2014-06-03 07:36:01 +04:00
2015-03-10 01:38:29 +03:00
if ( ! fs_devs - > device_dir_kobj )
2013-11-01 21:07:05 +04:00
return - ENOMEM ;
2015-03-10 01:38:28 +03:00
return 0 ;
}
2015-03-10 01:38:33 +03:00
int btrfs_kobj_add_device ( struct btrfs_fs_devices * fs_devices ,
struct btrfs_device * one_device )
2015-03-10 01:38:28 +03:00
{
int error = 0 ;
struct btrfs_device * dev ;
2015-03-10 01:38:29 +03:00
error = btrfs_sysfs_add_device ( fs_devices ) ;
2015-03-10 01:38:28 +03:00
if ( error )
return error ;
2013-11-01 21:07:05 +04:00
list_for_each_entry ( dev , & fs_devices - > devices , dev_list ) {
2014-01-15 13:22:28 +04:00
struct hd_struct * disk ;
struct kobject * disk_kobj ;
if ( ! dev - > bdev )
continue ;
2014-06-03 07:36:01 +04:00
if ( one_device & & one_device ! = dev )
continue ;
2014-01-15 13:22:28 +04:00
disk = dev - > bdev - > bd_part ;
disk_kobj = & part_to_dev ( disk ) - > kobj ;
2013-11-01 21:07:05 +04:00
2015-03-10 01:38:29 +03:00
error = sysfs_create_link ( fs_devices - > device_dir_kobj ,
2013-11-01 21:07:05 +04:00
disk_kobj , disk_kobj - > name ) ;
if ( error )
break ;
}
return error ;
}
2013-11-01 21:06:59 +04:00
/* /sys/fs/btrfs/ entry */
static struct kset * btrfs_kset ;
2014-02-05 18:36:18 +04:00
/* /sys/kernel/debug/btrfs */
static struct dentry * btrfs_debugfs_root_dentry ;
/* Debugging tunables and exported data */
u64 btrfs_debugfs_test ;
2015-03-10 01:38:26 +03:00
/*
* Can be called by the device discovery thread .
* And parent can be specified for seed device
*/
2015-03-10 01:38:35 +03:00
int btrfs_sysfs_add_fsid ( struct btrfs_fs_devices * fs_devs ,
2015-03-10 01:38:26 +03:00
struct kobject * parent )
2013-11-01 21:06:58 +04:00
{
int error ;
2015-03-10 01:38:29 +03:00
init_completion ( & fs_devs - > kobj_unregister ) ;
fs_devs - > super_kobj . kset = btrfs_kset ;
error = kobject_init_and_add ( & fs_devs - > super_kobj , & btrfs_ktype , NULL ,
" %pU " , fs_devs - > fsid ) ;
2015-03-10 01:38:26 +03:00
return error ;
}
int btrfs_sysfs_add_one ( struct btrfs_fs_info * fs_info )
{
int error ;
2015-03-10 01:38:29 +03:00
struct btrfs_fs_devices * fs_devs = fs_info - > fs_devices ;
struct kobject * super_kobj = & fs_devs - > super_kobj ;
2015-03-10 01:38:26 +03:00
2015-03-10 01:38:31 +03:00
btrfs_set_fs_info_ptr ( fs_info ) ;
2015-03-10 01:38:29 +03:00
error = btrfs_sysfs_add_fsid ( fs_devs , NULL ) ;
2013-11-21 19:37:16 +04:00
if ( error )
return error ;
2013-11-01 21:06:59 +04:00
2015-03-10 01:38:33 +03:00
error = btrfs_kobj_add_device ( fs_devs , NULL ) ;
2015-03-10 01:38:24 +03:00
if ( error ) {
2015-03-10 01:38:29 +03:00
btrfs_sysfs_remove_fsid ( fs_devs ) ;
2015-03-10 01:38:24 +03:00
return error ;
}
2015-03-10 01:38:29 +03:00
error = sysfs_create_files ( super_kobj , btrfs_attrs ) ;
2013-11-21 19:37:16 +04:00
if ( error ) {
2015-03-10 01:38:29 +03:00
btrfs_sysfs_remove_fsid ( fs_devs ) ;
2013-11-21 19:37:16 +04:00
return error ;
}
2013-11-01 21:07:00 +04:00
2015-03-10 01:38:29 +03:00
error = sysfs_create_group ( super_kobj ,
2015-03-10 01:38:27 +03:00
& btrfs_feature_attr_group ) ;
if ( error )
goto failure ;
2013-11-21 19:37:16 +04:00
error = addrm_unknown_feature_attrs ( fs_info , true ) ;
2013-11-01 21:07:00 +04:00
if ( error )
goto failure ;
2013-11-01 21:07:04 +04:00
fs_info - > space_info_kobj = kobject_create_and_add ( " allocation " ,
2015-03-10 01:38:29 +03:00
super_kobj ) ;
2013-11-01 21:07:04 +04:00
if ( ! fs_info - > space_info_kobj ) {
error = - ENOMEM ;
goto failure ;
}
error = sysfs_create_files ( fs_info - > space_info_kobj , allocation_attrs ) ;
if ( error )
goto failure ;
2013-11-01 21:07:00 +04:00
return 0 ;
failure :
btrfs_sysfs_remove_one ( fs_info ) ;
2013-11-01 21:06:58 +04:00
return error ;
}
2014-02-05 18:36:18 +04:00
static int btrfs_init_debugfs ( void )
{
# ifdef CONFIG_DEBUG_FS
btrfs_debugfs_root_dentry = debugfs_create_dir ( " btrfs " , NULL ) ;
if ( ! btrfs_debugfs_root_dentry )
return - ENOMEM ;
debugfs_create_u64 ( " test " , S_IRUGO | S_IWUGO , btrfs_debugfs_root_dentry ,
& btrfs_debugfs_test ) ;
# endif
return 0 ;
}
2008-09-06 00:43:31 +04:00
int btrfs_init_sysfs ( void )
2007-08-29 23:47:34 +04:00
{
2013-11-01 21:06:57 +04:00
int ret ;
2014-02-05 18:36:18 +04:00
2008-02-20 22:14:16 +03:00
btrfs_kset = kset_create_and_add ( " btrfs " , NULL , fs_kobj ) ;
if ( ! btrfs_kset )
return - ENOMEM ;
2013-11-01 21:06:57 +04:00
2014-02-05 18:36:18 +04:00
ret = btrfs_init_debugfs ( ) ;
if ( ret )
2015-01-23 21:27:00 +03:00
goto out1 ;
2013-11-01 21:07:00 +04:00
2014-02-05 18:36:18 +04:00
init_feature_attrs ( ) ;
2013-11-01 21:06:57 +04:00
ret = sysfs_create_group ( & btrfs_kset - > kobj , & btrfs_feature_attr_group ) ;
2015-01-23 21:27:00 +03:00
if ( ret )
goto out2 ;
return 0 ;
out2 :
debugfs_remove_recursive ( btrfs_debugfs_root_dentry ) ;
out1 :
kset_unregister ( btrfs_kset ) ;
2013-11-01 21:06:57 +04:00
2014-02-05 18:36:18 +04:00
return ret ;
2007-08-29 23:47:34 +04:00
}
2008-09-06 00:43:31 +04:00
void btrfs_exit_sysfs ( void )
2007-08-29 23:47:34 +04:00
{
2013-11-01 21:06:57 +04:00
sysfs_remove_group ( & btrfs_kset - > kobj , & btrfs_feature_attr_group ) ;
2008-02-20 22:14:16 +03:00
kset_unregister ( btrfs_kset ) ;
2014-02-05 18:36:18 +04:00
debugfs_remove_recursive ( btrfs_debugfs_root_dentry ) ;
2007-08-29 23:47:34 +04:00
}
2008-02-21 00:02:51 +03:00