2006-10-11 12:20:50 +04:00
/*
2006-10-11 12:20:53 +04:00
* linux / fs / ext4 / resize . c
2006-10-11 12:20:50 +04:00
*
2006-10-11 12:20:53 +04:00
* Support for resizing an ext4 filesystem while it is mounted .
2006-10-11 12:20:50 +04:00
*
* Copyright ( C ) 2001 , 2002 Andreas Dilger < adilger @ clusterfs . com >
*
* This could probably be made into a module , because it is not often in use .
*/
2006-10-11 12:20:53 +04:00
# define EXT4FS_DEBUG
2006-10-11 12:20:50 +04:00
# include <linux/errno.h>
# include <linux/slab.h>
2008-04-30 02:13:32 +04:00
# include "ext4_jbd2.h"
2006-10-11 12:20:50 +04:00
2011-07-27 05:35:44 +04:00
int ext4_resize_begin ( struct super_block * sb )
{
int ret = 0 ;
if ( ! capable ( CAP_SYS_RESOURCE ) )
return - EPERM ;
2014-12-27 07:58:21 +03:00
/*
* If we are not using the primary superblock / GDT copy don ' t resize ,
* because the user tools have no way of handling this . Probably a
* bad time to do it anyways .
*/
if ( EXT4_SB ( sb ) - > s_sbh - > b_blocknr ! =
le32_to_cpu ( EXT4_SB ( sb ) - > s_es - > s_first_data_block ) ) {
ext4_warning ( sb , " won't resize using backup superblock at %llu " ,
( unsigned long long ) EXT4_SB ( sb ) - > s_sbh - > b_blocknr ) ;
return - EPERM ;
}
2011-07-27 05:39:09 +04:00
/*
* We are not allowed to do online - resizing on a filesystem mounted
* with error , because it can destroy the filesystem easily .
*/
if ( EXT4_SB ( sb ) - > s_mount_state & EXT4_ERROR_FS ) {
ext4_warning ( sb , " There are errors in the filesystem, "
2016-04-27 08:11:21 +03:00
" so online resizing is not allowed " ) ;
2011-07-27 05:39:09 +04:00
return - EPERM ;
}
2011-07-27 05:35:44 +04:00
if ( test_and_set_bit_lock ( EXT4_RESIZING , & EXT4_SB ( sb ) - > s_resize_flags ) )
ret = - EBUSY ;
return ret ;
}
void ext4_resize_end ( struct super_block * sb )
{
clear_bit_unlock ( EXT4_RESIZING , & EXT4_SB ( sb ) - > s_resize_flags ) ;
2014-03-17 21:06:10 +04:00
smp_mb__after_atomic ( ) ;
2011-07-27 05:35:44 +04:00
}
2012-09-05 09:33:50 +04:00
static ext4_group_t ext4_meta_bg_first_group ( struct super_block * sb ,
ext4_group_t group ) {
return ( group > > EXT4_DESC_PER_BLOCK_BITS ( sb ) ) < <
EXT4_DESC_PER_BLOCK_BITS ( sb ) ;
}
static ext4_fsblk_t ext4_meta_bg_first_block_no ( struct super_block * sb ,
ext4_group_t group ) {
group = ext4_meta_bg_first_group ( sb , group ) ;
return ext4_group_first_block_no ( sb , group ) ;
}
static ext4_grpblk_t ext4_group_overhead_blocks ( struct super_block * sb ,
ext4_group_t group ) {
ext4_grpblk_t overhead ;
overhead = ext4_bg_num_gdb ( sb , group ) ;
if ( ext4_bg_has_super ( sb , group ) )
overhead + = 1 +
le16_to_cpu ( EXT4_SB ( sb ) - > s_es - > s_reserved_gdt_blocks ) ;
return overhead ;
}
2006-10-11 12:20:50 +04:00
# define outside(b, first, last) ((b) < (first) || (b) >= (last))
# define inside(b, first, last) ((b) >= (first) && (b) < (last))
static int verify_group_input ( struct super_block * sb ,
2006-10-11 12:20:53 +04:00
struct ext4_new_group_data * input )
2006-10-11 12:20:50 +04:00
{
2006-10-11 12:20:53 +04:00
struct ext4_sb_info * sbi = EXT4_SB ( sb ) ;
struct ext4_super_block * es = sbi - > s_es ;
2006-10-11 12:21:10 +04:00
ext4_fsblk_t start = ext4_blocks_count ( es ) ;
2006-10-11 12:20:53 +04:00
ext4_fsblk_t end = start + input - > blocks_count ;
2008-01-29 07:58:27 +03:00
ext4_group_t group = input - > group ;
2006-10-11 12:20:53 +04:00
ext4_fsblk_t itend = input - > inode_table + sbi - > s_itb_per_group ;
2013-06-06 19:14:31 +04:00
unsigned overhead ;
ext4_fsblk_t metaend ;
2006-10-11 12:20:50 +04:00
struct buffer_head * bh = NULL ;
2006-10-11 12:21:05 +04:00
ext4_grpblk_t free_blocks_count , offset ;
2006-10-11 12:20:50 +04:00
int err = - EINVAL ;
2013-06-06 19:14:31 +04:00
if ( group ! = sbi - > s_groups_count ) {
ext4_warning ( sb , " Cannot add at group %u (only %u groups) " ,
input - > group , sbi - > s_groups_count ) ;
return - EINVAL ;
}
overhead = ext4_group_overhead_blocks ( sb , group ) ;
metaend = start + overhead ;
2006-10-11 12:20:50 +04:00
input - > free_blocks_count = free_blocks_count =
input - > blocks_count - 2 - overhead - sbi - > s_itb_per_group ;
if ( test_opt ( sb , DEBUG ) )
2006-10-11 12:20:53 +04:00
printk ( KERN_DEBUG " EXT4-fs: adding %s group %u: %u blocks "
2006-10-11 12:20:50 +04:00
" (%d free, %u reserved) \n " ,
2006-10-11 12:20:53 +04:00
ext4_bg_has_super ( sb , input - > group ) ? " normal " :
2006-10-11 12:20:50 +04:00
" no-super " , input - > group , input - > blocks_count ,
free_blocks_count , input - > reserved_blocks ) ;
2006-10-11 12:21:05 +04:00
ext4_get_group_no_and_offset ( sb , start , NULL , & offset ) ;
2013-06-06 19:14:31 +04:00
if ( offset ! = 0 )
2010-02-15 22:19:27 +03:00
ext4_warning ( sb , " Last group not full " ) ;
2006-10-11 12:20:50 +04:00
else if ( input - > reserved_blocks > input - > blocks_count / 5 )
2010-02-15 22:19:27 +03:00
ext4_warning ( sb , " Reserved blocks too high (%u) " ,
2006-10-11 12:20:50 +04:00
input - > reserved_blocks ) ;
else if ( free_blocks_count < 0 )
2010-02-15 22:19:27 +03:00
ext4_warning ( sb , " Bad blocks count %u " ,
2006-10-11 12:20:50 +04:00
input - > blocks_count ) ;
else if ( ! ( bh = sb_bread ( sb , end - 1 ) ) )
2010-02-15 22:19:27 +03:00
ext4_warning ( sb , " Cannot read last block (%llu) " ,
2006-10-11 12:20:50 +04:00
end - 1 ) ;
else if ( outside ( input - > block_bitmap , start , end ) )
2010-02-15 22:19:27 +03:00
ext4_warning ( sb , " Block bitmap not in group (block %llu) " ,
[PATCH] ext4: fix printk format warnings
fs/ext4/resize.c:72: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:76: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:81: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:85: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:89: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:89: warning: long long unsigned int format, __u64 arg (arg 5)
fs/ext4/resize.c:93: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:93: warning: long long unsigned int format, __u64 arg (arg 5)
fs/ext4/resize.c:98: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:103: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:109: warning: long long unsigned int format, __u64 arg (arg 4)
Signed-off-by: Randy Dunlap <randy.dunlap@oracle.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-10-28 21:38:26 +04:00
( unsigned long long ) input - > block_bitmap ) ;
2006-10-11 12:20:50 +04:00
else if ( outside ( input - > inode_bitmap , start , end ) )
2010-02-15 22:19:27 +03:00
ext4_warning ( sb , " Inode bitmap not in group (block %llu) " ,
[PATCH] ext4: fix printk format warnings
fs/ext4/resize.c:72: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:76: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:81: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:85: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:89: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:89: warning: long long unsigned int format, __u64 arg (arg 5)
fs/ext4/resize.c:93: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:93: warning: long long unsigned int format, __u64 arg (arg 5)
fs/ext4/resize.c:98: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:103: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:109: warning: long long unsigned int format, __u64 arg (arg 4)
Signed-off-by: Randy Dunlap <randy.dunlap@oracle.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-10-28 21:38:26 +04:00
( unsigned long long ) input - > inode_bitmap ) ;
2006-10-11 12:20:50 +04:00
else if ( outside ( input - > inode_table , start , end ) | |
2008-07-27 00:15:44 +04:00
outside ( itend - 1 , start , end ) )
2010-02-15 22:19:27 +03:00
ext4_warning ( sb , " Inode table not in group (blocks %llu-%llu) " ,
[PATCH] ext4: fix printk format warnings
fs/ext4/resize.c:72: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:76: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:81: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:85: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:89: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:89: warning: long long unsigned int format, __u64 arg (arg 5)
fs/ext4/resize.c:93: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:93: warning: long long unsigned int format, __u64 arg (arg 5)
fs/ext4/resize.c:98: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:103: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:109: warning: long long unsigned int format, __u64 arg (arg 4)
Signed-off-by: Randy Dunlap <randy.dunlap@oracle.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-10-28 21:38:26 +04:00
( unsigned long long ) input - > inode_table , itend - 1 ) ;
2006-10-11 12:20:50 +04:00
else if ( input - > inode_bitmap = = input - > block_bitmap )
2010-02-15 22:19:27 +03:00
ext4_warning ( sb , " Block bitmap same as inode bitmap (%llu) " ,
[PATCH] ext4: fix printk format warnings
fs/ext4/resize.c:72: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:76: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:81: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:85: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:89: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:89: warning: long long unsigned int format, __u64 arg (arg 5)
fs/ext4/resize.c:93: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:93: warning: long long unsigned int format, __u64 arg (arg 5)
fs/ext4/resize.c:98: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:103: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:109: warning: long long unsigned int format, __u64 arg (arg 4)
Signed-off-by: Randy Dunlap <randy.dunlap@oracle.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-10-28 21:38:26 +04:00
( unsigned long long ) input - > block_bitmap ) ;
2006-10-11 12:20:50 +04:00
else if ( inside ( input - > block_bitmap , input - > inode_table , itend ) )
2010-02-15 22:19:27 +03:00
ext4_warning ( sb , " Block bitmap (%llu) in inode table "
" (%llu-%llu) " ,
[PATCH] ext4: fix printk format warnings
fs/ext4/resize.c:72: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:76: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:81: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:85: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:89: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:89: warning: long long unsigned int format, __u64 arg (arg 5)
fs/ext4/resize.c:93: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:93: warning: long long unsigned int format, __u64 arg (arg 5)
fs/ext4/resize.c:98: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:103: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:109: warning: long long unsigned int format, __u64 arg (arg 4)
Signed-off-by: Randy Dunlap <randy.dunlap@oracle.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-10-28 21:38:26 +04:00
( unsigned long long ) input - > block_bitmap ,
( unsigned long long ) input - > inode_table , itend - 1 ) ;
2006-10-11 12:20:50 +04:00
else if ( inside ( input - > inode_bitmap , input - > inode_table , itend ) )
2010-02-15 22:19:27 +03:00
ext4_warning ( sb , " Inode bitmap (%llu) in inode table "
" (%llu-%llu) " ,
[PATCH] ext4: fix printk format warnings
fs/ext4/resize.c:72: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:76: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:81: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:85: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:89: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:89: warning: long long unsigned int format, __u64 arg (arg 5)
fs/ext4/resize.c:93: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:93: warning: long long unsigned int format, __u64 arg (arg 5)
fs/ext4/resize.c:98: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:103: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:109: warning: long long unsigned int format, __u64 arg (arg 4)
Signed-off-by: Randy Dunlap <randy.dunlap@oracle.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-10-28 21:38:26 +04:00
( unsigned long long ) input - > inode_bitmap ,
( unsigned long long ) input - > inode_table , itend - 1 ) ;
2006-10-11 12:20:50 +04:00
else if ( inside ( input - > block_bitmap , start , metaend ) )
2010-02-15 22:19:27 +03:00
ext4_warning ( sb , " Block bitmap (%llu) in GDT table (%llu-%llu) " ,
[PATCH] ext4: fix printk format warnings
fs/ext4/resize.c:72: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:76: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:81: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:85: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:89: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:89: warning: long long unsigned int format, __u64 arg (arg 5)
fs/ext4/resize.c:93: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:93: warning: long long unsigned int format, __u64 arg (arg 5)
fs/ext4/resize.c:98: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:103: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:109: warning: long long unsigned int format, __u64 arg (arg 4)
Signed-off-by: Randy Dunlap <randy.dunlap@oracle.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-10-28 21:38:26 +04:00
( unsigned long long ) input - > block_bitmap ,
start , metaend - 1 ) ;
2006-10-11 12:20:50 +04:00
else if ( inside ( input - > inode_bitmap , start , metaend ) )
2010-02-15 22:19:27 +03:00
ext4_warning ( sb , " Inode bitmap (%llu) in GDT table (%llu-%llu) " ,
[PATCH] ext4: fix printk format warnings
fs/ext4/resize.c:72: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:76: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:81: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:85: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:89: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:89: warning: long long unsigned int format, __u64 arg (arg 5)
fs/ext4/resize.c:93: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:93: warning: long long unsigned int format, __u64 arg (arg 5)
fs/ext4/resize.c:98: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:103: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:109: warning: long long unsigned int format, __u64 arg (arg 4)
Signed-off-by: Randy Dunlap <randy.dunlap@oracle.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-10-28 21:38:26 +04:00
( unsigned long long ) input - > inode_bitmap ,
start , metaend - 1 ) ;
2006-10-11 12:20:50 +04:00
else if ( inside ( input - > inode_table , start , metaend ) | |
2008-07-27 00:15:44 +04:00
inside ( itend - 1 , start , metaend ) )
2010-02-15 22:19:27 +03:00
ext4_warning ( sb , " Inode table (%llu-%llu) overlaps GDT table "
" (%llu-%llu) " ,
[PATCH] ext4: fix printk format warnings
fs/ext4/resize.c:72: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:76: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:81: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:85: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:89: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:89: warning: long long unsigned int format, __u64 arg (arg 5)
fs/ext4/resize.c:93: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:93: warning: long long unsigned int format, __u64 arg (arg 5)
fs/ext4/resize.c:98: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:103: warning: long long unsigned int format, __u64 arg (arg 4)
fs/ext4/resize.c:109: warning: long long unsigned int format, __u64 arg (arg 4)
Signed-off-by: Randy Dunlap <randy.dunlap@oracle.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-10-28 21:38:26 +04:00
( unsigned long long ) input - > inode_table ,
itend - 1 , start , metaend - 1 ) ;
2006-10-11 12:20:50 +04:00
else
err = 0 ;
brelse ( bh ) ;
return err ;
}
2012-01-04 08:22:50 +04:00
/*
* ext4_new_flex_group_data is used by 64 bit - resize interface to add a flex
* group each time .
*/
struct ext4_new_flex_group_data {
struct ext4_new_group_data * groups ; /* new_group_data for groups
in the flex group */
__u16 * bg_flags ; /* block group flags of groups
in @ groups */
ext4_group_t count ; /* number of groups in @groups
*/
} ;
/*
* alloc_flex_gd ( ) allocates a ext4_new_flex_group_data with size of
* @ flexbg_size .
*
* Returns NULL on failure otherwise address of the allocated structure .
*/
static struct ext4_new_flex_group_data * alloc_flex_gd ( unsigned long flexbg_size )
{
struct ext4_new_flex_group_data * flex_gd ;
flex_gd = kmalloc ( sizeof ( * flex_gd ) , GFP_NOFS ) ;
if ( flex_gd = = NULL )
goto out3 ;
2016-02-12 09:15:59 +03:00
if ( flexbg_size > = UINT_MAX / sizeof ( struct ext4_new_group_data ) )
2012-05-28 22:21:55 +04:00
goto out2 ;
2012-01-04 08:22:50 +04:00
flex_gd - > count = flexbg_size ;
flex_gd - > groups = kmalloc ( sizeof ( struct ext4_new_group_data ) *
flexbg_size , GFP_NOFS ) ;
if ( flex_gd - > groups = = NULL )
goto out2 ;
flex_gd - > bg_flags = kmalloc ( flexbg_size * sizeof ( __u16 ) , GFP_NOFS ) ;
if ( flex_gd - > bg_flags = = NULL )
goto out1 ;
return flex_gd ;
out1 :
kfree ( flex_gd - > groups ) ;
out2 :
kfree ( flex_gd ) ;
out3 :
return NULL ;
}
static void free_flex_gd ( struct ext4_new_flex_group_data * flex_gd )
{
kfree ( flex_gd - > bg_flags ) ;
kfree ( flex_gd - > groups ) ;
kfree ( flex_gd ) ;
}
2012-01-04 08:44:38 +04:00
/*
* ext4_alloc_group_tables ( ) allocates block bitmaps , inode bitmaps
* and inode tables for a flex group .
*
* This function is used by 64 bit - resize . Note that this function allocates
* group tables from the 1 st group of groups contained by @ flexgd , which may
* be a partial of a flex group .
*
* @ sb : super block of fs to which the groups belongs
2012-09-05 09:21:50 +04:00
*
* Returns 0 on a successful allocation of the metadata blocks in the
* block group .
2012-01-04 08:44:38 +04:00
*/
2012-09-05 09:21:50 +04:00
static int ext4_alloc_group_tables ( struct super_block * sb ,
2012-01-04 08:44:38 +04:00
struct ext4_new_flex_group_data * flex_gd ,
int flexbg_size )
{
struct ext4_new_group_data * group_data = flex_gd - > groups ;
ext4_fsblk_t start_blk ;
ext4_fsblk_t last_blk ;
ext4_group_t src_group ;
ext4_group_t bb_index = 0 ;
ext4_group_t ib_index = 0 ;
ext4_group_t it_index = 0 ;
ext4_group_t group ;
ext4_group_t last_group ;
unsigned overhead ;
2014-02-16 06:33:13 +04:00
__u16 uninit_mask = ( flexbg_size > 1 ) ? ~ EXT4_BG_BLOCK_UNINIT : ~ 0 ;
2012-01-04 08:44:38 +04:00
BUG_ON ( flex_gd - > count = = 0 | | group_data = = NULL ) ;
src_group = group_data [ 0 ] . group ;
last_group = src_group + flex_gd - > count - 1 ;
BUG_ON ( ( flexbg_size > 1 ) & & ( ( src_group & ~ ( flexbg_size - 1 ) ) ! =
( last_group & ~ ( flexbg_size - 1 ) ) ) ) ;
next_group :
group = group_data [ 0 ] . group ;
2012-09-05 09:21:50 +04:00
if ( src_group > = group_data [ 0 ] . group + flex_gd - > count )
return - ENOSPC ;
2012-01-04 08:44:38 +04:00
start_blk = ext4_group_first_block_no ( sb , src_group ) ;
last_blk = start_blk + group_data [ src_group - group ] . blocks_count ;
2012-09-05 09:33:50 +04:00
overhead = ext4_group_overhead_blocks ( sb , src_group ) ;
2012-01-04 08:44:38 +04:00
start_blk + = overhead ;
/* We collect contiguous blocks as much as possible. */
src_group + + ;
2012-09-05 09:33:50 +04:00
for ( ; src_group < = last_group ; src_group + + ) {
overhead = ext4_group_overhead_blocks ( sb , src_group ) ;
2014-02-16 06:33:13 +04:00
if ( overhead = = 0 )
2012-01-04 08:44:38 +04:00
last_blk + = group_data [ src_group - group ] . blocks_count ;
else
break ;
2012-09-05 09:33:50 +04:00
}
2012-01-04 08:44:38 +04:00
/* Allocate block bitmaps */
for ( ; bb_index < flex_gd - > count ; bb_index + + ) {
if ( start_blk > = last_blk )
goto next_group ;
group_data [ bb_index ] . block_bitmap = start_blk + + ;
2013-04-04 07:32:34 +04:00
group = ext4_get_group_number ( sb , start_blk - 1 ) ;
2012-01-04 08:44:38 +04:00
group - = group_data [ 0 ] . group ;
group_data [ group ] . free_blocks_count - - ;
2014-02-16 06:33:13 +04:00
flex_gd - > bg_flags [ group ] & = uninit_mask ;
2012-01-04 08:44:38 +04:00
}
/* Allocate inode bitmaps */
for ( ; ib_index < flex_gd - > count ; ib_index + + ) {
if ( start_blk > = last_blk )
goto next_group ;
group_data [ ib_index ] . inode_bitmap = start_blk + + ;
2013-04-04 07:32:34 +04:00
group = ext4_get_group_number ( sb , start_blk - 1 ) ;
2012-01-04 08:44:38 +04:00
group - = group_data [ 0 ] . group ;
group_data [ group ] . free_blocks_count - - ;
2014-02-16 06:33:13 +04:00
flex_gd - > bg_flags [ group ] & = uninit_mask ;
2012-01-04 08:44:38 +04:00
}
/* Allocate inode tables */
for ( ; it_index < flex_gd - > count ; it_index + + ) {
2014-02-16 06:33:13 +04:00
unsigned int itb = EXT4_SB ( sb ) - > s_itb_per_group ;
ext4_fsblk_t next_group_start ;
if ( start_blk + itb > last_blk )
2012-01-04 08:44:38 +04:00
goto next_group ;
group_data [ it_index ] . inode_table = start_blk ;
2014-02-16 06:33:13 +04:00
group = ext4_get_group_number ( sb , start_blk ) ;
next_group_start = ext4_group_first_block_no ( sb , group + 1 ) ;
2012-01-04 08:44:38 +04:00
group - = group_data [ 0 ] . group ;
2014-02-16 06:33:13 +04:00
if ( start_blk + itb > next_group_start ) {
flex_gd - > bg_flags [ group + 1 ] & = uninit_mask ;
overhead = start_blk + itb - next_group_start ;
group_data [ group + 1 ] . free_blocks_count - = overhead ;
itb - = overhead ;
}
group_data [ group ] . free_blocks_count - = itb ;
flex_gd - > bg_flags [ group ] & = uninit_mask ;
2012-01-04 08:44:38 +04:00
start_blk + = EXT4_SB ( sb ) - > s_itb_per_group ;
}
if ( test_opt ( sb , DEBUG ) ) {
int i ;
group = group_data [ 0 ] . group ;
printk ( KERN_DEBUG " EXT4-fs: adding a flex group with "
" %d groups, flexbg size is %d: \n " , flex_gd - > count ,
flexbg_size ) ;
for ( i = 0 ; i < flex_gd - > count ; i + + ) {
printk ( KERN_DEBUG " adding %s group %u: %u "
" blocks (%d free) \n " ,
ext4_bg_has_super ( sb , group + i ) ? " normal " :
" no-super " , group + i ,
group_data [ i ] . blocks_count ,
group_data [ i ] . free_blocks_count ) ;
}
}
2012-09-05 09:21:50 +04:00
return 0 ;
2012-01-04 08:44:38 +04:00
}
2006-10-11 12:20:50 +04:00
static struct buffer_head * bclean ( handle_t * handle , struct super_block * sb ,
2006-10-11 12:20:53 +04:00
ext4_fsblk_t blk )
2006-10-11 12:20:50 +04:00
{
struct buffer_head * bh ;
int err ;
bh = sb_getblk ( sb , blk ) ;
2013-01-13 01:28:47 +04:00
if ( unlikely ( ! bh ) )
2013-01-13 01:19:36 +04:00
return ERR_PTR ( - ENOMEM ) ;
2014-05-13 06:06:43 +04:00
BUFFER_TRACE ( bh , " get_write_access " ) ;
2006-10-11 12:20:53 +04:00
if ( ( err = ext4_journal_get_write_access ( handle , bh ) ) ) {
2006-10-11 12:20:50 +04:00
brelse ( bh ) ;
bh = ERR_PTR ( err ) ;
} else {
memset ( bh - > b_data , 0 , sb - > s_blocksize ) ;
set_buffer_uptodate ( bh ) ;
}
return bh ;
}
2007-10-17 02:38:25 +04:00
/*
* If we have fewer than thresh credits , extend by EXT4_MAX_TRANS_DATA .
* If that fails , restart the transaction & regain write access for the
* buffer head which is used for block_bitmap modifications .
*/
2011-07-27 06:24:41 +04:00
static int extend_or_restart_transaction ( handle_t * handle , int thresh )
2007-10-17 02:38:25 +04:00
{
int err ;
2009-01-07 08:06:22 +03:00
if ( ext4_handle_has_enough_credits ( handle , thresh ) )
2007-10-17 02:38:25 +04:00
return 0 ;
err = ext4_journal_extend ( handle , EXT4_MAX_TRANS_DATA ) ;
if ( err < 0 )
return err ;
if ( err ) {
2011-07-27 06:24:41 +04:00
err = ext4_journal_restart ( handle , EXT4_MAX_TRANS_DATA ) ;
if ( err )
2007-10-17 02:38:25 +04:00
return err ;
2008-07-27 00:15:44 +04:00
}
2007-10-17 02:38:25 +04:00
return 0 ;
}
2012-01-04 08:32:52 +04:00
/*
* set_flexbg_block_bitmap ( ) mark @ count blocks starting from @ block used .
*
* Helper function for ext4_setup_new_group_blocks ( ) which set .
*
* @ sb : super block
* @ handle : journal handle
* @ flex_gd : flex group data
*/
static int set_flexbg_block_bitmap ( struct super_block * sb , handle_t * handle ,
struct ext4_new_flex_group_data * flex_gd ,
ext4_fsblk_t block , ext4_group_t count )
{
ext4_group_t count2 ;
ext4_debug ( " mark blocks [%llu/%u] used \n " , block , count ) ;
for ( count2 = count ; count > 0 ; count - = count2 , block + = count2 ) {
ext4_fsblk_t start ;
struct buffer_head * bh ;
ext4_group_t group ;
int err ;
2013-04-04 07:32:34 +04:00
group = ext4_get_group_number ( sb , block ) ;
2012-01-04 08:32:52 +04:00
start = ext4_group_first_block_no ( sb , group ) ;
group - = flex_gd - > groups [ 0 ] . group ;
2014-02-16 07:42:25 +04:00
count2 = EXT4_BLOCKS_PER_GROUP ( sb ) - ( block - start ) ;
2012-01-04 08:32:52 +04:00
if ( count2 > count )
count2 = count ;
if ( flex_gd - > bg_flags [ group ] & EXT4_BG_BLOCK_UNINIT ) {
BUG_ON ( flex_gd - > count > 1 ) ;
continue ;
}
err = extend_or_restart_transaction ( handle , 1 ) ;
if ( err )
return err ;
bh = sb_getblk ( sb , flex_gd - > groups [ group ] . block_bitmap ) ;
2013-01-13 01:28:47 +04:00
if ( unlikely ( ! bh ) )
2013-01-13 01:19:36 +04:00
return - ENOMEM ;
2012-01-04 08:32:52 +04:00
2014-05-13 06:06:43 +04:00
BUFFER_TRACE ( bh , " get_write_access " ) ;
2012-01-04 08:32:52 +04:00
err = ext4_journal_get_write_access ( handle , bh ) ;
if ( err )
return err ;
ext4_debug ( " mark block bitmap %#04llx (+%llu/%u) \n " , block ,
block - start , count2 ) ;
ext4_set_bits ( bh - > b_data , block - start , count2 ) ;
err = ext4_handle_dirty_metadata ( handle , NULL , bh ) ;
if ( unlikely ( err ) )
return err ;
brelse ( bh ) ;
}
return 0 ;
}
/*
* Set up the block and inode bitmaps , and the inode table for the new groups .
* This doesn ' t need to be part of the main transaction , since we are only
* changing blocks outside the actual filesystem . We still do journaling to
* ensure the recovery is correct in case of a failure just after resize .
* If any part of this fails , we simply abort the resize .
*
* setup_new_flex_group_blocks handles a flex group as follow :
* 1. copy super block and GDT , and initialize group tables if necessary .
* In this step , we only set bits in blocks bitmaps for blocks taken by
* super block and GDT .
* 2. allocate group tables in block bitmaps , that is , set bits in block
* bitmap for blocks taken by group tables .
*/
static int setup_new_flex_group_blocks ( struct super_block * sb ,
struct ext4_new_flex_group_data * flex_gd )
{
int group_table_count [ ] = { 1 , 1 , EXT4_SB ( sb ) - > s_itb_per_group } ;
ext4_fsblk_t start ;
ext4_fsblk_t block ;
struct ext4_sb_info * sbi = EXT4_SB ( sb ) ;
struct ext4_super_block * es = sbi - > s_es ;
struct ext4_new_group_data * group_data = flex_gd - > groups ;
__u16 * bg_flags = flex_gd - > bg_flags ;
handle_t * handle ;
ext4_group_t group , count ;
struct buffer_head * bh = NULL ;
int reserved_gdb , i , j , err = 0 , err2 ;
2012-09-05 09:33:50 +04:00
int meta_bg ;
2012-01-04 08:32:52 +04:00
BUG_ON ( ! flex_gd - > count | | ! group_data | |
group_data [ 0 ] . group ! = sbi - > s_groups_count ) ;
reserved_gdb = le16_to_cpu ( es - > s_reserved_gdt_blocks ) ;
2015-10-17 23:18:43 +03:00
meta_bg = ext4_has_feature_meta_bg ( sb ) ;
2012-01-04 08:32:52 +04:00
/* This transaction may be extended/restarted along the way */
2013-02-09 06:59:22 +04:00
handle = ext4_journal_start_sb ( sb , EXT4_HT_RESIZE , EXT4_MAX_TRANS_DATA ) ;
2012-01-04 08:32:52 +04:00
if ( IS_ERR ( handle ) )
return PTR_ERR ( handle ) ;
group = group_data [ 0 ] . group ;
for ( i = 0 ; i < flex_gd - > count ; i + + , group + + ) {
unsigned long gdblocks ;
2012-09-05 09:33:50 +04:00
ext4_grpblk_t overhead ;
2012-01-04 08:32:52 +04:00
gdblocks = ext4_bg_num_gdb ( sb , group ) ;
start = ext4_group_first_block_no ( sb , group ) ;
2012-09-05 09:33:50 +04:00
if ( meta_bg = = 0 & & ! ext4_bg_has_super ( sb , group ) )
2012-09-05 09:25:50 +04:00
goto handle_itb ;
2012-09-05 09:33:50 +04:00
if ( meta_bg = = 1 ) {
ext4_group_t first_group ;
first_group = ext4_meta_bg_first_group ( sb , group ) ;
if ( first_group ! = group + 1 & &
first_group ! = group + EXT4_DESC_PER_BLOCK ( sb ) - 1 )
goto handle_itb ;
}
block = start + ext4_bg_has_super ( sb , group ) ;
2012-01-04 08:32:52 +04:00
/* Copy all of the GDT blocks into the backup in this group */
2012-09-05 09:33:50 +04:00
for ( j = 0 ; j < gdblocks ; j + + , block + + ) {
2012-01-04 08:32:52 +04:00
struct buffer_head * gdb ;
ext4_debug ( " update backup group %#04llx \n " , block ) ;
err = extend_or_restart_transaction ( handle , 1 ) ;
if ( err )
goto out ;
gdb = sb_getblk ( sb , block ) ;
2013-01-13 01:28:47 +04:00
if ( unlikely ( ! gdb ) ) {
2013-01-13 01:19:36 +04:00
err = - ENOMEM ;
2012-01-04 08:32:52 +04:00
goto out ;
}
2014-05-13 06:06:43 +04:00
BUFFER_TRACE ( gdb , " get_write_access " ) ;
2012-01-04 08:32:52 +04:00
err = ext4_journal_get_write_access ( handle , gdb ) ;
if ( err ) {
brelse ( gdb ) ;
goto out ;
}
memcpy ( gdb - > b_data , sbi - > s_group_desc [ j ] - > b_data ,
gdb - > b_size ) ;
set_buffer_uptodate ( gdb ) ;
err = ext4_handle_dirty_metadata ( handle , NULL , gdb ) ;
if ( unlikely ( err ) ) {
brelse ( gdb ) ;
goto out ;
}
brelse ( gdb ) ;
}
/* Zero out all of the reserved backup group descriptor
* table blocks
*/
if ( ext4_bg_has_super ( sb , group ) ) {
err = sb_issue_zeroout ( sb , gdblocks + start + 1 ,
reserved_gdb , GFP_NOFS ) ;
if ( err )
goto out ;
}
2012-09-05 09:25:50 +04:00
handle_itb :
2012-01-04 08:32:52 +04:00
/* Initialize group tables of the grop @group */
if ( ! ( bg_flags [ i ] & EXT4_BG_INODE_ZEROED ) )
goto handle_bb ;
/* Zero out all of the inode table blocks */
block = group_data [ i ] . inode_table ;
ext4_debug ( " clear inode table blocks %#04llx -> %#04lx \n " ,
block , sbi - > s_itb_per_group ) ;
err = sb_issue_zeroout ( sb , block , sbi - > s_itb_per_group ,
GFP_NOFS ) ;
if ( err )
goto out ;
handle_bb :
if ( bg_flags [ i ] & EXT4_BG_BLOCK_UNINIT )
goto handle_ib ;
/* Initialize block bitmap of the @group */
block = group_data [ i ] . block_bitmap ;
err = extend_or_restart_transaction ( handle , 1 ) ;
if ( err )
goto out ;
bh = bclean ( handle , sb , block ) ;
if ( IS_ERR ( bh ) ) {
err = PTR_ERR ( bh ) ;
2014-09-03 17:33:00 +04:00
bh = NULL ;
2012-01-04 08:32:52 +04:00
goto out ;
}
2012-09-05 09:33:50 +04:00
overhead = ext4_group_overhead_blocks ( sb , group ) ;
if ( overhead ! = 0 ) {
2012-01-04 08:32:52 +04:00
ext4_debug ( " mark backup superblock %#04llx (+0) \n " ,
start ) ;
2012-09-05 09:33:50 +04:00
ext4_set_bits ( bh - > b_data , 0 , overhead ) ;
2012-01-04 08:32:52 +04:00
}
ext4_mark_bitmap_end ( group_data [ i ] . blocks_count ,
sb - > s_blocksize * 8 , bh - > b_data ) ;
err = ext4_handle_dirty_metadata ( handle , NULL , bh ) ;
if ( err )
goto out ;
brelse ( bh ) ;
handle_ib :
if ( bg_flags [ i ] & EXT4_BG_INODE_UNINIT )
continue ;
/* Initialize inode bitmap of the @group */
block = group_data [ i ] . inode_bitmap ;
err = extend_or_restart_transaction ( handle , 1 ) ;
if ( err )
goto out ;
/* Mark unused entries in inode bitmap used */
bh = bclean ( handle , sb , block ) ;
if ( IS_ERR ( bh ) ) {
err = PTR_ERR ( bh ) ;
2014-09-03 17:33:00 +04:00
bh = NULL ;
2012-01-04 08:32:52 +04:00
goto out ;
}
ext4_mark_bitmap_end ( EXT4_INODES_PER_GROUP ( sb ) ,
sb - > s_blocksize * 8 , bh - > b_data ) ;
err = ext4_handle_dirty_metadata ( handle , NULL , bh ) ;
if ( err )
goto out ;
brelse ( bh ) ;
}
bh = NULL ;
/* Mark group tables in block bitmap */
for ( j = 0 ; j < GROUP_TABLE_COUNT ; j + + ) {
count = group_table_count [ j ] ;
start = ( & group_data [ 0 ] . block_bitmap ) [ j ] ;
block = start ;
for ( i = 1 ; i < flex_gd - > count ; i + + ) {
block + = group_table_count [ j ] ;
if ( block = = ( & group_data [ i ] . block_bitmap ) [ j ] ) {
count + = group_table_count [ j ] ;
continue ;
}
err = set_flexbg_block_bitmap ( sb , handle ,
flex_gd , start , count ) ;
if ( err )
goto out ;
count = group_table_count [ j ] ;
2014-02-16 06:33:13 +04:00
start = ( & group_data [ i ] . block_bitmap ) [ j ] ;
2012-01-04 08:32:52 +04:00
block = start ;
}
if ( count ) {
err = set_flexbg_block_bitmap ( sb , handle ,
flex_gd , start , count ) ;
if ( err )
goto out ;
}
}
out :
brelse ( bh ) ;
err2 = ext4_journal_stop ( handle ) ;
if ( err2 & & ! err )
err = err2 ;
return err ;
}
2006-10-11 12:20:50 +04:00
/*
* Iterate through the groups which hold BACKUP superblock / GDT copies in an
2006-10-11 12:20:53 +04:00
* ext4 filesystem . The counters should be initialized to 1 , 5 , and 7 before
2006-10-11 12:20:50 +04:00
* calling this for the first time . In a sparse filesystem it will be the
* sequence of powers of 3 , 5 , and 7 : 1 , 3 , 5 , 7 , 9 , 25 , 27 , 49 , 81 , . . .
* For a non - sparse filesystem it will be every group : 1 , 2 , 3 , 4 , . . .
*/
2006-10-11 12:20:53 +04:00
static unsigned ext4_list_backups ( struct super_block * sb , unsigned * three ,
2006-10-11 12:20:50 +04:00
unsigned * five , unsigned * seven )
{
unsigned * min = three ;
int mult = 3 ;
unsigned ret ;
2015-10-17 23:18:43 +03:00
if ( ! ext4_has_feature_sparse_super ( sb ) ) {
2006-10-11 12:20:50 +04:00
ret = * min ;
* min + = 1 ;
return ret ;
}
if ( * five < * min ) {
min = five ;
mult = 5 ;
}
if ( * seven < * min ) {
min = seven ;
mult = 7 ;
}
ret = * min ;
* min * = mult ;
return ret ;
}
/*
* Check that all of the backup GDT blocks are held in the primary GDT block .
* It is assumed that they are stored in group order . Returns the number of
* groups in current filesystem that have BACKUPS , or - ve error code .
*/
static int verify_reserved_gdb ( struct super_block * sb ,
2012-01-04 08:43:39 +04:00
ext4_group_t end ,
2006-10-11 12:20:50 +04:00
struct buffer_head * primary )
{
2006-10-11 12:20:53 +04:00
const ext4_fsblk_t blk = primary - > b_blocknr ;
2006-10-11 12:20:50 +04:00
unsigned three = 1 ;
unsigned five = 5 ;
unsigned seven = 7 ;
unsigned grp ;
__le32 * p = ( __le32 * ) primary - > b_data ;
int gdbackups = 0 ;
2006-10-11 12:20:53 +04:00
while ( ( grp = ext4_list_backups ( sb , & three , & five , & seven ) ) < end ) {
2006-10-11 12:21:10 +04:00
if ( le32_to_cpu ( * p + + ) ! =
grp * EXT4_BLOCKS_PER_GROUP ( sb ) + blk ) {
2010-02-15 22:19:27 +03:00
ext4_warning ( sb , " reserved GDT %llu "
2006-10-11 12:21:11 +04:00
" missing grp %d (%llu) " ,
2006-10-11 12:20:50 +04:00
blk , grp ,
2006-10-11 12:21:10 +04:00
grp *
( ext4_fsblk_t ) EXT4_BLOCKS_PER_GROUP ( sb ) +
blk ) ;
2006-10-11 12:20:50 +04:00
return - EINVAL ;
}
2006-10-11 12:20:53 +04:00
if ( + + gdbackups > EXT4_ADDR_PER_BLOCK ( sb ) )
2006-10-11 12:20:50 +04:00
return - EFBIG ;
}
return gdbackups ;
}
/*
* Called when we need to bring a reserved group descriptor table block into
* use from the resize inode . The primary copy of the new GDT block currently
* is an indirect block ( under the double indirect block in the resize inode ) .
* The new backup GDT blocks will be stored as leaf blocks in this indirect
* block , in group order . Even though we know all the block numbers we need ,
* we check to ensure that the resize inode has actually reserved these blocks .
*
* Don ' t need to update the block bitmaps because the blocks are still in use .
*
* We get all of the error cases out of the way , so that we are sure to not
* fail once we start modifying the data on disk , because JBD has no rollback .
*/
static int add_new_gdb ( handle_t * handle , struct inode * inode ,
2011-07-28 05:16:33 +04:00
ext4_group_t group )
2006-10-11 12:20:50 +04:00
{
struct super_block * sb = inode - > i_sb ;
2006-10-11 12:20:53 +04:00
struct ext4_super_block * es = EXT4_SB ( sb ) - > s_es ;
2011-07-28 05:16:33 +04:00
unsigned long gdb_num = group / EXT4_DESC_PER_BLOCK ( sb ) ;
2006-10-11 12:20:53 +04:00
ext4_fsblk_t gdblock = EXT4_SB ( sb ) - > s_sbh - > b_blocknr + 1 + gdb_num ;
2006-10-11 12:20:50 +04:00
struct buffer_head * * o_group_desc , * * n_group_desc ;
struct buffer_head * dind ;
2011-07-28 05:16:33 +04:00
struct buffer_head * gdb_bh ;
2006-10-11 12:20:50 +04:00
int gdbackups ;
2006-10-11 12:20:53 +04:00
struct ext4_iloc iloc ;
2006-10-11 12:20:50 +04:00
__le32 * data ;
int err ;
if ( test_opt ( sb , DEBUG ) )
printk ( KERN_DEBUG
2006-10-11 12:20:53 +04:00
" EXT4-fs: ext4_add_new_gdb: adding group block %lu \n " ,
2006-10-11 12:20:50 +04:00
gdb_num ) ;
2011-07-28 05:16:33 +04:00
gdb_bh = sb_bread ( sb , gdblock ) ;
if ( ! gdb_bh )
2006-10-11 12:20:50 +04:00
return - EIO ;
2012-01-04 08:43:39 +04:00
gdbackups = verify_reserved_gdb ( sb , group , gdb_bh ) ;
2011-07-28 05:16:33 +04:00
if ( gdbackups < 0 ) {
2006-10-11 12:20:50 +04:00
err = gdbackups ;
goto exit_bh ;
}
2006-10-11 12:20:53 +04:00
data = EXT4_I ( inode ) - > i_data + EXT4_DIND_BLOCK ;
2006-10-11 12:20:50 +04:00
dind = sb_bread ( sb , le32_to_cpu ( * data ) ) ;
if ( ! dind ) {
err = - EIO ;
goto exit_bh ;
}
data = ( __le32 * ) dind - > b_data ;
2006-10-11 12:20:53 +04:00
if ( le32_to_cpu ( data [ gdb_num % EXT4_ADDR_PER_BLOCK ( sb ) ] ) ! = gdblock ) {
2010-02-15 22:19:27 +03:00
ext4_warning ( sb , " new group %u GDT block %llu not reserved " ,
2011-07-28 05:16:33 +04:00
group , gdblock ) ;
2006-10-11 12:20:50 +04:00
err = - EINVAL ;
goto exit_dind ;
}
2014-05-13 06:06:43 +04:00
BUFFER_TRACE ( EXT4_SB ( sb ) - > s_sbh , " get_write_access " ) ;
2011-01-10 20:46:59 +03:00
err = ext4_journal_get_write_access ( handle , EXT4_SB ( sb ) - > s_sbh ) ;
if ( unlikely ( err ) )
2006-10-11 12:20:50 +04:00
goto exit_dind ;
2014-05-13 06:06:43 +04:00
BUFFER_TRACE ( gdb_bh , " get_write_access " ) ;
2011-07-28 05:16:33 +04:00
err = ext4_journal_get_write_access ( handle , gdb_bh ) ;
2011-01-10 20:46:59 +03:00
if ( unlikely ( err ) )
2012-11-08 20:22:46 +04:00
goto exit_dind ;
2006-10-11 12:20:50 +04:00
2014-05-13 06:06:43 +04:00
BUFFER_TRACE ( dind , " get_write_access " ) ;
2011-01-10 20:46:59 +03:00
err = ext4_journal_get_write_access ( handle , dind ) ;
if ( unlikely ( err ) )
ext4_std_error ( sb , err ) ;
2006-10-11 12:20:50 +04:00
2006-10-11 12:20:53 +04:00
/* ext4_reserve_inode_write() gets a reference on the iloc */
2011-01-10 20:46:59 +03:00
err = ext4_reserve_inode_write ( handle , inode , & iloc ) ;
if ( unlikely ( err ) )
2012-11-08 20:22:46 +04:00
goto exit_dind ;
2006-10-11 12:20:50 +04:00
2011-08-01 16:45:38 +04:00
n_group_desc = ext4_kvmalloc ( ( gdb_num + 1 ) *
sizeof ( struct buffer_head * ) ,
GFP_NOFS ) ;
2006-10-11 12:20:50 +04:00
if ( ! n_group_desc ) {
err = - ENOMEM ;
2011-08-01 16:45:38 +04:00
ext4_warning ( sb , " not enough memory for %lu groups " ,
gdb_num + 1 ) ;
2006-10-11 12:20:50 +04:00
goto exit_inode ;
}
/*
* Finally , we have all of the possible failures behind us . . .
*
* Remove new GDT block from inode double - indirect block and clear out
* the new GDT block for use ( which also " frees " the backup GDT blocks
* from the reserved inode ) . We don ' t need to change the bitmaps for
* these blocks , because they are marked as in - use from being in the
* reserved inode , and will become GDT blocks ( primary and backup ) .
*/
2006-10-11 12:20:53 +04:00
data [ gdb_num % EXT4_ADDR_PER_BLOCK ( sb ) ] = 0 ;
2011-01-10 20:46:59 +03:00
err = ext4_handle_dirty_metadata ( handle , NULL , dind ) ;
if ( unlikely ( err ) ) {
ext4_std_error ( sb , err ) ;
goto exit_inode ;
}
2006-10-11 12:20:50 +04:00
inode - > i_blocks - = ( gdbackups + 1 ) * sb - > s_blocksize > > 9 ;
2006-10-11 12:20:53 +04:00
ext4_mark_iloc_dirty ( handle , inode , & iloc ) ;
2011-07-28 05:16:33 +04:00
memset ( gdb_bh - > b_data , 0 , sb - > s_blocksize ) ;
err = ext4_handle_dirty_metadata ( handle , NULL , gdb_bh ) ;
2011-01-10 20:46:59 +03:00
if ( unlikely ( err ) ) {
ext4_std_error ( sb , err ) ;
goto exit_inode ;
}
brelse ( dind ) ;
2006-10-11 12:20:50 +04:00
2006-10-11 12:20:53 +04:00
o_group_desc = EXT4_SB ( sb ) - > s_group_desc ;
2006-10-11 12:20:50 +04:00
memcpy ( n_group_desc , o_group_desc ,
2006-10-11 12:20:53 +04:00
EXT4_SB ( sb ) - > s_gdb_count * sizeof ( struct buffer_head * ) ) ;
2011-07-28 05:16:33 +04:00
n_group_desc [ gdb_num ] = gdb_bh ;
2006-10-11 12:20:53 +04:00
EXT4_SB ( sb ) - > s_group_desc = n_group_desc ;
EXT4_SB ( sb ) - > s_gdb_count + + ;
2014-11-20 20:19:11 +03:00
kvfree ( o_group_desc ) ;
2006-10-11 12:20:50 +04:00
2008-04-17 18:38:59 +04:00
le16_add_cpu ( & es - > s_reserved_gdt_blocks , - 1 ) ;
2012-07-23 04:37:31 +04:00
err = ext4_handle_dirty_super ( handle , sb ) ;
2011-01-10 20:46:59 +03:00
if ( err )
ext4_std_error ( sb , err ) ;
2006-10-11 12:20:50 +04:00
2011-01-10 20:46:59 +03:00
return err ;
2006-10-11 12:20:50 +04:00
exit_inode :
2014-11-20 20:19:11 +03:00
kvfree ( n_group_desc ) ;
2006-10-11 12:20:50 +04:00
brelse ( iloc . bh ) ;
exit_dind :
brelse ( dind ) ;
exit_bh :
2011-07-28 05:16:33 +04:00
brelse ( gdb_bh ) ;
2006-10-11 12:20:50 +04:00
2006-10-11 12:20:53 +04:00
ext4_debug ( " leaving with error %d \n " , err ) ;
2006-10-11 12:20:50 +04:00
return err ;
}
2012-09-05 09:33:50 +04:00
/*
* add_new_gdb_meta_bg is the sister of add_new_gdb .
*/
static int add_new_gdb_meta_bg ( struct super_block * sb ,
handle_t * handle , ext4_group_t group ) {
ext4_fsblk_t gdblock ;
struct buffer_head * gdb_bh ;
struct buffer_head * * o_group_desc , * * n_group_desc ;
unsigned long gdb_num = group / EXT4_DESC_PER_BLOCK ( sb ) ;
int err ;
gdblock = ext4_meta_bg_first_block_no ( sb , group ) +
ext4_bg_has_super ( sb , group ) ;
gdb_bh = sb_bread ( sb , gdblock ) ;
if ( ! gdb_bh )
return - EIO ;
n_group_desc = ext4_kvmalloc ( ( gdb_num + 1 ) *
sizeof ( struct buffer_head * ) ,
GFP_NOFS ) ;
if ( ! n_group_desc ) {
err = - ENOMEM ;
ext4_warning ( sb , " not enough memory for %lu groups " ,
gdb_num + 1 ) ;
return err ;
}
o_group_desc = EXT4_SB ( sb ) - > s_group_desc ;
memcpy ( n_group_desc , o_group_desc ,
EXT4_SB ( sb ) - > s_gdb_count * sizeof ( struct buffer_head * ) ) ;
n_group_desc [ gdb_num ] = gdb_bh ;
EXT4_SB ( sb ) - > s_group_desc = n_group_desc ;
EXT4_SB ( sb ) - > s_gdb_count + + ;
2014-11-20 20:19:11 +03:00
kvfree ( o_group_desc ) ;
2014-05-13 06:06:43 +04:00
BUFFER_TRACE ( gdb_bh , " get_write_access " ) ;
2012-09-05 09:33:50 +04:00
err = ext4_journal_get_write_access ( handle , gdb_bh ) ;
if ( unlikely ( err ) )
brelse ( gdb_bh ) ;
return err ;
}
2006-10-11 12:20:50 +04:00
/*
* Called when we are adding a new group which has a backup copy of each of
* the GDT blocks ( i . e . sparse group ) and there are reserved GDT blocks .
* We need to add these reserved backup GDT blocks to the resize inode , so
* that they are kept for future resizing and not allocated to files .
*
* Each reserved backup GDT block will go into a different indirect block .
* The indirect blocks are actually the primary reserved GDT blocks ,
* so we know in advance what their block numbers are . We only get the
* double - indirect block to verify it is pointing to the primary reserved
* GDT blocks so we don ' t overwrite a data block by accident . The reserved
* backup GDT blocks are stored in their reserved primary GDT block .
*/
static int reserve_backup_gdb ( handle_t * handle , struct inode * inode ,
2011-07-28 05:23:13 +04:00
ext4_group_t group )
2006-10-11 12:20:50 +04:00
{
struct super_block * sb = inode - > i_sb ;
2006-10-11 12:20:53 +04:00
int reserved_gdb = le16_to_cpu ( EXT4_SB ( sb ) - > s_es - > s_reserved_gdt_blocks ) ;
2006-10-11 12:20:50 +04:00
struct buffer_head * * primary ;
struct buffer_head * dind ;
2006-10-11 12:20:53 +04:00
struct ext4_iloc iloc ;
ext4_fsblk_t blk ;
2006-10-11 12:20:50 +04:00
__le32 * data , * end ;
int gdbackups = 0 ;
int res , i ;
int err ;
2008-04-30 06:02:02 +04:00
primary = kmalloc ( reserved_gdb * sizeof ( * primary ) , GFP_NOFS ) ;
2006-10-11 12:20:50 +04:00
if ( ! primary )
return - ENOMEM ;
2006-10-11 12:20:53 +04:00
data = EXT4_I ( inode ) - > i_data + EXT4_DIND_BLOCK ;
2006-10-11 12:20:50 +04:00
dind = sb_bread ( sb , le32_to_cpu ( * data ) ) ;
if ( ! dind ) {
err = - EIO ;
goto exit_free ;
}
2006-10-11 12:20:53 +04:00
blk = EXT4_SB ( sb ) - > s_sbh - > b_blocknr + 1 + EXT4_SB ( sb ) - > s_gdb_count ;
2008-06-07 02:05:52 +04:00
data = ( __le32 * ) dind - > b_data + ( EXT4_SB ( sb ) - > s_gdb_count %
EXT4_ADDR_PER_BLOCK ( sb ) ) ;
2006-10-11 12:20:53 +04:00
end = ( __le32 * ) dind - > b_data + EXT4_ADDR_PER_BLOCK ( sb ) ;
2006-10-11 12:20:50 +04:00
/* Get each reserved primary GDT block and verify it holds backups */
for ( res = 0 ; res < reserved_gdb ; res + + , blk + + ) {
if ( le32_to_cpu ( * data ) ! = blk ) {
2010-02-15 22:19:27 +03:00
ext4_warning ( sb , " reserved block %llu "
2006-10-11 12:20:50 +04:00
" not at offset %ld " ,
blk ,
( long ) ( data - ( __le32 * ) dind - > b_data ) ) ;
err = - EINVAL ;
goto exit_bh ;
}
primary [ res ] = sb_bread ( sb , blk ) ;
if ( ! primary [ res ] ) {
err = - EIO ;
goto exit_bh ;
}
2012-01-04 08:43:39 +04:00
gdbackups = verify_reserved_gdb ( sb , group , primary [ res ] ) ;
if ( gdbackups < 0 ) {
2006-10-11 12:20:50 +04:00
brelse ( primary [ res ] ) ;
err = gdbackups ;
goto exit_bh ;
}
if ( + + data > = end )
data = ( __le32 * ) dind - > b_data ;
}
for ( i = 0 ; i < reserved_gdb ; i + + ) {
2014-05-13 06:06:43 +04:00
BUFFER_TRACE ( primary [ i ] , " get_write_access " ) ;
2012-11-08 20:22:46 +04:00
if ( ( err = ext4_journal_get_write_access ( handle , primary [ i ] ) ) )
2006-10-11 12:20:50 +04:00
goto exit_bh ;
}
2006-10-11 12:20:53 +04:00
if ( ( err = ext4_reserve_inode_write ( handle , inode , & iloc ) ) )
2006-10-11 12:20:50 +04:00
goto exit_bh ;
/*
* Finally we can add each of the reserved backup GDT blocks from
* the new group to its reserved primary GDT block .
*/
2011-07-28 05:23:13 +04:00
blk = group * EXT4_BLOCKS_PER_GROUP ( sb ) ;
2006-10-11 12:20:50 +04:00
for ( i = 0 ; i < reserved_gdb ; i + + ) {
int err2 ;
data = ( __le32 * ) primary [ i ] - > b_data ;
/* printk("reserving backup %lu[%u] = %lu\n",
primary [ i ] - > b_blocknr , gdbackups ,
blk + primary [ i ] - > b_blocknr ) ; */
data [ gdbackups ] = cpu_to_le32 ( blk + primary [ i ] - > b_blocknr ) ;
2009-01-07 08:06:22 +03:00
err2 = ext4_handle_dirty_metadata ( handle , NULL , primary [ i ] ) ;
2006-10-11 12:20:50 +04:00
if ( ! err )
err = err2 ;
}
inode - > i_blocks + = reserved_gdb * sb - > s_blocksize > > 9 ;
2006-10-11 12:20:53 +04:00
ext4_mark_iloc_dirty ( handle , inode , & iloc ) ;
2006-10-11 12:20:50 +04:00
exit_bh :
while ( - - res > = 0 )
brelse ( primary [ res ] ) ;
brelse ( dind ) ;
exit_free :
kfree ( primary ) ;
return err ;
}
/*
2006-10-11 12:20:53 +04:00
* Update the backup copies of the ext4 metadata . These don ' t need to be part
2006-10-11 12:20:50 +04:00
* of the main resize transaction , because e2fsck will re - write them if there
* is a problem ( basically only OOM will cause a problem ) . However , we
* _should_ update the backups if possible , in case the primary gets trashed
* for some reason and we need to run e2fsck from a backup superblock . The
* important part is that the new block and inode counts are in the backup
* superblocks , and the location of the new group metadata in the GDT backups .
*
2009-04-26 06:53:39 +04:00
* We do not need take the s_resize_lock for this , because these
* blocks are not otherwise touched by the filesystem code when it is
* mounted . We don ' t need to worry about last changing from
* sbi - > s_groups_count , because the worst that can happen is that we
* do not copy the full number of backups at this time . The resize
* which changed s_groups_count will backup again .
2006-10-11 12:20:50 +04:00
*/
2015-10-18 07:36:29 +03:00
static void update_backups ( struct super_block * sb , sector_t blk_off , char * data ,
2012-09-05 09:33:50 +04:00
int size , int meta_bg )
2006-10-11 12:20:50 +04:00
{
2006-10-11 12:20:53 +04:00
struct ext4_sb_info * sbi = EXT4_SB ( sb ) ;
2012-09-05 09:33:50 +04:00
ext4_group_t last ;
2006-10-11 12:20:53 +04:00
const int bpg = EXT4_BLOCKS_PER_GROUP ( sb ) ;
2006-10-11 12:20:50 +04:00
unsigned three = 1 ;
unsigned five = 5 ;
unsigned seven = 7 ;
2012-09-05 09:33:50 +04:00
ext4_group_t group = 0 ;
2006-10-11 12:20:50 +04:00
int rest = sb - > s_blocksize - size ;
handle_t * handle ;
int err = 0 , err2 ;
2013-02-09 06:59:22 +04:00
handle = ext4_journal_start_sb ( sb , EXT4_HT_RESIZE , EXT4_MAX_TRANS_DATA ) ;
2006-10-11 12:20:50 +04:00
if ( IS_ERR ( handle ) ) {
group = 1 ;
err = PTR_ERR ( handle ) ;
goto exit_err ;
}
2012-09-05 09:33:50 +04:00
if ( meta_bg = = 0 ) {
group = ext4_list_backups ( sb , & three , & five , & seven ) ;
last = sbi - > s_groups_count ;
} else {
2015-10-18 07:36:29 +03:00
group = ext4_get_group_number ( sb , blk_off ) + 1 ;
2012-09-05 09:33:50 +04:00
last = ( ext4_group_t ) ( group + EXT4_DESC_PER_BLOCK ( sb ) - 2 ) ;
}
while ( group < sbi - > s_groups_count ) {
2006-10-11 12:20:50 +04:00
struct buffer_head * bh ;
2012-09-05 09:33:50 +04:00
ext4_fsblk_t backup_block ;
2006-10-11 12:20:50 +04:00
/* Out of journal space, and can't get more - abort - so sad */
2009-01-07 08:06:22 +03:00
if ( ext4_handle_valid ( handle ) & &
handle - > h_buffer_credits = = 0 & &
2006-10-11 12:20:53 +04:00
ext4_journal_extend ( handle , EXT4_MAX_TRANS_DATA ) & &
( err = ext4_journal_restart ( handle , EXT4_MAX_TRANS_DATA ) ) )
2006-10-11 12:20:50 +04:00
break ;
2012-09-05 09:33:50 +04:00
if ( meta_bg = = 0 )
2014-10-30 17:52:57 +03:00
backup_block = ( ( ext4_fsblk_t ) group ) * bpg + blk_off ;
2012-09-05 09:33:50 +04:00
else
backup_block = ( ext4_group_first_block_no ( sb , group ) +
ext4_bg_has_super ( sb , group ) ) ;
bh = sb_getblk ( sb , backup_block ) ;
2013-01-13 01:28:47 +04:00
if ( unlikely ( ! bh ) ) {
2013-01-13 01:19:36 +04:00
err = - ENOMEM ;
2006-10-11 12:20:50 +04:00
break ;
}
2012-09-05 09:33:50 +04:00
ext4_debug ( " update metadata backup %llu(+%llu) \n " ,
backup_block , backup_block -
ext4_group_first_block_no ( sb , group ) ) ;
2014-05-13 06:06:43 +04:00
BUFFER_TRACE ( bh , " get_write_access " ) ;
2006-10-11 12:20:53 +04:00
if ( ( err = ext4_journal_get_write_access ( handle , bh ) ) )
2006-10-11 12:20:50 +04:00
break ;
lock_buffer ( bh ) ;
memcpy ( bh - > b_data , data , size ) ;
if ( rest )
memset ( bh - > b_data + size , 0 , rest ) ;
set_buffer_uptodate ( bh ) ;
unlock_buffer ( bh ) ;
2011-01-10 20:46:59 +03:00
err = ext4_handle_dirty_metadata ( handle , NULL , bh ) ;
if ( unlikely ( err ) )
ext4_std_error ( sb , err ) ;
2006-10-11 12:20:50 +04:00
brelse ( bh ) ;
2012-09-05 09:33:50 +04:00
if ( meta_bg = = 0 )
group = ext4_list_backups ( sb , & three , & five , & seven ) ;
else if ( group = = last )
break ;
else
group = last ;
2006-10-11 12:20:50 +04:00
}
2006-10-11 12:20:53 +04:00
if ( ( err2 = ext4_journal_stop ( handle ) ) & & ! err )
2006-10-11 12:20:50 +04:00
err = err2 ;
/*
* Ugh ! Need to have e2fsck write the backup copies . It is too
* late to revert the resize , we shouldn ' t fail just because of
* the backup copies ( they are only needed in case of corruption ) .
*
* However , if we got here we have a journal problem too , so we
* can ' t really start a transaction to mark the superblock .
* Chicken out and just set the flag on the hope it will be written
* to disk , and if not - we will simply wait until next fsck .
*/
exit_err :
if ( err ) {
2010-02-15 22:19:27 +03:00
ext4_warning ( sb , " can't update backup for group %u (err %d), "
2006-10-11 12:20:50 +04:00
" forcing fsck on next reboot " , group , err ) ;
2006-10-11 12:20:53 +04:00
sbi - > s_mount_state & = ~ EXT4_VALID_FS ;
sbi - > s_es - > s_state & = cpu_to_le16 ( ~ EXT4_VALID_FS ) ;
2006-10-11 12:20:50 +04:00
mark_buffer_dirty ( sbi - > s_sbh ) ;
}
}
2012-01-04 08:20:50 +04:00
/*
* ext4_add_new_descs ( ) adds @ count group descriptor of groups
* starting at @ group
*
* @ handle : journal handle
* @ sb : super block
* @ group : the group no . of the first group desc to be added
* @ resize_inode : the resize inode
* @ count : number of group descriptors to be added
*/
static int ext4_add_new_descs ( handle_t * handle , struct super_block * sb ,
ext4_group_t group , struct inode * resize_inode ,
ext4_group_t count )
{
struct ext4_sb_info * sbi = EXT4_SB ( sb ) ;
struct ext4_super_block * es = sbi - > s_es ;
struct buffer_head * gdb_bh ;
int i , gdb_off , gdb_num , err = 0 ;
2012-09-05 09:33:50 +04:00
int meta_bg ;
2012-01-04 08:20:50 +04:00
2015-10-17 23:18:43 +03:00
meta_bg = ext4_has_feature_meta_bg ( sb ) ;
2012-01-04 08:20:50 +04:00
for ( i = 0 ; i < count ; i + + , group + + ) {
int reserved_gdb = ext4_bg_has_super ( sb , group ) ?
le16_to_cpu ( es - > s_reserved_gdt_blocks ) : 0 ;
gdb_off = group % EXT4_DESC_PER_BLOCK ( sb ) ;
gdb_num = group / EXT4_DESC_PER_BLOCK ( sb ) ;
/*
* We will only either add reserved group blocks to a backup group
* or remove reserved blocks for the first group in a new group block .
* Doing both would be mean more complex code , and sane people don ' t
* use non - sparse filesystems anymore . This is already checked above .
*/
if ( gdb_off ) {
gdb_bh = sbi - > s_group_desc [ gdb_num ] ;
2014-05-13 06:06:43 +04:00
BUFFER_TRACE ( gdb_bh , " get_write_access " ) ;
2012-01-04 08:20:50 +04:00
err = ext4_journal_get_write_access ( handle , gdb_bh ) ;
if ( ! err & & reserved_gdb & & ext4_bg_num_gdb ( sb , group ) )
err = reserve_backup_gdb ( handle , resize_inode , group ) ;
2012-09-05 09:33:50 +04:00
} else if ( meta_bg ! = 0 ) {
err = add_new_gdb_meta_bg ( sb , handle , group ) ;
} else {
2012-01-04 08:20:50 +04:00
err = add_new_gdb ( handle , resize_inode , group ) ;
2012-09-05 09:33:50 +04:00
}
2012-01-04 08:20:50 +04:00
if ( err )
break ;
}
return err ;
}
2012-04-30 02:33:10 +04:00
static struct buffer_head * ext4_get_bitmap ( struct super_block * sb , __u64 block )
{
struct buffer_head * bh = sb_getblk ( sb , block ) ;
2013-01-13 01:28:47 +04:00
if ( unlikely ( ! bh ) )
2012-04-30 02:33:10 +04:00
return NULL ;
2012-09-26 07:19:25 +04:00
if ( ! bh_uptodate_or_lock ( bh ) ) {
if ( bh_submit_read ( bh ) < 0 ) {
brelse ( bh ) ;
return NULL ;
}
2012-04-30 02:33:10 +04:00
}
return bh ;
}
static int ext4_set_bitmap_checksums ( struct super_block * sb ,
ext4_group_t group ,
struct ext4_group_desc * gdp ,
struct ext4_new_group_data * group_data )
{
struct buffer_head * bh ;
2014-10-13 11:36:16 +04:00
if ( ! ext4_has_metadata_csum ( sb ) )
2012-04-30 02:33:10 +04:00
return 0 ;
bh = ext4_get_bitmap ( sb , group_data - > inode_bitmap ) ;
if ( ! bh )
return - EIO ;
ext4_inode_bitmap_csum_set ( sb , group , gdp , bh ,
EXT4_INODES_PER_GROUP ( sb ) / 8 ) ;
brelse ( bh ) ;
2012-04-30 02:35:10 +04:00
bh = ext4_get_bitmap ( sb , group_data - > block_bitmap ) ;
if ( ! bh )
return - EIO ;
2012-10-22 08:34:32 +04:00
ext4_block_bitmap_csum_set ( sb , group , gdp , bh ) ;
2012-04-30 02:35:10 +04:00
brelse ( bh ) ;
2012-04-30 02:33:10 +04:00
return 0 ;
}
2012-01-04 08:37:31 +04:00
/*
* ext4_setup_new_descs ( ) will set up the group descriptor descriptors of a flex bg
*/
static int ext4_setup_new_descs ( handle_t * handle , struct super_block * sb ,
struct ext4_new_flex_group_data * flex_gd )
{
struct ext4_new_group_data * group_data = flex_gd - > groups ;
struct ext4_group_desc * gdp ;
struct ext4_sb_info * sbi = EXT4_SB ( sb ) ;
struct buffer_head * gdb_bh ;
ext4_group_t group ;
__u16 * bg_flags = flex_gd - > bg_flags ;
int i , gdb_off , gdb_num , err = 0 ;
for ( i = 0 ; i < flex_gd - > count ; i + + , group_data + + , bg_flags + + ) {
group = group_data - > group ;
gdb_off = group % EXT4_DESC_PER_BLOCK ( sb ) ;
gdb_num = group / EXT4_DESC_PER_BLOCK ( sb ) ;
/*
* get_write_access ( ) has been called on gdb_bh by ext4_add_new_desc ( ) .
*/
gdb_bh = sbi - > s_group_desc [ gdb_num ] ;
/* Update group descriptor block for new group */
2012-05-29 01:47:52 +04:00
gdp = ( struct ext4_group_desc * ) ( gdb_bh - > b_data +
2012-01-04 08:37:31 +04:00
gdb_off * EXT4_DESC_SIZE ( sb ) ) ;
memset ( gdp , 0 , EXT4_DESC_SIZE ( sb ) ) ;
ext4_block_bitmap_set ( sb , gdp , group_data - > block_bitmap ) ;
ext4_inode_bitmap_set ( sb , gdp , group_data - > inode_bitmap ) ;
2012-04-30 02:33:10 +04:00
err = ext4_set_bitmap_checksums ( sb , group , gdp , group_data ) ;
if ( err ) {
ext4_std_error ( sb , err ) ;
break ;
}
2012-01-04 08:37:31 +04:00
ext4_inode_table_set ( sb , gdp , group_data - > inode_table ) ;
ext4_free_group_clusters_set ( sb , gdp ,
2013-03-03 02:18:58 +04:00
EXT4_NUM_B2C ( sbi , group_data - > free_blocks_count ) ) ;
2012-01-04 08:37:31 +04:00
ext4_free_inodes_set ( sb , gdp , EXT4_INODES_PER_GROUP ( sb ) ) ;
2012-09-12 22:32:42 +04:00
if ( ext4_has_group_desc_csum ( sb ) )
ext4_itable_unused_set ( sb , gdp ,
EXT4_INODES_PER_GROUP ( sb ) ) ;
2012-01-04 08:37:31 +04:00
gdp - > bg_flags = cpu_to_le16 ( * bg_flags ) ;
2012-04-30 02:45:10 +04:00
ext4_group_desc_csum_set ( sb , group , gdp ) ;
2012-01-04 08:37:31 +04:00
err = ext4_handle_dirty_metadata ( handle , NULL , gdb_bh ) ;
if ( unlikely ( err ) ) {
ext4_std_error ( sb , err ) ;
break ;
}
/*
* We can allocate memory for mb_alloc based on the new group
* descriptor
*/
err = ext4_mb_add_groupinfo ( sb , group , gdp ) ;
if ( err )
break ;
}
return err ;
}
2012-01-04 08:41:39 +04:00
/*
* ext4_update_super ( ) updates the super block so that the newly added
* groups can be seen by the filesystem .
*
* @ sb : super block
* @ flex_gd : new added groups
*/
static void ext4_update_super ( struct super_block * sb ,
struct ext4_new_flex_group_data * flex_gd )
{
ext4_fsblk_t blocks_count = 0 ;
ext4_fsblk_t free_blocks = 0 ;
ext4_fsblk_t reserved_blocks = 0 ;
struct ext4_new_group_data * group_data = flex_gd - > groups ;
struct ext4_sb_info * sbi = EXT4_SB ( sb ) ;
struct ext4_super_block * es = sbi - > s_es ;
2012-07-23 04:23:31 +04:00
int i ;
2012-01-04 08:41:39 +04:00
BUG_ON ( flex_gd - > count = = 0 | | group_data = = NULL ) ;
/*
* Make the new blocks and inodes valid next . We do this before
* increasing the group count so that once the group is enabled ,
* all of its blocks and inodes are already valid .
*
* We always allocate group - by - group , then block - by - block or
* inode - by - inode within a group , so enabling these
* blocks / inodes before the group is live won ' t actually let us
* allocate the new space yet .
*/
for ( i = 0 ; i < flex_gd - > count ; i + + ) {
blocks_count + = group_data [ i ] . blocks_count ;
free_blocks + = group_data [ i ] . free_blocks_count ;
}
reserved_blocks = ext4_r_blocks_count ( es ) * 100 ;
2012-09-05 09:33:50 +04:00
reserved_blocks = div64_u64 ( reserved_blocks , ext4_blocks_count ( es ) ) ;
2012-01-04 08:41:39 +04:00
reserved_blocks * = blocks_count ;
do_div ( reserved_blocks , 100 ) ;
ext4_blocks_count_set ( es , ext4_blocks_count ( es ) + blocks_count ) ;
2012-03-20 23:46:11 +04:00
ext4_free_blocks_count_set ( es , ext4_free_blocks_count ( es ) + free_blocks ) ;
2012-01-04 08:41:39 +04:00
le32_add_cpu ( & es - > s_inodes_count , EXT4_INODES_PER_GROUP ( sb ) *
flex_gd - > count ) ;
2012-03-20 23:46:11 +04:00
le32_add_cpu ( & es - > s_free_inodes_count , EXT4_INODES_PER_GROUP ( sb ) *
flex_gd - > count ) ;
2012-01-04 08:41:39 +04:00
2012-09-05 09:33:50 +04:00
ext4_debug ( " free blocks count %llu " , ext4_free_blocks_count ( es ) ) ;
2012-01-04 08:41:39 +04:00
/*
* We need to protect s_groups_count against other CPUs seeing
* inconsistent state in the superblock .
*
* The precise rules we use are :
*
* * Writers must perform a smp_wmb ( ) after updating all
* dependent data and before modifying the groups count
*
* * Readers must perform an smp_rmb ( ) after reading the groups
* count and before reading any dependent data .
*
* NB . These rules can be relaxed when checking the group count
* while freeing data , as we can only allocate from a block
* group after serialising against the group count , and we can
* only then free after serialising in turn against that
* allocation .
*/
smp_wmb ( ) ;
/* Update the global fs size fields */
sbi - > s_groups_count + = flex_gd - > count ;
2013-04-22 04:19:43 +04:00
sbi - > s_blockfile_groups = min_t ( ext4_group_t , sbi - > s_groups_count ,
( EXT4_MAX_BLOCK_FILE_PHYS / EXT4_BLOCKS_PER_GROUP ( sb ) ) ) ;
2012-01-04 08:41:39 +04:00
/* Update the reserved block counts only once the new group is
* active . */
ext4_r_blocks_count_set ( es , ext4_r_blocks_count ( es ) +
reserved_blocks ) ;
/* Update the free space counts */
percpu_counter_add ( & sbi - > s_freeclusters_counter ,
2013-03-03 02:18:58 +04:00
EXT4_NUM_B2C ( sbi , free_blocks ) ) ;
2012-01-04 08:41:39 +04:00
percpu_counter_add ( & sbi - > s_freeinodes_counter ,
EXT4_INODES_PER_GROUP ( sb ) * flex_gd - > count ) ;
2012-09-05 09:33:50 +04:00
ext4_debug ( " free blocks count %llu " ,
percpu_counter_read ( & sbi - > s_freeclusters_counter ) ) ;
2015-10-17 23:18:43 +03:00
if ( ext4_has_feature_flex_bg ( sb ) & & sbi - > s_log_groups_per_flex ) {
2012-01-04 08:41:39 +04:00
ext4_group_t flex_group ;
flex_group = ext4_flex_group ( sbi , group_data [ 0 ] . group ) ;
2013-03-12 07:39:59 +04:00
atomic64_add ( EXT4_NUM_B2C ( sbi , free_blocks ) ,
& sbi - > s_flex_groups [ flex_group ] . free_clusters ) ;
2012-01-04 08:41:39 +04:00
atomic_add ( EXT4_INODES_PER_GROUP ( sb ) * flex_gd - > count ,
& sbi - > s_flex_groups [ flex_group ] . free_inodes ) ;
}
2012-07-10 00:27:05 +04:00
/*
* Update the fs overhead information
*/
ext4_calculate_overhead ( sb ) ;
2012-01-04 08:41:39 +04:00
if ( test_opt ( sb , DEBUG ) )
printk ( KERN_DEBUG " EXT4-fs: added group %u: "
" %llu blocks(%llu free %llu reserved) \n " , flex_gd - > count ,
blocks_count , free_blocks , reserved_blocks ) ;
}
2012-01-04 08:44:38 +04:00
/* Add a flex group to an fs. Ensure we handle all possible error conditions
* _before_ we start modifying the filesystem , because we cannot abort the
* transaction and not have it write the data to disk .
*/
static int ext4_flex_group_add ( struct super_block * sb ,
struct inode * resize_inode ,
struct ext4_new_flex_group_data * flex_gd )
{
struct ext4_sb_info * sbi = EXT4_SB ( sb ) ;
struct ext4_super_block * es = sbi - > s_es ;
ext4_fsblk_t o_blocks_count ;
ext4_grpblk_t last ;
ext4_group_t group ;
handle_t * handle ;
unsigned reserved_gdb ;
int err = 0 , err2 = 0 , credit ;
BUG_ON ( ! flex_gd - > count | | ! flex_gd - > groups | | ! flex_gd - > bg_flags ) ;
reserved_gdb = le16_to_cpu ( es - > s_reserved_gdt_blocks ) ;
o_blocks_count = ext4_blocks_count ( es ) ;
ext4_get_group_no_and_offset ( sb , o_blocks_count , & group , & last ) ;
BUG_ON ( last ) ;
err = setup_new_flex_group_blocks ( sb , flex_gd ) ;
if ( err )
goto exit ;
/*
* We will always be modifying at least the superblock and GDT
2015-05-03 06:58:32 +03:00
* blocks . If we are adding a group past the last current GDT block ,
2012-01-04 08:44:38 +04:00
* we will also modify the inode and the dindirect block . If we
* are adding a group with superblock / GDT backups we will also
* modify each of the reserved GDT dindirect blocks .
*/
2015-05-03 06:58:32 +03:00
credit = 3 ; /* sb, resize inode, resize inode dindirect */
/* GDT blocks */
credit + = 1 + DIV_ROUND_UP ( flex_gd - > count , EXT4_DESC_PER_BLOCK ( sb ) ) ;
credit + = reserved_gdb ; /* Reserved GDT dindirect blocks */
2013-02-09 06:59:22 +04:00
handle = ext4_journal_start_sb ( sb , EXT4_HT_RESIZE , credit ) ;
2012-01-04 08:44:38 +04:00
if ( IS_ERR ( handle ) ) {
err = PTR_ERR ( handle ) ;
goto exit ;
}
2014-05-13 06:06:43 +04:00
BUFFER_TRACE ( sbi - > s_sbh , " get_write_access " ) ;
2012-01-04 08:44:38 +04:00
err = ext4_journal_get_write_access ( handle , sbi - > s_sbh ) ;
if ( err )
goto exit_journal ;
group = flex_gd - > groups [ 0 ] . group ;
BUG_ON ( group ! = EXT4_SB ( sb ) - > s_groups_count ) ;
err = ext4_add_new_descs ( handle , sb , group ,
resize_inode , flex_gd - > count ) ;
if ( err )
goto exit_journal ;
err = ext4_setup_new_descs ( handle , sb , flex_gd ) ;
if ( err )
goto exit_journal ;
ext4_update_super ( sb , flex_gd ) ;
err = ext4_handle_dirty_super ( handle , sb ) ;
exit_journal :
err2 = ext4_journal_stop ( handle ) ;
if ( ! err )
err = err2 ;
if ( ! err ) {
2012-09-05 09:27:50 +04:00
int gdb_num = group / EXT4_DESC_PER_BLOCK ( sb ) ;
int gdb_num_end = ( ( group + flex_gd - > count - 1 ) /
EXT4_DESC_PER_BLOCK ( sb ) ) ;
2015-10-17 23:18:43 +03:00
int meta_bg = ext4_has_feature_meta_bg ( sb ) ;
2012-09-26 08:08:57 +04:00
sector_t old_gdb = 0 ;
2012-09-05 09:27:50 +04:00
2012-01-04 08:44:38 +04:00
update_backups ( sb , sbi - > s_sbh - > b_blocknr , ( char * ) es ,
2012-09-05 09:33:50 +04:00
sizeof ( struct ext4_super_block ) , 0 ) ;
2012-09-05 09:27:50 +04:00
for ( ; gdb_num < = gdb_num_end ; gdb_num + + ) {
2012-01-04 08:44:38 +04:00
struct buffer_head * gdb_bh ;
2012-09-05 09:27:50 +04:00
2012-01-04 08:44:38 +04:00
gdb_bh = sbi - > s_group_desc [ gdb_num ] ;
2012-09-26 08:08:57 +04:00
if ( old_gdb = = gdb_bh - > b_blocknr )
continue ;
2012-01-04 08:44:38 +04:00
update_backups ( sb , gdb_bh - > b_blocknr , gdb_bh - > b_data ,
2012-09-05 09:33:50 +04:00
gdb_bh - > b_size , meta_bg ) ;
2012-09-26 08:08:57 +04:00
old_gdb = gdb_bh - > b_blocknr ;
2012-01-04 08:44:38 +04:00
}
}
exit :
return err ;
}
2012-01-05 02:09:44 +04:00
static int ext4_setup_next_flex_gd ( struct super_block * sb ,
struct ext4_new_flex_group_data * flex_gd ,
ext4_fsblk_t n_blocks_count ,
unsigned long flexbg_size )
{
struct ext4_super_block * es = EXT4_SB ( sb ) - > s_es ;
struct ext4_new_group_data * group_data = flex_gd - > groups ;
ext4_fsblk_t o_blocks_count ;
ext4_group_t n_group ;
ext4_group_t group ;
ext4_group_t last_group ;
ext4_grpblk_t last ;
ext4_grpblk_t blocks_per_group ;
unsigned long i ;
blocks_per_group = EXT4_BLOCKS_PER_GROUP ( sb ) ;
o_blocks_count = ext4_blocks_count ( es ) ;
if ( o_blocks_count = = n_blocks_count )
return 0 ;
ext4_get_group_no_and_offset ( sb , o_blocks_count , & group , & last ) ;
BUG_ON ( last ) ;
ext4_get_group_no_and_offset ( sb , n_blocks_count - 1 , & n_group , & last ) ;
last_group = group | ( flexbg_size - 1 ) ;
if ( last_group > n_group )
last_group = n_group ;
flex_gd - > count = last_group - group + 1 ;
for ( i = 0 ; i < flex_gd - > count ; i + + ) {
int overhead ;
group_data [ i ] . group = group + i ;
group_data [ i ] . blocks_count = blocks_per_group ;
2012-09-05 09:33:50 +04:00
overhead = ext4_group_overhead_blocks ( sb , group + i ) ;
2012-01-05 02:09:44 +04:00
group_data [ i ] . free_blocks_count = blocks_per_group - overhead ;
2013-01-13 17:41:45 +04:00
if ( ext4_has_group_desc_csum ( sb ) ) {
2012-01-05 02:09:44 +04:00
flex_gd - > bg_flags [ i ] = EXT4_BG_BLOCK_UNINIT |
EXT4_BG_INODE_UNINIT ;
2013-01-13 17:41:45 +04:00
if ( ! test_opt ( sb , INIT_INODE_TABLE ) )
flex_gd - > bg_flags [ i ] | = EXT4_BG_INODE_ZEROED ;
} else
2012-01-05 02:09:44 +04:00
flex_gd - > bg_flags [ i ] = EXT4_BG_INODE_ZEROED ;
}
2012-04-30 02:45:10 +04:00
if ( last_group = = n_group & & ext4_has_group_desc_csum ( sb ) )
2012-01-05 02:09:44 +04:00
/* We need to initialize block bitmap of last group. */
flex_gd - > bg_flags [ i - 1 ] & = ~ EXT4_BG_BLOCK_UNINIT ;
if ( ( last_group = = n_group ) & & ( last ! = blocks_per_group - 1 ) ) {
group_data [ i - 1 ] . blocks_count = last + 1 ;
group_data [ i - 1 ] . free_blocks_count - = blocks_per_group -
last - 1 ;
}
return 1 ;
}
2006-10-11 12:20:50 +04:00
/* Add group descriptor data to an existing or new group descriptor block.
* Ensure we handle all possible error conditions _before_ we start modifying
* the filesystem , because we cannot abort the transaction and not have it
* write the data to disk .
*
* If we are on a GDT block boundary , we need to get the reserved GDT block .
* Otherwise , we may need to add backup GDT blocks for a sparse group .
*
* We only need to hold the superblock lock while we are actually adding
* in the new group ' s counts to the superblock . Prior to that we have
* not really " added " the group at all . We re - check that we are still
* adding in the last group in case things have changed since verifying .
*/
2006-10-11 12:20:53 +04:00
int ext4_group_add ( struct super_block * sb , struct ext4_new_group_data * input )
2006-10-11 12:20:50 +04:00
{
2012-01-05 02:09:50 +04:00
struct ext4_new_flex_group_data flex_gd ;
2006-10-11 12:20:53 +04:00
struct ext4_sb_info * sbi = EXT4_SB ( sb ) ;
struct ext4_super_block * es = sbi - > s_es ;
int reserved_gdb = ext4_bg_has_super ( sb , input - > group ) ?
2006-10-11 12:20:50 +04:00
le16_to_cpu ( es - > s_reserved_gdt_blocks ) : 0 ;
struct inode * inode = NULL ;
2013-06-17 16:56:26 +04:00
int gdb_off ;
2012-01-05 02:09:50 +04:00
int err ;
__u16 bg_flags = 0 ;
2006-10-11 12:20:50 +04:00
2006-10-11 12:20:53 +04:00
gdb_off = input - > group % EXT4_DESC_PER_BLOCK ( sb ) ;
2006-10-11 12:20:50 +04:00
2015-10-17 23:18:43 +03:00
if ( gdb_off = = 0 & & ! ext4_has_feature_sparse_super ( sb ) ) {
2010-02-15 22:19:27 +03:00
ext4_warning ( sb , " Can't resize non-sparse filesystem further " ) ;
2006-10-11 12:20:50 +04:00
return - EPERM ;
}
2006-10-11 12:21:10 +04:00
if ( ext4_blocks_count ( es ) + input - > blocks_count <
ext4_blocks_count ( es ) ) {
2010-02-15 22:19:27 +03:00
ext4_warning ( sb , " blocks_count overflow " ) ;
2006-10-11 12:20:50 +04:00
return - EINVAL ;
}
2006-10-11 12:20:53 +04:00
if ( le32_to_cpu ( es - > s_inodes_count ) + EXT4_INODES_PER_GROUP ( sb ) <
2006-10-11 12:20:50 +04:00
le32_to_cpu ( es - > s_inodes_count ) ) {
2010-02-15 22:19:27 +03:00
ext4_warning ( sb , " inodes_count overflow " ) ;
2006-10-11 12:20:50 +04:00
return - EINVAL ;
}
if ( reserved_gdb | | gdb_off = = 0 ) {
2015-10-17 23:18:43 +03:00
if ( ext4_has_feature_resize_inode ( sb ) | |
! le16_to_cpu ( es - > s_reserved_gdt_blocks ) ) {
2010-02-15 22:19:27 +03:00
ext4_warning ( sb ,
2006-10-11 12:20:50 +04:00
" No reserved GDT blocks, can't resize " ) ;
return - EPERM ;
}
2008-02-07 11:15:37 +03:00
inode = ext4_iget ( sb , EXT4_RESIZE_INO ) ;
if ( IS_ERR ( inode ) ) {
2010-02-15 22:19:27 +03:00
ext4_warning ( sb , " Error opening resize inode " ) ;
2008-02-07 11:15:37 +03:00
return PTR_ERR ( inode ) ;
2006-10-11 12:20:50 +04:00
}
}
2009-01-06 05:36:19 +03:00
2012-01-05 02:09:50 +04:00
err = verify_group_input ( sb , input ) ;
2009-09-10 07:50:17 +04:00
if ( err )
2012-01-05 02:09:50 +04:00
goto out ;
2006-10-11 12:20:50 +04:00
2012-09-05 09:29:50 +04:00
err = ext4_alloc_flex_bg_array ( sb , input - > group + 1 ) ;
if ( err )
2013-01-13 17:41:45 +04:00
goto out ;
2012-09-05 09:29:50 +04:00
2012-09-05 09:31:50 +04:00
err = ext4_mb_alloc_groupinfo ( sb , input - > group + 1 ) ;
if ( err )
goto out ;
2012-01-05 02:09:50 +04:00
flex_gd . count = 1 ;
flex_gd . groups = input ;
flex_gd . bg_flags = & bg_flags ;
err = ext4_flex_group_add ( sb , inode , & flex_gd ) ;
out :
2006-10-11 12:20:50 +04:00
iput ( inode ) ;
return err ;
2006-10-11 12:20:53 +04:00
} /* ext4_group_add */
2006-10-11 12:20:50 +04:00
2012-01-04 08:18:50 +04:00
/*
* extend a group without checking assuming that checking has been done .
*/
static int ext4_group_extend_no_check ( struct super_block * sb ,
ext4_fsblk_t o_blocks_count , ext4_grpblk_t add )
{
struct ext4_super_block * es = EXT4_SB ( sb ) - > s_es ;
handle_t * handle ;
int err = 0 , err2 ;
/* We will update the superblock, one block bitmap, and
* one group descriptor via ext4_group_add_blocks ( ) .
*/
2013-02-09 06:59:22 +04:00
handle = ext4_journal_start_sb ( sb , EXT4_HT_RESIZE , 3 ) ;
2012-01-04 08:18:50 +04:00
if ( IS_ERR ( handle ) ) {
err = PTR_ERR ( handle ) ;
ext4_warning ( sb , " error %d on journal start " , err ) ;
return err ;
}
2014-05-13 06:06:43 +04:00
BUFFER_TRACE ( EXT4_SB ( sb ) - > s_sbh , " get_write_access " ) ;
2012-01-04 08:18:50 +04:00
err = ext4_journal_get_write_access ( handle , EXT4_SB ( sb ) - > s_sbh ) ;
if ( err ) {
ext4_warning ( sb , " error %d on journal write access " , err ) ;
goto errout ;
}
ext4_blocks_count_set ( es , o_blocks_count + add ) ;
2012-03-20 23:46:11 +04:00
ext4_free_blocks_count_set ( es , ext4_free_blocks_count ( es ) + add ) ;
2012-01-04 08:18:50 +04:00
ext4_debug ( " freeing blocks %llu through %llu \n " , o_blocks_count ,
o_blocks_count + add ) ;
/* We add the blocks to the bitmap and set the group need init bit */
err = ext4_group_add_blocks ( handle , sb , o_blocks_count , add ) ;
if ( err )
goto errout ;
ext4_handle_dirty_super ( handle , sb ) ;
ext4_debug ( " freed blocks %llu through %llu \n " , o_blocks_count ,
o_blocks_count + add ) ;
errout :
err2 = ext4_journal_stop ( handle ) ;
if ( err2 & & ! err )
err = err2 ;
if ( ! err ) {
if ( test_opt ( sb , DEBUG ) )
printk ( KERN_DEBUG " EXT4-fs: extended group to %llu "
" blocks \n " , ext4_blocks_count ( es ) ) ;
2013-07-01 16:12:08 +04:00
update_backups ( sb , EXT4_SB ( sb ) - > s_sbh - > b_blocknr ,
2012-09-05 09:33:50 +04:00
( char * ) es , sizeof ( struct ext4_super_block ) , 0 ) ;
2012-01-04 08:18:50 +04:00
}
return err ;
}
2008-07-27 00:15:44 +04:00
/*
* Extend the filesystem to the new number of blocks specified . This entry
2006-10-11 12:20:50 +04:00
* point is only used to extend the current filesystem to the end of the last
* existing group . It can be accessed via ioctl , or by " remount,resize=<size> "
* for emergencies ( because it has no dependencies on reserved blocks ) .
*
2006-10-11 12:20:53 +04:00
* If we _really_ wanted , we could use default values to call ext4_group_add ( )
2006-10-11 12:20:50 +04:00
* allow the " remount " trick to work for arbitrary resizing , assuming enough
* GDT blocks are reserved to grow to the desired size .
*/
2006-10-11 12:20:53 +04:00
int ext4_group_extend ( struct super_block * sb , struct ext4_super_block * es ,
ext4_fsblk_t n_blocks_count )
2006-10-11 12:20:50 +04:00
{
2006-10-11 12:20:53 +04:00
ext4_fsblk_t o_blocks_count ;
ext4_grpblk_t last ;
ext4_grpblk_t add ;
2008-09-09 06:25:24 +04:00
struct buffer_head * bh ;
2012-01-05 02:09:48 +04:00
int err ;
2008-07-12 03:27:31 +04:00
ext4_group_t group ;
2006-10-11 12:20:50 +04:00
2006-10-11 12:21:10 +04:00
o_blocks_count = ext4_blocks_count ( es ) ;
2006-10-11 12:20:50 +04:00
if ( test_opt ( sb , DEBUG ) )
2012-03-20 07:41:49 +04:00
ext4_msg ( sb , KERN_DEBUG ,
" extending last group from %llu to %llu blocks " ,
o_blocks_count , n_blocks_count ) ;
2006-10-11 12:20:50 +04:00
if ( n_blocks_count = = 0 | | n_blocks_count = = o_blocks_count )
return 0 ;
if ( n_blocks_count > ( sector_t ) ( ~ 0ULL ) > > ( sb - > s_blocksize_bits - 9 ) ) {
2012-03-20 07:41:49 +04:00
ext4_msg ( sb , KERN_ERR ,
" filesystem too large to resize to %llu blocks safely " ,
n_blocks_count ) ;
2006-10-11 12:20:50 +04:00
if ( sizeof ( sector_t ) < 8 )
2010-02-15 22:19:27 +03:00
ext4_warning ( sb , " CONFIG_LBDAF not enabled " ) ;
2006-10-11 12:20:50 +04:00
return - EINVAL ;
}
if ( n_blocks_count < o_blocks_count ) {
2010-02-15 22:19:27 +03:00
ext4_warning ( sb , " can't shrink FS - resize aborted " ) ;
2011-07-27 05:35:44 +04:00
return - EINVAL ;
2006-10-11 12:20:50 +04:00
}
/* Handle the remaining blocks in the last group only. */
2008-07-12 03:27:31 +04:00
ext4_get_group_no_and_offset ( sb , o_blocks_count , & group , & last ) ;
2006-10-11 12:20:50 +04:00
if ( last = = 0 ) {
2010-02-15 22:19:27 +03:00
ext4_warning ( sb , " need to use ext2online to resize further " ) ;
2006-10-11 12:20:50 +04:00
return - EPERM ;
}
2006-10-11 12:20:53 +04:00
add = EXT4_BLOCKS_PER_GROUP ( sb ) - last ;
2006-10-11 12:20:50 +04:00
if ( o_blocks_count + add < o_blocks_count ) {
2010-02-15 22:19:27 +03:00
ext4_warning ( sb , " blocks_count overflow " ) ;
2006-10-11 12:20:50 +04:00
return - EINVAL ;
}
if ( o_blocks_count + add > n_blocks_count )
add = n_blocks_count - o_blocks_count ;
if ( o_blocks_count + add < n_blocks_count )
2010-02-15 22:19:27 +03:00
ext4_warning ( sb , " will only finish group (%llu blocks, %u new) " ,
2006-10-11 12:20:50 +04:00
o_blocks_count + add , add ) ;
/* See if the device is actually as big as what was requested */
2008-07-27 00:15:44 +04:00
bh = sb_bread ( sb , o_blocks_count + add - 1 ) ;
2006-10-11 12:20:50 +04:00
if ( ! bh ) {
2010-02-15 22:19:27 +03:00
ext4_warning ( sb , " can't read last block, resize aborted " ) ;
2006-10-11 12:20:50 +04:00
return - ENOSPC ;
}
brelse ( bh ) ;
2012-01-05 02:09:48 +04:00
err = ext4_group_extend_no_check ( sb , o_blocks_count , add ) ;
2006-10-11 12:20:50 +04:00
return err ;
2006-10-11 12:20:53 +04:00
} /* ext4_group_extend */
2012-01-05 02:09:44 +04:00
2012-09-13 18:19:24 +04:00
static int num_desc_blocks ( struct super_block * sb , ext4_group_t groups )
{
return ( groups + EXT4_DESC_PER_BLOCK ( sb ) - 1 ) / EXT4_DESC_PER_BLOCK ( sb ) ;
}
/*
* Release the resize inode and drop the resize_inode feature if there
* are no more reserved gdt blocks , and then convert the file system
* to enable meta_bg
*/
static int ext4_convert_meta_bg ( struct super_block * sb , struct inode * inode )
{
handle_t * handle ;
struct ext4_sb_info * sbi = EXT4_SB ( sb ) ;
struct ext4_super_block * es = sbi - > s_es ;
2012-09-19 08:55:56 +04:00
struct ext4_inode_info * ei = EXT4_I ( inode ) ;
2012-09-13 18:19:24 +04:00
ext4_fsblk_t nr ;
int i , ret , err = 0 ;
int credits = 1 ;
ext4_msg ( sb , KERN_INFO , " Converting file system to meta_bg " ) ;
2012-09-19 08:55:56 +04:00
if ( inode ) {
2012-09-13 18:19:24 +04:00
if ( es - > s_reserved_gdt_blocks ) {
ext4_error ( sb , " Unexpected non-zero "
" s_reserved_gdt_blocks " ) ;
return - EPERM ;
}
/* Do a quick sanity check of the resize inode */
if ( inode - > i_blocks ! = 1 < < ( inode - > i_blkbits - 9 ) )
goto invalid_resize_inode ;
for ( i = 0 ; i < EXT4_N_BLOCKS ; i + + ) {
if ( i = = EXT4_DIND_BLOCK ) {
if ( ei - > i_data [ i ] )
continue ;
else
goto invalid_resize_inode ;
}
if ( ei - > i_data [ i ] )
goto invalid_resize_inode ;
}
credits + = 3 ; /* block bitmap, bg descriptor, resize inode */
}
2013-02-09 06:59:22 +04:00
handle = ext4_journal_start_sb ( sb , EXT4_HT_RESIZE , credits ) ;
2012-09-13 18:19:24 +04:00
if ( IS_ERR ( handle ) )
return PTR_ERR ( handle ) ;
2014-05-13 06:06:43 +04:00
BUFFER_TRACE ( sbi - > s_sbh , " get_write_access " ) ;
2012-09-13 18:19:24 +04:00
err = ext4_journal_get_write_access ( handle , sbi - > s_sbh ) ;
if ( err )
goto errout ;
2015-10-17 23:18:43 +03:00
ext4_clear_feature_resize_inode ( sb ) ;
ext4_set_feature_meta_bg ( sb ) ;
2012-09-13 18:19:24 +04:00
sbi - > s_es - > s_first_meta_bg =
cpu_to_le32 ( num_desc_blocks ( sb , sbi - > s_groups_count ) ) ;
err = ext4_handle_dirty_super ( handle , sb ) ;
if ( err ) {
ext4_std_error ( sb , err ) ;
goto errout ;
}
if ( inode ) {
nr = le32_to_cpu ( ei - > i_data [ EXT4_DIND_BLOCK ] ) ;
ext4_free_blocks ( handle , inode , NULL , nr , 1 ,
EXT4_FREE_BLOCKS_METADATA |
EXT4_FREE_BLOCKS_FORGET ) ;
ei - > i_data [ EXT4_DIND_BLOCK ] = 0 ;
inode - > i_blocks = 0 ;
err = ext4_mark_inode_dirty ( handle , inode ) ;
if ( err )
ext4_std_error ( sb , err ) ;
}
errout :
ret = ext4_journal_stop ( handle ) ;
if ( ! err )
err = ret ;
return ret ;
invalid_resize_inode :
ext4_error ( sb , " corrupted/inconsistent resize inode " ) ;
return - EINVAL ;
}
2012-01-05 02:09:44 +04:00
/*
* ext4_resize_fs ( ) resizes a fs to new size specified by @ n_blocks_count
*
* @ sb : super block of the fs to be resized
* @ n_blocks_count : the number of blocks resides in the resized fs
*/
int ext4_resize_fs ( struct super_block * sb , ext4_fsblk_t n_blocks_count )
{
struct ext4_new_flex_group_data * flex_gd = NULL ;
struct ext4_sb_info * sbi = EXT4_SB ( sb ) ;
struct ext4_super_block * es = sbi - > s_es ;
struct buffer_head * bh ;
2012-09-05 09:33:50 +04:00
struct inode * resize_inode = NULL ;
ext4_grpblk_t add , offset ;
2012-01-05 02:09:44 +04:00
unsigned long n_desc_blocks ;
unsigned long o_desc_blocks ;
2012-09-05 09:33:50 +04:00
ext4_group_t o_group ;
ext4_group_t n_group ;
ext4_fsblk_t o_blocks_count ;
2012-09-13 18:19:24 +04:00
ext4_fsblk_t n_blocks_count_retry = 0 ;
2012-09-13 18:24:21 +04:00
unsigned long last_update_time = 0 ;
2012-09-05 09:29:50 +04:00
int err = 0 , flexbg_size = 1 < < sbi - > s_log_groups_per_flex ;
2012-09-05 09:33:50 +04:00
int meta_bg ;
2012-01-05 02:09:44 +04:00
2012-09-19 08:55:56 +04:00
/* See if the device is actually as big as what was requested */
bh = sb_bread ( sb , n_blocks_count - 1 ) ;
if ( ! bh ) {
ext4_warning ( sb , " can't read last block, resize aborted " ) ;
return - ENOSPC ;
}
brelse ( bh ) ;
2012-09-13 18:19:24 +04:00
retry :
2012-01-05 02:09:44 +04:00
o_blocks_count = ext4_blocks_count ( es ) ;
2012-09-19 08:55:56 +04:00
ext4_msg ( sb , KERN_INFO , " resizing filesystem from %llu "
" to %llu blocks " , o_blocks_count , n_blocks_count ) ;
2012-01-05 02:09:44 +04:00
if ( n_blocks_count < o_blocks_count ) {
/* On-line shrinking not supported */
ext4_warning ( sb , " can't shrink FS - resize aborted " ) ;
return - EINVAL ;
}
if ( n_blocks_count = = o_blocks_count )
/* Nothing need to do */
return 0 ;
2013-04-04 07:32:34 +04:00
n_group = ext4_get_group_number ( sb , n_blocks_count - 1 ) ;
2013-04-22 06:56:32 +04:00
if ( n_group > ( 0xFFFFFFFFUL / EXT4_INODES_PER_GROUP ( sb ) ) ) {
ext4_warning ( sb , " resize would cause inodes_count overflow " ) ;
return - EINVAL ;
}
2012-02-21 08:02:06 +04:00
ext4_get_group_no_and_offset ( sb , o_blocks_count - 1 , & o_group , & offset ) ;
2012-01-05 02:09:44 +04:00
2012-09-13 18:19:24 +04:00
n_desc_blocks = num_desc_blocks ( sb , n_group + 1 ) ;
o_desc_blocks = num_desc_blocks ( sb , sbi - > s_groups_count ) ;
2012-01-05 02:09:44 +04:00
2015-10-17 23:18:43 +03:00
meta_bg = ext4_has_feature_meta_bg ( sb ) ;
2012-01-05 02:09:44 +04:00
2015-10-17 23:18:43 +03:00
if ( ext4_has_feature_resize_inode ( sb ) ) {
2012-09-05 09:33:50 +04:00
if ( meta_bg ) {
ext4_error ( sb , " resize_inode and meta_bg enabled "
" simultaneously " ) ;
return - EINVAL ;
}
2012-09-13 18:19:24 +04:00
if ( n_desc_blocks > o_desc_blocks +
le16_to_cpu ( es - > s_reserved_gdt_blocks ) ) {
n_blocks_count_retry = n_blocks_count ;
n_desc_blocks = o_desc_blocks +
le16_to_cpu ( es - > s_reserved_gdt_blocks ) ;
n_group = n_desc_blocks * EXT4_DESC_PER_BLOCK ( sb ) ;
n_blocks_count = n_group * EXT4_BLOCKS_PER_GROUP ( sb ) ;
n_group - - ; /* set to last group number */
2012-09-05 09:33:50 +04:00
}
2012-09-13 18:19:24 +04:00
if ( ! resize_inode )
resize_inode = ext4_iget ( sb , EXT4_RESIZE_INO ) ;
2012-09-05 09:33:50 +04:00
if ( IS_ERR ( resize_inode ) ) {
ext4_warning ( sb , " Error opening resize inode " ) ;
return PTR_ERR ( resize_inode ) ;
}
2012-09-13 18:19:24 +04:00
}
2012-09-19 08:55:56 +04:00
if ( ( ! resize_inode & & ! meta_bg ) | | n_blocks_count = = o_blocks_count ) {
2012-09-13 18:19:24 +04:00
err = ext4_convert_meta_bg ( sb , resize_inode ) ;
if ( err )
goto out ;
if ( resize_inode ) {
iput ( resize_inode ) ;
resize_inode = NULL ;
}
if ( n_blocks_count_retry ) {
n_blocks_count = n_blocks_count_retry ;
n_blocks_count_retry = 0 ;
goto retry ;
}
2012-01-05 02:09:44 +04:00
}
2012-02-21 08:02:06 +04:00
/* extend the last group */
if ( n_group = = o_group )
add = n_blocks_count - o_blocks_count ;
else
add = EXT4_BLOCKS_PER_GROUP ( sb ) - ( offset + 1 ) ;
if ( add > 0 ) {
2012-01-05 02:09:44 +04:00
err = ext4_group_extend_no_check ( sb , o_blocks_count , add ) ;
if ( err )
goto out ;
}
2012-09-05 09:23:50 +04:00
if ( ext4_blocks_count ( es ) = = n_blocks_count )
2012-01-05 02:09:44 +04:00
goto out ;
2012-09-05 09:29:50 +04:00
err = ext4_alloc_flex_bg_array ( sb , n_group + 1 ) ;
if ( err )
return err ;
2012-09-05 09:31:50 +04:00
err = ext4_mb_alloc_groupinfo ( sb , n_group + 1 ) ;
if ( err )
goto out ;
2012-01-05 02:09:44 +04:00
flex_gd = alloc_flex_gd ( flexbg_size ) ;
if ( flex_gd = = NULL ) {
err = - ENOMEM ;
goto out ;
}
/* Add flex groups. Note that a regular group is a
* flex group with 1 group .
*/
while ( ext4_setup_next_flex_gd ( sb , flex_gd , n_blocks_count ,
flexbg_size ) ) {
2012-09-13 18:24:21 +04:00
if ( jiffies - last_update_time > HZ * 10 ) {
if ( last_update_time )
ext4_msg ( sb , KERN_INFO ,
" resized to %llu blocks " ,
ext4_blocks_count ( es ) ) ;
last_update_time = jiffies ;
}
2012-09-05 09:21:50 +04:00
if ( ext4_alloc_group_tables ( sb , flex_gd , flexbg_size ) ! = 0 )
break ;
2012-01-05 02:09:44 +04:00
err = ext4_flex_group_add ( sb , resize_inode , flex_gd ) ;
if ( unlikely ( err ) )
break ;
}
2012-09-13 18:19:24 +04:00
if ( ! err & & n_blocks_count_retry ) {
n_blocks_count = n_blocks_count_retry ;
n_blocks_count_retry = 0 ;
free_flex_gd ( flex_gd ) ;
flex_gd = NULL ;
goto retry ;
}
2012-01-05 02:09:44 +04:00
out :
if ( flex_gd )
free_flex_gd ( flex_gd ) ;
2012-09-05 09:33:50 +04:00
if ( resize_inode ! = NULL )
iput ( resize_inode ) ;
2012-09-19 08:55:56 +04:00
ext4_msg ( sb , KERN_INFO , " resized filesystem to %llu " , n_blocks_count ) ;
2012-01-05 02:09:44 +04:00
return err ;
}