[PATCH v3 2/2] 9p: use i_lock to protect update of inode->i_blocks under 32-bit

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



When v9fs_stat2inode() is invoked concurrently under 32-bit case
and CONFIG_LBDAF is enabled, multiple updates of inode->i_blocks
may corrupt the value of i_blocks because the assignment of 64-bit
value under 32-bit host is not atomic.

Fix it by using i_lock to protect update of inode->i_blocks. Also
Skip the update when it's not requested.

Suggested-by: Dominique Martinet <asmadeus@xxxxxxxxxxxxx>
Signed-off-by: Hou Tao <houtao1@xxxxxxxxxx>
---
 fs/9p/v9fs_vfs.h       | 10 ++++++++++
 fs/9p/vfs_inode.c      |  7 ++++---
 fs/9p/vfs_inode_dotl.c | 16 +++++++++-------
 3 files changed, 23 insertions(+), 10 deletions(-)

diff --git a/fs/9p/v9fs_vfs.h b/fs/9p/v9fs_vfs.h
index 3d371b9e461a..0bd4acfdb6af 100644
--- a/fs/9p/v9fs_vfs.h
+++ b/fs/9p/v9fs_vfs.h
@@ -101,4 +101,14 @@ static inline void v9fs_i_size_write(struct inode *inode, loff_t i_size)
 	if (sizeof(i_size) > sizeof(long))
 		spin_unlock(&inode->i_lock);
 }
+
+static inline void v9fs_i_blocks_write(struct inode *inode, blkcnt_t i_blocks)
+{
+	/* Avoid taking i_lock under 64-bits */
+	if (sizeof(i_blocks) > sizeof(long))
+		spin_lock(&inode->i_lock);
+	inode->i_blocks = i_blocks;
+	if (sizeof(i_blocks) > sizeof(long))
+		spin_unlock(&inode->i_lock);
+}
 #endif
diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c
index 72b779bc0942..f05ff3cfbfe7 100644
--- a/fs/9p/vfs_inode.c
+++ b/fs/9p/vfs_inode.c
@@ -1218,10 +1218,11 @@ v9fs_stat2inode(struct p9_wstat *stat, struct inode *inode,
 	mode |= inode->i_mode & ~S_IALLUGO;
 	inode->i_mode = mode;
 
-	if (!(flags & V9FS_STAT2INODE_KEEP_ISIZE))
+	if (!(flags & V9FS_STAT2INODE_KEEP_ISIZE)) {
 		v9fs_i_size_write(inode, stat->length);
-	/* not real number of blocks, but 512 byte ones ... */
-	inode->i_blocks = (stat->length + 512 - 1) >> 9;
+		/* not real number of blocks, but 512 byte ones ... */
+		v9fs_i_blocks_write(inode, (stat->length + 512 - 1) >> 9);
+	}
 	v9inode->cache_validity &= ~V9FS_INO_INVALID_ATTR;
 }
 
diff --git a/fs/9p/vfs_inode_dotl.c b/fs/9p/vfs_inode_dotl.c
index a950a927a626..c1e2416d83c1 100644
--- a/fs/9p/vfs_inode_dotl.c
+++ b/fs/9p/vfs_inode_dotl.c
@@ -633,9 +633,10 @@ v9fs_stat2inode_dotl(struct p9_stat_dotl *stat, struct inode *inode,
 		mode |= inode->i_mode & ~S_IALLUGO;
 		inode->i_mode = mode;
 
-		if (!(flags & V9FS_STAT2INODE_KEEP_ISIZE))
+		if (!(flags & V9FS_STAT2INODE_KEEP_ISIZE)) {
 			v9fs_i_size_write(inode, stat->st_size);
-		inode->i_blocks = stat->st_blocks;
+			v9fs_i_blocks_write(inode, stat->st_blocks);
+		}
 	} else {
 		if (stat->st_result_mask & P9_STATS_ATIME) {
 			inode->i_atime.tv_sec = stat->st_atime_sec;
@@ -664,11 +665,12 @@ v9fs_stat2inode_dotl(struct p9_stat_dotl *stat, struct inode *inode,
 		}
 		if (stat->st_result_mask & P9_STATS_RDEV)
 			inode->i_rdev = new_decode_dev(stat->st_rdev);
-		if (!(flags & V9FS_STAT2INODE_KEEP_ISIZE) &&
-		    stat->st_result_mask & P9_STATS_SIZE)
-			v9fs_i_size_write(inode, stat->st_size);
-		if (stat->st_result_mask & P9_STATS_BLOCKS)
-			inode->i_blocks = stat->st_blocks;
+		if (!(flags & V9FS_STAT2INODE_KEEP_ISIZE)) {
+			if (stat->st_result_mask & P9_STATS_SIZE)
+				v9fs_i_size_write(inode, stat->st_size);
+			if (stat->st_result_mask & P9_STATS_BLOCKS)
+				v9fs_i_blocks_write(inode, stat->st_blocks);
+		}
 	}
 	if (stat->st_result_mask & P9_STATS_GEN)
 		inode->i_generation = stat->st_gen;
-- 
2.16.2.dirty




[Index of Archives]     [Linux Ext4 Filesystem]     [Union Filesystem]     [Filesystem Testing]     [Ceph Users]     [Ecryptfs]     [AutoFS]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux Cachefs]     [Reiser Filesystem]     [Linux RAID]     [Samba]     [Device Mapper]     [CEPH Development]

  Powered by Linux