18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * linux/fs/ext4/resize.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Support for resizing an ext4 filesystem while it is mounted. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (C) 2001, 2002 Andreas Dilger <adilger@clusterfs.com> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * This could probably be made into a module, because it is not often in use. 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#define EXT4FS_DEBUG 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <linux/errno.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include "ext4_jbd2.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistruct ext4_rcu_ptr { 218c2ecf20Sopenharmony_ci struct rcu_head rcu; 228c2ecf20Sopenharmony_ci void *ptr; 238c2ecf20Sopenharmony_ci}; 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistatic void ext4_rcu_ptr_callback(struct rcu_head *head) 268c2ecf20Sopenharmony_ci{ 278c2ecf20Sopenharmony_ci struct ext4_rcu_ptr *ptr; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci ptr = container_of(head, struct ext4_rcu_ptr, rcu); 308c2ecf20Sopenharmony_ci kvfree(ptr->ptr); 318c2ecf20Sopenharmony_ci kfree(ptr); 328c2ecf20Sopenharmony_ci} 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_civoid ext4_kvfree_array_rcu(void *to_free) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci struct ext4_rcu_ptr *ptr = kzalloc(sizeof(*ptr), GFP_KERNEL); 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci if (ptr) { 398c2ecf20Sopenharmony_ci ptr->ptr = to_free; 408c2ecf20Sopenharmony_ci call_rcu(&ptr->rcu, ext4_rcu_ptr_callback); 418c2ecf20Sopenharmony_ci return; 428c2ecf20Sopenharmony_ci } 438c2ecf20Sopenharmony_ci synchronize_rcu(); 448c2ecf20Sopenharmony_ci kvfree(to_free); 458c2ecf20Sopenharmony_ci} 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ciint ext4_resize_begin(struct super_block *sb) 488c2ecf20Sopenharmony_ci{ 498c2ecf20Sopenharmony_ci struct ext4_sb_info *sbi = EXT4_SB(sb); 508c2ecf20Sopenharmony_ci int ret = 0; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_RESOURCE)) 538c2ecf20Sopenharmony_ci return -EPERM; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci /* 568c2ecf20Sopenharmony_ci * If the reserved GDT blocks is non-zero, the resize_inode feature 578c2ecf20Sopenharmony_ci * should always be set. 588c2ecf20Sopenharmony_ci */ 598c2ecf20Sopenharmony_ci if (EXT4_SB(sb)->s_es->s_reserved_gdt_blocks && 608c2ecf20Sopenharmony_ci !ext4_has_feature_resize_inode(sb)) { 618c2ecf20Sopenharmony_ci ext4_error(sb, "resize_inode disabled but reserved GDT blocks non-zero"); 628c2ecf20Sopenharmony_ci return -EFSCORRUPTED; 638c2ecf20Sopenharmony_ci } 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci /* 668c2ecf20Sopenharmony_ci * If we are not using the primary superblock/GDT copy don't resize, 678c2ecf20Sopenharmony_ci * because the user tools have no way of handling this. Probably a 688c2ecf20Sopenharmony_ci * bad time to do it anyways. 698c2ecf20Sopenharmony_ci */ 708c2ecf20Sopenharmony_ci if (EXT4_B2C(sbi, sbi->s_sbh->b_blocknr) != 718c2ecf20Sopenharmony_ci le32_to_cpu(EXT4_SB(sb)->s_es->s_first_data_block)) { 728c2ecf20Sopenharmony_ci ext4_warning(sb, "won't resize using backup superblock at %llu", 738c2ecf20Sopenharmony_ci (unsigned long long)EXT4_SB(sb)->s_sbh->b_blocknr); 748c2ecf20Sopenharmony_ci return -EPERM; 758c2ecf20Sopenharmony_ci } 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci /* 788c2ecf20Sopenharmony_ci * We are not allowed to do online-resizing on a filesystem mounted 798c2ecf20Sopenharmony_ci * with error, because it can destroy the filesystem easily. 808c2ecf20Sopenharmony_ci */ 818c2ecf20Sopenharmony_ci if (EXT4_SB(sb)->s_mount_state & EXT4_ERROR_FS) { 828c2ecf20Sopenharmony_ci ext4_warning(sb, "There are errors in the filesystem, " 838c2ecf20Sopenharmony_ci "so online resizing is not allowed"); 848c2ecf20Sopenharmony_ci return -EPERM; 858c2ecf20Sopenharmony_ci } 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci if (ext4_has_feature_sparse_super2(sb)) { 888c2ecf20Sopenharmony_ci ext4_msg(sb, KERN_ERR, "Online resizing not supported with sparse_super2"); 898c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci if (test_and_set_bit_lock(EXT4_FLAGS_RESIZING, 938c2ecf20Sopenharmony_ci &EXT4_SB(sb)->s_ext4_flags)) 948c2ecf20Sopenharmony_ci ret = -EBUSY; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci return ret; 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_civoid ext4_resize_end(struct super_block *sb) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci clear_bit_unlock(EXT4_FLAGS_RESIZING, &EXT4_SB(sb)->s_ext4_flags); 1028c2ecf20Sopenharmony_ci smp_mb__after_atomic(); 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic ext4_group_t ext4_meta_bg_first_group(struct super_block *sb, 1068c2ecf20Sopenharmony_ci ext4_group_t group) { 1078c2ecf20Sopenharmony_ci return (group >> EXT4_DESC_PER_BLOCK_BITS(sb)) << 1088c2ecf20Sopenharmony_ci EXT4_DESC_PER_BLOCK_BITS(sb); 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic ext4_fsblk_t ext4_meta_bg_first_block_no(struct super_block *sb, 1128c2ecf20Sopenharmony_ci ext4_group_t group) { 1138c2ecf20Sopenharmony_ci group = ext4_meta_bg_first_group(sb, group); 1148c2ecf20Sopenharmony_ci return ext4_group_first_block_no(sb, group); 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic ext4_grpblk_t ext4_group_overhead_blocks(struct super_block *sb, 1188c2ecf20Sopenharmony_ci ext4_group_t group) { 1198c2ecf20Sopenharmony_ci ext4_grpblk_t overhead; 1208c2ecf20Sopenharmony_ci overhead = ext4_bg_num_gdb(sb, group); 1218c2ecf20Sopenharmony_ci if (ext4_bg_has_super(sb, group)) 1228c2ecf20Sopenharmony_ci overhead += 1 + 1238c2ecf20Sopenharmony_ci le16_to_cpu(EXT4_SB(sb)->s_es->s_reserved_gdt_blocks); 1248c2ecf20Sopenharmony_ci return overhead; 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci#define outside(b, first, last) ((b) < (first) || (b) >= (last)) 1288c2ecf20Sopenharmony_ci#define inside(b, first, last) ((b) >= (first) && (b) < (last)) 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic int verify_group_input(struct super_block *sb, 1318c2ecf20Sopenharmony_ci struct ext4_new_group_data *input) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci struct ext4_sb_info *sbi = EXT4_SB(sb); 1348c2ecf20Sopenharmony_ci struct ext4_super_block *es = sbi->s_es; 1358c2ecf20Sopenharmony_ci ext4_fsblk_t start = ext4_blocks_count(es); 1368c2ecf20Sopenharmony_ci ext4_fsblk_t end = start + input->blocks_count; 1378c2ecf20Sopenharmony_ci ext4_group_t group = input->group; 1388c2ecf20Sopenharmony_ci ext4_fsblk_t itend = input->inode_table + sbi->s_itb_per_group; 1398c2ecf20Sopenharmony_ci unsigned overhead; 1408c2ecf20Sopenharmony_ci ext4_fsblk_t metaend; 1418c2ecf20Sopenharmony_ci struct buffer_head *bh = NULL; 1428c2ecf20Sopenharmony_ci ext4_grpblk_t free_blocks_count, offset; 1438c2ecf20Sopenharmony_ci int err = -EINVAL; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci if (group != sbi->s_groups_count) { 1468c2ecf20Sopenharmony_ci ext4_warning(sb, "Cannot add at group %u (only %u groups)", 1478c2ecf20Sopenharmony_ci input->group, sbi->s_groups_count); 1488c2ecf20Sopenharmony_ci return -EINVAL; 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci overhead = ext4_group_overhead_blocks(sb, group); 1528c2ecf20Sopenharmony_ci metaend = start + overhead; 1538c2ecf20Sopenharmony_ci input->free_clusters_count = free_blocks_count = 1548c2ecf20Sopenharmony_ci input->blocks_count - 2 - overhead - sbi->s_itb_per_group; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci if (test_opt(sb, DEBUG)) 1578c2ecf20Sopenharmony_ci printk(KERN_DEBUG "EXT4-fs: adding %s group %u: %u blocks " 1588c2ecf20Sopenharmony_ci "(%d free, %u reserved)\n", 1598c2ecf20Sopenharmony_ci ext4_bg_has_super(sb, input->group) ? "normal" : 1608c2ecf20Sopenharmony_ci "no-super", input->group, input->blocks_count, 1618c2ecf20Sopenharmony_ci free_blocks_count, input->reserved_blocks); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci ext4_get_group_no_and_offset(sb, start, NULL, &offset); 1648c2ecf20Sopenharmony_ci if (offset != 0) 1658c2ecf20Sopenharmony_ci ext4_warning(sb, "Last group not full"); 1668c2ecf20Sopenharmony_ci else if (input->reserved_blocks > input->blocks_count / 5) 1678c2ecf20Sopenharmony_ci ext4_warning(sb, "Reserved blocks too high (%u)", 1688c2ecf20Sopenharmony_ci input->reserved_blocks); 1698c2ecf20Sopenharmony_ci else if (free_blocks_count < 0) 1708c2ecf20Sopenharmony_ci ext4_warning(sb, "Bad blocks count %u", 1718c2ecf20Sopenharmony_ci input->blocks_count); 1728c2ecf20Sopenharmony_ci else if (IS_ERR(bh = ext4_sb_bread(sb, end - 1, 0))) { 1738c2ecf20Sopenharmony_ci err = PTR_ERR(bh); 1748c2ecf20Sopenharmony_ci bh = NULL; 1758c2ecf20Sopenharmony_ci ext4_warning(sb, "Cannot read last block (%llu)", 1768c2ecf20Sopenharmony_ci end - 1); 1778c2ecf20Sopenharmony_ci } else if (outside(input->block_bitmap, start, end)) 1788c2ecf20Sopenharmony_ci ext4_warning(sb, "Block bitmap not in group (block %llu)", 1798c2ecf20Sopenharmony_ci (unsigned long long)input->block_bitmap); 1808c2ecf20Sopenharmony_ci else if (outside(input->inode_bitmap, start, end)) 1818c2ecf20Sopenharmony_ci ext4_warning(sb, "Inode bitmap not in group (block %llu)", 1828c2ecf20Sopenharmony_ci (unsigned long long)input->inode_bitmap); 1838c2ecf20Sopenharmony_ci else if (outside(input->inode_table, start, end) || 1848c2ecf20Sopenharmony_ci outside(itend - 1, start, end)) 1858c2ecf20Sopenharmony_ci ext4_warning(sb, "Inode table not in group (blocks %llu-%llu)", 1868c2ecf20Sopenharmony_ci (unsigned long long)input->inode_table, itend - 1); 1878c2ecf20Sopenharmony_ci else if (input->inode_bitmap == input->block_bitmap) 1888c2ecf20Sopenharmony_ci ext4_warning(sb, "Block bitmap same as inode bitmap (%llu)", 1898c2ecf20Sopenharmony_ci (unsigned long long)input->block_bitmap); 1908c2ecf20Sopenharmony_ci else if (inside(input->block_bitmap, input->inode_table, itend)) 1918c2ecf20Sopenharmony_ci ext4_warning(sb, "Block bitmap (%llu) in inode table " 1928c2ecf20Sopenharmony_ci "(%llu-%llu)", 1938c2ecf20Sopenharmony_ci (unsigned long long)input->block_bitmap, 1948c2ecf20Sopenharmony_ci (unsigned long long)input->inode_table, itend - 1); 1958c2ecf20Sopenharmony_ci else if (inside(input->inode_bitmap, input->inode_table, itend)) 1968c2ecf20Sopenharmony_ci ext4_warning(sb, "Inode bitmap (%llu) in inode table " 1978c2ecf20Sopenharmony_ci "(%llu-%llu)", 1988c2ecf20Sopenharmony_ci (unsigned long long)input->inode_bitmap, 1998c2ecf20Sopenharmony_ci (unsigned long long)input->inode_table, itend - 1); 2008c2ecf20Sopenharmony_ci else if (inside(input->block_bitmap, start, metaend)) 2018c2ecf20Sopenharmony_ci ext4_warning(sb, "Block bitmap (%llu) in GDT table (%llu-%llu)", 2028c2ecf20Sopenharmony_ci (unsigned long long)input->block_bitmap, 2038c2ecf20Sopenharmony_ci start, metaend - 1); 2048c2ecf20Sopenharmony_ci else if (inside(input->inode_bitmap, start, metaend)) 2058c2ecf20Sopenharmony_ci ext4_warning(sb, "Inode bitmap (%llu) in GDT table (%llu-%llu)", 2068c2ecf20Sopenharmony_ci (unsigned long long)input->inode_bitmap, 2078c2ecf20Sopenharmony_ci start, metaend - 1); 2088c2ecf20Sopenharmony_ci else if (inside(input->inode_table, start, metaend) || 2098c2ecf20Sopenharmony_ci inside(itend - 1, start, metaend)) 2108c2ecf20Sopenharmony_ci ext4_warning(sb, "Inode table (%llu-%llu) overlaps GDT table " 2118c2ecf20Sopenharmony_ci "(%llu-%llu)", 2128c2ecf20Sopenharmony_ci (unsigned long long)input->inode_table, 2138c2ecf20Sopenharmony_ci itend - 1, start, metaend - 1); 2148c2ecf20Sopenharmony_ci else 2158c2ecf20Sopenharmony_ci err = 0; 2168c2ecf20Sopenharmony_ci brelse(bh); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci return err; 2198c2ecf20Sopenharmony_ci} 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci/* 2228c2ecf20Sopenharmony_ci * ext4_new_flex_group_data is used by 64bit-resize interface to add a flex 2238c2ecf20Sopenharmony_ci * group each time. 2248c2ecf20Sopenharmony_ci */ 2258c2ecf20Sopenharmony_cistruct ext4_new_flex_group_data { 2268c2ecf20Sopenharmony_ci struct ext4_new_group_data *groups; /* new_group_data for groups 2278c2ecf20Sopenharmony_ci in the flex group */ 2288c2ecf20Sopenharmony_ci __u16 *bg_flags; /* block group flags of groups 2298c2ecf20Sopenharmony_ci in @groups */ 2308c2ecf20Sopenharmony_ci ext4_group_t resize_bg; /* number of allocated 2318c2ecf20Sopenharmony_ci new_group_data */ 2328c2ecf20Sopenharmony_ci ext4_group_t count; /* number of groups in @groups 2338c2ecf20Sopenharmony_ci */ 2348c2ecf20Sopenharmony_ci}; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci/* 2378c2ecf20Sopenharmony_ci * Avoiding memory allocation failures due to too many groups added each time. 2388c2ecf20Sopenharmony_ci */ 2398c2ecf20Sopenharmony_ci#define MAX_RESIZE_BG 16384 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci/* 2428c2ecf20Sopenharmony_ci * alloc_flex_gd() allocates a ext4_new_flex_group_data with size of 2438c2ecf20Sopenharmony_ci * @flexbg_size. 2448c2ecf20Sopenharmony_ci * 2458c2ecf20Sopenharmony_ci * Returns NULL on failure otherwise address of the allocated structure. 2468c2ecf20Sopenharmony_ci */ 2478c2ecf20Sopenharmony_cistatic struct ext4_new_flex_group_data *alloc_flex_gd(unsigned int flexbg_size) 2488c2ecf20Sopenharmony_ci{ 2498c2ecf20Sopenharmony_ci struct ext4_new_flex_group_data *flex_gd; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci flex_gd = kmalloc(sizeof(*flex_gd), GFP_NOFS); 2528c2ecf20Sopenharmony_ci if (flex_gd == NULL) 2538c2ecf20Sopenharmony_ci goto out3; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci if (unlikely(flexbg_size > MAX_RESIZE_BG)) 2568c2ecf20Sopenharmony_ci flex_gd->resize_bg = MAX_RESIZE_BG; 2578c2ecf20Sopenharmony_ci else 2588c2ecf20Sopenharmony_ci flex_gd->resize_bg = flexbg_size; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci flex_gd->groups = kmalloc_array(flex_gd->resize_bg, 2618c2ecf20Sopenharmony_ci sizeof(struct ext4_new_group_data), 2628c2ecf20Sopenharmony_ci GFP_NOFS); 2638c2ecf20Sopenharmony_ci if (flexbg_size >= UINT_MAX / sizeof(struct ext4_new_group_data)) 2648c2ecf20Sopenharmony_ci goto out2; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci if (flex_gd->groups == NULL) 2678c2ecf20Sopenharmony_ci goto out2; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci flex_gd->bg_flags = kmalloc_array(flex_gd->resize_bg, sizeof(__u16), 2708c2ecf20Sopenharmony_ci GFP_NOFS); 2718c2ecf20Sopenharmony_ci if (flex_gd->bg_flags == NULL) 2728c2ecf20Sopenharmony_ci goto out1; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci return flex_gd; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ciout1: 2778c2ecf20Sopenharmony_ci kfree(flex_gd->groups); 2788c2ecf20Sopenharmony_ciout2: 2798c2ecf20Sopenharmony_ci kfree(flex_gd); 2808c2ecf20Sopenharmony_ciout3: 2818c2ecf20Sopenharmony_ci return NULL; 2828c2ecf20Sopenharmony_ci} 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_cistatic void free_flex_gd(struct ext4_new_flex_group_data *flex_gd) 2858c2ecf20Sopenharmony_ci{ 2868c2ecf20Sopenharmony_ci kfree(flex_gd->bg_flags); 2878c2ecf20Sopenharmony_ci kfree(flex_gd->groups); 2888c2ecf20Sopenharmony_ci kfree(flex_gd); 2898c2ecf20Sopenharmony_ci} 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci/* 2928c2ecf20Sopenharmony_ci * ext4_alloc_group_tables() allocates block bitmaps, inode bitmaps 2938c2ecf20Sopenharmony_ci * and inode tables for a flex group. 2948c2ecf20Sopenharmony_ci * 2958c2ecf20Sopenharmony_ci * This function is used by 64bit-resize. Note that this function allocates 2968c2ecf20Sopenharmony_ci * group tables from the 1st group of groups contained by @flexgd, which may 2978c2ecf20Sopenharmony_ci * be a partial of a flex group. 2988c2ecf20Sopenharmony_ci * 2998c2ecf20Sopenharmony_ci * @sb: super block of fs to which the groups belongs 3008c2ecf20Sopenharmony_ci * 3018c2ecf20Sopenharmony_ci * Returns 0 on a successful allocation of the metadata blocks in the 3028c2ecf20Sopenharmony_ci * block group. 3038c2ecf20Sopenharmony_ci */ 3048c2ecf20Sopenharmony_cistatic int ext4_alloc_group_tables(struct super_block *sb, 3058c2ecf20Sopenharmony_ci struct ext4_new_flex_group_data *flex_gd, 3068c2ecf20Sopenharmony_ci unsigned int flexbg_size) 3078c2ecf20Sopenharmony_ci{ 3088c2ecf20Sopenharmony_ci struct ext4_new_group_data *group_data = flex_gd->groups; 3098c2ecf20Sopenharmony_ci ext4_fsblk_t start_blk; 3108c2ecf20Sopenharmony_ci ext4_fsblk_t last_blk; 3118c2ecf20Sopenharmony_ci ext4_group_t src_group; 3128c2ecf20Sopenharmony_ci ext4_group_t bb_index = 0; 3138c2ecf20Sopenharmony_ci ext4_group_t ib_index = 0; 3148c2ecf20Sopenharmony_ci ext4_group_t it_index = 0; 3158c2ecf20Sopenharmony_ci ext4_group_t group; 3168c2ecf20Sopenharmony_ci ext4_group_t last_group; 3178c2ecf20Sopenharmony_ci unsigned overhead; 3188c2ecf20Sopenharmony_ci __u16 uninit_mask = (flexbg_size > 1) ? ~EXT4_BG_BLOCK_UNINIT : ~0; 3198c2ecf20Sopenharmony_ci int i; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci BUG_ON(flex_gd->count == 0 || group_data == NULL); 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci src_group = group_data[0].group; 3248c2ecf20Sopenharmony_ci last_group = src_group + flex_gd->count - 1; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci BUG_ON((flexbg_size > 1) && ((src_group & ~(flexbg_size - 1)) != 3278c2ecf20Sopenharmony_ci (last_group & ~(flexbg_size - 1)))); 3288c2ecf20Sopenharmony_cinext_group: 3298c2ecf20Sopenharmony_ci group = group_data[0].group; 3308c2ecf20Sopenharmony_ci if (src_group >= group_data[0].group + flex_gd->count) 3318c2ecf20Sopenharmony_ci return -ENOSPC; 3328c2ecf20Sopenharmony_ci start_blk = ext4_group_first_block_no(sb, src_group); 3338c2ecf20Sopenharmony_ci last_blk = start_blk + group_data[src_group - group].blocks_count; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci overhead = ext4_group_overhead_blocks(sb, src_group); 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci start_blk += overhead; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci /* We collect contiguous blocks as much as possible. */ 3408c2ecf20Sopenharmony_ci src_group++; 3418c2ecf20Sopenharmony_ci for (; src_group <= last_group; src_group++) { 3428c2ecf20Sopenharmony_ci overhead = ext4_group_overhead_blocks(sb, src_group); 3438c2ecf20Sopenharmony_ci if (overhead == 0) 3448c2ecf20Sopenharmony_ci last_blk += group_data[src_group - group].blocks_count; 3458c2ecf20Sopenharmony_ci else 3468c2ecf20Sopenharmony_ci break; 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci /* Allocate block bitmaps */ 3508c2ecf20Sopenharmony_ci for (; bb_index < flex_gd->count; bb_index++) { 3518c2ecf20Sopenharmony_ci if (start_blk >= last_blk) 3528c2ecf20Sopenharmony_ci goto next_group; 3538c2ecf20Sopenharmony_ci group_data[bb_index].block_bitmap = start_blk++; 3548c2ecf20Sopenharmony_ci group = ext4_get_group_number(sb, start_blk - 1); 3558c2ecf20Sopenharmony_ci group -= group_data[0].group; 3568c2ecf20Sopenharmony_ci group_data[group].mdata_blocks++; 3578c2ecf20Sopenharmony_ci flex_gd->bg_flags[group] &= uninit_mask; 3588c2ecf20Sopenharmony_ci } 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci /* Allocate inode bitmaps */ 3618c2ecf20Sopenharmony_ci for (; ib_index < flex_gd->count; ib_index++) { 3628c2ecf20Sopenharmony_ci if (start_blk >= last_blk) 3638c2ecf20Sopenharmony_ci goto next_group; 3648c2ecf20Sopenharmony_ci group_data[ib_index].inode_bitmap = start_blk++; 3658c2ecf20Sopenharmony_ci group = ext4_get_group_number(sb, start_blk - 1); 3668c2ecf20Sopenharmony_ci group -= group_data[0].group; 3678c2ecf20Sopenharmony_ci group_data[group].mdata_blocks++; 3688c2ecf20Sopenharmony_ci flex_gd->bg_flags[group] &= uninit_mask; 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci /* Allocate inode tables */ 3728c2ecf20Sopenharmony_ci for (; it_index < flex_gd->count; it_index++) { 3738c2ecf20Sopenharmony_ci unsigned int itb = EXT4_SB(sb)->s_itb_per_group; 3748c2ecf20Sopenharmony_ci ext4_fsblk_t next_group_start; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci if (start_blk + itb > last_blk) 3778c2ecf20Sopenharmony_ci goto next_group; 3788c2ecf20Sopenharmony_ci group_data[it_index].inode_table = start_blk; 3798c2ecf20Sopenharmony_ci group = ext4_get_group_number(sb, start_blk); 3808c2ecf20Sopenharmony_ci next_group_start = ext4_group_first_block_no(sb, group + 1); 3818c2ecf20Sopenharmony_ci group -= group_data[0].group; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci if (start_blk + itb > next_group_start) { 3848c2ecf20Sopenharmony_ci flex_gd->bg_flags[group + 1] &= uninit_mask; 3858c2ecf20Sopenharmony_ci overhead = start_blk + itb - next_group_start; 3868c2ecf20Sopenharmony_ci group_data[group + 1].mdata_blocks += overhead; 3878c2ecf20Sopenharmony_ci itb -= overhead; 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci group_data[group].mdata_blocks += itb; 3918c2ecf20Sopenharmony_ci flex_gd->bg_flags[group] &= uninit_mask; 3928c2ecf20Sopenharmony_ci start_blk += EXT4_SB(sb)->s_itb_per_group; 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci /* Update free clusters count to exclude metadata blocks */ 3968c2ecf20Sopenharmony_ci for (i = 0; i < flex_gd->count; i++) { 3978c2ecf20Sopenharmony_ci group_data[i].free_clusters_count -= 3988c2ecf20Sopenharmony_ci EXT4_NUM_B2C(EXT4_SB(sb), 3998c2ecf20Sopenharmony_ci group_data[i].mdata_blocks); 4008c2ecf20Sopenharmony_ci } 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci if (test_opt(sb, DEBUG)) { 4038c2ecf20Sopenharmony_ci int i; 4048c2ecf20Sopenharmony_ci group = group_data[0].group; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci printk(KERN_DEBUG "EXT4-fs: adding a flex group with " 4078c2ecf20Sopenharmony_ci "%u groups, flexbg size is %u:\n", flex_gd->count, 4088c2ecf20Sopenharmony_ci flexbg_size); 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci for (i = 0; i < flex_gd->count; i++) { 4118c2ecf20Sopenharmony_ci ext4_debug( 4128c2ecf20Sopenharmony_ci "adding %s group %u: %u blocks (%u free, %u mdata blocks)\n", 4138c2ecf20Sopenharmony_ci ext4_bg_has_super(sb, group + i) ? "normal" : 4148c2ecf20Sopenharmony_ci "no-super", group + i, 4158c2ecf20Sopenharmony_ci group_data[i].blocks_count, 4168c2ecf20Sopenharmony_ci group_data[i].free_clusters_count, 4178c2ecf20Sopenharmony_ci group_data[i].mdata_blocks); 4188c2ecf20Sopenharmony_ci } 4198c2ecf20Sopenharmony_ci } 4208c2ecf20Sopenharmony_ci return 0; 4218c2ecf20Sopenharmony_ci} 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_cistatic struct buffer_head *bclean(handle_t *handle, struct super_block *sb, 4248c2ecf20Sopenharmony_ci ext4_fsblk_t blk) 4258c2ecf20Sopenharmony_ci{ 4268c2ecf20Sopenharmony_ci struct buffer_head *bh; 4278c2ecf20Sopenharmony_ci int err; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci bh = sb_getblk(sb, blk); 4308c2ecf20Sopenharmony_ci if (unlikely(!bh)) 4318c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 4328c2ecf20Sopenharmony_ci BUFFER_TRACE(bh, "get_write_access"); 4338c2ecf20Sopenharmony_ci if ((err = ext4_journal_get_write_access(handle, bh))) { 4348c2ecf20Sopenharmony_ci brelse(bh); 4358c2ecf20Sopenharmony_ci bh = ERR_PTR(err); 4368c2ecf20Sopenharmony_ci } else { 4378c2ecf20Sopenharmony_ci memset(bh->b_data, 0, sb->s_blocksize); 4388c2ecf20Sopenharmony_ci set_buffer_uptodate(bh); 4398c2ecf20Sopenharmony_ci } 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci return bh; 4428c2ecf20Sopenharmony_ci} 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_cistatic int ext4_resize_ensure_credits_batch(handle_t *handle, int credits) 4458c2ecf20Sopenharmony_ci{ 4468c2ecf20Sopenharmony_ci return ext4_journal_ensure_credits_fn(handle, credits, 4478c2ecf20Sopenharmony_ci EXT4_MAX_TRANS_DATA, 0, 0); 4488c2ecf20Sopenharmony_ci} 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci/* 4518c2ecf20Sopenharmony_ci * set_flexbg_block_bitmap() mark clusters [@first_cluster, @last_cluster] used. 4528c2ecf20Sopenharmony_ci * 4538c2ecf20Sopenharmony_ci * Helper function for ext4_setup_new_group_blocks() which set . 4548c2ecf20Sopenharmony_ci * 4558c2ecf20Sopenharmony_ci * @sb: super block 4568c2ecf20Sopenharmony_ci * @handle: journal handle 4578c2ecf20Sopenharmony_ci * @flex_gd: flex group data 4588c2ecf20Sopenharmony_ci */ 4598c2ecf20Sopenharmony_cistatic int set_flexbg_block_bitmap(struct super_block *sb, handle_t *handle, 4608c2ecf20Sopenharmony_ci struct ext4_new_flex_group_data *flex_gd, 4618c2ecf20Sopenharmony_ci ext4_fsblk_t first_cluster, ext4_fsblk_t last_cluster) 4628c2ecf20Sopenharmony_ci{ 4638c2ecf20Sopenharmony_ci struct ext4_sb_info *sbi = EXT4_SB(sb); 4648c2ecf20Sopenharmony_ci ext4_group_t count = last_cluster - first_cluster + 1; 4658c2ecf20Sopenharmony_ci ext4_group_t count2; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci ext4_debug("mark clusters [%llu-%llu] used\n", first_cluster, 4688c2ecf20Sopenharmony_ci last_cluster); 4698c2ecf20Sopenharmony_ci for (count2 = count; count > 0; 4708c2ecf20Sopenharmony_ci count -= count2, first_cluster += count2) { 4718c2ecf20Sopenharmony_ci ext4_fsblk_t start; 4728c2ecf20Sopenharmony_ci struct buffer_head *bh; 4738c2ecf20Sopenharmony_ci ext4_group_t group; 4748c2ecf20Sopenharmony_ci int err; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci group = ext4_get_group_number(sb, EXT4_C2B(sbi, first_cluster)); 4778c2ecf20Sopenharmony_ci start = EXT4_B2C(sbi, ext4_group_first_block_no(sb, group)); 4788c2ecf20Sopenharmony_ci group -= flex_gd->groups[0].group; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci count2 = EXT4_CLUSTERS_PER_GROUP(sb) - (first_cluster - start); 4818c2ecf20Sopenharmony_ci if (count2 > count) 4828c2ecf20Sopenharmony_ci count2 = count; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci if (flex_gd->bg_flags[group] & EXT4_BG_BLOCK_UNINIT) { 4858c2ecf20Sopenharmony_ci BUG_ON(flex_gd->count > 1); 4868c2ecf20Sopenharmony_ci continue; 4878c2ecf20Sopenharmony_ci } 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci err = ext4_resize_ensure_credits_batch(handle, 1); 4908c2ecf20Sopenharmony_ci if (err < 0) 4918c2ecf20Sopenharmony_ci return err; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci bh = sb_getblk(sb, flex_gd->groups[group].block_bitmap); 4948c2ecf20Sopenharmony_ci if (unlikely(!bh)) 4958c2ecf20Sopenharmony_ci return -ENOMEM; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci BUFFER_TRACE(bh, "get_write_access"); 4988c2ecf20Sopenharmony_ci err = ext4_journal_get_write_access(handle, bh); 4998c2ecf20Sopenharmony_ci if (err) { 5008c2ecf20Sopenharmony_ci brelse(bh); 5018c2ecf20Sopenharmony_ci return err; 5028c2ecf20Sopenharmony_ci } 5038c2ecf20Sopenharmony_ci ext4_debug("mark block bitmap %#04llx (+%llu/%u)\n", 5048c2ecf20Sopenharmony_ci first_cluster, first_cluster - start, count2); 5058c2ecf20Sopenharmony_ci ext4_set_bits(bh->b_data, first_cluster - start, count2); 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci err = ext4_handle_dirty_metadata(handle, NULL, bh); 5088c2ecf20Sopenharmony_ci brelse(bh); 5098c2ecf20Sopenharmony_ci if (unlikely(err)) 5108c2ecf20Sopenharmony_ci return err; 5118c2ecf20Sopenharmony_ci } 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci return 0; 5148c2ecf20Sopenharmony_ci} 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci/* 5178c2ecf20Sopenharmony_ci * Set up the block and inode bitmaps, and the inode table for the new groups. 5188c2ecf20Sopenharmony_ci * This doesn't need to be part of the main transaction, since we are only 5198c2ecf20Sopenharmony_ci * changing blocks outside the actual filesystem. We still do journaling to 5208c2ecf20Sopenharmony_ci * ensure the recovery is correct in case of a failure just after resize. 5218c2ecf20Sopenharmony_ci * If any part of this fails, we simply abort the resize. 5228c2ecf20Sopenharmony_ci * 5238c2ecf20Sopenharmony_ci * setup_new_flex_group_blocks handles a flex group as follow: 5248c2ecf20Sopenharmony_ci * 1. copy super block and GDT, and initialize group tables if necessary. 5258c2ecf20Sopenharmony_ci * In this step, we only set bits in blocks bitmaps for blocks taken by 5268c2ecf20Sopenharmony_ci * super block and GDT. 5278c2ecf20Sopenharmony_ci * 2. allocate group tables in block bitmaps, that is, set bits in block 5288c2ecf20Sopenharmony_ci * bitmap for blocks taken by group tables. 5298c2ecf20Sopenharmony_ci */ 5308c2ecf20Sopenharmony_cistatic int setup_new_flex_group_blocks(struct super_block *sb, 5318c2ecf20Sopenharmony_ci struct ext4_new_flex_group_data *flex_gd) 5328c2ecf20Sopenharmony_ci{ 5338c2ecf20Sopenharmony_ci int group_table_count[] = {1, 1, EXT4_SB(sb)->s_itb_per_group}; 5348c2ecf20Sopenharmony_ci ext4_fsblk_t start; 5358c2ecf20Sopenharmony_ci ext4_fsblk_t block; 5368c2ecf20Sopenharmony_ci struct ext4_sb_info *sbi = EXT4_SB(sb); 5378c2ecf20Sopenharmony_ci struct ext4_super_block *es = sbi->s_es; 5388c2ecf20Sopenharmony_ci struct ext4_new_group_data *group_data = flex_gd->groups; 5398c2ecf20Sopenharmony_ci __u16 *bg_flags = flex_gd->bg_flags; 5408c2ecf20Sopenharmony_ci handle_t *handle; 5418c2ecf20Sopenharmony_ci ext4_group_t group, count; 5428c2ecf20Sopenharmony_ci struct buffer_head *bh = NULL; 5438c2ecf20Sopenharmony_ci int reserved_gdb, i, j, err = 0, err2; 5448c2ecf20Sopenharmony_ci int meta_bg; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci BUG_ON(!flex_gd->count || !group_data || 5478c2ecf20Sopenharmony_ci group_data[0].group != sbi->s_groups_count); 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci reserved_gdb = le16_to_cpu(es->s_reserved_gdt_blocks); 5508c2ecf20Sopenharmony_ci meta_bg = ext4_has_feature_meta_bg(sb); 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci /* This transaction may be extended/restarted along the way */ 5538c2ecf20Sopenharmony_ci handle = ext4_journal_start_sb(sb, EXT4_HT_RESIZE, EXT4_MAX_TRANS_DATA); 5548c2ecf20Sopenharmony_ci if (IS_ERR(handle)) 5558c2ecf20Sopenharmony_ci return PTR_ERR(handle); 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci group = group_data[0].group; 5588c2ecf20Sopenharmony_ci for (i = 0; i < flex_gd->count; i++, group++) { 5598c2ecf20Sopenharmony_ci unsigned long gdblocks; 5608c2ecf20Sopenharmony_ci ext4_grpblk_t overhead; 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci gdblocks = ext4_bg_num_gdb(sb, group); 5638c2ecf20Sopenharmony_ci start = ext4_group_first_block_no(sb, group); 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci if (meta_bg == 0 && !ext4_bg_has_super(sb, group)) 5668c2ecf20Sopenharmony_ci goto handle_itb; 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci if (meta_bg == 1) 5698c2ecf20Sopenharmony_ci goto handle_itb; 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci block = start + ext4_bg_has_super(sb, group); 5728c2ecf20Sopenharmony_ci /* Copy all of the GDT blocks into the backup in this group */ 5738c2ecf20Sopenharmony_ci for (j = 0; j < gdblocks; j++, block++) { 5748c2ecf20Sopenharmony_ci struct buffer_head *gdb; 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci ext4_debug("update backup group %#04llx\n", block); 5778c2ecf20Sopenharmony_ci err = ext4_resize_ensure_credits_batch(handle, 1); 5788c2ecf20Sopenharmony_ci if (err < 0) 5798c2ecf20Sopenharmony_ci goto out; 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci gdb = sb_getblk(sb, block); 5828c2ecf20Sopenharmony_ci if (unlikely(!gdb)) { 5838c2ecf20Sopenharmony_ci err = -ENOMEM; 5848c2ecf20Sopenharmony_ci goto out; 5858c2ecf20Sopenharmony_ci } 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci BUFFER_TRACE(gdb, "get_write_access"); 5888c2ecf20Sopenharmony_ci err = ext4_journal_get_write_access(handle, gdb); 5898c2ecf20Sopenharmony_ci if (err) { 5908c2ecf20Sopenharmony_ci brelse(gdb); 5918c2ecf20Sopenharmony_ci goto out; 5928c2ecf20Sopenharmony_ci } 5938c2ecf20Sopenharmony_ci memcpy(gdb->b_data, sbi_array_rcu_deref(sbi, 5948c2ecf20Sopenharmony_ci s_group_desc, j)->b_data, gdb->b_size); 5958c2ecf20Sopenharmony_ci set_buffer_uptodate(gdb); 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci err = ext4_handle_dirty_metadata(handle, NULL, gdb); 5988c2ecf20Sopenharmony_ci if (unlikely(err)) { 5998c2ecf20Sopenharmony_ci brelse(gdb); 6008c2ecf20Sopenharmony_ci goto out; 6018c2ecf20Sopenharmony_ci } 6028c2ecf20Sopenharmony_ci brelse(gdb); 6038c2ecf20Sopenharmony_ci } 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci /* Zero out all of the reserved backup group descriptor 6068c2ecf20Sopenharmony_ci * table blocks 6078c2ecf20Sopenharmony_ci */ 6088c2ecf20Sopenharmony_ci if (ext4_bg_has_super(sb, group)) { 6098c2ecf20Sopenharmony_ci err = sb_issue_zeroout(sb, gdblocks + start + 1, 6108c2ecf20Sopenharmony_ci reserved_gdb, GFP_NOFS); 6118c2ecf20Sopenharmony_ci if (err) 6128c2ecf20Sopenharmony_ci goto out; 6138c2ecf20Sopenharmony_ci } 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_cihandle_itb: 6168c2ecf20Sopenharmony_ci /* Initialize group tables of the grop @group */ 6178c2ecf20Sopenharmony_ci if (!(bg_flags[i] & EXT4_BG_INODE_ZEROED)) 6188c2ecf20Sopenharmony_ci goto handle_bb; 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci /* Zero out all of the inode table blocks */ 6218c2ecf20Sopenharmony_ci block = group_data[i].inode_table; 6228c2ecf20Sopenharmony_ci ext4_debug("clear inode table blocks %#04llx -> %#04lx\n", 6238c2ecf20Sopenharmony_ci block, sbi->s_itb_per_group); 6248c2ecf20Sopenharmony_ci err = sb_issue_zeroout(sb, block, sbi->s_itb_per_group, 6258c2ecf20Sopenharmony_ci GFP_NOFS); 6268c2ecf20Sopenharmony_ci if (err) 6278c2ecf20Sopenharmony_ci goto out; 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_cihandle_bb: 6308c2ecf20Sopenharmony_ci if (bg_flags[i] & EXT4_BG_BLOCK_UNINIT) 6318c2ecf20Sopenharmony_ci goto handle_ib; 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci /* Initialize block bitmap of the @group */ 6348c2ecf20Sopenharmony_ci block = group_data[i].block_bitmap; 6358c2ecf20Sopenharmony_ci err = ext4_resize_ensure_credits_batch(handle, 1); 6368c2ecf20Sopenharmony_ci if (err < 0) 6378c2ecf20Sopenharmony_ci goto out; 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci bh = bclean(handle, sb, block); 6408c2ecf20Sopenharmony_ci if (IS_ERR(bh)) { 6418c2ecf20Sopenharmony_ci err = PTR_ERR(bh); 6428c2ecf20Sopenharmony_ci goto out; 6438c2ecf20Sopenharmony_ci } 6448c2ecf20Sopenharmony_ci overhead = ext4_group_overhead_blocks(sb, group); 6458c2ecf20Sopenharmony_ci if (overhead != 0) { 6468c2ecf20Sopenharmony_ci ext4_debug("mark backup superblock %#04llx (+0)\n", 6478c2ecf20Sopenharmony_ci start); 6488c2ecf20Sopenharmony_ci ext4_set_bits(bh->b_data, 0, 6498c2ecf20Sopenharmony_ci EXT4_NUM_B2C(sbi, overhead)); 6508c2ecf20Sopenharmony_ci } 6518c2ecf20Sopenharmony_ci ext4_mark_bitmap_end(EXT4_B2C(sbi, group_data[i].blocks_count), 6528c2ecf20Sopenharmony_ci sb->s_blocksize * 8, bh->b_data); 6538c2ecf20Sopenharmony_ci err = ext4_handle_dirty_metadata(handle, NULL, bh); 6548c2ecf20Sopenharmony_ci brelse(bh); 6558c2ecf20Sopenharmony_ci if (err) 6568c2ecf20Sopenharmony_ci goto out; 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_cihandle_ib: 6598c2ecf20Sopenharmony_ci if (bg_flags[i] & EXT4_BG_INODE_UNINIT) 6608c2ecf20Sopenharmony_ci continue; 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci /* Initialize inode bitmap of the @group */ 6638c2ecf20Sopenharmony_ci block = group_data[i].inode_bitmap; 6648c2ecf20Sopenharmony_ci err = ext4_resize_ensure_credits_batch(handle, 1); 6658c2ecf20Sopenharmony_ci if (err < 0) 6668c2ecf20Sopenharmony_ci goto out; 6678c2ecf20Sopenharmony_ci /* Mark unused entries in inode bitmap used */ 6688c2ecf20Sopenharmony_ci bh = bclean(handle, sb, block); 6698c2ecf20Sopenharmony_ci if (IS_ERR(bh)) { 6708c2ecf20Sopenharmony_ci err = PTR_ERR(bh); 6718c2ecf20Sopenharmony_ci goto out; 6728c2ecf20Sopenharmony_ci } 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci ext4_mark_bitmap_end(EXT4_INODES_PER_GROUP(sb), 6758c2ecf20Sopenharmony_ci sb->s_blocksize * 8, bh->b_data); 6768c2ecf20Sopenharmony_ci err = ext4_handle_dirty_metadata(handle, NULL, bh); 6778c2ecf20Sopenharmony_ci brelse(bh); 6788c2ecf20Sopenharmony_ci if (err) 6798c2ecf20Sopenharmony_ci goto out; 6808c2ecf20Sopenharmony_ci } 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci /* Mark group tables in block bitmap */ 6838c2ecf20Sopenharmony_ci for (j = 0; j < GROUP_TABLE_COUNT; j++) { 6848c2ecf20Sopenharmony_ci count = group_table_count[j]; 6858c2ecf20Sopenharmony_ci start = (&group_data[0].block_bitmap)[j]; 6868c2ecf20Sopenharmony_ci block = start; 6878c2ecf20Sopenharmony_ci for (i = 1; i < flex_gd->count; i++) { 6888c2ecf20Sopenharmony_ci block += group_table_count[j]; 6898c2ecf20Sopenharmony_ci if (block == (&group_data[i].block_bitmap)[j]) { 6908c2ecf20Sopenharmony_ci count += group_table_count[j]; 6918c2ecf20Sopenharmony_ci continue; 6928c2ecf20Sopenharmony_ci } 6938c2ecf20Sopenharmony_ci err = set_flexbg_block_bitmap(sb, handle, 6948c2ecf20Sopenharmony_ci flex_gd, 6958c2ecf20Sopenharmony_ci EXT4_B2C(sbi, start), 6968c2ecf20Sopenharmony_ci EXT4_B2C(sbi, 6978c2ecf20Sopenharmony_ci start + count 6988c2ecf20Sopenharmony_ci - 1)); 6998c2ecf20Sopenharmony_ci if (err) 7008c2ecf20Sopenharmony_ci goto out; 7018c2ecf20Sopenharmony_ci count = group_table_count[j]; 7028c2ecf20Sopenharmony_ci start = (&group_data[i].block_bitmap)[j]; 7038c2ecf20Sopenharmony_ci block = start; 7048c2ecf20Sopenharmony_ci } 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci if (count) { 7078c2ecf20Sopenharmony_ci err = set_flexbg_block_bitmap(sb, handle, 7088c2ecf20Sopenharmony_ci flex_gd, 7098c2ecf20Sopenharmony_ci EXT4_B2C(sbi, start), 7108c2ecf20Sopenharmony_ci EXT4_B2C(sbi, 7118c2ecf20Sopenharmony_ci start + count 7128c2ecf20Sopenharmony_ci - 1)); 7138c2ecf20Sopenharmony_ci if (err) 7148c2ecf20Sopenharmony_ci goto out; 7158c2ecf20Sopenharmony_ci } 7168c2ecf20Sopenharmony_ci } 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ciout: 7198c2ecf20Sopenharmony_ci err2 = ext4_journal_stop(handle); 7208c2ecf20Sopenharmony_ci if (err2 && !err) 7218c2ecf20Sopenharmony_ci err = err2; 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci return err; 7248c2ecf20Sopenharmony_ci} 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci/* 7278c2ecf20Sopenharmony_ci * Iterate through the groups which hold BACKUP superblock/GDT copies in an 7288c2ecf20Sopenharmony_ci * ext4 filesystem. The counters should be initialized to 1, 5, and 7 before 7298c2ecf20Sopenharmony_ci * calling this for the first time. In a sparse filesystem it will be the 7308c2ecf20Sopenharmony_ci * sequence of powers of 3, 5, and 7: 1, 3, 5, 7, 9, 25, 27, 49, 81, ... 7318c2ecf20Sopenharmony_ci * For a non-sparse filesystem it will be every group: 1, 2, 3, 4, ... 7328c2ecf20Sopenharmony_ci */ 7338c2ecf20Sopenharmony_cistatic unsigned ext4_list_backups(struct super_block *sb, unsigned *three, 7348c2ecf20Sopenharmony_ci unsigned *five, unsigned *seven) 7358c2ecf20Sopenharmony_ci{ 7368c2ecf20Sopenharmony_ci unsigned *min = three; 7378c2ecf20Sopenharmony_ci int mult = 3; 7388c2ecf20Sopenharmony_ci unsigned ret; 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci if (!ext4_has_feature_sparse_super(sb)) { 7418c2ecf20Sopenharmony_ci ret = *min; 7428c2ecf20Sopenharmony_ci *min += 1; 7438c2ecf20Sopenharmony_ci return ret; 7448c2ecf20Sopenharmony_ci } 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci if (*five < *min) { 7478c2ecf20Sopenharmony_ci min = five; 7488c2ecf20Sopenharmony_ci mult = 5; 7498c2ecf20Sopenharmony_ci } 7508c2ecf20Sopenharmony_ci if (*seven < *min) { 7518c2ecf20Sopenharmony_ci min = seven; 7528c2ecf20Sopenharmony_ci mult = 7; 7538c2ecf20Sopenharmony_ci } 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci ret = *min; 7568c2ecf20Sopenharmony_ci *min *= mult; 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci return ret; 7598c2ecf20Sopenharmony_ci} 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci/* 7628c2ecf20Sopenharmony_ci * Check that all of the backup GDT blocks are held in the primary GDT block. 7638c2ecf20Sopenharmony_ci * It is assumed that they are stored in group order. Returns the number of 7648c2ecf20Sopenharmony_ci * groups in current filesystem that have BACKUPS, or -ve error code. 7658c2ecf20Sopenharmony_ci */ 7668c2ecf20Sopenharmony_cistatic int verify_reserved_gdb(struct super_block *sb, 7678c2ecf20Sopenharmony_ci ext4_group_t end, 7688c2ecf20Sopenharmony_ci struct buffer_head *primary) 7698c2ecf20Sopenharmony_ci{ 7708c2ecf20Sopenharmony_ci const ext4_fsblk_t blk = primary->b_blocknr; 7718c2ecf20Sopenharmony_ci unsigned three = 1; 7728c2ecf20Sopenharmony_ci unsigned five = 5; 7738c2ecf20Sopenharmony_ci unsigned seven = 7; 7748c2ecf20Sopenharmony_ci unsigned grp; 7758c2ecf20Sopenharmony_ci __le32 *p = (__le32 *)primary->b_data; 7768c2ecf20Sopenharmony_ci int gdbackups = 0; 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci while ((grp = ext4_list_backups(sb, &three, &five, &seven)) < end) { 7798c2ecf20Sopenharmony_ci if (le32_to_cpu(*p++) != 7808c2ecf20Sopenharmony_ci grp * EXT4_BLOCKS_PER_GROUP(sb) + blk){ 7818c2ecf20Sopenharmony_ci ext4_warning(sb, "reserved GDT %llu" 7828c2ecf20Sopenharmony_ci " missing grp %d (%llu)", 7838c2ecf20Sopenharmony_ci blk, grp, 7848c2ecf20Sopenharmony_ci grp * 7858c2ecf20Sopenharmony_ci (ext4_fsblk_t)EXT4_BLOCKS_PER_GROUP(sb) + 7868c2ecf20Sopenharmony_ci blk); 7878c2ecf20Sopenharmony_ci return -EINVAL; 7888c2ecf20Sopenharmony_ci } 7898c2ecf20Sopenharmony_ci if (++gdbackups > EXT4_ADDR_PER_BLOCK(sb)) 7908c2ecf20Sopenharmony_ci return -EFBIG; 7918c2ecf20Sopenharmony_ci } 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci return gdbackups; 7948c2ecf20Sopenharmony_ci} 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci/* 7978c2ecf20Sopenharmony_ci * Called when we need to bring a reserved group descriptor table block into 7988c2ecf20Sopenharmony_ci * use from the resize inode. The primary copy of the new GDT block currently 7998c2ecf20Sopenharmony_ci * is an indirect block (under the double indirect block in the resize inode). 8008c2ecf20Sopenharmony_ci * The new backup GDT blocks will be stored as leaf blocks in this indirect 8018c2ecf20Sopenharmony_ci * block, in group order. Even though we know all the block numbers we need, 8028c2ecf20Sopenharmony_ci * we check to ensure that the resize inode has actually reserved these blocks. 8038c2ecf20Sopenharmony_ci * 8048c2ecf20Sopenharmony_ci * Don't need to update the block bitmaps because the blocks are still in use. 8058c2ecf20Sopenharmony_ci * 8068c2ecf20Sopenharmony_ci * We get all of the error cases out of the way, so that we are sure to not 8078c2ecf20Sopenharmony_ci * fail once we start modifying the data on disk, because JBD has no rollback. 8088c2ecf20Sopenharmony_ci */ 8098c2ecf20Sopenharmony_cistatic int add_new_gdb(handle_t *handle, struct inode *inode, 8108c2ecf20Sopenharmony_ci ext4_group_t group) 8118c2ecf20Sopenharmony_ci{ 8128c2ecf20Sopenharmony_ci struct super_block *sb = inode->i_sb; 8138c2ecf20Sopenharmony_ci struct ext4_super_block *es = EXT4_SB(sb)->s_es; 8148c2ecf20Sopenharmony_ci unsigned long gdb_num = group / EXT4_DESC_PER_BLOCK(sb); 8158c2ecf20Sopenharmony_ci ext4_fsblk_t gdblock = EXT4_SB(sb)->s_sbh->b_blocknr + 1 + gdb_num; 8168c2ecf20Sopenharmony_ci struct buffer_head **o_group_desc, **n_group_desc = NULL; 8178c2ecf20Sopenharmony_ci struct buffer_head *dind = NULL; 8188c2ecf20Sopenharmony_ci struct buffer_head *gdb_bh = NULL; 8198c2ecf20Sopenharmony_ci int gdbackups; 8208c2ecf20Sopenharmony_ci struct ext4_iloc iloc = { .bh = NULL }; 8218c2ecf20Sopenharmony_ci __le32 *data; 8228c2ecf20Sopenharmony_ci int err; 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci if (test_opt(sb, DEBUG)) 8258c2ecf20Sopenharmony_ci printk(KERN_DEBUG 8268c2ecf20Sopenharmony_ci "EXT4-fs: ext4_add_new_gdb: adding group block %lu\n", 8278c2ecf20Sopenharmony_ci gdb_num); 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci gdb_bh = ext4_sb_bread(sb, gdblock, 0); 8308c2ecf20Sopenharmony_ci if (IS_ERR(gdb_bh)) 8318c2ecf20Sopenharmony_ci return PTR_ERR(gdb_bh); 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci gdbackups = verify_reserved_gdb(sb, group, gdb_bh); 8348c2ecf20Sopenharmony_ci if (gdbackups < 0) { 8358c2ecf20Sopenharmony_ci err = gdbackups; 8368c2ecf20Sopenharmony_ci goto errout; 8378c2ecf20Sopenharmony_ci } 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci data = EXT4_I(inode)->i_data + EXT4_DIND_BLOCK; 8408c2ecf20Sopenharmony_ci dind = ext4_sb_bread(sb, le32_to_cpu(*data), 0); 8418c2ecf20Sopenharmony_ci if (IS_ERR(dind)) { 8428c2ecf20Sopenharmony_ci err = PTR_ERR(dind); 8438c2ecf20Sopenharmony_ci dind = NULL; 8448c2ecf20Sopenharmony_ci goto errout; 8458c2ecf20Sopenharmony_ci } 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci data = (__le32 *)dind->b_data; 8488c2ecf20Sopenharmony_ci if (le32_to_cpu(data[gdb_num % EXT4_ADDR_PER_BLOCK(sb)]) != gdblock) { 8498c2ecf20Sopenharmony_ci ext4_warning(sb, "new group %u GDT block %llu not reserved", 8508c2ecf20Sopenharmony_ci group, gdblock); 8518c2ecf20Sopenharmony_ci err = -EINVAL; 8528c2ecf20Sopenharmony_ci goto errout; 8538c2ecf20Sopenharmony_ci } 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci BUFFER_TRACE(EXT4_SB(sb)->s_sbh, "get_write_access"); 8568c2ecf20Sopenharmony_ci err = ext4_journal_get_write_access(handle, EXT4_SB(sb)->s_sbh); 8578c2ecf20Sopenharmony_ci if (unlikely(err)) 8588c2ecf20Sopenharmony_ci goto errout; 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci BUFFER_TRACE(gdb_bh, "get_write_access"); 8618c2ecf20Sopenharmony_ci err = ext4_journal_get_write_access(handle, gdb_bh); 8628c2ecf20Sopenharmony_ci if (unlikely(err)) 8638c2ecf20Sopenharmony_ci goto errout; 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci BUFFER_TRACE(dind, "get_write_access"); 8668c2ecf20Sopenharmony_ci err = ext4_journal_get_write_access(handle, dind); 8678c2ecf20Sopenharmony_ci if (unlikely(err)) { 8688c2ecf20Sopenharmony_ci ext4_std_error(sb, err); 8698c2ecf20Sopenharmony_ci goto errout; 8708c2ecf20Sopenharmony_ci } 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci /* ext4_reserve_inode_write() gets a reference on the iloc */ 8738c2ecf20Sopenharmony_ci err = ext4_reserve_inode_write(handle, inode, &iloc); 8748c2ecf20Sopenharmony_ci if (unlikely(err)) 8758c2ecf20Sopenharmony_ci goto errout; 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci n_group_desc = kvmalloc((gdb_num + 1) * sizeof(struct buffer_head *), 8788c2ecf20Sopenharmony_ci GFP_KERNEL); 8798c2ecf20Sopenharmony_ci if (!n_group_desc) { 8808c2ecf20Sopenharmony_ci err = -ENOMEM; 8818c2ecf20Sopenharmony_ci ext4_warning(sb, "not enough memory for %lu groups", 8828c2ecf20Sopenharmony_ci gdb_num + 1); 8838c2ecf20Sopenharmony_ci goto errout; 8848c2ecf20Sopenharmony_ci } 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci /* 8878c2ecf20Sopenharmony_ci * Finally, we have all of the possible failures behind us... 8888c2ecf20Sopenharmony_ci * 8898c2ecf20Sopenharmony_ci * Remove new GDT block from inode double-indirect block and clear out 8908c2ecf20Sopenharmony_ci * the new GDT block for use (which also "frees" the backup GDT blocks 8918c2ecf20Sopenharmony_ci * from the reserved inode). We don't need to change the bitmaps for 8928c2ecf20Sopenharmony_ci * these blocks, because they are marked as in-use from being in the 8938c2ecf20Sopenharmony_ci * reserved inode, and will become GDT blocks (primary and backup). 8948c2ecf20Sopenharmony_ci */ 8958c2ecf20Sopenharmony_ci data[gdb_num % EXT4_ADDR_PER_BLOCK(sb)] = 0; 8968c2ecf20Sopenharmony_ci err = ext4_handle_dirty_metadata(handle, NULL, dind); 8978c2ecf20Sopenharmony_ci if (unlikely(err)) { 8988c2ecf20Sopenharmony_ci ext4_std_error(sb, err); 8998c2ecf20Sopenharmony_ci goto errout; 9008c2ecf20Sopenharmony_ci } 9018c2ecf20Sopenharmony_ci inode->i_blocks -= (gdbackups + 1) * sb->s_blocksize >> 9028c2ecf20Sopenharmony_ci (9 - EXT4_SB(sb)->s_cluster_bits); 9038c2ecf20Sopenharmony_ci ext4_mark_iloc_dirty(handle, inode, &iloc); 9048c2ecf20Sopenharmony_ci memset(gdb_bh->b_data, 0, sb->s_blocksize); 9058c2ecf20Sopenharmony_ci err = ext4_handle_dirty_metadata(handle, NULL, gdb_bh); 9068c2ecf20Sopenharmony_ci if (unlikely(err)) { 9078c2ecf20Sopenharmony_ci ext4_std_error(sb, err); 9088c2ecf20Sopenharmony_ci iloc.bh = NULL; 9098c2ecf20Sopenharmony_ci goto errout; 9108c2ecf20Sopenharmony_ci } 9118c2ecf20Sopenharmony_ci brelse(dind); 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci rcu_read_lock(); 9148c2ecf20Sopenharmony_ci o_group_desc = rcu_dereference(EXT4_SB(sb)->s_group_desc); 9158c2ecf20Sopenharmony_ci memcpy(n_group_desc, o_group_desc, 9168c2ecf20Sopenharmony_ci EXT4_SB(sb)->s_gdb_count * sizeof(struct buffer_head *)); 9178c2ecf20Sopenharmony_ci rcu_read_unlock(); 9188c2ecf20Sopenharmony_ci n_group_desc[gdb_num] = gdb_bh; 9198c2ecf20Sopenharmony_ci rcu_assign_pointer(EXT4_SB(sb)->s_group_desc, n_group_desc); 9208c2ecf20Sopenharmony_ci EXT4_SB(sb)->s_gdb_count++; 9218c2ecf20Sopenharmony_ci ext4_kvfree_array_rcu(o_group_desc); 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci lock_buffer(EXT4_SB(sb)->s_sbh); 9248c2ecf20Sopenharmony_ci le16_add_cpu(&es->s_reserved_gdt_blocks, -1); 9258c2ecf20Sopenharmony_ci ext4_superblock_csum_set(sb); 9268c2ecf20Sopenharmony_ci unlock_buffer(EXT4_SB(sb)->s_sbh); 9278c2ecf20Sopenharmony_ci err = ext4_handle_dirty_metadata(handle, NULL, EXT4_SB(sb)->s_sbh); 9288c2ecf20Sopenharmony_ci if (err) 9298c2ecf20Sopenharmony_ci ext4_std_error(sb, err); 9308c2ecf20Sopenharmony_ci return err; 9318c2ecf20Sopenharmony_cierrout: 9328c2ecf20Sopenharmony_ci kvfree(n_group_desc); 9338c2ecf20Sopenharmony_ci brelse(iloc.bh); 9348c2ecf20Sopenharmony_ci brelse(dind); 9358c2ecf20Sopenharmony_ci brelse(gdb_bh); 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci ext4_debug("leaving with error %d\n", err); 9388c2ecf20Sopenharmony_ci return err; 9398c2ecf20Sopenharmony_ci} 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci/* 9428c2ecf20Sopenharmony_ci * add_new_gdb_meta_bg is the sister of add_new_gdb. 9438c2ecf20Sopenharmony_ci */ 9448c2ecf20Sopenharmony_cistatic int add_new_gdb_meta_bg(struct super_block *sb, 9458c2ecf20Sopenharmony_ci handle_t *handle, ext4_group_t group) { 9468c2ecf20Sopenharmony_ci ext4_fsblk_t gdblock; 9478c2ecf20Sopenharmony_ci struct buffer_head *gdb_bh; 9488c2ecf20Sopenharmony_ci struct buffer_head **o_group_desc, **n_group_desc; 9498c2ecf20Sopenharmony_ci unsigned long gdb_num = group / EXT4_DESC_PER_BLOCK(sb); 9508c2ecf20Sopenharmony_ci int err; 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci gdblock = ext4_meta_bg_first_block_no(sb, group) + 9538c2ecf20Sopenharmony_ci ext4_bg_has_super(sb, group); 9548c2ecf20Sopenharmony_ci gdb_bh = ext4_sb_bread(sb, gdblock, 0); 9558c2ecf20Sopenharmony_ci if (IS_ERR(gdb_bh)) 9568c2ecf20Sopenharmony_ci return PTR_ERR(gdb_bh); 9578c2ecf20Sopenharmony_ci n_group_desc = kvmalloc((gdb_num + 1) * sizeof(struct buffer_head *), 9588c2ecf20Sopenharmony_ci GFP_KERNEL); 9598c2ecf20Sopenharmony_ci if (!n_group_desc) { 9608c2ecf20Sopenharmony_ci brelse(gdb_bh); 9618c2ecf20Sopenharmony_ci err = -ENOMEM; 9628c2ecf20Sopenharmony_ci ext4_warning(sb, "not enough memory for %lu groups", 9638c2ecf20Sopenharmony_ci gdb_num + 1); 9648c2ecf20Sopenharmony_ci return err; 9658c2ecf20Sopenharmony_ci } 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci rcu_read_lock(); 9688c2ecf20Sopenharmony_ci o_group_desc = rcu_dereference(EXT4_SB(sb)->s_group_desc); 9698c2ecf20Sopenharmony_ci memcpy(n_group_desc, o_group_desc, 9708c2ecf20Sopenharmony_ci EXT4_SB(sb)->s_gdb_count * sizeof(struct buffer_head *)); 9718c2ecf20Sopenharmony_ci rcu_read_unlock(); 9728c2ecf20Sopenharmony_ci n_group_desc[gdb_num] = gdb_bh; 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci BUFFER_TRACE(gdb_bh, "get_write_access"); 9758c2ecf20Sopenharmony_ci err = ext4_journal_get_write_access(handle, gdb_bh); 9768c2ecf20Sopenharmony_ci if (err) { 9778c2ecf20Sopenharmony_ci kvfree(n_group_desc); 9788c2ecf20Sopenharmony_ci brelse(gdb_bh); 9798c2ecf20Sopenharmony_ci return err; 9808c2ecf20Sopenharmony_ci } 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci rcu_assign_pointer(EXT4_SB(sb)->s_group_desc, n_group_desc); 9838c2ecf20Sopenharmony_ci EXT4_SB(sb)->s_gdb_count++; 9848c2ecf20Sopenharmony_ci ext4_kvfree_array_rcu(o_group_desc); 9858c2ecf20Sopenharmony_ci return err; 9868c2ecf20Sopenharmony_ci} 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci/* 9898c2ecf20Sopenharmony_ci * Called when we are adding a new group which has a backup copy of each of 9908c2ecf20Sopenharmony_ci * the GDT blocks (i.e. sparse group) and there are reserved GDT blocks. 9918c2ecf20Sopenharmony_ci * We need to add these reserved backup GDT blocks to the resize inode, so 9928c2ecf20Sopenharmony_ci * that they are kept for future resizing and not allocated to files. 9938c2ecf20Sopenharmony_ci * 9948c2ecf20Sopenharmony_ci * Each reserved backup GDT block will go into a different indirect block. 9958c2ecf20Sopenharmony_ci * The indirect blocks are actually the primary reserved GDT blocks, 9968c2ecf20Sopenharmony_ci * so we know in advance what their block numbers are. We only get the 9978c2ecf20Sopenharmony_ci * double-indirect block to verify it is pointing to the primary reserved 9988c2ecf20Sopenharmony_ci * GDT blocks so we don't overwrite a data block by accident. The reserved 9998c2ecf20Sopenharmony_ci * backup GDT blocks are stored in their reserved primary GDT block. 10008c2ecf20Sopenharmony_ci */ 10018c2ecf20Sopenharmony_cistatic int reserve_backup_gdb(handle_t *handle, struct inode *inode, 10028c2ecf20Sopenharmony_ci ext4_group_t group) 10038c2ecf20Sopenharmony_ci{ 10048c2ecf20Sopenharmony_ci struct super_block *sb = inode->i_sb; 10058c2ecf20Sopenharmony_ci int reserved_gdb =le16_to_cpu(EXT4_SB(sb)->s_es->s_reserved_gdt_blocks); 10068c2ecf20Sopenharmony_ci int cluster_bits = EXT4_SB(sb)->s_cluster_bits; 10078c2ecf20Sopenharmony_ci struct buffer_head **primary; 10088c2ecf20Sopenharmony_ci struct buffer_head *dind; 10098c2ecf20Sopenharmony_ci struct ext4_iloc iloc; 10108c2ecf20Sopenharmony_ci ext4_fsblk_t blk; 10118c2ecf20Sopenharmony_ci __le32 *data, *end; 10128c2ecf20Sopenharmony_ci int gdbackups = 0; 10138c2ecf20Sopenharmony_ci int res, i; 10148c2ecf20Sopenharmony_ci int err; 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci primary = kmalloc_array(reserved_gdb, sizeof(*primary), GFP_NOFS); 10178c2ecf20Sopenharmony_ci if (!primary) 10188c2ecf20Sopenharmony_ci return -ENOMEM; 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci data = EXT4_I(inode)->i_data + EXT4_DIND_BLOCK; 10218c2ecf20Sopenharmony_ci dind = ext4_sb_bread(sb, le32_to_cpu(*data), 0); 10228c2ecf20Sopenharmony_ci if (IS_ERR(dind)) { 10238c2ecf20Sopenharmony_ci err = PTR_ERR(dind); 10248c2ecf20Sopenharmony_ci dind = NULL; 10258c2ecf20Sopenharmony_ci goto exit_free; 10268c2ecf20Sopenharmony_ci } 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci blk = EXT4_SB(sb)->s_sbh->b_blocknr + 1 + EXT4_SB(sb)->s_gdb_count; 10298c2ecf20Sopenharmony_ci data = (__le32 *)dind->b_data + (EXT4_SB(sb)->s_gdb_count % 10308c2ecf20Sopenharmony_ci EXT4_ADDR_PER_BLOCK(sb)); 10318c2ecf20Sopenharmony_ci end = (__le32 *)dind->b_data + EXT4_ADDR_PER_BLOCK(sb); 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci /* Get each reserved primary GDT block and verify it holds backups */ 10348c2ecf20Sopenharmony_ci for (res = 0; res < reserved_gdb; res++, blk++) { 10358c2ecf20Sopenharmony_ci if (le32_to_cpu(*data) != blk) { 10368c2ecf20Sopenharmony_ci ext4_warning(sb, "reserved block %llu" 10378c2ecf20Sopenharmony_ci " not at offset %ld", 10388c2ecf20Sopenharmony_ci blk, 10398c2ecf20Sopenharmony_ci (long)(data - (__le32 *)dind->b_data)); 10408c2ecf20Sopenharmony_ci err = -EINVAL; 10418c2ecf20Sopenharmony_ci goto exit_bh; 10428c2ecf20Sopenharmony_ci } 10438c2ecf20Sopenharmony_ci primary[res] = ext4_sb_bread(sb, blk, 0); 10448c2ecf20Sopenharmony_ci if (IS_ERR(primary[res])) { 10458c2ecf20Sopenharmony_ci err = PTR_ERR(primary[res]); 10468c2ecf20Sopenharmony_ci primary[res] = NULL; 10478c2ecf20Sopenharmony_ci goto exit_bh; 10488c2ecf20Sopenharmony_ci } 10498c2ecf20Sopenharmony_ci gdbackups = verify_reserved_gdb(sb, group, primary[res]); 10508c2ecf20Sopenharmony_ci if (gdbackups < 0) { 10518c2ecf20Sopenharmony_ci brelse(primary[res]); 10528c2ecf20Sopenharmony_ci err = gdbackups; 10538c2ecf20Sopenharmony_ci goto exit_bh; 10548c2ecf20Sopenharmony_ci } 10558c2ecf20Sopenharmony_ci if (++data >= end) 10568c2ecf20Sopenharmony_ci data = (__le32 *)dind->b_data; 10578c2ecf20Sopenharmony_ci } 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci for (i = 0; i < reserved_gdb; i++) { 10608c2ecf20Sopenharmony_ci BUFFER_TRACE(primary[i], "get_write_access"); 10618c2ecf20Sopenharmony_ci if ((err = ext4_journal_get_write_access(handle, primary[i]))) 10628c2ecf20Sopenharmony_ci goto exit_bh; 10638c2ecf20Sopenharmony_ci } 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci if ((err = ext4_reserve_inode_write(handle, inode, &iloc))) 10668c2ecf20Sopenharmony_ci goto exit_bh; 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci /* 10698c2ecf20Sopenharmony_ci * Finally we can add each of the reserved backup GDT blocks from 10708c2ecf20Sopenharmony_ci * the new group to its reserved primary GDT block. 10718c2ecf20Sopenharmony_ci */ 10728c2ecf20Sopenharmony_ci blk = group * EXT4_BLOCKS_PER_GROUP(sb); 10738c2ecf20Sopenharmony_ci for (i = 0; i < reserved_gdb; i++) { 10748c2ecf20Sopenharmony_ci int err2; 10758c2ecf20Sopenharmony_ci data = (__le32 *)primary[i]->b_data; 10768c2ecf20Sopenharmony_ci /* printk("reserving backup %lu[%u] = %lu\n", 10778c2ecf20Sopenharmony_ci primary[i]->b_blocknr, gdbackups, 10788c2ecf20Sopenharmony_ci blk + primary[i]->b_blocknr); */ 10798c2ecf20Sopenharmony_ci data[gdbackups] = cpu_to_le32(blk + primary[i]->b_blocknr); 10808c2ecf20Sopenharmony_ci err2 = ext4_handle_dirty_metadata(handle, NULL, primary[i]); 10818c2ecf20Sopenharmony_ci if (!err) 10828c2ecf20Sopenharmony_ci err = err2; 10838c2ecf20Sopenharmony_ci } 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci inode->i_blocks += reserved_gdb * sb->s_blocksize >> (9 - cluster_bits); 10868c2ecf20Sopenharmony_ci ext4_mark_iloc_dirty(handle, inode, &iloc); 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ciexit_bh: 10898c2ecf20Sopenharmony_ci while (--res >= 0) 10908c2ecf20Sopenharmony_ci brelse(primary[res]); 10918c2ecf20Sopenharmony_ci brelse(dind); 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ciexit_free: 10948c2ecf20Sopenharmony_ci kfree(primary); 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci return err; 10978c2ecf20Sopenharmony_ci} 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci/* 11008c2ecf20Sopenharmony_ci * Update the backup copies of the ext4 metadata. These don't need to be part 11018c2ecf20Sopenharmony_ci * of the main resize transaction, because e2fsck will re-write them if there 11028c2ecf20Sopenharmony_ci * is a problem (basically only OOM will cause a problem). However, we 11038c2ecf20Sopenharmony_ci * _should_ update the backups if possible, in case the primary gets trashed 11048c2ecf20Sopenharmony_ci * for some reason and we need to run e2fsck from a backup superblock. The 11058c2ecf20Sopenharmony_ci * important part is that the new block and inode counts are in the backup 11068c2ecf20Sopenharmony_ci * superblocks, and the location of the new group metadata in the GDT backups. 11078c2ecf20Sopenharmony_ci * 11088c2ecf20Sopenharmony_ci * We do not need take the s_resize_lock for this, because these 11098c2ecf20Sopenharmony_ci * blocks are not otherwise touched by the filesystem code when it is 11108c2ecf20Sopenharmony_ci * mounted. We don't need to worry about last changing from 11118c2ecf20Sopenharmony_ci * sbi->s_groups_count, because the worst that can happen is that we 11128c2ecf20Sopenharmony_ci * do not copy the full number of backups at this time. The resize 11138c2ecf20Sopenharmony_ci * which changed s_groups_count will backup again. 11148c2ecf20Sopenharmony_ci */ 11158c2ecf20Sopenharmony_cistatic void update_backups(struct super_block *sb, sector_t blk_off, char *data, 11168c2ecf20Sopenharmony_ci int size, int meta_bg) 11178c2ecf20Sopenharmony_ci{ 11188c2ecf20Sopenharmony_ci struct ext4_sb_info *sbi = EXT4_SB(sb); 11198c2ecf20Sopenharmony_ci ext4_group_t last; 11208c2ecf20Sopenharmony_ci const int bpg = EXT4_BLOCKS_PER_GROUP(sb); 11218c2ecf20Sopenharmony_ci unsigned three = 1; 11228c2ecf20Sopenharmony_ci unsigned five = 5; 11238c2ecf20Sopenharmony_ci unsigned seven = 7; 11248c2ecf20Sopenharmony_ci ext4_group_t group = 0; 11258c2ecf20Sopenharmony_ci int rest = sb->s_blocksize - size; 11268c2ecf20Sopenharmony_ci handle_t *handle; 11278c2ecf20Sopenharmony_ci int err = 0, err2; 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci handle = ext4_journal_start_sb(sb, EXT4_HT_RESIZE, EXT4_MAX_TRANS_DATA); 11308c2ecf20Sopenharmony_ci if (IS_ERR(handle)) { 11318c2ecf20Sopenharmony_ci group = 1; 11328c2ecf20Sopenharmony_ci err = PTR_ERR(handle); 11338c2ecf20Sopenharmony_ci goto exit_err; 11348c2ecf20Sopenharmony_ci } 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci if (meta_bg == 0) { 11378c2ecf20Sopenharmony_ci group = ext4_list_backups(sb, &three, &five, &seven); 11388c2ecf20Sopenharmony_ci last = sbi->s_groups_count; 11398c2ecf20Sopenharmony_ci } else { 11408c2ecf20Sopenharmony_ci group = ext4_get_group_number(sb, blk_off) + 1; 11418c2ecf20Sopenharmony_ci last = (ext4_group_t)(group + EXT4_DESC_PER_BLOCK(sb) - 2); 11428c2ecf20Sopenharmony_ci } 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci while (group < sbi->s_groups_count) { 11458c2ecf20Sopenharmony_ci struct buffer_head *bh; 11468c2ecf20Sopenharmony_ci ext4_fsblk_t backup_block; 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_ci /* Out of journal space, and can't get more - abort - so sad */ 11498c2ecf20Sopenharmony_ci err = ext4_resize_ensure_credits_batch(handle, 1); 11508c2ecf20Sopenharmony_ci if (err < 0) 11518c2ecf20Sopenharmony_ci break; 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_ci if (meta_bg == 0) 11548c2ecf20Sopenharmony_ci backup_block = ((ext4_fsblk_t)group) * bpg + blk_off; 11558c2ecf20Sopenharmony_ci else 11568c2ecf20Sopenharmony_ci backup_block = (ext4_group_first_block_no(sb, group) + 11578c2ecf20Sopenharmony_ci ext4_bg_has_super(sb, group)); 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_ci bh = sb_getblk(sb, backup_block); 11608c2ecf20Sopenharmony_ci if (unlikely(!bh)) { 11618c2ecf20Sopenharmony_ci err = -ENOMEM; 11628c2ecf20Sopenharmony_ci break; 11638c2ecf20Sopenharmony_ci } 11648c2ecf20Sopenharmony_ci ext4_debug("update metadata backup %llu(+%llu)\n", 11658c2ecf20Sopenharmony_ci backup_block, backup_block - 11668c2ecf20Sopenharmony_ci ext4_group_first_block_no(sb, group)); 11678c2ecf20Sopenharmony_ci BUFFER_TRACE(bh, "get_write_access"); 11688c2ecf20Sopenharmony_ci if ((err = ext4_journal_get_write_access(handle, bh))) { 11698c2ecf20Sopenharmony_ci brelse(bh); 11708c2ecf20Sopenharmony_ci break; 11718c2ecf20Sopenharmony_ci } 11728c2ecf20Sopenharmony_ci lock_buffer(bh); 11738c2ecf20Sopenharmony_ci memcpy(bh->b_data, data, size); 11748c2ecf20Sopenharmony_ci if (rest) 11758c2ecf20Sopenharmony_ci memset(bh->b_data + size, 0, rest); 11768c2ecf20Sopenharmony_ci set_buffer_uptodate(bh); 11778c2ecf20Sopenharmony_ci unlock_buffer(bh); 11788c2ecf20Sopenharmony_ci err = ext4_handle_dirty_metadata(handle, NULL, bh); 11798c2ecf20Sopenharmony_ci if (unlikely(err)) 11808c2ecf20Sopenharmony_ci ext4_std_error(sb, err); 11818c2ecf20Sopenharmony_ci brelse(bh); 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_ci if (meta_bg == 0) 11848c2ecf20Sopenharmony_ci group = ext4_list_backups(sb, &three, &five, &seven); 11858c2ecf20Sopenharmony_ci else if (group == last) 11868c2ecf20Sopenharmony_ci break; 11878c2ecf20Sopenharmony_ci else 11888c2ecf20Sopenharmony_ci group = last; 11898c2ecf20Sopenharmony_ci } 11908c2ecf20Sopenharmony_ci if ((err2 = ext4_journal_stop(handle)) && !err) 11918c2ecf20Sopenharmony_ci err = err2; 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_ci /* 11948c2ecf20Sopenharmony_ci * Ugh! Need to have e2fsck write the backup copies. It is too 11958c2ecf20Sopenharmony_ci * late to revert the resize, we shouldn't fail just because of 11968c2ecf20Sopenharmony_ci * the backup copies (they are only needed in case of corruption). 11978c2ecf20Sopenharmony_ci * 11988c2ecf20Sopenharmony_ci * However, if we got here we have a journal problem too, so we 11998c2ecf20Sopenharmony_ci * can't really start a transaction to mark the superblock. 12008c2ecf20Sopenharmony_ci * Chicken out and just set the flag on the hope it will be written 12018c2ecf20Sopenharmony_ci * to disk, and if not - we will simply wait until next fsck. 12028c2ecf20Sopenharmony_ci */ 12038c2ecf20Sopenharmony_ciexit_err: 12048c2ecf20Sopenharmony_ci if (err) { 12058c2ecf20Sopenharmony_ci ext4_warning(sb, "can't update backup for group %u (err %d), " 12068c2ecf20Sopenharmony_ci "forcing fsck on next reboot", group, err); 12078c2ecf20Sopenharmony_ci sbi->s_mount_state &= ~EXT4_VALID_FS; 12088c2ecf20Sopenharmony_ci sbi->s_es->s_state &= cpu_to_le16(~EXT4_VALID_FS); 12098c2ecf20Sopenharmony_ci mark_buffer_dirty(sbi->s_sbh); 12108c2ecf20Sopenharmony_ci } 12118c2ecf20Sopenharmony_ci} 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_ci/* 12148c2ecf20Sopenharmony_ci * ext4_add_new_descs() adds @count group descriptor of groups 12158c2ecf20Sopenharmony_ci * starting at @group 12168c2ecf20Sopenharmony_ci * 12178c2ecf20Sopenharmony_ci * @handle: journal handle 12188c2ecf20Sopenharmony_ci * @sb: super block 12198c2ecf20Sopenharmony_ci * @group: the group no. of the first group desc to be added 12208c2ecf20Sopenharmony_ci * @resize_inode: the resize inode 12218c2ecf20Sopenharmony_ci * @count: number of group descriptors to be added 12228c2ecf20Sopenharmony_ci */ 12238c2ecf20Sopenharmony_cistatic int ext4_add_new_descs(handle_t *handle, struct super_block *sb, 12248c2ecf20Sopenharmony_ci ext4_group_t group, struct inode *resize_inode, 12258c2ecf20Sopenharmony_ci ext4_group_t count) 12268c2ecf20Sopenharmony_ci{ 12278c2ecf20Sopenharmony_ci struct ext4_sb_info *sbi = EXT4_SB(sb); 12288c2ecf20Sopenharmony_ci struct ext4_super_block *es = sbi->s_es; 12298c2ecf20Sopenharmony_ci struct buffer_head *gdb_bh; 12308c2ecf20Sopenharmony_ci int i, gdb_off, gdb_num, err = 0; 12318c2ecf20Sopenharmony_ci int meta_bg; 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_ci meta_bg = ext4_has_feature_meta_bg(sb); 12348c2ecf20Sopenharmony_ci for (i = 0; i < count; i++, group++) { 12358c2ecf20Sopenharmony_ci int reserved_gdb = ext4_bg_has_super(sb, group) ? 12368c2ecf20Sopenharmony_ci le16_to_cpu(es->s_reserved_gdt_blocks) : 0; 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_ci gdb_off = group % EXT4_DESC_PER_BLOCK(sb); 12398c2ecf20Sopenharmony_ci gdb_num = group / EXT4_DESC_PER_BLOCK(sb); 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_ci /* 12428c2ecf20Sopenharmony_ci * We will only either add reserved group blocks to a backup group 12438c2ecf20Sopenharmony_ci * or remove reserved blocks for the first group in a new group block. 12448c2ecf20Sopenharmony_ci * Doing both would be mean more complex code, and sane people don't 12458c2ecf20Sopenharmony_ci * use non-sparse filesystems anymore. This is already checked above. 12468c2ecf20Sopenharmony_ci */ 12478c2ecf20Sopenharmony_ci if (gdb_off) { 12488c2ecf20Sopenharmony_ci gdb_bh = sbi_array_rcu_deref(sbi, s_group_desc, 12498c2ecf20Sopenharmony_ci gdb_num); 12508c2ecf20Sopenharmony_ci BUFFER_TRACE(gdb_bh, "get_write_access"); 12518c2ecf20Sopenharmony_ci err = ext4_journal_get_write_access(handle, gdb_bh); 12528c2ecf20Sopenharmony_ci 12538c2ecf20Sopenharmony_ci if (!err && reserved_gdb && ext4_bg_num_gdb(sb, group)) 12548c2ecf20Sopenharmony_ci err = reserve_backup_gdb(handle, resize_inode, group); 12558c2ecf20Sopenharmony_ci } else if (meta_bg != 0) { 12568c2ecf20Sopenharmony_ci err = add_new_gdb_meta_bg(sb, handle, group); 12578c2ecf20Sopenharmony_ci } else { 12588c2ecf20Sopenharmony_ci err = add_new_gdb(handle, resize_inode, group); 12598c2ecf20Sopenharmony_ci } 12608c2ecf20Sopenharmony_ci if (err) 12618c2ecf20Sopenharmony_ci break; 12628c2ecf20Sopenharmony_ci } 12638c2ecf20Sopenharmony_ci return err; 12648c2ecf20Sopenharmony_ci} 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_cistatic struct buffer_head *ext4_get_bitmap(struct super_block *sb, __u64 block) 12678c2ecf20Sopenharmony_ci{ 12688c2ecf20Sopenharmony_ci struct buffer_head *bh = sb_getblk(sb, block); 12698c2ecf20Sopenharmony_ci if (unlikely(!bh)) 12708c2ecf20Sopenharmony_ci return NULL; 12718c2ecf20Sopenharmony_ci if (!bh_uptodate_or_lock(bh)) { 12728c2ecf20Sopenharmony_ci if (ext4_read_bh(bh, 0, NULL) < 0) { 12738c2ecf20Sopenharmony_ci brelse(bh); 12748c2ecf20Sopenharmony_ci return NULL; 12758c2ecf20Sopenharmony_ci } 12768c2ecf20Sopenharmony_ci } 12778c2ecf20Sopenharmony_ci 12788c2ecf20Sopenharmony_ci return bh; 12798c2ecf20Sopenharmony_ci} 12808c2ecf20Sopenharmony_ci 12818c2ecf20Sopenharmony_cistatic int ext4_set_bitmap_checksums(struct super_block *sb, 12828c2ecf20Sopenharmony_ci ext4_group_t group, 12838c2ecf20Sopenharmony_ci struct ext4_group_desc *gdp, 12848c2ecf20Sopenharmony_ci struct ext4_new_group_data *group_data) 12858c2ecf20Sopenharmony_ci{ 12868c2ecf20Sopenharmony_ci struct buffer_head *bh; 12878c2ecf20Sopenharmony_ci 12888c2ecf20Sopenharmony_ci if (!ext4_has_metadata_csum(sb)) 12898c2ecf20Sopenharmony_ci return 0; 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ci bh = ext4_get_bitmap(sb, group_data->inode_bitmap); 12928c2ecf20Sopenharmony_ci if (!bh) 12938c2ecf20Sopenharmony_ci return -EIO; 12948c2ecf20Sopenharmony_ci ext4_inode_bitmap_csum_set(sb, group, gdp, bh, 12958c2ecf20Sopenharmony_ci EXT4_INODES_PER_GROUP(sb) / 8); 12968c2ecf20Sopenharmony_ci brelse(bh); 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ci bh = ext4_get_bitmap(sb, group_data->block_bitmap); 12998c2ecf20Sopenharmony_ci if (!bh) 13008c2ecf20Sopenharmony_ci return -EIO; 13018c2ecf20Sopenharmony_ci ext4_block_bitmap_csum_set(sb, group, gdp, bh); 13028c2ecf20Sopenharmony_ci brelse(bh); 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_ci return 0; 13058c2ecf20Sopenharmony_ci} 13068c2ecf20Sopenharmony_ci 13078c2ecf20Sopenharmony_ci/* 13088c2ecf20Sopenharmony_ci * ext4_setup_new_descs() will set up the group descriptor descriptors of a flex bg 13098c2ecf20Sopenharmony_ci */ 13108c2ecf20Sopenharmony_cistatic int ext4_setup_new_descs(handle_t *handle, struct super_block *sb, 13118c2ecf20Sopenharmony_ci struct ext4_new_flex_group_data *flex_gd) 13128c2ecf20Sopenharmony_ci{ 13138c2ecf20Sopenharmony_ci struct ext4_new_group_data *group_data = flex_gd->groups; 13148c2ecf20Sopenharmony_ci struct ext4_group_desc *gdp; 13158c2ecf20Sopenharmony_ci struct ext4_sb_info *sbi = EXT4_SB(sb); 13168c2ecf20Sopenharmony_ci struct buffer_head *gdb_bh; 13178c2ecf20Sopenharmony_ci ext4_group_t group; 13188c2ecf20Sopenharmony_ci __u16 *bg_flags = flex_gd->bg_flags; 13198c2ecf20Sopenharmony_ci int i, gdb_off, gdb_num, err = 0; 13208c2ecf20Sopenharmony_ci 13218c2ecf20Sopenharmony_ci 13228c2ecf20Sopenharmony_ci for (i = 0; i < flex_gd->count; i++, group_data++, bg_flags++) { 13238c2ecf20Sopenharmony_ci group = group_data->group; 13248c2ecf20Sopenharmony_ci 13258c2ecf20Sopenharmony_ci gdb_off = group % EXT4_DESC_PER_BLOCK(sb); 13268c2ecf20Sopenharmony_ci gdb_num = group / EXT4_DESC_PER_BLOCK(sb); 13278c2ecf20Sopenharmony_ci 13288c2ecf20Sopenharmony_ci /* 13298c2ecf20Sopenharmony_ci * get_write_access() has been called on gdb_bh by ext4_add_new_desc(). 13308c2ecf20Sopenharmony_ci */ 13318c2ecf20Sopenharmony_ci gdb_bh = sbi_array_rcu_deref(sbi, s_group_desc, gdb_num); 13328c2ecf20Sopenharmony_ci /* Update group descriptor block for new group */ 13338c2ecf20Sopenharmony_ci gdp = (struct ext4_group_desc *)(gdb_bh->b_data + 13348c2ecf20Sopenharmony_ci gdb_off * EXT4_DESC_SIZE(sb)); 13358c2ecf20Sopenharmony_ci 13368c2ecf20Sopenharmony_ci memset(gdp, 0, EXT4_DESC_SIZE(sb)); 13378c2ecf20Sopenharmony_ci ext4_block_bitmap_set(sb, gdp, group_data->block_bitmap); 13388c2ecf20Sopenharmony_ci ext4_inode_bitmap_set(sb, gdp, group_data->inode_bitmap); 13398c2ecf20Sopenharmony_ci err = ext4_set_bitmap_checksums(sb, group, gdp, group_data); 13408c2ecf20Sopenharmony_ci if (err) { 13418c2ecf20Sopenharmony_ci ext4_std_error(sb, err); 13428c2ecf20Sopenharmony_ci break; 13438c2ecf20Sopenharmony_ci } 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_ci ext4_inode_table_set(sb, gdp, group_data->inode_table); 13468c2ecf20Sopenharmony_ci ext4_free_group_clusters_set(sb, gdp, 13478c2ecf20Sopenharmony_ci group_data->free_clusters_count); 13488c2ecf20Sopenharmony_ci ext4_free_inodes_set(sb, gdp, EXT4_INODES_PER_GROUP(sb)); 13498c2ecf20Sopenharmony_ci if (ext4_has_group_desc_csum(sb)) 13508c2ecf20Sopenharmony_ci ext4_itable_unused_set(sb, gdp, 13518c2ecf20Sopenharmony_ci EXT4_INODES_PER_GROUP(sb)); 13528c2ecf20Sopenharmony_ci gdp->bg_flags = cpu_to_le16(*bg_flags); 13538c2ecf20Sopenharmony_ci ext4_group_desc_csum_set(sb, group, gdp); 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_ci err = ext4_handle_dirty_metadata(handle, NULL, gdb_bh); 13568c2ecf20Sopenharmony_ci if (unlikely(err)) { 13578c2ecf20Sopenharmony_ci ext4_std_error(sb, err); 13588c2ecf20Sopenharmony_ci break; 13598c2ecf20Sopenharmony_ci } 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_ci /* 13628c2ecf20Sopenharmony_ci * We can allocate memory for mb_alloc based on the new group 13638c2ecf20Sopenharmony_ci * descriptor 13648c2ecf20Sopenharmony_ci */ 13658c2ecf20Sopenharmony_ci err = ext4_mb_add_groupinfo(sb, group, gdp); 13668c2ecf20Sopenharmony_ci if (err) 13678c2ecf20Sopenharmony_ci break; 13688c2ecf20Sopenharmony_ci } 13698c2ecf20Sopenharmony_ci return err; 13708c2ecf20Sopenharmony_ci} 13718c2ecf20Sopenharmony_ci 13728c2ecf20Sopenharmony_ci/* 13738c2ecf20Sopenharmony_ci * ext4_update_super() updates the super block so that the newly added 13748c2ecf20Sopenharmony_ci * groups can be seen by the filesystem. 13758c2ecf20Sopenharmony_ci * 13768c2ecf20Sopenharmony_ci * @sb: super block 13778c2ecf20Sopenharmony_ci * @flex_gd: new added groups 13788c2ecf20Sopenharmony_ci */ 13798c2ecf20Sopenharmony_cistatic void ext4_update_super(struct super_block *sb, 13808c2ecf20Sopenharmony_ci struct ext4_new_flex_group_data *flex_gd) 13818c2ecf20Sopenharmony_ci{ 13828c2ecf20Sopenharmony_ci ext4_fsblk_t blocks_count = 0; 13838c2ecf20Sopenharmony_ci ext4_fsblk_t free_blocks = 0; 13848c2ecf20Sopenharmony_ci ext4_fsblk_t reserved_blocks = 0; 13858c2ecf20Sopenharmony_ci struct ext4_new_group_data *group_data = flex_gd->groups; 13868c2ecf20Sopenharmony_ci struct ext4_sb_info *sbi = EXT4_SB(sb); 13878c2ecf20Sopenharmony_ci struct ext4_super_block *es = sbi->s_es; 13888c2ecf20Sopenharmony_ci int i; 13898c2ecf20Sopenharmony_ci 13908c2ecf20Sopenharmony_ci BUG_ON(flex_gd->count == 0 || group_data == NULL); 13918c2ecf20Sopenharmony_ci /* 13928c2ecf20Sopenharmony_ci * Make the new blocks and inodes valid next. We do this before 13938c2ecf20Sopenharmony_ci * increasing the group count so that once the group is enabled, 13948c2ecf20Sopenharmony_ci * all of its blocks and inodes are already valid. 13958c2ecf20Sopenharmony_ci * 13968c2ecf20Sopenharmony_ci * We always allocate group-by-group, then block-by-block or 13978c2ecf20Sopenharmony_ci * inode-by-inode within a group, so enabling these 13988c2ecf20Sopenharmony_ci * blocks/inodes before the group is live won't actually let us 13998c2ecf20Sopenharmony_ci * allocate the new space yet. 14008c2ecf20Sopenharmony_ci */ 14018c2ecf20Sopenharmony_ci for (i = 0; i < flex_gd->count; i++) { 14028c2ecf20Sopenharmony_ci blocks_count += group_data[i].blocks_count; 14038c2ecf20Sopenharmony_ci free_blocks += EXT4_C2B(sbi, group_data[i].free_clusters_count); 14048c2ecf20Sopenharmony_ci } 14058c2ecf20Sopenharmony_ci 14068c2ecf20Sopenharmony_ci reserved_blocks = ext4_r_blocks_count(es) * 100; 14078c2ecf20Sopenharmony_ci reserved_blocks = div64_u64(reserved_blocks, ext4_blocks_count(es)); 14088c2ecf20Sopenharmony_ci reserved_blocks *= blocks_count; 14098c2ecf20Sopenharmony_ci do_div(reserved_blocks, 100); 14108c2ecf20Sopenharmony_ci 14118c2ecf20Sopenharmony_ci lock_buffer(sbi->s_sbh); 14128c2ecf20Sopenharmony_ci ext4_blocks_count_set(es, ext4_blocks_count(es) + blocks_count); 14138c2ecf20Sopenharmony_ci ext4_free_blocks_count_set(es, ext4_free_blocks_count(es) + free_blocks); 14148c2ecf20Sopenharmony_ci le32_add_cpu(&es->s_inodes_count, EXT4_INODES_PER_GROUP(sb) * 14158c2ecf20Sopenharmony_ci flex_gd->count); 14168c2ecf20Sopenharmony_ci le32_add_cpu(&es->s_free_inodes_count, EXT4_INODES_PER_GROUP(sb) * 14178c2ecf20Sopenharmony_ci flex_gd->count); 14188c2ecf20Sopenharmony_ci 14198c2ecf20Sopenharmony_ci ext4_debug("free blocks count %llu", ext4_free_blocks_count(es)); 14208c2ecf20Sopenharmony_ci /* 14218c2ecf20Sopenharmony_ci * We need to protect s_groups_count against other CPUs seeing 14228c2ecf20Sopenharmony_ci * inconsistent state in the superblock. 14238c2ecf20Sopenharmony_ci * 14248c2ecf20Sopenharmony_ci * The precise rules we use are: 14258c2ecf20Sopenharmony_ci * 14268c2ecf20Sopenharmony_ci * * Writers must perform a smp_wmb() after updating all 14278c2ecf20Sopenharmony_ci * dependent data and before modifying the groups count 14288c2ecf20Sopenharmony_ci * 14298c2ecf20Sopenharmony_ci * * Readers must perform an smp_rmb() after reading the groups 14308c2ecf20Sopenharmony_ci * count and before reading any dependent data. 14318c2ecf20Sopenharmony_ci * 14328c2ecf20Sopenharmony_ci * NB. These rules can be relaxed when checking the group count 14338c2ecf20Sopenharmony_ci * while freeing data, as we can only allocate from a block 14348c2ecf20Sopenharmony_ci * group after serialising against the group count, and we can 14358c2ecf20Sopenharmony_ci * only then free after serialising in turn against that 14368c2ecf20Sopenharmony_ci * allocation. 14378c2ecf20Sopenharmony_ci */ 14388c2ecf20Sopenharmony_ci smp_wmb(); 14398c2ecf20Sopenharmony_ci 14408c2ecf20Sopenharmony_ci /* Update the global fs size fields */ 14418c2ecf20Sopenharmony_ci sbi->s_groups_count += flex_gd->count; 14428c2ecf20Sopenharmony_ci sbi->s_blockfile_groups = min_t(ext4_group_t, sbi->s_groups_count, 14438c2ecf20Sopenharmony_ci (EXT4_MAX_BLOCK_FILE_PHYS / EXT4_BLOCKS_PER_GROUP(sb))); 14448c2ecf20Sopenharmony_ci 14458c2ecf20Sopenharmony_ci /* Update the reserved block counts only once the new group is 14468c2ecf20Sopenharmony_ci * active. */ 14478c2ecf20Sopenharmony_ci ext4_r_blocks_count_set(es, ext4_r_blocks_count(es) + 14488c2ecf20Sopenharmony_ci reserved_blocks); 14498c2ecf20Sopenharmony_ci ext4_superblock_csum_set(sb); 14508c2ecf20Sopenharmony_ci unlock_buffer(sbi->s_sbh); 14518c2ecf20Sopenharmony_ci 14528c2ecf20Sopenharmony_ci /* Update the free space counts */ 14538c2ecf20Sopenharmony_ci percpu_counter_add(&sbi->s_freeclusters_counter, 14548c2ecf20Sopenharmony_ci EXT4_NUM_B2C(sbi, free_blocks)); 14558c2ecf20Sopenharmony_ci percpu_counter_add(&sbi->s_freeinodes_counter, 14568c2ecf20Sopenharmony_ci EXT4_INODES_PER_GROUP(sb) * flex_gd->count); 14578c2ecf20Sopenharmony_ci 14588c2ecf20Sopenharmony_ci ext4_debug("free blocks count %llu", 14598c2ecf20Sopenharmony_ci percpu_counter_read(&sbi->s_freeclusters_counter)); 14608c2ecf20Sopenharmony_ci if (ext4_has_feature_flex_bg(sb) && sbi->s_log_groups_per_flex) { 14618c2ecf20Sopenharmony_ci ext4_group_t flex_group; 14628c2ecf20Sopenharmony_ci struct flex_groups *fg; 14638c2ecf20Sopenharmony_ci 14648c2ecf20Sopenharmony_ci flex_group = ext4_flex_group(sbi, group_data[0].group); 14658c2ecf20Sopenharmony_ci fg = sbi_array_rcu_deref(sbi, s_flex_groups, flex_group); 14668c2ecf20Sopenharmony_ci atomic64_add(EXT4_NUM_B2C(sbi, free_blocks), 14678c2ecf20Sopenharmony_ci &fg->free_clusters); 14688c2ecf20Sopenharmony_ci atomic_add(EXT4_INODES_PER_GROUP(sb) * flex_gd->count, 14698c2ecf20Sopenharmony_ci &fg->free_inodes); 14708c2ecf20Sopenharmony_ci } 14718c2ecf20Sopenharmony_ci 14728c2ecf20Sopenharmony_ci /* 14738c2ecf20Sopenharmony_ci * Update the fs overhead information 14748c2ecf20Sopenharmony_ci */ 14758c2ecf20Sopenharmony_ci ext4_calculate_overhead(sb); 14768c2ecf20Sopenharmony_ci es->s_overhead_clusters = cpu_to_le32(sbi->s_overhead); 14778c2ecf20Sopenharmony_ci 14788c2ecf20Sopenharmony_ci if (test_opt(sb, DEBUG)) 14798c2ecf20Sopenharmony_ci printk(KERN_DEBUG "EXT4-fs: added group %u:" 14808c2ecf20Sopenharmony_ci "%llu blocks(%llu free %llu reserved)\n", flex_gd->count, 14818c2ecf20Sopenharmony_ci blocks_count, free_blocks, reserved_blocks); 14828c2ecf20Sopenharmony_ci} 14838c2ecf20Sopenharmony_ci 14848c2ecf20Sopenharmony_ci/* Add a flex group to an fs. Ensure we handle all possible error conditions 14858c2ecf20Sopenharmony_ci * _before_ we start modifying the filesystem, because we cannot abort the 14868c2ecf20Sopenharmony_ci * transaction and not have it write the data to disk. 14878c2ecf20Sopenharmony_ci */ 14888c2ecf20Sopenharmony_cistatic int ext4_flex_group_add(struct super_block *sb, 14898c2ecf20Sopenharmony_ci struct inode *resize_inode, 14908c2ecf20Sopenharmony_ci struct ext4_new_flex_group_data *flex_gd) 14918c2ecf20Sopenharmony_ci{ 14928c2ecf20Sopenharmony_ci struct ext4_sb_info *sbi = EXT4_SB(sb); 14938c2ecf20Sopenharmony_ci struct ext4_super_block *es = sbi->s_es; 14948c2ecf20Sopenharmony_ci ext4_fsblk_t o_blocks_count; 14958c2ecf20Sopenharmony_ci ext4_grpblk_t last; 14968c2ecf20Sopenharmony_ci ext4_group_t group; 14978c2ecf20Sopenharmony_ci handle_t *handle; 14988c2ecf20Sopenharmony_ci unsigned reserved_gdb; 14998c2ecf20Sopenharmony_ci int err = 0, err2 = 0, credit; 15008c2ecf20Sopenharmony_ci 15018c2ecf20Sopenharmony_ci BUG_ON(!flex_gd->count || !flex_gd->groups || !flex_gd->bg_flags); 15028c2ecf20Sopenharmony_ci 15038c2ecf20Sopenharmony_ci reserved_gdb = le16_to_cpu(es->s_reserved_gdt_blocks); 15048c2ecf20Sopenharmony_ci o_blocks_count = ext4_blocks_count(es); 15058c2ecf20Sopenharmony_ci ext4_get_group_no_and_offset(sb, o_blocks_count, &group, &last); 15068c2ecf20Sopenharmony_ci BUG_ON(last); 15078c2ecf20Sopenharmony_ci 15088c2ecf20Sopenharmony_ci err = setup_new_flex_group_blocks(sb, flex_gd); 15098c2ecf20Sopenharmony_ci if (err) 15108c2ecf20Sopenharmony_ci goto exit; 15118c2ecf20Sopenharmony_ci /* 15128c2ecf20Sopenharmony_ci * We will always be modifying at least the superblock and GDT 15138c2ecf20Sopenharmony_ci * blocks. If we are adding a group past the last current GDT block, 15148c2ecf20Sopenharmony_ci * we will also modify the inode and the dindirect block. If we 15158c2ecf20Sopenharmony_ci * are adding a group with superblock/GDT backups we will also 15168c2ecf20Sopenharmony_ci * modify each of the reserved GDT dindirect blocks. 15178c2ecf20Sopenharmony_ci */ 15188c2ecf20Sopenharmony_ci credit = 3; /* sb, resize inode, resize inode dindirect */ 15198c2ecf20Sopenharmony_ci /* GDT blocks */ 15208c2ecf20Sopenharmony_ci credit += 1 + DIV_ROUND_UP(flex_gd->count, EXT4_DESC_PER_BLOCK(sb)); 15218c2ecf20Sopenharmony_ci credit += reserved_gdb; /* Reserved GDT dindirect blocks */ 15228c2ecf20Sopenharmony_ci handle = ext4_journal_start_sb(sb, EXT4_HT_RESIZE, credit); 15238c2ecf20Sopenharmony_ci if (IS_ERR(handle)) { 15248c2ecf20Sopenharmony_ci err = PTR_ERR(handle); 15258c2ecf20Sopenharmony_ci goto exit; 15268c2ecf20Sopenharmony_ci } 15278c2ecf20Sopenharmony_ci 15288c2ecf20Sopenharmony_ci BUFFER_TRACE(sbi->s_sbh, "get_write_access"); 15298c2ecf20Sopenharmony_ci err = ext4_journal_get_write_access(handle, sbi->s_sbh); 15308c2ecf20Sopenharmony_ci if (err) 15318c2ecf20Sopenharmony_ci goto exit_journal; 15328c2ecf20Sopenharmony_ci 15338c2ecf20Sopenharmony_ci group = flex_gd->groups[0].group; 15348c2ecf20Sopenharmony_ci BUG_ON(group != sbi->s_groups_count); 15358c2ecf20Sopenharmony_ci err = ext4_add_new_descs(handle, sb, group, 15368c2ecf20Sopenharmony_ci resize_inode, flex_gd->count); 15378c2ecf20Sopenharmony_ci if (err) 15388c2ecf20Sopenharmony_ci goto exit_journal; 15398c2ecf20Sopenharmony_ci 15408c2ecf20Sopenharmony_ci err = ext4_setup_new_descs(handle, sb, flex_gd); 15418c2ecf20Sopenharmony_ci if (err) 15428c2ecf20Sopenharmony_ci goto exit_journal; 15438c2ecf20Sopenharmony_ci 15448c2ecf20Sopenharmony_ci ext4_update_super(sb, flex_gd); 15458c2ecf20Sopenharmony_ci 15468c2ecf20Sopenharmony_ci err = ext4_handle_dirty_metadata(handle, NULL, sbi->s_sbh); 15478c2ecf20Sopenharmony_ci 15488c2ecf20Sopenharmony_ciexit_journal: 15498c2ecf20Sopenharmony_ci err2 = ext4_journal_stop(handle); 15508c2ecf20Sopenharmony_ci if (!err) 15518c2ecf20Sopenharmony_ci err = err2; 15528c2ecf20Sopenharmony_ci 15538c2ecf20Sopenharmony_ci if (!err) { 15548c2ecf20Sopenharmony_ci int gdb_num = group / EXT4_DESC_PER_BLOCK(sb); 15558c2ecf20Sopenharmony_ci int gdb_num_end = ((group + flex_gd->count - 1) / 15568c2ecf20Sopenharmony_ci EXT4_DESC_PER_BLOCK(sb)); 15578c2ecf20Sopenharmony_ci int meta_bg = ext4_has_feature_meta_bg(sb) && 15588c2ecf20Sopenharmony_ci gdb_num >= le32_to_cpu(es->s_first_meta_bg); 15598c2ecf20Sopenharmony_ci sector_t padding_blocks = meta_bg ? 0 : sbi->s_sbh->b_blocknr - 15608c2ecf20Sopenharmony_ci ext4_group_first_block_no(sb, 0); 15618c2ecf20Sopenharmony_ci sector_t old_gdb = 0; 15628c2ecf20Sopenharmony_ci 15638c2ecf20Sopenharmony_ci update_backups(sb, ext4_group_first_block_no(sb, 0), 15648c2ecf20Sopenharmony_ci (char *)es, sizeof(struct ext4_super_block), 0); 15658c2ecf20Sopenharmony_ci for (; gdb_num <= gdb_num_end; gdb_num++) { 15668c2ecf20Sopenharmony_ci struct buffer_head *gdb_bh; 15678c2ecf20Sopenharmony_ci 15688c2ecf20Sopenharmony_ci gdb_bh = sbi_array_rcu_deref(sbi, s_group_desc, 15698c2ecf20Sopenharmony_ci gdb_num); 15708c2ecf20Sopenharmony_ci if (old_gdb == gdb_bh->b_blocknr) 15718c2ecf20Sopenharmony_ci continue; 15728c2ecf20Sopenharmony_ci update_backups(sb, gdb_bh->b_blocknr - padding_blocks, 15738c2ecf20Sopenharmony_ci gdb_bh->b_data, gdb_bh->b_size, meta_bg); 15748c2ecf20Sopenharmony_ci old_gdb = gdb_bh->b_blocknr; 15758c2ecf20Sopenharmony_ci } 15768c2ecf20Sopenharmony_ci } 15778c2ecf20Sopenharmony_ciexit: 15788c2ecf20Sopenharmony_ci return err; 15798c2ecf20Sopenharmony_ci} 15808c2ecf20Sopenharmony_ci 15818c2ecf20Sopenharmony_cistatic int ext4_setup_next_flex_gd(struct super_block *sb, 15828c2ecf20Sopenharmony_ci struct ext4_new_flex_group_data *flex_gd, 15838c2ecf20Sopenharmony_ci ext4_fsblk_t n_blocks_count) 15848c2ecf20Sopenharmony_ci{ 15858c2ecf20Sopenharmony_ci struct ext4_sb_info *sbi = EXT4_SB(sb); 15868c2ecf20Sopenharmony_ci struct ext4_super_block *es = sbi->s_es; 15878c2ecf20Sopenharmony_ci struct ext4_new_group_data *group_data = flex_gd->groups; 15888c2ecf20Sopenharmony_ci ext4_fsblk_t o_blocks_count; 15898c2ecf20Sopenharmony_ci ext4_group_t n_group; 15908c2ecf20Sopenharmony_ci ext4_group_t group; 15918c2ecf20Sopenharmony_ci ext4_group_t last_group; 15928c2ecf20Sopenharmony_ci ext4_grpblk_t last; 15938c2ecf20Sopenharmony_ci ext4_grpblk_t clusters_per_group; 15948c2ecf20Sopenharmony_ci unsigned long i; 15958c2ecf20Sopenharmony_ci 15968c2ecf20Sopenharmony_ci clusters_per_group = EXT4_CLUSTERS_PER_GROUP(sb); 15978c2ecf20Sopenharmony_ci 15988c2ecf20Sopenharmony_ci o_blocks_count = ext4_blocks_count(es); 15998c2ecf20Sopenharmony_ci 16008c2ecf20Sopenharmony_ci if (o_blocks_count == n_blocks_count) 16018c2ecf20Sopenharmony_ci return 0; 16028c2ecf20Sopenharmony_ci 16038c2ecf20Sopenharmony_ci ext4_get_group_no_and_offset(sb, o_blocks_count, &group, &last); 16048c2ecf20Sopenharmony_ci BUG_ON(last); 16058c2ecf20Sopenharmony_ci ext4_get_group_no_and_offset(sb, n_blocks_count - 1, &n_group, &last); 16068c2ecf20Sopenharmony_ci 16078c2ecf20Sopenharmony_ci last_group = group | (flex_gd->resize_bg - 1); 16088c2ecf20Sopenharmony_ci if (last_group > n_group) 16098c2ecf20Sopenharmony_ci last_group = n_group; 16108c2ecf20Sopenharmony_ci 16118c2ecf20Sopenharmony_ci flex_gd->count = last_group - group + 1; 16128c2ecf20Sopenharmony_ci 16138c2ecf20Sopenharmony_ci for (i = 0; i < flex_gd->count; i++) { 16148c2ecf20Sopenharmony_ci int overhead; 16158c2ecf20Sopenharmony_ci 16168c2ecf20Sopenharmony_ci group_data[i].group = group + i; 16178c2ecf20Sopenharmony_ci group_data[i].blocks_count = EXT4_BLOCKS_PER_GROUP(sb); 16188c2ecf20Sopenharmony_ci overhead = ext4_group_overhead_blocks(sb, group + i); 16198c2ecf20Sopenharmony_ci group_data[i].mdata_blocks = overhead; 16208c2ecf20Sopenharmony_ci group_data[i].free_clusters_count = EXT4_CLUSTERS_PER_GROUP(sb); 16218c2ecf20Sopenharmony_ci if (ext4_has_group_desc_csum(sb)) { 16228c2ecf20Sopenharmony_ci flex_gd->bg_flags[i] = EXT4_BG_BLOCK_UNINIT | 16238c2ecf20Sopenharmony_ci EXT4_BG_INODE_UNINIT; 16248c2ecf20Sopenharmony_ci if (!test_opt(sb, INIT_INODE_TABLE)) 16258c2ecf20Sopenharmony_ci flex_gd->bg_flags[i] |= EXT4_BG_INODE_ZEROED; 16268c2ecf20Sopenharmony_ci } else 16278c2ecf20Sopenharmony_ci flex_gd->bg_flags[i] = EXT4_BG_INODE_ZEROED; 16288c2ecf20Sopenharmony_ci } 16298c2ecf20Sopenharmony_ci 16308c2ecf20Sopenharmony_ci if (last_group == n_group && ext4_has_group_desc_csum(sb)) 16318c2ecf20Sopenharmony_ci /* We need to initialize block bitmap of last group. */ 16328c2ecf20Sopenharmony_ci flex_gd->bg_flags[i - 1] &= ~EXT4_BG_BLOCK_UNINIT; 16338c2ecf20Sopenharmony_ci 16348c2ecf20Sopenharmony_ci if ((last_group == n_group) && (last != clusters_per_group - 1)) { 16358c2ecf20Sopenharmony_ci group_data[i - 1].blocks_count = EXT4_C2B(sbi, last + 1); 16368c2ecf20Sopenharmony_ci group_data[i - 1].free_clusters_count -= clusters_per_group - 16378c2ecf20Sopenharmony_ci last - 1; 16388c2ecf20Sopenharmony_ci } 16398c2ecf20Sopenharmony_ci 16408c2ecf20Sopenharmony_ci return 1; 16418c2ecf20Sopenharmony_ci} 16428c2ecf20Sopenharmony_ci 16438c2ecf20Sopenharmony_ci/* Add group descriptor data to an existing or new group descriptor block. 16448c2ecf20Sopenharmony_ci * Ensure we handle all possible error conditions _before_ we start modifying 16458c2ecf20Sopenharmony_ci * the filesystem, because we cannot abort the transaction and not have it 16468c2ecf20Sopenharmony_ci * write the data to disk. 16478c2ecf20Sopenharmony_ci * 16488c2ecf20Sopenharmony_ci * If we are on a GDT block boundary, we need to get the reserved GDT block. 16498c2ecf20Sopenharmony_ci * Otherwise, we may need to add backup GDT blocks for a sparse group. 16508c2ecf20Sopenharmony_ci * 16518c2ecf20Sopenharmony_ci * We only need to hold the superblock lock while we are actually adding 16528c2ecf20Sopenharmony_ci * in the new group's counts to the superblock. Prior to that we have 16538c2ecf20Sopenharmony_ci * not really "added" the group at all. We re-check that we are still 16548c2ecf20Sopenharmony_ci * adding in the last group in case things have changed since verifying. 16558c2ecf20Sopenharmony_ci */ 16568c2ecf20Sopenharmony_ciint ext4_group_add(struct super_block *sb, struct ext4_new_group_data *input) 16578c2ecf20Sopenharmony_ci{ 16588c2ecf20Sopenharmony_ci struct ext4_new_flex_group_data flex_gd; 16598c2ecf20Sopenharmony_ci struct ext4_sb_info *sbi = EXT4_SB(sb); 16608c2ecf20Sopenharmony_ci struct ext4_super_block *es = sbi->s_es; 16618c2ecf20Sopenharmony_ci int reserved_gdb = ext4_bg_has_super(sb, input->group) ? 16628c2ecf20Sopenharmony_ci le16_to_cpu(es->s_reserved_gdt_blocks) : 0; 16638c2ecf20Sopenharmony_ci struct inode *inode = NULL; 16648c2ecf20Sopenharmony_ci int gdb_off; 16658c2ecf20Sopenharmony_ci int err; 16668c2ecf20Sopenharmony_ci __u16 bg_flags = 0; 16678c2ecf20Sopenharmony_ci 16688c2ecf20Sopenharmony_ci gdb_off = input->group % EXT4_DESC_PER_BLOCK(sb); 16698c2ecf20Sopenharmony_ci 16708c2ecf20Sopenharmony_ci if (gdb_off == 0 && !ext4_has_feature_sparse_super(sb)) { 16718c2ecf20Sopenharmony_ci ext4_warning(sb, "Can't resize non-sparse filesystem further"); 16728c2ecf20Sopenharmony_ci return -EPERM; 16738c2ecf20Sopenharmony_ci } 16748c2ecf20Sopenharmony_ci 16758c2ecf20Sopenharmony_ci if (ext4_blocks_count(es) + input->blocks_count < 16768c2ecf20Sopenharmony_ci ext4_blocks_count(es)) { 16778c2ecf20Sopenharmony_ci ext4_warning(sb, "blocks_count overflow"); 16788c2ecf20Sopenharmony_ci return -EINVAL; 16798c2ecf20Sopenharmony_ci } 16808c2ecf20Sopenharmony_ci 16818c2ecf20Sopenharmony_ci if (le32_to_cpu(es->s_inodes_count) + EXT4_INODES_PER_GROUP(sb) < 16828c2ecf20Sopenharmony_ci le32_to_cpu(es->s_inodes_count)) { 16838c2ecf20Sopenharmony_ci ext4_warning(sb, "inodes_count overflow"); 16848c2ecf20Sopenharmony_ci return -EINVAL; 16858c2ecf20Sopenharmony_ci } 16868c2ecf20Sopenharmony_ci 16878c2ecf20Sopenharmony_ci if (reserved_gdb || gdb_off == 0) { 16888c2ecf20Sopenharmony_ci if (!ext4_has_feature_resize_inode(sb) || 16898c2ecf20Sopenharmony_ci !le16_to_cpu(es->s_reserved_gdt_blocks)) { 16908c2ecf20Sopenharmony_ci ext4_warning(sb, 16918c2ecf20Sopenharmony_ci "No reserved GDT blocks, can't resize"); 16928c2ecf20Sopenharmony_ci return -EPERM; 16938c2ecf20Sopenharmony_ci } 16948c2ecf20Sopenharmony_ci inode = ext4_iget(sb, EXT4_RESIZE_INO, EXT4_IGET_SPECIAL); 16958c2ecf20Sopenharmony_ci if (IS_ERR(inode)) { 16968c2ecf20Sopenharmony_ci ext4_warning(sb, "Error opening resize inode"); 16978c2ecf20Sopenharmony_ci return PTR_ERR(inode); 16988c2ecf20Sopenharmony_ci } 16998c2ecf20Sopenharmony_ci } 17008c2ecf20Sopenharmony_ci 17018c2ecf20Sopenharmony_ci 17028c2ecf20Sopenharmony_ci err = verify_group_input(sb, input); 17038c2ecf20Sopenharmony_ci if (err) 17048c2ecf20Sopenharmony_ci goto out; 17058c2ecf20Sopenharmony_ci 17068c2ecf20Sopenharmony_ci err = ext4_alloc_flex_bg_array(sb, input->group + 1); 17078c2ecf20Sopenharmony_ci if (err) 17088c2ecf20Sopenharmony_ci goto out; 17098c2ecf20Sopenharmony_ci 17108c2ecf20Sopenharmony_ci err = ext4_mb_alloc_groupinfo(sb, input->group + 1); 17118c2ecf20Sopenharmony_ci if (err) 17128c2ecf20Sopenharmony_ci goto out; 17138c2ecf20Sopenharmony_ci 17148c2ecf20Sopenharmony_ci flex_gd.count = 1; 17158c2ecf20Sopenharmony_ci flex_gd.groups = input; 17168c2ecf20Sopenharmony_ci flex_gd.bg_flags = &bg_flags; 17178c2ecf20Sopenharmony_ci err = ext4_flex_group_add(sb, inode, &flex_gd); 17188c2ecf20Sopenharmony_ciout: 17198c2ecf20Sopenharmony_ci iput(inode); 17208c2ecf20Sopenharmony_ci return err; 17218c2ecf20Sopenharmony_ci} /* ext4_group_add */ 17228c2ecf20Sopenharmony_ci 17238c2ecf20Sopenharmony_ci/* 17248c2ecf20Sopenharmony_ci * extend a group without checking assuming that checking has been done. 17258c2ecf20Sopenharmony_ci */ 17268c2ecf20Sopenharmony_cistatic int ext4_group_extend_no_check(struct super_block *sb, 17278c2ecf20Sopenharmony_ci ext4_fsblk_t o_blocks_count, ext4_grpblk_t add) 17288c2ecf20Sopenharmony_ci{ 17298c2ecf20Sopenharmony_ci struct ext4_super_block *es = EXT4_SB(sb)->s_es; 17308c2ecf20Sopenharmony_ci handle_t *handle; 17318c2ecf20Sopenharmony_ci int err = 0, err2; 17328c2ecf20Sopenharmony_ci 17338c2ecf20Sopenharmony_ci /* We will update the superblock, one block bitmap, and 17348c2ecf20Sopenharmony_ci * one group descriptor via ext4_group_add_blocks(). 17358c2ecf20Sopenharmony_ci */ 17368c2ecf20Sopenharmony_ci handle = ext4_journal_start_sb(sb, EXT4_HT_RESIZE, 3); 17378c2ecf20Sopenharmony_ci if (IS_ERR(handle)) { 17388c2ecf20Sopenharmony_ci err = PTR_ERR(handle); 17398c2ecf20Sopenharmony_ci ext4_warning(sb, "error %d on journal start", err); 17408c2ecf20Sopenharmony_ci return err; 17418c2ecf20Sopenharmony_ci } 17428c2ecf20Sopenharmony_ci 17438c2ecf20Sopenharmony_ci BUFFER_TRACE(EXT4_SB(sb)->s_sbh, "get_write_access"); 17448c2ecf20Sopenharmony_ci err = ext4_journal_get_write_access(handle, EXT4_SB(sb)->s_sbh); 17458c2ecf20Sopenharmony_ci if (err) { 17468c2ecf20Sopenharmony_ci ext4_warning(sb, "error %d on journal write access", err); 17478c2ecf20Sopenharmony_ci goto errout; 17488c2ecf20Sopenharmony_ci } 17498c2ecf20Sopenharmony_ci 17508c2ecf20Sopenharmony_ci lock_buffer(EXT4_SB(sb)->s_sbh); 17518c2ecf20Sopenharmony_ci ext4_blocks_count_set(es, o_blocks_count + add); 17528c2ecf20Sopenharmony_ci ext4_free_blocks_count_set(es, ext4_free_blocks_count(es) + add); 17538c2ecf20Sopenharmony_ci ext4_superblock_csum_set(sb); 17548c2ecf20Sopenharmony_ci unlock_buffer(EXT4_SB(sb)->s_sbh); 17558c2ecf20Sopenharmony_ci ext4_debug("freeing blocks %llu through %llu\n", o_blocks_count, 17568c2ecf20Sopenharmony_ci o_blocks_count + add); 17578c2ecf20Sopenharmony_ci /* We add the blocks to the bitmap and set the group need init bit */ 17588c2ecf20Sopenharmony_ci err = ext4_group_add_blocks(handle, sb, o_blocks_count, add); 17598c2ecf20Sopenharmony_ci if (err) 17608c2ecf20Sopenharmony_ci goto errout; 17618c2ecf20Sopenharmony_ci ext4_handle_dirty_metadata(handle, NULL, EXT4_SB(sb)->s_sbh); 17628c2ecf20Sopenharmony_ci ext4_debug("freed blocks %llu through %llu\n", o_blocks_count, 17638c2ecf20Sopenharmony_ci o_blocks_count + add); 17648c2ecf20Sopenharmony_cierrout: 17658c2ecf20Sopenharmony_ci err2 = ext4_journal_stop(handle); 17668c2ecf20Sopenharmony_ci if (err2 && !err) 17678c2ecf20Sopenharmony_ci err = err2; 17688c2ecf20Sopenharmony_ci 17698c2ecf20Sopenharmony_ci if (!err) { 17708c2ecf20Sopenharmony_ci if (test_opt(sb, DEBUG)) 17718c2ecf20Sopenharmony_ci printk(KERN_DEBUG "EXT4-fs: extended group to %llu " 17728c2ecf20Sopenharmony_ci "blocks\n", ext4_blocks_count(es)); 17738c2ecf20Sopenharmony_ci update_backups(sb, ext4_group_first_block_no(sb, 0), 17748c2ecf20Sopenharmony_ci (char *)es, sizeof(struct ext4_super_block), 0); 17758c2ecf20Sopenharmony_ci } 17768c2ecf20Sopenharmony_ci return err; 17778c2ecf20Sopenharmony_ci} 17788c2ecf20Sopenharmony_ci 17798c2ecf20Sopenharmony_ci/* 17808c2ecf20Sopenharmony_ci * Extend the filesystem to the new number of blocks specified. This entry 17818c2ecf20Sopenharmony_ci * point is only used to extend the current filesystem to the end of the last 17828c2ecf20Sopenharmony_ci * existing group. It can be accessed via ioctl, or by "remount,resize=<size>" 17838c2ecf20Sopenharmony_ci * for emergencies (because it has no dependencies on reserved blocks). 17848c2ecf20Sopenharmony_ci * 17858c2ecf20Sopenharmony_ci * If we _really_ wanted, we could use default values to call ext4_group_add() 17868c2ecf20Sopenharmony_ci * allow the "remount" trick to work for arbitrary resizing, assuming enough 17878c2ecf20Sopenharmony_ci * GDT blocks are reserved to grow to the desired size. 17888c2ecf20Sopenharmony_ci */ 17898c2ecf20Sopenharmony_ciint ext4_group_extend(struct super_block *sb, struct ext4_super_block *es, 17908c2ecf20Sopenharmony_ci ext4_fsblk_t n_blocks_count) 17918c2ecf20Sopenharmony_ci{ 17928c2ecf20Sopenharmony_ci ext4_fsblk_t o_blocks_count; 17938c2ecf20Sopenharmony_ci ext4_grpblk_t last; 17948c2ecf20Sopenharmony_ci ext4_grpblk_t add; 17958c2ecf20Sopenharmony_ci struct buffer_head *bh; 17968c2ecf20Sopenharmony_ci int err; 17978c2ecf20Sopenharmony_ci ext4_group_t group; 17988c2ecf20Sopenharmony_ci 17998c2ecf20Sopenharmony_ci o_blocks_count = ext4_blocks_count(es); 18008c2ecf20Sopenharmony_ci 18018c2ecf20Sopenharmony_ci if (test_opt(sb, DEBUG)) 18028c2ecf20Sopenharmony_ci ext4_msg(sb, KERN_DEBUG, 18038c2ecf20Sopenharmony_ci "extending last group from %llu to %llu blocks", 18048c2ecf20Sopenharmony_ci o_blocks_count, n_blocks_count); 18058c2ecf20Sopenharmony_ci 18068c2ecf20Sopenharmony_ci if (n_blocks_count == 0 || n_blocks_count == o_blocks_count) 18078c2ecf20Sopenharmony_ci return 0; 18088c2ecf20Sopenharmony_ci 18098c2ecf20Sopenharmony_ci if (n_blocks_count > (sector_t)(~0ULL) >> (sb->s_blocksize_bits - 9)) { 18108c2ecf20Sopenharmony_ci ext4_msg(sb, KERN_ERR, 18118c2ecf20Sopenharmony_ci "filesystem too large to resize to %llu blocks safely", 18128c2ecf20Sopenharmony_ci n_blocks_count); 18138c2ecf20Sopenharmony_ci return -EINVAL; 18148c2ecf20Sopenharmony_ci } 18158c2ecf20Sopenharmony_ci 18168c2ecf20Sopenharmony_ci if (n_blocks_count < o_blocks_count) { 18178c2ecf20Sopenharmony_ci ext4_warning(sb, "can't shrink FS - resize aborted"); 18188c2ecf20Sopenharmony_ci return -EINVAL; 18198c2ecf20Sopenharmony_ci } 18208c2ecf20Sopenharmony_ci 18218c2ecf20Sopenharmony_ci /* Handle the remaining blocks in the last group only. */ 18228c2ecf20Sopenharmony_ci ext4_get_group_no_and_offset(sb, o_blocks_count, &group, &last); 18238c2ecf20Sopenharmony_ci 18248c2ecf20Sopenharmony_ci if (last == 0) { 18258c2ecf20Sopenharmony_ci ext4_warning(sb, "need to use ext2online to resize further"); 18268c2ecf20Sopenharmony_ci return -EPERM; 18278c2ecf20Sopenharmony_ci } 18288c2ecf20Sopenharmony_ci 18298c2ecf20Sopenharmony_ci add = EXT4_BLOCKS_PER_GROUP(sb) - last; 18308c2ecf20Sopenharmony_ci 18318c2ecf20Sopenharmony_ci if (o_blocks_count + add < o_blocks_count) { 18328c2ecf20Sopenharmony_ci ext4_warning(sb, "blocks_count overflow"); 18338c2ecf20Sopenharmony_ci return -EINVAL; 18348c2ecf20Sopenharmony_ci } 18358c2ecf20Sopenharmony_ci 18368c2ecf20Sopenharmony_ci if (o_blocks_count + add > n_blocks_count) 18378c2ecf20Sopenharmony_ci add = n_blocks_count - o_blocks_count; 18388c2ecf20Sopenharmony_ci 18398c2ecf20Sopenharmony_ci if (o_blocks_count + add < n_blocks_count) 18408c2ecf20Sopenharmony_ci ext4_warning(sb, "will only finish group (%llu blocks, %u new)", 18418c2ecf20Sopenharmony_ci o_blocks_count + add, add); 18428c2ecf20Sopenharmony_ci 18438c2ecf20Sopenharmony_ci /* See if the device is actually as big as what was requested */ 18448c2ecf20Sopenharmony_ci bh = ext4_sb_bread(sb, o_blocks_count + add - 1, 0); 18458c2ecf20Sopenharmony_ci if (IS_ERR(bh)) { 18468c2ecf20Sopenharmony_ci ext4_warning(sb, "can't read last block, resize aborted"); 18478c2ecf20Sopenharmony_ci return -ENOSPC; 18488c2ecf20Sopenharmony_ci } 18498c2ecf20Sopenharmony_ci brelse(bh); 18508c2ecf20Sopenharmony_ci 18518c2ecf20Sopenharmony_ci err = ext4_group_extend_no_check(sb, o_blocks_count, add); 18528c2ecf20Sopenharmony_ci return err; 18538c2ecf20Sopenharmony_ci} /* ext4_group_extend */ 18548c2ecf20Sopenharmony_ci 18558c2ecf20Sopenharmony_ci 18568c2ecf20Sopenharmony_cistatic int num_desc_blocks(struct super_block *sb, ext4_group_t groups) 18578c2ecf20Sopenharmony_ci{ 18588c2ecf20Sopenharmony_ci return (groups + EXT4_DESC_PER_BLOCK(sb) - 1) / EXT4_DESC_PER_BLOCK(sb); 18598c2ecf20Sopenharmony_ci} 18608c2ecf20Sopenharmony_ci 18618c2ecf20Sopenharmony_ci/* 18628c2ecf20Sopenharmony_ci * Release the resize inode and drop the resize_inode feature if there 18638c2ecf20Sopenharmony_ci * are no more reserved gdt blocks, and then convert the file system 18648c2ecf20Sopenharmony_ci * to enable meta_bg 18658c2ecf20Sopenharmony_ci */ 18668c2ecf20Sopenharmony_cistatic int ext4_convert_meta_bg(struct super_block *sb, struct inode *inode) 18678c2ecf20Sopenharmony_ci{ 18688c2ecf20Sopenharmony_ci handle_t *handle; 18698c2ecf20Sopenharmony_ci struct ext4_sb_info *sbi = EXT4_SB(sb); 18708c2ecf20Sopenharmony_ci struct ext4_super_block *es = sbi->s_es; 18718c2ecf20Sopenharmony_ci struct ext4_inode_info *ei = EXT4_I(inode); 18728c2ecf20Sopenharmony_ci ext4_fsblk_t nr; 18738c2ecf20Sopenharmony_ci int i, ret, err = 0; 18748c2ecf20Sopenharmony_ci int credits = 1; 18758c2ecf20Sopenharmony_ci 18768c2ecf20Sopenharmony_ci ext4_msg(sb, KERN_INFO, "Converting file system to meta_bg"); 18778c2ecf20Sopenharmony_ci if (inode) { 18788c2ecf20Sopenharmony_ci if (es->s_reserved_gdt_blocks) { 18798c2ecf20Sopenharmony_ci ext4_error(sb, "Unexpected non-zero " 18808c2ecf20Sopenharmony_ci "s_reserved_gdt_blocks"); 18818c2ecf20Sopenharmony_ci return -EPERM; 18828c2ecf20Sopenharmony_ci } 18838c2ecf20Sopenharmony_ci 18848c2ecf20Sopenharmony_ci /* Do a quick sanity check of the resize inode */ 18858c2ecf20Sopenharmony_ci if (inode->i_blocks != 1 << (inode->i_blkbits - 18868c2ecf20Sopenharmony_ci (9 - sbi->s_cluster_bits))) 18878c2ecf20Sopenharmony_ci goto invalid_resize_inode; 18888c2ecf20Sopenharmony_ci for (i = 0; i < EXT4_N_BLOCKS; i++) { 18898c2ecf20Sopenharmony_ci if (i == EXT4_DIND_BLOCK) { 18908c2ecf20Sopenharmony_ci if (ei->i_data[i]) 18918c2ecf20Sopenharmony_ci continue; 18928c2ecf20Sopenharmony_ci else 18938c2ecf20Sopenharmony_ci goto invalid_resize_inode; 18948c2ecf20Sopenharmony_ci } 18958c2ecf20Sopenharmony_ci if (ei->i_data[i]) 18968c2ecf20Sopenharmony_ci goto invalid_resize_inode; 18978c2ecf20Sopenharmony_ci } 18988c2ecf20Sopenharmony_ci credits += 3; /* block bitmap, bg descriptor, resize inode */ 18998c2ecf20Sopenharmony_ci } 19008c2ecf20Sopenharmony_ci 19018c2ecf20Sopenharmony_ci handle = ext4_journal_start_sb(sb, EXT4_HT_RESIZE, credits); 19028c2ecf20Sopenharmony_ci if (IS_ERR(handle)) 19038c2ecf20Sopenharmony_ci return PTR_ERR(handle); 19048c2ecf20Sopenharmony_ci 19058c2ecf20Sopenharmony_ci BUFFER_TRACE(sbi->s_sbh, "get_write_access"); 19068c2ecf20Sopenharmony_ci err = ext4_journal_get_write_access(handle, sbi->s_sbh); 19078c2ecf20Sopenharmony_ci if (err) 19088c2ecf20Sopenharmony_ci goto errout; 19098c2ecf20Sopenharmony_ci 19108c2ecf20Sopenharmony_ci lock_buffer(sbi->s_sbh); 19118c2ecf20Sopenharmony_ci ext4_clear_feature_resize_inode(sb); 19128c2ecf20Sopenharmony_ci ext4_set_feature_meta_bg(sb); 19138c2ecf20Sopenharmony_ci sbi->s_es->s_first_meta_bg = 19148c2ecf20Sopenharmony_ci cpu_to_le32(num_desc_blocks(sb, sbi->s_groups_count)); 19158c2ecf20Sopenharmony_ci ext4_superblock_csum_set(sb); 19168c2ecf20Sopenharmony_ci unlock_buffer(sbi->s_sbh); 19178c2ecf20Sopenharmony_ci 19188c2ecf20Sopenharmony_ci err = ext4_handle_dirty_metadata(handle, NULL, sbi->s_sbh); 19198c2ecf20Sopenharmony_ci if (err) { 19208c2ecf20Sopenharmony_ci ext4_std_error(sb, err); 19218c2ecf20Sopenharmony_ci goto errout; 19228c2ecf20Sopenharmony_ci } 19238c2ecf20Sopenharmony_ci 19248c2ecf20Sopenharmony_ci if (inode) { 19258c2ecf20Sopenharmony_ci nr = le32_to_cpu(ei->i_data[EXT4_DIND_BLOCK]); 19268c2ecf20Sopenharmony_ci ext4_free_blocks(handle, inode, NULL, nr, 1, 19278c2ecf20Sopenharmony_ci EXT4_FREE_BLOCKS_METADATA | 19288c2ecf20Sopenharmony_ci EXT4_FREE_BLOCKS_FORGET); 19298c2ecf20Sopenharmony_ci ei->i_data[EXT4_DIND_BLOCK] = 0; 19308c2ecf20Sopenharmony_ci inode->i_blocks = 0; 19318c2ecf20Sopenharmony_ci 19328c2ecf20Sopenharmony_ci err = ext4_mark_inode_dirty(handle, inode); 19338c2ecf20Sopenharmony_ci if (err) 19348c2ecf20Sopenharmony_ci ext4_std_error(sb, err); 19358c2ecf20Sopenharmony_ci } 19368c2ecf20Sopenharmony_ci 19378c2ecf20Sopenharmony_cierrout: 19388c2ecf20Sopenharmony_ci ret = ext4_journal_stop(handle); 19398c2ecf20Sopenharmony_ci return err ? err : ret; 19408c2ecf20Sopenharmony_ci 19418c2ecf20Sopenharmony_ciinvalid_resize_inode: 19428c2ecf20Sopenharmony_ci ext4_error(sb, "corrupted/inconsistent resize inode"); 19438c2ecf20Sopenharmony_ci return -EINVAL; 19448c2ecf20Sopenharmony_ci} 19458c2ecf20Sopenharmony_ci 19468c2ecf20Sopenharmony_ci/* 19478c2ecf20Sopenharmony_ci * ext4_resize_fs() resizes a fs to new size specified by @n_blocks_count 19488c2ecf20Sopenharmony_ci * 19498c2ecf20Sopenharmony_ci * @sb: super block of the fs to be resized 19508c2ecf20Sopenharmony_ci * @n_blocks_count: the number of blocks resides in the resized fs 19518c2ecf20Sopenharmony_ci */ 19528c2ecf20Sopenharmony_ciint ext4_resize_fs(struct super_block *sb, ext4_fsblk_t n_blocks_count) 19538c2ecf20Sopenharmony_ci{ 19548c2ecf20Sopenharmony_ci struct ext4_new_flex_group_data *flex_gd = NULL; 19558c2ecf20Sopenharmony_ci struct ext4_sb_info *sbi = EXT4_SB(sb); 19568c2ecf20Sopenharmony_ci struct ext4_super_block *es = sbi->s_es; 19578c2ecf20Sopenharmony_ci struct buffer_head *bh; 19588c2ecf20Sopenharmony_ci struct inode *resize_inode = NULL; 19598c2ecf20Sopenharmony_ci ext4_grpblk_t add, offset; 19608c2ecf20Sopenharmony_ci unsigned long n_desc_blocks; 19618c2ecf20Sopenharmony_ci unsigned long o_desc_blocks; 19628c2ecf20Sopenharmony_ci ext4_group_t o_group; 19638c2ecf20Sopenharmony_ci ext4_group_t n_group; 19648c2ecf20Sopenharmony_ci ext4_fsblk_t o_blocks_count; 19658c2ecf20Sopenharmony_ci ext4_fsblk_t n_blocks_count_retry = 0; 19668c2ecf20Sopenharmony_ci unsigned long last_update_time = 0; 19678c2ecf20Sopenharmony_ci int err = 0; 19688c2ecf20Sopenharmony_ci int meta_bg; 19698c2ecf20Sopenharmony_ci unsigned int flexbg_size = ext4_flex_bg_size(sbi); 19708c2ecf20Sopenharmony_ci 19718c2ecf20Sopenharmony_ci /* See if the device is actually as big as what was requested */ 19728c2ecf20Sopenharmony_ci bh = ext4_sb_bread(sb, n_blocks_count - 1, 0); 19738c2ecf20Sopenharmony_ci if (IS_ERR(bh)) { 19748c2ecf20Sopenharmony_ci ext4_warning(sb, "can't read last block, resize aborted"); 19758c2ecf20Sopenharmony_ci return -ENOSPC; 19768c2ecf20Sopenharmony_ci } 19778c2ecf20Sopenharmony_ci brelse(bh); 19788c2ecf20Sopenharmony_ci 19798c2ecf20Sopenharmony_ci /* 19808c2ecf20Sopenharmony_ci * For bigalloc, trim the requested size to the nearest cluster 19818c2ecf20Sopenharmony_ci * boundary to avoid creating an unusable filesystem. We do this 19828c2ecf20Sopenharmony_ci * silently, instead of returning an error, to avoid breaking 19838c2ecf20Sopenharmony_ci * callers that blindly resize the filesystem to the full size of 19848c2ecf20Sopenharmony_ci * the underlying block device. 19858c2ecf20Sopenharmony_ci */ 19868c2ecf20Sopenharmony_ci if (ext4_has_feature_bigalloc(sb)) 19878c2ecf20Sopenharmony_ci n_blocks_count &= ~((1 << EXT4_CLUSTER_BITS(sb)) - 1); 19888c2ecf20Sopenharmony_ci 19898c2ecf20Sopenharmony_ciretry: 19908c2ecf20Sopenharmony_ci o_blocks_count = ext4_blocks_count(es); 19918c2ecf20Sopenharmony_ci 19928c2ecf20Sopenharmony_ci ext4_msg(sb, KERN_INFO, "resizing filesystem from %llu " 19938c2ecf20Sopenharmony_ci "to %llu blocks", o_blocks_count, n_blocks_count); 19948c2ecf20Sopenharmony_ci 19958c2ecf20Sopenharmony_ci if (n_blocks_count < o_blocks_count) { 19968c2ecf20Sopenharmony_ci /* On-line shrinking not supported */ 19978c2ecf20Sopenharmony_ci ext4_warning(sb, "can't shrink FS - resize aborted"); 19988c2ecf20Sopenharmony_ci return -EINVAL; 19998c2ecf20Sopenharmony_ci } 20008c2ecf20Sopenharmony_ci 20018c2ecf20Sopenharmony_ci if (n_blocks_count == o_blocks_count) 20028c2ecf20Sopenharmony_ci /* Nothing need to do */ 20038c2ecf20Sopenharmony_ci return 0; 20048c2ecf20Sopenharmony_ci 20058c2ecf20Sopenharmony_ci n_group = ext4_get_group_number(sb, n_blocks_count - 1); 20068c2ecf20Sopenharmony_ci if (n_group >= (0xFFFFFFFFUL / EXT4_INODES_PER_GROUP(sb))) { 20078c2ecf20Sopenharmony_ci ext4_warning(sb, "resize would cause inodes_count overflow"); 20088c2ecf20Sopenharmony_ci return -EINVAL; 20098c2ecf20Sopenharmony_ci } 20108c2ecf20Sopenharmony_ci ext4_get_group_no_and_offset(sb, o_blocks_count - 1, &o_group, &offset); 20118c2ecf20Sopenharmony_ci 20128c2ecf20Sopenharmony_ci n_desc_blocks = num_desc_blocks(sb, n_group + 1); 20138c2ecf20Sopenharmony_ci o_desc_blocks = num_desc_blocks(sb, sbi->s_groups_count); 20148c2ecf20Sopenharmony_ci 20158c2ecf20Sopenharmony_ci meta_bg = ext4_has_feature_meta_bg(sb); 20168c2ecf20Sopenharmony_ci 20178c2ecf20Sopenharmony_ci if (ext4_has_feature_resize_inode(sb)) { 20188c2ecf20Sopenharmony_ci if (meta_bg) { 20198c2ecf20Sopenharmony_ci ext4_error(sb, "resize_inode and meta_bg enabled " 20208c2ecf20Sopenharmony_ci "simultaneously"); 20218c2ecf20Sopenharmony_ci return -EINVAL; 20228c2ecf20Sopenharmony_ci } 20238c2ecf20Sopenharmony_ci if (n_desc_blocks > o_desc_blocks + 20248c2ecf20Sopenharmony_ci le16_to_cpu(es->s_reserved_gdt_blocks)) { 20258c2ecf20Sopenharmony_ci n_blocks_count_retry = n_blocks_count; 20268c2ecf20Sopenharmony_ci n_desc_blocks = o_desc_blocks + 20278c2ecf20Sopenharmony_ci le16_to_cpu(es->s_reserved_gdt_blocks); 20288c2ecf20Sopenharmony_ci n_group = n_desc_blocks * EXT4_DESC_PER_BLOCK(sb); 20298c2ecf20Sopenharmony_ci n_blocks_count = (ext4_fsblk_t)n_group * 20308c2ecf20Sopenharmony_ci EXT4_BLOCKS_PER_GROUP(sb) + 20318c2ecf20Sopenharmony_ci le32_to_cpu(es->s_first_data_block); 20328c2ecf20Sopenharmony_ci n_group--; /* set to last group number */ 20338c2ecf20Sopenharmony_ci } 20348c2ecf20Sopenharmony_ci 20358c2ecf20Sopenharmony_ci if (!resize_inode) 20368c2ecf20Sopenharmony_ci resize_inode = ext4_iget(sb, EXT4_RESIZE_INO, 20378c2ecf20Sopenharmony_ci EXT4_IGET_SPECIAL); 20388c2ecf20Sopenharmony_ci if (IS_ERR(resize_inode)) { 20398c2ecf20Sopenharmony_ci ext4_warning(sb, "Error opening resize inode"); 20408c2ecf20Sopenharmony_ci return PTR_ERR(resize_inode); 20418c2ecf20Sopenharmony_ci } 20428c2ecf20Sopenharmony_ci } 20438c2ecf20Sopenharmony_ci 20448c2ecf20Sopenharmony_ci if ((!resize_inode && !meta_bg) || n_blocks_count == o_blocks_count) { 20458c2ecf20Sopenharmony_ci err = ext4_convert_meta_bg(sb, resize_inode); 20468c2ecf20Sopenharmony_ci if (err) 20478c2ecf20Sopenharmony_ci goto out; 20488c2ecf20Sopenharmony_ci if (resize_inode) { 20498c2ecf20Sopenharmony_ci iput(resize_inode); 20508c2ecf20Sopenharmony_ci resize_inode = NULL; 20518c2ecf20Sopenharmony_ci } 20528c2ecf20Sopenharmony_ci if (n_blocks_count_retry) { 20538c2ecf20Sopenharmony_ci n_blocks_count = n_blocks_count_retry; 20548c2ecf20Sopenharmony_ci n_blocks_count_retry = 0; 20558c2ecf20Sopenharmony_ci goto retry; 20568c2ecf20Sopenharmony_ci } 20578c2ecf20Sopenharmony_ci } 20588c2ecf20Sopenharmony_ci 20598c2ecf20Sopenharmony_ci /* 20608c2ecf20Sopenharmony_ci * Make sure the last group has enough space so that it's 20618c2ecf20Sopenharmony_ci * guaranteed to have enough space for all metadata blocks 20628c2ecf20Sopenharmony_ci * that it might need to hold. (We might not need to store 20638c2ecf20Sopenharmony_ci * the inode table blocks in the last block group, but there 20648c2ecf20Sopenharmony_ci * will be cases where this might be needed.) 20658c2ecf20Sopenharmony_ci */ 20668c2ecf20Sopenharmony_ci if ((ext4_group_first_block_no(sb, n_group) + 20678c2ecf20Sopenharmony_ci ext4_group_overhead_blocks(sb, n_group) + 2 + 20688c2ecf20Sopenharmony_ci sbi->s_itb_per_group + sbi->s_cluster_ratio) >= n_blocks_count) { 20698c2ecf20Sopenharmony_ci n_blocks_count = ext4_group_first_block_no(sb, n_group); 20708c2ecf20Sopenharmony_ci n_group--; 20718c2ecf20Sopenharmony_ci n_blocks_count_retry = 0; 20728c2ecf20Sopenharmony_ci if (resize_inode) { 20738c2ecf20Sopenharmony_ci iput(resize_inode); 20748c2ecf20Sopenharmony_ci resize_inode = NULL; 20758c2ecf20Sopenharmony_ci } 20768c2ecf20Sopenharmony_ci goto retry; 20778c2ecf20Sopenharmony_ci } 20788c2ecf20Sopenharmony_ci 20798c2ecf20Sopenharmony_ci /* extend the last group */ 20808c2ecf20Sopenharmony_ci if (n_group == o_group) 20818c2ecf20Sopenharmony_ci add = n_blocks_count - o_blocks_count; 20828c2ecf20Sopenharmony_ci else 20838c2ecf20Sopenharmony_ci add = EXT4_C2B(sbi, EXT4_CLUSTERS_PER_GROUP(sb) - (offset + 1)); 20848c2ecf20Sopenharmony_ci if (add > 0) { 20858c2ecf20Sopenharmony_ci err = ext4_group_extend_no_check(sb, o_blocks_count, add); 20868c2ecf20Sopenharmony_ci if (err) 20878c2ecf20Sopenharmony_ci goto out; 20888c2ecf20Sopenharmony_ci } 20898c2ecf20Sopenharmony_ci 20908c2ecf20Sopenharmony_ci if (ext4_blocks_count(es) == n_blocks_count && n_blocks_count_retry == 0) 20918c2ecf20Sopenharmony_ci goto out; 20928c2ecf20Sopenharmony_ci 20938c2ecf20Sopenharmony_ci err = ext4_alloc_flex_bg_array(sb, n_group + 1); 20948c2ecf20Sopenharmony_ci if (err) 20958c2ecf20Sopenharmony_ci goto out; 20968c2ecf20Sopenharmony_ci 20978c2ecf20Sopenharmony_ci err = ext4_mb_alloc_groupinfo(sb, n_group + 1); 20988c2ecf20Sopenharmony_ci if (err) 20998c2ecf20Sopenharmony_ci goto out; 21008c2ecf20Sopenharmony_ci 21018c2ecf20Sopenharmony_ci flex_gd = alloc_flex_gd(flexbg_size); 21028c2ecf20Sopenharmony_ci if (flex_gd == NULL) { 21038c2ecf20Sopenharmony_ci err = -ENOMEM; 21048c2ecf20Sopenharmony_ci goto out; 21058c2ecf20Sopenharmony_ci } 21068c2ecf20Sopenharmony_ci 21078c2ecf20Sopenharmony_ci /* Add flex groups. Note that a regular group is a 21088c2ecf20Sopenharmony_ci * flex group with 1 group. 21098c2ecf20Sopenharmony_ci */ 21108c2ecf20Sopenharmony_ci while (ext4_setup_next_flex_gd(sb, flex_gd, n_blocks_count)) { 21118c2ecf20Sopenharmony_ci if (jiffies - last_update_time > HZ * 10) { 21128c2ecf20Sopenharmony_ci if (last_update_time) 21138c2ecf20Sopenharmony_ci ext4_msg(sb, KERN_INFO, 21148c2ecf20Sopenharmony_ci "resized to %llu blocks", 21158c2ecf20Sopenharmony_ci ext4_blocks_count(es)); 21168c2ecf20Sopenharmony_ci last_update_time = jiffies; 21178c2ecf20Sopenharmony_ci } 21188c2ecf20Sopenharmony_ci if (ext4_alloc_group_tables(sb, flex_gd, flexbg_size) != 0) 21198c2ecf20Sopenharmony_ci break; 21208c2ecf20Sopenharmony_ci err = ext4_flex_group_add(sb, resize_inode, flex_gd); 21218c2ecf20Sopenharmony_ci if (unlikely(err)) 21228c2ecf20Sopenharmony_ci break; 21238c2ecf20Sopenharmony_ci } 21248c2ecf20Sopenharmony_ci 21258c2ecf20Sopenharmony_ci if (!err && n_blocks_count_retry) { 21268c2ecf20Sopenharmony_ci n_blocks_count = n_blocks_count_retry; 21278c2ecf20Sopenharmony_ci n_blocks_count_retry = 0; 21288c2ecf20Sopenharmony_ci free_flex_gd(flex_gd); 21298c2ecf20Sopenharmony_ci flex_gd = NULL; 21308c2ecf20Sopenharmony_ci if (resize_inode) { 21318c2ecf20Sopenharmony_ci iput(resize_inode); 21328c2ecf20Sopenharmony_ci resize_inode = NULL; 21338c2ecf20Sopenharmony_ci } 21348c2ecf20Sopenharmony_ci goto retry; 21358c2ecf20Sopenharmony_ci } 21368c2ecf20Sopenharmony_ci 21378c2ecf20Sopenharmony_ciout: 21388c2ecf20Sopenharmony_ci if (flex_gd) 21398c2ecf20Sopenharmony_ci free_flex_gd(flex_gd); 21408c2ecf20Sopenharmony_ci if (resize_inode != NULL) 21418c2ecf20Sopenharmony_ci iput(resize_inode); 21428c2ecf20Sopenharmony_ci if (err) 21438c2ecf20Sopenharmony_ci ext4_warning(sb, "error (%d) occurred during " 21448c2ecf20Sopenharmony_ci "file system resize", err); 21458c2ecf20Sopenharmony_ci ext4_msg(sb, KERN_INFO, "resized filesystem to %llu", 21468c2ecf20Sopenharmony_ci ext4_blocks_count(es)); 21478c2ecf20Sopenharmony_ci return err; 21488c2ecf20Sopenharmony_ci} 2149