2017-03-17 09:18:50 +03:00
// SPDX-License-Identifier: GPL-2.0
# include "bcachefs.h"
# include "checksum.h"
# include "disk_groups.h"
# include "error.h"
# include "io.h"
# include "journal.h"
# include "replicas.h"
# include "quota.h"
# include "super-io.h"
# include "super.h"
# include "vstructs.h"
# include <linux/backing-dev.h>
# include <linux/sort.h>
static const struct blk_holder_ops bch2_sb_handle_bdev_ops = {
} ;
const char * const bch2_sb_fields [ ] = {
# define x(name, nr) #name,
BCH_SB_FIELDS ( )
# undef x
NULL
} ;
static const char * bch2_sb_field_validate ( struct bch_sb * ,
struct bch_sb_field * ) ;
struct bch_sb_field * bch2_sb_field_get ( struct bch_sb * sb ,
enum bch_sb_field_type type )
{
struct bch_sb_field * f ;
/* XXX: need locking around superblock to access optional fields */
vstruct_for_each ( sb , f )
if ( le32_to_cpu ( f - > type ) = = type )
return f ;
return NULL ;
}
static struct bch_sb_field * __bch2_sb_field_resize ( struct bch_sb_handle * sb ,
struct bch_sb_field * f ,
unsigned u64s )
{
unsigned old_u64s = f ? le32_to_cpu ( f - > u64s ) : 0 ;
unsigned sb_u64s = le32_to_cpu ( sb - > sb - > u64s ) + u64s - old_u64s ;
BUG_ON ( get_order ( __vstruct_bytes ( struct bch_sb , sb_u64s ) ) >
sb - > page_order ) ;
if ( ! f ) {
f = vstruct_last ( sb - > sb ) ;
memset ( f , 0 , sizeof ( u64 ) * u64s ) ;
f - > u64s = cpu_to_le32 ( u64s ) ;
f - > type = 0 ;
} else {
void * src , * dst ;
src = vstruct_end ( f ) ;
f - > u64s = cpu_to_le32 ( u64s ) ;
dst = vstruct_end ( f ) ;
memmove ( dst , src , vstruct_end ( sb - > sb ) - src ) ;
if ( dst > src )
memset ( src , 0 , dst - src ) ;
}
sb - > sb - > u64s = cpu_to_le32 ( sb_u64s ) ;
return f ;
}
/* Superblock realloc/free: */
void bch2_free_super ( struct bch_sb_handle * sb )
{
if ( sb - > bio )
kfree ( sb - > bio ) ;
if ( ! IS_ERR_OR_NULL ( sb - > bdev ) )
blkdev_put ( sb - > bdev , sb - > holder ) ;
kfree ( sb - > holder ) ;
free_pages ( ( unsigned long ) sb - > sb , sb - > page_order ) ;
memset ( sb , 0 , sizeof ( * sb ) ) ;
}
int bch2_sb_realloc ( struct bch_sb_handle * sb , unsigned u64s )
{
size_t new_bytes = __vstruct_bytes ( struct bch_sb , u64s ) ;
unsigned order = get_order ( new_bytes ) ;
struct bch_sb * new_sb ;
struct bio * bio ;
if ( sb - > sb & & sb - > page_order > = order )
return 0 ;
if ( sb - > have_layout ) {
u64 max_bytes = 512 < < sb - > sb - > layout . sb_max_size_bits ;
if ( new_bytes > max_bytes ) {
pr_err ( " %pg: superblock too big: want %zu but have %llu " ,
sb - > bdev , new_bytes , max_bytes ) ;
return - ENOSPC ;
}
}
if ( sb - > page_order > = order & & sb - > sb )
return 0 ;
if ( dynamic_fault ( " bcachefs:add:super_realloc " ) )
return - ENOMEM ;
if ( sb - > have_bio ) {
unsigned nr_bvecs = 1 < < order ;
bio = bio_kmalloc ( nr_bvecs , GFP_KERNEL ) ;
if ( ! bio )
return - ENOMEM ;
bio_init ( bio , NULL , bio - > bi_inline_vecs , nr_bvecs , 0 ) ;
if ( sb - > bio )
kfree ( sb - > bio ) ;
sb - > bio = bio ;
}
new_sb = ( void * ) __get_free_pages ( GFP_KERNEL | __GFP_ZERO , order ) ;
if ( ! new_sb )
return - ENOMEM ;
if ( sb - > sb )
memcpy ( new_sb , sb - > sb , PAGE_SIZE < < sb - > page_order ) ;
free_pages ( ( unsigned long ) sb - > sb , sb - > page_order ) ;
sb - > sb = new_sb ;
sb - > page_order = order ;
return 0 ;
}
struct bch_sb_field * bch2_sb_field_resize ( struct bch_sb_handle * sb ,
enum bch_sb_field_type type ,
unsigned u64s )
{
struct bch_sb_field * f = bch2_sb_field_get ( sb - > sb , type ) ;
ssize_t old_u64s = f ? le32_to_cpu ( f - > u64s ) : 0 ;
ssize_t d = - old_u64s + u64s ;
if ( bch2_sb_realloc ( sb , le32_to_cpu ( sb - > sb - > u64s ) + d ) )
return NULL ;
if ( sb - > fs_sb ) {
struct bch_fs * c = container_of ( sb , struct bch_fs , disk_sb ) ;
struct bch_dev * ca ;
unsigned i ;
lockdep_assert_held ( & c - > sb_lock ) ;
/* XXX: we're not checking that offline device have enough space */
for_each_online_member ( ca , c , i ) {
struct bch_sb_handle * sb = & ca - > disk_sb ;
if ( bch2_sb_realloc ( sb , le32_to_cpu ( sb - > sb - > u64s ) + d ) ) {
percpu_ref_put ( & ca - > ref ) ;
return NULL ;
}
}
}
f = __bch2_sb_field_resize ( sb , f , u64s ) ;
f - > type = cpu_to_le32 ( type ) ;
return f ;
}
/* Superblock validate: */
static inline void __bch2_sb_layout_size_assert ( void )
{
BUILD_BUG_ON ( sizeof ( struct bch_sb_layout ) ! = 512 ) ;
}
static const char * validate_sb_layout ( struct bch_sb_layout * layout )
{
u64 offset , prev_offset , max_sectors ;
unsigned i ;
if ( ! uuid_equal ( & layout - > magic , & BCACHE_MAGIC ) & &
! uuid_equal ( & layout - > magic , & BCHFS_MAGIC ) )
return " Not a bcachefs superblock layout " ;
if ( layout - > layout_type ! = 0 )
return " Invalid superblock layout type " ;
if ( ! layout - > nr_superblocks )
return " Invalid superblock layout: no superblocks " ;
if ( layout - > nr_superblocks > ARRAY_SIZE ( layout - > sb_offset ) )
return " Invalid superblock layout: too many superblocks " ;
max_sectors = 1 < < layout - > sb_max_size_bits ;
prev_offset = le64_to_cpu ( layout - > sb_offset [ 0 ] ) ;
for ( i = 1 ; i < layout - > nr_superblocks ; i + + ) {
offset = le64_to_cpu ( layout - > sb_offset [ i ] ) ;
if ( offset < prev_offset + max_sectors )
return " Invalid superblock layout: superblocks overlap " ;
prev_offset = offset ;
}
return NULL ;
}
const char * bch2_sb_validate ( struct bch_sb_handle * disk_sb )
{
struct bch_sb * sb = disk_sb - > sb ;
struct bch_sb_field * f ;
struct bch_sb_field_members * mi ;
const char * err ;
u16 block_size ;
if ( le16_to_cpu ( sb - > version ) < BCH_SB_VERSION_MIN | |
le16_to_cpu ( sb - > version ) > BCH_SB_VERSION_MAX )
return " Unsupported superblock version " ;
if ( le16_to_cpu ( sb - > version ) < BCH_SB_VERSION_EXTENT_MAX ) {
SET_BCH_SB_ENCODED_EXTENT_MAX_BITS ( sb , 7 ) ;
SET_BCH_SB_POSIX_ACL ( sb , 1 ) ;
}
block_size = le16_to_cpu ( sb - > block_size ) ;
if ( ! is_power_of_2 ( block_size ) | |
block_size > PAGE_SECTORS )
return " Bad block size " ;
if ( bch2_is_zero ( sb - > user_uuid . b , sizeof ( sb - > user_uuid ) ) )
return " Bad user UUID " ;
if ( bch2_is_zero ( sb - > uuid . b , sizeof ( sb - > uuid ) ) )
return " Bad internal UUID " ;
if ( ! sb - > nr_devices | |
sb - > nr_devices < = sb - > dev_idx | |
sb - > nr_devices > BCH_SB_MEMBERS_MAX )
return " Bad number of member devices " ;
if ( ! BCH_SB_META_REPLICAS_WANT ( sb ) | |
BCH_SB_META_REPLICAS_WANT ( sb ) > = BCH_REPLICAS_MAX )
return " Invalid number of metadata replicas " ;
if ( ! BCH_SB_META_REPLICAS_REQ ( sb ) | |
BCH_SB_META_REPLICAS_REQ ( sb ) > = BCH_REPLICAS_MAX )
return " Invalid number of metadata replicas " ;
if ( ! BCH_SB_DATA_REPLICAS_WANT ( sb ) | |
BCH_SB_DATA_REPLICAS_WANT ( sb ) > = BCH_REPLICAS_MAX )
return " Invalid number of data replicas " ;
if ( ! BCH_SB_DATA_REPLICAS_REQ ( sb ) | |
BCH_SB_DATA_REPLICAS_REQ ( sb ) > = BCH_REPLICAS_MAX )
return " Invalid number of data replicas " ;
if ( BCH_SB_META_CSUM_TYPE ( sb ) > = BCH_CSUM_OPT_NR )
return " Invalid metadata checksum type " ;
if ( BCH_SB_DATA_CSUM_TYPE ( sb ) > = BCH_CSUM_OPT_NR )
return " Invalid metadata checksum type " ;
if ( BCH_SB_COMPRESSION_TYPE ( sb ) > = BCH_COMPRESSION_OPT_NR )
return " Invalid compression type " ;
if ( ! BCH_SB_BTREE_NODE_SIZE ( sb ) )
return " Btree node size not set " ;
if ( ! is_power_of_2 ( BCH_SB_BTREE_NODE_SIZE ( sb ) ) )
return " Btree node size not a power of two " ;
if ( BCH_SB_GC_RESERVE ( sb ) < 5 )
return " gc reserve percentage too small " ;
if ( ! sb - > time_precision | |
le32_to_cpu ( sb - > time_precision ) > NSEC_PER_SEC )
return " invalid time precision " ;
/* validate layout */
err = validate_sb_layout ( & sb - > layout ) ;
if ( err )
return err ;
vstruct_for_each ( sb , f ) {
if ( ! f - > u64s )
return " Invalid superblock: invalid optional field " ;
if ( vstruct_next ( f ) > vstruct_last ( sb ) )
return " Invalid superblock: invalid optional field " ;
}
/* members must be validated first: */
mi = bch2_sb_get_members ( sb ) ;
if ( ! mi )
return " Invalid superblock: member info area missing " ;
err = bch2_sb_field_validate ( sb , & mi - > field ) ;
if ( err )
return err ;
vstruct_for_each ( sb , f ) {
if ( le32_to_cpu ( f - > type ) = = BCH_SB_FIELD_members )
continue ;
err = bch2_sb_field_validate ( sb , f ) ;
if ( err )
return err ;
}
if ( le16_to_cpu ( sb - > version ) < BCH_SB_VERSION_EXTENT_NONCE_V1 & &
bch2_sb_get_crypt ( sb ) & &
BCH_SB_INITIALIZED ( sb ) )
return " Incompatible extent nonces " ;
sb - > version = cpu_to_le16 ( BCH_SB_VERSION_MAX ) ;
return NULL ;
}
/* device open: */
static void bch2_sb_update ( struct bch_fs * c )
{
struct bch_sb * src = c - > disk_sb . sb ;
struct bch_sb_field_members * mi = bch2_sb_get_members ( src ) ;
struct bch_dev * ca ;
unsigned i ;
lockdep_assert_held ( & c - > sb_lock ) ;
c - > sb . uuid = src - > uuid ;
c - > sb . user_uuid = src - > user_uuid ;
c - > sb . nr_devices = src - > nr_devices ;
c - > sb . clean = BCH_SB_CLEAN ( src ) ;
c - > sb . encryption_type = BCH_SB_ENCRYPTION_TYPE ( src ) ;
c - > sb . encoded_extent_max = 1 < < BCH_SB_ENCODED_EXTENT_MAX_BITS ( src ) ;
c - > sb . time_base_lo = le64_to_cpu ( src - > time_base_lo ) ;
c - > sb . time_base_hi = le32_to_cpu ( src - > time_base_hi ) ;
c - > sb . time_precision = le32_to_cpu ( src - > time_precision ) ;
c - > sb . features = le64_to_cpu ( src - > features [ 0 ] ) ;
for_each_member_device ( ca , c , i )
ca - > mi = bch2_mi_to_cpu ( mi - > members + i ) ;
}
/* doesn't copy member info */
static void __copy_super ( struct bch_sb_handle * dst_handle , struct bch_sb * src )
{
struct bch_sb_field * src_f , * dst_f ;
struct bch_sb * dst = dst_handle - > sb ;
dst - > version = src - > version ;
dst - > seq = src - > seq ;
dst - > uuid = src - > uuid ;
dst - > user_uuid = src - > user_uuid ;
memcpy ( dst - > label , src - > label , sizeof ( dst - > label ) ) ;
dst - > block_size = src - > block_size ;
dst - > nr_devices = src - > nr_devices ;
dst - > time_base_lo = src - > time_base_lo ;
dst - > time_base_hi = src - > time_base_hi ;
dst - > time_precision = src - > time_precision ;
memcpy ( dst - > flags , src - > flags , sizeof ( dst - > flags ) ) ;
memcpy ( dst - > features , src - > features , sizeof ( dst - > features ) ) ;
memcpy ( dst - > compat , src - > compat , sizeof ( dst - > compat ) ) ;
vstruct_for_each ( src , src_f ) {
if ( src_f - > type = = BCH_SB_FIELD_journal )
continue ;
dst_f = bch2_sb_field_get ( dst , le32_to_cpu ( src_f - > type ) ) ;
dst_f = __bch2_sb_field_resize ( dst_handle , dst_f ,
le32_to_cpu ( src_f - > u64s ) ) ;
memcpy ( dst_f , src_f , vstruct_bytes ( src_f ) ) ;
}
}
int bch2_sb_to_fs ( struct bch_fs * c , struct bch_sb * src )
{
struct bch_sb_field_journal * journal_buckets =
bch2_sb_get_journal ( src ) ;
unsigned journal_u64s = journal_buckets
? le32_to_cpu ( journal_buckets - > field . u64s )
: 0 ;
int ret ;
lockdep_assert_held ( & c - > sb_lock ) ;
ret = bch2_sb_realloc ( & c - > disk_sb ,
le32_to_cpu ( src - > u64s ) - journal_u64s ) ;
if ( ret )
return ret ;
__copy_super ( & c - > disk_sb , src ) ;
ret = bch2_sb_replicas_to_cpu_replicas ( c ) ;
if ( ret )
return ret ;
ret = bch2_sb_disk_groups_to_cpu ( c ) ;
if ( ret )
return ret ;
bch2_sb_update ( c ) ;
return 0 ;
}
int bch2_sb_from_fs ( struct bch_fs * c , struct bch_dev * ca )
{
struct bch_sb * src = c - > disk_sb . sb , * dst = ca - > disk_sb . sb ;
struct bch_sb_field_journal * journal_buckets =
bch2_sb_get_journal ( dst ) ;
unsigned journal_u64s = journal_buckets
? le32_to_cpu ( journal_buckets - > field . u64s )
: 0 ;
unsigned u64s = le32_to_cpu ( src - > u64s ) + journal_u64s ;
int ret ;
ret = bch2_sb_realloc ( & ca - > disk_sb , u64s ) ;
if ( ret )
return ret ;
__copy_super ( & ca - > disk_sb , src ) ;
return 0 ;
}
/* read superblock: */
static const char * read_one_super ( struct bch_sb_handle * sb , u64 offset )
{
struct bch_csum csum ;
size_t bytes ;
reread :
bio_reset ( sb - > bio , sb - > bdev , REQ_OP_READ | REQ_SYNC | REQ_META ) ;
sb - > bio - > bi_iter . bi_sector = offset ;
sb - > bio - > bi_iter . bi_size = PAGE_SIZE < < sb - > page_order ;
bch2_bio_map ( sb - > bio , sb - > sb ) ;
if ( submit_bio_wait ( sb - > bio ) )
return " IO error " ;
if ( ! uuid_equal ( & sb - > sb - > magic , & BCACHE_MAGIC ) & &
! uuid_equal ( & sb - > sb - > magic , & BCHFS_MAGIC ) )
return " Not a bcachefs superblock " ;
if ( le16_to_cpu ( sb - > sb - > version ) < BCH_SB_VERSION_MIN | |
le16_to_cpu ( sb - > sb - > version ) > BCH_SB_VERSION_MAX )
return " Unsupported superblock version " ;
bytes = vstruct_bytes ( sb - > sb ) ;
if ( bytes > 512 < < sb - > sb - > layout . sb_max_size_bits )
return " Bad superblock: too big " ;
if ( get_order ( bytes ) > sb - > page_order ) {
if ( bch2_sb_realloc ( sb , le32_to_cpu ( sb - > sb - > u64s ) ) )
return " cannot allocate memory " ;
goto reread ;
}
if ( BCH_SB_CSUM_TYPE ( sb - > sb ) > = BCH_CSUM_NR )
return " unknown csum type " ;
/* XXX: verify MACs */
csum = csum_vstruct ( NULL , BCH_SB_CSUM_TYPE ( sb - > sb ) ,
null_nonce ( ) , sb - > sb ) ;
if ( bch2_crc_cmp ( csum , sb - > sb - > csum ) )
return " bad checksum reading superblock " ;
return NULL ;
}
int bch2_read_super ( const char * path , struct bch_opts * opts ,
struct bch_sb_handle * sb )
{
u64 offset = opt_get ( * opts , sb ) ;
struct bch_sb_layout layout ;
const char * err ;
__le64 * i ;
int ret ;
pr_verbose_init ( * opts , " " ) ;
memset ( sb , 0 , sizeof ( * sb ) ) ;
sb - > mode = BLK_OPEN_READ ;
sb - > have_bio = true ;
sb - > holder = kmalloc ( 1 , GFP_KERNEL ) ;
if ( ! sb - > holder )
return - ENOMEM ;
if ( ! opt_get ( * opts , noexcl ) )
sb - > mode | = BLK_OPEN_EXCL ;
if ( ! opt_get ( * opts , nochanges ) )
sb - > mode | = BLK_OPEN_WRITE ;
sb - > bdev = blkdev_get_by_path ( path , sb - > mode , sb - > holder , & bch2_sb_handle_bdev_ops ) ;
if ( IS_ERR ( sb - > bdev ) & &
PTR_ERR ( sb - > bdev ) = = - EACCES & &
opt_get ( * opts , read_only ) ) {
sb - > mode & = ~ BLK_OPEN_WRITE ;
sb - > bdev = blkdev_get_by_path ( path , sb - > mode , sb - > holder , & bch2_sb_handle_bdev_ops ) ;
if ( ! IS_ERR ( sb - > bdev ) )
opt_set ( * opts , nochanges , true ) ;
}
if ( IS_ERR ( sb - > bdev ) ) {
ret = PTR_ERR ( sb - > bdev ) ;
goto out ;
}
err = " cannot allocate memory " ;
ret = bch2_sb_realloc ( sb , 0 ) ;
if ( ret )
goto err ;
ret = - EFAULT ;
err = " dynamic fault " ;
if ( bch2_fs_init_fault ( " read_super " ) )
goto err ;
ret = - EINVAL ;
err = read_one_super ( sb , offset ) ;
if ( ! err )
goto got_super ;
if ( opt_defined ( * opts , sb ) )
goto err ;
pr_err ( " error reading default superblock: %s " , err ) ;
/*
* Error reading primary superblock - read location of backup
* superblocks :
*/
bio_reset ( sb - > bio , sb - > bdev , REQ_OP_READ | REQ_SYNC | REQ_META ) ;
sb - > bio - > bi_iter . bi_sector = BCH_SB_LAYOUT_SECTOR ;
sb - > bio - > bi_iter . bi_size = sizeof ( struct bch_sb_layout ) ;
/*
* use sb buffer to read layout , since sb buffer is page aligned but
* layout won ' t be :
*/
bch2_bio_map ( sb - > bio , sb - > sb ) ;
err = " IO error " ;
if ( submit_bio_wait ( sb - > bio ) )
goto err ;
memcpy ( & layout , sb - > sb , sizeof ( layout ) ) ;
err = validate_sb_layout ( & layout ) ;
if ( err )
goto err ;
for ( i = layout . sb_offset ;
i < layout . sb_offset + layout . nr_superblocks ; i + + ) {
offset = le64_to_cpu ( * i ) ;
if ( offset = = opt_get ( * opts , sb ) )
continue ;
err = read_one_super ( sb , offset ) ;
if ( ! err )
goto got_super ;
}
ret = - EINVAL ;
goto err ;
got_super :
err = " Superblock block size smaller than device block size " ;
ret = - EINVAL ;
if ( le16_to_cpu ( sb - > sb - > block_size ) < < 9 <
bdev_logical_block_size ( sb - > bdev ) )
goto err ;
ret = 0 ;
sb - > have_layout = true ;
out :
pr_verbose_init ( * opts , " ret %i " , ret ) ;
return ret ;
err :
bch2_free_super ( sb ) ;
pr_err ( " error reading superblock: %s " , err ) ;
goto out ;
}
/* write superblock: */
static void write_super_endio ( struct bio * bio )
{
struct bch_dev * ca = bio - > bi_private ;
/* XXX: return errors directly */
if ( bch2_dev_io_err_on ( bio - > bi_status , ca , " superblock write " ) )
ca - > sb_write_error = 1 ;
closure_put ( & ca - > fs - > sb_write ) ;
percpu_ref_put ( & ca - > io_ref ) ;
}
static void write_one_super ( struct bch_fs * c , struct bch_dev * ca , unsigned idx )
{
struct bch_sb * sb = ca - > disk_sb . sb ;
struct bio * bio = ca - > disk_sb . bio ;
sb - > offset = sb - > layout . sb_offset [ idx ] ;
SET_BCH_SB_CSUM_TYPE ( sb , c - > opts . metadata_checksum ) ;
sb - > csum = csum_vstruct ( c , BCH_SB_CSUM_TYPE ( sb ) ,
null_nonce ( ) , sb ) ;
bio_reset ( bio , ca - > disk_sb . bdev , REQ_OP_WRITE | REQ_SYNC | REQ_META ) ;
bio - > bi_iter . bi_sector = le64_to_cpu ( sb - > offset ) ;
bio - > bi_iter . bi_size =
roundup ( ( size_t ) vstruct_bytes ( sb ) ,
bdev_logical_block_size ( ca - > disk_sb . bdev ) ) ;
bio - > bi_end_io = write_super_endio ;
bio - > bi_private = ca ;
bch2_bio_map ( bio , sb ) ;
this_cpu_add ( ca - > io_done - > sectors [ WRITE ] [ BCH_DATA_SB ] ,
bio_sectors ( bio ) ) ;
percpu_ref_get ( & ca - > io_ref ) ;
closure_bio_submit ( bio , & c - > sb_write ) ;
}
void bch2_write_super ( struct bch_fs * c )
{
struct closure * cl = & c - > sb_write ;
struct bch_dev * ca ;
unsigned i , sb = 0 , nr_wrote ;
const char * err ;
struct bch_devs_mask sb_written ;
bool wrote , can_mount_without_written , can_mount_with_written ;
lockdep_assert_held ( & c - > sb_lock ) ;
closure_init_stack ( cl ) ;
memset ( & sb_written , 0 , sizeof ( sb_written ) ) ;
le64_add_cpu ( & c - > disk_sb . sb - > seq , 1 ) ;
for_each_online_member ( ca , c , i )
bch2_sb_from_fs ( c , ca ) ;
for_each_online_member ( ca , c , i ) {
err = bch2_sb_validate ( & ca - > disk_sb ) ;
if ( err ) {
bch2_fs_inconsistent ( c , " sb invalid before write: %s " , err ) ;
goto out ;
}
}
if ( c - > opts . nochanges | |
test_bit ( BCH_FS_ERROR , & c - > flags ) )
goto out ;
for_each_online_member ( ca , c , i ) {
__set_bit ( ca - > dev_idx , sb_written . d ) ;
ca - > sb_write_error = 0 ;
}
do {
wrote = false ;
for_each_online_member ( ca , c , i )
if ( sb < ca - > disk_sb . sb - > layout . nr_superblocks ) {
write_one_super ( c , ca , sb ) ;
wrote = true ;
}
closure_sync ( cl ) ;
sb + + ;
} while ( wrote ) ;
for_each_online_member ( ca , c , i )
if ( ca - > sb_write_error )
__clear_bit ( ca - > dev_idx , sb_written . d ) ;
nr_wrote = dev_mask_nr ( & sb_written ) ;
can_mount_with_written =
bch2_have_enough_devs ( __bch2_replicas_status ( c , sb_written ) ,
BCH_FORCE_IF_DEGRADED ) ;
for ( i = 0 ; i < ARRAY_SIZE ( sb_written . d ) ; i + + )
sb_written . d [ i ] = ~ sb_written . d [ i ] ;
can_mount_without_written =
bch2_have_enough_devs ( __bch2_replicas_status ( c , sb_written ) ,
BCH_FORCE_IF_DEGRADED ) ;
/*
* If we would be able to mount _without_ the devices we successfully
* wrote superblocks to , we weren ' t able to write to enough devices :
*
* Exception : if we can mount without the successes because we haven ' t
* written anything ( new filesystem ) , we continue if we ' d be able to
* mount with the devices we did successfully write to :
*/
bch2_fs_fatal_err_on ( ! nr_wrote | |
( can_mount_without_written & &
! can_mount_with_written ) , c ,
" Unable to write superblock to sufficient devices " ) ;
out :
/* Make new options visible after they're persistent: */
bch2_sb_update ( c ) ;
}
/* BCH_SB_FIELD_journal: */
static int u64_cmp ( const void * _l , const void * _r )
{
u64 l = * ( ( const u64 * ) _l ) , r = * ( ( const u64 * ) _r ) ;
return l < r ? - 1 : l > r ? 1 : 0 ;
}
static const char * bch2_sb_validate_journal ( struct bch_sb * sb ,
struct bch_sb_field * f )
{
struct bch_sb_field_journal * journal = field_to_type ( f , journal ) ;
struct bch_member * m = bch2_sb_get_members ( sb ) - > members + sb - > dev_idx ;
const char * err ;
unsigned nr ;
unsigned i ;
u64 * b ;
journal = bch2_sb_get_journal ( sb ) ;
if ( ! journal )
return NULL ;
nr = bch2_nr_journal_buckets ( journal ) ;
if ( ! nr )
return NULL ;
b = kmalloc_array ( sizeof ( u64 ) , nr , GFP_KERNEL ) ;
if ( ! b )
return " cannot allocate memory " ;
for ( i = 0 ; i < nr ; i + + )
b [ i ] = le64_to_cpu ( journal - > buckets [ i ] ) ;
sort ( b , nr , sizeof ( u64 ) , u64_cmp , NULL ) ;
err = " journal bucket at sector 0 " ;
if ( ! b [ 0 ] )
goto err ;
err = " journal bucket before first bucket " ;
if ( m & & b [ 0 ] < le16_to_cpu ( m - > first_bucket ) )
goto err ;
err = " journal bucket past end of device " ;
if ( m & & b [ nr - 1 ] > = le64_to_cpu ( m - > nbuckets ) )
goto err ;
err = " duplicate journal buckets " ;
for ( i = 0 ; i + 1 < nr ; i + + )
if ( b [ i ] = = b [ i + 1 ] )
goto err ;
err = NULL ;
err :
kfree ( b ) ;
return err ;
}
static const struct bch_sb_field_ops bch_sb_field_ops_journal = {
. validate = bch2_sb_validate_journal ,
} ;
/* BCH_SB_FIELD_members: */
static const char * bch2_sb_validate_members ( struct bch_sb * sb ,
struct bch_sb_field * f )
{
struct bch_sb_field_members * mi = field_to_type ( f , members ) ;
struct bch_member * m ;
if ( ( void * ) ( mi - > members + sb - > nr_devices ) >
vstruct_end ( & mi - > field ) )
return " Invalid superblock: bad member info " ;
for ( m = mi - > members ;
m < mi - > members + sb - > nr_devices ;
m + + ) {
if ( ! bch2_member_exists ( m ) )
continue ;
if ( le64_to_cpu ( m - > nbuckets ) > LONG_MAX )
return " Too many buckets " ;
if ( le64_to_cpu ( m - > nbuckets ) -
2018-11-05 06:09:51 +03:00
le16_to_cpu ( m - > first_bucket ) < BCH_MIN_NR_NBUCKETS )
2017-03-17 09:18:50 +03:00
return " Not enough buckets " ;
if ( le16_to_cpu ( m - > bucket_size ) <
le16_to_cpu ( sb - > block_size ) )
return " bucket size smaller than block size " ;
if ( le16_to_cpu ( m - > bucket_size ) <
BCH_SB_BTREE_NODE_SIZE ( sb ) )
return " bucket size smaller than btree node size " ;
}
if ( le16_to_cpu ( sb - > version ) < BCH_SB_VERSION_EXTENT_MAX )
for ( m = mi - > members ;
m < mi - > members + sb - > nr_devices ;
m + + )
SET_BCH_MEMBER_DATA_ALLOWED ( m , ~ 0 ) ;
return NULL ;
}
static const struct bch_sb_field_ops bch_sb_field_ops_members = {
. validate = bch2_sb_validate_members ,
} ;
/* BCH_SB_FIELD_crypt: */
static const char * bch2_sb_validate_crypt ( struct bch_sb * sb ,
struct bch_sb_field * f )
{
struct bch_sb_field_crypt * crypt = field_to_type ( f , crypt ) ;
if ( vstruct_bytes ( & crypt - > field ) ! = sizeof ( * crypt ) )
return " invalid field crypt: wrong size " ;
if ( BCH_CRYPT_KDF_TYPE ( crypt ) )
return " invalid field crypt: bad kdf type " ;
return NULL ;
}
static const struct bch_sb_field_ops bch_sb_field_ops_crypt = {
. validate = bch2_sb_validate_crypt ,
} ;
/* BCH_SB_FIELD_clean: */
void bch2_fs_mark_clean ( struct bch_fs * c , bool clean )
{
struct bch_sb_field_clean * sb_clean ;
unsigned u64s = sizeof ( * sb_clean ) / sizeof ( u64 ) ;
struct jset_entry * entry ;
struct btree_root * r ;
mutex_lock ( & c - > sb_lock ) ;
if ( clean = = BCH_SB_CLEAN ( c - > disk_sb . sb ) )
goto out ;
SET_BCH_SB_CLEAN ( c - > disk_sb . sb , clean ) ;
if ( ! clean )
goto write_super ;
mutex_lock ( & c - > btree_root_lock ) ;
for ( r = c - > btree_roots ;
r < c - > btree_roots + BTREE_ID_NR ;
r + + )
if ( r - > alive )
u64s + = jset_u64s ( r - > key . u64s ) ;
sb_clean = bch2_sb_resize_clean ( & c - > disk_sb , u64s ) ;
if ( ! sb_clean ) {
bch_err ( c , " error resizing superblock while setting filesystem clean " ) ;
goto out ;
}
sb_clean - > flags = 0 ;
sb_clean - > read_clock = cpu_to_le16 ( c - > bucket_clock [ READ ] . hand ) ;
sb_clean - > write_clock = cpu_to_le16 ( c - > bucket_clock [ WRITE ] . hand ) ;
sb_clean - > journal_seq = journal_cur_seq ( & c - > journal ) - 1 ;
entry = sb_clean - > start ;
memset ( entry , 0 ,
vstruct_end ( & sb_clean - > field ) - ( void * ) entry ) ;
for ( r = c - > btree_roots ;
r < c - > btree_roots + BTREE_ID_NR ;
r + + )
if ( r - > alive ) {
entry - > u64s = r - > key . u64s ;
entry - > btree_id = r - c - > btree_roots ;
entry - > level = r - > level ;
entry - > type = BCH_JSET_ENTRY_btree_root ;
bkey_copy ( & entry - > start [ 0 ] , & r - > key ) ;
entry = vstruct_next ( entry ) ;
BUG_ON ( ( void * ) entry > vstruct_end ( & sb_clean - > field ) ) ;
}
BUG_ON ( entry ! = vstruct_end ( & sb_clean - > field ) ) ;
mutex_unlock ( & c - > btree_root_lock ) ;
write_super :
bch2_write_super ( c ) ;
out :
mutex_unlock ( & c - > sb_lock ) ;
}
static const char * bch2_sb_validate_clean ( struct bch_sb * sb ,
struct bch_sb_field * f )
{
struct bch_sb_field_clean * clean = field_to_type ( f , clean ) ;
if ( vstruct_bytes ( & clean - > field ) < sizeof ( * clean ) )
return " invalid field crypt: wrong size " ;
return NULL ;
}
static const struct bch_sb_field_ops bch_sb_field_ops_clean = {
. validate = bch2_sb_validate_clean ,
} ;
static const struct bch_sb_field_ops * bch2_sb_field_ops [ ] = {
# define x(f, nr) \
[ BCH_SB_FIELD_ # # f ] = & bch_sb_field_ops_ # # f ,
BCH_SB_FIELDS ( )
# undef x
} ;
static const char * bch2_sb_field_validate ( struct bch_sb * sb ,
struct bch_sb_field * f )
{
unsigned type = le32_to_cpu ( f - > type ) ;
return type < BCH_SB_FIELD_NR
? bch2_sb_field_ops [ type ] - > validate ( sb , f )
: NULL ;
}
size_t bch2_sb_field_to_text ( char * buf , size_t size ,
struct bch_sb * sb , struct bch_sb_field * f )
{
unsigned type = le32_to_cpu ( f - > type ) ;
size_t ( * to_text ) ( char * , size_t , struct bch_sb * ,
struct bch_sb_field * ) =
type < BCH_SB_FIELD_NR
? bch2_sb_field_ops [ type ] - > to_text
: NULL ;
if ( ! to_text ) {
if ( size )
buf [ 0 ] = ' \0 ' ;
return 0 ;
}
return to_text ( buf , size , sb , f ) ;
}