diff options
Diffstat (limited to 'fs/ext4/super.c')
| -rw-r--r-- | fs/ext4/super.c | 263 | 
1 files changed, 175 insertions, 88 deletions
diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 44d0c8db223..9953d80145a 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -45,6 +45,7 @@  #include <linux/freezer.h>  #include "ext4.h" +#include "ext4_extents.h"  #include "ext4_jbd2.h"  #include "xattr.h"  #include "acl.h" @@ -163,8 +164,8 @@ ext4_fsblk_t ext4_inode_table(struct super_block *sb,  		 (ext4_fsblk_t)le32_to_cpu(bg->bg_inode_table_hi) << 32 : 0);  } -__u32 ext4_free_blks_count(struct super_block *sb, -			      struct ext4_group_desc *bg) +__u32 ext4_free_group_clusters(struct super_block *sb, +			       struct ext4_group_desc *bg)  {  	return le16_to_cpu(bg->bg_free_blocks_count_lo) |  		(EXT4_DESC_SIZE(sb) >= EXT4_MIN_DESC_SIZE_64BIT ? @@ -219,8 +220,8 @@ void ext4_inode_table_set(struct super_block *sb,  		bg->bg_inode_table_hi = cpu_to_le32(blk >> 32);  } -void ext4_free_blks_set(struct super_block *sb, -			  struct ext4_group_desc *bg, __u32 count) +void ext4_free_group_clusters_set(struct super_block *sb, +				  struct ext4_group_desc *bg, __u32 count)  {  	bg->bg_free_blocks_count_lo = cpu_to_le16((__u16)count);  	if (EXT4_DESC_SIZE(sb) >= EXT4_MIN_DESC_SIZE_64BIT) @@ -414,6 +415,22 @@ static void save_error_info(struct super_block *sb, const char *func,  	ext4_commit_super(sb, 1);  } +/* + * The del_gendisk() function uninitializes the disk-specific data + * structures, including the bdi structure, without telling anyone + * else.  Once this happens, any attempt to call mark_buffer_dirty() + * (for example, by ext4_commit_super), will cause a kernel OOPS. + * This is a kludge to prevent these oops until we can put in a proper + * hook in del_gendisk() to inform the VFS and file system layers. + */ +static int block_device_ejected(struct super_block *sb) +{ +	struct inode *bd_inode = sb->s_bdev->bd_inode; +	struct backing_dev_info *bdi = bd_inode->i_mapping->backing_dev_info; + +	return bdi->dev == NULL; +} +  /* Deal with the reporting of failure conditions on a filesystem such as   * inconsistencies detected or read IO failures. @@ -821,10 +838,10 @@ static void ext4_put_super(struct super_block *sb)  		brelse(sbi->s_group_desc[i]);  	ext4_kvfree(sbi->s_group_desc);  	ext4_kvfree(sbi->s_flex_groups); -	percpu_counter_destroy(&sbi->s_freeblocks_counter); +	percpu_counter_destroy(&sbi->s_freeclusters_counter);  	percpu_counter_destroy(&sbi->s_freeinodes_counter);  	percpu_counter_destroy(&sbi->s_dirs_counter); -	percpu_counter_destroy(&sbi->s_dirtyblocks_counter); +	percpu_counter_destroy(&sbi->s_dirtyclusters_counter);  	brelse(sbi->s_sbh);  #ifdef CONFIG_QUOTA  	for (i = 0; i < MAXQUOTAS; i++) @@ -1057,8 +1074,6 @@ static int ext4_show_options(struct seq_file *seq, struct vfsmount *vfs)  		seq_puts(seq, ",nouid32");  	if (test_opt(sb, DEBUG) && !(def_mount_opts & EXT4_DEFM_DEBUG))  		seq_puts(seq, ",debug"); -	if (test_opt(sb, OLDALLOC)) -		seq_puts(seq, ",oldalloc");  #ifdef CONFIG_EXT4_FS_XATTR  	if (test_opt(sb, XATTR_USER))  		seq_puts(seq, ",user_xattr"); @@ -1567,10 +1582,12 @@ static int parse_options(char *options, struct super_block *sb,  			set_opt(sb, DEBUG);  			break;  		case Opt_oldalloc: -			set_opt(sb, OLDALLOC); +			ext4_msg(sb, KERN_WARNING, +				 "Ignoring deprecated oldalloc option");  			break;  		case Opt_orlov: -			clear_opt(sb, OLDALLOC); +			ext4_msg(sb, KERN_WARNING, +				 "Ignoring deprecated orlov option");  			break;  #ifdef CONFIG_EXT4_FS_XATTR  		case Opt_user_xattr: @@ -1801,6 +1818,7 @@ set_qf_format:  			break;  		case Opt_nodelalloc:  			clear_opt(sb, DELALLOC); +			clear_opt2(sb, EXPLICIT_DELALLOC);  			break;  		case Opt_mblk_io_submit:  			set_opt(sb, MBLK_IO_SUBMIT); @@ -1817,6 +1835,7 @@ set_qf_format:  			break;  		case Opt_delalloc:  			set_opt(sb, DELALLOC); +			set_opt2(sb, EXPLICIT_DELALLOC);  			break;  		case Opt_block_validity:  			set_opt(sb, BLOCK_VALIDITY); @@ -1935,7 +1954,7 @@ static int ext4_setup_super(struct super_block *sb, struct ext4_super_block *es,  		res = MS_RDONLY;  	}  	if (read_only) -		return res; +		goto done;  	if (!(sbi->s_mount_state & EXT4_VALID_FS))  		ext4_msg(sb, KERN_WARNING, "warning: mounting unchecked fs, "  			 "running e2fsck is recommended"); @@ -1966,6 +1985,7 @@ static int ext4_setup_super(struct super_block *sb, struct ext4_super_block *es,  		EXT4_SET_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_RECOVER);  	ext4_commit_super(sb, 1); +done:  	if (test_opt(sb, DEBUG))  		printk(KERN_INFO "[EXT4 FS bs=%lu, gc=%u, "  				"bpg=%lu, ipg=%lu, mo=%04x, mo2=%04x]\n", @@ -2015,8 +2035,8 @@ static int ext4_fill_flex_info(struct super_block *sb)  		flex_group = ext4_flex_group(sbi, i);  		atomic_add(ext4_free_inodes_count(sb, gdp),  			   &sbi->s_flex_groups[flex_group].free_inodes); -		atomic_add(ext4_free_blks_count(sb, gdp), -			   &sbi->s_flex_groups[flex_group].free_blocks); +		atomic_add(ext4_free_group_clusters(sb, gdp), +			   &sbi->s_flex_groups[flex_group].free_clusters);  		atomic_add(ext4_used_dirs_count(sb, gdp),  			   &sbi->s_flex_groups[flex_group].used_dirs);  	} @@ -2134,7 +2154,8 @@ static int ext4_check_descriptors(struct super_block *sb,  	if (NULL != first_not_zeroed)  		*first_not_zeroed = grp; -	ext4_free_blocks_count_set(sbi->s_es, ext4_count_free_blocks(sb)); +	ext4_free_blocks_count_set(sbi->s_es, +				   EXT4_C2B(sbi, ext4_count_free_clusters(sb)));  	sbi->s_es->s_free_inodes_count =cpu_to_le32(ext4_count_free_inodes(sb));  	return 1;  } @@ -2454,7 +2475,8 @@ static ssize_t delayed_allocation_blocks_show(struct ext4_attr *a,  					      char *buf)  {  	return snprintf(buf, PAGE_SIZE, "%llu\n", -			(s64) percpu_counter_sum(&sbi->s_dirtyblocks_counter)); +		(s64) EXT4_C2B(sbi, +			percpu_counter_sum(&sbi->s_dirtyclusters_counter)));  }  static ssize_t session_write_kbytes_show(struct ext4_attr *a, @@ -2682,6 +2704,13 @@ static int ext4_feature_set_ok(struct super_block *sb, int readonly)  			return 0;  		}  	} +	if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_BIGALLOC) && +	    !EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_EXTENTS)) { +		ext4_msg(sb, KERN_ERR, +			 "Can't support bigalloc feature without " +			 "extents feature\n"); +		return 0; +	}  	return 1;  } @@ -3087,10 +3116,10 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)  	char *cp;  	const char *descr;  	int ret = -ENOMEM; -	int blocksize; +	int blocksize, clustersize;  	unsigned int db_count;  	unsigned int i; -	int needs_recovery, has_huge_files; +	int needs_recovery, has_huge_files, has_bigalloc;  	__u64 blocks_count;  	int err;  	unsigned int journal_ioprio = DEFAULT_JOURNAL_IOPRIO; @@ -3224,6 +3253,33 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)  			   &journal_ioprio, NULL, 0))  		goto failed_mount; +	if (test_opt(sb, DATA_FLAGS) == EXT4_MOUNT_JOURNAL_DATA) { +		printk_once(KERN_WARNING "EXT4-fs: Warning: mounting " +			    "with data=journal disables delayed " +			    "allocation and O_DIRECT support!\n"); +		if (test_opt2(sb, EXPLICIT_DELALLOC)) { +			ext4_msg(sb, KERN_ERR, "can't mount with " +				 "both data=journal and delalloc"); +			goto failed_mount; +		} +		if (test_opt(sb, DIOREAD_NOLOCK)) { +			ext4_msg(sb, KERN_ERR, "can't mount with " +				 "both data=journal and delalloc"); +			goto failed_mount; +		} +		if (test_opt(sb, DELALLOC)) +			clear_opt(sb, DELALLOC); +	} + +	blocksize = BLOCK_SIZE << le32_to_cpu(es->s_log_block_size); +	if (test_opt(sb, DIOREAD_NOLOCK)) { +		if (blocksize < PAGE_SIZE) { +			ext4_msg(sb, KERN_ERR, "can't mount with " +				 "dioread_nolock if block size != PAGE_SIZE"); +			goto failed_mount; +		} +	} +  	sb->s_flags = (sb->s_flags & ~MS_POSIXACL) |  		(test_opt(sb, POSIX_ACL) ? MS_POSIXACL : 0); @@ -3265,8 +3321,6 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)  	if (!ext4_feature_set_ok(sb, (sb->s_flags & MS_RDONLY)))  		goto failed_mount; -	blocksize = BLOCK_SIZE << le32_to_cpu(es->s_log_block_size); -  	if (blocksize < EXT4_MIN_BLOCK_SIZE ||  	    blocksize > EXT4_MAX_BLOCK_SIZE) {  		ext4_msg(sb, KERN_ERR, @@ -3369,12 +3423,53 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)  		sb->s_dirt = 1;  	} -	if (sbi->s_blocks_per_group > blocksize * 8) { -		ext4_msg(sb, KERN_ERR, -		       "#blocks per group too big: %lu", -		       sbi->s_blocks_per_group); -		goto failed_mount; +	/* Handle clustersize */ +	clustersize = BLOCK_SIZE << le32_to_cpu(es->s_log_cluster_size); +	has_bigalloc = EXT4_HAS_RO_COMPAT_FEATURE(sb, +				EXT4_FEATURE_RO_COMPAT_BIGALLOC); +	if (has_bigalloc) { +		if (clustersize < blocksize) { +			ext4_msg(sb, KERN_ERR, +				 "cluster size (%d) smaller than " +				 "block size (%d)", clustersize, blocksize); +			goto failed_mount; +		} +		sbi->s_cluster_bits = le32_to_cpu(es->s_log_cluster_size) - +			le32_to_cpu(es->s_log_block_size); +		sbi->s_clusters_per_group = +			le32_to_cpu(es->s_clusters_per_group); +		if (sbi->s_clusters_per_group > blocksize * 8) { +			ext4_msg(sb, KERN_ERR, +				 "#clusters per group too big: %lu", +				 sbi->s_clusters_per_group); +			goto failed_mount; +		} +		if (sbi->s_blocks_per_group != +		    (sbi->s_clusters_per_group * (clustersize / blocksize))) { +			ext4_msg(sb, KERN_ERR, "blocks per group (%lu) and " +				 "clusters per group (%lu) inconsistent", +				 sbi->s_blocks_per_group, +				 sbi->s_clusters_per_group); +			goto failed_mount; +		} +	} else { +		if (clustersize != blocksize) { +			ext4_warning(sb, "fragment/cluster size (%d) != " +				     "block size (%d)", clustersize, +				     blocksize); +			clustersize = blocksize; +		} +		if (sbi->s_blocks_per_group > blocksize * 8) { +			ext4_msg(sb, KERN_ERR, +				 "#blocks per group too big: %lu", +				 sbi->s_blocks_per_group); +			goto failed_mount; +		} +		sbi->s_clusters_per_group = sbi->s_blocks_per_group; +		sbi->s_cluster_bits = 0;  	} +	sbi->s_cluster_ratio = clustersize / blocksize; +  	if (sbi->s_inodes_per_group > blocksize * 8) {  		ext4_msg(sb, KERN_ERR,  		       "#inodes per group too big: %lu", @@ -3446,10 +3541,8 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)  		goto failed_mount;  	} -#ifdef CONFIG_PROC_FS  	if (ext4_proc_root)  		sbi->s_proc = proc_mkdir(sb->s_id, ext4_proc_root); -#endif  	bgl_lock_init(sbi->s_blockgroup_lock); @@ -3483,8 +3576,8 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)  	sbi->s_err_report.function = print_daily_error_info;  	sbi->s_err_report.data = (unsigned long) sb; -	err = percpu_counter_init(&sbi->s_freeblocks_counter, -			ext4_count_free_blocks(sb)); +	err = percpu_counter_init(&sbi->s_freeclusters_counter, +			ext4_count_free_clusters(sb));  	if (!err) {  		err = percpu_counter_init(&sbi->s_freeinodes_counter,  				ext4_count_free_inodes(sb)); @@ -3494,7 +3587,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)  				ext4_count_dirs(sb));  	}  	if (!err) { -		err = percpu_counter_init(&sbi->s_dirtyblocks_counter, 0); +		err = percpu_counter_init(&sbi->s_dirtyclusters_counter, 0);  	}  	if (err) {  		ext4_msg(sb, KERN_ERR, "insufficient memory"); @@ -3609,13 +3702,13 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)  	 * The journal may have updated the bg summary counts, so we  	 * need to update the global counters.  	 */ -	percpu_counter_set(&sbi->s_freeblocks_counter, -			   ext4_count_free_blocks(sb)); +	percpu_counter_set(&sbi->s_freeclusters_counter, +			   ext4_count_free_clusters(sb));  	percpu_counter_set(&sbi->s_freeinodes_counter,  			   ext4_count_free_inodes(sb));  	percpu_counter_set(&sbi->s_dirs_counter,  			   ext4_count_dirs(sb)); -	percpu_counter_set(&sbi->s_dirtyblocks_counter, 0); +	percpu_counter_set(&sbi->s_dirtyclusters_counter, 0);  no_journal:  	/* @@ -3679,25 +3772,6 @@ no_journal:  			 "available");  	} -	if (test_opt(sb, DELALLOC) && -	    (test_opt(sb, DATA_FLAGS) == EXT4_MOUNT_JOURNAL_DATA)) { -		ext4_msg(sb, KERN_WARNING, "Ignoring delalloc option - " -			 "requested data journaling mode"); -		clear_opt(sb, DELALLOC); -	} -	if (test_opt(sb, DIOREAD_NOLOCK)) { -		if (test_opt(sb, DATA_FLAGS) == EXT4_MOUNT_JOURNAL_DATA) { -			ext4_msg(sb, KERN_WARNING, "Ignoring dioread_nolock " -				"option - requested data journaling mode"); -			clear_opt(sb, DIOREAD_NOLOCK); -		} -		if (sb->s_blocksize < PAGE_SIZE) { -			ext4_msg(sb, KERN_WARNING, "Ignoring dioread_nolock " -				"option - block size is too small"); -			clear_opt(sb, DIOREAD_NOLOCK); -		} -	} -  	err = ext4_setup_system_zone(sb);  	if (err) {  		ext4_msg(sb, KERN_ERR, "failed to initialize system " @@ -3710,22 +3784,19 @@ no_journal:  	if (err) {  		ext4_msg(sb, KERN_ERR, "failed to initialize mballoc (%d)",  			 err); -		goto failed_mount4; +		goto failed_mount5;  	}  	err = ext4_register_li_request(sb, first_not_zeroed);  	if (err) -		goto failed_mount4; +		goto failed_mount6;  	sbi->s_kobj.kset = ext4_kset;  	init_completion(&sbi->s_kobj_unregister);  	err = kobject_init_and_add(&sbi->s_kobj, &ext4_ktype, NULL,  				   "%s", sb->s_id); -	if (err) { -		ext4_mb_release(sb); -		ext4_ext_release(sb); -		goto failed_mount4; -	}; +	if (err) +		goto failed_mount7;  	EXT4_SB(sb)->s_mount_state |= EXT4_ORPHAN_FS;  	ext4_orphan_cleanup(sb, es); @@ -3759,13 +3830,19 @@ cantfind_ext4:  		ext4_msg(sb, KERN_ERR, "VFS: Can't find ext4 filesystem");  	goto failed_mount; +failed_mount7: +	ext4_unregister_li_request(sb); +failed_mount6: +	ext4_ext_release(sb); +failed_mount5: +	ext4_mb_release(sb); +	ext4_release_system_zone(sb);  failed_mount4:  	iput(root);  	sb->s_root = NULL;  	ext4_msg(sb, KERN_ERR, "mount failed");  	destroy_workqueue(EXT4_SB(sb)->dio_unwritten_wq);  failed_mount_wq: -	ext4_release_system_zone(sb);  	if (sbi->s_journal) {  		jbd2_journal_destroy(sbi->s_journal);  		sbi->s_journal = NULL; @@ -3774,10 +3851,10 @@ failed_mount3:  	del_timer(&sbi->s_err_report);  	if (sbi->s_flex_groups)  		ext4_kvfree(sbi->s_flex_groups); -	percpu_counter_destroy(&sbi->s_freeblocks_counter); +	percpu_counter_destroy(&sbi->s_freeclusters_counter);  	percpu_counter_destroy(&sbi->s_freeinodes_counter);  	percpu_counter_destroy(&sbi->s_dirs_counter); -	percpu_counter_destroy(&sbi->s_dirtyblocks_counter); +	percpu_counter_destroy(&sbi->s_dirtyclusters_counter);  	if (sbi->s_mmp_tsk)  		kthread_stop(sbi->s_mmp_tsk);  failed_mount2: @@ -4064,7 +4141,7 @@ static int ext4_commit_super(struct super_block *sb, int sync)  	struct buffer_head *sbh = EXT4_SB(sb)->s_sbh;  	int error = 0; -	if (!sbh) +	if (!sbh || block_device_ejected(sb))  		return error;  	if (buffer_write_io_error(sbh)) {  		/* @@ -4100,8 +4177,9 @@ static int ext4_commit_super(struct super_block *sb, int sync)  	else  		es->s_kbytes_written =  			cpu_to_le64(EXT4_SB(sb)->s_kbytes_written); -	ext4_free_blocks_count_set(es, percpu_counter_sum_positive( -					   &EXT4_SB(sb)->s_freeblocks_counter)); +	ext4_free_blocks_count_set(es, +			EXT4_C2B(EXT4_SB(sb), percpu_counter_sum_positive( +				&EXT4_SB(sb)->s_freeclusters_counter)));  	es->s_free_inodes_count =  		cpu_to_le32(percpu_counter_sum_positive(  				&EXT4_SB(sb)->s_freeinodes_counter)); @@ -4506,16 +4584,34 @@ restore_opts:  	return err;  } +/* + * Note: calculating the overhead so we can be compatible with + * historical BSD practice is quite difficult in the face of + * clusters/bigalloc.  This is because multiple metadata blocks from + * different block group can end up in the same allocation cluster. + * Calculating the exact overhead in the face of clustered allocation + * requires either O(all block bitmaps) in memory or O(number of block + * groups**2) in time.  We will still calculate the superblock for + * older file systems --- and if we come across with a bigalloc file + * system with zero in s_overhead_clusters the estimate will be close to + * correct especially for very large cluster sizes --- but for newer + * file systems, it's better to calculate this figure once at mkfs + * time, and store it in the superblock.  If the superblock value is + * present (even for non-bigalloc file systems), we will use it. + */  static int ext4_statfs(struct dentry *dentry, struct kstatfs *buf)  {  	struct super_block *sb = dentry->d_sb;  	struct ext4_sb_info *sbi = EXT4_SB(sb);  	struct ext4_super_block *es = sbi->s_es; +	struct ext4_group_desc *gdp;  	u64 fsid;  	s64 bfree;  	if (test_opt(sb, MINIX_DF)) {  		sbi->s_overhead_last = 0; +	} else if (es->s_overhead_clusters) { +		sbi->s_overhead_last = le32_to_cpu(es->s_overhead_clusters);  	} else if (sbi->s_blocks_last != ext4_blocks_count(es)) {  		ext4_group_t i, ngroups = ext4_get_groups_count(sb);  		ext4_fsblk_t overhead = 0; @@ -4530,24 +4626,16 @@ static int ext4_statfs(struct dentry *dentry, struct kstatfs *buf)  		 * All of the blocks before first_data_block are  		 * overhead  		 */ -		overhead = le32_to_cpu(es->s_first_data_block); +		overhead = EXT4_B2C(sbi, le32_to_cpu(es->s_first_data_block));  		/* -		 * Add the overhead attributed to the superblock and -		 * block group descriptors.  If the sparse superblocks -		 * feature is turned on, then not all groups have this. +		 * Add the overhead found in each block group  		 */  		for (i = 0; i < ngroups; i++) { -			overhead += ext4_bg_has_super(sb, i) + -				ext4_bg_num_gdb(sb, i); +			gdp = ext4_get_group_desc(sb, i, NULL); +			overhead += ext4_num_overhead_clusters(sb, i, gdp);  			cond_resched();  		} - -		/* -		 * Every block group has an inode bitmap, a block -		 * bitmap, and an inode table. -		 */ -		overhead += ngroups * (2 + sbi->s_itb_per_group);  		sbi->s_overhead_last = overhead;  		smp_wmb();  		sbi->s_blocks_last = ext4_blocks_count(es); @@ -4555,11 +4643,12 @@ static int ext4_statfs(struct dentry *dentry, struct kstatfs *buf)  	buf->f_type = EXT4_SUPER_MAGIC;  	buf->f_bsize = sb->s_blocksize; -	buf->f_blocks = ext4_blocks_count(es) - sbi->s_overhead_last; -	bfree = percpu_counter_sum_positive(&sbi->s_freeblocks_counter) - -		       percpu_counter_sum_positive(&sbi->s_dirtyblocks_counter); +	buf->f_blocks = (ext4_blocks_count(es) - +			 EXT4_C2B(sbi, sbi->s_overhead_last)); +	bfree = percpu_counter_sum_positive(&sbi->s_freeclusters_counter) - +		percpu_counter_sum_positive(&sbi->s_dirtyclusters_counter);  	/* prevent underflow in case that few free space is available */ -	buf->f_bfree = max_t(s64, bfree, 0); +	buf->f_bfree = EXT4_C2B(sbi, max_t(s64, bfree, 0));  	buf->f_bavail = buf->f_bfree - ext4_r_blocks_count(es);  	if (buf->f_bfree < ext4_r_blocks_count(es))  		buf->f_bavail = 0; @@ -4980,13 +5069,11 @@ static int __init ext4_init_fs(void)  		return err;  	err = ext4_init_system_zone();  	if (err) -		goto out7; +		goto out6;  	ext4_kset = kset_create_and_add("ext4", NULL, fs_kobj);  	if (!ext4_kset) -		goto out6; -	ext4_proc_root = proc_mkdir("fs/ext4", NULL); -	if (!ext4_proc_root)  		goto out5; +	ext4_proc_root = proc_mkdir("fs/ext4", NULL);  	err = ext4_init_feat_adverts();  	if (err) @@ -5022,12 +5109,12 @@ out2:  out3:  	ext4_exit_feat_adverts();  out4: -	remove_proc_entry("fs/ext4", NULL); -out5: +	if (ext4_proc_root) +		remove_proc_entry("fs/ext4", NULL);  	kset_unregister(ext4_kset); -out6: +out5:  	ext4_exit_system_zone(); -out7: +out6:  	ext4_exit_pageio();  	return err;  }  |