[PATCH 2/3] PMFS: Fix two bugs with last block calculation

Ross Zwisler ross.zwisler at linux.intel.com
Wed Jun 19 19:56:35 EDT 2013


From: Sanjay Kumar <sanjay.k.kumar at intel.com>

This commit fixes two bugs in the calculation of last block number while
deleting/truncating files.

1) While deleting a file, we were not looking at the PMFS_EOFBLOCKS_FL
flag and checking the size for 0 to determine last_blocknr which caused
last_blocknr to be set to invalid value which in-turn caused crash.

2) When determining the last_blocknr based on file size, we were not
truncating last_blocknr according to the inode's tree height. Sparse
files may have large file sizes (and hence large last_blocknr) but their
tree height may be too small to accomodate such a large file size.

Signed-off-by: Sanjay Kumar <sanjay.k.kumar at intel.com>
Signed-off-by: Ross Zwisler <ross.zwisler at linux.intel.com>
---
 fs/pmfs/bbuild.c |  9 ++++++---
 fs/pmfs/inode.c  | 36 ++++++++++++++++++++++++++++++------
 fs/pmfs/pmfs.h   |  2 +-
 3 files changed, 37 insertions(+), 10 deletions(-)

diff --git a/fs/pmfs/bbuild.c b/fs/pmfs/bbuild.c
index 9405882..71fee7f 100644
--- a/fs/pmfs/bbuild.c
+++ b/fs/pmfs/bbuild.c
@@ -82,8 +82,9 @@ static bool pmfs_can_skip_full_scan(struct super_block *sb)
 	struct pmfs_inode *pi =  pmfs_get_inode(sb, PMFS_BLOCKNODE_IN0);
 	struct pmfs_super_block *super = pmfs_get_super(sb);
 	struct pmfs_sb_info *sbi = PMFS_SB(sb);
-	u64 root, isize;
+	u64 root;
 	unsigned int height, btype;
+	unsigned long last_blocknr;
 
 	if (!pi->root)
 	{
@@ -103,12 +104,14 @@ static bool pmfs_can_skip_full_scan(struct super_block *sb)
 	root = pi->root;
 	height = pi->height;
 	btype = pi->i_blk_type;
-	isize = le64_to_cpu(pi->i_size);
+	/* pi->i_size can not be zero */
+	last_blocknr = (le64_to_cpu(pi->i_size) - 1) >>
+					pmfs_inode_blk_shift(pi);
 
 	/* Clearing the datablock inode */
 	pmfs_clear_datablock_inode(sb);
 
-	pmfs_free_inode_subtree(sb, root, height, btype, isize);
+	pmfs_free_inode_subtree(sb, root, height, btype, last_blocknr);
 	
 	return true;
 }
diff --git a/fs/pmfs/inode.c b/fs/pmfs/inode.c
index b13560d..4de4238 100644
--- a/fs/pmfs/inode.c
+++ b/fs/pmfs/inode.c
@@ -315,11 +315,10 @@ static int recursive_truncate_blocks(struct super_block *sb, u64 block,
 }
 
 unsigned int pmfs_free_inode_subtree(struct super_block *sb,
-		u64 root, u32 height, u32 btype, loff_t end)
+		u64 root, u32 height, u32 btype, unsigned long last_blocknr)
 {
-	unsigned long first_blocknr, last_blocknr;
+	unsigned long first_blocknr;
 	unsigned int freed;
-	unsigned int data_bits = blk_type_to_shift[btype];
 	bool mpty;
 
 	if (!root)
@@ -332,7 +331,6 @@ unsigned int pmfs_free_inode_subtree(struct super_block *sb,
 		freed = 1;
 	} else {
 		first_blocknr = 0;
-		last_blocknr = (end - 1) >> data_bits;
 
 		freed = recursive_truncate_blocks(sb, root, height, btype,
 			first_blocknr, last_blocknr, &mpty);
@@ -420,6 +418,18 @@ static inline unsigned long pmfs_inode_count_iblocks (struct super_block *sb,
 	return (iblocks << (pmfs_inode_blk_shift(pi) - sb->s_blocksize_bits));
 }
 
+/* Support for sparse files: even though pi->i_size may indicate a certain
+ * last_blocknr, it may not be true for sparse files. Specifically, last_blocknr
+ * can not be more than the maximum allowed by the inode's tree height.
+ */
+static inline unsigned long pmfs_sparse_last_blocknr(unsigned int height,
+		unsigned long last_blocknr)
+{
+	if (last_blocknr >= (1UL << (height * META_BLK_SHIFT)))
+		last_blocknr = (1UL << (height * META_BLK_SHIFT)) - 1;
+	return last_blocknr;
+}
+
 /*
  * Free data blocks from inode in the range start <=> end
  */
@@ -451,6 +461,8 @@ static void __pmfs_truncate_blocks(struct inode *inode, loff_t start,
 		if (end == 0)
 			goto end_truncate_blocks;
 		last_blocknr = (end - 1) >> data_bits;
+		last_blocknr = pmfs_sparse_last_blocknr(pi->height,
+			last_blocknr);
 	}
 
 	if (first_blocknr > last_blocknr)
@@ -996,6 +1008,7 @@ void pmfs_evict_inode(struct inode *inode)
 	struct super_block *sb = inode->i_sb;
 	struct pmfs_inode *pi = pmfs_get_inode(sb, inode->i_ino);
 	u64 root;
+	unsigned long last_blocknr;
 	unsigned int height, btype;
 	int err = 0;
 
@@ -1015,8 +1028,19 @@ void pmfs_evict_inode(struct inode *inode)
 		if (err)
 			goto out;
 		/* then free the blocks from the inode's b-tree */
-		pmfs_free_inode_subtree(sb, root, height, btype,
-			inode->i_size);
+		if (pi->i_flags & cpu_to_le32(PMFS_EOFBLOCKS_FL)) {
+			last_blocknr = (1UL << (pi->height * META_BLK_SHIFT))
+			    - 1;
+		} else {
+			if (likely(inode->i_size))
+				last_blocknr = (inode->i_size - 1) >>
+					pmfs_inode_blk_shift(pi);
+			else
+				last_blocknr = 0;
+			last_blocknr = pmfs_sparse_last_blocknr(pi->height,
+				last_blocknr);
+		}
+		pmfs_free_inode_subtree(sb, root, height, btype, last_blocknr);
 		inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC;
 		inode->i_size = 0;
 	}
diff --git a/fs/pmfs/pmfs.h b/fs/pmfs/pmfs.h
index 5dbb74c..dbff31d 100644
--- a/fs/pmfs/pmfs.h
+++ b/fs/pmfs/pmfs.h
@@ -133,7 +133,7 @@ extern struct dentry *pmfs_get_parent(struct dentry *child);
 
 /* inode.c */
 extern unsigned int pmfs_free_inode_subtree(struct super_block *sb,
-                u64 root, u32 height, u32 btype, loff_t end);
+		u64 root, u32 height, u32 btype, unsigned long last_blocknr);
 extern int __pmfs_alloc_blocks(pmfs_transaction_t *trans,
 		struct super_block *sb, struct pmfs_inode *pi,
 		unsigned long file_blocknr, unsigned int num, bool zero);
-- 
1.8.2.GIT




More information about the Linux-pmfs mailing list