From 0191f8697bbdfefcd36e7b8dc3eeddfe82893e4b Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Mon, 24 May 2010 19:15:57 +0200 Subject: pipe: F_SETPIPE_SZ should return -EPERM for non-root If the passed in size is larger than what has been set as the system wide limit and the user is not root, we want to return permission denied (not invalid value). Signed-off-by: Jens Axboe --- fs/pipe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/pipe.c b/fs/pipe.c index d79872eba09a..01ca9fab95fa 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -1170,7 +1170,7 @@ long pipe_fcntl(struct file *file, unsigned int cmd, unsigned long arg) switch (cmd) { case F_SETPIPE_SZ: if (!capable(CAP_SYS_ADMIN) && arg > pipe_max_pages) - return -EINVAL; + return -EPERM; /* * The pipe needs to be at least 2 pages large to * guarantee POSIX behaviour. -- cgit v1.2.3 From b9598db3401282bb27b4aef77e3eee12015f7f29 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Mon, 24 May 2010 19:34:43 +0200 Subject: pipe: make F_{GET,SET}PIPE_SZ deal with byte sizes Instead of requiring an exact number of pages as the argument and return value, change the API to deal with number of bytes instead. This also relaxes the requirement that the passed in size must result in a power-of-2 page array size. Round up to the nearest power-of-2 automatically and return the resulting size of the pipe on success. Signed-off-by: Jens Axboe --- fs/pipe.c | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) (limited to 'fs') diff --git a/fs/pipe.c b/fs/pipe.c index 01ca9fab95fa..bdd3f96054b9 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -1112,26 +1112,20 @@ SYSCALL_DEFINE1(pipe, int __user *, fildes) * Allocate a new array of pipe buffers and copy the info over. Returns the * pipe size if successful, or return -ERROR on error. */ -static long pipe_set_size(struct pipe_inode_info *pipe, unsigned long arg) +static long pipe_set_size(struct pipe_inode_info *pipe, unsigned long nr_pages) { struct pipe_buffer *bufs; - /* - * Must be a power-of-2 currently - */ - if (!is_power_of_2(arg)) - return -EINVAL; - /* * We can shrink the pipe, if arg >= pipe->nrbufs. Since we don't * expect a lot of shrink+grow operations, just free and allocate * again like we would do for growing. If the pipe currently * contains more buffers than arg, then return busy. */ - if (arg < pipe->nrbufs) + if (nr_pages < pipe->nrbufs) return -EBUSY; - bufs = kcalloc(arg, sizeof(struct pipe_buffer), GFP_KERNEL); + bufs = kcalloc(nr_pages, sizeof(struct pipe_buffer), GFP_KERNEL); if (unlikely(!bufs)) return -ENOMEM; @@ -1152,8 +1146,8 @@ static long pipe_set_size(struct pipe_inode_info *pipe, unsigned long arg) pipe->curbuf = 0; kfree(pipe->bufs); pipe->bufs = bufs; - pipe->buffers = arg; - return arg; + pipe->buffers = nr_pages; + return nr_pages * PAGE_SIZE; } long pipe_fcntl(struct file *file, unsigned int cmd, unsigned long arg) @@ -1168,19 +1162,30 @@ long pipe_fcntl(struct file *file, unsigned int cmd, unsigned long arg) mutex_lock(&pipe->inode->i_mutex); switch (cmd) { - case F_SETPIPE_SZ: - if (!capable(CAP_SYS_ADMIN) && arg > pipe_max_pages) + case F_SETPIPE_SZ: { + unsigned long nr_pages; + + /* + * Currently the array must be a power-of-2 size, so adjust + * upwards if needed. + */ + nr_pages = (arg + PAGE_SIZE - 1) >> PAGE_SHIFT; + nr_pages = roundup_pow_of_two(nr_pages); + + if (!capable(CAP_SYS_ADMIN) && nr_pages > pipe_max_pages) return -EPERM; + /* * The pipe needs to be at least 2 pages large to * guarantee POSIX behaviour. */ - if (arg < 2) + if (nr_pages < 2) return -EINVAL; - ret = pipe_set_size(pipe, arg); + ret = pipe_set_size(pipe, nr_pages); break; + } case F_GETPIPE_SZ: - ret = pipe->buffers; + ret = pipe->buffers * PAGE_SIZE; break; default: ret = -EINVAL; -- cgit v1.2.3 From 0ae0b5d0557264bad65e22f1e2da4b83a02c4535 Mon Sep 17 00:00:00 2001 From: Nick Piggin Date: Tue, 25 May 2010 10:25:26 +0200 Subject: fs/splice.c: fix mapping_gfp_mask usage mapping_gfp_mask() is not supposed to store allocation contex details, only page location details. So mapping_gfp_mask should be applied to the pagecache page allocation, wheras normal (kernel mapped) memory should be used for surrounding allocations such as radix-tree nodes allocated by add_to_page_cache. Context modifiers should be applied on a per-callsite basis. So change splice to follow this convention (which is followed in similar code patterns in core code). Signed-off-by: Nick Piggin Signed-off-by: Andrew Morton Signed-off-by: Jens Axboe --- fs/splice.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/splice.c b/fs/splice.c index ac22b00d86c3..740e6b9faf7a 100644 --- a/fs/splice.c +++ b/fs/splice.c @@ -354,7 +354,7 @@ __generic_file_splice_read(struct file *in, loff_t *ppos, break; error = add_to_page_cache_lru(page, mapping, index, - mapping_gfp_mask(mapping)); + GFP_KERNEL); if (unlikely(error)) { page_cache_release(page); if (error == -EEXIST) -- cgit v1.2.3 From 657a4cffde065beca7d919e867f61e7d322708b6 Mon Sep 17 00:00:00 2001 From: Eric Sandeen Date: Fri, 30 Apr 2010 03:42:49 +0000 Subject: xfs: replace E2BIG with EFBIG where appropriate Many places in the xfs code return E2BIG when they really mean EFBIG; trying to grow past 16T on a 32 bit machine, for example, says "Argument list too long" rather than "File too large" which is not particularly helpful. Some of these don't make perfect sense as EFBIG either, but still better than E2BIG IMHO. Signed-off-by: Eric Sandeen Reviewed-by: Christoph Hellwig Signed-off-by: Alex Elder --- fs/xfs/xfs_mount.c | 14 +++++++------- fs/xfs/xfs_rtalloc.c | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) (limited to 'fs') diff --git a/fs/xfs/xfs_mount.c b/fs/xfs/xfs_mount.c index d7bf38c8cd1c..2d811e57b332 100644 --- a/fs/xfs/xfs_mount.c +++ b/fs/xfs/xfs_mount.c @@ -268,10 +268,10 @@ xfs_sb_validate_fsb_count( #if XFS_BIG_BLKNOS /* Limited by ULONG_MAX of page cache index */ if (nblocks >> (PAGE_CACHE_SHIFT - sbp->sb_blocklog) > ULONG_MAX) - return E2BIG; + return EFBIG; #else /* Limited by UINT_MAX of sectors */ if (nblocks << (sbp->sb_blocklog - BBSHIFT) > UINT_MAX) - return E2BIG; + return EFBIG; #endif return 0; } @@ -393,7 +393,7 @@ xfs_mount_validate_sb( xfs_sb_validate_fsb_count(sbp, sbp->sb_rblocks)) { xfs_fs_mount_cmn_err(flags, "file system too large to be mounted on this system."); - return XFS_ERROR(E2BIG); + return XFS_ERROR(EFBIG); } if (unlikely(sbp->sb_inprogress)) { @@ -1009,7 +1009,7 @@ xfs_check_sizes(xfs_mount_t *mp) d = (xfs_daddr_t)XFS_FSB_TO_BB(mp, mp->m_sb.sb_dblocks); if (XFS_BB_TO_FSB(mp, d) != mp->m_sb.sb_dblocks) { cmn_err(CE_WARN, "XFS: size check 1 failed"); - return XFS_ERROR(E2BIG); + return XFS_ERROR(EFBIG); } error = xfs_read_buf(mp, mp->m_ddev_targp, d - XFS_FSS_TO_BB(mp, 1), @@ -1019,7 +1019,7 @@ xfs_check_sizes(xfs_mount_t *mp) } else { cmn_err(CE_WARN, "XFS: size check 2 failed"); if (error == ENOSPC) - error = XFS_ERROR(E2BIG); + error = XFS_ERROR(EFBIG); return error; } @@ -1027,7 +1027,7 @@ xfs_check_sizes(xfs_mount_t *mp) d = (xfs_daddr_t)XFS_FSB_TO_BB(mp, mp->m_sb.sb_logblocks); if (XFS_BB_TO_FSB(mp, d) != mp->m_sb.sb_logblocks) { cmn_err(CE_WARN, "XFS: size check 3 failed"); - return XFS_ERROR(E2BIG); + return XFS_ERROR(EFBIG); } error = xfs_read_buf(mp, mp->m_logdev_targp, d - XFS_FSB_TO_BB(mp, 1), @@ -1037,7 +1037,7 @@ xfs_check_sizes(xfs_mount_t *mp) } else { cmn_err(CE_WARN, "XFS: size check 3 failed"); if (error == ENOSPC) - error = XFS_ERROR(E2BIG); + error = XFS_ERROR(EFBIG); return error; } } diff --git a/fs/xfs/xfs_rtalloc.c b/fs/xfs/xfs_rtalloc.c index 6be05f756d59..16445518506d 100644 --- a/fs/xfs/xfs_rtalloc.c +++ b/fs/xfs/xfs_rtalloc.c @@ -2247,7 +2247,7 @@ xfs_rtmount_init( cmn_err(CE_WARN, "XFS: realtime mount -- %llu != %llu", (unsigned long long) XFS_BB_TO_FSB(mp, d), (unsigned long long) mp->m_sb.sb_rblocks); - return XFS_ERROR(E2BIG); + return XFS_ERROR(EFBIG); } error = xfs_read_buf(mp, mp->m_rtdev_targp, d - XFS_FSB_TO_BB(mp, 1), @@ -2256,7 +2256,7 @@ xfs_rtmount_init( cmn_err(CE_WARN, "XFS: realtime mount -- xfs_read_buf failed, returned %d", error); if (error == ENOSPC) - return XFS_ERROR(E2BIG); + return XFS_ERROR(EFBIG); return error; } xfs_buf_relse(bp); -- cgit v1.2.3 From 32891b292d6262d1db8e553cf3f4b38a91247b5a Mon Sep 17 00:00:00 2001 From: Eric Sandeen Date: Fri, 30 Apr 2010 16:43:48 +0000 Subject: xfs: be more explicit if RT mount fails due to config Recent testers were slightly confused that a realtime mount failed due to missing CONFIG_XFS_RT; we can make that a little more obvious. V2: drop the else as suggested by Christoph Signed-off-by: Eric Sandeen Reviewed-by: Christoph Hellwig Signed-off-by: Alex Elder --- fs/xfs/xfs_rtalloc.h | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/xfs/xfs_rtalloc.h b/fs/xfs/xfs_rtalloc.h index b2d67adb6a08..ff614c29b441 100644 --- a/fs/xfs/xfs_rtalloc.h +++ b/fs/xfs/xfs_rtalloc.h @@ -147,7 +147,16 @@ xfs_growfs_rt( # define xfs_rtfree_extent(t,b,l) (ENOSYS) # define xfs_rtpick_extent(m,t,l,rb) (ENOSYS) # define xfs_growfs_rt(mp,in) (ENOSYS) -# define xfs_rtmount_init(m) (((mp)->m_sb.sb_rblocks == 0)? 0 : (ENOSYS)) +static inline int /* error */ +xfs_rtmount_init( + xfs_mount_t *mp) /* file system mount structure */ +{ + if (mp->m_sb.sb_rblocks == 0) + return 0; + + cmn_err(CE_WARN, "XFS: Not built with CONFIG_XFS_RT"); + return ENOSYS; +} # define xfs_rtmount_inodes(m) (((mp)->m_sb.sb_rblocks == 0)? 0 : (ENOSYS)) # define xfs_rtunmount_inodes(m) #endif /* CONFIG_XFS_RT */ -- cgit v1.2.3 From 025101dca4480eff9da948405e872d5115030850 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 4 May 2010 13:53:48 +0000 Subject: xfs: cleanup log reservation calculactions Instead of having small helper functions calling big macros do the calculations for the log reservations directly in the functions. These are mostly 1:1 from the macros execept that the macros kept the quota calculations in their callers. Signed-off-by: Christoph Hellwig Signed-off-by: Alex Elder --- fs/xfs/xfs_trans.c | 446 +++++++++++++++++++++++++++++++++++++++++++++++------ fs/xfs/xfs_trans.h | 411 ------------------------------------------------ 2 files changed, 400 insertions(+), 457 deletions(-) (limited to 'fs') diff --git a/fs/xfs/xfs_trans.c b/fs/xfs/xfs_trans.c index ce558efa2ea0..28547dfce037 100644 --- a/fs/xfs/xfs_trans.c +++ b/fs/xfs/xfs_trans.c @@ -48,134 +48,489 @@ kmem_zone_t *xfs_trans_zone; + /* - * Reservation functions here avoid a huge stack in xfs_trans_init - * due to register overflow from temporaries in the calculations. + * Various log reservation values. + * + * These are based on the size of the file system block because that is what + * most transactions manipulate. Each adds in an additional 128 bytes per + * item logged to try to account for the overhead of the transaction mechanism. + * + * Note: Most of the reservations underestimate the number of allocation + * groups into which they could free extents in the xfs_bmap_finish() call. + * This is because the number in the worst case is quite high and quite + * unusual. In order to fix this we need to change xfs_bmap_finish() to free + * extents in only a single AG at a time. This will require changes to the + * EFI code as well, however, so that the EFI for the extents not freed is + * logged again in each transaction. See SGI PV #261917. + * + * Reservation functions here avoid a huge stack in xfs_trans_init due to + * register overflow from temporaries in the calculations. + */ + + +/* + * In a write transaction we can allocate a maximum of 2 + * extents. This gives: + * the inode getting the new extents: inode size + * the inode's bmap btree: max depth * block size + * the agfs of the ags from which the extents are allocated: 2 * sector + * the superblock free block counter: sector size + * the allocation btrees: 2 exts * 2 trees * (2 * max depth - 1) * block size + * And the bmap_finish transaction can free bmap blocks in a join: + * the agfs of the ags containing the blocks: 2 * sector size + * the agfls of the ags containing the blocks: 2 * sector size + * the super block free block counter: sector size + * the allocation btrees: 2 exts * 2 trees * (2 * max depth - 1) * block size */ STATIC uint -xfs_calc_write_reservation(xfs_mount_t *mp) +xfs_calc_write_reservation( + struct xfs_mount *mp) { - return XFS_CALC_WRITE_LOG_RES(mp) + XFS_DQUOT_LOGRES(mp); + return XFS_DQUOT_LOGRES(mp) + + MAX((mp->m_sb.sb_inodesize + + XFS_FSB_TO_B(mp, XFS_BM_MAXLEVELS(mp, XFS_DATA_FORK)) + + 2 * mp->m_sb.sb_sectsize + + mp->m_sb.sb_sectsize + + XFS_ALLOCFREE_LOG_RES(mp, 2) + + 128 * (4 + XFS_BM_MAXLEVELS(mp, XFS_DATA_FORK) + + XFS_ALLOCFREE_LOG_COUNT(mp, 2))), + (2 * mp->m_sb.sb_sectsize + + 2 * mp->m_sb.sb_sectsize + + mp->m_sb.sb_sectsize + + XFS_ALLOCFREE_LOG_RES(mp, 2) + + 128 * (5 + XFS_ALLOCFREE_LOG_COUNT(mp, 2)))); } +/* + * In truncating a file we free up to two extents at once. We can modify: + * the inode being truncated: inode size + * the inode's bmap btree: (max depth + 1) * block size + * And the bmap_finish transaction can free the blocks and bmap blocks: + * the agf for each of the ags: 4 * sector size + * the agfl for each of the ags: 4 * sector size + * the super block to reflect the freed blocks: sector size + * worst case split in allocation btrees per extent assuming 4 extents: + * 4 exts * 2 trees * (2 * max depth - 1) * block size + * the inode btree: max depth * blocksize + * the allocation btrees: 2 trees * (max depth - 1) * block size + */ STATIC uint -xfs_calc_itruncate_reservation(xfs_mount_t *mp) +xfs_calc_itruncate_reservation( + struct xfs_mount *mp) { - return XFS_CALC_ITRUNCATE_LOG_RES(mp) + XFS_DQUOT_LOGRES(mp); + return XFS_DQUOT_LOGRES(mp) + + MAX((mp->m_sb.sb_inodesize + + XFS_FSB_TO_B(mp, XFS_BM_MAXLEVELS(mp, XFS_DATA_FORK) + 1) + + 128 * (2 + XFS_BM_MAXLEVELS(mp, XFS_DATA_FORK))), + (4 * mp->m_sb.sb_sectsize + + 4 * mp->m_sb.sb_sectsize + + mp->m_sb.sb_sectsize + + XFS_ALLOCFREE_LOG_RES(mp, 4) + + 128 * (9 + XFS_ALLOCFREE_LOG_COUNT(mp, 4)) + + 128 * 5 + + XFS_ALLOCFREE_LOG_RES(mp, 1) + + 128 * (2 + XFS_IALLOC_BLOCKS(mp) + mp->m_in_maxlevels + + XFS_ALLOCFREE_LOG_COUNT(mp, 1)))); } +/* + * In renaming a files we can modify: + * the four inodes involved: 4 * inode size + * the two directory btrees: 2 * (max depth + v2) * dir block size + * the two directory bmap btrees: 2 * max depth * block size + * And the bmap_finish transaction can free dir and bmap blocks (two sets + * of bmap blocks) giving: + * the agf for the ags in which the blocks live: 3 * sector size + * the agfl for the ags in which the blocks live: 3 * sector size + * the superblock for the free block count: sector size + * the allocation btrees: 3 exts * 2 trees * (2 * max depth - 1) * block size + */ STATIC uint -xfs_calc_rename_reservation(xfs_mount_t *mp) +xfs_calc_rename_reservation( + struct xfs_mount *mp) { - return XFS_CALC_RENAME_LOG_RES(mp) + XFS_DQUOT_LOGRES(mp); + return XFS_DQUOT_LOGRES(mp) + + MAX((4 * mp->m_sb.sb_inodesize + + 2 * XFS_DIROP_LOG_RES(mp) + + 128 * (4 + 2 * XFS_DIROP_LOG_COUNT(mp))), + (3 * mp->m_sb.sb_sectsize + + 3 * mp->m_sb.sb_sectsize + + mp->m_sb.sb_sectsize + + XFS_ALLOCFREE_LOG_RES(mp, 3) + + 128 * (7 + XFS_ALLOCFREE_LOG_COUNT(mp, 3)))); } +/* + * For creating a link to an inode: + * the parent directory inode: inode size + * the linked inode: inode size + * the directory btree could split: (max depth + v2) * dir block size + * the directory bmap btree could join or split: (max depth + v2) * blocksize + * And the bmap_finish transaction can free some bmap blocks giving: + * the agf for the ag in which the blocks live: sector size + * the agfl for the ag in which the blocks live: sector size + * the superblock for the free block count: sector size + * the allocation btrees: 2 trees * (2 * max depth - 1) * block size + */ STATIC uint -xfs_calc_link_reservation(xfs_mount_t *mp) +xfs_calc_link_reservation( + struct xfs_mount *mp) { - return XFS_CALC_LINK_LOG_RES(mp) + XFS_DQUOT_LOGRES(mp); + return XFS_DQUOT_LOGRES(mp) + + MAX((mp->m_sb.sb_inodesize + + mp->m_sb.sb_inodesize + + XFS_DIROP_LOG_RES(mp) + + 128 * (2 + XFS_DIROP_LOG_COUNT(mp))), + (mp->m_sb.sb_sectsize + + mp->m_sb.sb_sectsize + + mp->m_sb.sb_sectsize + + XFS_ALLOCFREE_LOG_RES(mp, 1) + + 128 * (3 + XFS_ALLOCFREE_LOG_COUNT(mp, 1)))); } +/* + * For removing a directory entry we can modify: + * the parent directory inode: inode size + * the removed inode: inode size + * the directory btree could join: (max depth + v2) * dir block size + * the directory bmap btree could join or split: (max depth + v2) * blocksize + * And the bmap_finish transaction can free the dir and bmap blocks giving: + * the agf for the ag in which the blocks live: 2 * sector size + * the agfl for the ag in which the blocks live: 2 * sector size + * the superblock for the free block count: sector size + * the allocation btrees: 2 exts * 2 trees * (2 * max depth - 1) * block size + */ STATIC uint -xfs_calc_remove_reservation(xfs_mount_t *mp) +xfs_calc_remove_reservation( + struct xfs_mount *mp) { - return XFS_CALC_REMOVE_LOG_RES(mp) + XFS_DQUOT_LOGRES(mp); + return XFS_DQUOT_LOGRES(mp) + + MAX((mp->m_sb.sb_inodesize + + mp->m_sb.sb_inodesize + + XFS_DIROP_LOG_RES(mp) + + 128 * (2 + XFS_DIROP_LOG_COUNT(mp))), + (2 * mp->m_sb.sb_sectsize + + 2 * mp->m_sb.sb_sectsize + + mp->m_sb.sb_sectsize + + XFS_ALLOCFREE_LOG_RES(mp, 2) + + 128 * (5 + XFS_ALLOCFREE_LOG_COUNT(mp, 2)))); } +/* + * For symlink we can modify: + * the parent directory inode: inode size + * the new inode: inode size + * the inode btree entry: 1 block + * the directory btree: (max depth + v2) * dir block size + * the directory inode's bmap btree: (max depth + v2) * block size + * the blocks for the symlink: 1 kB + * Or in the first xact we allocate some inodes giving: + * the agi and agf of the ag getting the new inodes: 2 * sectorsize + * the inode blocks allocated: XFS_IALLOC_BLOCKS * blocksize + * the inode btree: max depth * blocksize + * the allocation btrees: 2 trees * (2 * max depth - 1) * block size + */ STATIC uint -xfs_calc_symlink_reservation(xfs_mount_t *mp) +xfs_calc_symlink_reservation( + struct xfs_mount *mp) { - return XFS_CALC_SYMLINK_LOG_RES(mp) + XFS_DQUOT_LOGRES(mp); + return XFS_DQUOT_LOGRES(mp) + + MAX((mp->m_sb.sb_inodesize + + mp->m_sb.sb_inodesize + + XFS_FSB_TO_B(mp, 1) + + XFS_DIROP_LOG_RES(mp) + + 1024 + + 128 * (4 + XFS_DIROP_LOG_COUNT(mp))), + (2 * mp->m_sb.sb_sectsize + + XFS_FSB_TO_B(mp, XFS_IALLOC_BLOCKS(mp)) + + XFS_FSB_TO_B(mp, mp->m_in_maxlevels) + + XFS_ALLOCFREE_LOG_RES(mp, 1) + + 128 * (2 + XFS_IALLOC_BLOCKS(mp) + mp->m_in_maxlevels + + XFS_ALLOCFREE_LOG_COUNT(mp, 1)))); } +/* + * For create we can modify: + * the parent directory inode: inode size + * the new inode: inode size + * the inode btree entry: block size + * the superblock for the nlink flag: sector size + * the directory btree: (max depth + v2) * dir block size + * the directory inode's bmap btree: (max depth + v2) * block size + * Or in the first xact we allocate some inodes giving: + * the agi and agf of the ag getting the new inodes: 2 * sectorsize + * the superblock for the nlink flag: sector size + * the inode blocks allocated: XFS_IALLOC_BLOCKS * blocksize + * the inode btree: max depth * blocksize + * the allocation btrees: 2 trees * (max depth - 1) * block size + */ STATIC uint -xfs_calc_create_reservation(xfs_mount_t *mp) +xfs_calc_create_reservation( + struct xfs_mount *mp) { - return XFS_CALC_CREATE_LOG_RES(mp) + XFS_DQUOT_LOGRES(mp); + return XFS_DQUOT_LOGRES(mp) + + MAX((mp->m_sb.sb_inodesize + + mp->m_sb.sb_inodesize + + mp->m_sb.sb_sectsize + + XFS_FSB_TO_B(mp, 1) + + XFS_DIROP_LOG_RES(mp) + + 128 * (3 + XFS_DIROP_LOG_COUNT(mp))), + (3 * mp->m_sb.sb_sectsize + + XFS_FSB_TO_B(mp, XFS_IALLOC_BLOCKS(mp)) + + XFS_FSB_TO_B(mp, mp->m_in_maxlevels) + + XFS_ALLOCFREE_LOG_RES(mp, 1) + + 128 * (2 + XFS_IALLOC_BLOCKS(mp) + mp->m_in_maxlevels + + XFS_ALLOCFREE_LOG_COUNT(mp, 1)))); } +/* + * Making a new directory is the same as creating a new file. + */ STATIC uint -xfs_calc_mkdir_reservation(xfs_mount_t *mp) +xfs_calc_mkdir_reservation( + struct xfs_mount *mp) { - return XFS_CALC_MKDIR_LOG_RES(mp) + XFS_DQUOT_LOGRES(mp); + return xfs_calc_create_reservation(mp); } +/* + * In freeing an inode we can modify: + * the inode being freed: inode size + * the super block free inode counter: sector size + * the agi hash list and counters: sector size + * the inode btree entry: block size + * the on disk inode before ours in the agi hash list: inode cluster size + * the inode btree: max depth * blocksize + * the allocation btrees: 2 trees * (max depth - 1) * block size + */ STATIC uint -xfs_calc_ifree_reservation(xfs_mount_t *mp) +xfs_calc_ifree_reservation( + struct xfs_mount *mp) { - return XFS_CALC_IFREE_LOG_RES(mp) + XFS_DQUOT_LOGRES(mp); + return XFS_DQUOT_LOGRES(mp) + + mp->m_sb.sb_inodesize + + mp->m_sb.sb_sectsize + + mp->m_sb.sb_sectsize + + XFS_FSB_TO_B(mp, 1) + + MAX((__uint16_t)XFS_FSB_TO_B(mp, 1), + XFS_INODE_CLUSTER_SIZE(mp)) + + 128 * 5 + + XFS_ALLOCFREE_LOG_RES(mp, 1) + + 128 * (2 + XFS_IALLOC_BLOCKS(mp) + mp->m_in_maxlevels + + XFS_ALLOCFREE_LOG_COUNT(mp, 1)); } +/* + * When only changing the inode we log the inode and possibly the superblock + * We also add a bit of slop for the transaction stuff. + */ STATIC uint -xfs_calc_ichange_reservation(xfs_mount_t *mp) +xfs_calc_ichange_reservation( + struct xfs_mount *mp) { - return XFS_CALC_ICHANGE_LOG_RES(mp) + XFS_DQUOT_LOGRES(mp); + return XFS_DQUOT_LOGRES(mp) + + mp->m_sb.sb_inodesize + + mp->m_sb.sb_sectsize + + 512; + } +/* + * Growing the data section of the filesystem. + * superblock + * agi and agf + * allocation btrees + */ STATIC uint -xfs_calc_growdata_reservation(xfs_mount_t *mp) +xfs_calc_growdata_reservation( + struct xfs_mount *mp) { - return XFS_CALC_GROWDATA_LOG_RES(mp); + return mp->m_sb.sb_sectsize * 3 + + XFS_ALLOCFREE_LOG_RES(mp, 1) + + 128 * (3 + XFS_ALLOCFREE_LOG_COUNT(mp, 1)); } +/* + * Growing the rt section of the filesystem. + * In the first set of transactions (ALLOC) we allocate space to the + * bitmap or summary files. + * superblock: sector size + * agf of the ag from which the extent is allocated: sector size + * bmap btree for bitmap/summary inode: max depth * blocksize + * bitmap/summary inode: inode size + * allocation btrees for 1 block alloc: 2 * (2 * maxdepth - 1) * blocksize + */ STATIC uint -xfs_calc_growrtalloc_reservation(xfs_mount_t *mp) +xfs_calc_growrtalloc_reservation( + struct xfs_mount *mp) { - return XFS_CALC_GROWRTALLOC_LOG_RES(mp); + return 2 * mp->m_sb.sb_sectsize + + XFS_FSB_TO_B(mp, XFS_BM_MAXLEVELS(mp, XFS_DATA_FORK)) + + mp->m_sb.sb_inodesize + + XFS_ALLOCFREE_LOG_RES(mp, 1) + + 128 * (3 + XFS_BM_MAXLEVELS(mp, XFS_DATA_FORK) + + XFS_ALLOCFREE_LOG_COUNT(mp, 1)); } +/* + * Growing the rt section of the filesystem. + * In the second set of transactions (ZERO) we zero the new metadata blocks. + * one bitmap/summary block: blocksize + */ STATIC uint -xfs_calc_growrtzero_reservation(xfs_mount_t *mp) +xfs_calc_growrtzero_reservation( + struct xfs_mount *mp) { - return XFS_CALC_GROWRTZERO_LOG_RES(mp); + return mp->m_sb.sb_blocksize + 128; } +/* + * Growing the rt section of the filesystem. + * In the third set of transactions (FREE) we update metadata without + * allocating any new blocks. + * superblock: sector size + * bitmap inode: inode size + * summary inode: inode size + * one bitmap block: blocksize + * summary blocks: new summary size + */ STATIC uint -xfs_calc_growrtfree_reservation(xfs_mount_t *mp) +xfs_calc_growrtfree_reservation( + struct xfs_mount *mp) { - return XFS_CALC_GROWRTFREE_LOG_RES(mp); + return mp->m_sb.sb_sectsize + + 2 * mp->m_sb.sb_inodesize + + mp->m_sb.sb_blocksize + + mp->m_rsumsize + + 128 * 5; } +/* + * Logging the inode modification timestamp on a synchronous write. + * inode + */ STATIC uint -xfs_calc_swrite_reservation(xfs_mount_t *mp) +xfs_calc_swrite_reservation( + struct xfs_mount *mp) { - return XFS_CALC_SWRITE_LOG_RES(mp); + return mp->m_sb.sb_inodesize + 128; } +/* + * Logging the inode mode bits when writing a setuid/setgid file + * inode + */ STATIC uint xfs_calc_writeid_reservation(xfs_mount_t *mp) { - return XFS_CALC_WRITEID_LOG_RES(mp); + return mp->m_sb.sb_inodesize + 128; } +/* + * Converting the inode from non-attributed to attributed. + * the inode being converted: inode size + * agf block and superblock (for block allocation) + * the new block (directory sized) + * bmap blocks for the new directory block + * allocation btrees + */ STATIC uint -xfs_calc_addafork_reservation(xfs_mount_t *mp) +xfs_calc_addafork_reservation( + struct xfs_mount *mp) { - return XFS_CALC_ADDAFORK_LOG_RES(mp) + XFS_DQUOT_LOGRES(mp); + return XFS_DQUOT_LOGRES(mp) + + mp->m_sb.sb_inodesize + + mp->m_sb.sb_sectsize * 2 + + mp->m_dirblksize + + XFS_FSB_TO_B(mp, XFS_DAENTER_BMAP1B(mp, XFS_DATA_FORK) + 1) + + XFS_ALLOCFREE_LOG_RES(mp, 1) + + 128 * (4 + XFS_DAENTER_BMAP1B(mp, XFS_DATA_FORK) + 1 + + XFS_ALLOCFREE_LOG_COUNT(mp, 1)); } +/* + * Removing the attribute fork of a file + * the inode being truncated: inode size + * the inode's bmap btree: max depth * block size + * And the bmap_finish transaction can free the blocks and bmap blocks: + * the agf for each of the ags: 4 * sector size + * the agfl for each of the ags: 4 * sector size + * the super block to reflect the freed blocks: sector size + * worst case split in allocation btrees per extent assuming 4 extents: + * 4 exts * 2 trees * (2 * max depth - 1) * block size + */ STATIC uint -xfs_calc_attrinval_reservation(xfs_mount_t *mp) +xfs_calc_attrinval_reservation( + struct xfs_mount *mp) { - return XFS_CALC_ATTRINVAL_LOG_RES(mp); + return MAX((mp->m_sb.sb_inodesize + + XFS_FSB_TO_B(mp, XFS_BM_MAXLEVELS(mp, XFS_ATTR_FORK)) + + 128 * (1 + XFS_BM_MAXLEVELS(mp, XFS_ATTR_FORK))), + (4 * mp->m_sb.sb_sectsize + + 4 * mp->m_sb.sb_sectsize + + mp->m_sb.sb_sectsize + + XFS_ALLOCFREE_LOG_RES(mp, 4) + + 128 * (9 + XFS_ALLOCFREE_LOG_COUNT(mp, 4)))); } +/* + * Setting an attribute. + * the inode getting the attribute + * the superblock for allocations + * the agfs extents are allocated from + * the attribute btree * max depth + * the inode allocation btree + * Since attribute transaction space is dependent on the size of the attribute, + * the calculation is done partially at mount time and partially at runtime. + */ STATIC uint -xfs_calc_attrset_reservation(xfs_mount_t *mp) +xfs_calc_attrset_reservation( + struct xfs_mount *mp) { - return XFS_CALC_ATTRSET_LOG_RES(mp) + XFS_DQUOT_LOGRES(mp); + return XFS_DQUOT_LOGRES(mp) + + mp->m_sb.sb_inodesize + + mp->m_sb.sb_sectsize + + XFS_FSB_TO_B(mp, XFS_DA_NODE_MAXDEPTH) + + 128 * (2 + XFS_DA_NODE_MAXDEPTH); } +/* + * Removing an attribute. + * the inode: inode size + * the attribute btree could join: max depth * block size + * the inode bmap btree could join or split: max depth * block size + * And the bmap_finish transaction can free the attr blocks freed giving: + * the agf for the ag in which the blocks live: 2 * sector size + * the agfl for the ag in which the blocks live: 2 * sector size + * the superblock for the free block count: sector size + * the allocation btrees: 2 exts * 2 trees * (2 * max depth - 1) * block size + */ STATIC uint -xfs_calc_attrrm_reservation(xfs_mount_t *mp) +xfs_calc_attrrm_reservation( + struct xfs_mount *mp) { - return XFS_CALC_ATTRRM_LOG_RES(mp) + XFS_DQUOT_LOGRES(mp); + return XFS_DQUOT_LOGRES(mp) + + MAX((mp->m_sb.sb_inodesize + + XFS_FSB_TO_B(mp, XFS_DA_NODE_MAXDEPTH) + + XFS_FSB_TO_B(mp, XFS_BM_MAXLEVELS(mp, XFS_ATTR_FORK)) + + 128 * (1 + XFS_DA_NODE_MAXDEPTH + + XFS_BM_MAXLEVELS(mp, XFS_DATA_FORK))), + (2 * mp->m_sb.sb_sectsize + + 2 * mp->m_sb.sb_sectsize + + mp->m_sb.sb_sectsize + + XFS_ALLOCFREE_LOG_RES(mp, 2) + + 128 * (5 + XFS_ALLOCFREE_LOG_COUNT(mp, 2)))); } +/* + * Clearing a bad agino number in an agi hash bucket. + */ STATIC uint -xfs_calc_clear_agi_bucket_reservation(xfs_mount_t *mp) +xfs_calc_clear_agi_bucket_reservation( + struct xfs_mount *mp) { - return XFS_CALC_CLEAR_AGI_BUCKET_LOG_RES(mp); + return mp->m_sb.sb_sectsize + 128; } /* @@ -184,11 +539,10 @@ xfs_calc_clear_agi_bucket_reservation(xfs_mount_t *mp) */ void xfs_trans_init( - xfs_mount_t *mp) + struct xfs_mount *mp) { - xfs_trans_reservations_t *resp; + struct xfs_trans_reservations *resp = &mp->m_reservations; - resp = &(mp->m_reservations); resp->tr_write = xfs_calc_write_reservation(mp); resp->tr_itruncate = xfs_calc_itruncate_reservation(mp); resp->tr_rename = xfs_calc_rename_reservation(mp); diff --git a/fs/xfs/xfs_trans.h b/fs/xfs/xfs_trans.h index 8c69e7824f68..e639e8e9a2a9 100644 --- a/fs/xfs/xfs_trans.h +++ b/fs/xfs/xfs_trans.h @@ -299,24 +299,6 @@ xfs_lic_desc_to_chunk(xfs_log_item_desc_t *dp) #define XFS_TRANS_SB_REXTSLOG 0x00002000 -/* - * Various log reservation values. - * These are based on the size of the file system block - * because that is what most transactions manipulate. - * Each adds in an additional 128 bytes per item logged to - * try to account for the overhead of the transaction mechanism. - * - * Note: - * Most of the reservations underestimate the number of allocation - * groups into which they could free extents in the xfs_bmap_finish() - * call. This is because the number in the worst case is quite high - * and quite unusual. In order to fix this we need to change - * xfs_bmap_finish() to free extents in only a single AG at a time. - * This will require changes to the EFI code as well, however, so that - * the EFI for the extents not freed is logged again in each transaction. - * See bug 261917. - */ - /* * Per-extent log reservation for the allocation btree changes * involved in freeing or allocating an extent. @@ -341,429 +323,36 @@ xfs_lic_desc_to_chunk(xfs_log_item_desc_t *dp) (XFS_DAENTER_BLOCKS(mp, XFS_DATA_FORK) + \ XFS_DAENTER_BMAPS(mp, XFS_DATA_FORK) + 1) -/* - * In a write transaction we can allocate a maximum of 2 - * extents. This gives: - * the inode getting the new extents: inode size - * the inode's bmap btree: max depth * block size - * the agfs of the ags from which the extents are allocated: 2 * sector - * the superblock free block counter: sector size - * the allocation btrees: 2 exts * 2 trees * (2 * max depth - 1) * block size - * And the bmap_finish transaction can free bmap blocks in a join: - * the agfs of the ags containing the blocks: 2 * sector size - * the agfls of the ags containing the blocks: 2 * sector size - * the super block free block counter: sector size - * the allocation btrees: 2 exts * 2 trees * (2 * max depth - 1) * block size - */ -#define XFS_CALC_WRITE_LOG_RES(mp) \ - (MAX( \ - ((mp)->m_sb.sb_inodesize + \ - XFS_FSB_TO_B((mp), XFS_BM_MAXLEVELS(mp, XFS_DATA_FORK)) + \ - (2 * (mp)->m_sb.sb_sectsize) + \ - (mp)->m_sb.sb_sectsize + \ - XFS_ALLOCFREE_LOG_RES(mp, 2) + \ - (128 * (4 + XFS_BM_MAXLEVELS(mp, XFS_DATA_FORK) + XFS_ALLOCFREE_LOG_COUNT(mp, 2)))),\ - ((2 * (mp)->m_sb.sb_sectsize) + \ - (2 * (mp)->m_sb.sb_sectsize) + \ - (mp)->m_sb.sb_sectsize + \ - XFS_ALLOCFREE_LOG_RES(mp, 2) + \ - (128 * (5 + XFS_ALLOCFREE_LOG_COUNT(mp, 2)))))) #define XFS_WRITE_LOG_RES(mp) ((mp)->m_reservations.tr_write) - -/* - * In truncating a file we free up to two extents at once. We can modify: - * the inode being truncated: inode size - * the inode's bmap btree: (max depth + 1) * block size - * And the bmap_finish transaction can free the blocks and bmap blocks: - * the agf for each of the ags: 4 * sector size - * the agfl for each of the ags: 4 * sector size - * the super block to reflect the freed blocks: sector size - * worst case split in allocation btrees per extent assuming 4 extents: - * 4 exts * 2 trees * (2 * max depth - 1) * block size - * the inode btree: max depth * blocksize - * the allocation btrees: 2 trees * (max depth - 1) * block size - */ -#define XFS_CALC_ITRUNCATE_LOG_RES(mp) \ - (MAX( \ - ((mp)->m_sb.sb_inodesize + \ - XFS_FSB_TO_B((mp), XFS_BM_MAXLEVELS(mp, XFS_DATA_FORK) + 1) + \ - (128 * (2 + XFS_BM_MAXLEVELS(mp, XFS_DATA_FORK)))), \ - ((4 * (mp)->m_sb.sb_sectsize) + \ - (4 * (mp)->m_sb.sb_sectsize) + \ - (mp)->m_sb.sb_sectsize + \ - XFS_ALLOCFREE_LOG_RES(mp, 4) + \ - (128 * (9 + XFS_ALLOCFREE_LOG_COUNT(mp, 4))) + \ - (128 * 5) + \ - XFS_ALLOCFREE_LOG_RES(mp, 1) + \ - (128 * (2 + XFS_IALLOC_BLOCKS(mp) + (mp)->m_in_maxlevels + \ - XFS_ALLOCFREE_LOG_COUNT(mp, 1)))))) - #define XFS_ITRUNCATE_LOG_RES(mp) ((mp)->m_reservations.tr_itruncate) - -/* - * In renaming a files we can modify: - * the four inodes involved: 4 * inode size - * the two directory btrees: 2 * (max depth + v2) * dir block size - * the two directory bmap btrees: 2 * max depth * block size - * And the bmap_finish transaction can free dir and bmap blocks (two sets - * of bmap blocks) giving: - * the agf for the ags in which the blocks live: 3 * sector size - * the agfl for the ags in which the blocks live: 3 * sector size - * the superblock for the free block count: sector size - * the allocation btrees: 3 exts * 2 trees * (2 * max depth - 1) * block size - */ -#define XFS_CALC_RENAME_LOG_RES(mp) \ - (MAX( \ - ((4 * (mp)->m_sb.sb_inodesize) + \ - (2 * XFS_DIROP_LOG_RES(mp)) + \ - (128 * (4 + 2 * XFS_DIROP_LOG_COUNT(mp)))), \ - ((3 * (mp)->m_sb.sb_sectsize) + \ - (3 * (mp)->m_sb.sb_sectsize) + \ - (mp)->m_sb.sb_sectsize + \ - XFS_ALLOCFREE_LOG_RES(mp, 3) + \ - (128 * (7 + XFS_ALLOCFREE_LOG_COUNT(mp, 3)))))) - #define XFS_RENAME_LOG_RES(mp) ((mp)->m_reservations.tr_rename) - -/* - * For creating a link to an inode: - * the parent directory inode: inode size - * the linked inode: inode size - * the directory btree could split: (max depth + v2) * dir block size - * the directory bmap btree could join or split: (max depth + v2) * blocksize - * And the bmap_finish transaction can free some bmap blocks giving: - * the agf for the ag in which the blocks live: sector size - * the agfl for the ag in which the blocks live: sector size - * the superblock for the free block count: sector size - * the allocation btrees: 2 trees * (2 * max depth - 1) * block size - */ -#define XFS_CALC_LINK_LOG_RES(mp) \ - (MAX( \ - ((mp)->m_sb.sb_inodesize + \ - (mp)->m_sb.sb_inodesize + \ - XFS_DIROP_LOG_RES(mp) + \ - (128 * (2 + XFS_DIROP_LOG_COUNT(mp)))), \ - ((mp)->m_sb.sb_sectsize + \ - (mp)->m_sb.sb_sectsize + \ - (mp)->m_sb.sb_sectsize + \ - XFS_ALLOCFREE_LOG_RES(mp, 1) + \ - (128 * (3 + XFS_ALLOCFREE_LOG_COUNT(mp, 1)))))) - #define XFS_LINK_LOG_RES(mp) ((mp)->m_reservations.tr_link) - -/* - * For removing a directory entry we can modify: - * the parent directory inode: inode size - * the removed inode: inode size - * the directory btree could join: (max depth + v2) * dir block size - * the directory bmap btree could join or split: (max depth + v2) * blocksize - * And the bmap_finish transaction can free the dir and bmap blocks giving: - * the agf for the ag in which the blocks live: 2 * sector size - * the agfl for the ag in which the blocks live: 2 * sector size - * the superblock for the free block count: sector size - * the allocation btrees: 2 exts * 2 trees * (2 * max depth - 1) * block size - */ -#define XFS_CALC_REMOVE_LOG_RES(mp) \ - (MAX( \ - ((mp)->m_sb.sb_inodesize + \ - (mp)->m_sb.sb_inodesize + \ - XFS_DIROP_LOG_RES(mp) + \ - (128 * (2 + XFS_DIROP_LOG_COUNT(mp)))), \ - ((2 * (mp)->m_sb.sb_sectsize) + \ - (2 * (mp)->m_sb.sb_sectsize) + \ - (mp)->m_sb.sb_sectsize + \ - XFS_ALLOCFREE_LOG_RES(mp, 2) + \ - (128 * (5 + XFS_ALLOCFREE_LOG_COUNT(mp, 2)))))) - #define XFS_REMOVE_LOG_RES(mp) ((mp)->m_reservations.tr_remove) - -/* - * For symlink we can modify: - * the parent directory inode: inode size - * the new inode: inode size - * the inode btree entry: 1 block - * the directory btree: (max depth + v2) * dir block size - * the directory inode's bmap btree: (max depth + v2) * block size - * the blocks for the symlink: 1 kB - * Or in the first xact we allocate some inodes giving: - * the agi and agf of the ag getting the new inodes: 2 * sectorsize - * the inode blocks allocated: XFS_IALLOC_BLOCKS * blocksize - * the inode btree: max depth * blocksize - * the allocation btrees: 2 trees * (2 * max depth - 1) * block size - */ -#define XFS_CALC_SYMLINK_LOG_RES(mp) \ - (MAX( \ - ((mp)->m_sb.sb_inodesize + \ - (mp)->m_sb.sb_inodesize + \ - XFS_FSB_TO_B(mp, 1) + \ - XFS_DIROP_LOG_RES(mp) + \ - 1024 + \ - (128 * (4 + XFS_DIROP_LOG_COUNT(mp)))), \ - (2 * (mp)->m_sb.sb_sectsize + \ - XFS_FSB_TO_B((mp), XFS_IALLOC_BLOCKS((mp))) + \ - XFS_FSB_TO_B((mp), (mp)->m_in_maxlevels) + \ - XFS_ALLOCFREE_LOG_RES(mp, 1) + \ - (128 * (2 + XFS_IALLOC_BLOCKS(mp) + (mp)->m_in_maxlevels + \ - XFS_ALLOCFREE_LOG_COUNT(mp, 1)))))) - #define XFS_SYMLINK_LOG_RES(mp) ((mp)->m_reservations.tr_symlink) - -/* - * For create we can modify: - * the parent directory inode: inode size - * the new inode: inode size - * the inode btree entry: block size - * the superblock for the nlink flag: sector size - * the directory btree: (max depth + v2) * dir block size - * the directory inode's bmap btree: (max depth + v2) * block size - * Or in the first xact we allocate some inodes giving: - * the agi and agf of the ag getting the new inodes: 2 * sectorsize - * the superblock for the nlink flag: sector size - * the inode blocks allocated: XFS_IALLOC_BLOCKS * blocksize - * the inode btree: max depth * blocksize - * the allocation btrees: 2 trees * (max depth - 1) * block size - */ -#define XFS_CALC_CREATE_LOG_RES(mp) \ - (MAX( \ - ((mp)->m_sb.sb_inodesize + \ - (mp)->m_sb.sb_inodesize + \ - (mp)->m_sb.sb_sectsize + \ - XFS_FSB_TO_B(mp, 1) + \ - XFS_DIROP_LOG_RES(mp) + \ - (128 * (3 + XFS_DIROP_LOG_COUNT(mp)))), \ - (3 * (mp)->m_sb.sb_sectsize + \ - XFS_FSB_TO_B((mp), XFS_IALLOC_BLOCKS((mp))) + \ - XFS_FSB_TO_B((mp), (mp)->m_in_maxlevels) + \ - XFS_ALLOCFREE_LOG_RES(mp, 1) + \ - (128 * (2 + XFS_IALLOC_BLOCKS(mp) + (mp)->m_in_maxlevels + \ - XFS_ALLOCFREE_LOG_COUNT(mp, 1)))))) - #define XFS_CREATE_LOG_RES(mp) ((mp)->m_reservations.tr_create) - -/* - * Making a new directory is the same as creating a new file. - */ -#define XFS_CALC_MKDIR_LOG_RES(mp) XFS_CALC_CREATE_LOG_RES(mp) - #define XFS_MKDIR_LOG_RES(mp) ((mp)->m_reservations.tr_mkdir) - -/* - * In freeing an inode we can modify: - * the inode being freed: inode size - * the super block free inode counter: sector size - * the agi hash list and counters: sector size - * the inode btree entry: block size - * the on disk inode before ours in the agi hash list: inode cluster size - * the inode btree: max depth * blocksize - * the allocation btrees: 2 trees * (max depth - 1) * block size - */ -#define XFS_CALC_IFREE_LOG_RES(mp) \ - ((mp)->m_sb.sb_inodesize + \ - (mp)->m_sb.sb_sectsize + \ - (mp)->m_sb.sb_sectsize + \ - XFS_FSB_TO_B((mp), 1) + \ - MAX((__uint16_t)XFS_FSB_TO_B((mp), 1), XFS_INODE_CLUSTER_SIZE(mp)) + \ - (128 * 5) + \ - XFS_ALLOCFREE_LOG_RES(mp, 1) + \ - (128 * (2 + XFS_IALLOC_BLOCKS(mp) + (mp)->m_in_maxlevels + \ - XFS_ALLOCFREE_LOG_COUNT(mp, 1)))) - - #define XFS_IFREE_LOG_RES(mp) ((mp)->m_reservations.tr_ifree) - -/* - * When only changing the inode we log the inode and possibly the superblock - * We also add a bit of slop for the transaction stuff. - */ -#define XFS_CALC_ICHANGE_LOG_RES(mp) ((mp)->m_sb.sb_inodesize + \ - (mp)->m_sb.sb_sectsize + 512) - #define XFS_ICHANGE_LOG_RES(mp) ((mp)->m_reservations.tr_ichange) - -/* - * Growing the data section of the filesystem. - * superblock - * agi and agf - * allocation btrees - */ -#define XFS_CALC_GROWDATA_LOG_RES(mp) \ - ((mp)->m_sb.sb_sectsize * 3 + \ - XFS_ALLOCFREE_LOG_RES(mp, 1) + \ - (128 * (3 + XFS_ALLOCFREE_LOG_COUNT(mp, 1)))) - #define XFS_GROWDATA_LOG_RES(mp) ((mp)->m_reservations.tr_growdata) - -/* - * Growing the rt section of the filesystem. - * In the first set of transactions (ALLOC) we allocate space to the - * bitmap or summary files. - * superblock: sector size - * agf of the ag from which the extent is allocated: sector size - * bmap btree for bitmap/summary inode: max depth * blocksize - * bitmap/summary inode: inode size - * allocation btrees for 1 block alloc: 2 * (2 * maxdepth - 1) * blocksize - */ -#define XFS_CALC_GROWRTALLOC_LOG_RES(mp) \ - (2 * (mp)->m_sb.sb_sectsize + \ - XFS_FSB_TO_B((mp), XFS_BM_MAXLEVELS(mp, XFS_DATA_FORK)) + \ - (mp)->m_sb.sb_inodesize + \ - XFS_ALLOCFREE_LOG_RES(mp, 1) + \ - (128 * \ - (3 + XFS_BM_MAXLEVELS(mp, XFS_DATA_FORK) + \ - XFS_ALLOCFREE_LOG_COUNT(mp, 1)))) - #define XFS_GROWRTALLOC_LOG_RES(mp) ((mp)->m_reservations.tr_growrtalloc) - -/* - * Growing the rt section of the filesystem. - * In the second set of transactions (ZERO) we zero the new metadata blocks. - * one bitmap/summary block: blocksize - */ -#define XFS_CALC_GROWRTZERO_LOG_RES(mp) \ - ((mp)->m_sb.sb_blocksize + 128) - #define XFS_GROWRTZERO_LOG_RES(mp) ((mp)->m_reservations.tr_growrtzero) - -/* - * Growing the rt section of the filesystem. - * In the third set of transactions (FREE) we update metadata without - * allocating any new blocks. - * superblock: sector size - * bitmap inode: inode size - * summary inode: inode size - * one bitmap block: blocksize - * summary blocks: new summary size - */ -#define XFS_CALC_GROWRTFREE_LOG_RES(mp) \ - ((mp)->m_sb.sb_sectsize + \ - 2 * (mp)->m_sb.sb_inodesize + \ - (mp)->m_sb.sb_blocksize + \ - (mp)->m_rsumsize + \ - (128 * 5)) - #define XFS_GROWRTFREE_LOG_RES(mp) ((mp)->m_reservations.tr_growrtfree) - -/* - * Logging the inode modification timestamp on a synchronous write. - * inode - */ -#define XFS_CALC_SWRITE_LOG_RES(mp) \ - ((mp)->m_sb.sb_inodesize + 128) - #define XFS_SWRITE_LOG_RES(mp) ((mp)->m_reservations.tr_swrite) - /* * Logging the inode timestamps on an fsync -- same as SWRITE * as long as SWRITE logs the entire inode core */ #define XFS_FSYNC_TS_LOG_RES(mp) ((mp)->m_reservations.tr_swrite) - -/* - * Logging the inode mode bits when writing a setuid/setgid file - * inode - */ -#define XFS_CALC_WRITEID_LOG_RES(mp) \ - ((mp)->m_sb.sb_inodesize + 128) - #define XFS_WRITEID_LOG_RES(mp) ((mp)->m_reservations.tr_swrite) - -/* - * Converting the inode from non-attributed to attributed. - * the inode being converted: inode size - * agf block and superblock (for block allocation) - * the new block (directory sized) - * bmap blocks for the new directory block - * allocation btrees - */ -#define XFS_CALC_ADDAFORK_LOG_RES(mp) \ - ((mp)->m_sb.sb_inodesize + \ - (mp)->m_sb.sb_sectsize * 2 + \ - (mp)->m_dirblksize + \ - XFS_FSB_TO_B(mp, (XFS_DAENTER_BMAP1B(mp, XFS_DATA_FORK) + 1)) + \ - XFS_ALLOCFREE_LOG_RES(mp, 1) + \ - (128 * (4 + (XFS_DAENTER_BMAP1B(mp, XFS_DATA_FORK) + 1) + \ - XFS_ALLOCFREE_LOG_COUNT(mp, 1)))) - #define XFS_ADDAFORK_LOG_RES(mp) ((mp)->m_reservations.tr_addafork) - -/* - * Removing the attribute fork of a file - * the inode being truncated: inode size - * the inode's bmap btree: max depth * block size - * And the bmap_finish transaction can free the blocks and bmap blocks: - * the agf for each of the ags: 4 * sector size - * the agfl for each of the ags: 4 * sector size - * the super block to reflect the freed blocks: sector size - * worst case split in allocation btrees per extent assuming 4 extents: - * 4 exts * 2 trees * (2 * max depth - 1) * block size - */ -#define XFS_CALC_ATTRINVAL_LOG_RES(mp) \ - (MAX( \ - ((mp)->m_sb.sb_inodesize + \ - XFS_FSB_TO_B((mp), XFS_BM_MAXLEVELS(mp, XFS_ATTR_FORK)) + \ - (128 * (1 + XFS_BM_MAXLEVELS(mp, XFS_ATTR_FORK)))), \ - ((4 * (mp)->m_sb.sb_sectsize) + \ - (4 * (mp)->m_sb.sb_sectsize) + \ - (mp)->m_sb.sb_sectsize + \ - XFS_ALLOCFREE_LOG_RES(mp, 4) + \ - (128 * (9 + XFS_ALLOCFREE_LOG_COUNT(mp, 4)))))) - #define XFS_ATTRINVAL_LOG_RES(mp) ((mp)->m_reservations.tr_attrinval) - -/* - * Setting an attribute. - * the inode getting the attribute - * the superblock for allocations - * the agfs extents are allocated from - * the attribute btree * max depth - * the inode allocation btree - * Since attribute transaction space is dependent on the size of the attribute, - * the calculation is done partially at mount time and partially at runtime. - */ -#define XFS_CALC_ATTRSET_LOG_RES(mp) \ - ((mp)->m_sb.sb_inodesize + \ - (mp)->m_sb.sb_sectsize + \ - XFS_FSB_TO_B((mp), XFS_DA_NODE_MAXDEPTH) + \ - (128 * (2 + XFS_DA_NODE_MAXDEPTH))) - #define XFS_ATTRSET_LOG_RES(mp, ext) \ ((mp)->m_reservations.tr_attrset + \ (ext * (mp)->m_sb.sb_sectsize) + \ (ext * XFS_FSB_TO_B((mp), XFS_BM_MAXLEVELS(mp, XFS_ATTR_FORK))) + \ (128 * (ext + (ext * XFS_BM_MAXLEVELS(mp, XFS_ATTR_FORK))))) - -/* - * Removing an attribute. - * the inode: inode size - * the attribute btree could join: max depth * block size - * the inode bmap btree could join or split: max depth * block size - * And the bmap_finish transaction can free the attr blocks freed giving: - * the agf for the ag in which the blocks live: 2 * sector size - * the agfl for the ag in which the blocks live: 2 * sector size - * the superblock for the free block count: sector size - * the allocation btrees: 2 exts * 2 trees * (2 * max depth - 1) * block size - */ -#define XFS_CALC_ATTRRM_LOG_RES(mp) \ - (MAX( \ - ((mp)->m_sb.sb_inodesize + \ - XFS_FSB_TO_B((mp), XFS_DA_NODE_MAXDEPTH) + \ - XFS_FSB_TO_B((mp), XFS_BM_MAXLEVELS(mp, XFS_ATTR_FORK)) + \ - (128 * (1 + XFS_DA_NODE_MAXDEPTH + XFS_BM_MAXLEVELS(mp, XFS_DATA_FORK)))), \ - ((2 * (mp)->m_sb.sb_sectsize) + \ - (2 * (mp)->m_sb.sb_sectsize) + \ - (mp)->m_sb.sb_sectsize + \ - XFS_ALLOCFREE_LOG_RES(mp, 2) + \ - (128 * (5 + XFS_ALLOCFREE_LOG_COUNT(mp, 2)))))) - #define XFS_ATTRRM_LOG_RES(mp) ((mp)->m_reservations.tr_attrrm) - -/* - * Clearing a bad agino number in an agi hash bucket. - */ -#define XFS_CALC_CLEAR_AGI_BUCKET_LOG_RES(mp) \ - ((mp)->m_sb.sb_sectsize + 128) - #define XFS_CLEAR_AGI_BUCKET_LOG_RES(mp) ((mp)->m_reservations.tr_clearagi) -- cgit v1.2.3 From fdc07f44c891d3fdee7722a03e3881614a293b3c Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 10 May 2010 17:28:14 +0000 Subject: xfs: clean up xlog_align Add suggested cleanups to commit 29db3370a1369541d58d692fbfb168b8a0bd7f41 from review that didn't end up being commited. Signed-off-by: Christoph Hellwig Signed-off-by: Alex Elder --- fs/xfs/xfs_log_recover.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) (limited to 'fs') diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c index 14a69aec2c0b..ed0684cc50ee 100644 --- a/fs/xfs/xfs_log_recover.c +++ b/fs/xfs/xfs_log_recover.c @@ -132,15 +132,10 @@ xlog_align( int nbblks, xfs_buf_t *bp) { - xfs_daddr_t offset; - xfs_caddr_t ptr; + xfs_daddr_t offset = blk_no & ((xfs_daddr_t)log->l_sectBBsize - 1); - offset = blk_no & ((xfs_daddr_t) log->l_sectBBsize - 1); - ptr = XFS_BUF_PTR(bp) + BBTOB(offset); - - ASSERT(ptr + BBTOB(nbblks) <= XFS_BUF_PTR(bp) + XFS_BUF_SIZE(bp)); - - return ptr; + ASSERT(BBTOB(offset + nbblks) <= XFS_BUF_SIZE(bp)); + return XFS_BUF_PTR(bp) + BBTOB(offset); } -- cgit v1.2.3 From 07f1a4f5e89cd4e6c79d67d41e8a18c451214ae2 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Fri, 21 May 2010 05:47:59 +0000 Subject: xfs: Check new inode size is OK before preallocating The new xfsqa test 228 tries to preallocate more space than the filesystem contains. it should fail, but instead triggers an assert about lock flags. The failure is due to the size extension failing in vmtruncate() due to rlimit being set. Check this before we start the preallocation to avoid allocating space that will never be used. Also the path through xfs_vn_allocate already holds the IO lock, so it should not be present in the lock flags when the setattr fails. Hence the assert needs to take this into account. This will prevent other such callers from hitting this incorrect ASSERT. (Fixed a reference to "newsize" to read "new_size". -Alex) Signed-off-by: Dave Chinner Reviewed-by: Christoph Hellwig Signed-off-by: Alex Elder --- fs/xfs/linux-2.6/xfs_iops.c | 16 +++++++++++++--- fs/xfs/xfs_vnodeops.c | 2 +- 2 files changed, 14 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_iops.c b/fs/xfs/linux-2.6/xfs_iops.c index 9c8019c78c92..44f0b2de153e 100644 --- a/fs/xfs/linux-2.6/xfs_iops.c +++ b/fs/xfs/linux-2.6/xfs_iops.c @@ -585,11 +585,20 @@ xfs_vn_fallocate( bf.l_len = len; xfs_ilock(ip, XFS_IOLOCK_EXCL); + + /* check the new inode size is valid before allocating */ + if (!(mode & FALLOC_FL_KEEP_SIZE) && + offset + len > i_size_read(inode)) { + new_size = offset + len; + error = inode_newsize_ok(inode, new_size); + if (error) + goto out_unlock; + } + error = -xfs_change_file_space(ip, XFS_IOC_RESVSP, &bf, 0, XFS_ATTR_NOLOCK); - if (!error && !(mode & FALLOC_FL_KEEP_SIZE) && - offset + len > i_size_read(inode)) - new_size = offset + len; + if (error) + goto out_unlock; /* Change file size if needed */ if (new_size) { @@ -600,6 +609,7 @@ xfs_vn_fallocate( error = -xfs_setattr(ip, &iattr, XFS_ATTR_NOLOCK); } +out_unlock: xfs_iunlock(ip, XFS_IOLOCK_EXCL); out_error: return error; diff --git a/fs/xfs/xfs_vnodeops.c b/fs/xfs/xfs_vnodeops.c index 9d376be0ea38..a06bd62504fc 100644 --- a/fs/xfs/xfs_vnodeops.c +++ b/fs/xfs/xfs_vnodeops.c @@ -267,7 +267,7 @@ xfs_setattr( if (code) { ASSERT(tp == NULL); lock_flags &= ~XFS_ILOCK_EXCL; - ASSERT(lock_flags == XFS_IOLOCK_EXCL); + ASSERT(lock_flags == XFS_IOLOCK_EXCL || !need_iolock); goto error_return; } tp = xfs_trans_alloc(mp, XFS_TRANS_SETATTR_SIZE); -- cgit v1.2.3 From 292ec4cf3536a5ed8809e8823341b203e497bbaf Mon Sep 17 00:00:00 2001 From: Huang Weiyi Date: Sat, 22 May 2010 17:13:20 +0000 Subject: xfs: xfs_trace.c: remove duplicated #include Remove duplicated #include('s) in fs/xfs/linux-2.6/xfs_trace.c Signed-off-by: Huang Weiyi Reviewed-by: Dave Chinner Signed-off-by: Alex Elder --- fs/xfs/linux-2.6/xfs_trace.c | 1 - 1 file changed, 1 deletion(-) (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_trace.c b/fs/xfs/linux-2.6/xfs_trace.c index 207fa77f63ae..d12be8470cba 100644 --- a/fs/xfs/linux-2.6/xfs_trace.c +++ b/fs/xfs/linux-2.6/xfs_trace.c @@ -50,7 +50,6 @@ #include "quota/xfs_dquot_item.h" #include "quota/xfs_dquot.h" #include "xfs_log_recover.h" -#include "xfs_buf_item.h" #include "xfs_inode_item.h" /* -- cgit v1.2.3 From f8adb4d574cf8c67f8391367136edc5469258fdc Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Mon, 24 May 2010 08:25:57 +0000 Subject: xfs: convert more trace events to DEFINE_EVENT Use DECLARE_EVENT_CLASS, and save ~15K: text data bss dec hex filename 171949 43028 48 215025 347f1 fs/xfs/linux-2.6/xfs_trace.o.orig 156521 43028 36 199585 30ba1 fs/xfs/linux-2.6/xfs_trace.o No change in functionality. Signed-off-by: Li Zefan Acked-by: Steven Rostedt Signed-off-by: Alex Elder --- fs/xfs/linux-2.6/xfs_trace.h | 356 +++++++++++++++++++++++-------------------- 1 file changed, 188 insertions(+), 168 deletions(-) (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_trace.h b/fs/xfs/linux-2.6/xfs_trace.h index ff6bc797baf2..73d5aa117384 100644 --- a/fs/xfs/linux-2.6/xfs_trace.h +++ b/fs/xfs/linux-2.6/xfs_trace.h @@ -82,33 +82,6 @@ DECLARE_EVENT_CLASS(xfs_attr_list_class, ) ) -#define DEFINE_PERAG_REF_EVENT(name) \ -TRACE_EVENT(name, \ - TP_PROTO(struct xfs_mount *mp, xfs_agnumber_t agno, int refcount, \ - unsigned long caller_ip), \ - TP_ARGS(mp, agno, refcount, caller_ip), \ - TP_STRUCT__entry( \ - __field(dev_t, dev) \ - __field(xfs_agnumber_t, agno) \ - __field(int, refcount) \ - __field(unsigned long, caller_ip) \ - ), \ - TP_fast_assign( \ - __entry->dev = mp->m_super->s_dev; \ - __entry->agno = agno; \ - __entry->refcount = refcount; \ - __entry->caller_ip = caller_ip; \ - ), \ - TP_printk("dev %d:%d agno %u refcount %d caller %pf", \ - MAJOR(__entry->dev), MINOR(__entry->dev), \ - __entry->agno, \ - __entry->refcount, \ - (char *)__entry->caller_ip) \ -); - -DEFINE_PERAG_REF_EVENT(xfs_perag_get) -DEFINE_PERAG_REF_EVENT(xfs_perag_put) - #define DEFINE_ATTR_LIST_EVENT(name) \ DEFINE_EVENT(xfs_attr_list_class, name, \ TP_PROTO(struct xfs_attr_list_context *ctx), \ @@ -122,6 +95,37 @@ DEFINE_ATTR_LIST_EVENT(xfs_attr_list_add); DEFINE_ATTR_LIST_EVENT(xfs_attr_list_wrong_blk); DEFINE_ATTR_LIST_EVENT(xfs_attr_list_notfound); +DECLARE_EVENT_CLASS(xfs_perag_class, + TP_PROTO(struct xfs_mount *mp, xfs_agnumber_t agno, int refcount, + unsigned long caller_ip), + TP_ARGS(mp, agno, refcount, caller_ip), + TP_STRUCT__entry( + __field(dev_t, dev) + __field(xfs_agnumber_t, agno) + __field(int, refcount) + __field(unsigned long, caller_ip) + ), + TP_fast_assign( + __entry->dev = mp->m_super->s_dev; + __entry->agno = agno; + __entry->refcount = refcount; + __entry->caller_ip = caller_ip; + ), + TP_printk("dev %d:%d agno %u refcount %d caller %pf", + MAJOR(__entry->dev), MINOR(__entry->dev), + __entry->agno, + __entry->refcount, + (char *)__entry->caller_ip) +); + +#define DEFINE_PERAG_REF_EVENT(name) \ +DEFINE_EVENT(xfs_perag_class, name, \ + TP_PROTO(struct xfs_mount *mp, xfs_agnumber_t agno, int refcount, \ + unsigned long caller_ip), \ + TP_ARGS(mp, agno, refcount, caller_ip)) +DEFINE_PERAG_REF_EVENT(xfs_perag_get); +DEFINE_PERAG_REF_EVENT(xfs_perag_put); + TRACE_EVENT(xfs_attr_list_node_descend, TP_PROTO(struct xfs_attr_list_context *ctx, struct xfs_da_node_entry *btree), @@ -775,165 +779,181 @@ DEFINE_LOGGRANT_EVENT(xfs_log_ungrant_enter); DEFINE_LOGGRANT_EVENT(xfs_log_ungrant_exit); DEFINE_LOGGRANT_EVENT(xfs_log_ungrant_sub); -#define DEFINE_RW_EVENT(name) \ -TRACE_EVENT(name, \ - TP_PROTO(struct xfs_inode *ip, size_t count, loff_t offset, int flags), \ - TP_ARGS(ip, count, offset, flags), \ - TP_STRUCT__entry( \ - __field(dev_t, dev) \ - __field(xfs_ino_t, ino) \ - __field(xfs_fsize_t, size) \ - __field(xfs_fsize_t, new_size) \ - __field(loff_t, offset) \ - __field(size_t, count) \ - __field(int, flags) \ - ), \ - TP_fast_assign( \ - __entry->dev = VFS_I(ip)->i_sb->s_dev; \ - __entry->ino = ip->i_ino; \ - __entry->size = ip->i_d.di_size; \ - __entry->new_size = ip->i_new_size; \ - __entry->offset = offset; \ - __entry->count = count; \ - __entry->flags = flags; \ - ), \ - TP_printk("dev %d:%d ino 0x%llx size 0x%llx new_size 0x%llx " \ - "offset 0x%llx count 0x%zx ioflags %s", \ - MAJOR(__entry->dev), MINOR(__entry->dev), \ - __entry->ino, \ - __entry->size, \ - __entry->new_size, \ - __entry->offset, \ - __entry->count, \ - __print_flags(__entry->flags, "|", XFS_IO_FLAGS)) \ +DECLARE_EVENT_CLASS(xfs_file_class, + TP_PROTO(struct xfs_inode *ip, size_t count, loff_t offset, int flags), + TP_ARGS(ip, count, offset, flags), + TP_STRUCT__entry( + __field(dev_t, dev) + __field(xfs_ino_t, ino) + __field(xfs_fsize_t, size) + __field(xfs_fsize_t, new_size) + __field(loff_t, offset) + __field(size_t, count) + __field(int, flags) + ), + TP_fast_assign( + __entry->dev = VFS_I(ip)->i_sb->s_dev; + __entry->ino = ip->i_ino; + __entry->size = ip->i_d.di_size; + __entry->new_size = ip->i_new_size; + __entry->offset = offset; + __entry->count = count; + __entry->flags = flags; + ), + TP_printk("dev %d:%d ino 0x%llx size 0x%llx new_size 0x%llx " + "offset 0x%llx count 0x%zx ioflags %s", + MAJOR(__entry->dev), MINOR(__entry->dev), + __entry->ino, + __entry->size, + __entry->new_size, + __entry->offset, + __entry->count, + __print_flags(__entry->flags, "|", XFS_IO_FLAGS)) ) + +#define DEFINE_RW_EVENT(name) \ +DEFINE_EVENT(xfs_file_class, name, \ + TP_PROTO(struct xfs_inode *ip, size_t count, loff_t offset, int flags), \ + TP_ARGS(ip, count, offset, flags)) DEFINE_RW_EVENT(xfs_file_read); DEFINE_RW_EVENT(xfs_file_buffered_write); DEFINE_RW_EVENT(xfs_file_direct_write); DEFINE_RW_EVENT(xfs_file_splice_read); DEFINE_RW_EVENT(xfs_file_splice_write); - -#define DEFINE_PAGE_EVENT(name) \ -TRACE_EVENT(name, \ - TP_PROTO(struct inode *inode, struct page *page, unsigned long off), \ - TP_ARGS(inode, page, off), \ - TP_STRUCT__entry( \ - __field(dev_t, dev) \ - __field(xfs_ino_t, ino) \ - __field(pgoff_t, pgoff) \ - __field(loff_t, size) \ - __field(unsigned long, offset) \ - __field(int, delalloc) \ - __field(int, unmapped) \ - __field(int, unwritten) \ - ), \ - TP_fast_assign( \ - int delalloc = -1, unmapped = -1, unwritten = -1; \ - \ - if (page_has_buffers(page)) \ - xfs_count_page_state(page, &delalloc, \ - &unmapped, &unwritten); \ - __entry->dev = inode->i_sb->s_dev; \ - __entry->ino = XFS_I(inode)->i_ino; \ - __entry->pgoff = page_offset(page); \ - __entry->size = i_size_read(inode); \ - __entry->offset = off; \ - __entry->delalloc = delalloc; \ - __entry->unmapped = unmapped; \ - __entry->unwritten = unwritten; \ - ), \ - TP_printk("dev %d:%d ino 0x%llx pgoff 0x%lx size 0x%llx offset %lx " \ - "delalloc %d unmapped %d unwritten %d", \ - MAJOR(__entry->dev), MINOR(__entry->dev), \ - __entry->ino, \ - __entry->pgoff, \ - __entry->size, \ - __entry->offset, \ - __entry->delalloc, \ - __entry->unmapped, \ - __entry->unwritten) \ +DECLARE_EVENT_CLASS(xfs_page_class, + TP_PROTO(struct inode *inode, struct page *page, unsigned long off), + TP_ARGS(inode, page, off), + TP_STRUCT__entry( + __field(dev_t, dev) + __field(xfs_ino_t, ino) + __field(pgoff_t, pgoff) + __field(loff_t, size) + __field(unsigned long, offset) + __field(int, delalloc) + __field(int, unmapped) + __field(int, unwritten) + ), + TP_fast_assign( + int delalloc = -1, unmapped = -1, unwritten = -1; + + if (page_has_buffers(page)) + xfs_count_page_state(page, &delalloc, + &unmapped, &unwritten); + __entry->dev = inode->i_sb->s_dev; + __entry->ino = XFS_I(inode)->i_ino; + __entry->pgoff = page_offset(page); + __entry->size = i_size_read(inode); + __entry->offset = off; + __entry->delalloc = delalloc; + __entry->unmapped = unmapped; + __entry->unwritten = unwritten; + ), + TP_printk("dev %d:%d ino 0x%llx pgoff 0x%lx size 0x%llx offset %lx " + "delalloc %d unmapped %d unwritten %d", + MAJOR(__entry->dev), MINOR(__entry->dev), + __entry->ino, + __entry->pgoff, + __entry->size, + __entry->offset, + __entry->delalloc, + __entry->unmapped, + __entry->unwritten) ) + +#define DEFINE_PAGE_EVENT(name) \ +DEFINE_EVENT(xfs_page_class, name, \ + TP_PROTO(struct inode *inode, struct page *page, unsigned long off), \ + TP_ARGS(inode, page, off)) DEFINE_PAGE_EVENT(xfs_writepage); DEFINE_PAGE_EVENT(xfs_releasepage); DEFINE_PAGE_EVENT(xfs_invalidatepage); -#define DEFINE_IOMAP_EVENT(name) \ -TRACE_EVENT(name, \ - TP_PROTO(struct xfs_inode *ip, xfs_off_t offset, ssize_t count, \ - int flags, struct xfs_bmbt_irec *irec), \ - TP_ARGS(ip, offset, count, flags, irec), \ - TP_STRUCT__entry( \ - __field(dev_t, dev) \ - __field(xfs_ino_t, ino) \ - __field(loff_t, size) \ - __field(loff_t, new_size) \ - __field(loff_t, offset) \ - __field(size_t, count) \ - __field(int, flags) \ - __field(xfs_fileoff_t, startoff) \ - __field(xfs_fsblock_t, startblock) \ - __field(xfs_filblks_t, blockcount) \ - ), \ - TP_fast_assign( \ - __entry->dev = VFS_I(ip)->i_sb->s_dev; \ - __entry->ino = ip->i_ino; \ - __entry->size = ip->i_d.di_size; \ - __entry->new_size = ip->i_new_size; \ - __entry->offset = offset; \ - __entry->count = count; \ - __entry->flags = flags; \ - __entry->startoff = irec ? irec->br_startoff : 0; \ - __entry->startblock = irec ? irec->br_startblock : 0; \ - __entry->blockcount = irec ? irec->br_blockcount : 0; \ - ), \ - TP_printk("dev %d:%d ino 0x%llx size 0x%llx new_size 0x%llx " \ - "offset 0x%llx count %zd flags %s " \ - "startoff 0x%llx startblock %lld blockcount 0x%llx", \ - MAJOR(__entry->dev), MINOR(__entry->dev), \ - __entry->ino, \ - __entry->size, \ - __entry->new_size, \ - __entry->offset, \ - __entry->count, \ - __print_flags(__entry->flags, "|", BMAPI_FLAGS), \ - __entry->startoff, \ - (__int64_t)__entry->startblock, \ - __entry->blockcount) \ +DECLARE_EVENT_CLASS(xfs_iomap_class, + TP_PROTO(struct xfs_inode *ip, xfs_off_t offset, ssize_t count, + int flags, struct xfs_bmbt_irec *irec), + TP_ARGS(ip, offset, count, flags, irec), + TP_STRUCT__entry( + __field(dev_t, dev) + __field(xfs_ino_t, ino) + __field(loff_t, size) + __field(loff_t, new_size) + __field(loff_t, offset) + __field(size_t, count) + __field(int, flags) + __field(xfs_fileoff_t, startoff) + __field(xfs_fsblock_t, startblock) + __field(xfs_filblks_t, blockcount) + ), + TP_fast_assign( + __entry->dev = VFS_I(ip)->i_sb->s_dev; + __entry->ino = ip->i_ino; + __entry->size = ip->i_d.di_size; + __entry->new_size = ip->i_new_size; + __entry->offset = offset; + __entry->count = count; + __entry->flags = flags; + __entry->startoff = irec ? irec->br_startoff : 0; + __entry->startblock = irec ? irec->br_startblock : 0; + __entry->blockcount = irec ? irec->br_blockcount : 0; + ), + TP_printk("dev %d:%d ino 0x%llx size 0x%llx new_size 0x%llx " + "offset 0x%llx count %zd flags %s " + "startoff 0x%llx startblock %lld blockcount 0x%llx", + MAJOR(__entry->dev), MINOR(__entry->dev), + __entry->ino, + __entry->size, + __entry->new_size, + __entry->offset, + __entry->count, + __print_flags(__entry->flags, "|", BMAPI_FLAGS), + __entry->startoff, + (__int64_t)__entry->startblock, + __entry->blockcount) ) + +#define DEFINE_IOMAP_EVENT(name) \ +DEFINE_EVENT(xfs_iomap_class, name, \ + TP_PROTO(struct xfs_inode *ip, xfs_off_t offset, ssize_t count, \ + int flags, struct xfs_bmbt_irec *irec), \ + TP_ARGS(ip, offset, count, flags, irec)) DEFINE_IOMAP_EVENT(xfs_iomap_enter); DEFINE_IOMAP_EVENT(xfs_iomap_found); DEFINE_IOMAP_EVENT(xfs_iomap_alloc); -#define DEFINE_SIMPLE_IO_EVENT(name) \ -TRACE_EVENT(name, \ - TP_PROTO(struct xfs_inode *ip, xfs_off_t offset, ssize_t count), \ - TP_ARGS(ip, offset, count), \ - TP_STRUCT__entry( \ - __field(dev_t, dev) \ - __field(xfs_ino_t, ino) \ - __field(loff_t, size) \ - __field(loff_t, new_size) \ - __field(loff_t, offset) \ - __field(size_t, count) \ - ), \ - TP_fast_assign( \ - __entry->dev = VFS_I(ip)->i_sb->s_dev; \ - __entry->ino = ip->i_ino; \ - __entry->size = ip->i_d.di_size; \ - __entry->new_size = ip->i_new_size; \ - __entry->offset = offset; \ - __entry->count = count; \ - ), \ - TP_printk("dev %d:%d ino 0x%llx size 0x%llx new_size 0x%llx " \ - "offset 0x%llx count %zd", \ - MAJOR(__entry->dev), MINOR(__entry->dev), \ - __entry->ino, \ - __entry->size, \ - __entry->new_size, \ - __entry->offset, \ - __entry->count) \ +DECLARE_EVENT_CLASS(xfs_simple_io_class, + TP_PROTO(struct xfs_inode *ip, xfs_off_t offset, ssize_t count), + TP_ARGS(ip, offset, count), + TP_STRUCT__entry( + __field(dev_t, dev) + __field(xfs_ino_t, ino) + __field(loff_t, size) + __field(loff_t, new_size) + __field(loff_t, offset) + __field(size_t, count) + ), + TP_fast_assign( + __entry->dev = VFS_I(ip)->i_sb->s_dev; + __entry->ino = ip->i_ino; + __entry->size = ip->i_d.di_size; + __entry->new_size = ip->i_new_size; + __entry->offset = offset; + __entry->count = count; + ), + TP_printk("dev %d:%d ino 0x%llx size 0x%llx new_size 0x%llx " + "offset 0x%llx count %zd", + MAJOR(__entry->dev), MINOR(__entry->dev), + __entry->ino, + __entry->size, + __entry->new_size, + __entry->offset, + __entry->count) ); + +#define DEFINE_SIMPLE_IO_EVENT(name) \ +DEFINE_EVENT(xfs_simple_io_class, name, \ + TP_PROTO(struct xfs_inode *ip, xfs_off_t offset, ssize_t count), \ + TP_ARGS(ip, offset, count)) DEFINE_SIMPLE_IO_EVENT(xfs_delalloc_enospc); DEFINE_SIMPLE_IO_EVENT(xfs_unwritten_convert); -- cgit v1.2.3 From 3bd0946eb157e26240ca858d1a42738b095dc6f3 Mon Sep 17 00:00:00 2001 From: Huang Weiyi Date: Tue, 25 May 2010 22:15:26 +0000 Subject: xfs: remove duplicated #include Remove duplicated #include('s) in fs/xfs/linux-2.6/xfs_quotaops.c Signed-off-by: Huang Weiyi Reviewed-by: Dave Chinner Signed-off-by: Alex Elder --- fs/xfs/linux-2.6/xfs_quotaops.c | 1 - 1 file changed, 1 deletion(-) (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_quotaops.c b/fs/xfs/linux-2.6/xfs_quotaops.c index 2e73688dae9c..a184793f2399 100644 --- a/fs/xfs/linux-2.6/xfs_quotaops.c +++ b/fs/xfs/linux-2.6/xfs_quotaops.c @@ -23,7 +23,6 @@ #include "xfs_ag.h" #include "xfs_mount.h" #include "xfs_quota.h" -#include "xfs_log.h" #include "xfs_trans.h" #include "xfs_bmap_btree.h" #include "xfs_inode.h" -- cgit v1.2.3 From 38e712ab3d28d79725eaade02fe8aba51abac196 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Wed, 26 May 2010 15:57:23 +0000 Subject: fs/xfs/quota: Add missing mutex_unlock Add a mutex_unlock missing on the error path. The use of this lock is balanced elsewhere in the file. The semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ expression E1; @@ * mutex_lock(E1,...); <+... when != E1 if (...) { ... when != E1 * return ...; } ...+> * mutex_unlock(E1,...); // Signed-off-by: Julia Lawall Signed-off-by: Alex Elder --- fs/xfs/quota/xfs_qm.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/xfs/quota/xfs_qm.c b/fs/xfs/quota/xfs_qm.c index 38e764146644..2d8b7bc792c9 100644 --- a/fs/xfs/quota/xfs_qm.c +++ b/fs/xfs/quota/xfs_qm.c @@ -249,8 +249,10 @@ xfs_qm_hold_quotafs_ref( if (!xfs_Gqm) { xfs_Gqm = xfs_Gqm_init(); - if (!xfs_Gqm) + if (!xfs_Gqm) { + mutex_unlock(&xfs_Gqm_lock); return ENOMEM; + } } /* -- cgit v1.2.3 From 9b98b6f3e1534bba2efcd5b16318945cf2218d99 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Thu, 27 May 2010 01:58:13 +0000 Subject: xfs: fix might_sleep() warning when initialising per-ag tree The use of radix_tree_preload() only works if the radix tree was initialised without the __GFP_WAIT flag. The per-ag tree uses GFP_NOFS, so does not trigger allocation of new tree nodes from the preloaded array. Hence it enters the allocator with a spinlock held and triggers the might_sleep() warnings. Reported-by; Chris Mason Signed-off-by: Dave Chinner Reviewed-by: Christoph Hellwig Signed-off-by: Alex Elder --- fs/xfs/xfs_mount.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/xfs/xfs_mount.c b/fs/xfs/xfs_mount.c index 2d811e57b332..ef1233b09683 100644 --- a/fs/xfs/xfs_mount.c +++ b/fs/xfs/xfs_mount.c @@ -1254,7 +1254,7 @@ xfs_mountfs( * Allocate and initialize the per-ag data. */ spin_lock_init(&mp->m_perag_lock); - INIT_RADIX_TREE(&mp->m_perag_tree, GFP_NOFS); + INIT_RADIX_TREE(&mp->m_perag_tree, GFP_ATOMIC); error = xfs_initialize_perag(mp, sbp->sb_agcount, &mp->m_maxagi); if (error) { cmn_err(CE_WARN, "XFS: Failed per-ag init: %d", error); -- cgit v1.2.3 From fb3b504adeee942e55393396fea8fdf406acf037 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 28 May 2010 19:03:10 +0000 Subject: xfs: fix access to upper inodes without inode64 If a filesystem is mounted without the inode64 mount option we should still be able to access inodes not fitting into 32 bits, just not created new ones. For this to work we need to make sure the inode cache radix tree is initialized for all allocation groups, not just those we plan to allocate inodes from. This patch makes sure we initialize the inode cache radix tree for all allocation groups, and also cleans xfs_initialize_perag up a bit to separate the inode32 logical from the general perag structure setup. Signed-off-by: Christoph Hellwig Signed-off-by: Alex Elder --- fs/xfs/linux-2.6/xfs_sync.c | 9 -------- fs/xfs/xfs_ag.h | 1 - fs/xfs/xfs_iget.c | 3 --- fs/xfs/xfs_inode.c | 2 -- fs/xfs/xfs_mount.c | 52 ++++++++++++++++++--------------------------- 5 files changed, 21 insertions(+), 46 deletions(-) (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_sync.c b/fs/xfs/linux-2.6/xfs_sync.c index 3884e20bc14e..ef7f0218bccb 100644 --- a/fs/xfs/linux-2.6/xfs_sync.c +++ b/fs/xfs/linux-2.6/xfs_sync.c @@ -164,10 +164,6 @@ xfs_inode_ag_iterator( struct xfs_perag *pag; pag = xfs_perag_get(mp, ag); - if (!pag->pag_ici_init) { - xfs_perag_put(pag); - continue; - } error = xfs_inode_ag_walk(mp, pag, execute, flags, tag, exclusive, &nr); xfs_perag_put(pag); @@ -867,12 +863,7 @@ xfs_reclaim_inode_shrink( down_read(&xfs_mount_list_lock); list_for_each_entry(mp, &xfs_mount_list, m_mplist) { for (ag = 0; ag < mp->m_sb.sb_agcount; ag++) { - pag = xfs_perag_get(mp, ag); - if (!pag->pag_ici_init) { - xfs_perag_put(pag); - continue; - } reclaimable += pag->pag_ici_reclaimable; xfs_perag_put(pag); } diff --git a/fs/xfs/xfs_ag.h b/fs/xfs/xfs_ag.h index 401f364ad36c..4917d4eed4ed 100644 --- a/fs/xfs/xfs_ag.h +++ b/fs/xfs/xfs_ag.h @@ -227,7 +227,6 @@ typedef struct xfs_perag { atomic_t pagf_fstrms; /* # of filestreams active in this AG */ - int pag_ici_init; /* incore inode cache initialised */ rwlock_t pag_ici_lock; /* incore inode lock */ struct radix_tree_root pag_ici_root; /* incore inode cache root */ int pag_ici_reclaimable; /* reclaimable inodes */ diff --git a/fs/xfs/xfs_iget.c b/fs/xfs/xfs_iget.c index 6845db90818f..f44a367907a8 100644 --- a/fs/xfs/xfs_iget.c +++ b/fs/xfs/xfs_iget.c @@ -382,9 +382,6 @@ xfs_iget( /* get the perag structure and ensure that it's inode capable */ pag = xfs_perag_get(mp, XFS_INO_TO_AGNO(mp, ino)); - if (!pag->pagi_inodeok) - return EINVAL; - ASSERT(pag->pag_ici_init); agino = XFS_INO_TO_AGINO(mp, ino); again: diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index 8cd6e8d8fe9c..6ba7765026f6 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -2649,8 +2649,6 @@ xfs_iflush_cluster( int i; pag = xfs_perag_get(mp, XFS_INO_TO_AGNO(mp, ip->i_ino)); - ASSERT(pag->pagi_inodeok); - ASSERT(pag->pag_ici_init); inodes_per_cluster = XFS_INODE_CLUSTER_SIZE(mp) >> mp->m_sb.sb_inodelog; ilist_size = inodes_per_cluster * sizeof(xfs_inode_t *); diff --git a/fs/xfs/xfs_mount.c b/fs/xfs/xfs_mount.c index ef1233b09683..d59f4e8bedcf 100644 --- a/fs/xfs/xfs_mount.c +++ b/fs/xfs/xfs_mount.c @@ -413,17 +413,6 @@ xfs_mount_validate_sb( return 0; } -STATIC void -xfs_initialize_perag_icache( - xfs_perag_t *pag) -{ - if (!pag->pag_ici_init) { - rwlock_init(&pag->pag_ici_lock); - INIT_RADIX_TREE(&pag->pag_ici_root, GFP_ATOMIC); - pag->pag_ici_init = 1; - } -} - int xfs_initialize_perag( xfs_mount_t *mp, @@ -436,13 +425,8 @@ xfs_initialize_perag( xfs_agino_t agino; xfs_ino_t ino; xfs_sb_t *sbp = &mp->m_sb; - xfs_ino_t max_inum = XFS_MAXINUMBER_32; int error = -ENOMEM; - /* Check to see if the filesystem can overflow 32 bit inodes */ - agino = XFS_OFFBNO_TO_AGINO(mp, sbp->sb_agblocks - 1, 0); - ino = XFS_AGINO_TO_INO(mp, agcount - 1, agino); - /* * Walk the current per-ag tree so we don't try to initialise AGs * that already exist (growfs case). Allocate and insert all the @@ -456,11 +440,18 @@ xfs_initialize_perag( } if (!first_initialised) first_initialised = index; + pag = kmem_zalloc(sizeof(*pag), KM_MAYFAIL); if (!pag) goto out_unwind; + pag->pag_agno = index; + pag->pag_mount = mp; + rwlock_init(&pag->pag_ici_lock); + INIT_RADIX_TREE(&pag->pag_ici_root, GFP_ATOMIC); + if (radix_tree_preload(GFP_NOFS)) goto out_unwind; + spin_lock(&mp->m_perag_lock); if (radix_tree_insert(&mp->m_perag_tree, index, pag)) { BUG(); @@ -469,25 +460,26 @@ xfs_initialize_perag( error = -EEXIST; goto out_unwind; } - pag->pag_agno = index; - pag->pag_mount = mp; spin_unlock(&mp->m_perag_lock); radix_tree_preload_end(); } - /* Clear the mount flag if no inode can overflow 32 bits - * on this filesystem, or if specifically requested.. + /* + * If we mount with the inode64 option, or no inode overflows + * the legacy 32-bit address space clear the inode32 option. */ - if ((mp->m_flags & XFS_MOUNT_SMALL_INUMS) && ino > max_inum) { + agino = XFS_OFFBNO_TO_AGINO(mp, sbp->sb_agblocks - 1, 0); + ino = XFS_AGINO_TO_INO(mp, agcount - 1, agino); + + if ((mp->m_flags & XFS_MOUNT_SMALL_INUMS) && ino > XFS_MAXINUMBER_32) mp->m_flags |= XFS_MOUNT_32BITINODES; - } else { + else mp->m_flags &= ~XFS_MOUNT_32BITINODES; - } - /* If we can overflow then setup the ag headers accordingly */ if (mp->m_flags & XFS_MOUNT_32BITINODES) { - /* Calculate how much should be reserved for inodes to - * meet the max inode percentage. + /* + * Calculate how much should be reserved for inodes to meet + * the max inode percentage. */ if (mp->m_maxicount) { __uint64_t icount; @@ -500,30 +492,28 @@ xfs_initialize_perag( } else { max_metadata = agcount; } + for (index = 0; index < agcount; index++) { ino = XFS_AGINO_TO_INO(mp, index, agino); - if (ino > max_inum) { + if (ino > XFS_MAXINUMBER_32) { index++; break; } - /* This ag is preferred for inodes */ pag = xfs_perag_get(mp, index); pag->pagi_inodeok = 1; if (index < max_metadata) pag->pagf_metadata = 1; - xfs_initialize_perag_icache(pag); xfs_perag_put(pag); } } else { - /* Setup default behavior for smaller filesystems */ for (index = 0; index < agcount; index++) { pag = xfs_perag_get(mp, index); pag->pagi_inodeok = 1; - xfs_initialize_perag_icache(pag); xfs_perag_put(pag); } } + if (maxagi) *maxagi = index; return 0; -- cgit v1.2.3 From 84cb0999851e25bc4bd4aaa717cc8f8acbf42b2a Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Sat, 22 May 2010 12:49:32 +0900 Subject: nilfs2: fix style issue in nilfs_destroy_cachep This gets rid of unwanted space chars in front of conditional sentences of nilfs_destroy_cachep(). Signed-off-by: Ryusuke Konishi --- fs/nilfs2/super.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c index 03b34b738993..414ef68931cf 100644 --- a/fs/nilfs2/super.c +++ b/fs/nilfs2/super.c @@ -1130,13 +1130,13 @@ static void nilfs_segbuf_init_once(void *obj) static void nilfs_destroy_cachep(void) { - if (nilfs_inode_cachep) + if (nilfs_inode_cachep) kmem_cache_destroy(nilfs_inode_cachep); - if (nilfs_transaction_cachep) + if (nilfs_transaction_cachep) kmem_cache_destroy(nilfs_transaction_cachep); - if (nilfs_segbuf_cachep) + if (nilfs_segbuf_cachep) kmem_cache_destroy(nilfs_segbuf_cachep); - if (nilfs_btree_path_cache) + if (nilfs_btree_path_cache) kmem_cache_destroy(nilfs_btree_path_cache); } -- cgit v1.2.3 From c29684d6834af7b3792f2feb6bdcf8c906ad8db6 Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Sun, 23 May 2010 20:40:32 +0900 Subject: nilfs2: remove obsolete declarations of cache constructor and destructor The commit 41c88bd7 ("nilfs2: cleanup multi kmem_cache_{create,destroy} code") consolidated slab constructors and destructors used in nilfs, but it left some declarations in header files. This gets rid of the obsolete declarations. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/btree.h | 2 -- fs/nilfs2/segbuf.h | 2 -- fs/nilfs2/segment.h | 2 -- 3 files changed, 6 deletions(-) (limited to 'fs') diff --git a/fs/nilfs2/btree.h b/fs/nilfs2/btree.h index af638d59e3bf..43c8c5b541fd 100644 --- a/fs/nilfs2/btree.h +++ b/fs/nilfs2/btree.h @@ -75,8 +75,6 @@ struct nilfs_btree_path { extern struct kmem_cache *nilfs_btree_path_cache; -int nilfs_btree_path_cache_init(void); -void nilfs_btree_path_cache_destroy(void); int nilfs_btree_init(struct nilfs_bmap *); int nilfs_btree_convert_and_insert(struct nilfs_bmap *, __u64, __u64, const __u64 *, const __u64 *, int); diff --git a/fs/nilfs2/segbuf.h b/fs/nilfs2/segbuf.h index fdf1c3b6d673..85fbb66455e2 100644 --- a/fs/nilfs2/segbuf.h +++ b/fs/nilfs2/segbuf.h @@ -127,8 +127,6 @@ struct nilfs_segment_buffer { extern struct kmem_cache *nilfs_segbuf_cachep; -int __init nilfs_init_segbuf_cache(void); -void nilfs_destroy_segbuf_cache(void); struct nilfs_segment_buffer *nilfs_segbuf_new(struct super_block *); void nilfs_segbuf_free(struct nilfs_segment_buffer *); void nilfs_segbuf_map(struct nilfs_segment_buffer *, __u64, unsigned long, diff --git a/fs/nilfs2/segment.h b/fs/nilfs2/segment.h index dca142361ccf..01e20dbb217d 100644 --- a/fs/nilfs2/segment.h +++ b/fs/nilfs2/segment.h @@ -221,8 +221,6 @@ enum { extern struct kmem_cache *nilfs_transaction_cachep; /* segment.c */ -extern int nilfs_init_transaction_cache(void); -extern void nilfs_destroy_transaction_cache(void); extern void nilfs_relax_pressure_in_lock(struct super_block *); extern int nilfs_construct_segment(struct super_block *); -- cgit v1.2.3 From a02956e4cc655f746134806119cce1bccfc2c610 Mon Sep 17 00:00:00 2001 From: Phillip Lougher Date: Mon, 31 May 2010 18:32:17 +0100 Subject: squashfs: fix compiler inline warning Fix compiler warning where inline conflicts with non-inline prototype. Reported-by: Geert Uytterhoeven Signed-off-by: Phillip Lougher --- fs/squashfs/xattr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/squashfs/xattr.c b/fs/squashfs/xattr.c index c7655e8b31cd..da58d8f5aa5f 100644 --- a/fs/squashfs/xattr.c +++ b/fs/squashfs/xattr.c @@ -295,7 +295,7 @@ static const struct xattr_handler squashfs_xattr_security_handler = { .get = squashfs_security_get }; -static inline const struct xattr_handler *squashfs_xattr_handler(int type) +static const struct xattr_handler *squashfs_xattr_handler(int type) { if (type & ~(SQUASHFS_XATTR_PREFIX_MASK | SQUASHFS_XATTR_VALUE_OOL)) /* ignore unrecognised type */ -- cgit v1.2.3 From 637d5c9a36bcb2b101401d52191ff3c7580f9068 Mon Sep 17 00:00:00 2001 From: Phillip Lougher Date: Mon, 31 May 2010 18:46:29 +0100 Subject: Squashfs: Make XATTR config name consistent with other file systems Reported-by: Geert Uytterhoeven Signed-off-by: Phillip Lougher --- fs/squashfs/Kconfig | 2 +- fs/squashfs/Makefile | 2 +- fs/squashfs/xattr.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/squashfs/Kconfig b/fs/squashfs/Kconfig index cc6ce8a84c21..3e9ee95ef37d 100644 --- a/fs/squashfs/Kconfig +++ b/fs/squashfs/Kconfig @@ -26,7 +26,7 @@ config SQUASHFS If unsure, say N. -config SQUASHFS_XATTRS +config SQUASHFS_XATTR bool "Squashfs XATTR support" depends on SQUASHFS default n diff --git a/fs/squashfs/Makefile b/fs/squashfs/Makefile index 2cee3e9fa452..6fa6b9deecd3 100644 --- a/fs/squashfs/Makefile +++ b/fs/squashfs/Makefile @@ -5,5 +5,5 @@ obj-$(CONFIG_SQUASHFS) += squashfs.o squashfs-y += block.o cache.o dir.o export.o file.o fragment.o id.o inode.o squashfs-y += namei.o super.o symlink.o zlib_wrapper.o decompressor.o -squashfs-$(CONFIG_SQUASHFS_XATTRS) += xattr.o xattr_id.o +squashfs-$(CONFIG_SQUASHFS_XATTR) += xattr.o xattr_id.o diff --git a/fs/squashfs/xattr.h b/fs/squashfs/xattr.h index 9da071ae181c..49fe0d719fbf 100644 --- a/fs/squashfs/xattr.h +++ b/fs/squashfs/xattr.h @@ -21,7 +21,7 @@ * xattr.h */ -#ifdef CONFIG_SQUASHFS_XATTRS +#ifdef CONFIG_SQUASHFS_XATTR extern __le64 *squashfs_read_xattr_id_table(struct super_block *, u64, u64 *, int *); extern int squashfs_xattr_lookup(struct super_block *, unsigned int, int *, -- cgit v1.2.3 From 4690148f77f90ec132b5eb780650ba8769b9ed39 Mon Sep 17 00:00:00 2001 From: Phillip Lougher Date: Mon, 31 May 2010 18:50:22 +0100 Subject: squashfs: fix filename in header comment Signed-off-by: Phillip Lougher --- fs/squashfs/xattr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/squashfs/xattr.c b/fs/squashfs/xattr.c index da58d8f5aa5f..652b8541f9c6 100644 --- a/fs/squashfs/xattr.c +++ b/fs/squashfs/xattr.c @@ -18,7 +18,7 @@ * along with this program; if not, write to the Free Software * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - * xattr_id.c + * xattr.c */ #include -- cgit v1.2.3 From f17625b318d9b151e7bd41e31223e9d89b2aaa77 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Tue, 1 Jun 2010 11:05:22 +0200 Subject: Revert "writeback: ensure that WB_SYNC_NONE writeback with sb pinned is sync" This reverts commit 7c8a3554c683f512dbcee26faedb42e4c05f12fa. We are investigating a hang associated with the WB_SYNC_NONE changes, so revert them for now. Signed-off-by: Jens Axboe --- fs/fs-writeback.c | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) (limited to 'fs') diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 5c4161f1fd9a..6753912641b4 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -193,8 +193,7 @@ static void bdi_wait_on_work_clear(struct bdi_work *work) } static void bdi_alloc_queue_work(struct backing_dev_info *bdi, - struct wb_writeback_args *args, - int wait) + struct wb_writeback_args *args) { struct bdi_work *work; @@ -206,8 +205,6 @@ static void bdi_alloc_queue_work(struct backing_dev_info *bdi, if (work) { bdi_work_init(work, args); bdi_queue_work(bdi, work); - if (wait) - bdi_wait_on_work_clear(work); } else { struct bdi_writeback *wb = &bdi->wb; @@ -282,7 +279,7 @@ void bdi_start_writeback(struct backing_dev_info *bdi, struct super_block *sb, args.for_background = 1; } - bdi_alloc_queue_work(bdi, &args, sb_locked); + bdi_alloc_queue_work(bdi, &args); } /* @@ -912,7 +909,6 @@ long wb_do_writeback(struct bdi_writeback *wb, int force_wait) while ((work = get_next_work_item(bdi, wb)) != NULL) { struct wb_writeback_args args = work->args; - int post_clear; /* * Override sync mode, in case we must wait for completion @@ -920,13 +916,11 @@ long wb_do_writeback(struct bdi_writeback *wb, int force_wait) if (force_wait) work->args.sync_mode = args.sync_mode = WB_SYNC_ALL; - post_clear = WB_SYNC_ALL || args.sb_pinned; - /* * If this isn't a data integrity operation, just notify * that we have seen this work and we are now starting it. */ - if (!post_clear) + if (args.sync_mode == WB_SYNC_NONE) wb_clear_pending(wb, work); wrote += wb_writeback(wb, &args); @@ -935,7 +929,7 @@ long wb_do_writeback(struct bdi_writeback *wb, int force_wait) * This is a data integrity writeback, so only do the * notification when we have completed the work. */ - if (post_clear) + if (args.sync_mode == WB_SYNC_ALL) wb_clear_pending(wb, work); } @@ -1011,7 +1005,7 @@ static void bdi_writeback_all(struct super_block *sb, long nr_pages) if (!bdi_has_dirty_io(bdi)) continue; - bdi_alloc_queue_work(bdi, &args, 0); + bdi_alloc_queue_work(bdi, &args); } rcu_read_unlock(); -- cgit v1.2.3 From 0e3c9a2284f5417f196e327c254d0b84c9ee8929 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Tue, 1 Jun 2010 11:08:43 +0200 Subject: Revert "writeback: fix WB_SYNC_NONE writeback from umount" This reverts commit e913fc825dc685a444cb4c1d0f9d32f372f59861. We are investigating a hang associated with the WB_SYNC_NONE changes, so revert them for now. Conflicts: fs/fs-writeback.c mm/page-writeback.c Signed-off-by: Jens Axboe --- fs/fs-writeback.c | 48 +++++++++++---------------------------------- fs/sync.c | 2 +- include/linux/backing-dev.h | 2 +- include/linux/writeback.h | 10 ---------- mm/page-writeback.c | 4 ++-- 5 files changed, 15 insertions(+), 51 deletions(-) (limited to 'fs') diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 6753912641b4..408a7877b79d 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -45,7 +45,6 @@ struct wb_writeback_args { unsigned int for_kupdate:1; unsigned int range_cyclic:1; unsigned int for_background:1; - unsigned int sb_pinned:1; }; /* @@ -231,11 +230,6 @@ static void bdi_sync_writeback(struct backing_dev_info *bdi, .sync_mode = WB_SYNC_ALL, .nr_pages = LONG_MAX, .range_cyclic = 0, - /* - * Setting sb_pinned is not necessary for WB_SYNC_ALL, but - * lets make it explicitly clear. - */ - .sb_pinned = 1, }; struct bdi_work work; @@ -251,23 +245,21 @@ static void bdi_sync_writeback(struct backing_dev_info *bdi, * @bdi: the backing device to write from * @sb: write inodes from this super_block * @nr_pages: the number of pages to write - * @sb_locked: caller already holds sb umount sem. * * Description: * This does WB_SYNC_NONE opportunistic writeback. The IO is only * started when this function returns, we make no guarentees on - * completion. Caller specifies whether sb umount sem is held already or not. + * completion. Caller need not hold sb s_umount semaphore. * */ void bdi_start_writeback(struct backing_dev_info *bdi, struct super_block *sb, - long nr_pages, int sb_locked) + long nr_pages) { struct wb_writeback_args args = { .sb = sb, .sync_mode = WB_SYNC_NONE, .nr_pages = nr_pages, .range_cyclic = 1, - .sb_pinned = sb_locked, }; /* @@ -592,7 +584,7 @@ static enum sb_pin_state pin_sb_for_writeback(struct writeback_control *wbc, /* * Caller must already hold the ref for this */ - if (wbc->sync_mode == WB_SYNC_ALL || wbc->sb_pinned) { + if (wbc->sync_mode == WB_SYNC_ALL) { WARN_ON(!rwsem_is_locked(&sb->s_umount)); return SB_NOT_PINNED; } @@ -766,7 +758,6 @@ static long wb_writeback(struct bdi_writeback *wb, .for_kupdate = args->for_kupdate, .for_background = args->for_background, .range_cyclic = args->range_cyclic, - .sb_pinned = args->sb_pinned, }; unsigned long oldest_jif; long wrote = 0; @@ -1214,18 +1205,6 @@ static void wait_sb_inodes(struct super_block *sb) iput(old_inode); } -static void __writeback_inodes_sb(struct super_block *sb, int sb_locked) -{ - unsigned long nr_dirty = global_page_state(NR_FILE_DIRTY); - unsigned long nr_unstable = global_page_state(NR_UNSTABLE_NFS); - long nr_to_write; - - nr_to_write = nr_dirty + nr_unstable + - (inodes_stat.nr_inodes - inodes_stat.nr_unused); - - bdi_start_writeback(sb->s_bdi, sb, nr_to_write, sb_locked); -} - /** * writeback_inodes_sb - writeback dirty inodes from given super_block * @sb: the superblock @@ -1237,21 +1216,16 @@ static void __writeback_inodes_sb(struct super_block *sb, int sb_locked) */ void writeback_inodes_sb(struct super_block *sb) { - __writeback_inodes_sb(sb, 0); -} -EXPORT_SYMBOL(writeback_inodes_sb); + unsigned long nr_dirty = global_page_state(NR_FILE_DIRTY); + unsigned long nr_unstable = global_page_state(NR_UNSTABLE_NFS); + long nr_to_write; -/** - * writeback_inodes_sb_locked - writeback dirty inodes from given super_block - * @sb: the superblock - * - * Like writeback_inodes_sb(), except the caller already holds the - * sb umount sem. - */ -void writeback_inodes_sb_locked(struct super_block *sb) -{ - __writeback_inodes_sb(sb, 1); + nr_to_write = nr_dirty + nr_unstable + + (inodes_stat.nr_inodes - inodes_stat.nr_unused); + + bdi_start_writeback(sb->s_bdi, sb, nr_to_write); } +EXPORT_SYMBOL(writeback_inodes_sb); /** * writeback_inodes_sb_if_idle - start writeback if none underway diff --git a/fs/sync.c b/fs/sync.c index e8cbd415e50a..5a537ccd2e85 100644 --- a/fs/sync.c +++ b/fs/sync.c @@ -42,7 +42,7 @@ static int __sync_filesystem(struct super_block *sb, int wait) if (wait) sync_inodes_sb(sb); else - writeback_inodes_sb_locked(sb); + writeback_inodes_sb(sb); if (sb->s_op->sync_fs) sb->s_op->sync_fs(sb, wait); diff --git a/include/linux/backing-dev.h b/include/linux/backing-dev.h index e6e0cb5437e6..aee5f6ce166e 100644 --- a/include/linux/backing-dev.h +++ b/include/linux/backing-dev.h @@ -106,7 +106,7 @@ int bdi_register_dev(struct backing_dev_info *bdi, dev_t dev); void bdi_unregister(struct backing_dev_info *bdi); int bdi_setup_and_register(struct backing_dev_info *, char *, unsigned int); void bdi_start_writeback(struct backing_dev_info *bdi, struct super_block *sb, - long nr_pages, int sb_locked); + long nr_pages); int bdi_writeback_task(struct bdi_writeback *wb); int bdi_has_dirty_io(struct backing_dev_info *bdi); void bdi_arm_supers_timer(void); diff --git a/include/linux/writeback.h b/include/linux/writeback.h index cc97d6caf2b3..f64134653a8c 100644 --- a/include/linux/writeback.h +++ b/include/linux/writeback.h @@ -65,15 +65,6 @@ struct writeback_control { * so we use a single control to update them */ unsigned no_nrwrite_index_update:1; - - /* - * For WB_SYNC_ALL, the sb must always be pinned. For WB_SYNC_NONE, - * the writeback code will pin the sb for the caller. However, - * for eg umount, the caller does WB_SYNC_NONE but already has - * the sb pinned. If the below is set, caller already has the - * sb pinned. - */ - unsigned sb_pinned:1; }; /* @@ -82,7 +73,6 @@ struct writeback_control { struct bdi_writeback; int inode_wait(void *); void writeback_inodes_sb(struct super_block *); -void writeback_inodes_sb_locked(struct super_block *); int writeback_inodes_sb_if_idle(struct super_block *); void sync_inodes_sb(struct super_block *); void writeback_inodes_wbc(struct writeback_control *wbc); diff --git a/mm/page-writeback.c b/mm/page-writeback.c index b289310e2c89..5fa63bdf52e4 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c @@ -597,7 +597,7 @@ static void balance_dirty_pages(struct address_space *mapping, (!laptop_mode && ((global_page_state(NR_FILE_DIRTY) + global_page_state(NR_UNSTABLE_NFS)) > background_thresh))) - bdi_start_writeback(bdi, NULL, 0, 0); + bdi_start_writeback(bdi, NULL, 0); } void set_page_dirty_balance(struct page *page, int page_mkwrite) @@ -707,7 +707,7 @@ void laptop_mode_timer_fn(unsigned long data) */ if (bdi_has_dirty_io(&q->backing_dev_info)) - bdi_start_writeback(&q->backing_dev_info, NULL, nr_pages, 0); + bdi_start_writeback(&q->backing_dev_info, NULL, nr_pages); } /* -- cgit v1.2.3 From e30c7c3b306312c157d67eedd6a01920518b756c Mon Sep 17 00:00:00 2001 From: Takuya Yoshikawa Date: Tue, 1 Jun 2010 14:10:47 +0100 Subject: binfmt_elf_fdpic: Fix clear_user() error handling clear_user() returns the number of bytes that could not be copied rather than an error code. So we should return -EFAULT rather than directly returning the results. Without this patch, positive values may be returned to elf_fdpic_map_file() and the following error handlings do not function as expected. 1. ret = elf_fdpic_map_file_constdisp_on_uclinux(params, file, mm); if (ret < 0) return ret; 2. ret = elf_fdpic_map_file_by_direct_mmap(params, file, mm); if (ret < 0) return ret; Signed-off-by: Takuya Yoshikawa Signed-off-by: David Howells Acked-by: Mike Frysinger CC: Alexander Viro CC: Andrew Morton CC: Daisuke HATAYAMA CC: Paul Mundt Signed-off-by: Linus Torvalds --- fs/binfmt_elf_fdpic.c | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) (limited to 'fs') diff --git a/fs/binfmt_elf_fdpic.c b/fs/binfmt_elf_fdpic.c index 2c5f9a0e5d72..63039ed9576f 100644 --- a/fs/binfmt_elf_fdpic.c +++ b/fs/binfmt_elf_fdpic.c @@ -990,10 +990,9 @@ static int elf_fdpic_map_file_constdisp_on_uclinux( /* clear any space allocated but not loaded */ if (phdr->p_filesz < phdr->p_memsz) { - ret = clear_user((void *) (seg->addr + phdr->p_filesz), - phdr->p_memsz - phdr->p_filesz); - if (ret) - return ret; + if (clear_user((void *) (seg->addr + phdr->p_filesz), + phdr->p_memsz - phdr->p_filesz)) + return -EFAULT; } if (mm) { @@ -1027,7 +1026,7 @@ static int elf_fdpic_map_file_by_direct_mmap(struct elf_fdpic_params *params, struct elf32_fdpic_loadseg *seg; struct elf32_phdr *phdr; unsigned long load_addr, delta_vaddr; - int loop, dvset, ret; + int loop, dvset; load_addr = params->load_addr; delta_vaddr = 0; @@ -1127,9 +1126,8 @@ static int elf_fdpic_map_file_by_direct_mmap(struct elf_fdpic_params *params, * PT_LOAD */ if (prot & PROT_WRITE && disp > 0) { kdebug("clear[%d] ad=%lx sz=%lx", loop, maddr, disp); - ret = clear_user((void __user *) maddr, disp); - if (ret) - return ret; + if (clear_user((void __user *) maddr, disp)) + return -EFAULT; maddr += disp; } @@ -1164,19 +1162,17 @@ static int elf_fdpic_map_file_by_direct_mmap(struct elf_fdpic_params *params, if (prot & PROT_WRITE && excess1 > 0) { kdebug("clear[%d] ad=%lx sz=%lx", loop, maddr + phdr->p_filesz, excess1); - ret = clear_user((void __user *) maddr + phdr->p_filesz, - excess1); - if (ret) - return ret; + if (clear_user((void __user *) maddr + phdr->p_filesz, + excess1)) + return -EFAULT; } #else if (excess > 0) { kdebug("clear[%d] ad=%lx sz=%lx", loop, maddr + phdr->p_filesz, excess); - ret = clear_user((void *) maddr + phdr->p_filesz, excess); - if (ret) - return ret; + if (clear_user((void *) maddr + phdr->p_filesz, excess)) + return -EFAULT; } #endif -- cgit v1.2.3 From 037776fcbe73236408f6c9ca97c782457efd6b53 Mon Sep 17 00:00:00 2001 From: Denis Kirjanov Date: Tue, 1 Jun 2010 17:15:39 +0100 Subject: AFS: Fix possible null pointer dereference in afs_alloc_server() Fix a possible null pointer dereference in afs_alloc_server(): the server pointer is NULL if there was an allocation failure, and under such a condition, we can't dereference it in the _leave() statement. Signed-off-by: Denis Kirjanov Signed-off-by: David Howells Signed-off-by: Linus Torvalds --- fs/afs/server.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/afs/server.c b/fs/afs/server.c index f49099516675..9fdc7fe3a7bc 100644 --- a/fs/afs/server.c +++ b/fs/afs/server.c @@ -91,9 +91,10 @@ static struct afs_server *afs_alloc_server(struct afs_cell *cell, memcpy(&server->addr, addr, sizeof(struct in_addr)); server->addr.s_addr = addr->s_addr; + _leave(" = %p{%d}", server, atomic_read(&server->usage)); + } else { + _leave(" = NULL [nomem]"); } - - _leave(" = %p{%d}", server, atomic_read(&server->usage)); return server; } -- cgit v1.2.3 From 06b43672a9e665cab18dc7b77d56d36884b90d45 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 1 Jun 2010 10:54:45 -0400 Subject: cifs: fix page refcount leak Commit 315e995c63a15cb4d4efdbfd70fe2db191917f7a is causing OOM kills when stress-testing a CIFS filesystem. The VFS readpages operation takes a page reference. The older code just handed this reference off to the page cache, but the new code takes an extra one. The simplest fix is to put the new reference after add_to_page_cache_lru. Signed-off-by: Jeff Layton Acked-by: Nick Piggin Signed-off-by: Steve French --- fs/cifs/file.c | 1 + 1 file changed, 1 insertion(+) (limited to 'fs') diff --git a/fs/cifs/file.c b/fs/cifs/file.c index f1ff785b2292..75541af4b3db 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -1952,6 +1952,7 @@ static void cifs_copy_cache_pages(struct address_space *mapping, bytes_read -= PAGE_CACHE_SIZE; continue; } + page_cache_release(page); target = kmap_atomic(page, KM_USER0); -- cgit v1.2.3 From 08a66859e69264f3223560d06b88e80c1a6a6387 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 1 Jun 2010 20:58:22 +0100 Subject: FS-Cache: Remove unneeded null checks fscache_write_op() makes unnecessary checks of the page variable to see if it is NULL. It can't be NULL at those points as the kernel would already have crashed a little higher up where we examined page->index. Furthermore, unless radix_tree_gang_lookup_tag() can return 1 but no page, a NULL pointer crash should not be encountered there as we can only get there if r_t_g_l_t() returned 1. Signed-off-by: Dan Carpenter Signed-off-by: David Howells Signed-off-by: Linus Torvalds --- fs/fscache/page.c | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) (limited to 'fs') diff --git a/fs/fscache/page.c b/fs/fscache/page.c index 47aefd376e54..723b889fd219 100644 --- a/fs/fscache/page.c +++ b/fs/fscache/page.c @@ -710,30 +710,26 @@ static void fscache_write_op(struct fscache_operation *_op) goto superseded; } - if (page) { - radix_tree_tag_set(&cookie->stores, page->index, - FSCACHE_COOKIE_STORING_TAG); - radix_tree_tag_clear(&cookie->stores, page->index, - FSCACHE_COOKIE_PENDING_TAG); - } + radix_tree_tag_set(&cookie->stores, page->index, + FSCACHE_COOKIE_STORING_TAG); + radix_tree_tag_clear(&cookie->stores, page->index, + FSCACHE_COOKIE_PENDING_TAG); spin_unlock(&cookie->stores_lock); spin_unlock(&object->lock); - if (page) { - fscache_set_op_state(&op->op, "Store"); - fscache_stat(&fscache_n_store_pages); - fscache_stat(&fscache_n_cop_write_page); - ret = object->cache->ops->write_page(op, page); - fscache_stat_d(&fscache_n_cop_write_page); - fscache_set_op_state(&op->op, "EndWrite"); - fscache_end_page_write(object, page); - if (ret < 0) { - fscache_set_op_state(&op->op, "Abort"); - fscache_abort_object(object); - } else { - fscache_enqueue_operation(&op->op); - } + fscache_set_op_state(&op->op, "Store"); + fscache_stat(&fscache_n_store_pages); + fscache_stat(&fscache_n_cop_write_page); + ret = object->cache->ops->write_page(op, page); + fscache_stat_d(&fscache_n_cop_write_page); + fscache_set_op_state(&op->op, "EndWrite"); + fscache_end_page_write(object, page); + if (ret < 0) { + fscache_set_op_state(&op->op, "Abort"); + fscache_abort_object(object); + } else { + fscache_enqueue_operation(&op->op); } _leave(""); -- cgit v1.2.3 From b160fdabe93a8a53094f90f02bf4dcb500782aab Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 1 Jun 2010 21:59:18 +0200 Subject: nfsd: nfsd_setattr needs to call commit_metadata The conversion of write_inode_now calls to commit_metadata in commit f501912a35c02eadc55ca9396ece55fe36f785d0 missed out the call in nfsd_setattr. But without this conversion we can't guarantee that a SETATTR request has actually been commited to disk with XFS, which causes a regression from 2.6.32 (only for NFSv2, but anyway). Signed-off-by: Christoph Hellwig Cc: stable@kernel.org Signed-off-by: J. Bruce Fields --- fs/nfsd/vfs.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 6dd5f1970e01..3440dd8a4fb3 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -443,8 +443,7 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap, if (size_change) put_write_access(inode); if (!err) - if (EX_ISSYNC(fhp->fh_export)) - write_inode_now(inode, 1); + commit_metadata(fhp); out: return err; -- cgit v1.2.3 From 13a4214cd9ec14d7b77e98bd3ee51f60f868a6e5 Mon Sep 17 00:00:00 2001 From: Henry C Chang Date: Tue, 1 Jun 2010 11:31:08 -0700 Subject: ceph: fix d_subdirs ordering problem We misused list_move_tail() to order the dentry in d_subdirs. This will screw up the d_subdirs order. This bug can be reliably reproduced by: 1. mount ceph fs. 2. on ceph fs, git clone git://ceph.newdream.net/git/ceph.git 3. Run autogen.sh in ceph directory. (Note: Errors only occur at the first time you run autogen.sh.) Signed-off-by: Henry C Chang Signed-off-by: Sage Weil --- fs/ceph/inode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c index 226f5a50d362..ab47f46ca282 100644 --- a/fs/ceph/inode.c +++ b/fs/ceph/inode.c @@ -827,7 +827,7 @@ static void ceph_set_dentry_offset(struct dentry *dn) spin_lock(&dcache_lock); spin_lock(&dn->d_lock); - list_move_tail(&dir->d_subdirs, &dn->d_u.d_child); + list_move(&dn->d_u.d_child, &dir->d_subdirs); dout("set_dentry_offset %p %lld (%p %p)\n", dn, di->offset, dn->d_u.d_child.prev, dn->d_u.d_child.next); spin_unlock(&dn->d_lock); -- cgit v1.2.3 From 205475679a74fe40b63a1c7f41110fdb64daa8b9 Mon Sep 17 00:00:00 2001 From: Yehuda Sadeh Date: Tue, 1 Jun 2010 10:37:40 -0700 Subject: ceph: fix memory leak in statfs Freeing the statfs request structure when required. Signed-off-by: Yehuda Sadeh Signed-off-by: Sage Weil --- fs/ceph/mon_client.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'fs') diff --git a/fs/ceph/mon_client.c b/fs/ceph/mon_client.c index 21c62e9b7d1d..07a539906e67 100644 --- a/fs/ceph/mon_client.c +++ b/fs/ceph/mon_client.c @@ -400,6 +400,8 @@ static void release_generic_request(struct kref *kref) ceph_msg_put(req->reply); if (req->request) ceph_msg_put(req->request); + + kfree(req); } static void put_generic_request(struct ceph_mon_generic_request *req) -- cgit v1.2.3 From 558d3499bd059d4534b1f2b69dc1c562acc733fe Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Tue, 1 Jun 2010 12:51:12 -0700 Subject: ceph: fix f_namelen reported by statfs We were setting f_namelen in kstatfs to PATH_MAX instead of NAME_MAX. That disagrees with ceph_lookup behavior (which checks against NAME_MAX), and also makes the pjd posix test suite spit out ugly errors because with can't clean up its temporary files. Signed-off-by: Sage Weil --- fs/ceph/super.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/ceph/super.c b/fs/ceph/super.c index 4e0bee240b9d..8db88792b5ad 100644 --- a/fs/ceph/super.c +++ b/fs/ceph/super.c @@ -89,7 +89,7 @@ static int ceph_statfs(struct dentry *dentry, struct kstatfs *buf) buf->f_files = le64_to_cpu(st.num_objects); buf->f_ffree = -1; - buf->f_namelen = PATH_MAX; + buf->f_namelen = NAME_MAX; buf->f_frsize = PAGE_CACHE_SIZE; /* leave fsid little-endian, regardless of host endianness */ -- cgit v1.2.3 From 1f5a81e41f8b1a782c68d3843e9ec1bfaadf7d72 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Wed, 2 Jun 2010 22:04:39 -0400 Subject: ext4: Make sure the MOVE_EXT ioctl can't overwrite append-only files Dan Roseberg has reported a problem with the MOVE_EXT ioctl. If the donor file is an append-only file, we should not allow the operation to proceed, lest we end up overwriting the contents of an append-only file. Signed-off-by: "Theodore Ts'o" Cc: Dan Rosenberg --- fs/ext4/move_extent.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'fs') diff --git a/fs/ext4/move_extent.c b/fs/ext4/move_extent.c index 3a6c92ac131c..52abfa12762a 100644 --- a/fs/ext4/move_extent.c +++ b/fs/ext4/move_extent.c @@ -960,6 +960,9 @@ mext_check_arguments(struct inode *orig_inode, return -EINVAL; } + if (IS_IMMUTABLE(donor_inode) || IS_APPEND(donor_inode)) + return -EPERM; + /* Ext4 move extent does not support swapfile */ if (IS_SWAPFILE(orig_inode) || IS_SWAPFILE(donor_inode)) { ext4_debug("ext4 move extent: The argument files should " -- cgit v1.2.3 From 5b257b4a1f9239624c6b5e669763de04e482c2b3 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Thu, 3 Jun 2010 16:22:29 +1000 Subject: xfs: fix race in inode cluster freeing failing to stale inodes When an inode cluster is freed, it needs to mark all inodes in memory as XFS_ISTALE before marking the buffer as stale. This is eeded because the inodes have a different life cycle to the buffer, and once the buffer is torn down during transaction completion, we must ensure none of the inodes get written back (which is what XFS_ISTALE does). Unfortunately, xfs_ifree_cluster() has some bugs that lead to inodes not being marked with XFS_ISTALE. This shows up when xfs_iflush() is called on these inodes either during inode reclaim or tail pushing on the AIL. The buffer is read back, but no longer contains inodes and so triggers assert failures and shutdowns. This was reproducable with at run.dbench10 invocation from xfstests. There are two main causes of xfs_ifree_cluster() failing. The first is simple - it checks in-memory inodes it finds in the per-ag icache to see if they are clean without holding the flush lock. if they are clean it skips them completely. However, If an inode is flushed delwri, it will appear clean, but is not guaranteed to be written back until the flush lock has been dropped. Hence we may have raced on the clean check and the inode may actually be dirty. Hence always mark inodes found in memory stale before we check properly if they are clean. The second is more complex, and makes the first problem easier to hit. Basically the in-memory inode scan is done with full knowledge it can be racing with inode flushing and AIl tail pushing, which means that inodes that it can't get the flush lock on might not be attached to the buffer after then in-memory inode scan due to IO completion occurring. This is actually documented in the code as "needs better interlocking". i.e. this is a zero-day bug. Effectively, the in-memory scan must be done while the inode buffer is locked and Io cannot be issued on it while we do the in-memory inode scan. This ensures that inodes we couldn't get the flush lock on are guaranteed to be attached to the cluster buffer, so we can then catch all in-memory inodes and mark them stale. Now that the inode cluster buffer is locked before the in-memory scan is done, there is no need for the two-phase update of the in-memory inodes, so simplify the code into two loops and remove the allocation of the temporary buffer used to hold locked inodes across the phases. Signed-off-by: Dave Chinner Reviewed-by: Christoph Hellwig --- fs/xfs/xfs_inode.c | 142 +++++++++++++++++++++++------------------------------ 1 file changed, 62 insertions(+), 80 deletions(-) (limited to 'fs') diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index 6ba7765026f6..d53c39de7d05 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -1940,10 +1940,10 @@ xfs_ifree_cluster( int blks_per_cluster; int nbufs; int ninodes; - int i, j, found, pre_flushed; + int i, j; xfs_daddr_t blkno; xfs_buf_t *bp; - xfs_inode_t *ip, **ip_found; + xfs_inode_t *ip; xfs_inode_log_item_t *iip; xfs_log_item_t *lip; struct xfs_perag *pag; @@ -1960,114 +1960,97 @@ xfs_ifree_cluster( nbufs = XFS_IALLOC_BLOCKS(mp) / blks_per_cluster; } - ip_found = kmem_alloc(ninodes * sizeof(xfs_inode_t *), KM_NOFS); - for (j = 0; j < nbufs; j++, inum += ninodes) { + int found = 0; + blkno = XFS_AGB_TO_DADDR(mp, XFS_INO_TO_AGNO(mp, inum), XFS_INO_TO_AGBNO(mp, inum)); + /* + * We obtain and lock the backing buffer first in the process + * here, as we have to ensure that any dirty inode that we + * can't get the flush lock on is attached to the buffer. + * If we scan the in-memory inodes first, then buffer IO can + * complete before we get a lock on it, and hence we may fail + * to mark all the active inodes on the buffer stale. + */ + bp = xfs_trans_get_buf(tp, mp->m_ddev_targp, blkno, + mp->m_bsize * blks_per_cluster, + XBF_LOCK); + + /* + * Walk the inodes already attached to the buffer and mark them + * stale. These will all have the flush locks held, so an + * in-memory inode walk can't lock them. + */ + lip = XFS_BUF_FSPRIVATE(bp, xfs_log_item_t *); + while (lip) { + if (lip->li_type == XFS_LI_INODE) { + iip = (xfs_inode_log_item_t *)lip; + ASSERT(iip->ili_logged == 1); + lip->li_cb = (void(*)(xfs_buf_t*,xfs_log_item_t*)) xfs_istale_done; + xfs_trans_ail_copy_lsn(mp->m_ail, + &iip->ili_flush_lsn, + &iip->ili_item.li_lsn); + xfs_iflags_set(iip->ili_inode, XFS_ISTALE); + found++; + } + lip = lip->li_bio_list; + } /* - * Look for each inode in memory and attempt to lock it, - * we can be racing with flush and tail pushing here. - * any inode we get the locks on, add to an array of - * inode items to process later. + * For each inode in memory attempt to add it to the inode + * buffer and set it up for being staled on buffer IO + * completion. This is safe as we've locked out tail pushing + * and flushing by locking the buffer. * - * The get the buffer lock, we could beat a flush - * or tail pushing thread to the lock here, in which - * case they will go looking for the inode buffer - * and fail, we need some other form of interlock - * here. + * We have already marked every inode that was part of a + * transaction stale above, which means there is no point in + * even trying to lock them. */ - found = 0; for (i = 0; i < ninodes; i++) { read_lock(&pag->pag_ici_lock); ip = radix_tree_lookup(&pag->pag_ici_root, XFS_INO_TO_AGINO(mp, (inum + i))); - /* Inode not in memory or we found it already, - * nothing to do - */ + /* Inode not in memory or stale, nothing to do */ if (!ip || xfs_iflags_test(ip, XFS_ISTALE)) { read_unlock(&pag->pag_ici_lock); continue; } - if (xfs_inode_clean(ip)) { - read_unlock(&pag->pag_ici_lock); - continue; - } - - /* If we can get the locks then add it to the - * list, otherwise by the time we get the bp lock - * below it will already be attached to the - * inode buffer. - */ - - /* This inode will already be locked - by us, lets - * keep it that way. - */ - - if (ip == free_ip) { - if (xfs_iflock_nowait(ip)) { - xfs_iflags_set(ip, XFS_ISTALE); - if (xfs_inode_clean(ip)) { - xfs_ifunlock(ip); - } else { - ip_found[found++] = ip; - } - } + /* don't try to lock/unlock the current inode */ + if (ip != free_ip && + !xfs_ilock_nowait(ip, XFS_ILOCK_EXCL)) { read_unlock(&pag->pag_ici_lock); continue; } + read_unlock(&pag->pag_ici_lock); - if (xfs_ilock_nowait(ip, XFS_ILOCK_EXCL)) { - if (xfs_iflock_nowait(ip)) { - xfs_iflags_set(ip, XFS_ISTALE); - - if (xfs_inode_clean(ip)) { - xfs_ifunlock(ip); - xfs_iunlock(ip, XFS_ILOCK_EXCL); - } else { - ip_found[found++] = ip; - } - } else { + if (!xfs_iflock_nowait(ip)) { + if (ip != free_ip) xfs_iunlock(ip, XFS_ILOCK_EXCL); - } + continue; } - read_unlock(&pag->pag_ici_lock); - } - bp = xfs_trans_get_buf(tp, mp->m_ddev_targp, blkno, - mp->m_bsize * blks_per_cluster, - XBF_LOCK); - - pre_flushed = 0; - lip = XFS_BUF_FSPRIVATE(bp, xfs_log_item_t *); - while (lip) { - if (lip->li_type == XFS_LI_INODE) { - iip = (xfs_inode_log_item_t *)lip; - ASSERT(iip->ili_logged == 1); - lip->li_cb = (void(*)(xfs_buf_t*,xfs_log_item_t*)) xfs_istale_done; - xfs_trans_ail_copy_lsn(mp->m_ail, - &iip->ili_flush_lsn, - &iip->ili_item.li_lsn); - xfs_iflags_set(iip->ili_inode, XFS_ISTALE); - pre_flushed++; + xfs_iflags_set(ip, XFS_ISTALE); + if (xfs_inode_clean(ip)) { + ASSERT(ip != free_ip); + xfs_ifunlock(ip); + xfs_iunlock(ip, XFS_ILOCK_EXCL); + continue; } - lip = lip->li_bio_list; - } - for (i = 0; i < found; i++) { - ip = ip_found[i]; iip = ip->i_itemp; - if (!iip) { + /* inode with unlogged changes only */ + ASSERT(ip != free_ip); ip->i_update_core = 0; xfs_ifunlock(ip); xfs_iunlock(ip, XFS_ILOCK_EXCL); continue; } + found++; iip->ili_last_fields = iip->ili_format.ilf_fields; iip->ili_format.ilf_fields = 0; @@ -2078,17 +2061,16 @@ xfs_ifree_cluster( xfs_buf_attach_iodone(bp, (void(*)(xfs_buf_t*,xfs_log_item_t*)) xfs_istale_done, (xfs_log_item_t *)iip); - if (ip != free_ip) { + + if (ip != free_ip) xfs_iunlock(ip, XFS_ILOCK_EXCL); - } } - if (found || pre_flushed) + if (found) xfs_trans_stale_inode_buf(tp, bp); xfs_trans_binval(tp, bp); } - kmem_free(ip_found); xfs_perag_put(pag); } -- cgit v1.2.3 From 070ecdca54dde9577d2697088e74e45568f48efb Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 3 Jun 2010 16:22:29 +1000 Subject: xfs: skip writeback from reclaim context Allowing writeback from reclaim context causes massive problems with stack overflows as we can call into the writeback code which tends to be a heavy stack user both in the generic code and XFS from random contexts that perform memory allocations. Follow the example of btrfs (and in slightly different form ext4) and refuse to write out data from reclaim context. This issue should really be handled by the VM so that we can tune better for this case, but until we get it sorted out there we have to hack around this in each filesystem with a complex writeback path. Signed-off-by: Christoph Hellwig Reviewed-by: Dave Chinner --- fs/xfs/linux-2.6/xfs_aops.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_aops.c b/fs/xfs/linux-2.6/xfs_aops.c index 089eaca860b4..a0fa3bf0d1bb 100644 --- a/fs/xfs/linux-2.6/xfs_aops.c +++ b/fs/xfs/linux-2.6/xfs_aops.c @@ -1332,6 +1332,21 @@ xfs_vm_writepage( trace_xfs_writepage(inode, page, 0); + /* + * Refuse to write the page out if we are called from reclaim context. + * + * This is primarily to avoid stack overflows when called from deep + * used stacks in random callers for direct reclaim, but disabling + * reclaim for kswap is a nice side-effect as kswapd causes rather + * suboptimal I/O patters, too. + * + * This should really be done by the core VM, but until that happens + * filesystems like XFS, btrfs and ext4 have to take care of this + * by themselves. + */ + if (current->flags & PF_MEMALLOC) + goto out_fail; + /* * We need a transaction if: * 1. There are delalloc buffers on the page -- cgit v1.2.3 From f9369729496a0f4c607a4cc1ea4dfeddbbfc505a Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 3 Jun 2010 16:22:29 +1000 Subject: xfs: improve xfs_isilocked Use rwsem_is_locked to make the assertations for shared locks work. Signed-off-by: Christoph Hellwig Reviewed-by: Dave Chinner --- fs/xfs/xfs_iget.c | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) (limited to 'fs') diff --git a/fs/xfs/xfs_iget.c b/fs/xfs/xfs_iget.c index f44a367907a8..75df75f43d48 100644 --- a/fs/xfs/xfs_iget.c +++ b/fs/xfs/xfs_iget.c @@ -741,30 +741,24 @@ xfs_ilock_demote( } #ifdef DEBUG -/* - * Debug-only routine, without additional rw_semaphore APIs, we can - * now only answer requests regarding whether we hold the lock for write - * (reader state is outside our visibility, we only track writer state). - * - * Note: this means !xfs_isilocked would give false positives, so don't do that. - */ int xfs_isilocked( xfs_inode_t *ip, uint lock_flags) { - if ((lock_flags & (XFS_ILOCK_EXCL|XFS_ILOCK_SHARED)) == - XFS_ILOCK_EXCL) { - if (!ip->i_lock.mr_writer) - return 0; + if (lock_flags & (XFS_ILOCK_EXCL|XFS_ILOCK_SHARED)) { + if (!(lock_flags & XFS_ILOCK_SHARED)) + return !!ip->i_lock.mr_writer; + return rwsem_is_locked(&ip->i_lock.mr_lock); } - if ((lock_flags & (XFS_IOLOCK_EXCL|XFS_IOLOCK_SHARED)) == - XFS_IOLOCK_EXCL) { - if (!ip->i_iolock.mr_writer) - return 0; + if (lock_flags & (XFS_IOLOCK_EXCL|XFS_IOLOCK_SHARED)) { + if (!(lock_flags & XFS_IOLOCK_SHARED)) + return !!ip->i_iolock.mr_writer; + return rwsem_is_locked(&ip->i_iolock.mr_lock); } - return 1; + ASSERT(0); + return 0; } #endif -- cgit v1.2.3 From f324e4cb2cadd9a42932c8a158e761ae31b88e72 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Thu, 3 Jun 2010 08:03:39 +0100 Subject: jffs2: Fix in-core inode leaks on error paths Pointed out by Al Viro. Signed-off-by: David Woodhouse --- fs/jffs2/dir.c | 115 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 56 insertions(+), 59 deletions(-) (limited to 'fs') diff --git a/fs/jffs2/dir.c b/fs/jffs2/dir.c index 7aa4417e085f..cb7ef34d384c 100644 --- a/fs/jffs2/dir.c +++ b/fs/jffs2/dir.c @@ -360,8 +360,8 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char /* Eeek. Wave bye bye */ mutex_unlock(&f->sem); jffs2_complete_reservation(c); - jffs2_clear_inode(inode); - return PTR_ERR(fn); + ret = PTR_ERR(fn); + goto fail; } /* We use f->target field to store the target path. */ @@ -370,8 +370,8 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char printk(KERN_WARNING "Can't allocate %d bytes of memory\n", targetlen + 1); mutex_unlock(&f->sem); jffs2_complete_reservation(c); - jffs2_clear_inode(inode); - return -ENOMEM; + ret = -ENOMEM; + goto fail; } memcpy(f->target, target, targetlen + 1); @@ -386,30 +386,24 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char jffs2_complete_reservation(c); ret = jffs2_init_security(inode, dir_i); - if (ret) { - jffs2_clear_inode(inode); - return ret; - } + if (ret) + goto fail; + ret = jffs2_init_acl_post(inode); - if (ret) { - jffs2_clear_inode(inode); - return ret; - } + if (ret) + goto fail; ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &alloclen, ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen)); - if (ret) { - /* Eep. */ - jffs2_clear_inode(inode); - return ret; - } + if (ret) + goto fail; rd = jffs2_alloc_raw_dirent(); if (!rd) { /* Argh. Now we treat it like a normal delete */ jffs2_complete_reservation(c); - jffs2_clear_inode(inode); - return -ENOMEM; + ret = -ENOMEM; + goto fail; } dir_f = JFFS2_INODE_INFO(dir_i); @@ -437,8 +431,8 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char jffs2_complete_reservation(c); jffs2_free_raw_dirent(rd); mutex_unlock(&dir_f->sem); - jffs2_clear_inode(inode); - return PTR_ERR(fd); + ret = PTR_ERR(fd); + goto fail; } dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(rd->mctime)); @@ -454,6 +448,11 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char d_instantiate(dentry, inode); return 0; + + fail: + make_bad_inode(inode); + iput(inode); + return ret; } @@ -519,8 +518,8 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode) /* Eeek. Wave bye bye */ mutex_unlock(&f->sem); jffs2_complete_reservation(c); - jffs2_clear_inode(inode); - return PTR_ERR(fn); + ret = PTR_ERR(fn); + goto fail; } /* No data here. Only a metadata node, which will be obsoleted by the first data write @@ -531,30 +530,24 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode) jffs2_complete_reservation(c); ret = jffs2_init_security(inode, dir_i); - if (ret) { - jffs2_clear_inode(inode); - return ret; - } + if (ret) + goto fail; + ret = jffs2_init_acl_post(inode); - if (ret) { - jffs2_clear_inode(inode); - return ret; - } + if (ret) + goto fail; ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &alloclen, ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen)); - if (ret) { - /* Eep. */ - jffs2_clear_inode(inode); - return ret; - } + if (ret) + goto fail; rd = jffs2_alloc_raw_dirent(); if (!rd) { /* Argh. Now we treat it like a normal delete */ jffs2_complete_reservation(c); - jffs2_clear_inode(inode); - return -ENOMEM; + ret = -ENOMEM; + goto fail; } dir_f = JFFS2_INODE_INFO(dir_i); @@ -582,8 +575,8 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode) jffs2_complete_reservation(c); jffs2_free_raw_dirent(rd); mutex_unlock(&dir_f->sem); - jffs2_clear_inode(inode); - return PTR_ERR(fd); + ret = PTR_ERR(fd); + goto fail; } dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(rd->mctime)); @@ -600,6 +593,11 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode) d_instantiate(dentry, inode); return 0; + + fail: + make_bad_inode(inode); + iput(inode); + return ret; } static int jffs2_rmdir (struct inode *dir_i, struct dentry *dentry) @@ -693,8 +691,8 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, de /* Eeek. Wave bye bye */ mutex_unlock(&f->sem); jffs2_complete_reservation(c); - jffs2_clear_inode(inode); - return PTR_ERR(fn); + ret = PTR_ERR(fn); + goto fail; } /* No data here. Only a metadata node, which will be obsoleted by the first data write @@ -705,30 +703,24 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, de jffs2_complete_reservation(c); ret = jffs2_init_security(inode, dir_i); - if (ret) { - jffs2_clear_inode(inode); - return ret; - } + if (ret) + goto fail; + ret = jffs2_init_acl_post(inode); - if (ret) { - jffs2_clear_inode(inode); - return ret; - } + if (ret) + goto fail; ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &alloclen, ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen)); - if (ret) { - /* Eep. */ - jffs2_clear_inode(inode); - return ret; - } + if (ret) + goto fail; rd = jffs2_alloc_raw_dirent(); if (!rd) { /* Argh. Now we treat it like a normal delete */ jffs2_complete_reservation(c); - jffs2_clear_inode(inode); - return -ENOMEM; + ret = -ENOMEM; + goto fail; } dir_f = JFFS2_INODE_INFO(dir_i); @@ -759,8 +751,8 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, de jffs2_complete_reservation(c); jffs2_free_raw_dirent(rd); mutex_unlock(&dir_f->sem); - jffs2_clear_inode(inode); - return PTR_ERR(fd); + ret = PTR_ERR(fd); + goto fail; } dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(rd->mctime)); @@ -777,6 +769,11 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, de d_instantiate(dentry, inode); return 0; + + fail: + make_bad_inode(inode); + iput(inode); + return ret; } static int jffs2_rename (struct inode *old_dir_i, struct dentry *old_dentry, -- cgit v1.2.3 From e72e6497e74811e01d72b4c1b7537b3aea3ee857 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Thu, 3 Jun 2010 08:09:12 +0100 Subject: jffs2: Fix NFS race by using insert_inode_locked() New inodes need to be locked as we're creating them, so they don't get used by other things (like NFSd) before they're ready. Pointed out by Al Viro. Signed-off-by: David Woodhouse --- fs/jffs2/dir.c | 12 ++++++++++-- fs/jffs2/fs.c | 7 ++++++- 2 files changed, 16 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/jffs2/dir.c b/fs/jffs2/dir.c index cb7ef34d384c..166062a68230 100644 --- a/fs/jffs2/dir.c +++ b/fs/jffs2/dir.c @@ -222,15 +222,18 @@ static int jffs2_create(struct inode *dir_i, struct dentry *dentry, int mode, dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(ri->ctime)); jffs2_free_raw_inode(ri); - d_instantiate(dentry, inode); D1(printk(KERN_DEBUG "jffs2_create: Created ino #%lu with mode %o, nlink %d(%d). nrpages %ld\n", inode->i_ino, inode->i_mode, inode->i_nlink, f->inocache->pino_nlink, inode->i_mapping->nrpages)); + + d_instantiate(dentry, inode); + unlock_new_inode(inode); return 0; fail: make_bad_inode(inode); + unlock_new_inode(inode); iput(inode); jffs2_free_raw_inode(ri); return ret; @@ -447,10 +450,12 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char jffs2_complete_reservation(c); d_instantiate(dentry, inode); + unlock_new_inode(inode); return 0; fail: make_bad_inode(inode); + unlock_new_inode(inode); iput(inode); return ret; } @@ -592,10 +597,12 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode) jffs2_complete_reservation(c); d_instantiate(dentry, inode); + unlock_new_inode(inode); return 0; fail: make_bad_inode(inode); + unlock_new_inode(inode); iput(inode); return ret; } @@ -767,11 +774,12 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, de jffs2_complete_reservation(c); d_instantiate(dentry, inode); - + unlock_new_inode(inode); return 0; fail: make_bad_inode(inode); + unlock_new_inode(inode); iput(inode); return ret; } diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c index 86e0821fc989..26037e2d6154 100644 --- a/fs/jffs2/fs.c +++ b/fs/jffs2/fs.c @@ -465,7 +465,12 @@ struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_i inode->i_blocks = 0; inode->i_size = 0; - insert_inode_hash(inode); + if (insert_inode_locked(inode) < 0) { + make_bad_inode(inode); + unlock_new_inode(inode); + iput(inode); + return ERR_PTR(-EINVAL); + } return inode; } -- cgit v1.2.3 From 6a6ca57de92fcae34603551ac944aa74758c30d4 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Thu, 3 Jun 2010 12:44:30 +0200 Subject: pipe: adjust minimum pipe size to 1 page We don't need to pages to guarantee the POSIX requirement that upto a page size write must be atomic to an empty pipe. Signed-off-by: Jens Axboe --- fs/pipe.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'fs') diff --git a/fs/pipe.c b/fs/pipe.c index 541d6626f9d9..369a0245aab6 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -1181,13 +1181,7 @@ long pipe_fcntl(struct file *file, unsigned int cmd, unsigned long arg) if (!capable(CAP_SYS_ADMIN) && nr_pages > pipe_max_pages) { ret = -EPERM; goto out; - } - - /* - * The pipe needs to be at least 2 pages large to - * guarantee POSIX behaviour. - */ - if (arg < 2) { + } else if (nr_pages < 1) { ret = -EINVAL; goto out; } -- cgit v1.2.3 From 419f8367ea37e5adc5d95479e8fd5554b92b49fe Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Thu, 3 Jun 2010 12:45:28 +0200 Subject: pipe: change the privilege required for growing a pipe beyond system max Change it to CAP_SYS_RESOURCE, as that more accurately models what we want to control. Suggested-by: Michael Kerrisk Signed-off-by: Jens Axboe --- fs/pipe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/pipe.c b/fs/pipe.c index 369a0245aab6..f98fae3e36b0 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -1178,7 +1178,7 @@ long pipe_fcntl(struct file *file, unsigned int cmd, unsigned long arg) nr_pages = (arg + PAGE_SIZE - 1) >> PAGE_SHIFT; nr_pages = roundup_pow_of_two(nr_pages); - if (!capable(CAP_SYS_ADMIN) && nr_pages > pipe_max_pages) { + if (!capable(CAP_SYS_RESOURCE) && nr_pages > pipe_max_pages) { ret = -EPERM; goto out; } else if (nr_pages < 1) { -- cgit v1.2.3 From ff9da691c0498ff81fdd014e7a0731dab2337dac Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Thu, 3 Jun 2010 14:54:39 +0200 Subject: pipe: change /proc/sys/fs/pipe-max-pages to byte sized interface This changes the interface to be based on bytes instead. The API matches that of F_SETPIPE_SZ in that it rounds up the passed in size so that the resulting page array is a power-of-2 in size. The proc file is renamed to /proc/sys/fs/pipe-max-size to reflect this change. Signed-off-by: Jens Axboe --- fs/pipe.c | 54 ++++++++++++++++++++++++++++++++++++----------- include/linux/pipe_fs_i.h | 4 +++- kernel/sysctl.c | 8 +++---- 3 files changed, 49 insertions(+), 17 deletions(-) (limited to 'fs') diff --git a/fs/pipe.c b/fs/pipe.c index f98fae3e36b0..69c4c7c13ea9 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -26,9 +26,14 @@ /* * The max size that a non-root user is allowed to grow the pipe. Can - * be set by root in /proc/sys/fs/pipe-max-pages + * be set by root in /proc/sys/fs/pipe-max-size */ -unsigned int pipe_max_pages = PIPE_DEF_BUFFERS * 16; +unsigned int pipe_max_size = 1048576; + +/* + * Minimum pipe size, as required by POSIX + */ +unsigned int pipe_min_size = PAGE_SIZE; /* * We use a start+len construction, which provides full use of the @@ -1156,6 +1161,35 @@ static long pipe_set_size(struct pipe_inode_info *pipe, unsigned long nr_pages) return nr_pages * PAGE_SIZE; } +/* + * Currently we rely on the pipe array holding a power-of-2 number + * of pages. + */ +static inline unsigned int round_pipe_size(unsigned int size) +{ + unsigned long nr_pages; + + nr_pages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT; + return roundup_pow_of_two(nr_pages) << PAGE_SHIFT; +} + +/* + * This should work even if CONFIG_PROC_FS isn't set, as proc_dointvec_minmax + * will return an error. + */ +int pipe_proc_fn(struct ctl_table *table, int write, void __user *buf, + size_t *lenp, loff_t *ppos) +{ + int ret; + + ret = proc_dointvec_minmax(table, write, buf, lenp, ppos); + if (ret < 0 || !write) + return ret; + + pipe_max_size = round_pipe_size(pipe_max_size); + return ret; +} + long pipe_fcntl(struct file *file, unsigned int cmd, unsigned long arg) { struct pipe_inode_info *pipe; @@ -1169,23 +1203,19 @@ long pipe_fcntl(struct file *file, unsigned int cmd, unsigned long arg) switch (cmd) { case F_SETPIPE_SZ: { - unsigned long nr_pages; + unsigned int size, nr_pages; - /* - * Currently the array must be a power-of-2 size, so adjust - * upwards if needed. - */ - nr_pages = (arg + PAGE_SIZE - 1) >> PAGE_SHIFT; - nr_pages = roundup_pow_of_two(nr_pages); + size = round_pipe_size(arg); + nr_pages = size >> PAGE_SHIFT; - if (!capable(CAP_SYS_RESOURCE) && nr_pages > pipe_max_pages) { + if (!capable(CAP_SYS_RESOURCE) && size > pipe_max_size) { ret = -EPERM; goto out; - } else if (nr_pages < 1) { + } else if (nr_pages < PAGE_SIZE) { ret = -EINVAL; goto out; } - ret = pipe_set_size(pipe, arg); + ret = pipe_set_size(pipe, nr_pages); break; } case F_GETPIPE_SZ: diff --git a/include/linux/pipe_fs_i.h b/include/linux/pipe_fs_i.h index 16de3933c45e..445796945ac9 100644 --- a/include/linux/pipe_fs_i.h +++ b/include/linux/pipe_fs_i.h @@ -139,7 +139,9 @@ void pipe_lock(struct pipe_inode_info *); void pipe_unlock(struct pipe_inode_info *); void pipe_double_lock(struct pipe_inode_info *, struct pipe_inode_info *); -extern unsigned int pipe_max_pages; +extern unsigned int pipe_max_size, pipe_min_size; +int pipe_proc_fn(struct ctl_table *, int, void __user *, size_t *, loff_t *); + /* Drop the inode semaphore and wait for a pipe event, atomically */ void pipe_wait(struct pipe_inode_info *pipe); diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 997080f00e0b..d24f761f4876 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -1471,12 +1471,12 @@ static struct ctl_table fs_table[] = { }, #endif { - .procname = "pipe-max-pages", - .data = &pipe_max_pages, + .procname = "pipe-max-size", + .data = &pipe_max_size, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = &proc_dointvec_minmax, - .extra1 = &two, + .proc_handler = &pipe_proc_fn, + .extra1 = &pipe_min_size, }, /* * NOTE: do not add new entries to this table unless you have read -- cgit v1.2.3 From 1e5ea23df11c7c90c7e7268dd3a6603bfa5aadf7 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Fri, 4 Jun 2010 10:05:40 -0700 Subject: ceph: fix lease revocation when seq doesn't match If the client revokes a lease with a higher seq than what we have, keep the mds's seq, so that it honors our release. Otherwise, we can hang indefinitely. Signed-off-by: Sage Weil --- fs/ceph/mds_client.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index b49f12822cbc..29b4485cf1ca 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -2433,6 +2433,7 @@ static void handle_lease(struct ceph_mds_client *mdsc, struct ceph_dentry_info *di; int mds = session->s_mds; struct ceph_mds_lease *h = msg->front.iov_base; + u32 seq; struct ceph_vino vino; int mask; struct qstr dname; @@ -2446,6 +2447,7 @@ static void handle_lease(struct ceph_mds_client *mdsc, vino.ino = le64_to_cpu(h->ino); vino.snap = CEPH_NOSNAP; mask = le16_to_cpu(h->mask); + seq = le32_to_cpu(h->seq); dname.name = (void *)h + sizeof(*h) + sizeof(u32); dname.len = msg->front.iov_len - sizeof(*h) - sizeof(u32); if (dname.len != get_unaligned_le32(h+1)) @@ -2456,8 +2458,9 @@ static void handle_lease(struct ceph_mds_client *mdsc, /* lookup inode */ inode = ceph_find_inode(sb, vino); - dout("handle_lease '%s', mask %d, ino %llx %p\n", - ceph_lease_op_name(h->action), mask, vino.ino, inode); + dout("handle_lease %s, mask %d, ino %llx %p %.*s\n", + ceph_lease_op_name(h->action), mask, vino.ino, inode, + dname.len, dname.name); if (inode == NULL) { dout("handle_lease no inode %llx\n", vino.ino); goto release; @@ -2482,7 +2485,8 @@ static void handle_lease(struct ceph_mds_client *mdsc, switch (h->action) { case CEPH_MDS_LEASE_REVOKE: if (di && di->lease_session == session) { - h->seq = cpu_to_le32(di->lease_seq); + if (ceph_seq_cmp(di->lease_seq, seq) > 0) + h->seq = cpu_to_le32(di->lease_seq); __ceph_mdsc_drop_dentry_lease(dentry); } release = 1; @@ -2496,7 +2500,7 @@ static void handle_lease(struct ceph_mds_client *mdsc, unsigned long duration = le32_to_cpu(h->duration_ms) * HZ / 1000; - di->lease_seq = le32_to_cpu(h->seq); + di->lease_seq = seq; dentry->d_time = di->lease_renew_from + duration; di->lease_renew_after = di->lease_renew_from + (duration >> 1); -- cgit v1.2.3 From 75de46b98dda624397ccb17c106e51f478a79c15 Mon Sep 17 00:00:00 2001 From: Nick Piggin Date: Mon, 31 May 2010 17:58:02 +1000 Subject: fix setattr error handling in sysfs, configfs sysfs and configfs setattr functions have error cases after the generic inode's attributes have been changed. Fix consistency by changing the generic inode attributes only when it is guaranteed to succeed. Signed-off-by: Nick Piggin Acked-by: Joel Becker Signed-off-by: Greg Kroah-Hartman --- fs/configfs/inode.c | 9 ++++----- fs/sysfs/inode.c | 6 ++++-- 2 files changed, 8 insertions(+), 7 deletions(-) (limited to 'fs') diff --git a/fs/configfs/inode.c b/fs/configfs/inode.c index 41645142b88b..cf78d44a8d6a 100644 --- a/fs/configfs/inode.c +++ b/fs/configfs/inode.c @@ -72,10 +72,6 @@ int configfs_setattr(struct dentry * dentry, struct iattr * iattr) if (!sd) return -EINVAL; - error = simple_setattr(dentry, iattr); - if (error) - return error; - sd_iattr = sd->s_iattr; if (!sd_iattr) { /* setting attributes for the first time, allocate now */ @@ -89,9 +85,12 @@ int configfs_setattr(struct dentry * dentry, struct iattr * iattr) sd_iattr->ia_atime = sd_iattr->ia_mtime = sd_iattr->ia_ctime = CURRENT_TIME; sd->s_iattr = sd_iattr; } - /* attributes were changed atleast once in past */ + error = simple_setattr(dentry, iattr); + if (error) + return error; + if (ia_valid & ATTR_UID) sd_iattr->ia_uid = iattr->ia_uid; if (ia_valid & ATTR_GID) diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index bde1a4c3679a..0835a3b70e03 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -117,11 +117,13 @@ int sysfs_setattr(struct dentry *dentry, struct iattr *iattr) if (error) goto out; + error = sysfs_sd_setattr(sd, iattr); + if (error) + goto out; + /* this ignores size changes */ generic_setattr(inode, iattr); - error = sysfs_sd_setattr(sd, iattr); - out: mutex_unlock(&sysfs_mutex); return error; -- cgit v1.2.3 From 7d683a09990ff095a91b6e724ecee0ff8733274a Mon Sep 17 00:00:00 2001 From: Roberto Sassu Date: Thu, 3 Jun 2010 11:58:28 +0200 Subject: wrong type for 'magic' argument in simple_fill_super() It's used to superblock ->s_magic, which is unsigned long. Signed-off-by: Roberto Sassu Reviewed-by: Mimi Zohar Signed-off-by: Eric Paris CC: stable@kernel.org Signed-off-by: Al Viro --- fs/libfs.c | 3 ++- include/linux/fs.h | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/libfs.c b/fs/libfs.c index 09e1016eb774..dcaf972cbf1b 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -489,7 +489,8 @@ int simple_write_end(struct file *file, struct address_space *mapping, * unique inode values later for this filesystem, then you must take care * to pass it an appropriate max_reserved value to avoid collisions. */ -int simple_fill_super(struct super_block *s, int magic, struct tree_descr *files) +int simple_fill_super(struct super_block *s, unsigned long magic, + struct tree_descr *files) { struct inode *inode; struct dentry *root; diff --git a/include/linux/fs.h b/include/linux/fs.h index 3428393942a6..471e1ff5079a 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2388,7 +2388,7 @@ extern const struct file_operations simple_dir_operations; extern const struct inode_operations simple_dir_inode_operations; struct tree_descr { char *name; const struct file_operations *ops; int mode; }; struct dentry *d_alloc_name(struct dentry *, const char *); -extern int simple_fill_super(struct super_block *, int, struct tree_descr *); +extern int simple_fill_super(struct super_block *, unsigned long, struct tree_descr *); extern int simple_pin_fs(struct file_system_type *, struct vfsmount **mount, int *count); extern void simple_release_fs(struct vfsmount **mount, int *count); -- cgit v1.2.3 From 5b54470daded19d83ea2bbf5f6bc12662942cd63 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 3 Jun 2010 12:35:42 +0200 Subject: fcntl: return -EFAULT if copy_to_user fails copy_to_user() returns the number of bytes remaining, but we want to return -EFAULT. ret = fcntl(fd, F_SETOWN_EX, NULL); With the original code ret would be 8 here. V2: Takuya Yoshikawa pointed out a similar issue in f_getown_ex() Signed-off-by: Dan Carpenter Signed-off-by: Al Viro --- fs/fcntl.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/fcntl.c b/fs/fcntl.c index f74d270ba155..51e11bf5708f 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c @@ -274,7 +274,7 @@ static int f_setown_ex(struct file *filp, unsigned long arg) ret = copy_from_user(&owner, owner_p, sizeof(owner)); if (ret) - return ret; + return -EFAULT; switch (owner.type) { case F_OWNER_TID: @@ -332,8 +332,11 @@ static int f_getown_ex(struct file *filp, unsigned long arg) } read_unlock(&filp->f_owner.lock); - if (!ret) + if (!ret) { ret = copy_to_user(owner_p, &owner, sizeof(owner)); + if (ret) + ret = -EFAULT; + } return ret; } -- cgit v1.2.3 From 8718d36cf99f5acf0f37487557ec25aee54b930b Mon Sep 17 00:00:00 2001 From: Nick Piggin Date: Mon, 31 May 2010 17:58:02 +1000 Subject: fix setattr error handling in sysfs, configfs sysfs and configfs setattr functions have error cases after the generic inode's attributes have been changed. Fix consistency by changing the generic inode attributes only when it is guaranteed to succeed. Signed-off-by: Nick Piggin Signed-off-by: Al Viro --- fs/configfs/inode.c | 9 ++++----- fs/sysfs/inode.c | 6 ++++-- 2 files changed, 8 insertions(+), 7 deletions(-) (limited to 'fs') diff --git a/fs/configfs/inode.c b/fs/configfs/inode.c index 41645142b88b..cf78d44a8d6a 100644 --- a/fs/configfs/inode.c +++ b/fs/configfs/inode.c @@ -72,10 +72,6 @@ int configfs_setattr(struct dentry * dentry, struct iattr * iattr) if (!sd) return -EINVAL; - error = simple_setattr(dentry, iattr); - if (error) - return error; - sd_iattr = sd->s_iattr; if (!sd_iattr) { /* setting attributes for the first time, allocate now */ @@ -89,9 +85,12 @@ int configfs_setattr(struct dentry * dentry, struct iattr * iattr) sd_iattr->ia_atime = sd_iattr->ia_mtime = sd_iattr->ia_ctime = CURRENT_TIME; sd->s_iattr = sd_iattr; } - /* attributes were changed atleast once in past */ + error = simple_setattr(dentry, iattr); + if (error) + return error; + if (ia_valid & ATTR_UID) sd_iattr->ia_uid = iattr->ia_uid; if (ia_valid & ATTR_GID) diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index bde1a4c3679a..0835a3b70e03 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -117,11 +117,13 @@ int sysfs_setattr(struct dentry *dentry, struct iattr *iattr) if (error) goto out; + error = sysfs_sd_setattr(sd, iattr); + if (error) + goto out; + /* this ignores size changes */ generic_setattr(inode, iattr); - error = sysfs_sd_setattr(sd, iattr); - out: mutex_unlock(&sysfs_mutex); return error; -- cgit v1.2.3 From af5a30d8cfcfc561336f982b06345d6b815e0bb3 Mon Sep 17 00:00:00 2001 From: Nick Piggin Date: Thu, 3 Jun 2010 22:01:46 +1000 Subject: fix truncate inode time modification breakage mtime and ctime should be changed only if the file size has actually changed. Patches changing ext2 and tmpfs from vmtruncate to new truncate sequence has caused regressions where they always update timestamps. There is some strange cases in POSIX where truncate(2) must not update times unless the size has acutally changed, see 6e656be89. This area is all still rather buggy in different ways in a lot of filesystems and needs a cleanup and audit (ideally the vfs will provide a simple attribute or call to direct all filesystems exactly which attributes to change). But coming up with the best solution will take a while and is not appropriate for rc anyway. So fix recent regression for now. Signed-off-by: Nick Piggin Signed-off-by: Al Viro --- fs/ext2/inode.c | 2 +- mm/shmem.c | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index 19214435b752..3675088cb88c 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -1552,7 +1552,7 @@ int ext2_setattr(struct dentry *dentry, struct iattr *iattr) if (error) return error; } - if (iattr->ia_valid & ATTR_SIZE) { + if (iattr->ia_valid & ATTR_SIZE && iattr->ia_size != inode->i_size) { error = ext2_setsize(inode, iattr->ia_size); if (error) return error; diff --git a/mm/shmem.c b/mm/shmem.c index 7e5030ae18ff..f65f84062db5 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -764,10 +764,11 @@ done2: static int shmem_notify_change(struct dentry *dentry, struct iattr *attr) { struct inode *inode = dentry->d_inode; + loff_t newsize = attr->ia_size; int error; - if (S_ISREG(inode->i_mode) && (attr->ia_valid & ATTR_SIZE)) { - loff_t newsize = attr->ia_size; + if (S_ISREG(inode->i_mode) && (attr->ia_valid & ATTR_SIZE) + && newsize != inode->i_size) { struct page *page = NULL; if (newsize < inode->i_size) { -- cgit v1.2.3 From 01afaf61983d08ed1c9e5e8f2fcf4f40e9008033 Mon Sep 17 00:00:00 2001 From: Andrew Hendry Date: Fri, 4 Jun 2010 22:51:24 +1000 Subject: Minix: Clean up left over label Remove a left over fail label. Signed-off-by: Andrew Hendry Signed-off-by: Al Viro --- fs/minix/dir.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'fs') diff --git a/fs/minix/dir.c b/fs/minix/dir.c index 91969589131c..1dbf921ca44b 100644 --- a/fs/minix/dir.c +++ b/fs/minix/dir.c @@ -75,10 +75,6 @@ static struct page * dir_get_page(struct inode *dir, unsigned long n) if (!IS_ERR(page)) kmap(page); return page; - -fail: - dir_put_page(page); - return ERR_PTR(-EIO); } static inline void *minix_next_entry(void *de, struct minix_sb_info *sbi) -- cgit v1.2.3 From 7cbe17701a0379c7b05a79a6df4f24e41d2afde8 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Fri, 4 Jun 2010 14:14:47 -0700 Subject: fs/compat_rw_copy_check_uvector: add missing compat_ptr call A call to access_ok is missing a compat_ptr conversion. Introduced with b83733639a494d5f42fa00a2506563fbd2d3015d "compat: factor out compat_rw_copy_check_uvector from compat_do_readv_writev" fs/compat.c: In function 'compat_rw_copy_check_uvector': fs/compat.c:629: warning: passing argument 1 of '__access_ok' makes pointer from integer without a cast Signed-off-by: Heiko Carstens Reviewed-by: Jeff Moyer Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/compat.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/compat.c b/fs/compat.c index f0b391c50552..6490d2134ff3 100644 --- a/fs/compat.c +++ b/fs/compat.c @@ -626,7 +626,7 @@ ssize_t compat_rw_copy_check_uvector(int type, tot_len += len; if (tot_len < tmp) /* maths overflow on the compat_ssize_t */ goto out; - if (!access_ok(vrfy_dir(type), buf, len)) { + if (!access_ok(vrfy_dir(type), compat_ptr(buf), len)) { ret = -EFAULT; goto out; } -- cgit v1.2.3 From 2e94de8acbe524d919f1ea8807913d7b005e1578 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Fri, 4 Jun 2010 14:14:53 -0700 Subject: fs/binfmt_flat.c: split the stack & data alignments The stack and data have different alignment requirements, so don't force them to wear the same shoe. Increase the data alignment to match that which the elf2flt linker script has always been using: 0x20 bytes. Not only does this bring the kernel loader in line with the toolchain, but it also fixes a swath of gcc tests which try to force larger alignment values but randomly fail when the FLAT loader fails to deliver. Signed-off-by: Mike Frysinger Cc: Herbert Xu Cc: David Woodhouse Cc: Pekka Enberg Acked-by: David McCullough Acked-by: Greg Ungerer Cc: Paul Mundt Tested-by: Michal Simek Cc: Hirokazu Takata Cc: Yoshinori Sato Cc: Geert Uytterhoeven Cc: Jie Zhang Cc: David Howells Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/binfmt_flat.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) (limited to 'fs') diff --git a/fs/binfmt_flat.c b/fs/binfmt_flat.c index 49566c1687d8..b8656225b34b 100644 --- a/fs/binfmt_flat.c +++ b/fs/binfmt_flat.c @@ -56,15 +56,22 @@ #endif /* - * User data (stack, data section and bss) needs to be aligned - * for the same reasons as SLAB memory is, and to the same amount. - * Avoid duplicating architecture specific code by using the same - * macro as with SLAB allocation: + * User data (data section and bss) needs to be aligned. + * We pick 0x20 here because it is the max value elf2flt has always + * used in producing FLAT files, and because it seems to be large + * enough to make all the gcc alignment related tests happy. + */ +#define FLAT_DATA_ALIGN (0x20) + +/* + * User data (stack) also needs to be aligned. + * Here we can be a bit looser than the data sections since this + * needs to only meet arch ABI requirements. */ #ifdef ARCH_SLAB_MINALIGN -#define FLAT_DATA_ALIGN (ARCH_SLAB_MINALIGN) +#define FLAT_STACK_ALIGN (ARCH_SLAB_MINALIGN) #else -#define FLAT_DATA_ALIGN (sizeof(void *)) +#define FLAT_STACK_ALIGN (sizeof(void *)) #endif #define RELOC_FAILED 0xff00ff01 /* Relocation incorrect somewhere */ @@ -129,7 +136,7 @@ static unsigned long create_flat_tables( sp = (unsigned long *)p; sp -= (envc + argc + 2) + 1 + (flat_argvp_envp_on_stack() ? 2 : 0); - sp = (unsigned long *) ((unsigned long)sp & -FLAT_DATA_ALIGN); + sp = (unsigned long *) ((unsigned long)sp & -FLAT_STACK_ALIGN); argv = sp + 1 + (flat_argvp_envp_on_stack() ? 2 : 0); envp = argv + (argc + 1); @@ -876,7 +883,7 @@ static int load_flat_binary(struct linux_binprm * bprm, struct pt_regs * regs) stack_len = TOP_OF_ARGS - bprm->p; /* the strings */ stack_len += (bprm->argc + 1) * sizeof(char *); /* the argv array */ stack_len += (bprm->envc + 1) * sizeof(char *); /* the envp array */ - stack_len += FLAT_DATA_ALIGN - 1; /* reserve for upcoming alignment */ + stack_len += FLAT_STACK_ALIGN - 1; /* reserve for upcoming alignment */ res = load_flat_file(bprm, &libinfo, 0, &stack_len); if (IS_ERR_VALUE(res)) -- cgit v1.2.3 From 1da083c9b23dafd6bcb08dcfec443e66e90efff0 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Fri, 4 Jun 2010 14:14:55 -0700 Subject: flat: fix unmap len in load error path The data chunk is mmaped with 'len' which remains unchanged, so use that when unmapping in the error path rather than trying to recalculate (and incorrectly so) the value used originally. Signed-off-by: Mike Frysinger Acked-by: David McCullough Acked-by: Greg Ungerer Cc: Paul Mundt Cc: Michal Simek Cc: Hirokazu Takata Cc: Geert Uytterhoeven Acked-by: David Howells Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/binfmt_flat.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/binfmt_flat.c b/fs/binfmt_flat.c index b8656225b34b..b6ab27ccf214 100644 --- a/fs/binfmt_flat.c +++ b/fs/binfmt_flat.c @@ -596,7 +596,7 @@ static int load_flat_file(struct linux_binprm * bprm, if (IS_ERR_VALUE(result)) { printk("Unable to read data+bss, errno %d\n", (int)-result); do_munmap(current->mm, textpos, text_len); - do_munmap(current->mm, realdatastart, data_len + extra); + do_munmap(current->mm, realdatastart, len); ret = result; goto err; } -- cgit v1.2.3 From 84a8dce2710cc425089a2b92acc354d4fbb5788d Mon Sep 17 00:00:00 2001 From: Dmitry Monakhov Date: Sat, 5 Jun 2010 11:51:27 -0400 Subject: ext4: Fix remaining racy updates of EXT4_I(inode)->i_flags A few functions were still modifying i_flags in a racy manner. Signed-off-by: Dmitry Monakhov Signed-off-by: "Theodore Ts'o" --- fs/ext4/inode.c | 40 +++++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 17 deletions(-) (limited to 'fs') diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 19df61c321fd..42272d67955a 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -4942,20 +4942,26 @@ void ext4_set_inode_flags(struct inode *inode) /* Propagate flags from i_flags to EXT4_I(inode)->i_flags */ void ext4_get_inode_flags(struct ext4_inode_info *ei) { - unsigned int flags = ei->vfs_inode.i_flags; - - ei->i_flags &= ~(EXT4_SYNC_FL|EXT4_APPEND_FL| - EXT4_IMMUTABLE_FL|EXT4_NOATIME_FL|EXT4_DIRSYNC_FL); - if (flags & S_SYNC) - ei->i_flags |= EXT4_SYNC_FL; - if (flags & S_APPEND) - ei->i_flags |= EXT4_APPEND_FL; - if (flags & S_IMMUTABLE) - ei->i_flags |= EXT4_IMMUTABLE_FL; - if (flags & S_NOATIME) - ei->i_flags |= EXT4_NOATIME_FL; - if (flags & S_DIRSYNC) - ei->i_flags |= EXT4_DIRSYNC_FL; + unsigned int vfs_fl; + unsigned long old_fl, new_fl; + + do { + vfs_fl = ei->vfs_inode.i_flags; + old_fl = ei->i_flags; + new_fl = old_fl & ~(EXT4_SYNC_FL|EXT4_APPEND_FL| + EXT4_IMMUTABLE_FL|EXT4_NOATIME_FL| + EXT4_DIRSYNC_FL); + if (vfs_fl & S_SYNC) + new_fl |= EXT4_SYNC_FL; + if (vfs_fl & S_APPEND) + new_fl |= EXT4_APPEND_FL; + if (vfs_fl & S_IMMUTABLE) + new_fl |= EXT4_IMMUTABLE_FL; + if (vfs_fl & S_NOATIME) + new_fl |= EXT4_NOATIME_FL; + if (vfs_fl & S_DIRSYNC) + new_fl |= EXT4_DIRSYNC_FL; + } while (cmpxchg(&ei->i_flags, old_fl, new_fl) != old_fl); } static blkcnt_t ext4_inode_blocks(struct ext4_inode *raw_inode, @@ -5191,7 +5197,7 @@ static int ext4_inode_blocks_set(handle_t *handle, */ raw_inode->i_blocks_lo = cpu_to_le32(i_blocks); raw_inode->i_blocks_high = 0; - ei->i_flags &= ~EXT4_HUGE_FILE_FL; + ext4_clear_inode_flag(inode, EXT4_INODE_HUGE_FILE); return 0; } if (!EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_HUGE_FILE)) @@ -5204,9 +5210,9 @@ static int ext4_inode_blocks_set(handle_t *handle, */ raw_inode->i_blocks_lo = cpu_to_le32(i_blocks); raw_inode->i_blocks_high = cpu_to_le16(i_blocks >> 32); - ei->i_flags &= ~EXT4_HUGE_FILE_FL; + ext4_clear_inode_flag(inode, EXT4_INODE_HUGE_FILE); } else { - ei->i_flags |= EXT4_HUGE_FILE_FL; + ext4_set_inode_flag(inode, EXT4_INODE_HUGE_FILE); /* i_block is stored in file system block size */ i_blocks = i_blocks >> (inode->i_blkbits - 9); raw_inode->i_blocks_lo = cpu_to_le32(i_blocks); -- cgit v1.2.3 From 1c24d06f8e065023ebb428db5af5514500839ee6 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Fri, 4 Jun 2010 17:07:55 +0200 Subject: jffs2: update ctime when changing the file's permission by setfacl jffs2 didn't update the ctime of the file when its permission was changed. Steps to reproduce: # touch aaa # stat -c %Z aaa 1275289822 # setfacl -m 'u::x,g::x,o::x' aaa # stat -c %Z aaa 1275289822 <- unchanged But, according to the spec of the ctime, jffs2 must update it. Port of ext3 patch by Miao Xie . Signed-off-by: Jan Kara Signed-off-by: David Woodhouse --- fs/jffs2/acl.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/jffs2/acl.c b/fs/jffs2/acl.c index 7cdc3196476a..64d3b4b48dbf 100644 --- a/fs/jffs2/acl.c +++ b/fs/jffs2/acl.c @@ -234,8 +234,9 @@ static int jffs2_set_acl(struct inode *inode, int type, struct posix_acl *acl) if (inode->i_mode != mode) { struct iattr attr; - attr.ia_valid = ATTR_MODE; + attr.ia_valid = ATTR_MODE | ATTR_CTIME; attr.ia_mode = mode; + attr.ia_ctime = CURRENT_TIME_SEC; rc = jffs2_do_setattr(inode, &attr); if (rc < 0) return rc; -- cgit v1.2.3 From c3935e30495869dd611e1cd62253c94ebc7c6c04 Mon Sep 17 00:00:00 2001 From: "J. Bruce Fields" Date: Fri, 4 Jun 2010 16:42:08 -0400 Subject: nfsd4: shut down callback queue outside state lock This reportedly causes a lockdep warning on nfsd shutdown. That looks like a false positive to me, but there's no reason why this needs the state lock anyway. Reported-by: Jeff Layton Signed-off-by: J. Bruce Fields --- fs/nfsd/nfs4state.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 12f7109720c2..4a2734758778 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -4122,8 +4122,8 @@ nfs4_state_shutdown(void) nfs4_lock_state(); nfs4_release_reclaim(); __nfs4_state_shutdown(); - nfsd4_destroy_callback_queue(); nfs4_unlock_state(); + nfsd4_destroy_callback_queue(); } /* -- cgit v1.2.3 From 254c8c2dbf0e06a560a5814eb90cb628adb2de66 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Wed, 9 Jun 2010 10:37:19 +1000 Subject: xfs: remove nr_to_write writeback windup. Now that the background flush code has been fixed, we shouldn't need to silently multiply the wbc->nr_to_write to get good writeback. Remove that code. Signed-off-by: Dave Chinner Reviewed-by: Christoph Hellwig Signed-off-by: Linus Torvalds --- fs/xfs/linux-2.6/xfs_aops.c | 8 -------- 1 file changed, 8 deletions(-) (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_aops.c b/fs/xfs/linux-2.6/xfs_aops.c index a0fa3bf0d1bb..34640d6dbdcb 100644 --- a/fs/xfs/linux-2.6/xfs_aops.c +++ b/fs/xfs/linux-2.6/xfs_aops.c @@ -1381,14 +1381,6 @@ xfs_vm_writepage( if (!page_has_buffers(page)) create_empty_buffers(page, 1 << inode->i_blkbits, 0); - - /* - * VM calculation for nr_to_write seems off. Bump it way - * up, this gets simple streaming writes zippy again. - * To be reviewed again after Jens' writeback changes. - */ - wbc->nr_to_write *= 4; - /* * Convert delayed allocate, unwritten or unmapped space * to real space and flush out to disk. -- cgit v1.2.3 From 3af9e859281bda7eb7c20b51879cf43aa788ac2e Mon Sep 17 00:00:00 2001 From: Eric B Munson Date: Tue, 18 May 2010 15:30:49 +0100 Subject: perf: Add non-exec mmap() tracking Add the capacility to track data mmap()s. This can be used together with PERF_SAMPLE_ADDR for data profiling. Signed-off-by: Anton Blanchard [Updated code for stable perf ABI] Signed-off-by: Eric B Munson Signed-off-by: Peter Zijlstra Cc: Arnaldo Carvalho de Melo Cc: Frederic Weisbecker Cc: Paul Mackerras Cc: Mike Galbraith Cc: Steven Rostedt LKML-Reference: <1274193049-25997-1-git-send-email-ebmunson@us.ibm.com> Signed-off-by: Ingo Molnar --- fs/exec.c | 1 + include/linux/perf_event.h | 12 +++--------- kernel/perf_event.c | 34 +++++++++++++++++++++++----------- mm/mmap.c | 6 +++++- tools/perf/builtin-record.c | 4 +++- 5 files changed, 35 insertions(+), 22 deletions(-) (limited to 'fs') diff --git a/fs/exec.c b/fs/exec.c index e19de6a80339..97d91a03fb13 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -653,6 +653,7 @@ int setup_arg_pages(struct linux_binprm *bprm, else stack_base = vma->vm_start - stack_expand; #endif + current->mm->start_stack = bprm->p; ret = expand_stack(vma, stack_base); if (ret) ret = -EFAULT; diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index c691a0b27bcd..36efad90cd43 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -214,8 +214,9 @@ struct perf_event_attr { * See also PERF_RECORD_MISC_EXACT_IP */ precise_ip : 2, /* skid constraint */ + mmap_data : 1, /* non-exec mmap data */ - __reserved_1 : 47; + __reserved_1 : 46; union { __u32 wakeup_events; /* wakeup every n events */ @@ -962,14 +963,7 @@ perf_sw_event(u32 event_id, u64 nr, int nmi, struct pt_regs *regs, u64 addr) } } -extern void __perf_event_mmap(struct vm_area_struct *vma); - -static inline void perf_event_mmap(struct vm_area_struct *vma) -{ - if (vma->vm_flags & VM_EXEC) - __perf_event_mmap(vma); -} - +extern void perf_event_mmap(struct vm_area_struct *vma); extern struct perf_guest_info_callbacks *perf_guest_cbs; extern int perf_register_guest_info_callbacks(struct perf_guest_info_callbacks *callbacks); extern int perf_unregister_guest_info_callbacks(struct perf_guest_info_callbacks *callbacks); diff --git a/kernel/perf_event.c b/kernel/perf_event.c index b39bec346e80..227ed9c8ec34 100644 --- a/kernel/perf_event.c +++ b/kernel/perf_event.c @@ -1891,7 +1891,7 @@ static void free_event(struct perf_event *event) if (!event->parent) { atomic_dec(&nr_events); - if (event->attr.mmap) + if (event->attr.mmap || event->attr.mmap_data) atomic_dec(&nr_mmap_events); if (event->attr.comm) atomic_dec(&nr_comm_events); @@ -3491,7 +3491,7 @@ perf_event_read_event(struct perf_event *event, /* * task tracking -- fork/exit * - * enabled by: attr.comm | attr.mmap | attr.task + * enabled by: attr.comm | attr.mmap | attr.mmap_data | attr.task */ struct perf_task_event { @@ -3541,7 +3541,8 @@ static int perf_event_task_match(struct perf_event *event) if (event->cpu != -1 && event->cpu != smp_processor_id()) return 0; - if (event->attr.comm || event->attr.mmap || event->attr.task) + if (event->attr.comm || event->attr.mmap || + event->attr.mmap_data || event->attr.task) return 1; return 0; @@ -3766,7 +3767,8 @@ static void perf_event_mmap_output(struct perf_event *event, } static int perf_event_mmap_match(struct perf_event *event, - struct perf_mmap_event *mmap_event) + struct perf_mmap_event *mmap_event, + int executable) { if (event->state < PERF_EVENT_STATE_INACTIVE) return 0; @@ -3774,19 +3776,21 @@ static int perf_event_mmap_match(struct perf_event *event, if (event->cpu != -1 && event->cpu != smp_processor_id()) return 0; - if (event->attr.mmap) + if ((!executable && event->attr.mmap_data) || + (executable && event->attr.mmap)) return 1; return 0; } static void perf_event_mmap_ctx(struct perf_event_context *ctx, - struct perf_mmap_event *mmap_event) + struct perf_mmap_event *mmap_event, + int executable) { struct perf_event *event; list_for_each_entry_rcu(event, &ctx->event_list, event_entry) { - if (perf_event_mmap_match(event, mmap_event)) + if (perf_event_mmap_match(event, mmap_event, executable)) perf_event_mmap_output(event, mmap_event); } } @@ -3830,6 +3834,14 @@ static void perf_event_mmap_event(struct perf_mmap_event *mmap_event) if (!vma->vm_mm) { name = strncpy(tmp, "[vdso]", sizeof(tmp)); goto got_name; + } else if (vma->vm_start <= vma->vm_mm->start_brk && + vma->vm_end >= vma->vm_mm->brk) { + name = strncpy(tmp, "[heap]", sizeof(tmp)); + goto got_name; + } else if (vma->vm_start <= vma->vm_mm->start_stack && + vma->vm_end >= vma->vm_mm->start_stack) { + name = strncpy(tmp, "[stack]", sizeof(tmp)); + goto got_name; } name = strncpy(tmp, "//anon", sizeof(tmp)); @@ -3846,17 +3858,17 @@ got_name: rcu_read_lock(); cpuctx = &get_cpu_var(perf_cpu_context); - perf_event_mmap_ctx(&cpuctx->ctx, mmap_event); + perf_event_mmap_ctx(&cpuctx->ctx, mmap_event, vma->vm_flags & VM_EXEC); ctx = rcu_dereference(current->perf_event_ctxp); if (ctx) - perf_event_mmap_ctx(ctx, mmap_event); + perf_event_mmap_ctx(ctx, mmap_event, vma->vm_flags & VM_EXEC); put_cpu_var(perf_cpu_context); rcu_read_unlock(); kfree(buf); } -void __perf_event_mmap(struct vm_area_struct *vma) +void perf_event_mmap(struct vm_area_struct *vma) { struct perf_mmap_event mmap_event; @@ -4911,7 +4923,7 @@ done: if (!event->parent) { atomic_inc(&nr_events); - if (event->attr.mmap) + if (event->attr.mmap || event->attr.mmap_data) atomic_inc(&nr_mmap_events); if (event->attr.comm) atomic_inc(&nr_comm_events); diff --git a/mm/mmap.c b/mm/mmap.c index 456ec6f27889..e38e910cb756 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -1734,8 +1734,10 @@ int expand_upwards(struct vm_area_struct *vma, unsigned long address) grow = (address - vma->vm_end) >> PAGE_SHIFT; error = acct_stack_growth(vma, size, grow); - if (!error) + if (!error) { vma->vm_end = address; + perf_event_mmap(vma); + } } anon_vma_unlock(vma); return error; @@ -1781,6 +1783,7 @@ static int expand_downwards(struct vm_area_struct *vma, if (!error) { vma->vm_start = address; vma->vm_pgoff -= grow; + perf_event_mmap(vma); } } anon_vma_unlock(vma); @@ -2208,6 +2211,7 @@ unsigned long do_brk(unsigned long addr, unsigned long len) vma->vm_page_prot = vm_get_page_prot(flags); vma_link(mm, vma, prev, rb_link, rb_parent); out: + perf_event_mmap(vma); mm->total_vm += len >> PAGE_SHIFT; if (flags & VM_LOCKED) { if (!mlock_vma_pages_range(vma, addr, addr + len)) diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 5e5c6403a315..39c7247bc54a 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -268,8 +268,10 @@ static void create_counter(int counter, int cpu) if (inherit_stat) attr->inherit_stat = 1; - if (sample_address) + if (sample_address) { attr->sample_type |= PERF_SAMPLE_ADDR; + attr->mmap_data = track; + } if (call_graph) attr->sample_type |= PERF_SAMPLE_CALLCHAIN; -- cgit v1.2.3 From cf3425707ed9ce0d5ebaba20bc3d22dd39e52f2f Mon Sep 17 00:00:00 2001 From: Nick Piggin Date: Wed, 26 May 2010 01:50:21 +1000 Subject: block: bd_start_claiming fix module refcount bd_start_claiming has an unbalanced module_put introduced in 6b4517a79. Signed-off-by: Nick Piggin Acked-by: Tejun Heo Signed-off-by: Jens Axboe --- fs/block_dev.c | 1 + 1 file changed, 1 insertion(+) (limited to 'fs') diff --git a/fs/block_dev.c b/fs/block_dev.c index 7346c96308a5..204a7632c511 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -734,6 +734,7 @@ static struct block_device *bd_start_claiming(struct block_device *bdev, return ERR_PTR(-ENXIO); whole = bdget_disk(disk, 0); + module_put(disk->fops->owner); put_disk(disk); if (!whole) return ERR_PTR(-ENOMEM); -- cgit v1.2.3 From b0018361c3f934858592cbbb5e1a4f318c2a70ed Mon Sep 17 00:00:00 2001 From: Nick Piggin Date: Wed, 26 May 2010 01:51:19 +1000 Subject: block: bd_start_claiming cleanup I don't like the subtle multi-context code in bd_claim (ie. detects where it has been called based on bd_claiming). It seems clearer to just require a new function to finish a 2-part claim. Also improve commentary in bd_start_claiming as to how it should be used. Signed-off-by: Nick Piggin Acked-by: Tejun Heo Signed-off-by: Jens Axboe --- fs/block_dev.c | 72 ++++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 48 insertions(+), 24 deletions(-) (limited to 'fs') diff --git a/fs/block_dev.c b/fs/block_dev.c index 204a7632c511..a1642ef60197 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -706,8 +706,13 @@ retry: * @bdev is about to be opened exclusively. Check @bdev can be opened * exclusively and mark that an exclusive open is in progress. Each * successful call to this function must be matched with a call to - * either bd_claim() or bd_abort_claiming(). If this function - * succeeds, the matching bd_claim() is guaranteed to succeed. + * either bd_finish_claiming() or bd_abort_claiming() (which do not + * fail). + * + * This function is used to gain exclusive access to the block device + * without actually causing other exclusive open attempts to fail. It + * should be used when the open sequence itself requires exclusive + * access but may subsequently fail. * * CONTEXT: * Might sleep. @@ -783,15 +788,47 @@ static void bd_abort_claiming(struct block_device *whole, void *holder) __bd_abort_claiming(whole, holder); /* releases bdev_lock */ } +/* increment holders when we have a legitimate claim. requires bdev_lock */ +static void __bd_claim(struct block_device *bdev, struct block_device *whole, + void *holder) +{ + /* note that for a whole device bd_holders + * will be incremented twice, and bd_holder will + * be set to bd_claim before being set to holder + */ + whole->bd_holders++; + whole->bd_holder = bd_claim; + bdev->bd_holders++; + bdev->bd_holder = holder; +} + +/** + * bd_finish_claiming - finish claiming a block device + * @bdev: block device of interest (passed to bd_start_claiming()) + * @whole: whole block device returned by bd_start_claiming() + * @holder: holder trying to claim @bdev + * + * Finish a claiming block started by bd_start_claiming(). + * + * CONTEXT: + * Grabs and releases bdev_lock. + */ +static void bd_finish_claiming(struct block_device *bdev, + struct block_device *whole, void *holder) +{ + spin_lock(&bdev_lock); + BUG_ON(whole->bd_claiming != holder); + BUG_ON(!bd_may_claim(bdev, whole, holder)); + __bd_claim(bdev, whole, holder); + __bd_abort_claiming(whole, holder); /* not actually an abort */ +} + /** * bd_claim - claim a block device * @bdev: block device to claim * @holder: holder trying to claim @bdev * - * Try to claim @bdev which must have been opened successfully. This - * function may be called with or without preceding - * blk_start_claiming(). In the former case, this function is always - * successful and terminates the claiming block. + * Try to claim @bdev which must have been opened successfully. * * CONTEXT: * Might sleep. @@ -807,23 +844,10 @@ int bd_claim(struct block_device *bdev, void *holder) might_sleep(); spin_lock(&bdev_lock); - res = bd_prepare_to_claim(bdev, whole, holder); - if (res == 0) { - /* note that for a whole device bd_holders - * will be incremented twice, and bd_holder will - * be set to bd_claim before being set to holder - */ - whole->bd_holders++; - whole->bd_holder = bd_claim; - bdev->bd_holders++; - bdev->bd_holder = holder; - } - - if (whole->bd_claiming) - __bd_abort_claiming(whole, holder); /* releases bdev_lock */ - else - spin_unlock(&bdev_lock); + if (res == 0) + __bd_claim(bdev, whole, holder); + spin_unlock(&bdev_lock); return res; } @@ -1477,7 +1501,7 @@ static int blkdev_open(struct inode * inode, struct file * filp) if (whole) { if (res == 0) - BUG_ON(bd_claim(bdev, filp) != 0); + bd_finish_claiming(bdev, whole, filp); else bd_abort_claiming(whole, filp); } @@ -1713,7 +1737,7 @@ struct block_device *open_bdev_exclusive(const char *path, fmode_t mode, void *h if ((mode & FMODE_WRITE) && bdev_read_only(bdev)) goto out_blkdev_put; - BUG_ON(bd_claim(bdev, holder) != 0); + bd_finish_claiming(bdev, whole, holder); return bdev; out_blkdev_put: -- cgit v1.2.3 From 3e6c05052c262ebe7fdd85e75e9d4f956cdd8d82 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Mon, 7 Jun 2010 20:17:38 +0200 Subject: block: remove duplicate BUG_ON() in bd_finish_claiming() We do the same BUG_ON() just a line later when calling into __bd_abort_claiming(). Reported-by: Tejun Heo Signed-off-by: Jens Axboe --- fs/block_dev.c | 1 - 1 file changed, 1 deletion(-) (limited to 'fs') diff --git a/fs/block_dev.c b/fs/block_dev.c index a1642ef60197..99d6af811747 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -817,7 +817,6 @@ static void bd_finish_claiming(struct block_device *bdev, struct block_device *whole, void *holder) { spin_lock(&bdev_lock); - BUG_ON(whole->bd_claiming != holder); BUG_ON(!bd_may_claim(bdev, whole, holder)); __bd_claim(bdev, whole, holder); __bd_abort_claiming(whole, holder); /* not actually an abort */ -- cgit v1.2.3 From 1d862f41222b7f385bada9f85a67ca5592ffd33e Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Tue, 8 Jun 2010 16:28:45 +0200 Subject: pipe: fix pipe buffer resizing pipe_set_size() needs to copy pipe bufs from the old circular buffer to the new. The current code gets this wrong in multiple ways, resulting in oops. Test program is available here: http://www.kernel.org/pub/linux/kernel/people/mszeredi/piperesize/ Signed-off-by: Miklos Szeredi Signed-off-by: Jens Axboe --- fs/pipe.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/pipe.c b/fs/pipe.c index 69c4c7c13ea9..f31e2d472984 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -1145,13 +1145,20 @@ static long pipe_set_size(struct pipe_inode_info *pipe, unsigned long nr_pages) * and adjust the indexes. */ if (pipe->nrbufs) { - const unsigned int tail = pipe->nrbufs & (pipe->buffers - 1); - const unsigned int head = pipe->nrbufs - tail; + unsigned int tail; + unsigned int head; + tail = pipe->curbuf + pipe->nrbufs; + if (tail < pipe->buffers) + tail = 0; + else + tail &= (pipe->buffers - 1); + + head = pipe->nrbufs - tail; if (head) memcpy(bufs, pipe->bufs + pipe->curbuf, head * sizeof(struct pipe_buffer)); if (tail) - memcpy(bufs + head, pipe->bufs + pipe->curbuf, tail * sizeof(struct pipe_buffer)); + memcpy(bufs + head, pipe->bufs, tail * sizeof(struct pipe_buffer)); } pipe->curbuf = 0; -- cgit v1.2.3 From 6db40cf047a8723095caf79f5569d21b388d7b31 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Wed, 9 Jun 2010 09:27:57 +0200 Subject: pipe: fix check in "set size" fcntl As it stands this check compares the number of pages to the page size. This makes no sense and makes the fcntl fail in almost any sane case. Fix it by checking if nr_pages is not zero (it can become zero only if arg is too big and round_pipe_size() overflows). Signed-off-by: Miklos Szeredi Signed-off-by: Jens Axboe --- fs/pipe.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/pipe.c b/fs/pipe.c index f31e2d472984..279eef96c51c 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -1215,12 +1215,13 @@ long pipe_fcntl(struct file *file, unsigned int cmd, unsigned long arg) size = round_pipe_size(arg); nr_pages = size >> PAGE_SHIFT; + ret = -EINVAL; + if (!nr_pages) + goto out; + if (!capable(CAP_SYS_RESOURCE) && size > pipe_max_size) { ret = -EPERM; goto out; - } else if (nr_pages < PAGE_SIZE) { - ret = -EINVAL; - goto out; } ret = pipe_set_size(pipe, nr_pages); break; -- cgit v1.2.3 From 00d5643e7c5ed4ae1bb0b385fe2f41bb951cc3cd Mon Sep 17 00:00:00 2001 From: Jeff Mahoney Date: Thu, 10 Jun 2010 11:13:58 -0400 Subject: ceph: fix atomic64_t initialization on ia64 bdi_seq is an atomic_long_t but we're using ATOMIC_INIT, which causes build failures on ia64. This patch fixes it to use ATOMIC_LONG_INIT. Signed-off-by: Jeff Mahoney Signed-off-by: Sage Weil --- fs/ceph/super.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/ceph/super.c b/fs/ceph/super.c index 8db88792b5ad..fa87f51e38e1 100644 --- a/fs/ceph/super.c +++ b/fs/ceph/super.c @@ -926,7 +926,7 @@ static int ceph_compare_super(struct super_block *sb, void *data) /* * construct our own bdi so we can control readahead, etc. */ -static atomic_long_t bdi_seq = ATOMIC_INIT(0); +static atomic_long_t bdi_seq = ATOMIC_LONG_INIT(0); static int ceph_register_bdi(struct super_block *sb, struct ceph_client *client) { -- cgit v1.2.3 From 9dbd412f56c453f15014396c6024b895c1485ccb Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Thu, 10 Jun 2010 13:21:20 -0700 Subject: ceph: fix misleading/incorrect debug message Nothing is released here: the caps message is simply ignored in this case. Signed-off-by: Sage Weil --- fs/ceph/caps.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index ae3e3a306445..da2a0e3cb200 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -2714,7 +2714,7 @@ void ceph_handle_caps(struct ceph_mds_session *session, spin_lock(&inode->i_lock); cap = __get_cap_for_mds(ceph_inode(inode), mds); if (!cap) { - dout("no cap on %p ino %llx.%llx from mds%d, releasing\n", + dout(" no cap on %p ino %llx.%llx from mds%d\n", inode, ceph_ino(inode), ceph_snap(inode), mds); spin_unlock(&inode->i_lock); goto done; -- cgit v1.2.3 From 3d7ded4d81d807c2f75f310a8d74a5d72be13a1b Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Wed, 9 Jun 2010 16:47:10 -0700 Subject: ceph: release cap on import if we don't have the inode If we get an IMPORT that give us a cap, but we don't have the inode, queue a release (and try to send it immediately) so that the MDS doesn't get stuck waiting for us. Signed-off-by: Sage Weil --- fs/ceph/caps.c | 90 ++++++++++++++++++++++++++++++++-------------------- fs/ceph/mds_client.c | 6 ++-- fs/ceph/mds_client.h | 3 ++ 3 files changed, 61 insertions(+), 38 deletions(-) (limited to 'fs') diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index da2a0e3cb200..7c692e746237 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -981,6 +981,46 @@ static int send_cap_msg(struct ceph_mds_session *session, return 0; } +static void __queue_cap_release(struct ceph_mds_session *session, + u64 ino, u64 cap_id, u32 migrate_seq, + u32 issue_seq) +{ + struct ceph_msg *msg; + struct ceph_mds_cap_release *head; + struct ceph_mds_cap_item *item; + + spin_lock(&session->s_cap_lock); + BUG_ON(!session->s_num_cap_releases); + msg = list_first_entry(&session->s_cap_releases, + struct ceph_msg, list_head); + + dout(" adding %llx release to mds%d msg %p (%d left)\n", + ino, session->s_mds, msg, session->s_num_cap_releases); + + BUG_ON(msg->front.iov_len + sizeof(*item) > PAGE_CACHE_SIZE); + head = msg->front.iov_base; + head->num = cpu_to_le32(le32_to_cpu(head->num) + 1); + item = msg->front.iov_base + msg->front.iov_len; + item->ino = cpu_to_le64(ino); + item->cap_id = cpu_to_le64(cap_id); + item->migrate_seq = cpu_to_le32(migrate_seq); + item->seq = cpu_to_le32(issue_seq); + + session->s_num_cap_releases--; + + msg->front.iov_len += sizeof(*item); + if (le32_to_cpu(head->num) == CEPH_CAPS_PER_RELEASE) { + dout(" release msg %p full\n", msg); + list_move_tail(&msg->list_head, &session->s_cap_releases_done); + } else { + dout(" release msg %p at %d/%d (%d)\n", msg, + (int)le32_to_cpu(head->num), + (int)CEPH_CAPS_PER_RELEASE, + (int)msg->front.iov_len); + } + spin_unlock(&session->s_cap_lock); +} + /* * Queue cap releases when an inode is dropped from our cache. Since * inode is about to be destroyed, there is no need for i_lock. @@ -994,41 +1034,9 @@ void ceph_queue_caps_release(struct inode *inode) while (p) { struct ceph_cap *cap = rb_entry(p, struct ceph_cap, ci_node); struct ceph_mds_session *session = cap->session; - struct ceph_msg *msg; - struct ceph_mds_cap_release *head; - struct ceph_mds_cap_item *item; - spin_lock(&session->s_cap_lock); - BUG_ON(!session->s_num_cap_releases); - msg = list_first_entry(&session->s_cap_releases, - struct ceph_msg, list_head); - - dout(" adding %p release to mds%d msg %p (%d left)\n", - inode, session->s_mds, msg, session->s_num_cap_releases); - - BUG_ON(msg->front.iov_len + sizeof(*item) > PAGE_CACHE_SIZE); - head = msg->front.iov_base; - head->num = cpu_to_le32(le32_to_cpu(head->num) + 1); - item = msg->front.iov_base + msg->front.iov_len; - item->ino = cpu_to_le64(ceph_ino(inode)); - item->cap_id = cpu_to_le64(cap->cap_id); - item->migrate_seq = cpu_to_le32(cap->mseq); - item->seq = cpu_to_le32(cap->issue_seq); - - session->s_num_cap_releases--; - - msg->front.iov_len += sizeof(*item); - if (le32_to_cpu(head->num) == CEPH_CAPS_PER_RELEASE) { - dout(" release msg %p full\n", msg); - list_move_tail(&msg->list_head, - &session->s_cap_releases_done); - } else { - dout(" release msg %p at %d/%d (%d)\n", msg, - (int)le32_to_cpu(head->num), - (int)CEPH_CAPS_PER_RELEASE, - (int)msg->front.iov_len); - } - spin_unlock(&session->s_cap_lock); + __queue_cap_release(session, ceph_ino(inode), cap->cap_id, + cap->mseq, cap->issue_seq); p = rb_next(p); __ceph_remove_cap(cap); } @@ -2655,7 +2663,7 @@ void ceph_handle_caps(struct ceph_mds_session *session, struct ceph_mds_caps *h; int mds = session->s_mds; int op; - u32 seq; + u32 seq, mseq; struct ceph_vino vino; u64 cap_id; u64 size, max_size; @@ -2675,6 +2683,7 @@ void ceph_handle_caps(struct ceph_mds_session *session, vino.snap = CEPH_NOSNAP; cap_id = le64_to_cpu(h->cap_id); seq = le32_to_cpu(h->seq); + mseq = le32_to_cpu(h->migrate_seq); size = le64_to_cpu(h->size); max_size = le64_to_cpu(h->max_size); @@ -2689,6 +2698,17 @@ void ceph_handle_caps(struct ceph_mds_session *session, vino.snap, inode); if (!inode) { dout(" i don't have ino %llx\n", vino.ino); + + if (op == CEPH_CAP_OP_IMPORT) + __queue_cap_release(session, vino.ino, cap_id, + mseq, seq); + + /* + * send any full release message to try to move things + * along for the mds (who clearly thinks we still have this + * cap). + */ + ceph_send_cap_releases(mdsc, session); goto done; } diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 29b4485cf1ca..d28b6a9c0f96 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -1176,8 +1176,8 @@ static int check_cap_flush(struct ceph_mds_client *mdsc, u64 want_flush_seq) /* * called under s_mutex */ -static void send_cap_releases(struct ceph_mds_client *mdsc, - struct ceph_mds_session *session) +void ceph_send_cap_releases(struct ceph_mds_client *mdsc, + struct ceph_mds_session *session) { struct ceph_msg *msg; @@ -2693,7 +2693,7 @@ static void delayed_work(struct work_struct *work) add_cap_releases(mdsc, s, -1); if (s->s_state == CEPH_MDS_SESSION_OPEN || s->s_state == CEPH_MDS_SESSION_HUNG) - send_cap_releases(mdsc, s); + ceph_send_cap_releases(mdsc, s); mutex_unlock(&s->s_mutex); ceph_put_mds_session(s); diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h index d9936c4f1212..e43752b52635 100644 --- a/fs/ceph/mds_client.h +++ b/fs/ceph/mds_client.h @@ -322,6 +322,9 @@ static inline void ceph_mdsc_put_request(struct ceph_mds_request *req) kref_put(&req->r_kref, ceph_mdsc_release_request); } +extern void ceph_send_cap_releases(struct ceph_mds_client *mdsc, + struct ceph_mds_session *session); + extern void ceph_mdsc_pre_umount(struct ceph_mds_client *mdsc); extern char *ceph_mdsc_build_path(struct dentry *dentry, int *plen, u64 *base, -- cgit v1.2.3 From 2b2300d62ea413bec631d5b880effa2cc5363acb Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Wed, 9 Jun 2010 16:52:04 -0700 Subject: ceph: try to send partial cap release on cap message on missing inode If we have enough memory to allocate a new cap release message, do so, so that we can send a partial release message immediately. This keeps us from making the MDS wait when the cap release it needs is in a partially full release message. If we fail because of ENOMEM, oh well, they'll just have to wait a bit longer. Signed-off-by: Sage Weil --- fs/ceph/caps.c | 1 + fs/ceph/mds_client.c | 10 +++++----- fs/ceph/mds_client.h | 3 +++ 3 files changed, 9 insertions(+), 5 deletions(-) (limited to 'fs') diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index 7c692e746237..619b61655ee5 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -2708,6 +2708,7 @@ void ceph_handle_caps(struct ceph_mds_session *session, * along for the mds (who clearly thinks we still have this * cap). */ + ceph_add_cap_releases(mdsc, session, -1); ceph_send_cap_releases(mdsc, session); goto done; } diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index d28b6a9c0f96..1766947fc07a 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -1066,9 +1066,9 @@ static int trim_caps(struct ceph_mds_client *mdsc, * * Called under s_mutex. */ -static int add_cap_releases(struct ceph_mds_client *mdsc, - struct ceph_mds_session *session, - int extra) +int ceph_add_cap_releases(struct ceph_mds_client *mdsc, + struct ceph_mds_session *session, + int extra) { struct ceph_msg *msg; struct ceph_mds_cap_release *head; @@ -1980,7 +1980,7 @@ out_err: } mutex_unlock(&mdsc->mutex); - add_cap_releases(mdsc, req->r_session, -1); + ceph_add_cap_releases(mdsc, req->r_session, -1); mutex_unlock(&session->s_mutex); /* kick calling process */ @@ -2690,7 +2690,7 @@ static void delayed_work(struct work_struct *work) send_renew_caps(mdsc, s); else ceph_con_keepalive(&s->s_con); - add_cap_releases(mdsc, s, -1); + ceph_add_cap_releases(mdsc, s, -1); if (s->s_state == CEPH_MDS_SESSION_OPEN || s->s_state == CEPH_MDS_SESSION_HUNG) ceph_send_cap_releases(mdsc, s); diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h index e43752b52635..b292fa42a66d 100644 --- a/fs/ceph/mds_client.h +++ b/fs/ceph/mds_client.h @@ -322,6 +322,9 @@ static inline void ceph_mdsc_put_request(struct ceph_mds_request *req) kref_put(&req->r_kref, ceph_mdsc_release_request); } +extern int ceph_add_cap_releases(struct ceph_mds_client *mdsc, + struct ceph_mds_session *session, + int extra); extern void ceph_send_cap_releases(struct ceph_mds_client *mdsc, struct ceph_mds_session *session); -- cgit v1.2.3 From 7f0e7bed936a0c422641a046551829a01341dd80 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 8 Jun 2010 18:14:34 +0200 Subject: writeback: fix writeback completion notifications The code dealing with bdi_work->state and completion of a bdi_work is a major mess currently. This patch makes sure we directly use one set of flags to deal with it, and use it consistently, which means: - always notify about completion from the rcu callback. We only ever wait for it from on-stack callers, so this simplification does not even cause a theoretical slowdown currently. It also makes sure we don't miss out on the notification if we ever add other callers to wait for it. - make earlier completion notification depending on the on-stack allocation, not the sync mode. If we introduce new callers that want to do WB_SYNC_NONE writeback from on-stack callers this will be nessecary. Also rename bdi_wait_on_work_clear to bdi_wait_on_work_done and inline a few small functions into their only caller to make the code understandable. Signed-off-by: Christoph Hellwig Signed-off-by: Jens Axboe --- fs/fs-writeback.c | 65 +++++++++++++------------------------------------------ 1 file changed, 15 insertions(+), 50 deletions(-) (limited to 'fs') diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 1d1088f48bc2..dbf6f108e868 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -63,24 +63,16 @@ struct bdi_work { }; enum { - WS_USED_B = 0, - WS_ONSTACK_B, + WS_INPROGRESS = 0, + WS_ONSTACK, }; -#define WS_USED (1 << WS_USED_B) -#define WS_ONSTACK (1 << WS_ONSTACK_B) - -static inline bool bdi_work_on_stack(struct bdi_work *work) -{ - return test_bit(WS_ONSTACK_B, &work->state); -} - static inline void bdi_work_init(struct bdi_work *work, struct wb_writeback_args *args) { INIT_RCU_HEAD(&work->rcu_head); work->args = *args; - work->state = WS_USED; + __set_bit(WS_INPROGRESS, &work->state); } /** @@ -95,43 +87,16 @@ int writeback_in_progress(struct backing_dev_info *bdi) return !list_empty(&bdi->work_list); } -static void bdi_work_clear(struct bdi_work *work) -{ - clear_bit(WS_USED_B, &work->state); - smp_mb__after_clear_bit(); - /* - * work can have disappeared at this point. bit waitq functions - * should be able to tolerate this, provided bdi_sched_wait does - * not dereference it's pointer argument. - */ - wake_up_bit(&work->state, WS_USED_B); -} - static void bdi_work_free(struct rcu_head *head) { struct bdi_work *work = container_of(head, struct bdi_work, rcu_head); - if (!bdi_work_on_stack(work)) - kfree(work); - else - bdi_work_clear(work); -} - -static void wb_work_complete(struct bdi_work *work) -{ - const enum writeback_sync_modes sync_mode = work->args.sync_mode; - int onstack = bdi_work_on_stack(work); + clear_bit(WS_INPROGRESS, &work->state); + smp_mb__after_clear_bit(); + wake_up_bit(&work->state, WS_INPROGRESS); - /* - * For allocated work, we can clear the done/seen bit right here. - * For on-stack work, we need to postpone both the clear and free - * to after the RCU grace period, since the stack could be invalidated - * as soon as bdi_work_clear() has done the wakeup. - */ - if (!onstack) - bdi_work_clear(work); - if (sync_mode == WB_SYNC_NONE || onstack) - call_rcu(&work->rcu_head, bdi_work_free); + if (!test_bit(WS_ONSTACK, &work->state)) + kfree(work); } static void wb_clear_pending(struct bdi_writeback *wb, struct bdi_work *work) @@ -147,7 +112,7 @@ static void wb_clear_pending(struct bdi_writeback *wb, struct bdi_work *work) list_del_rcu(&work->list); spin_unlock(&bdi->wb_lock); - wb_work_complete(work); + call_rcu(&work->rcu_head, bdi_work_free); } } @@ -185,9 +150,9 @@ static void bdi_queue_work(struct backing_dev_info *bdi, struct bdi_work *work) * Used for on-stack allocated work items. The caller needs to wait until * the wb threads have acked the work before it's safe to continue. */ -static void bdi_wait_on_work_clear(struct bdi_work *work) +static void bdi_wait_on_work_done(struct bdi_work *work) { - wait_on_bit(&work->state, WS_USED_B, bdi_sched_wait, + wait_on_bit(&work->state, WS_INPROGRESS, bdi_sched_wait, TASK_UNINTERRUPTIBLE); } @@ -234,10 +199,10 @@ static void bdi_sync_writeback(struct backing_dev_info *bdi, struct bdi_work work; bdi_work_init(&work, &args); - work.state |= WS_ONSTACK; + __set_bit(WS_ONSTACK, &work.state); bdi_queue_work(bdi, &work); - bdi_wait_on_work_clear(&work); + bdi_wait_on_work_done(&work); } /** @@ -911,7 +876,7 @@ long wb_do_writeback(struct bdi_writeback *wb, int force_wait) * If this isn't a data integrity operation, just notify * that we have seen this work and we are now starting it. */ - if (args.sync_mode == WB_SYNC_NONE) + if (!test_bit(WS_ONSTACK, &work->state)) wb_clear_pending(wb, work); wrote += wb_writeback(wb, &args); @@ -920,7 +885,7 @@ long wb_do_writeback(struct bdi_writeback *wb, int force_wait) * This is a data integrity writeback, so only do the * notification when we have completed the work. */ - if (args.sync_mode == WB_SYNC_ALL) + if (test_bit(WS_ONSTACK, &work->state)) wb_clear_pending(wb, work); } -- cgit v1.2.3 From 3c4d716538f3eefb1c1f10961a047a6456a2b590 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 8 Jun 2010 18:14:43 +0200 Subject: writeback: queue work on stack in writeback_inodes_sb If we want to rely on s_umount in the caller we need to wait for completion of the I/O submission before returning to the caller. Refactor bdi_sync_writeback into a bdi_queue_work_onstack helper and use it for this case. Signed-off-by: Christoph Hellwig Signed-off-by: Jens Axboe --- fs/fs-writeback.c | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) (limited to 'fs') diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index dbf6f108e868..759666966c6d 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -178,30 +178,22 @@ static void bdi_alloc_queue_work(struct backing_dev_info *bdi, } /** - * bdi_sync_writeback - start and wait for writeback - * @bdi: the backing device to write from + * bdi_queue_work_onstack - start and wait for writeback * @sb: write inodes from this super_block * * Description: - * This does WB_SYNC_ALL data integrity writeback and waits for the - * IO to complete. Callers must hold the sb s_umount semaphore for + * This function initiates writeback and waits for the operation to + * complete. Callers must hold the sb s_umount semaphore for * reading, to avoid having the super disappear before we are done. */ -static void bdi_sync_writeback(struct backing_dev_info *bdi, - struct super_block *sb) +static void bdi_queue_work_onstack(struct wb_writeback_args *args) { - struct wb_writeback_args args = { - .sb = sb, - .sync_mode = WB_SYNC_ALL, - .nr_pages = LONG_MAX, - .range_cyclic = 0, - }; struct bdi_work work; - bdi_work_init(&work, &args); + bdi_work_init(&work, args); __set_bit(WS_ONSTACK, &work.state); - bdi_queue_work(bdi, &work); + bdi_queue_work(args->sb->s_bdi, &work); bdi_wait_on_work_done(&work); } @@ -944,7 +936,7 @@ int bdi_writeback_task(struct bdi_writeback *wb) /* * Schedule writeback for all backing devices. This does WB_SYNC_NONE - * writeback, for integrity writeback see bdi_sync_writeback(). + * writeback, for integrity writeback see bdi_queue_work_onstack(). */ static void bdi_writeback_all(struct super_block *sb, long nr_pages) { @@ -1183,12 +1175,15 @@ void writeback_inodes_sb(struct super_block *sb) { unsigned long nr_dirty = global_page_state(NR_FILE_DIRTY); unsigned long nr_unstable = global_page_state(NR_UNSTABLE_NFS); - long nr_to_write; + struct wb_writeback_args args = { + .sb = sb, + .sync_mode = WB_SYNC_NONE, + }; - nr_to_write = nr_dirty + nr_unstable + + args.nr_pages = nr_dirty + nr_unstable + (inodes_stat.nr_inodes - inodes_stat.nr_unused); - bdi_start_writeback(sb->s_bdi, sb, nr_to_write); + bdi_queue_work_onstack(&args); } EXPORT_SYMBOL(writeback_inodes_sb); @@ -1218,7 +1213,14 @@ EXPORT_SYMBOL(writeback_inodes_sb_if_idle); */ void sync_inodes_sb(struct super_block *sb) { - bdi_sync_writeback(sb->s_bdi, sb); + struct wb_writeback_args args = { + .sb = sb, + .sync_mode = WB_SYNC_ALL, + .nr_pages = LONG_MAX, + .range_cyclic = 0, + }; + + bdi_queue_work_onstack(&args); wait_sb_inodes(sb); } EXPORT_SYMBOL(sync_inodes_sb); -- cgit v1.2.3 From cf37e972478ec58a8a54a6b4f951815f0ae28f78 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 8 Jun 2010 18:14:51 +0200 Subject: writeback: enforce s_umount locking in writeback_inodes_sb Make sure that not only sync_filesystem but all callers of writeback_inodes_sb have the superblock protected against remount. As-is this disables all functionality for these callers, but the next patch relies on this locking to fix writeback_inodes_sb for sync_filesystem. Signed-off-by: Christoph Hellwig Signed-off-by: Jens Axboe --- fs/fs-writeback.c | 6 ++++++ fs/ubifs/budget.c | 2 ++ 2 files changed, 8 insertions(+) (limited to 'fs') diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 759666966c6d..2627f0dfcd9c 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -1180,6 +1180,8 @@ void writeback_inodes_sb(struct super_block *sb) .sync_mode = WB_SYNC_NONE, }; + WARN_ON(!rwsem_is_locked(&sb->s_umount)); + args.nr_pages = nr_dirty + nr_unstable + (inodes_stat.nr_inodes - inodes_stat.nr_unused); @@ -1197,7 +1199,9 @@ EXPORT_SYMBOL(writeback_inodes_sb); int writeback_inodes_sb_if_idle(struct super_block *sb) { if (!writeback_in_progress(sb->s_bdi)) { + down_read(&sb->s_umount); writeback_inodes_sb(sb); + up_read(&sb->s_umount); return 1; } else return 0; @@ -1220,6 +1224,8 @@ void sync_inodes_sb(struct super_block *sb) .range_cyclic = 0, }; + WARN_ON(!rwsem_is_locked(&sb->s_umount)); + bdi_queue_work_onstack(&args); wait_sb_inodes(sb); } diff --git a/fs/ubifs/budget.c b/fs/ubifs/budget.c index 076ca50e9933..c8ff0d1ae5d3 100644 --- a/fs/ubifs/budget.c +++ b/fs/ubifs/budget.c @@ -62,7 +62,9 @@ */ static void shrink_liability(struct ubifs_info *c, int nr_to_write) { + down_read(&c->vfs_sb->s_umount); writeback_inodes_sb(c->vfs_sb); + up_read(&c->vfs_sb->s_umount); } /** -- cgit v1.2.3 From d19de7edf59cdd586777b009e0e8fbe5412dd35f Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 8 Jun 2010 18:14:58 +0200 Subject: writeback: fix writeback_inodes_wb from writeback_inodes_sb When we call writeback_inodes_wb from writeback_inodes_sb we always have s_umount held, which currently makes the whole operation a no-op. But if we are called to write out inodes for a specific superblock we always have s_umount held, so replace the incorrect logic checking for WB_SYNC_ALL which only worked by coincidence with the proper check for an explicit superblock argument. Signed-off-by: Christoph Hellwig Signed-off-by: Jens Axboe --- fs/fs-writeback.c | 61 +++++++++++++++++++++---------------------------------- 1 file changed, 23 insertions(+), 38 deletions(-) (limited to 'fs') diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 2627f0dfcd9c..68ece4b18916 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -518,39 +518,19 @@ select_queue: return ret; } -static void unpin_sb_for_writeback(struct super_block *sb) -{ - up_read(&sb->s_umount); - put_super(sb); -} - -enum sb_pin_state { - SB_PINNED, - SB_NOT_PINNED, - SB_PIN_FAILED -}; - /* - * For WB_SYNC_NONE writeback, the caller does not have the sb pinned + * For background writeback the caller does not have the sb pinned * before calling writeback. So make sure that we do pin it, so it doesn't * go away while we are writing inodes from it. */ -static enum sb_pin_state pin_sb_for_writeback(struct writeback_control *wbc, - struct super_block *sb) +static bool pin_sb_for_writeback(struct super_block *sb) { - /* - * Caller must already hold the ref for this - */ - if (wbc->sync_mode == WB_SYNC_ALL) { - WARN_ON(!rwsem_is_locked(&sb->s_umount)); - return SB_NOT_PINNED; - } spin_lock(&sb_lock); sb->s_count++; if (down_read_trylock(&sb->s_umount)) { if (sb->s_root) { spin_unlock(&sb_lock); - return SB_PINNED; + return true; } /* * umounted, drop rwsem again and fall through to failure @@ -559,7 +539,7 @@ static enum sb_pin_state pin_sb_for_writeback(struct writeback_control *wbc, } sb->s_count--; spin_unlock(&sb_lock); - return SB_PIN_FAILED; + return false; } /* @@ -638,24 +618,29 @@ static void writeback_inodes_wb(struct bdi_writeback *wb, struct inode *inode = list_entry(wb->b_io.prev, struct inode, i_list); struct super_block *sb = inode->i_sb; - enum sb_pin_state state; - if (wbc->sb && sb != wbc->sb) { - /* super block given and doesn't - match, skip this inode */ - redirty_tail(inode); - continue; - } - state = pin_sb_for_writeback(wbc, sb); + if (wbc->sb) { + /* + * We are requested to write out inodes for a specific + * superblock. This means we already have s_umount + * taken by the caller which also waits for us to + * complete the writeout. + */ + if (sb != wbc->sb) { + redirty_tail(inode); + continue; + } - if (state == SB_PIN_FAILED) { - requeue_io(inode); - continue; + WARN_ON(!rwsem_is_locked(&sb->s_umount)); + + ret = writeback_sb_inodes(sb, wb, wbc); + } else { + if (!pin_sb_for_writeback(sb)) + continue; + ret = writeback_sb_inodes(sb, wb, wbc); + drop_super(sb); } - ret = writeback_sb_inodes(sb, wb, wbc); - if (state == SB_PINNED) - unpin_sb_for_writeback(sb); if (ret) break; } -- cgit v1.2.3 From b8c2f3474f1077599ec6e90c2f263f17055cc3d8 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 8 Jun 2010 18:15:07 +0200 Subject: writeback: simplify wakeup_flusher_threads bdi_writeback_all only has one caller, so fold it to simplify the code and flatten the call stack. Signed-off-by: Christoph Hellwig Signed-off-by: Jens Axboe --- fs/fs-writeback.c | 32 +++++++++++--------------------- 1 file changed, 11 insertions(+), 21 deletions(-) (limited to 'fs') diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 68ece4b18916..4fcca4f74940 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -920,42 +920,32 @@ int bdi_writeback_task(struct bdi_writeback *wb) } /* - * Schedule writeback for all backing devices. This does WB_SYNC_NONE - * writeback, for integrity writeback see bdi_queue_work_onstack(). + * Start writeback of `nr_pages' pages. If `nr_pages' is zero, write back + * the whole world. */ -static void bdi_writeback_all(struct super_block *sb, long nr_pages) +void wakeup_flusher_threads(long nr_pages) { + struct backing_dev_info *bdi; struct wb_writeback_args args = { - .sb = sb, - .nr_pages = nr_pages, .sync_mode = WB_SYNC_NONE, }; - struct backing_dev_info *bdi; - rcu_read_lock(); + if (nr_pages) { + args.nr_pages = nr_pages; + } else { + args.nr_pages = global_page_state(NR_FILE_DIRTY) + + global_page_state(NR_UNSTABLE_NFS); + } + rcu_read_lock(); list_for_each_entry_rcu(bdi, &bdi_list, bdi_list) { if (!bdi_has_dirty_io(bdi)) continue; - bdi_alloc_queue_work(bdi, &args); } - rcu_read_unlock(); } -/* - * Start writeback of `nr_pages' pages. If `nr_pages' is zero, write back - * the whole world. - */ -void wakeup_flusher_threads(long nr_pages) -{ - if (nr_pages == 0) - nr_pages = global_page_state(NR_FILE_DIRTY) + - global_page_state(NR_UNSTABLE_NFS); - bdi_writeback_all(NULL, nr_pages); -} - static noinline void block_dump___mark_inode_dirty(struct inode *inode) { if (inode->i_ino || strcmp(inode->i_sb->s_id, "bdev")) { -- cgit v1.2.3 From c5444198ca210498e8ac0ba121b4cd3537aa12f7 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 8 Jun 2010 18:15:15 +0200 Subject: writeback: simplify and split bdi_start_writeback bdi_start_writeback now never gets a superblock passed, so we can just remove that case. And to further untangle the code and flatten the call stack split it into two trivial helpers for it's two callers. Signed-off-by: Christoph Hellwig Signed-off-by: Jens Axboe --- fs/fs-writeback.c | 32 ++++++++++++++++++++------------ include/linux/backing-dev.h | 4 ++-- mm/page-writeback.c | 5 ++--- 3 files changed, 24 insertions(+), 17 deletions(-) (limited to 'fs') diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 4fcca4f74940..0079bf59b583 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -200,7 +200,6 @@ static void bdi_queue_work_onstack(struct wb_writeback_args *args) /** * bdi_start_writeback - start writeback * @bdi: the backing device to write from - * @sb: write inodes from this super_block * @nr_pages: the number of pages to write * * Description: @@ -209,25 +208,34 @@ static void bdi_queue_work_onstack(struct wb_writeback_args *args) * completion. Caller need not hold sb s_umount semaphore. * */ -void bdi_start_writeback(struct backing_dev_info *bdi, struct super_block *sb, - long nr_pages) +void bdi_start_writeback(struct backing_dev_info *bdi, long nr_pages) { struct wb_writeback_args args = { - .sb = sb, .sync_mode = WB_SYNC_NONE, .nr_pages = nr_pages, .range_cyclic = 1, }; - /* - * We treat @nr_pages=0 as the special case to do background writeback, - * ie. to sync pages until the background dirty threshold is reached. - */ - if (!nr_pages) { - args.nr_pages = LONG_MAX; - args.for_background = 1; - } + bdi_alloc_queue_work(bdi, &args); +} +/** + * bdi_start_background_writeback - start background writeback + * @bdi: the backing device to write from + * + * Description: + * This does WB_SYNC_NONE background writeback. The IO is only + * started when this function returns, we make no guarentees on + * completion. Caller need not hold sb s_umount semaphore. + */ +void bdi_start_background_writeback(struct backing_dev_info *bdi) +{ + struct wb_writeback_args args = { + .sync_mode = WB_SYNC_NONE, + .nr_pages = LONG_MAX, + .for_background = 1, + .range_cyclic = 1, + }; bdi_alloc_queue_work(bdi, &args); } diff --git a/include/linux/backing-dev.h b/include/linux/backing-dev.h index aee5f6ce166e..9ae2889096b6 100644 --- a/include/linux/backing-dev.h +++ b/include/linux/backing-dev.h @@ -105,8 +105,8 @@ int bdi_register(struct backing_dev_info *bdi, struct device *parent, int bdi_register_dev(struct backing_dev_info *bdi, dev_t dev); void bdi_unregister(struct backing_dev_info *bdi); int bdi_setup_and_register(struct backing_dev_info *, char *, unsigned int); -void bdi_start_writeback(struct backing_dev_info *bdi, struct super_block *sb, - long nr_pages); +void bdi_start_writeback(struct backing_dev_info *bdi, long nr_pages); +void bdi_start_background_writeback(struct backing_dev_info *bdi); int bdi_writeback_task(struct bdi_writeback *wb); int bdi_has_dirty_io(struct backing_dev_info *bdi); void bdi_arm_supers_timer(void); diff --git a/mm/page-writeback.c b/mm/page-writeback.c index bbd396ac9546..54f28bd493d3 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c @@ -597,7 +597,7 @@ static void balance_dirty_pages(struct address_space *mapping, (!laptop_mode && ((global_page_state(NR_FILE_DIRTY) + global_page_state(NR_UNSTABLE_NFS)) > background_thresh))) - bdi_start_writeback(bdi, NULL, 0); + bdi_start_background_writeback(bdi); } void set_page_dirty_balance(struct page *page, int page_mkwrite) @@ -705,9 +705,8 @@ void laptop_mode_timer_fn(unsigned long data) * We want to write everything out, not just down to the dirty * threshold */ - if (bdi_has_dirty_io(&q->backing_dev_info)) - bdi_start_writeback(&q->backing_dev_info, NULL, nr_pages); + bdi_start_writeback(&q->backing_dev_info, nr_pages); } /* -- cgit v1.2.3 From 334132ae921a14ac2b2ba48e174136f7f2c9aae1 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 9 Jun 2010 14:28:43 +0200 Subject: writeback: add missing requeue_io in writeback_inodes_wb In "writeback: fix writeback_inodes_wb from writeback_inodes_sb" I accidentally removed the requeue_io if we need to skip a superblock because we can't pin it. Add it back, otherwise we're getting spurious lockups after multiple xfstests runs. Signed-off-by: Christoph Hellwig Signed-off-by: Jens Axboe --- fs/fs-writeback.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 0079bf59b583..3a066e91ec8d 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -643,8 +643,10 @@ static void writeback_inodes_wb(struct bdi_writeback *wb, ret = writeback_sb_inodes(sb, wb, wbc); } else { - if (!pin_sb_for_writeback(sb)) + if (!pin_sb_for_writeback(sb)) { + requeue_io(inode); continue; + } ret = writeback_sb_inodes(sb, wb, wbc); drop_super(sb); } -- cgit v1.2.3 From 29cb48594b873f6193d6327097e504bd3e2314de Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 9 Jun 2010 15:31:01 +0200 Subject: writeback: fix pin_sb_for_writeback We need to check for s_instances to make sure we don't bother working against a filesystem that is beeing unmounted, and we need to call put_super to make sure a superblock is freed when we race against umount. Also no need to keep sb_lock after we got a reference on it. Signed-off-by: Christoph Hellwig Signed-off-by: Jens Axboe --- fs/fs-writeback.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) (limited to 'fs') diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 3a066e91ec8d..0609607d3955 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -534,19 +534,21 @@ select_queue: static bool pin_sb_for_writeback(struct super_block *sb) { spin_lock(&sb_lock); + if (list_empty(&sb->s_instances)) { + spin_unlock(&sb_lock); + return false; + } + sb->s_count++; + spin_unlock(&sb_lock); + if (down_read_trylock(&sb->s_umount)) { - if (sb->s_root) { - spin_unlock(&sb_lock); + if (sb->s_root) return true; - } - /* - * umounted, drop rwsem again and fall through to failure - */ up_read(&sb->s_umount); } - sb->s_count--; - spin_unlock(&sb_lock); + + put_super(sb); return false; } -- cgit v1.2.3 From 4a001071d3549f596c7c3736c5dda8a3a4aba9ed Mon Sep 17 00:00:00 2001 From: Miao Xie Date: Mon, 7 Jun 2010 03:38:51 +0000 Subject: Btrfs: fix loop device on top of btrfs We cannot use the loop device which has been connected to a file in the btrf The reproduce steps is following: # dd if=/dev/zero of=vdev0 bs=1M count=1024 # losetup /dev/loop0 vdev0 # mkfs.btrfs /dev/loop0 ... failed to zero device start -5 The reason is that the btrfs don't implement either ->write_begin or ->write the VFS API, so we fix it by setting ->write to do_sync_write(). Signed-off-by: Miao Xie Signed-off-by: Chris Mason --- fs/btrfs/file.c | 1 + 1 file changed, 1 insertion(+) (limited to 'fs') diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index 79437c5eeb1e..abcb91867b56 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -1197,6 +1197,7 @@ static int btrfs_file_mmap(struct file *filp, struct vm_area_struct *vma) const struct file_operations btrfs_file_operations = { .llseek = generic_file_llseek, .read = do_sync_read, + .write = do_sync_write, .aio_read = generic_file_aio_read, .splice_read = generic_file_splice_read, .aio_write = btrfs_file_aio_write, -- cgit v1.2.3 From 836097797236fd727f82ec2f3f376ac41a430876 Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Mon, 7 Jun 2010 18:26:37 +0000 Subject: Btrfs: fix fallocate regression Seems that when btrfs_fallocate was converted to use the new ENOSPC stuff we dropped passing the mode to the function that actually does the preallocation. This breaks anybody who wants to use FALLOC_FL_KEEP_SIZE. Thanks, Signed-off-by: Josef Bacik Signed-off-by: Chris Mason --- fs/btrfs/inode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 2551b8018399..d999c538cdc2 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -6893,7 +6893,7 @@ static long btrfs_fallocate(struct inode *inode, int mode, if (em->block_start == EXTENT_MAP_HOLE || (cur_offset >= inode->i_size && !test_bit(EXTENT_FLAG_PREALLOC, &em->flags))) { - ret = btrfs_prealloc_file_range(inode, 0, cur_offset, + ret = btrfs_prealloc_file_range(inode, mode, cur_offset, last_byte - cur_offset, 1 << inode->i_blkbits, offset + len, -- cgit v1.2.3 From 0e4dcbef1c0c3e29f9c7f824359445d385b2649a Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 1 Jun 2010 08:23:11 +0000 Subject: Btrfs: uninitialized data is check_path_shared() refs can be used with uninitialized data if btrfs_lookup_extent_info() fails on the first pass through the loop. In the original code if that happens then check_path_shared() probably returns 1, this patch changes it to return 1 for safety. Signed-off-by: Dan Carpenter Signed-off-by: Chris Mason --- fs/btrfs/inode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index d999c538cdc2..f08427c70a78 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -2673,7 +2673,7 @@ static int check_path_shared(struct btrfs_root *root, struct extent_buffer *eb; int level; int ret; - u64 refs; + u64 refs = 1; for (level = 0; level < BTRFS_MAX_LEVEL; level++) { if (!path->nodes[level]) -- cgit v1.2.3 From 058a457ef0ce28d595af53d6103db73332383cbc Mon Sep 17 00:00:00 2001 From: Miao Xie Date: Thu, 20 May 2010 07:21:50 +0000 Subject: Btrfs: fix remap_file_pages error when we use remap_file_pages() to remap a file, remap_file_pages always return error. It is because btrfs didn't set VM_CAN_NONLINEAR for vma. Signed-off-by: Miao Xie Signed-off-by: Chris Mason --- fs/btrfs/file.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index abcb91867b56..ce0cd29efa9e 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -1189,8 +1189,15 @@ static const struct vm_operations_struct btrfs_file_vm_ops = { static int btrfs_file_mmap(struct file *filp, struct vm_area_struct *vma) { - vma->vm_ops = &btrfs_file_vm_ops; + struct address_space *mapping = filp->f_mapping; + + if (!mapping->a_ops->readpage) + return -ENOEXEC; + file_accessed(filp); + vma->vm_ops = &btrfs_file_vm_ops; + vma->vm_flags |= VM_CAN_NONLINEAR; + return 0; } -- cgit v1.2.3 From 046f264f6b3b2cf7e5a1769fc92335d8a9316282 Mon Sep 17 00:00:00 2001 From: "Yan, Zheng" Date: Mon, 31 May 2010 08:58:47 +0000 Subject: Btrfs: Fix null dereference in relocation.c Fix a potential null dereference in relocation.c Signed-off-by: Yan Zheng Acked-by: Dan Carpenter Signed-off-by: Chris Mason --- fs/btrfs/relocation.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c index 05d41e569236..b37d723b9d4a 100644 --- a/fs/btrfs/relocation.c +++ b/fs/btrfs/relocation.c @@ -784,16 +784,17 @@ again: struct btrfs_extent_ref_v0 *ref0; ref0 = btrfs_item_ptr(eb, path1->slots[0], struct btrfs_extent_ref_v0); - root = find_tree_root(rc, eb, ref0); - if (!root->ref_cows) - cur->cowonly = 1; if (key.objectid == key.offset) { + root = find_tree_root(rc, eb, ref0); if (root && !should_ignore_root(root)) cur->root = root; else list_add(&cur->list, &useless); break; } + if (is_cowonly_root(btrfs_ref_root_v0(eb, + ref0))) + cur->cowonly = 1; } #else BUG_ON(key.type == BTRFS_EXTENT_REF_V0_KEY); -- cgit v1.2.3 From 3bf84a5a834d13e7c5c3e8e5b5c6b26012118dd8 Mon Sep 17 00:00:00 2001 From: "Yan, Zheng" Date: Mon, 31 May 2010 09:04:46 +0000 Subject: Btrfs: Fix BUG_ON for fs converted from extN Tree blocks can live in data block groups in FS converted from extN. So it's easy to trigger the BUG_ON. Signed-off-by: Yan Zheng Signed-off-by: Chris Mason --- fs/btrfs/extent-tree.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 6c14101506e1..a46b64de8f02 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -4360,7 +4360,8 @@ void btrfs_free_tree_block(struct btrfs_trans_handle *trans, block_rsv = get_block_rsv(trans, root); cache = btrfs_lookup_block_group(root->fs_info, buf->start); - BUG_ON(block_rsv->space_info != cache->space_info); + if (block_rsv->space_info != cache->space_info) + goto out; if (btrfs_header_generation(buf) == trans->transid) { if (root->root_key.objectid != BTRFS_TREE_LOG_OBJECTID) { -- cgit v1.2.3 From fb4f6f910ca6f58564c31a680ef88940d8192713 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sat, 29 May 2010 09:40:57 +0000 Subject: Btrfs: handle error returns from btrfs_lookup_dir_item() If btrfs_lookup_dir_item() fails, we should can just let the mount fail with an error. Signed-off-by: Dan Carpenter Signed-off-by: Chris Mason --- fs/btrfs/super.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'fs') diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index 574285c8cbd4..9ea711430466 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -360,6 +360,8 @@ static struct dentry *get_default_root(struct super_block *sb, */ dir_id = btrfs_super_root_dir(&root->fs_info->super_copy); di = btrfs_lookup_dir_item(NULL, root, path, dir_id, "default", 7, 0); + if (IS_ERR(di)) + return ERR_CAST(di); if (!di) { /* * Ok the default dir item isn't there. This is weird since -- cgit v1.2.3 From 676e4c86391936795c82ccd11ca9671ee6307936 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sat, 29 May 2010 09:43:07 +0000 Subject: Btrfs: handle kzalloc() failure in open_ctree() Unwind and return -ENOMEM if the allocation fails here. Signed-off-by: Dan Carpenter Signed-off-by: Chris Mason --- fs/btrfs/disk-io.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index f3b287c22caf..73895baf6e07 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -1941,8 +1941,11 @@ struct btrfs_root *open_ctree(struct super_block *sb, btrfs_level_size(tree_root, btrfs_super_log_root_level(disk_super)); - log_tree_root = kzalloc(sizeof(struct btrfs_root), - GFP_NOFS); + log_tree_root = kzalloc(sizeof(struct btrfs_root), GFP_NOFS); + if (!log_tree_root) { + err = -ENOMEM; + goto fail_trans_kthread; + } __setup_root(nodesize, leafsize, sectorsize, stripesize, log_tree_root, fs_info, BTRFS_TREE_LOG_OBJECTID); -- cgit v1.2.3 From 4cbd1149fbcc351bdf08ab749867d157905d0d35 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sat, 29 May 2010 09:42:19 +0000 Subject: Btrfs: btrfs_iget() returns ERR_PTR btrfs_iget() returns an ERR_PTR() on failure and not null. Signed-off-by: Dan Carpenter Signed-off-by: Chris Mason --- fs/btrfs/super.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index 9ea711430466..859ddaab5e02 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -392,8 +392,8 @@ setup_root: location.offset = 0; inode = btrfs_iget(sb, &location, new_root, &new); - if (!inode) - return ERR_PTR(-ENOMEM); + if (IS_ERR(inode)) + return ERR_CAST(inode); /* * If we're just mounting the root most subvol put the inode and return -- cgit v1.2.3 From d327099a23e3d0c8ec09137e9b4b115449d4eb29 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sat, 29 May 2010 09:46:47 +0000 Subject: Btrfs: unwind after btrfs_start_transaction() errors This was added by a22285a6a3: "Btrfs: Integrate metadata reservation with start_transaction". If we goto out here then we skip all the unwinding and there are locks still held etc. Signed-off-by: Dan Carpenter Signed-off-by: Chris Mason --- fs/btrfs/ioctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 4cdb98cf26de..9f9a1d9607a7 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -1280,7 +1280,7 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file, trans = btrfs_start_transaction(root, 0); if (IS_ERR(trans)) { err = PTR_ERR(trans); - goto out; + goto out_up_write; } trans->block_rsv = &root->fs_info->global_block_rsv; -- cgit v1.2.3 From 3140c9a34b44cd4013baae8704fdb34a93a44475 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sat, 29 May 2010 09:44:10 +0000 Subject: Btrfs: btrfs_read_fs_root_no_name() returns ERR_PTRs btrfs_read_fs_root_no_name() returns ERR_PTRs on error so I added a check for that. It's not clear to me if it can also return NULL pointers or not so I left the original NULL pointer check as is. Signed-off-by: Dan Carpenter Signed-off-by: Chris Mason --- fs/btrfs/disk-io.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'fs') diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 73895baf6e07..34f7c375567e 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -1985,6 +1985,10 @@ struct btrfs_root *open_ctree(struct super_block *sb, fs_info->fs_root = btrfs_read_fs_root_no_name(fs_info, &location); if (!fs_info->fs_root) goto fail_trans_kthread; + if (IS_ERR(fs_info->fs_root)) { + err = PTR_ERR(fs_info->fs_root); + goto fail_trans_kthread; + } if (!(sb->s_flags & MS_RDONLY)) { down_read(&fs_info->cleanup_work_sem); -- cgit v1.2.3 From cf1e99a4e0daa4a5623cd71108509823b9ff2d30 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sat, 29 May 2010 09:47:24 +0000 Subject: Btrfs: btrfs_lookup_dir_item() can return ERR_PTR btrfs_lookup_dir_item() can return either ERR_PTRs or null. Signed-off-by: Dan Carpenter Signed-off-by: Chris Mason --- fs/btrfs/ioctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 9f9a1d9607a7..4dbaf89b1337 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -1845,7 +1845,7 @@ static long btrfs_ioctl_default_subvol(struct file *file, void __user *argp) dir_id = btrfs_super_root_dir(&root->fs_info->super_copy); di = btrfs_lookup_dir_item(trans, root->fs_info->tree_root, path, dir_id, "default", 7, 1); - if (!di) { + if (IS_ERR_OR_NULL(di)) { btrfs_free_path(path); btrfs_end_transaction(trans, root); printk(KERN_ERR "Umm, you don't have the default dir item, " -- cgit v1.2.3 From 2f26afba46f0ebf155cf9be746496a0304a5b7cf Mon Sep 17 00:00:00 2001 From: Shi Weihua Date: Tue, 18 May 2010 00:50:32 +0000 Subject: Btrfs: should add a permission check for setfacl On btrfs, do the following ------------------ # su user1 # cd btrfs-part/ # touch aaa # getfacl aaa # file: aaa # owner: user1 # group: user1 user::rw- group::rw- other::r-- # su user2 # cd btrfs-part/ # setfacl -m u::rwx aaa # getfacl aaa # file: aaa # owner: user1 # group: user1 user::rwx <- successed to setfacl group::rw- other::r-- ------------------ but we should prohibit it that user2 changing user1's acl. In fact, on ext3 and other fs, a message occurs: setfacl: aaa: Operation not permitted This patch fixed it. Signed-off-by: Shi Weihua Signed-off-by: Chris Mason --- fs/btrfs/acl.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'fs') diff --git a/fs/btrfs/acl.c b/fs/btrfs/acl.c index 6ef7b26724ec..6b4d0cca5c7f 100644 --- a/fs/btrfs/acl.c +++ b/fs/btrfs/acl.c @@ -160,6 +160,9 @@ static int btrfs_xattr_acl_set(struct dentry *dentry, const char *name, int ret; struct posix_acl *acl = NULL; + if (!is_owner_or_cap(dentry->d_inode)) + return -EPERM; + if (value) { acl = posix_acl_from_xattr(value, size); if (acl == NULL) { -- cgit v1.2.3 From 731e3d1b4348a96d53de6c084774424dedc64a3b Mon Sep 17 00:00:00 2001 From: Shi Weihua Date: Tue, 18 May 2010 00:51:54 +0000 Subject: Btrfs: prohibit a operation of changing acl's mask when noacl mount option used when used Posix File System Test Suite(pjd-fstest) to test btrfs, some cases about setfacl failed when noacl mount option used. I simplified used commands in pjd-fstest, and the following steps can reproduce it. ------------------------ # cd btrfs-part/ # mkdir aaa # setfacl -m m::rw aaa <- successed, but not expected by pjd-fstest. ------------------------ I checked ext3, a warning message occured, like as: setfacl: aaa/: Operation not supported Certainly, it's expected by pjd-fstest. So, i compared acl.c of btrfs and ext3. Based on that, a patch created. Fortunately, it works. Signed-off-by: Shi Weihua Signed-off-by: Chris Mason --- fs/btrfs/acl.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'fs') diff --git a/fs/btrfs/acl.c b/fs/btrfs/acl.c index 6b4d0cca5c7f..a372985b3a9a 100644 --- a/fs/btrfs/acl.c +++ b/fs/btrfs/acl.c @@ -163,6 +163,9 @@ static int btrfs_xattr_acl_set(struct dentry *dentry, const char *name, if (!is_owner_or_cap(dentry->d_inode)) return -EPERM; + if (!IS_POSIXACL(dentry->d_inode)) + return -EOPNOTSUPP; + if (value) { acl = posix_acl_from_xattr(value, size); if (acl == NULL) { -- cgit v1.2.3 From 15e7000095e6fc9ad07e476a100c900c72c14225 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Mon, 17 May 2010 17:15:27 +0000 Subject: Btrfs: avoid BUG when dropping root and reference in same transaction If btrfs_ioctl_snap_destroy() deletes a snapshot but finishes with end_transaction(), the cleaner kthread may come in and drop the root in the same transaction. If that's the case, the root's refs still == 1 in the tree when btrfs_del_root() deletes the item, because commit_fs_roots() hasn't updated it yet (that happens during the commit). This wasn't a problem before only because btrfs_ioctl_snap_destroy() would commit the transaction before dropping the dentry reference, so the dead root wouldn't get queued up until after the fs root item was updated in the btree. Since it is not an error to drop the root reference and the root in the same transaction, just drop the BUG_ON() in btrfs_del_root(). Signed-off-by: Sage Weil Signed-off-by: Chris Mason --- fs/btrfs/root-tree.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'fs') diff --git a/fs/btrfs/root-tree.c b/fs/btrfs/root-tree.c index b91ccd972644..2d958be761c8 100644 --- a/fs/btrfs/root-tree.c +++ b/fs/btrfs/root-tree.c @@ -330,7 +330,6 @@ int btrfs_del_root(struct btrfs_trans_handle *trans, struct btrfs_root *root, { struct btrfs_path *path; int ret; - u32 refs; struct btrfs_root_item *ri; struct extent_buffer *leaf; @@ -344,8 +343,6 @@ int btrfs_del_root(struct btrfs_trans_handle *trans, struct btrfs_root *root, leaf = path->nodes[0]; ri = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_root_item); - refs = btrfs_disk_root_refs(leaf, ri); - BUG_ON(refs != 0); ret = btrfs_del_item(trans, root, path); out: btrfs_free_path(path); -- cgit v1.2.3 From 834e74759a473f8101a273e843d1edec2778801d Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sat, 29 May 2010 09:48:35 +0000 Subject: Btrfs: handle ERR_PTR from posix_acl_from_xattr() posix_acl_from_xattr() returns both ERR_PTRs and null, but it's OK to pass null values to set_cached_acl() Signed-off-by: Dan Carpenter Signed-off-by: Chris Mason --- fs/btrfs/acl.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'fs') diff --git a/fs/btrfs/acl.c b/fs/btrfs/acl.c index a372985b3a9a..1606dc1e8d4a 100644 --- a/fs/btrfs/acl.c +++ b/fs/btrfs/acl.c @@ -60,6 +60,8 @@ static struct posix_acl *btrfs_get_acl(struct inode *inode, int type) size = __btrfs_getxattr(inode, name, value, size); if (size > 0) { acl = posix_acl_from_xattr(value, size); + if (IS_ERR(acl)) + return acl; set_cached_acl(inode, type, acl); } kfree(value); -- cgit v1.2.3 From 6f902af400b2499c80865c62a06fbbd15cf804fd Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sat, 29 May 2010 09:49:07 +0000 Subject: Btrfs: The file argument for fsync() is never null The "file" argument for fsync is never null so we can remove this check. What drew my attention here is that 7ea8085910e: "drop unused dentry argument to ->fsync" introduced an unconditional dereference at the start of the function and that generated a smatch warning. Signed-off-by: Dan Carpenter Signed-off-by: Chris Mason --- fs/btrfs/file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index ce0cd29efa9e..7f29464c0ebf 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -1139,7 +1139,7 @@ int btrfs_sync_file(struct file *file, struct dentry *dentry, int datasync) /* * ok we haven't committed the transaction yet, lets do a commit */ - if (file && file->private_data) + if (file->private_data) btrfs_ioctl_trans_end(file); trans = btrfs_start_transaction(root, 0); -- cgit v1.2.3 From ed0e3ace576d297a5c7015401db1060bbf677b94 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 1 Jun 2010 16:21:01 -0400 Subject: cifs: don't attempt busy-file rename unless it's in same directory Busy-file renames don't actually work across directories, so we need to limit this code to renames within the same dir. This fixes the bug detailed here: https://bugzilla.redhat.com/show_bug.cgi?id=591938 Signed-off-by: Jeff Layton CC: Stable Signed-off-by: Steve French --- fs/cifs/inode.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'fs') diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 62b324f26a56..6f0683c68952 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -1401,6 +1401,10 @@ cifs_do_rename(int xid, struct dentry *from_dentry, const char *fromPath, if (rc == 0 || rc != -ETXTBSY) return rc; + /* open-file renames don't work across directories */ + if (to_dentry->d_parent != from_dentry->d_parent) + return rc; + /* open the file to be renamed -- we need DELETE perms */ rc = CIFSSMBOpen(xid, pTcon, fromPath, FILE_OPEN, DELETE, CREATE_NOT_DIR, &srcfid, &oplock, NULL, -- cgit v1.2.3 From 12420ac341533f3715b3deb788637568f22b78ff Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 1 Jun 2010 14:47:40 -0400 Subject: cifs: implement drop_inode superblock op The standard behavior for drop_inode is to delete the inode when the last reference to it is put and the nlink count goes to 0. This helps keep inodes that are still considered "not deleted" in cache as long as possible even when there aren't dentries attached to them. When server inode numbers are disabled, it's not possible for cifs_iget to ever match an existing inode (since inode numbers are generated via iunique). In this situation, cifs can keep a lot of inodes in cache that will never be used again. Implement a drop_inode routine that deletes the inode if server inode numbers are disabled on the mount. This helps keep the cifs inode caches down to a more manageable size when server inode numbers are disabled. Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/cifsfs.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 78c02eb4cb1f..484e52bb40bb 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -473,14 +473,24 @@ static int cifs_remount(struct super_block *sb, int *flags, char *data) return 0; } +void cifs_drop_inode(struct inode *inode) +{ + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) + return generic_drop_inode(inode); + + return generic_delete_inode(inode); +} + static const struct super_operations cifs_super_ops = { .put_super = cifs_put_super, .statfs = cifs_statfs, .alloc_inode = cifs_alloc_inode, .destroy_inode = cifs_destroy_inode, -/* .drop_inode = generic_delete_inode, - .delete_inode = cifs_delete_inode, */ /* Do not need above two - functions unless later we add lazy close of inodes or unless the + .drop_inode = cifs_drop_inode, +/* .delete_inode = cifs_delete_inode, */ /* Do not need above + function unless later we add lazy close of inodes or unless the kernel forgets to call us with the same number of releases (closes) as opens */ .show_options = cifs_show_options, -- cgit v1.2.3 From a0375156ca1041574b5d47cc7e32f10b891151b0 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Fri, 11 Jun 2010 23:14:04 -0400 Subject: ext4: Clean up s_dirt handling We don't need to set s_dirt in most of the ext4 code when journaling is enabled. In ext3/4 some of the summary statistics for # of free inodes, blocks, and directories are calculated from the per-block group statistics when the file system is mounted or unmounted. As a result the superblock doesn't have to be updated, either via the journal or by setting s_dirt. There are a few exceptions, most notably when resizing the file system, where the superblock needs to be modified --- and in that case it should be done as a journalled operation if possible, and s_dirt set only in no-journal mode. This patch will optimize out some unneeded disk writes when using ext4 with a journal. Signed-off-by: "Theodore Ts'o" --- fs/ext4/balloc.c | 6 +----- fs/ext4/ext4.h | 6 ++++++ fs/ext4/ext4_jbd2.c | 16 ++++++++++++++++ fs/ext4/ext4_jbd2.h | 5 +++++ fs/ext4/file.c | 2 +- fs/ext4/ialloc.c | 4 ++-- fs/ext4/mballoc.c | 4 ++-- fs/ext4/resize.c | 6 ++---- fs/ext4/xattr.c | 3 +-- 9 files changed, 36 insertions(+), 16 deletions(-) (limited to 'fs') diff --git a/fs/ext4/balloc.c b/fs/ext4/balloc.c index 95b7594c76f9..bd30799a43ed 100644 --- a/fs/ext4/balloc.c +++ b/fs/ext4/balloc.c @@ -377,14 +377,11 @@ void ext4_add_groupblocks(handle_t *handle, struct super_block *sb, ext4_grpblk_t bit; unsigned int i; struct ext4_group_desc *desc; - struct ext4_super_block *es; - struct ext4_sb_info *sbi; + struct ext4_sb_info *sbi = EXT4_SB(sb); int err = 0, ret, blk_free_count; ext4_grpblk_t blocks_freed; struct ext4_group_info *grp; - sbi = EXT4_SB(sb); - es = sbi->s_es; ext4_debug("Adding block(s) %llu-%llu\n", block, block + count - 1); ext4_get_group_no_and_offset(sb, block, &block_group, &bit); @@ -477,7 +474,6 @@ void ext4_add_groupblocks(handle_t *handle, struct super_block *sb, ret = ext4_handle_dirty_metadata(handle, NULL, gd_bh); if (!err) err = ret; - sb->s_dirt = 1; error_return: brelse(bitmap_bh); diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 19a4de57128a..8b56b53328eb 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -1860,6 +1860,12 @@ static inline void ext4_unlock_group(struct super_block *sb, spin_unlock(ext4_group_lock_ptr(sb, group)); } +static inline void ext4_mark_super_dirty(struct super_block *sb) +{ + if (EXT4_SB(sb)->s_journal == NULL) + sb->s_dirt =1; +} + /* * Inodes and files operations */ diff --git a/fs/ext4/ext4_jbd2.c b/fs/ext4/ext4_jbd2.c index 53d2764d71ca..cfd27b38fa15 100644 --- a/fs/ext4/ext4_jbd2.c +++ b/fs/ext4/ext4_jbd2.c @@ -143,3 +143,19 @@ int __ext4_handle_dirty_metadata(const char *where, handle_t *handle, } return err; } + +int __ext4_handle_dirty_super(const char *where, handle_t *handle, + struct super_block *sb) +{ + struct buffer_head *bh = EXT4_SB(sb)->s_sbh; + int err = 0; + + if (ext4_handle_valid(handle)) { + err = jbd2_journal_dirty_metadata(handle, bh); + if (err) + ext4_journal_abort_handle(where, __func__, bh, + handle, err); + } else + sb->s_dirt = 1; + return err; +} diff --git a/fs/ext4/ext4_jbd2.h b/fs/ext4/ext4_jbd2.h index dade0c024797..8ae8168900bf 100644 --- a/fs/ext4/ext4_jbd2.h +++ b/fs/ext4/ext4_jbd2.h @@ -141,6 +141,9 @@ int __ext4_journal_get_create_access(const char *where, int __ext4_handle_dirty_metadata(const char *where, handle_t *handle, struct inode *inode, struct buffer_head *bh); +int __ext4_handle_dirty_super(const char *where, handle_t *handle, + struct super_block *sb); + #define ext4_journal_get_undo_access(handle, bh) \ __ext4_journal_get_undo_access(__func__, (handle), (bh)) #define ext4_journal_get_write_access(handle, bh) \ @@ -152,6 +155,8 @@ int __ext4_handle_dirty_metadata(const char *where, handle_t *handle, __ext4_journal_get_create_access(__func__, (handle), (bh)) #define ext4_handle_dirty_metadata(handle, inode, bh) \ __ext4_handle_dirty_metadata(__func__, (handle), (inode), (bh)) +#define ext4_handle_dirty_super(handle, sb) \ + __ext4_handle_dirty_super(__func__, (handle), (sb)) handle_t *ext4_journal_start_sb(struct super_block *sb, int nblocks); int __ext4_journal_stop(const char *where, handle_t *handle); diff --git a/fs/ext4/file.c b/fs/ext4/file.c index 5313ae4cda2d..bd411c12d63d 100644 --- a/fs/ext4/file.c +++ b/fs/ext4/file.c @@ -123,7 +123,7 @@ static int ext4_file_open(struct inode * inode, struct file * filp) if (!IS_ERR(cp)) { memcpy(sbi->s_es->s_last_mounted, cp, sizeof(sbi->s_es->s_last_mounted)); - sb->s_dirt = 1; + ext4_mark_super_dirty(sb); } } return dquot_file_open(inode, filp); diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c index 25c4b3173fd9..ac377505ed57 100644 --- a/fs/ext4/ialloc.c +++ b/fs/ext4/ialloc.c @@ -279,7 +279,7 @@ out: err = ext4_handle_dirty_metadata(handle, NULL, bitmap_bh); if (!fatal) fatal = err; - sb->s_dirt = 1; + ext4_mark_super_dirty(sb); } else ext4_error(sb, "bit already cleared for inode %lu", ino); @@ -965,7 +965,7 @@ got: percpu_counter_dec(&sbi->s_freeinodes_counter); if (S_ISDIR(mode)) percpu_counter_inc(&sbi->s_dirs_counter); - sb->s_dirt = 1; + ext4_mark_super_dirty(sb); if (sbi->s_log_groups_per_flex) { flex_group = ext4_flex_group(sbi, group); diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c index 12b3bc026a68..d9d267181ddc 100644 --- a/fs/ext4/mballoc.c +++ b/fs/ext4/mballoc.c @@ -2812,7 +2812,7 @@ ext4_mb_mark_diskspace_used(struct ext4_allocation_context *ac, err = ext4_handle_dirty_metadata(handle, NULL, gdp_bh); out_err: - sb->s_dirt = 1; + ext4_mark_super_dirty(sb); brelse(bitmap_bh); return err; } @@ -4680,7 +4680,7 @@ do_more: put_bh(bitmap_bh); goto do_more; } - sb->s_dirt = 1; + ext4_mark_super_dirty(sb); error_return: if (freed) dquot_free_block(inode, freed); diff --git a/fs/ext4/resize.c b/fs/ext4/resize.c index 6df797eb9aeb..27527ae466ea 100644 --- a/fs/ext4/resize.c +++ b/fs/ext4/resize.c @@ -921,8 +921,7 @@ int ext4_group_add(struct super_block *sb, struct ext4_new_group_data *input) &sbi->s_flex_groups[flex_group].free_inodes); } - ext4_handle_dirty_metadata(handle, NULL, sbi->s_sbh); - sb->s_dirt = 1; + ext4_handle_dirty_super(handle, sb); exit_journal: mutex_unlock(&sbi->s_resize_lock); @@ -1045,13 +1044,12 @@ int ext4_group_extend(struct super_block *sb, struct ext4_super_block *es, goto exit_put; } ext4_blocks_count_set(es, o_blocks_count + add); - ext4_handle_dirty_metadata(handle, NULL, EXT4_SB(sb)->s_sbh); - sb->s_dirt = 1; mutex_unlock(&EXT4_SB(sb)->s_resize_lock); ext4_debug("freeing blocks %llu through %llu\n", o_blocks_count, o_blocks_count + add); /* We add the blocks to the bitmap and set the group need init bit */ ext4_add_groupblocks(handle, sb, o_blocks_count, add); + ext4_handle_dirty_super(handle, sb); ext4_debug("freed blocks %llu through %llu\n", o_blocks_count, o_blocks_count + add); if ((err = ext4_journal_stop(handle))) diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c index 04338009793a..a6f314249574 100644 --- a/fs/ext4/xattr.c +++ b/fs/ext4/xattr.c @@ -458,8 +458,7 @@ static void ext4_xattr_update_super_block(handle_t *handle, if (ext4_journal_get_write_access(handle, EXT4_SB(sb)->s_sbh) == 0) { EXT4_SET_COMPAT_FEATURE(sb, EXT4_FEATURE_COMPAT_EXT_ATTR); - sb->s_dirt = 1; - ext4_handle_dirty_metadata(handle, NULL, EXT4_SB(sb)->s_sbh); + ext4_handle_dirty_super(handle, sb); } } -- cgit v1.2.3 From 276de5d2a18bcdc69e6d48a4d96afc14cfef9dcb Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Sun, 23 May 2010 14:16:13 +0300 Subject: UBIFS: check return code The error code from 'ubifs_rcvry_gc_commit()' was ignored, so UBIFS failed to recover and continued. Instead, we should refuse mounting the file-system. Signed-off-by: Artem Bityutskiy --- fs/ubifs/super.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'fs') diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c index 4d2f2157dd3f..010eea009036 100644 --- a/fs/ubifs/super.c +++ b/fs/ubifs/super.c @@ -1307,6 +1307,8 @@ static int mount_ubifs(struct ubifs_info *c) if (err) goto out_orphans; err = ubifs_rcvry_gc_commit(c); + if (err) + goto out_orphans; } else { err = take_gc_lnum(c); if (err) -- cgit v1.2.3 From 6da5156fad495730cca9238ead566222175b30b9 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sat, 22 May 2010 12:00:21 +0200 Subject: UBIFS: use ERR_CAST Use ERR_CAST(x) rather than ERR_PTR(PTR_ERR(x)). The former makes more clear what is the purpose of the operation, which otherwise looks like a no-op. Signed-off-by: Julia Lawall Signed-off-by: Artem Bityutskiy --- fs/ubifs/lpt.c | 14 +++++++------- fs/ubifs/lpt_commit.c | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'fs') diff --git a/fs/ubifs/lpt.c b/fs/ubifs/lpt.c index ad7f67b827ea..0084a33c4c69 100644 --- a/fs/ubifs/lpt.c +++ b/fs/ubifs/lpt.c @@ -1457,13 +1457,13 @@ struct ubifs_lprops *ubifs_lpt_lookup(struct ubifs_info *c, int lnum) shft -= UBIFS_LPT_FANOUT_SHIFT; nnode = ubifs_get_nnode(c, nnode, iip); if (IS_ERR(nnode)) - return ERR_PTR(PTR_ERR(nnode)); + return ERR_CAST(nnode); } iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1)); shft -= UBIFS_LPT_FANOUT_SHIFT; pnode = ubifs_get_pnode(c, nnode, iip); if (IS_ERR(pnode)) - return ERR_PTR(PTR_ERR(pnode)); + return ERR_CAST(pnode); iip = (i & (UBIFS_LPT_FANOUT - 1)); dbg_lp("LEB %d, free %d, dirty %d, flags %d", lnum, pnode->lprops[iip].free, pnode->lprops[iip].dirty, @@ -1586,7 +1586,7 @@ struct ubifs_lprops *ubifs_lpt_lookup_dirty(struct ubifs_info *c, int lnum) nnode = c->nroot; nnode = dirty_cow_nnode(c, nnode); if (IS_ERR(nnode)) - return ERR_PTR(PTR_ERR(nnode)); + return ERR_CAST(nnode); i = lnum - c->main_first; shft = c->lpt_hght * UBIFS_LPT_FANOUT_SHIFT; for (h = 1; h < c->lpt_hght; h++) { @@ -1594,19 +1594,19 @@ struct ubifs_lprops *ubifs_lpt_lookup_dirty(struct ubifs_info *c, int lnum) shft -= UBIFS_LPT_FANOUT_SHIFT; nnode = ubifs_get_nnode(c, nnode, iip); if (IS_ERR(nnode)) - return ERR_PTR(PTR_ERR(nnode)); + return ERR_CAST(nnode); nnode = dirty_cow_nnode(c, nnode); if (IS_ERR(nnode)) - return ERR_PTR(PTR_ERR(nnode)); + return ERR_CAST(nnode); } iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1)); shft -= UBIFS_LPT_FANOUT_SHIFT; pnode = ubifs_get_pnode(c, nnode, iip); if (IS_ERR(pnode)) - return ERR_PTR(PTR_ERR(pnode)); + return ERR_CAST(pnode); pnode = dirty_cow_pnode(c, pnode); if (IS_ERR(pnode)) - return ERR_PTR(PTR_ERR(pnode)); + return ERR_CAST(pnode); iip = (i & (UBIFS_LPT_FANOUT - 1)); dbg_lp("LEB %d, free %d, dirty %d, flags %d", lnum, pnode->lprops[iip].free, pnode->lprops[iip].dirty, diff --git a/fs/ubifs/lpt_commit.c b/fs/ubifs/lpt_commit.c index 13cb7a4237bf..d12535b7fc78 100644 --- a/fs/ubifs/lpt_commit.c +++ b/fs/ubifs/lpt_commit.c @@ -646,7 +646,7 @@ static struct ubifs_pnode *pnode_lookup(struct ubifs_info *c, int i) shft -= UBIFS_LPT_FANOUT_SHIFT; nnode = ubifs_get_nnode(c, nnode, iip); if (IS_ERR(nnode)) - return ERR_PTR(PTR_ERR(nnode)); + return ERR_CAST(nnode); } iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1)); return ubifs_get_pnode(c, nnode, iip); -- cgit v1.2.3 From 0cf5537b158caae42bcc03f0f6db10f68585b1ec Mon Sep 17 00:00:00 2001 From: Yehuda Sadeh Date: Fri, 11 Jun 2010 15:57:06 -0700 Subject: ceph: some endianity fixes Fix some problems that came up with sparse. Signed-off-by: Yehuda Sadeh Signed-off-by: Sage Weil --- fs/ceph/auth_x.c | 2 +- fs/ceph/messenger.c | 2 +- fs/ceph/mon_client.c | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/ceph/auth_x.c b/fs/ceph/auth_x.c index 83d4d2785ffe..3fe49042d8ad 100644 --- a/fs/ceph/auth_x.c +++ b/fs/ceph/auth_x.c @@ -493,7 +493,7 @@ static int ceph_x_handle_reply(struct ceph_auth_client *ac, int result, return -EAGAIN; } - op = le32_to_cpu(head->op); + op = le16_to_cpu(head->op); result = le32_to_cpu(head->result); dout("handle_reply op %d result %d\n", op, result); switch (op) { diff --git a/fs/ceph/messenger.c b/fs/ceph/messenger.c index 64b8b1f7863d..cf1c7845d877 100644 --- a/fs/ceph/messenger.c +++ b/fs/ceph/messenger.c @@ -657,7 +657,7 @@ static void prepare_write_connect(struct ceph_messenger *msgr, dout("prepare_write_connect %p cseq=%d gseq=%d proto=%d\n", con, con->connect_seq, global_seq, proto); - con->out_connect.features = CEPH_FEATURE_SUPPORTED_CLIENT; + con->out_connect.features = cpu_to_le64(CEPH_FEATURE_SUPPORTED_CLIENT); con->out_connect.host_type = cpu_to_le32(CEPH_ENTITY_TYPE_CLIENT); con->out_connect.connect_seq = cpu_to_le32(con->connect_seq); con->out_connect.global_seq = cpu_to_le32(global_seq); diff --git a/fs/ceph/mon_client.c b/fs/ceph/mon_client.c index 07a539906e67..cc115eafae11 100644 --- a/fs/ceph/mon_client.c +++ b/fs/ceph/mon_client.c @@ -725,7 +725,8 @@ static void handle_auth_reply(struct ceph_mon_client *monc, dout("authenticated, starting session\n"); monc->client->msgr->inst.name.type = CEPH_ENTITY_TYPE_CLIENT; - monc->client->msgr->inst.name.num = monc->auth->global_id; + monc->client->msgr->inst.name.num = + cpu_to_le64(monc->auth->global_id); __send_subscribe(monc); __resend_generic_request(monc); -- cgit v1.2.3 From 4a32f93d29b05cdab63c0e2979bc1524c8ea6bf5 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Sun, 13 Jun 2010 10:27:53 -0700 Subject: ceph: fix map handler error path Don't leak message if we receive an unexpected message type. Signed-off-by: Sage Weil --- fs/ceph/osd_client.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/ceph/osd_client.c b/fs/ceph/osd_client.c index d25b4add85b4..92b7251a53f1 100644 --- a/fs/ceph/osd_client.c +++ b/fs/ceph/osd_client.c @@ -1344,7 +1344,7 @@ static void dispatch(struct ceph_connection *con, struct ceph_msg *msg) int type = le16_to_cpu(msg->hdr.type); if (!osd) - return; + goto out; osdc = osd->o_osdc; switch (type) { @@ -1359,6 +1359,7 @@ static void dispatch(struct ceph_connection *con, struct ceph_msg *msg) pr_err("received unknown message type %d %s\n", type, ceph_msg_type_name(type)); } +out: ceph_msg_put(msg); } -- cgit v1.2.3 From ae32be31341a5fecfa16c5b3eb78095207182cce Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Sun, 13 Jun 2010 10:30:19 -0700 Subject: ceph: fix message memory leak, uninitialized variable We need to properly initialize skip, as not all alloc_msg op instances set it. Also, BUG if someone says skip but also allocates a message. Signed-off-by: Sage Weil --- fs/ceph/messenger.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'fs') diff --git a/fs/ceph/messenger.c b/fs/ceph/messenger.c index cf1c7845d877..9ad43a310a41 100644 --- a/fs/ceph/messenger.c +++ b/fs/ceph/messenger.c @@ -1396,10 +1396,12 @@ static int read_partial_message(struct ceph_connection *con) if (!con->in_msg) { dout("got hdr type %d front %d data %d\n", con->in_hdr.type, con->in_hdr.front_len, con->in_hdr.data_len); + skip = 0; con->in_msg = ceph_alloc_msg(con, &con->in_hdr, &skip); if (skip) { /* skip this message */ dout("alloc_msg said skip message\n"); + BUG_ON(con->in_msg); con->in_base_pos = -front_len - middle_len - data_len - sizeof(m->footer); con->in_tag = CEPH_MSGR_TAG_READY; -- cgit v1.2.3 From 9f069af5b62919151d76b37a3b168cbb34c874c3 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Wed, 19 May 2010 02:32:29 +0000 Subject: of: Drop properties with "/" in their name Some bogus firmwares include properties with "/" in their name. This causes problems when creating the /proc/device-tree file system, because the slash is taken to indicate a directory. We don't care about those properties, and we don't want to encourage them, so just throw them away when creating /proc/device-tree. Signed-off-by: Michael Ellerman Tested-by: Christian Kujau Signed-off-by: Grant Likely --- fs/proc/proc_devtree.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'fs') diff --git a/fs/proc/proc_devtree.c b/fs/proc/proc_devtree.c index ce94801f48ca..d9396a4fc7ff 100644 --- a/fs/proc/proc_devtree.c +++ b/fs/proc/proc_devtree.c @@ -209,6 +209,9 @@ void proc_device_tree_add_node(struct device_node *np, for (pp = np->properties; pp != NULL; pp = pp->next) { p = pp->name; + if (strchr(p, '/')) + continue; + if (duplicate_name(de, p)) p = fixup_name(np, de, p); -- cgit v1.2.3 From 07a038245b28df9196ffb2e8cc626e9b956a4e23 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Mon, 14 Jun 2010 09:54:48 -0400 Subject: ext4: Convert more i_flags references to use accessor functions These changes are not ones which are likely to result in races, but they should be fixed. Signed-off-by: "Theodore Ts'o" --- fs/ext4/dir.c | 3 ++- fs/ext4/inode.c | 4 ++-- fs/ext4/migrate.c | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/ext4/dir.c b/fs/ext4/dir.c index ea5e6cb7e2a5..2965c39d4183 100644 --- a/fs/ext4/dir.c +++ b/fs/ext4/dir.c @@ -121,7 +121,8 @@ static int ext4_readdir(struct file *filp, * We don't set the inode dirty flag since it's not * critical that it get flushed back to the disk. */ - ext4_clear_inode_flag(filp->f_path.dentry->d_inode, EXT4_INODE_INDEX); + ext4_clear_inode_flag(filp->f_path.dentry->d_inode, + EXT4_INODE_INDEX); } stored = 0; offset = filp->f_pos & (sb->s_blocksize - 1); diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 42272d67955a..6c6614cae9e6 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -4976,7 +4976,7 @@ static blkcnt_t ext4_inode_blocks(struct ext4_inode *raw_inode, /* we are using combined 48 bit field */ i_blocks = ((u64)le16_to_cpu(raw_inode->i_blocks_high)) << 32 | le32_to_cpu(raw_inode->i_blocks_lo); - if (ei->i_flags & EXT4_HUGE_FILE_FL) { + if (ext4_test_inode_flag(inode, EXT4_INODE_HUGE_FILE)) { /* i_blocks represent file system block size */ return i_blocks << (inode->i_blkbits - 9); } else { @@ -5126,7 +5126,7 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino) ei->i_file_acl); ret = -EIO; goto bad_inode; - } else if (ei->i_flags & EXT4_EXTENTS_FL) { + } else if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) { if (S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || (S_ISLNK(inode->i_mode) && !ext4_inode_is_fast_symlink(inode))) diff --git a/fs/ext4/migrate.c b/fs/ext4/migrate.c index 6f3a27ec30bf..1765c2c50a9b 100644 --- a/fs/ext4/migrate.c +++ b/fs/ext4/migrate.c @@ -376,7 +376,7 @@ static int ext4_ext_swap_inode_data(handle_t *handle, struct inode *inode, * We have the extent map build with the tmp inode. * Now copy the i_data across */ - ei->i_flags |= EXT4_EXTENTS_FL; + ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS); memcpy(ei->i_data, tmp_ei->i_data, sizeof(ei->i_data)); /* -- cgit v1.2.3 From 5a0790c2c4a18435759a70e1562450035d778339 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Mon, 14 Jun 2010 13:28:03 -0400 Subject: ext4: remove initialized but not read variables No real bugs found, just removed some dead code. Found by gcc 4.6's new warnings. Signed-off-by: Andi Kleen Signed-off-by: "Theodore Ts'o" --- fs/ext4/extents.c | 8 ++------ fs/ext4/inode.c | 5 ----- fs/ext4/mballoc.c | 21 ++++++--------------- fs/ext4/namei.c | 2 -- fs/ext4/resize.c | 2 -- fs/jbd2/journal.c | 12 ------------ fs/jbd2/recovery.c | 10 ++-------- 7 files changed, 10 insertions(+), 50 deletions(-) (limited to 'fs') diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index 377309c1af65..346de3daab79 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c @@ -1083,7 +1083,6 @@ static int ext4_ext_grow_indepth(handle_t *handle, struct inode *inode, { struct ext4_ext_path *curp = path; struct ext4_extent_header *neh; - struct ext4_extent_idx *fidx; struct buffer_head *bh; ext4_fsblk_t newblock; int err = 0; @@ -1144,10 +1143,10 @@ static int ext4_ext_grow_indepth(handle_t *handle, struct inode *inode, ext4_idx_store_pblock(curp->p_idx, newblock); neh = ext_inode_hdr(inode); - fidx = EXT_FIRST_INDEX(neh); ext_debug("new root: num %d(%d), lblock %d, ptr %llu\n", le16_to_cpu(neh->eh_entries), le16_to_cpu(neh->eh_max), - le32_to_cpu(fidx->ei_block), idx_pblock(fidx)); + le32_to_cpu(EXT_FIRST_INDEX(neh)->ei_block), + idx_pblock(EXT_FIRST_INDEX(neh))); neh->eh_depth = cpu_to_le16(path->p_depth + 1); err = ext4_ext_dirty(handle, inode, curp); @@ -2954,7 +2953,6 @@ static int ext4_split_unwritten_extents(handle_t *handle, struct ext4_extent *ex1 = NULL; struct ext4_extent *ex2 = NULL; struct ext4_extent *ex3 = NULL; - struct ext4_extent_header *eh; ext4_lblk_t ee_block, eof_block; unsigned int allocated, ee_len, depth; ext4_fsblk_t newblock; @@ -2971,7 +2969,6 @@ static int ext4_split_unwritten_extents(handle_t *handle, eof_block = map->m_lblk + map->m_len; depth = ext_depth(inode); - eh = path[depth].p_hdr; ex = path[depth].p_ext; ee_block = le32_to_cpu(ex->ee_block); ee_len = ext4_ext_get_actual_len(ex); @@ -3058,7 +3055,6 @@ static int ext4_split_unwritten_extents(handle_t *handle, err = PTR_ERR(path); goto out; } - eh = path[depth].p_hdr; ex = path[depth].p_ext; if (ex2 != &newex) ex2 = ex; diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 6c6614cae9e6..15ff8d8d839b 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -3146,13 +3146,10 @@ static int ext4_da_write_begin(struct file *file, struct address_space *mapping, int ret, retries = 0; struct page *page; pgoff_t index; - unsigned from, to; struct inode *inode = mapping->host; handle_t *handle; index = pos >> PAGE_CACHE_SHIFT; - from = pos & (PAGE_CACHE_SIZE - 1); - to = from + len; if (ext4_nonda_switch(inode->i_sb)) { *fsdata = (void *)FALL_BACK_TO_NONDELALLOC; @@ -5754,7 +5751,6 @@ static int ext4_expand_extra_isize(struct inode *inode, { struct ext4_inode *raw_inode; struct ext4_xattr_ibody_header *header; - struct ext4_xattr_entry *entry; if (EXT4_I(inode)->i_extra_isize >= new_extra_isize) return 0; @@ -5762,7 +5758,6 @@ static int ext4_expand_extra_isize(struct inode *inode, raw_inode = ext4_raw_inode(&iloc); header = IHDR(inode, raw_inode); - entry = IFIRST(header); /* No extended attributes present */ if (!ext4_test_inode_state(inode, EXT4_STATE_XATTR) || diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c index d9d267181ddc..b2948b047973 100644 --- a/fs/ext4/mballoc.c +++ b/fs/ext4/mballoc.c @@ -1999,7 +1999,6 @@ ext4_mb_regular_allocator(struct ext4_allocation_context *ac) ext4_group_t ngroups, group, i; int cr; int err = 0; - int bsbits; struct ext4_sb_info *sbi; struct super_block *sb; struct ext4_buddy e4b; @@ -2041,8 +2040,6 @@ ext4_mb_regular_allocator(struct ext4_allocation_context *ac) ac->ac_2order = i - 1; } - bsbits = ac->ac_sb->s_blocksize_bits; - /* if stream allocation is enabled, use global goal */ if (ac->ac_flags & EXT4_MB_STREAM_ALLOC) { /* TBD: may be hot point */ @@ -2712,7 +2709,6 @@ ext4_mb_mark_diskspace_used(struct ext4_allocation_context *ac, handle_t *handle, unsigned int reserv_blks) { struct buffer_head *bitmap_bh = NULL; - struct ext4_super_block *es; struct ext4_group_desc *gdp; struct buffer_head *gdp_bh; struct ext4_sb_info *sbi; @@ -2725,8 +2721,6 @@ ext4_mb_mark_diskspace_used(struct ext4_allocation_context *ac, sb = ac->ac_sb; sbi = EXT4_SB(sb); - es = sbi->s_es; - err = -EIO; bitmap_bh = ext4_read_block_bitmap(sb, ac->ac_b_ex.fe_group); @@ -2850,7 +2844,7 @@ ext4_mb_normalize_request(struct ext4_allocation_context *ac, int bsbits, max; ext4_lblk_t end; loff_t size, orig_size, start_off; - ext4_lblk_t start, orig_start; + ext4_lblk_t start; struct ext4_inode_info *ei = EXT4_I(ac->ac_inode); struct ext4_prealloc_space *pa; @@ -2881,6 +2875,7 @@ ext4_mb_normalize_request(struct ext4_allocation_context *ac, size = size << bsbits; if (size < i_size_read(ac->ac_inode)) size = i_size_read(ac->ac_inode); + orig_size = size; /* max size of free chunks */ max = 2 << bsbits; @@ -2922,8 +2917,8 @@ ext4_mb_normalize_request(struct ext4_allocation_context *ac, start_off = (loff_t)ac->ac_o_ex.fe_logical << bsbits; size = ac->ac_o_ex.fe_len << bsbits; } - orig_size = size = size >> bsbits; - orig_start = start = start_off >> bsbits; + size = size >> bsbits; + start = start_off >> bsbits; /* don't cover already allocated blocks in selected range */ if (ar->pleft && start <= ar->lleft) { @@ -3547,7 +3542,6 @@ ext4_mb_release_inode_pa(struct ext4_buddy *e4b, struct buffer_head *bitmap_bh, ext4_group_t group; ext4_grpblk_t bit; unsigned long long grp_blk_start; - sector_t start; int err = 0; int free = 0; @@ -3567,10 +3561,9 @@ ext4_mb_release_inode_pa(struct ext4_buddy *e4b, struct buffer_head *bitmap_bh, if (bit >= end) break; next = mb_find_next_bit(bitmap_bh->b_data, end, bit); - start = ext4_group_first_block_no(sb, group) + bit; mb_debug(1, " free preallocated %u/%u in group %u\n", - (unsigned) start, (unsigned) next - bit, - (unsigned) group); + (unsigned) ext4_group_first_block_no(sb, group) + bit, + (unsigned) next - bit, (unsigned) group); free += next - bit; if (ac) { @@ -4494,7 +4487,6 @@ void ext4_free_blocks(handle_t *handle, struct inode *inode, struct super_block *sb = inode->i_sb; struct ext4_allocation_context *ac = NULL; struct ext4_group_desc *gdp; - struct ext4_super_block *es; unsigned long freed = 0; unsigned int overflow; ext4_grpblk_t bit; @@ -4513,7 +4505,6 @@ void ext4_free_blocks(handle_t *handle, struct inode *inode, } sbi = EXT4_SB(sb); - es = EXT4_SB(sb)->s_es; if (!(flags & EXT4_FREE_BLOCKS_VALIDATED) && !ext4_data_block_valid(sbi, block, count)) { ext4_error(sb, "Freeing blocks not in datazone - " diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index a43e6617b351..5a61f77e7d7c 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -1088,7 +1088,6 @@ static struct dentry *ext4_lookup(struct inode *dir, struct dentry *dentry, stru struct dentry *ext4_get_parent(struct dentry *child) { __u32 ino; - struct inode *inode; static const struct qstr dotdot = { .name = "..", .len = 2, @@ -1097,7 +1096,6 @@ struct dentry *ext4_get_parent(struct dentry *child) struct buffer_head *bh; bh = ext4_find_entry(child->d_inode, &dotdot, &de); - inode = NULL; if (!bh) return ERR_PTR(-ENOENT); ino = le32_to_cpu(de->inode); diff --git a/fs/ext4/resize.c b/fs/ext4/resize.c index 27527ae466ea..ca5c8aa00a2f 100644 --- a/fs/ext4/resize.c +++ b/fs/ext4/resize.c @@ -952,7 +952,6 @@ int ext4_group_extend(struct super_block *sb, struct ext4_super_block *es, ext4_fsblk_t n_blocks_count) { ext4_fsblk_t o_blocks_count; - ext4_group_t o_groups_count; ext4_grpblk_t last; ext4_grpblk_t add; struct buffer_head *bh; @@ -964,7 +963,6 @@ int ext4_group_extend(struct super_block *sb, struct ext4_super_block *es, * yet: we're going to revalidate es->s_blocks_count after * taking the s_resize_lock below. */ o_blocks_count = ext4_blocks_count(es); - o_groups_count = EXT4_SB(sb)->s_groups_count; if (test_opt(sb, DEBUG)) printk(KERN_DEBUG "EXT4-fs: extending last group from %llu uto %llu blocks\n", diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c index bc2ff5932769..f7bf15787d68 100644 --- a/fs/jbd2/journal.c +++ b/fs/jbd2/journal.c @@ -1392,13 +1392,9 @@ int jbd2_journal_check_used_features (journal_t *journal, unsigned long compat, int jbd2_journal_check_available_features (journal_t *journal, unsigned long compat, unsigned long ro, unsigned long incompat) { - journal_superblock_t *sb; - if (!compat && !ro && !incompat) return 1; - sb = journal->j_superblock; - /* We can support any known requested features iff the * superblock is in version 2. Otherwise we fail to support any * extended sb features. */ @@ -1618,7 +1614,6 @@ int jbd2_journal_flush(journal_t *journal) int jbd2_journal_wipe(journal_t *journal, int write) { - journal_superblock_t *sb; int err = 0; J_ASSERT (!(journal->j_flags & JBD2_LOADED)); @@ -1627,8 +1622,6 @@ int jbd2_journal_wipe(journal_t *journal, int write) if (err) return err; - sb = journal->j_superblock; - if (!journal->j_tail) goto no_recovery; @@ -2202,8 +2195,6 @@ void jbd2_journal_init_jbd_inode(struct jbd2_inode *jinode, struct inode *inode) void jbd2_journal_release_jbd_inode(journal_t *journal, struct jbd2_inode *jinode) { - int writeout = 0; - if (!journal) return; restart: @@ -2220,9 +2211,6 @@ restart: goto restart; } - /* Do we need to wait for data writeback? */ - if (journal->j_committing_transaction == jinode->i_transaction) - writeout = 1; if (jinode->i_transaction) { list_del(&jinode->i_list); jinode->i_transaction = NULL; diff --git a/fs/jbd2/recovery.c b/fs/jbd2/recovery.c index 049281b7cb89..2bc4d5f116f1 100644 --- a/fs/jbd2/recovery.c +++ b/fs/jbd2/recovery.c @@ -285,12 +285,10 @@ int jbd2_journal_recover(journal_t *journal) int jbd2_journal_skip_recovery(journal_t *journal) { int err; - journal_superblock_t * sb; struct recovery_info info; memset (&info, 0, sizeof(info)); - sb = journal->j_superblock; err = do_one_pass(journal, &info, PASS_SCAN); @@ -299,7 +297,8 @@ int jbd2_journal_skip_recovery(journal_t *journal) ++journal->j_transaction_sequence; } else { #ifdef CONFIG_JBD2_DEBUG - int dropped = info.end_transaction - be32_to_cpu(sb->s_sequence); + int dropped = info.end_transaction - + be32_to_cpu(journal->j_superblock->s_sequence); #endif jbd_debug(1, "JBD: ignoring %d transaction%s from the journal.\n", @@ -365,11 +364,6 @@ static int do_one_pass(journal_t *journal, int tag_bytes = journal_tag_bytes(journal); __u32 crc32_sum = ~0; /* Transactional Checksums */ - /* Precompute the maximum metadata descriptors in a descriptor block */ - int MAX_BLOCKS_PER_DESC; - MAX_BLOCKS_PER_DESC = ((journal->j_blocksize-sizeof(journal_header_t)) - / tag_bytes); - /* * First thing is to establish what we expect to find in the log * (in terms of transaction IDs), and where (in terms of log -- cgit v1.2.3 From 206f7ab4f49a2021fcb8687f25395be77711ddee Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 14 Jun 2010 14:42:49 -0400 Subject: ext4: remove vestiges of nobh support The nobh option was only supported for writeback mode, but given that all write paths actually create buffer heads it effectively was a no-op already. Signed-off-by: Christoph Hellwig Signed-off-by: "Theodore Ts'o" --- fs/ext4/ext4.h | 1 - fs/ext4/ext4_jbd2.h | 10 ++++------ fs/ext4/inode.c | 27 ++++++--------------------- fs/ext4/super.c | 21 +++++---------------- 4 files changed, 15 insertions(+), 44 deletions(-) (limited to 'fs') diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 8b56b53328eb..8b6d297c8c73 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -873,7 +873,6 @@ struct ext4_inode_info { #define EXT4_MOUNT_POSIX_ACL 0x08000 /* POSIX Access Control Lists */ #define EXT4_MOUNT_NO_AUTO_DA_ALLOC 0x10000 /* No auto delalloc mapping */ #define EXT4_MOUNT_BARRIER 0x20000 /* Use block barriers */ -#define EXT4_MOUNT_NOBH 0x40000 /* No bufferheads */ #define EXT4_MOUNT_QUOTA 0x80000 /* Some quota option set */ #define EXT4_MOUNT_USRQUOTA 0x100000 /* "old" user quota */ #define EXT4_MOUNT_GRPQUOTA 0x200000 /* "old" group quota */ diff --git a/fs/ext4/ext4_jbd2.h b/fs/ext4/ext4_jbd2.h index 8ae8168900bf..38d1e66e5843 100644 --- a/fs/ext4/ext4_jbd2.h +++ b/fs/ext4/ext4_jbd2.h @@ -313,17 +313,15 @@ static inline int ext4_should_writeback_data(struct inode *inode) * This function controls whether or not we should try to go down the * dioread_nolock code paths, which makes it safe to avoid taking * i_mutex for direct I/O reads. This only works for extent-based - * files, and it doesn't work for nobh or if data journaling is - * enabled, since the dioread_nolock code uses b_private to pass - * information back to the I/O completion handler, and this conflicts - * with the jbd's use of b_private. + * files, and it doesn't work if data journaling is enabled, since the + * dioread_nolock code uses b_private to pass information back to the + * I/O completion handler, and this conflicts with the jbd's use of + * b_private. */ static inline int ext4_should_dioread_nolock(struct inode *inode) { if (!test_opt(inode->i_sb, DIOREAD_NOLOCK)) return 0; - if (test_opt(inode->i_sb, NOBH)) - return 0; if (!S_ISREG(inode->i_mode)) return 0; if (!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))) diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 15ff8d8d839b..b485987f0146 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -2553,18 +2553,16 @@ static int ext4_da_get_block_prep(struct inode *inode, sector_t iblock, /* * This function is used as a standard get_block_t calback function * when there is no desire to allocate any blocks. It is used as a - * callback function for block_prepare_write(), nobh_writepage(), and - * block_write_full_page(). These functions should only try to map a - * single block at a time. + * callback function for block_prepare_write() and block_write_full_page(). + * These functions should only try to map a single block at a time. * * Since this function doesn't do block allocations even if the caller * requests it by passing in create=1, it is critically important that * any caller checks to make sure that any buffer heads are returned * by this function are either all already mapped or marked for - * delayed allocation before calling nobh_writepage() or - * block_write_full_page(). Otherwise, b_blocknr could be left - * unitialized, and the page write functions will be taken by - * surprise. + * delayed allocation before calling block_write_full_page(). Otherwise, + * b_blocknr could be left unitialized, and the page write functions will + * be taken by surprise. */ static int noalloc_get_block_write(struct inode *inode, sector_t iblock, struct buffer_head *bh_result, int create) @@ -2749,9 +2747,7 @@ static int ext4_writepage(struct page *page, return __ext4_journalled_writepage(page, len); } - if (test_opt(inode->i_sb, NOBH) && ext4_should_writeback_data(inode)) - ret = nobh_writepage(page, noalloc_get_block_write, wbc); - else if (page_bufs && buffer_uninit(page_bufs)) { + if (page_bufs && buffer_uninit(page_bufs)) { ext4_set_bh_endio(page_bufs, inode); ret = block_write_full_page_endio(page, noalloc_get_block_write, wbc, ext4_end_io_buffer_write); @@ -4125,17 +4121,6 @@ int ext4_block_truncate_page(handle_t *handle, length = blocksize - (offset & (blocksize - 1)); iblock = index << (PAGE_CACHE_SHIFT - inode->i_sb->s_blocksize_bits); - /* - * For "nobh" option, we can only work if we don't need to - * read-in the page - otherwise we create buffers to do the IO. - */ - if (!page_has_buffers(page) && test_opt(inode->i_sb, NOBH) && - ext4_should_writeback_data(inode) && PageUptodate(page)) { - zero_user(page, offset, length); - set_page_dirty(page); - goto unlock; - } - if (!page_has_buffers(page)) create_empty_buffers(page, blocksize, 0); diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 4e8983a9811b..422a4ce66778 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -946,8 +946,6 @@ static int ext4_show_options(struct seq_file *seq, struct vfsmount *vfs) seq_puts(seq, ",journal_async_commit"); else if (test_opt(sb, JOURNAL_CHECKSUM)) seq_puts(seq, ",journal_checksum"); - if (test_opt(sb, NOBH)) - seq_puts(seq, ",nobh"); if (test_opt(sb, I_VERSION)) seq_puts(seq, ",i_version"); if (!test_opt(sb, DELALLOC)) @@ -1624,10 +1622,12 @@ set_qf_format: *n_blocks_count = option; break; case Opt_nobh: - set_opt(sbi->s_mount_opt, NOBH); + ext4_msg(sb, KERN_WARNING, + "Ignoring deprecated nobh option"); break; case Opt_bh: - clear_opt(sbi->s_mount_opt, NOBH); + ext4_msg(sb, KERN_WARNING, + "Ignoring deprecated bh option"); break; case Opt_i_version: set_opt(sbi->s_mount_opt, I_VERSION); @@ -2912,18 +2912,7 @@ no_journal: ext4_msg(sb, KERN_ERR, "insufficient memory"); goto failed_mount_wq; } - if (test_opt(sb, NOBH)) { - if (!(test_opt(sb, DATA_FLAGS) == EXT4_MOUNT_WRITEBACK_DATA)) { - ext4_msg(sb, KERN_WARNING, "Ignoring nobh option - " - "its supported only with writeback mode"); - clear_opt(sbi->s_mount_opt, NOBH); - } - if (test_opt(sb, DIOREAD_NOLOCK)) { - ext4_msg(sb, KERN_WARNING, "dioread_nolock option is " - "not supported with nobh mode"); - goto failed_mount_wq; - } - } + EXT4_SB(sb)->dio_unwritten_wq = create_workqueue("ext4-dio-unwritten"); if (!EXT4_SB(sb)->dio_unwritten_wq) { printk(KERN_ERR "EXT4-fs: failed to create DIO workqueue\n"); -- cgit v1.2.3 From b97181f24212f4c29197890ce1b2b9100bcc184d Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Mon, 10 May 2010 17:09:25 -0700 Subject: fs: remove all rcu head initializations, except on_stack initializations Remove all rcu head inits. We don't care about the RCU head state before passing it to call_rcu() anyway. Only leave the "on_stack" variants so debugobjects can keep track of objects on stack. Signed-off-by: Alexey Dobriyan Signed-off-by: Mathieu Desnoyers Signed-off-by: Paul E. McKenney Cc: Alexander Viro Cc: Andries Brouwer --- fs/file.c | 3 --- fs/fs-writeback.c | 31 ++++++++++++++++++++++++++----- fs/partitions/check.c | 1 - 3 files changed, 26 insertions(+), 9 deletions(-) (limited to 'fs') diff --git a/fs/file.c b/fs/file.c index 34bb7f71d994..cccaead962c2 100644 --- a/fs/file.c +++ b/fs/file.c @@ -178,7 +178,6 @@ static struct fdtable * alloc_fdtable(unsigned int nr) fdt->open_fds = (fd_set *)data; data += nr / BITS_PER_BYTE; fdt->close_on_exec = (fd_set *)data; - INIT_RCU_HEAD(&fdt->rcu); fdt->next = NULL; return fdt; @@ -312,7 +311,6 @@ struct files_struct *dup_fd(struct files_struct *oldf, int *errorp) new_fdt->close_on_exec = (fd_set *)&newf->close_on_exec_init; new_fdt->open_fds = (fd_set *)&newf->open_fds_init; new_fdt->fd = &newf->fd_array[0]; - INIT_RCU_HEAD(&new_fdt->rcu); new_fdt->next = NULL; spin_lock(&oldf->file_lock); @@ -430,7 +428,6 @@ struct files_struct init_files = { .fd = &init_files.fd_array[0], .close_on_exec = (fd_set *)&init_files.close_on_exec_init, .open_fds = (fd_set *)&init_files.open_fds_init, - .rcu = RCU_HEAD_INIT, }, .file_lock = __SPIN_LOCK_UNLOCKED(init_task.file_lock), }; diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 1d1088f48bc2..af92100a7411 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -75,12 +75,33 @@ static inline bool bdi_work_on_stack(struct bdi_work *work) return test_bit(WS_ONSTACK_B, &work->state); } -static inline void bdi_work_init(struct bdi_work *work, - struct wb_writeback_args *args) +static inline void __bdi_work_init(struct bdi_work *work, + struct wb_writeback_args *args, + int on_stack) { - INIT_RCU_HEAD(&work->rcu_head); work->args = *args; work->state = WS_USED; + if (on_stack) { + work->state |= WS_ONSTACK; + init_rcu_head_on_stack(&work->rcu_head); + } +} + +static inline void bdi_work_init(struct bdi_work *work, + struct wb_writeback_args *args) +{ + __bdi_work_init(work, args, false); +} + +static inline void bdi_work_init_on_stack(struct bdi_work *work, + struct wb_writeback_args *args) +{ + __bdi_work_init(work, args, true); +} + +static inline void bdi_destroy_work_on_stack(struct bdi_work *work) +{ + destroy_rcu_head_on_stack(&work->rcu_head); } /** @@ -233,11 +254,11 @@ static void bdi_sync_writeback(struct backing_dev_info *bdi, }; struct bdi_work work; - bdi_work_init(&work, &args); - work.state |= WS_ONSTACK; + bdi_work_init_on_stack(&work, &args); bdi_queue_work(bdi, &work); bdi_wait_on_work_clear(&work); + bdi_destroy_work_on_stack(&work); } /** diff --git a/fs/partitions/check.c b/fs/partitions/check.c index 5dcd4b0c5533..72c52656dc2e 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c @@ -459,7 +459,6 @@ struct hd_struct *add_partition(struct gendisk *disk, int partno, } /* everything is up and running, commence */ - INIT_RCU_HEAD(&p->rcu_head); rcu_assign_pointer(ptbl->part[partno], p); /* suppress uevent if the disk supresses it */ -- cgit v1.2.3 From c6ac12a6159c802ae8b757dd13563564e64333df Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Tue, 15 Jun 2010 12:19:59 -0400 Subject: ext4: update ctime when changing the file's permission by setfacl ext4 didn't update the ctime of the file when its permission was changed. Steps to reproduce: # touch aaa # stat -c %Z aaa 1275289822 # setfacl -m 'u::x,g::x,o::x' aaa # stat -c %Z aaa 1275289822 <- unchanged But, according to the spec of the ctime, ext4 must update it. Port of ext3 patch by Miao Xie . CC: linux-ext4@vger.kernel.org Signed-off-by: Jan Kara Signed-off-by: "Theodore Ts'o" --- fs/ext4/acl.c | 1 + 1 file changed, 1 insertion(+) (limited to 'fs') diff --git a/fs/ext4/acl.c b/fs/ext4/acl.c index feaf498feaa6..5e2ed4504ead 100644 --- a/fs/ext4/acl.c +++ b/fs/ext4/acl.c @@ -204,6 +204,7 @@ ext4_set_acl(handle_t *handle, struct inode *inode, int type, return error; else { inode->i_mode = mode; + inode->i_ctime = ext4_current_time(inode); ext4_mark_inode_dirty(handle, inode); if (error == 0) acl = NULL; -- cgit v1.2.3 From 6469272c350872980891dbe38e81c936c43f2d9b Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Wed, 26 May 2010 17:58:53 +0200 Subject: fs/ocfs2/dlm: Add missing spin_unlock Add a spin_unlock missing on the error path. Unlock as in the other code that leads to the leave label. The semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ expression E1; @@ * spin_lock(E1,...); <+... when != E1 if (...) { ... when != E1 * return ...; } ...+> * spin_unlock(E1,...); // Signed-off-by: Julia Lawall Signed-off-by: Joel Becker --- fs/ocfs2/dlm/dlmdomain.c | 1 + 1 file changed, 1 insertion(+) (limited to 'fs') diff --git a/fs/ocfs2/dlm/dlmdomain.c b/fs/ocfs2/dlm/dlmdomain.c index 6b5a492e1749..2ccad86fb590 100644 --- a/fs/ocfs2/dlm/dlmdomain.c +++ b/fs/ocfs2/dlm/dlmdomain.c @@ -1709,6 +1709,7 @@ retry: } if (dlm_protocol_compare(&dlm->fs_locking_proto, fs_proto)) { + spin_unlock(&dlm_domain_lock); mlog(ML_ERROR, "Requested locking protocol version is not " "compatible with already registered domain " -- cgit v1.2.3 From 40f165f416bde747d85cdf71bc9dde700912f71f Mon Sep 17 00:00:00 2001 From: Tao Ma Date: Fri, 28 May 2010 14:22:59 +0800 Subject: ocfs2: Move orphan scan work to ocfs2_wq. We used to let orphan scan work in the default work queue, but there is a corner case which will make the system deadlock. The scenario is like this: 1. set heartbeat threadshold to 200. this will allow us to have a great chance to have a orphan scan work before our quorum decision. 2. mount node 1. 3. after 1~2 minutes, mount node 2(in order to make the bug easier to reproduce, better add maxcpus=1 to kernel command line). 4. node 1 do orphan scan work. 5. node 2 do orphan scan work. 6. node 1 do orphan scan work. After this, node 1 hold the orphan scan lock while node 2 know node 1 is the master. 7. ifdown eth2 in node 2(eth2 is what we do ocfs2 interconnection). Now when node 2 begins orphan scan, the system queue is blocked. The root cause is that both orphan scan work and quorum decision work will use the system event work queue. orphan scan has a chance of blocking the event work queue(in dlm_wait_for_node_death) so that there is no chance for quorum decision work to proceed. This patch resolve it by moving orphan scan work to ocfs2_wq. Signed-off-by: Tao Ma Signed-off-by: Joel Becker --- fs/ocfs2/journal.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/ocfs2/journal.c b/fs/ocfs2/journal.c index 47878cf16418..39113b5e79e7 100644 --- a/fs/ocfs2/journal.c +++ b/fs/ocfs2/journal.c @@ -1936,7 +1936,7 @@ void ocfs2_orphan_scan_work(struct work_struct *work) mutex_lock(&os->os_lock); ocfs2_queue_orphan_scan(osb); if (atomic_read(&os->os_state) == ORPHAN_SCAN_ACTIVE) - schedule_delayed_work(&os->os_orphan_scan_work, + queue_delayed_work(ocfs2_wq, &os->os_orphan_scan_work, ocfs2_orphan_scan_timeout()); mutex_unlock(&os->os_lock); } @@ -1976,8 +1976,8 @@ void ocfs2_orphan_scan_start(struct ocfs2_super *osb) atomic_set(&os->os_state, ORPHAN_SCAN_INACTIVE); else { atomic_set(&os->os_state, ORPHAN_SCAN_ACTIVE); - schedule_delayed_work(&os->os_orphan_scan_work, - ocfs2_orphan_scan_timeout()); + queue_delayed_work(ocfs2_wq, &os->os_orphan_scan_work, + ocfs2_orphan_scan_timeout()); } } -- cgit v1.2.3 From 1739da40543ed2129050ccfa8a076a851ab6ed00 Mon Sep 17 00:00:00 2001 From: Tao Ma Date: Wed, 9 Jun 2010 16:43:05 +0800 Subject: ocfs2: Limit default local alloc size within bitmap range. In commit 6b82021b9e91cd689fdffadbcdb9a42597bbe764, we increase our local alloc size and calculate how much megabytes we can get according to group size and volume size. But we also need to check the maximum bits a local alloc block bitmap can have. With a bs=512, cs=32K, local volume with 160G, it calculate 96MB while the maximum local alloc size is only 76M. So the bitmap will overflow and corrupt the system truncate log file. See bug http://oss.oracle.com/bugzilla/show_bug.cgi?id=1262 Signed-off-by: Tao Ma Acked-by: Mark Fasheh Signed-off-by: Joel Becker --- fs/ocfs2/localalloc.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'fs') diff --git a/fs/ocfs2/localalloc.c b/fs/ocfs2/localalloc.c index 3d7419682dc0..ec6adbf8f551 100644 --- a/fs/ocfs2/localalloc.c +++ b/fs/ocfs2/localalloc.c @@ -118,6 +118,7 @@ unsigned int ocfs2_la_default_mb(struct ocfs2_super *osb) { unsigned int la_mb; unsigned int gd_mb; + unsigned int la_max_mb; unsigned int megs_per_slot; struct super_block *sb = osb->sb; @@ -182,6 +183,12 @@ unsigned int ocfs2_la_default_mb(struct ocfs2_super *osb) if (megs_per_slot < la_mb) la_mb = megs_per_slot; + /* We can't store more bits than we can in a block. */ + la_max_mb = ocfs2_clusters_to_megabytes(osb->sb, + ocfs2_local_alloc_size(sb) * 8); + if (la_mb > la_max_mb) + la_mb = la_max_mb; + return la_mb; } -- cgit v1.2.3 From 421f91d21ad6f799dc7b489bb33cc560ccc56f98 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 11 Jun 2010 12:17:00 +0200 Subject: fix typos concerning "initiali[zs]e" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Uwe Kleine-König Signed-off-by: Jiri Kosina --- arch/arm/mach-msm/acpuclock-arm11.c | 4 ++-- arch/arm/mach-u300/gpio.c | 2 +- arch/arm/plat-s3c24xx/clock.c | 2 +- arch/arm/plat-samsung/clock.c | 2 +- arch/h8300/kernel/timer/itu.c | 2 +- arch/h8300/kernel/timer/timer16.c | 2 +- arch/h8300/kernel/timer/timer8.c | 2 +- arch/ia64/kvm/kvm-ia64.c | 6 +++--- arch/ia64/sn/kernel/setup.c | 2 +- arch/sparc/boot/btfixupprep.c | 4 ++-- arch/x86/kernel/apic/apic.c | 2 +- arch/x86/kernel/head32.c | 2 +- drivers/crypto/amcc/crypto4xx_reg_def.h | 2 +- drivers/dma/at_hdmac.c | 2 +- drivers/gpu/drm/savage/savage_bci.c | 2 +- drivers/ide/ide-gd.c | 2 +- drivers/infiniband/hw/ehca/hcp_if.h | 2 +- drivers/input/misc/ad714x.c | 2 +- drivers/media/video/ov511.c | 2 +- drivers/media/video/zoran/zoran.h | 2 +- drivers/media/video/zoran/zr36050.c | 2 +- drivers/media/video/zoran/zr36060.c | 2 +- drivers/message/fusion/mptbase.c | 4 ++-- drivers/mtd/nand/denali.c | 2 +- drivers/net/3c527.c | 4 ++-- drivers/net/appletalk/ipddp.c | 2 +- drivers/net/hp100.c | 2 +- drivers/net/ibm_newemac/core.c | 2 +- drivers/net/ksz884x.c | 2 +- drivers/net/ll_temac_main.c | 2 +- drivers/net/tulip/dmfe.c | 20 ++++++++++---------- drivers/net/wimax/i2400m/control.c | 2 +- drivers/parisc/ccio-dma.c | 4 ++-- drivers/pcmcia/sa11xx_base.c | 2 +- drivers/scsi/advansys.c | 2 +- drivers/scsi/aic94xx/aic94xx_seq.c | 4 ++-- drivers/scsi/bfa/vport.c | 2 +- drivers/scsi/pm8001/pm8001_hwi.c | 2 +- drivers/scsi/qla4xxx/ql4_init.c | 2 +- drivers/serial/sn_console.c | 6 +++--- drivers/staging/comedi/drivers/usbdux.c | 2 +- drivers/staging/octeon/cvmx-cmd-queue.c | 6 +++--- drivers/staging/pohmelfs/inode.c | 2 +- drivers/staging/rt2860/common/cmm_wpa.c | 4 ++-- drivers/staging/rtl8192e/r8190_rtl8256.c | 6 +++--- drivers/usb/serial/kl5kusb105.c | 2 +- drivers/usb/wusbcore/wusbhc.c | 2 +- drivers/uwb/wlp/wss-lc.c | 2 +- drivers/video/carminefb.c | 2 +- drivers/video/tgafb.c | 2 +- fs/befs/linuxvfs.c | 2 +- fs/ecryptfs/crypto.c | 2 +- fs/ext4/extents.c | 2 +- fs/ext4/super.c | 2 +- fs/freevxfs/vxfs_super.c | 2 +- fs/ocfs2/super.c | 2 +- fs/reiserfs/inode.c | 2 +- lib/random32.c | 2 +- net/netfilter/ipvs/ip_vs_lblc.c | 2 +- net/netfilter/ipvs/ip_vs_lblcr.c | 2 +- net/sctp/associola.c | 2 +- net/sctp/protocol.c | 2 +- security/smack/smack_lsm.c | 2 +- sound/pci/trident/trident_main.c | 2 +- sound/soc/fsl/mpc8610_hpcd.c | 2 +- sound/soc/soc-core.c | 2 +- 66 files changed, 90 insertions(+), 90 deletions(-) (limited to 'fs') diff --git a/arch/arm/mach-msm/acpuclock-arm11.c b/arch/arm/mach-msm/acpuclock-arm11.c index af5e85b91d02..f060a3959a75 100644 --- a/arch/arm/mach-msm/acpuclock-arm11.c +++ b/arch/arm/mach-msm/acpuclock-arm11.c @@ -98,7 +98,7 @@ struct clkctl_acpu_speed { /* * ACPU speed table. Complete table is shown but certain speeds are commented - * out to optimized speed switching. Initalize loops_per_jiffy to 0. + * out to optimized speed switching. Initialize loops_per_jiffy to 0. * * Table stepping up/down is optimized for 256mhz jumps while staying on the * same PLL. @@ -494,7 +494,7 @@ uint32_t acpuclk_get_switch_time(void) * Clock driver initialization *---------------------------------------------------------------------------*/ -/* Initalize the lpj field in the acpu_freq_tbl. */ +/* Initialize the lpj field in the acpu_freq_tbl. */ static void __init lpj_init(void) { int i; diff --git a/arch/arm/mach-u300/gpio.c b/arch/arm/mach-u300/gpio.c index 5f61fd45a0c8..d92790140fe5 100644 --- a/arch/arm/mach-u300/gpio.c +++ b/arch/arm/mach-u300/gpio.c @@ -523,7 +523,7 @@ static void gpio_set_initial_values(void) /* * Put all pins that are set to either 'GPIO_OUT' or 'GPIO_NOT_USED' - * to output and 'GPIO_IN' to input for each port. And initalize + * to output and 'GPIO_IN' to input for each port. And initialize * default value on outputs. */ for (i = 0; i < U300_GPIO_NUM_PORTS; i++) { diff --git a/arch/arm/plat-s3c24xx/clock.c b/arch/arm/plat-s3c24xx/clock.c index 8474d05274bd..931d26d1a54b 100644 --- a/arch/arm/plat-s3c24xx/clock.c +++ b/arch/arm/plat-s3c24xx/clock.c @@ -43,7 +43,7 @@ #include #include -/* initalise all the clocks */ +/* initialise all the clocks */ void __init_or_cpufreq s3c24xx_setup_clocks(unsigned long fclk, unsigned long hclk, diff --git a/arch/arm/plat-samsung/clock.c b/arch/arm/plat-samsung/clock.c index 8bf79f3efdfb..90a20512d68d 100644 --- a/arch/arm/plat-samsung/clock.c +++ b/arch/arm/plat-samsung/clock.c @@ -391,7 +391,7 @@ void __init s3c_disable_clocks(struct clk *clkp, int nr_clks) (clkp->enable)(clkp, 0); } -/* initalise all the clocks */ +/* initialise all the clocks */ int __init s3c24xx_register_baseclocks(unsigned long xtal) { diff --git a/arch/h8300/kernel/timer/itu.c b/arch/h8300/kernel/timer/itu.c index 4883ba7103a8..a2ae5e952137 100644 --- a/arch/h8300/kernel/timer/itu.c +++ b/arch/h8300/kernel/timer/itu.c @@ -73,7 +73,7 @@ void __init h8300_timer_setup(void) setup_irq(ITUIRQ, &itu_irq); - /* initalize timer */ + /* initialize timer */ ctrl_outb(0, TSTR); ctrl_outb(CCLR0 | div, ITUBASE + TCR); ctrl_outb(0x01, ITUBASE + TIER); diff --git a/arch/h8300/kernel/timer/timer16.c b/arch/h8300/kernel/timer/timer16.c index 042dbb53f3fb..ae0d38161139 100644 --- a/arch/h8300/kernel/timer/timer16.c +++ b/arch/h8300/kernel/timer/timer16.c @@ -68,7 +68,7 @@ void __init h8300_timer_setup(void) setup_irq(_16IRQ, &timer16_irq); - /* initalize timer */ + /* initialize timer */ ctrl_outb(0, TSTR); ctrl_outb(CCLR0 | div, _16BASE + TCR); ctrl_outw(cnt, _16BASE + GRA); diff --git a/arch/h8300/kernel/timer/timer8.c b/arch/h8300/kernel/timer/timer8.c index 38be0cabef0d..3946c0fa8374 100644 --- a/arch/h8300/kernel/timer/timer8.c +++ b/arch/h8300/kernel/timer/timer8.c @@ -94,7 +94,7 @@ void __init h8300_timer_setup(void) ctrl_bclr(0, MSTPCRL) #endif - /* initalize timer */ + /* initialize timer */ ctrl_outw(cnt, _8BASE + TCORA); ctrl_outw(0x0000, _8BASE + _8TCSR); ctrl_outw((CMIEA|CCLR_CMA|CKS2) << 8 | div, diff --git a/arch/ia64/kvm/kvm-ia64.c b/arch/ia64/kvm/kvm-ia64.c index 7f3c0a2e60cd..29afd9a252ff 100644 --- a/arch/ia64/kvm/kvm-ia64.c +++ b/arch/ia64/kvm/kvm-ia64.c @@ -1234,7 +1234,7 @@ int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu) p_ctx->cr[2] = (unsigned long)kvm_vmm_info->vmm_ivt; p_ctx->cr[8] = 0x3c; - /*Initilize region register*/ + /*Initialize region register*/ p_ctx->rr[0] = 0x30; p_ctx->rr[1] = 0x30; p_ctx->rr[2] = 0x30; @@ -1243,7 +1243,7 @@ int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu) p_ctx->rr[5] = 0x30; p_ctx->rr[7] = 0x30; - /*Initilize branch register 0*/ + /*Initialize branch register 0*/ p_ctx->br[0] = *(unsigned long *)kvm_vmm_info->vmm_entry; vcpu->arch.vmm_rr = kvm->arch.vmm_init_rr; @@ -1702,7 +1702,7 @@ static int kvm_relocate_vmm(struct kvm_vmm_info *vmm_info, BUG_ON(!module); if (!kvm_vmm_base) { - printk("kvm: kvm area hasn't been initilized yet!!\n"); + printk("kvm: kvm area hasn't been initialized yet!!\n"); return -EFAULT; } diff --git a/arch/ia64/sn/kernel/setup.c b/arch/ia64/sn/kernel/setup.c index d00dfc180021..dbc4cbecb5ed 100644 --- a/arch/ia64/sn/kernel/setup.c +++ b/arch/ia64/sn/kernel/setup.c @@ -507,7 +507,7 @@ static void __init sn_init_pdas(char **cmdline_p) cnodeid_t cnode; /* - * Allocate & initalize the nodepda for each node. + * Allocate & initialize the nodepda for each node. */ for_each_online_node(cnode) { nodepdaindr[cnode] = diff --git a/arch/sparc/boot/btfixupprep.c b/arch/sparc/boot/btfixupprep.c index bbf91b9c3d39..b60491102237 100644 --- a/arch/sparc/boot/btfixupprep.c +++ b/arch/sparc/boot/btfixupprep.c @@ -216,7 +216,7 @@ main1: switch (buffer[nbase+3]) { case 'f': if (initval) { - fprintf(stderr, "Cannot use pre-initalized fixups for calls\n%s\n", buffer); + fprintf(stderr, "Cannot use pre-initialized fixups for calls\n%s\n", buffer); exit(1); } if (!strcmp (sect, "__ksymtab")) { @@ -273,7 +273,7 @@ main1: break; case 'i': if (initval) { - fprintf(stderr, "Cannot use pre-initalized fixups for INT\n%s\n", buffer); + fprintf(stderr, "Cannot use pre-initialized fixups for INT\n%s\n", buffer); exit(1); } if (strncmp (buffer + mode+9, "HI22 ", 10) && strncmp (buffer + mode+9, "LO10 ", 10)) { diff --git a/arch/x86/kernel/apic/apic.c b/arch/x86/kernel/apic/apic.c index e5a4a1e01618..192cd7ee35cc 100644 --- a/arch/x86/kernel/apic/apic.c +++ b/arch/x86/kernel/apic/apic.c @@ -459,7 +459,7 @@ static void lapic_timer_broadcast(const struct cpumask *mask) } /* - * Setup the local APIC timer for this CPU. Copy the initilized values + * Setup the local APIC timer for this CPU. Copy the initialized values * of the boot CPU and register the clock event in the framework. */ static void __cpuinit setup_APIC_timer(void) diff --git a/arch/x86/kernel/head32.c b/arch/x86/kernel/head32.c index b2e246037392..784360c0625c 100644 --- a/arch/x86/kernel/head32.c +++ b/arch/x86/kernel/head32.c @@ -20,7 +20,7 @@ static void __init i386_default_early_setup(void) { - /* Initilize 32bit specific setup functions */ + /* Initialize 32bit specific setup functions */ x86_init.resources.probe_roms = probe_roms; x86_init.resources.reserve_resources = i386_reserve_resources; x86_init.mpparse.setup_ioapic_ids = setup_ioapic_ids_from_mpc; diff --git a/drivers/crypto/amcc/crypto4xx_reg_def.h b/drivers/crypto/amcc/crypto4xx_reg_def.h index 7d4edb002619..5f5fbc0716ff 100644 --- a/drivers/crypto/amcc/crypto4xx_reg_def.h +++ b/drivers/crypto/amcc/crypto4xx_reg_def.h @@ -113,7 +113,7 @@ #define CRYPTO4XX_PRNG_LFSR_H 0x00070034 /** - * Initilize CRYPTO ENGINE registers, and memory bases. + * Initialize CRYPTO ENGINE registers, and memory bases. */ #define PPC4XX_PDR_POLL 0x3ff #define PPC4XX_OUTPUT_THRESHOLD 2 diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index 278cf5bceef2..308ab320e20b 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -69,7 +69,7 @@ static struct at_desc *atc_first_queued(struct at_dma_chan *atchan) } /** - * atc_alloc_descriptor - allocate and return an initilized descriptor + * atc_alloc_descriptor - allocate and return an initialized descriptor * @chan: the channel to allocate descriptors for * @gfp_flags: GFP allocation flags * diff --git a/drivers/gpu/drm/savage/savage_bci.c b/drivers/gpu/drm/savage/savage_bci.c index 2d0c9ca484c5..fa05cda8c98f 100644 --- a/drivers/gpu/drm/savage/savage_bci.c +++ b/drivers/gpu/drm/savage/savage_bci.c @@ -552,7 +552,7 @@ int savage_driver_load(struct drm_device *dev, unsigned long chipset) /* - * Initalize mappings. On Savage4 and SavageIX the alignment + * Initialize mappings. On Savage4 and SavageIX the alignment * and size of the aperture is not suitable for automatic MTRR setup * in drm_addmap. Therefore we add them manually before the maps are * initialized, and tear them down on last close. diff --git a/drivers/ide/ide-gd.c b/drivers/ide/ide-gd.c index c32d83996ae1..27d9fe08d80b 100644 --- a/drivers/ide/ide-gd.c +++ b/drivers/ide/ide-gd.c @@ -92,7 +92,7 @@ static void ide_disk_release(struct device *dev) /* * On HPA drives the capacity needs to be - * reinitilized on resume otherwise the disk + * reinitialized on resume otherwise the disk * can not be used and a hard reset is required */ static void ide_gd_resume(ide_drive_t *drive) diff --git a/drivers/infiniband/hw/ehca/hcp_if.h b/drivers/infiniband/hw/ehca/hcp_if.h index 39c1c3618ec7..a46e514c367b 100644 --- a/drivers/infiniband/hw/ehca/hcp_if.h +++ b/drivers/infiniband/hw/ehca/hcp_if.h @@ -49,7 +49,7 @@ #include "hipz_hw.h" /* - * hipz_h_alloc_resource_eq allocates EQ resources in HW and FW, initalize + * hipz_h_alloc_resource_eq allocates EQ resources in HW and FW, initialize * resources, create the empty EQPT (ring). */ u64 hipz_h_alloc_resource_eq(const struct ipz_adapter_handle adapter_handle, diff --git a/drivers/input/misc/ad714x.c b/drivers/input/misc/ad714x.c index 0fe27baf5e72..c431d09e401a 100644 --- a/drivers/input/misc/ad714x.c +++ b/drivers/input/misc/ad714x.c @@ -1118,7 +1118,7 @@ struct ad714x_chip *ad714x_probe(struct device *dev, u16 bus_type, int irq, if (error) goto err_free_mem; - /* initilize and request sw/hw resources */ + /* initialize and request sw/hw resources */ ad714x_hw_init(ad714x); mutex_init(&ad714x->mutex); diff --git a/drivers/media/video/ov511.c b/drivers/media/video/ov511.c index a10912097b7a..78a6eb698b0a 100644 --- a/drivers/media/video/ov511.c +++ b/drivers/media/video/ov511.c @@ -4808,7 +4808,7 @@ ov7xx0_configure(struct usb_ov511 *ov) return -1; if (init_ov_sensor(ov) >= 0) { - PDEBUG(1, "OV7xx0 sensor initalized (method 1)"); + PDEBUG(1, "OV7xx0 sensor initialized (method 1)"); } else { /* Reset the 76xx */ if (i2c_w(ov, 0x12, 0x80) < 0) diff --git a/drivers/media/video/zoran/zoran.h b/drivers/media/video/zoran/zoran.h index 8997add1248e..307e847fe1cd 100644 --- a/drivers/media/video/zoran/zoran.h +++ b/drivers/media/video/zoran/zoran.h @@ -391,7 +391,7 @@ struct zoran { struct mutex resource_lock; /* prevent evil stuff */ - u8 initialized; /* flag if zoran has been correctly initalized */ + u8 initialized; /* flag if zoran has been correctly initialized */ int user; /* number of current users */ struct card_info card; struct tvnorm *timing; diff --git a/drivers/media/video/zoran/zr36050.c b/drivers/media/video/zoran/zr36050.c index 639dd87c663f..e1985609af4b 100644 --- a/drivers/media/video/zoran/zr36050.c +++ b/drivers/media/video/zoran/zr36050.c @@ -236,7 +236,7 @@ zr36050_pushit (struct zr36050 *ptr, Could be variable, but until it's not needed it they are just fixed to save memory. Otherwise expand zr36050 structure with arrays, push the values to - it and initalize from there, as e.g. the linux zr36057/60 driver does it. + it and initialize from there, as e.g. the linux zr36057/60 driver does it. ========================================================================= */ static const char zr36050_dqt[0x86] = { diff --git a/drivers/media/video/zoran/zr36060.c b/drivers/media/video/zoran/zr36060.c index 008746ff7746..5e4f57cbf314 100644 --- a/drivers/media/video/zoran/zr36060.c +++ b/drivers/media/video/zoran/zr36060.c @@ -227,7 +227,7 @@ zr36060_pushit (struct zr36060 *ptr, Could be variable, but until it's not needed it they are just fixed to save memory. Otherwise expand zr36060 structure with arrays, push the values to - it and initalize from there, as e.g. the linux zr36057/60 driver does it. + it and initialize from there, as e.g. the linux zr36057/60 driver does it. ========================================================================= */ static const char zr36060_dqt[0x86] = { diff --git a/drivers/message/fusion/mptbase.c b/drivers/message/fusion/mptbase.c index a6a57011ba6c..14d162fb8a2a 100644 --- a/drivers/message/fusion/mptbase.c +++ b/drivers/message/fusion/mptbase.c @@ -1794,7 +1794,7 @@ mpt_attach(struct pci_dev *pdev, const struct pci_device_id *id) ioc->sh = NULL; ioc->cached_fw = NULL; - /* Initilize SCSI Config Data structure + /* Initialize SCSI Config Data structure */ memset(&ioc->spi_data, 0, sizeof(SpiCfgData)); @@ -2471,7 +2471,7 @@ mpt_do_ioc_recovery(MPT_ADAPTER *ioc, u32 reason, int sleepFlag) if ((ret == 0) && (reason == MPT_HOSTEVENT_IOC_BRINGUP)) { /* - * Initalize link list for inactive raid volumes. + * Initialize link list for inactive raid volumes. */ mutex_init(&ioc->raid_data.inactive_list_mutex); INIT_LIST_HEAD(&ioc->raid_data.inactive_list); diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c index ca03428b59cc..3dfda9cc677d 100644 --- a/drivers/mtd/nand/denali.c +++ b/drivers/mtd/nand/denali.c @@ -1836,7 +1836,7 @@ static struct nand_bbt_descr bbt_mirror_descr = { .pattern = mirror_pattern, }; -/* initalize driver data structures */ +/* initialize driver data structures */ void denali_drv_init(struct denali_nand_info *denali) { denali->idx = 0; diff --git a/drivers/net/3c527.c b/drivers/net/3c527.c index 38395dfa4963..70705d1306b9 100644 --- a/drivers/net/3c527.c +++ b/drivers/net/3c527.c @@ -729,14 +729,14 @@ static void mc32_halt_transceiver(struct net_device *dev) * mc32_load_rx_ring - load the ring of receive buffers * @dev: 3c527 to build the ring for * - * This initalises the on-card and driver datastructures to + * This initialises the on-card and driver datastructures to * the point where mc32_start_transceiver() can be called. * * The card sets up the receive ring for us. We are required to use the * ring it provides, although the size of the ring is configurable. * * We allocate an sk_buff for each ring entry in turn and - * initalise its house-keeping info. At the same time, we read + * initialise its house-keeping info. At the same time, we read * each 'next' pointer in our rx_ring array. This reduces slow * shared-memory reads and makes it easy to access predecessor * descriptors. diff --git a/drivers/net/appletalk/ipddp.c b/drivers/net/appletalk/ipddp.c index 79636ee35829..0362c8d31a08 100644 --- a/drivers/net/appletalk/ipddp.c +++ b/drivers/net/appletalk/ipddp.c @@ -80,7 +80,7 @@ static struct net_device * __init ipddp_init(void) if (version_printed++ == 0) printk(version); - /* Initalize the device structure. */ + /* Initialize the device structure. */ dev->netdev_ops = &ipddp_netdev_ops; dev->type = ARPHRD_IPDDP; /* IP over DDP tunnel */ diff --git a/drivers/net/hp100.c b/drivers/net/hp100.c index 68e5ac8832ad..dfc787fa8b17 100644 --- a/drivers/net/hp100.c +++ b/drivers/net/hp100.c @@ -1071,7 +1071,7 @@ static void hp100_mmuinit(struct net_device *dev) if (lp->mode == 1) hp100_init_pdls(dev); - /* Go to performance page and initalize isr and imr registers */ + /* Go to performance page and initialize isr and imr registers */ hp100_page(PERFORMANCE); hp100_outw(0xfefe, IRQ_MASK); /* mask off all ints */ hp100_outw(0xffff, IRQ_STATUS); /* ack IRQ */ diff --git a/drivers/net/ibm_newemac/core.c b/drivers/net/ibm_newemac/core.c index 2484e9e6c1ed..6a45f8f3a0c7 100644 --- a/drivers/net/ibm_newemac/core.c +++ b/drivers/net/ibm_newemac/core.c @@ -1044,7 +1044,7 @@ static int emac_change_mtu(struct net_device *ndev, int new_mtu) DBG(dev, "change_mtu(%d)" NL, new_mtu); if (netif_running(ndev)) { - /* Check if we really need to reinitalize RX ring */ + /* Check if we really need to reinitialize RX ring */ if (emac_rx_skb_size(ndev->mtu) != emac_rx_skb_size(new_mtu)) ret = emac_resize_rx_ring(dev, new_mtu); } diff --git a/drivers/net/ksz884x.c b/drivers/net/ksz884x.c index c80ca64277b2..c02ce1ab6571 100644 --- a/drivers/net/ksz884x.c +++ b/drivers/net/ksz884x.c @@ -6812,7 +6812,7 @@ static int stp; static int fast_aging; /** - * netdev_init - initalize network device. + * netdev_init - initialize network device. * @dev: Network device. * * This function initializes the network device. diff --git a/drivers/net/ll_temac_main.c b/drivers/net/ll_temac_main.c index b59b24d667f0..0ace2a46d31c 100644 --- a/drivers/net/ll_temac_main.c +++ b/drivers/net/ll_temac_main.c @@ -449,7 +449,7 @@ static u32 temac_setoptions(struct net_device *ndev, u32 options) return (0); } -/* Initilize temac */ +/* Initialize temac */ static void temac_device_reset(struct net_device *ndev) { struct temac_local *lp = netdev_priv(ndev); diff --git a/drivers/net/tulip/dmfe.c b/drivers/net/tulip/dmfe.c index 29e6c63d39fd..0bc4f3030a80 100644 --- a/drivers/net/tulip/dmfe.c +++ b/drivers/net/tulip/dmfe.c @@ -589,7 +589,7 @@ static int dmfe_open(struct DEVICE *dev) db->dm910x_chk_mode = 1; /* Enter the check mode */ } - /* Initilize DM910X board */ + /* Initialize DM910X board */ dmfe_init_dm910x(dev); /* Active System Interface */ @@ -606,9 +606,9 @@ static int dmfe_open(struct DEVICE *dev) } -/* Initilize DM910X board +/* Initialize DM910X board * Reset DM910X board - * Initilize TX/Rx descriptor chain structure + * Initialize TX/Rx descriptor chain structure * Send the set-up frame * Enable Tx/Rx machine */ @@ -649,7 +649,7 @@ static void dmfe_init_dm910x(struct DEVICE *dev) if ( !(db->media_mode & DMFE_AUTO) ) db->op_mode = db->media_mode; /* Force Mode */ - /* Initiliaze Transmit/Receive decriptor and CR3/4 */ + /* Initialize Transmit/Receive decriptor and CR3/4 */ dmfe_descriptor_init(db, ioaddr); /* Init CR6 to program DM910x operation */ @@ -1288,7 +1288,7 @@ static void dmfe_timer(unsigned long data) * Stop DM910X board * Free Tx/Rx allocated memory * Reset DM910X board - * Re-initilize DM910X board + * Re-initialize DM910X board */ static void dmfe_dynamic_reset(struct DEVICE *dev) @@ -1316,7 +1316,7 @@ static void dmfe_dynamic_reset(struct DEVICE *dev) netif_carrier_off(dev); db->wait_reset = 0; - /* Re-initilize DM910X board */ + /* Re-initialize DM910X board */ dmfe_init_dm910x(dev); /* Restart upper layer interface */ @@ -1447,7 +1447,7 @@ static void update_cr6(u32 cr6_data, unsigned long ioaddr) /* * Send a setup frame for DM9132 - * This setup frame initilize DM910X address filter mode + * This setup frame initialize DM910X address filter mode */ static void dm9132_id_table(struct DEVICE *dev) @@ -1489,7 +1489,7 @@ static void dm9132_id_table(struct DEVICE *dev) /* * Send a setup frame for DM9102/DM9102A - * This setup frame initilize DM910X address filter mode + * This setup frame initialize DM910X address filter mode */ static void send_filter_frame(struct DEVICE *dev) @@ -2142,7 +2142,7 @@ static int dmfe_resume(struct pci_dev *pci_dev) pci_set_power_state(pci_dev, PCI_D0); pci_restore_state(pci_dev); - /* Re-initilize DM910X board */ + /* Re-initialize DM910X board */ dmfe_init_dm910x(dev); /* Disable WOL */ @@ -2196,7 +2196,7 @@ MODULE_PARM_DESC(SF_mode, "Davicom DM9xxx special function " /* Description: * when user used insmod to add module, system invoked init_module() - * to initilize and register. + * to initialize and register. */ static int __init dmfe_init_module(void) diff --git a/drivers/net/wimax/i2400m/control.c b/drivers/net/wimax/i2400m/control.c index d86e8f31e7fc..7f48e040c3be 100644 --- a/drivers/net/wimax/i2400m/control.c +++ b/drivers/net/wimax/i2400m/control.c @@ -50,7 +50,7 @@ * * ROADMAP * - * i2400m_dev_initalize() Called by i2400m_dev_start() + * i2400m_dev_initialize() Called by i2400m_dev_start() * i2400m_set_init_config() * i2400m_cmd_get_state() * i2400m_dev_shutdown() Called by i2400m_dev_stop() diff --git a/drivers/parisc/ccio-dma.c b/drivers/parisc/ccio-dma.c index f511e70d454c..75a80e46b391 100644 --- a/drivers/parisc/ccio-dma.c +++ b/drivers/parisc/ccio-dma.c @@ -1241,10 +1241,10 @@ static struct parisc_driver ccio_driver = { }; /** - * ccio_ioc_init - Initalize the I/O Controller + * ccio_ioc_init - Initialize the I/O Controller * @ioc: The I/O Controller. * - * Initalize the I/O Controller which includes setting up the + * Initialize the I/O Controller which includes setting up the * I/O Page Directory, the resource map, and initalizing the * U2/Uturn chip into virtual mode. */ diff --git a/drivers/pcmcia/sa11xx_base.c b/drivers/pcmcia/sa11xx_base.c index fa28d8911b00..0c62fe31a40e 100644 --- a/drivers/pcmcia/sa11xx_base.c +++ b/drivers/pcmcia/sa11xx_base.c @@ -231,7 +231,7 @@ int sa11xx_drv_pcmcia_probe(struct device *dev, struct pcmcia_low_level *ops, sinfo->nskt = nr; - /* Initiliaze processor specific parameters */ + /* Initialize processor specific parameters */ for (i = 0; i < nr; i++) { skt = &sinfo->skt[i]; diff --git a/drivers/scsi/advansys.c b/drivers/scsi/advansys.c index 7f87979da22d..0ec3da6f3e12 100644 --- a/drivers/scsi/advansys.c +++ b/drivers/scsi/advansys.c @@ -9717,7 +9717,7 @@ static ushort __devinit AscInitAscDvcVar(ASC_DVC_VAR *asc_dvc) asc_dvc->bug_fix_cntl = 0; asc_dvc->pci_fix_asyn_xfer = 0; asc_dvc->pci_fix_asyn_xfer_always = 0; - /* asc_dvc->init_state initalized in AscInitGetConfig(). */ + /* asc_dvc->init_state initialized in AscInitGetConfig(). */ asc_dvc->sdtr_done = 0; asc_dvc->cur_total_qng = 0; asc_dvc->is_in_int = 0; diff --git a/drivers/scsi/aic94xx/aic94xx_seq.c b/drivers/scsi/aic94xx/aic94xx_seq.c index d01dcc62b39a..74374618010c 100644 --- a/drivers/scsi/aic94xx/aic94xx_seq.c +++ b/drivers/scsi/aic94xx/aic94xx_seq.c @@ -588,7 +588,7 @@ static void asd_init_cseq_mdp(struct asd_ha_struct *asd_ha) * asd_init_cseq_scratch -- setup and init CSEQ * @asd_ha: pointer to host adapter structure * - * Setup and initialize Central sequencers. Initialiaze the mode + * Setup and initialize Central sequencers. Initialize the mode * independent and dependent scratch page to the default settings. */ static void asd_init_cseq_scratch(struct asd_ha_struct *asd_ha) @@ -782,7 +782,7 @@ static void asd_init_lseq_mdp(struct asd_ha_struct *asd_ha, int lseq) asd_write_reg_word(asd_ha, LmSEQ_OOB_INT_ENABLES(lseq), 0); /* * Set the desired interval between transmissions of the NOTIFY - * (ENABLE SPINUP) primitive. Must be initilized to val - 1. + * (ENABLE SPINUP) primitive. Must be initialized to val - 1. */ asd_write_reg_word(asd_ha, LmSEQ_NOTIFY_TIMER_TIMEOUT(lseq), ASD_NOTIFY_TIMEOUT - 1); diff --git a/drivers/scsi/bfa/vport.c b/drivers/scsi/bfa/vport.c index 27cd619a227a..e2720c8a6661 100644 --- a/drivers/scsi/bfa/vport.c +++ b/drivers/scsi/bfa/vport.c @@ -789,7 +789,7 @@ bfa_cb_lps_fdisc_comp(void *bfad, void *uarg, bfa_status_t status) switch (status) { case BFA_STATUS_OK: /* - * Initialiaze the V-Port fields + * Initialize the V-Port fields */ __vport_fcid(vport) = bfa_lps_get_pid(vport->lps); vport->vport_stats.fdisc_accepts++; diff --git a/drivers/scsi/pm8001/pm8001_hwi.c b/drivers/scsi/pm8001/pm8001_hwi.c index 0e05e8a22167..e81efac25fa4 100644 --- a/drivers/scsi/pm8001/pm8001_hwi.c +++ b/drivers/scsi/pm8001/pm8001_hwi.c @@ -1082,7 +1082,7 @@ static void pm8001_hw_chip_rst(struct pm8001_hba_info *pm8001_ha) } /** - * pm8001_chip_iounmap - which maped when initilized. + * pm8001_chip_iounmap - which maped when initialized. * @pm8001_ha: our hba card information */ static void pm8001_chip_iounmap(struct pm8001_hba_info *pm8001_ha) diff --git a/drivers/scsi/qla4xxx/ql4_init.c b/drivers/scsi/qla4xxx/ql4_init.c index 5510df8a7fa6..cd3043265a6d 100644 --- a/drivers/scsi/qla4xxx/ql4_init.c +++ b/drivers/scsi/qla4xxx/ql4_init.c @@ -183,7 +183,7 @@ static int qla4xxx_validate_mac_address(struct scsi_qla_host *ha) **/ static int qla4xxx_init_local_data(struct scsi_qla_host *ha) { - /* Initilize aen queue */ + /* Initialize aen queue */ ha->aen_q_count = MAX_AEN_ENTRIES; return qla4xxx_get_firmware_status(ha); diff --git a/drivers/serial/sn_console.c b/drivers/serial/sn_console.c index 9794e0cd3dcc..7e5e5efea4e2 100644 --- a/drivers/serial/sn_console.c +++ b/drivers/serial/sn_console.c @@ -470,7 +470,7 @@ sn_receive_chars(struct sn_cons_port *port, unsigned long flags) } if (port->sc_port.state) { - /* The serial_core stuffs are initilized, use them */ + /* The serial_core stuffs are initialized, use them */ tty = port->sc_port.state->port.tty; } else { @@ -551,11 +551,11 @@ static void sn_transmit_chars(struct sn_cons_port *port, int raw) BUG_ON(!port->sc_is_asynch); if (port->sc_port.state) { - /* We're initilized, using serial core infrastructure */ + /* We're initialized, using serial core infrastructure */ xmit = &port->sc_port.state->xmit; } else { /* Probably sn_sal_switch_to_asynch has been run but serial core isn't - * initilized yet. Just return. Writes are going through + * initialized yet. Just return. Writes are going through * sn_sal_console_write (due to register_console) at this time. */ return; diff --git a/drivers/staging/comedi/drivers/usbdux.c b/drivers/staging/comedi/drivers/usbdux.c index 8942ae45708d..e7271685f235 100644 --- a/drivers/staging/comedi/drivers/usbdux.c +++ b/drivers/staging/comedi/drivers/usbdux.c @@ -2087,7 +2087,7 @@ static int usbdux_pwm_start(struct comedi_device *dev, if (ret < 0) return ret; - /* initalise the buffer */ + /* initialise the buffer */ for (i = 0; i < this_usbduxsub->sizePwmBuf; i++) ((char *)(this_usbduxsub->urbPwm->transfer_buffer))[i] = 0; diff --git a/drivers/staging/octeon/cvmx-cmd-queue.c b/drivers/staging/octeon/cvmx-cmd-queue.c index 976227b01273..e9809d375162 100644 --- a/drivers/staging/octeon/cvmx-cmd-queue.c +++ b/drivers/staging/octeon/cvmx-cmd-queue.c @@ -140,21 +140,21 @@ cvmx_cmd_queue_result_t cvmx_cmd_queue_initialize(cvmx_cmd_queue_id_t queue_id, if (qstate->base_ptr_div128) { if (max_depth != (int)qstate->max_depth) { cvmx_dprintf("ERROR: cvmx_cmd_queue_initialize: " - "Queue already initalized with different " + "Queue already initialized with different " "max_depth (%d).\n", (int)qstate->max_depth); return CVMX_CMD_QUEUE_INVALID_PARAM; } if (fpa_pool != qstate->fpa_pool) { cvmx_dprintf("ERROR: cvmx_cmd_queue_initialize: " - "Queue already initalized with different " + "Queue already initialized with different " "FPA pool (%u).\n", qstate->fpa_pool); return CVMX_CMD_QUEUE_INVALID_PARAM; } if ((pool_size >> 3) - 1 != qstate->pool_size_m1) { cvmx_dprintf("ERROR: cvmx_cmd_queue_initialize: " - "Queue already initalized with different " + "Queue already initialized with different " "FPA pool size (%u).\n", (qstate->pool_size_m1 + 1) << 3); return CVMX_CMD_QUEUE_INVALID_PARAM; diff --git a/drivers/staging/pohmelfs/inode.c b/drivers/staging/pohmelfs/inode.c index 63275529ff55..fe8b093fb616 100644 --- a/drivers/staging/pohmelfs/inode.c +++ b/drivers/staging/pohmelfs/inode.c @@ -848,7 +848,7 @@ static void pohmelfs_destroy_inode(struct inode *inode) } /* - * ->alloc_inode() callback. Allocates inode and initilizes private data. + * ->alloc_inode() callback. Allocates inode and initializes private data. */ static struct inode *pohmelfs_alloc_inode(struct super_block *sb) { diff --git a/drivers/staging/rt2860/common/cmm_wpa.c b/drivers/staging/rt2860/common/cmm_wpa.c index 94e119faaa71..e1ead76b907d 100644 --- a/drivers/staging/rt2860/common/cmm_wpa.c +++ b/drivers/staging/rt2860/common/cmm_wpa.c @@ -427,7 +427,7 @@ void RTMPToWirelessSta(struct rt_rtmp_adapter *pAd, /* ========================================================================== Description: - This is a function to initilize 4-way handshake + This is a function to initialize 4-way handshake Return: @@ -867,7 +867,7 @@ void PeerPairMsg3Action(struct rt_rtmp_adapter *pAd, ========================================================================== Description: When receiving the last packet of 4-way pairwisekey handshake. - Initilize 2-way groupkey handshake following. + Initialize 2-way groupkey handshake following. Return: ========================================================================== */ diff --git a/drivers/staging/rtl8192e/r8190_rtl8256.c b/drivers/staging/rtl8192e/r8190_rtl8256.c index 1bd054d42f24..eff47f9cddb9 100644 --- a/drivers/staging/rtl8192e/r8190_rtl8256.c +++ b/drivers/staging/rtl8192e/r8190_rtl8256.c @@ -501,13 +501,13 @@ SetRFPowerState8190( if((priv->ieee80211->eRFPowerState == eRfOff) && RT_IN_PS_LEVEL(pPSC, RT_RF_OFF_LEVL_HALT_NIC)) { // The current RF state is OFF and the RF OFF level is halting the NIC, re-initialize the NIC. bool rtstatus = true; - u32 InitilizeCount = 3; + u32 InitializeCount = 3; do { - InitilizeCount--; + InitializeCount--; priv->RegRfOff = false; rtstatus = NicIFEnableNIC(dev); - }while( (rtstatus != true) &&(InitilizeCount >0) ); + }while( (rtstatus != true) &&(InitializeCount >0) ); if(rtstatus != true) { diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c index cdbe8bf7f674..e8a65ce45a2f 100644 --- a/drivers/usb/serial/kl5kusb105.c +++ b/drivers/usb/serial/kl5kusb105.c @@ -261,7 +261,7 @@ static int klsi_105_startup(struct usb_serial *serial) spin_lock_init(&priv->lock); - /* priv->termios is left uninitalized until port opening */ + /* priv->termios is left uninitialized until port opening */ init_waitqueue_head(&serial->port[i]->write_wait); } diff --git a/drivers/usb/wusbcore/wusbhc.c b/drivers/usb/wusbcore/wusbhc.c index eab86e4bc770..2054d4ee9774 100644 --- a/drivers/usb/wusbcore/wusbhc.c +++ b/drivers/usb/wusbcore/wusbhc.c @@ -26,7 +26,7 @@ * the one that requires (phase B, wusbhc_b_{create,destroy}). * * This is so because usb_add_hcd() will start the HC, and thus, all - * the HC specific stuff has to be already initialiazed (like sysfs + * the HC specific stuff has to be already initialized (like sysfs * thingies). */ #include diff --git a/drivers/uwb/wlp/wss-lc.c b/drivers/uwb/wlp/wss-lc.c index 90accdd54c07..a005d2a03b5d 100644 --- a/drivers/uwb/wlp/wss-lc.c +++ b/drivers/uwb/wlp/wss-lc.c @@ -180,7 +180,7 @@ error_kobject_register: * If memory was allocated for the kobject's name then it will * be freed by the kobject system during this time. * - * The EDA cache is removed and reinitilized when the WSS is removed. We + * The EDA cache is removed and reinitialized when the WSS is removed. We * thus loose knowledge of members of this WSS at that time and need not do * it here. */ diff --git a/drivers/video/carminefb.c b/drivers/video/carminefb.c index d8345fcc4fe3..6b19136aa181 100644 --- a/drivers/video/carminefb.c +++ b/drivers/video/carminefb.c @@ -432,7 +432,7 @@ static int init_hardware(struct carmine_hw *hw) u32 loops; u32 ret; - /* Initalize Carmine */ + /* Initialize Carmine */ /* Sets internal clock */ c_set_hw_reg(hw, CARMINE_CTL_REG + CARMINE_CTL_REG_CLOCK_ENABLE, CARMINE_DFLT_IP_CLOCK_ENABLE); diff --git a/drivers/video/tgafb.c b/drivers/video/tgafb.c index 1b3b1c718e80..aba7686b1a32 100644 --- a/drivers/video/tgafb.c +++ b/drivers/video/tgafb.c @@ -305,7 +305,7 @@ tgafb_set_par(struct fb_info *info) TGA_WRITE_REG(par, htimings, TGA_HORIZ_REG); TGA_WRITE_REG(par, vtimings, TGA_VERT_REG); - /* Initalise RAMDAC. */ + /* Initialise RAMDAC. */ if (tga_type == TGA_TYPE_8PLANE && tga_bus_pci) { /* Init BT485 RAMDAC registers. */ diff --git a/fs/befs/linuxvfs.c b/fs/befs/linuxvfs.c index 34ddda888e63..dc39d2824885 100644 --- a/fs/befs/linuxvfs.c +++ b/fs/befs/linuxvfs.c @@ -436,7 +436,7 @@ befs_init_inodecache(void) init_once); if (befs_inode_cachep == NULL) { printk(KERN_ERR "befs_init_inodecache: " - "Couldn't initalize inode slabcache\n"); + "Couldn't initialize inode slabcache\n"); return -ENOMEM; } diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c index 1cc087635a5e..a2e3b562e65d 100644 --- a/fs/ecryptfs/crypto.c +++ b/fs/ecryptfs/crypto.c @@ -762,7 +762,7 @@ ecryptfs_decrypt_page_offset(struct ecryptfs_crypt_stat *crypt_stat, /** * ecryptfs_init_crypt_ctx - * @crypt_stat: Uninitilized crypt stats structure + * @crypt_stat: Uninitialized crypt stats structure * * Initialize the crypto context. * diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index 236b834b4ca8..146f1f6a9203 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c @@ -2918,7 +2918,7 @@ fix_extent_len: * One of more index blocks maybe needed if the extent tree grow after * the unintialized extent split. To prevent ENOSPC occur at the IO * complete, we need to split the uninitialized extent before DIO submit - * the IO. The uninitilized extent called at this time will be split + * the IO. The uninitialized extent called at this time will be split * into three uninitialized extent(at most). After IO complete, the part * being filled will be convert to initialized by the end_io callback function * via ext4_convert_unwritten_extents(). diff --git a/fs/ext4/super.c b/fs/ext4/super.c index e14d22c170d5..8d7539c9d778 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -3008,7 +3008,7 @@ no_journal: ext4_ext_init(sb); err = ext4_mb_init(sb, needs_recovery); if (err) { - ext4_msg(sb, KERN_ERR, "failed to initalize mballoc (%d)", + ext4_msg(sb, KERN_ERR, "failed to initialize mballoc (%d)", err); goto failed_mount4; } diff --git a/fs/freevxfs/vxfs_super.c b/fs/freevxfs/vxfs_super.c index 1e8af939b3e4..5132c99b1ca2 100644 --- a/fs/freevxfs/vxfs_super.c +++ b/fs/freevxfs/vxfs_super.c @@ -135,7 +135,7 @@ static int vxfs_remount(struct super_block *sb, int *flags, char *data) } /** - * vxfs_read_super - read superblock into memory and initalize filesystem + * vxfs_read_super - read superblock into memory and initialize filesystem * @sbp: VFS superblock (to fill) * @dp: fs private mount data * @silent: do not complain loudly when sth is wrong diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c index 2c26ce251cb3..812f10233b10 100644 --- a/fs/ocfs2/super.c +++ b/fs/ocfs2/super.c @@ -2476,7 +2476,7 @@ static void ocfs2_delete_osb(struct ocfs2_super *osb) kfree(osb->slot_recovery_generations); /* FIXME * This belongs in journal shutdown, but because we have to - * allocate osb->journal at the start of ocfs2_initalize_osb(), + * allocate osb->journal at the start of ocfs2_initialize_osb(), * we free it here. */ kfree(osb->journal); diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c index 0f22fdaf54ac..29db72203bde 100644 --- a/fs/reiserfs/inode.c +++ b/fs/reiserfs/inode.c @@ -1221,7 +1221,7 @@ static void init_inode(struct inode *inode, struct treepath *path) inode_set_bytes(inode, to_real_used_space(inode, inode->i_blocks, SD_V2_SIZE)); - /* read persistent inode attributes from sd and initalise + /* read persistent inode attributes from sd and initialise generic inode flags from them */ REISERFS_I(inode)->i_attrs = sd_v2_attrs(sd); sd_attrs_to_i_attrs(sd_v2_attrs(sd), inode); diff --git a/lib/random32.c b/lib/random32.c index 217d5c4b666d..556d5ffe1106 100644 --- a/lib/random32.c +++ b/lib/random32.c @@ -131,7 +131,7 @@ core_initcall(random32_init); /* * Generate better values after random number generator - * is fully initalized. + * is fully initialized. */ static int __init random32_reseed(void) { diff --git a/net/netfilter/ipvs/ip_vs_lblc.c b/net/netfilter/ipvs/ip_vs_lblc.c index 94a45213faa6..9323f8944199 100644 --- a/net/netfilter/ipvs/ip_vs_lblc.c +++ b/net/netfilter/ipvs/ip_vs_lblc.c @@ -11,7 +11,7 @@ * Changes: * Martin Hamilton : fixed the terrible locking bugs * *lock(tbl->lock) ==> *lock(&tbl->lock) - * Wensong Zhang : fixed the uninitilized tbl->lock bug + * Wensong Zhang : fixed the uninitialized tbl->lock bug * Wensong Zhang : added doing full expiration check to * collect stale entries of 24+ hours when * no partial expire check in a half hour diff --git a/net/netfilter/ipvs/ip_vs_lblcr.c b/net/netfilter/ipvs/ip_vs_lblcr.c index 535dc2b419d8..dbeed8ea421a 100644 --- a/net/netfilter/ipvs/ip_vs_lblcr.c +++ b/net/netfilter/ipvs/ip_vs_lblcr.c @@ -386,7 +386,7 @@ ip_vs_lblcr_new(struct ip_vs_lblcr_table *tbl, const union nf_inet_addr *daddr, ip_vs_addr_copy(dest->af, &en->addr, daddr); en->lastuse = jiffies; - /* initilize its dest set */ + /* initialize its dest set */ atomic_set(&(en->set.size), 0); INIT_LIST_HEAD(&en->set.list); rwlock_init(&en->set.lock); diff --git a/net/sctp/associola.c b/net/sctp/associola.c index e41feff19e43..0b85e5256434 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c @@ -172,7 +172,7 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a asoc->timeouts[SCTP_EVENT_TIMEOUT_AUTOCLOSE] = (unsigned long)sp->autoclose * HZ; - /* Initilizes the timers */ + /* Initializes the timers */ for (i = SCTP_EVENT_TIMEOUT_NONE; i < SCTP_NUM_TIMEOUT_TYPES; ++i) setup_timer(&asoc->timers[i], sctp_timer_events[i], (unsigned long)asoc); diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index 182749867c72..0f41b05bd4d6 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -1162,7 +1162,7 @@ SCTP_STATIC __init int sctp_init(void) /* Set the pressure threshold to be a fraction of global memory that * is up to 1/2 at 256 MB, decreasing toward zero with the amount of * memory, with a floor of 128 pages. - * Note this initalizes the data in sctpv6_prot too + * Note this initializes the data in sctpv6_prot too * Unabashedly stolen from tcp_init */ nr_pages = totalram_pages - totalhigh_pages; diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 0f2fc480fc61..276bdc7325e5 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -3227,7 +3227,7 @@ static __init int smack_init(void) cred = (struct cred *) current->cred; cred->security = &smack_known_floor.smk_known; - /* initilize the smack_know_list */ + /* initialize the smack_know_list */ init_smack_know_list(); /* * Initialize locks diff --git a/sound/pci/trident/trident_main.c b/sound/pci/trident/trident_main.c index 6d943f6f6b70..2870a4fdc130 100644 --- a/sound/pci/trident/trident_main.c +++ b/sound/pci/trident/trident_main.c @@ -1055,7 +1055,7 @@ static int snd_trident_capture_prepare(struct snd_pcm_substream *substream) spin_lock_irq(&trident->reg_lock); - // Initilize the channel and set channel Mode + // Initialize the channel and set channel Mode outb(0, TRID_REG(trident, LEGACY_DMAR15)); // Set DMA channel operation mode register diff --git a/sound/soc/fsl/mpc8610_hpcd.c b/sound/soc/fsl/mpc8610_hpcd.c index 83de1c81c8c4..604a91fa31bc 100644 --- a/sound/soc/fsl/mpc8610_hpcd.c +++ b/sound/soc/fsl/mpc8610_hpcd.c @@ -46,7 +46,7 @@ struct mpc8610_hpcd_data { }; /** - * mpc8610_hpcd_machine_probe: initalize the board + * mpc8610_hpcd_machine_probe: initialize the board * * This function is called when platform_device_add() is called. It is used * to initialize the board-specific hardware. diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 998569d60330..e048e0910099 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1307,7 +1307,7 @@ cpu_dai_err: } /* - * Attempt to initialise any uninitalised cards. Must be called with + * Attempt to initialise any uninitialised cards. Must be called with * client_mutex. */ static void snd_soc_instantiate_cards(void) -- cgit v1.2.3 From 2422f676fb78942d054f7e7a2c3ceaeb7945d814 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 16 Jun 2010 13:40:16 -0400 Subject: cifs: move cifs_new_fileinfo call out of cifs_posix_open Having cifs_posix_open call cifs_new_fileinfo is problematic and inconsistent with how "regular" opens work. It's also buggy as cifs_reopen_file calls this function on a reconnect, which creates a new struct cifsFileInfo that just gets leaked. Push it out into the callers. This also allows us to get rid of the "mnt" arg to cifs_posix_open. Finally, in the event that a cifsFileInfo isn't or can't be created, we always want to close the filehandle out on the server as the client won't have a record of the filehandle and can't actually use it. Make sure that CIFSSMBClose is called in those cases. Signed-off-by: Jeff Layton Reviewed-and-Tested-by: Suresh Jayaraman --- fs/cifs/cifsproto.h | 1 - fs/cifs/dir.c | 43 ++++++++++++++++++------------------------- fs/cifs/file.c | 17 ++++++++++++----- 3 files changed, 30 insertions(+), 31 deletions(-) (limited to 'fs') diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index fb1657e0fdb8..fb6318b81509 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -106,7 +106,6 @@ extern struct cifsFileInfo *cifs_new_fileinfo(struct inode *newinode, __u16 fileHandle, struct file *file, struct vfsmount *mnt, unsigned int oflags); extern int cifs_posix_open(char *full_path, struct inode **pinode, - struct vfsmount *mnt, struct super_block *sb, int mode, int oflags, __u32 *poplock, __u16 *pnetfid, int xid); diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index 391816b461ca..f49afb9980e7 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -188,8 +188,7 @@ cifs_new_fileinfo(struct inode *newinode, __u16 fileHandle, } int cifs_posix_open(char *full_path, struct inode **pinode, - struct vfsmount *mnt, struct super_block *sb, - int mode, int oflags, + struct super_block *sb, int mode, int oflags, __u32 *poplock, __u16 *pnetfid, int xid) { int rc; @@ -258,19 +257,6 @@ int cifs_posix_open(char *full_path, struct inode **pinode, cifs_fattr_to_inode(*pinode, &fattr); } - /* - * cifs_fill_filedata() takes care of setting cifsFileInfo pointer to - * file->private_data. - */ - if (mnt) { - struct cifsFileInfo *pfile_info; - - pfile_info = cifs_new_fileinfo(*pinode, *pnetfid, NULL, mnt, - oflags); - if (pfile_info == NULL) - rc = -ENOMEM; - } - posix_open_ret: kfree(presp_data); return rc; @@ -298,7 +284,6 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode, int create_options = CREATE_NOT_DIR; __u32 oplock = 0; int oflags; - bool posix_create = false; /* * BB below access is probably too much for mknod to request * but we have to do query and setpathinfo so requesting @@ -339,7 +324,6 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode, (CIFS_UNIX_POSIX_PATH_OPS_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability))) { rc = cifs_posix_open(full_path, &newinode, - nd ? nd->path.mnt : NULL, inode->i_sb, mode, oflags, &oplock, &fileHandle, xid); /* EIO could indicate that (posix open) operation is not supported, despite what server claimed in capability @@ -347,7 +331,6 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode, handled in posix open */ if (rc == 0) { - posix_create = true; if (newinode == NULL) /* query inode info */ goto cifs_create_get_file_info; else /* success, no need to query */ @@ -478,11 +461,7 @@ cifs_create_set_dentry: else cFYI(1, "Create worked, get_inode_info failed rc = %d", rc); - /* nfsd case - nfs srv does not set nd */ - if ((nd == NULL) || (!(nd->flags & LOOKUP_OPEN))) { - /* mknod case - do not leave file open */ - CIFSSMBClose(xid, tcon, fileHandle); - } else if (!(posix_create) && (newinode)) { + if (newinode && nd && (nd->flags & LOOKUP_OPEN)) { struct cifsFileInfo *pfile_info; /* * cifs_fill_filedata() takes care of setting cifsFileInfo @@ -492,7 +471,10 @@ cifs_create_set_dentry: nd->path.mnt, oflags); if (pfile_info == NULL) rc = -ENOMEM; + } else { + CIFSSMBClose(xid, tcon, fileHandle); } + cifs_create_out: kfree(buf); kfree(full_path); @@ -636,6 +618,7 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, bool posix_open = false; struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; + struct cifsFileInfo *cfile; struct inode *newInode = NULL; char *full_path = NULL; struct file *filp; @@ -703,7 +686,7 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, if (nd && !(nd->flags & (LOOKUP_PARENT | LOOKUP_DIRECTORY)) && (nd->flags & LOOKUP_OPEN) && !pTcon->broken_posix_open && (nd->intent.open.flags & O_CREAT)) { - rc = cifs_posix_open(full_path, &newInode, nd->path.mnt, + rc = cifs_posix_open(full_path, &newInode, parent_dir_inode->i_sb, nd->intent.open.create_mode, nd->intent.open.flags, &oplock, @@ -733,8 +716,17 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, else direntry->d_op = &cifs_dentry_ops; d_add(direntry, newInode); - if (posix_open) + if (posix_open) { + cfile = cifs_new_fileinfo(newInode, fileHandle, NULL, + nd->path.mnt, + nd->intent.open.flags); + if (cfile == NULL) { + CIFSSMBClose(xid, pTcon, fileHandle); + rc = -ENOMEM; + goto lookup_out; + } filp = lookup_instantiate_filp(nd, direntry, NULL); + } /* since paths are not looked up by component - the parent directories are presumed to be good here */ renew_parental_timestamps(direntry); @@ -755,6 +747,7 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, is a common return code */ } +lookup_out: kfree(full_path); FreeXid(xid); return ERR_PTR(rc); diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 75541af4b3db..542e0c874d64 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -299,8 +299,7 @@ int cifs_open(struct inode *inode, struct file *file) int oflags = (int) cifs_posix_convert_flags(file->f_flags); oflags |= SMB_O_CREAT; /* can not refresh inode info since size could be stale */ - rc = cifs_posix_open(full_path, &inode, file->f_path.mnt, - inode->i_sb, + rc = cifs_posix_open(full_path, &inode, inode->i_sb, cifs_sb->mnt_file_mode /* ignored */, oflags, &oplock, &netfid, xid); if (rc == 0) { @@ -308,7 +307,16 @@ int cifs_open(struct inode *inode, struct file *file) /* no need for special case handling of setting mode on read only files needed here */ - pCifsFile = cifs_fill_filedata(file); + pCifsFile = cifs_new_fileinfo(inode, netfid, file, + file->f_path.mnt, + oflags); + if (pCifsFile == NULL) { + CIFSSMBClose(xid, tcon, netfid); + rc = -ENOMEM; + goto out; + } + file->private_data = pCifsFile; + cifs_posix_open_inode_helper(inode, file, pCifsInode, oplock, netfid); goto out; @@ -513,8 +521,7 @@ reopen_error_exit: le64_to_cpu(tcon->fsUnixInfo.Capability))) { int oflags = (int) cifs_posix_convert_flags(file->f_flags); /* can not refresh inode info since size could be stale */ - rc = cifs_posix_open(full_path, NULL, file->f_path.mnt, - inode->i_sb, + rc = cifs_posix_open(full_path, NULL, inode->i_sb, cifs_sb->mnt_file_mode /* ignored */, oflags, &oplock, &netfid, xid); if (rc == 0) { -- cgit v1.2.3 From 6ca9f3bae8b1854794dfa63cdd3b88b7dfe24c13 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 16 Jun 2010 13:40:16 -0400 Subject: cifs: pass instantiated filp back after open call The current scheme of sticking open files on a list and assuming that cifs_open will scoop them off of it is broken and leads to "Busy inodes after umount..." errors at unmount time. The problem is that there is no guarantee that cifs_open will always be called after a ->lookup or ->create operation. If there are permissions or other problems, then it's quite likely that it *won't* be called. Fix this by fully instantiating the filp whenever the file is created and pass that filp back to the VFS. If there is a problem, the VFS can clean up the references. Signed-off-by: Jeff Layton Reviewed-and-Tested-by: Suresh Jayaraman --- fs/cifs/dir.c | 35 +++++++++++++++++++++++++++-------- fs/cifs/file.c | 44 ++------------------------------------------ 2 files changed, 29 insertions(+), 50 deletions(-) (limited to 'fs') diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index f49afb9980e7..e7ae78b66fa1 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "cifsfs.h" #include "cifspdu.h" #include "cifsglob.h" @@ -184,6 +185,8 @@ cifs_new_fileinfo(struct inode *newinode, __u16 fileHandle, } write_unlock(&GlobalSMBSeslock); + file->private_data = pCifsFile; + return pCifsFile; } @@ -463,14 +466,22 @@ cifs_create_set_dentry: if (newinode && nd && (nd->flags & LOOKUP_OPEN)) { struct cifsFileInfo *pfile_info; - /* - * cifs_fill_filedata() takes care of setting cifsFileInfo - * pointer to file->private_data. - */ - pfile_info = cifs_new_fileinfo(newinode, fileHandle, NULL, + struct file *filp; + + filp = lookup_instantiate_filp(nd, direntry, generic_file_open); + if (IS_ERR(filp)) { + rc = PTR_ERR(filp); + CIFSSMBClose(xid, tcon, fileHandle); + goto cifs_create_out; + } + + pfile_info = cifs_new_fileinfo(newinode, fileHandle, filp, nd->path.mnt, oflags); - if (pfile_info == NULL) + if (pfile_info == NULL) { + fput(filp); + CIFSSMBClose(xid, tcon, fileHandle); rc = -ENOMEM; + } } else { CIFSSMBClose(xid, tcon, fileHandle); } @@ -717,15 +728,23 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, direntry->d_op = &cifs_dentry_ops; d_add(direntry, newInode); if (posix_open) { - cfile = cifs_new_fileinfo(newInode, fileHandle, NULL, + filp = lookup_instantiate_filp(nd, direntry, + generic_file_open); + if (IS_ERR(filp)) { + rc = PTR_ERR(filp); + CIFSSMBClose(xid, pTcon, fileHandle); + goto lookup_out; + } + + cfile = cifs_new_fileinfo(newInode, fileHandle, filp, nd->path.mnt, nd->intent.open.flags); if (cfile == NULL) { + fput(filp); CIFSSMBClose(xid, pTcon, fileHandle); rc = -ENOMEM; goto lookup_out; } - filp = lookup_instantiate_filp(nd, direntry, NULL); } /* since paths are not looked up by component - the parent directories are presumed to be good here */ diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 542e0c874d64..9cbf0f097a39 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -162,38 +162,6 @@ psx_client_can_cache: return 0; } -static struct cifsFileInfo * -cifs_fill_filedata(struct file *file) -{ - struct list_head *tmp; - struct cifsFileInfo *pCifsFile = NULL; - struct cifsInodeInfo *pCifsInode = NULL; - - /* search inode for this file and fill in file->private_data */ - pCifsInode = CIFS_I(file->f_path.dentry->d_inode); - read_lock(&GlobalSMBSeslock); - list_for_each(tmp, &pCifsInode->openFileList) { - pCifsFile = list_entry(tmp, struct cifsFileInfo, flist); - if ((pCifsFile->pfile == NULL) && - (pCifsFile->pid == current->tgid)) { - /* mode set in cifs_create */ - - /* needed for writepage */ - pCifsFile->pfile = file; - file->private_data = pCifsFile; - break; - } - } - read_unlock(&GlobalSMBSeslock); - - if (file->private_data != NULL) { - return pCifsFile; - } else if ((file->f_flags & O_CREAT) && (file->f_flags & O_EXCL)) - cERROR(1, "could not find file instance for " - "new file %p", file); - return NULL; -} - /* all arguments to this function must be checked for validity in caller */ static inline int cifs_open_inode_helper(struct inode *inode, struct file *file, struct cifsInodeInfo *pCifsInode, struct cifsFileInfo *pCifsFile, @@ -256,7 +224,7 @@ int cifs_open(struct inode *inode, struct file *file) __u32 oplock; struct cifs_sb_info *cifs_sb; struct cifsTconInfo *tcon; - struct cifsFileInfo *pCifsFile; + struct cifsFileInfo *pCifsFile = NULL; struct cifsInodeInfo *pCifsInode; char *full_path = NULL; int desiredAccess; @@ -270,12 +238,6 @@ int cifs_open(struct inode *inode, struct file *file) tcon = cifs_sb->tcon; pCifsInode = CIFS_I(file->f_path.dentry->d_inode); - pCifsFile = cifs_fill_filedata(file); - if (pCifsFile) { - rc = 0; - FreeXid(xid); - return rc; - } full_path = build_path_from_dentry(file->f_path.dentry); if (full_path == NULL) { @@ -315,7 +277,6 @@ int cifs_open(struct inode *inode, struct file *file) rc = -ENOMEM; goto out; } - file->private_data = pCifsFile; cifs_posix_open_inode_helper(inode, file, pCifsInode, oplock, netfid); @@ -401,8 +362,7 @@ int cifs_open(struct inode *inode, struct file *file) pCifsFile = cifs_new_fileinfo(inode, netfid, file, file->f_path.mnt, file->f_flags); - file->private_data = pCifsFile; - if (file->private_data == NULL) { + if (pCifsFile == NULL) { rc = -ENOMEM; goto out; } -- cgit v1.2.3 From db460242bf75624344efd670ec0f620f476529a3 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 16 Jun 2010 13:40:17 -0400 Subject: cifs: clean up arguments to cifs_open_inode_helper ...which takes a ton of unneeded arguments and does a lot more pointer dereferencing than is really needed. Signed-off-by: Jeff Layton Reviewed-and-Tested-by: Suresh Jayaraman --- fs/cifs/file.c | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) (limited to 'fs') diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 9cbf0f097a39..5b9d1f25aaec 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -163,11 +163,11 @@ psx_client_can_cache: } /* all arguments to this function must be checked for validity in caller */ -static inline int cifs_open_inode_helper(struct inode *inode, struct file *file, - struct cifsInodeInfo *pCifsInode, struct cifsFileInfo *pCifsFile, +static inline int cifs_open_inode_helper(struct inode *inode, struct cifsTconInfo *pTcon, int *oplock, FILE_ALL_INFO *buf, char *full_path, int xid) { + struct cifsInodeInfo *pCifsInode = CIFS_I(inode); struct timespec temp; int rc; @@ -181,36 +181,35 @@ static inline int cifs_open_inode_helper(struct inode *inode, struct file *file, /* if not oplocked, invalidate inode pages if mtime or file size changed */ temp = cifs_NTtimeToUnix(buf->LastWriteTime); - if (timespec_equal(&file->f_path.dentry->d_inode->i_mtime, &temp) && - (file->f_path.dentry->d_inode->i_size == + if (timespec_equal(&inode->i_mtime, &temp) && + (inode->i_size == (loff_t)le64_to_cpu(buf->EndOfFile))) { cFYI(1, "inode unchanged on server"); } else { - if (file->f_path.dentry->d_inode->i_mapping) { + if (inode->i_mapping) { /* BB no need to lock inode until after invalidate since namei code should already have it locked? */ - rc = filemap_write_and_wait(file->f_path.dentry->d_inode->i_mapping); + rc = filemap_write_and_wait(inode->i_mapping); if (rc != 0) - CIFS_I(file->f_path.dentry->d_inode)->write_behind_rc = rc; + pCifsInode->write_behind_rc = rc; } cFYI(1, "invalidating remote inode since open detected it " "changed"); - invalidate_remote_inode(file->f_path.dentry->d_inode); + invalidate_remote_inode(inode); } client_can_cache: if (pTcon->unix_ext) - rc = cifs_get_inode_info_unix(&file->f_path.dentry->d_inode, - full_path, inode->i_sb, xid); + rc = cifs_get_inode_info_unix(&inode, full_path, inode->i_sb, + xid); else - rc = cifs_get_inode_info(&file->f_path.dentry->d_inode, - full_path, buf, inode->i_sb, xid, NULL); + rc = cifs_get_inode_info(&inode, full_path, buf, inode->i_sb, + xid, NULL); if ((*oplock & 0xF) == OPLOCK_EXCLUSIVE) { pCifsInode->clientCanCacheAll = true; pCifsInode->clientCanCacheRead = true; - cFYI(1, "Exclusive Oplock granted on inode %p", - file->f_path.dentry->d_inode); + cFYI(1, "Exclusive Oplock granted on inode %p", inode); } else if ((*oplock & 0xF) == OPLOCK_READ) pCifsInode->clientCanCacheRead = true; @@ -367,8 +366,7 @@ int cifs_open(struct inode *inode, struct file *file) goto out; } - rc = cifs_open_inode_helper(inode, file, pCifsInode, pCifsFile, tcon, - &oplock, buf, full_path, xid); + rc = cifs_open_inode_helper(inode, tcon, &oplock, buf, full_path, xid); if (oplock & CIFS_CREATE_ACTION) { /* time to set mode which we can not set earlier due to -- cgit v1.2.3 From d9d5d8df953a98621be5b8889e05043d6e32052e Mon Sep 17 00:00:00 2001 From: Suresh Jayaraman Date: Wed, 16 Jun 2010 13:40:17 -0400 Subject: cifs: don't ignore cifs_posix_open_inode_helper return value ...and ensure that we propagate the error back to avoid any surprises. Signed-off-by: Suresh Jayaraman Reviewed-and-Tested-by: Jeff Layton --- fs/cifs/file.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 5b9d1f25aaec..02a2df9cdd9c 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -277,8 +277,8 @@ int cifs_open(struct inode *inode, struct file *file) goto out; } - cifs_posix_open_inode_helper(inode, file, pCifsInode, - oplock, netfid); + rc = cifs_posix_open_inode_helper(inode, file, + pCifsInode, oplock, netfid); goto out; } else if ((rc == -EINVAL) || (rc == -EOPNOTSUPP)) { if (tcon->ses->serverNOS) -- cgit v1.2.3 From 47c78b7f40a9931a264e3c9bddccacdf8dfb9a30 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 16 Jun 2010 13:40:17 -0400 Subject: cifs: don't call cifs_new_fileinfo unless cifs_open succeeds It's currently possible for cifs_open to fail after it has already called cifs_new_fileinfo. In that situation, the new fileinfo will be leaked as the caller doesn't call fput. That in turn leads to a busy inodes after umount problem since the fileinfo holds an extra inode reference now. Shuffle cifs_open around a bit so that it only calls cifs_new_fileinfo if it's going to succeed. Signed-off-by: Jeff Layton Reviewed-and-Tested-by: Suresh Jayaraman --- fs/cifs/file.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'fs') diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 02a2df9cdd9c..409e4f523e61 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -268,17 +268,20 @@ int cifs_open(struct inode *inode, struct file *file) /* no need for special case handling of setting mode on read only files needed here */ + rc = cifs_posix_open_inode_helper(inode, file, + pCifsInode, oplock, netfid); + if (rc != 0) { + CIFSSMBClose(xid, tcon, netfid); + goto out; + } + pCifsFile = cifs_new_fileinfo(inode, netfid, file, file->f_path.mnt, oflags); if (pCifsFile == NULL) { CIFSSMBClose(xid, tcon, netfid); rc = -ENOMEM; - goto out; } - - rc = cifs_posix_open_inode_helper(inode, file, - pCifsInode, oplock, netfid); goto out; } else if ((rc == -EINVAL) || (rc == -EOPNOTSUPP)) { if (tcon->ses->serverNOS) @@ -359,6 +362,10 @@ int cifs_open(struct inode *inode, struct file *file) goto out; } + rc = cifs_open_inode_helper(inode, tcon, &oplock, buf, full_path, xid); + if (rc != 0) + goto out; + pCifsFile = cifs_new_fileinfo(inode, netfid, file, file->f_path.mnt, file->f_flags); if (pCifsFile == NULL) { @@ -366,8 +373,6 @@ int cifs_open(struct inode *inode, struct file *file) goto out; } - rc = cifs_open_inode_helper(inode, tcon, &oplock, buf, full_path, xid); - if (oplock & CIFS_CREATE_ACTION) { /* time to set mode which we can not set earlier due to problems creating new read-only files */ -- cgit v1.2.3 From 8a224d489454b7457105848610cfebebdec5638d Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Wed, 16 Jun 2010 13:40:18 -0400 Subject: cifs: remove bogus first_time check in NTLMv2 session setup code This bug appears to be the result of a cut-and-paste mistake from the NTLMv1 code. The function to generate the MAC key was commented out, but not the conditional above it. The conditional then ended up causing the session setup key not to be copied to the buffer unless this was the first session on the socket, and that made all but the first NTLMv2 session setup fail. Fix this by removing the conditional and all of the commented clutter that made it difficult to see. Cc: Stable Reported-by: Gunther Deschner Signed-off-by: Jeff Layton --- fs/cifs/sess.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) (limited to 'fs') diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c index 7707389bdf2c..0a57cb7db5dd 100644 --- a/fs/cifs/sess.c +++ b/fs/cifs/sess.c @@ -730,15 +730,7 @@ ssetup_ntlmssp_authenticate: /* calculate session key */ setup_ntlmv2_rsp(ses, v2_sess_key, nls_cp); - if (first_time) /* should this be moved into common code - with similar ntlmv2 path? */ - /* cifs_calculate_ntlmv2_mac_key(ses->server->mac_signing_key, - response BB FIXME, v2_sess_key); */ - - /* copy session key */ - - /* memcpy(bcc_ptr, (char *)ntlm_session_key,LM2_SESS_KEY_SIZE); - bcc_ptr += LM2_SESS_KEY_SIZE; */ + /* FIXME: calculate MAC key */ memcpy(bcc_ptr, (char *)v2_sess_key, sizeof(struct ntlmv2_resp)); bcc_ptr += sizeof(struct ntlmv2_resp); -- cgit v1.2.3 From cebc5be6b6c82a99231e9c9af451e9e3d3399ec6 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Thu, 17 Jun 2010 10:22:48 -0700 Subject: ceph: fix crush map update decoding If the incremental osdmap has a new crush map, advance the position after decoding so that we can parse the rest of the osdmap properly. Signed-off-by: Sage Weil --- fs/ceph/osdmap.c | 1 + 1 file changed, 1 insertion(+) (limited to 'fs') diff --git a/fs/ceph/osdmap.c b/fs/ceph/osdmap.c index ddc656fb5c05..50ce64ebd330 100644 --- a/fs/ceph/osdmap.c +++ b/fs/ceph/osdmap.c @@ -707,6 +707,7 @@ struct ceph_osdmap *osdmap_apply_incremental(void **p, void *end, newcrush = crush_decode(*p, min(*p+len, end)); if (IS_ERR(newcrush)) return ERR_CAST(newcrush); + *p += len; } /* new flags? */ -- cgit v1.2.3 From d69ed05a80f23b25f06e73af9b7e701ce4900edc Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Mon, 21 Jun 2010 10:38:14 -0700 Subject: ceph: handle splice_dentry/d_materialize_unique error in readdir_prepopulate Handle a splice_dentry failure (due to a d_materialize_unique error) without crashing. (Also, report the error code.) Signed-off-by: Sage Weil --- fs/ceph/inode.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) (limited to 'fs') diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c index ab47f46ca282..8f9b9fe8ef9f 100644 --- a/fs/ceph/inode.c +++ b/fs/ceph/inode.c @@ -854,8 +854,8 @@ static struct dentry *splice_dentry(struct dentry *dn, struct inode *in, d_drop(dn); realdn = d_materialise_unique(dn, in); if (IS_ERR(realdn)) { - pr_err("splice_dentry error %p inode %p ino %llx.%llx\n", - dn, in, ceph_vinop(in)); + pr_err("splice_dentry error %ld %p inode %p ino %llx.%llx\n", + PTR_ERR(realdn), dn, in, ceph_vinop(in)); if (prehash) *prehash = false; /* don't rehash on error */ dn = realdn; /* note realdn contains the error */ @@ -1234,18 +1234,23 @@ retry_lookup: goto out; } dn = splice_dentry(dn, in, NULL); + if (IS_ERR(dn)) + dn = NULL; } if (fill_inode(in, &rinfo->dir_in[i], NULL, session, req->r_request_started, -1, &req->r_caps_reservation) < 0) { pr_err("fill_inode badness on %p\n", in); - dput(dn); - continue; + goto next_item; } - update_dentry_lease(dn, rinfo->dir_dlease[i], - req->r_session, req->r_request_started); - dput(dn); + if (dn) + update_dentry_lease(dn, rinfo->dir_dlease[i], + req->r_session, + req->r_request_started); +next_item: + if (dn) + dput(dn); } req->r_did_prepopulate = true; -- cgit v1.2.3 From 17c688c3dfffc274c87be00033da0050bb6eefc0 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Mon, 21 Jun 2010 16:12:26 -0700 Subject: ceph: delay umount until all mds requests drop inode+dentry refs This fixes a race between handle_reply finishing an mds request, signalling completion, and then dropping the request structing and its dentry+inode refs, and pre_umount function waiting for requests to finish before letting the vfs tear down the dcache. If umount was delayed waiting for mds requests, we could race and BUG in shrink_dcache_for_umount_subtree because of a slow dput. This delays umount until the msgr queue flushes, which means handle_reply will exit and will have dropped the ceph_mds_request struct. I'm assuming the VFS has already ensured that its calls have all completed and those request refs have thus been dropped as well (I haven't seen that race, at least). Signed-off-by: Sage Weil --- fs/ceph/mds_client.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'fs') diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 1766947fc07a..3ab79f6c4ce8 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -2783,6 +2783,12 @@ void ceph_mdsc_pre_umount(struct ceph_mds_client *mdsc) drop_leases(mdsc); ceph_flush_dirty_caps(mdsc); wait_requests(mdsc); + + /* + * wait for reply handlers to drop their request refs and + * their inode/dcache refs + */ + ceph_msgr_flush(); } /* -- cgit v1.2.3 From f799bdb355edaabd81b778087613409a8932fbe9 Mon Sep 17 00:00:00 2001 From: Andy Adamson Date: Wed, 16 Jun 2010 09:51:02 -0400 Subject: nfs4 use mandatory attribute file type in nfs4_get_root S_ISDIR(fsinfo.fattr->mode) checks the file type rather than the mode bits, so we should be checking for the NFS_ATTR_FATTR_TYPE fattr property. Signed-off-by: Andy Adamson Signed-off-by: Trond Myklebust Cc: stable@kernel.org --- fs/nfs/getroot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/nfs/getroot.c b/fs/nfs/getroot.c index 7428f7d6273b..a70e446e1605 100644 --- a/fs/nfs/getroot.c +++ b/fs/nfs/getroot.c @@ -146,7 +146,7 @@ int nfs4_get_rootfh(struct nfs_server *server, struct nfs_fh *mntfh) goto out; } - if (!(fsinfo.fattr->valid & NFS_ATTR_FATTR_MODE) + if (!(fsinfo.fattr->valid & NFS_ATTR_FATTR_TYPE) || !S_ISDIR(fsinfo.fattr->mode)) { printk(KERN_ERR "nfs4_get_rootfh:" " getroot encountered non-directory\n"); -- cgit v1.2.3 From 44950b67a6239b377a9e6fd52c498b310bcdd713 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Thu, 17 Jun 2010 11:45:12 -0400 Subject: NFSv4.1: Ensure that we initialise the session when following a referral Put the code that is common to both the referral and ordinary mount cases into a common helper routine. Signed-off-by: Trond Myklebust --- fs/nfs/client.c | 122 +++++++++++++++++++++++--------------------------------- 1 file changed, 51 insertions(+), 71 deletions(-) (limited to 'fs') diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 7ec9b34a59f8..d25b5257b7a1 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -1286,6 +1286,55 @@ static void nfs4_session_set_rwsize(struct nfs_server *server) #endif /* CONFIG_NFS_V4_1 */ } +static int nfs4_server_common_setup(struct nfs_server *server, + struct nfs_fh *mntfh) +{ + struct nfs_fattr *fattr; + int error; + + BUG_ON(!server->nfs_client); + BUG_ON(!server->nfs_client->rpc_ops); + BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops); + + fattr = nfs_alloc_fattr(); + if (fattr == NULL) + return -ENOMEM; + + /* We must ensure the session is initialised first */ + error = nfs4_init_session(server); + if (error < 0) + goto out; + + /* Probe the root fh to retrieve its FSID and filehandle */ + error = nfs4_get_rootfh(server, mntfh); + if (error < 0) + goto out; + + dprintk("Server FSID: %llx:%llx\n", + (unsigned long long) server->fsid.major, + (unsigned long long) server->fsid.minor); + dprintk("Mount FH: %d\n", mntfh->size); + + nfs4_session_set_rwsize(server); + + error = nfs_probe_fsinfo(server, mntfh, fattr); + if (error < 0) + goto out; + + if (server->namelen == 0 || server->namelen > NFS4_MAXNAMLEN) + server->namelen = NFS4_MAXNAMLEN; + + spin_lock(&nfs_client_lock); + list_add_tail(&server->client_link, &server->nfs_client->cl_superblocks); + list_add_tail(&server->master_link, &nfs_volume_list); + spin_unlock(&nfs_client_lock); + + server->mount_time = jiffies; +out: + nfs_free_fattr(fattr); + return error; +} + /* * Create a version 4 volume record */ @@ -1346,7 +1395,6 @@ error: struct nfs_server *nfs4_create_server(const struct nfs_parsed_mount_data *data, struct nfs_fh *mntfh) { - struct nfs_fattr *fattr; struct nfs_server *server; int error; @@ -1356,55 +1404,19 @@ struct nfs_server *nfs4_create_server(const struct nfs_parsed_mount_data *data, if (!server) return ERR_PTR(-ENOMEM); - error = -ENOMEM; - fattr = nfs_alloc_fattr(); - if (fattr == NULL) - goto error; - /* set up the general RPC client */ error = nfs4_init_server(server, data); if (error < 0) goto error; - BUG_ON(!server->nfs_client); - BUG_ON(!server->nfs_client->rpc_ops); - BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops); - - error = nfs4_init_session(server); - if (error < 0) - goto error; - - /* Probe the root fh to retrieve its FSID */ - error = nfs4_get_rootfh(server, mntfh); + error = nfs4_server_common_setup(server, mntfh); if (error < 0) goto error; - dprintk("Server FSID: %llx:%llx\n", - (unsigned long long) server->fsid.major, - (unsigned long long) server->fsid.minor); - dprintk("Mount FH: %d\n", mntfh->size); - - nfs4_session_set_rwsize(server); - - error = nfs_probe_fsinfo(server, mntfh, fattr); - if (error < 0) - goto error; - - if (server->namelen == 0 || server->namelen > NFS4_MAXNAMLEN) - server->namelen = NFS4_MAXNAMLEN; - - spin_lock(&nfs_client_lock); - list_add_tail(&server->client_link, &server->nfs_client->cl_superblocks); - list_add_tail(&server->master_link, &nfs_volume_list); - spin_unlock(&nfs_client_lock); - - server->mount_time = jiffies; dprintk("<-- nfs4_create_server() = %p\n", server); - nfs_free_fattr(fattr); return server; error: - nfs_free_fattr(fattr); nfs_free_server(server); dprintk("<-- nfs4_create_server() = error %d\n", error); return ERR_PTR(error); @@ -1418,7 +1430,6 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data, { struct nfs_client *parent_client; struct nfs_server *server, *parent_server; - struct nfs_fattr *fattr; int error; dprintk("--> nfs4_create_referral_server()\n"); @@ -1427,11 +1438,6 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data, if (!server) return ERR_PTR(-ENOMEM); - error = -ENOMEM; - fattr = nfs_alloc_fattr(); - if (fattr == NULL) - goto error; - parent_server = NFS_SB(data->sb); parent_client = parent_server->nfs_client; @@ -1456,40 +1462,14 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data, if (error < 0) goto error; - BUG_ON(!server->nfs_client); - BUG_ON(!server->nfs_client->rpc_ops); - BUG_ON(!server->nfs_client->rpc_ops->file_inode_ops); - - /* Probe the root fh to retrieve its FSID and filehandle */ - error = nfs4_get_rootfh(server, mntfh); - if (error < 0) - goto error; - - /* probe the filesystem info for this server filesystem */ - error = nfs_probe_fsinfo(server, mntfh, fattr); + error = nfs4_server_common_setup(server, mntfh); if (error < 0) goto error; - if (server->namelen == 0 || server->namelen > NFS4_MAXNAMLEN) - server->namelen = NFS4_MAXNAMLEN; - - dprintk("Referral FSID: %llx:%llx\n", - (unsigned long long) server->fsid.major, - (unsigned long long) server->fsid.minor); - - spin_lock(&nfs_client_lock); - list_add_tail(&server->client_link, &server->nfs_client->cl_superblocks); - list_add_tail(&server->master_link, &nfs_volume_list); - spin_unlock(&nfs_client_lock); - - server->mount_time = jiffies; - - nfs_free_fattr(fattr); dprintk("<-- nfs_create_referral_server() = %p\n", server); return server; error: - nfs_free_fattr(fattr); nfs_free_server(server); dprintk("<-- nfs4_create_referral_server() = error %d\n", error); return ERR_PTR(error); -- cgit v1.2.3 From 0be8189f2c87fcc747d6a4a657a0b6e2161b2318 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Fri, 18 Jun 2010 12:23:58 -0400 Subject: NFSv4: Ensure that /proc/self/mountinfo displays the minor version number Currently, we do not display the minor version mount parameter in the /proc mount info. Signed-off-by: Trond Myklebust Cc: stable@kernel.org --- fs/nfs/super.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/nfs/super.c b/fs/nfs/super.c index 04214fc5c304..f9df16de4a56 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -570,6 +570,22 @@ static void nfs_show_mountd_options(struct seq_file *m, struct nfs_server *nfss, nfs_show_mountd_netid(m, nfss, showdefaults); } +#ifdef CONFIG_NFS_V4 +static void nfs_show_nfsv4_options(struct seq_file *m, struct nfs_server *nfss, + int showdefaults) +{ + struct nfs_client *clp = nfss->nfs_client; + + seq_printf(m, ",clientaddr=%s", clp->cl_ipaddr); + seq_printf(m, ",minorversion=%u", clp->cl_minorversion); +} +#else +static void nfs_show_nfsv4_options(struct seq_file *m, struct nfs_server *nfss, + int showdefaults) +{ +} +#endif + /* * Describe the mount options in force on this server representation */ @@ -631,11 +647,9 @@ static void nfs_show_mount_options(struct seq_file *m, struct nfs_server *nfss, if (version != 4) nfs_show_mountd_options(m, nfss, showdefaults); + else + nfs_show_nfsv4_options(m, nfss, showdefaults); -#ifdef CONFIG_NFS_V4 - if (clp->rpc_ops->version == 4) - seq_printf(m, ",clientaddr=%s", clp->cl_ipaddr); -#endif if (nfss->options & NFS_OPTION_FSCACHE) seq_printf(m, ",fsc"); } -- cgit v1.2.3 From d3f6baaa34c54040b3ef30950e59b54ac0624b21 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Tue, 22 Jun 2010 08:52:39 -0400 Subject: NFSv4: Fix an embarassing typo in encode_attrs() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Apparently, we have never been able to set the atime correctly from the NFSv4 client. Reported-by: 小倉一夫 Signed-off-by: Trond Myklebust Cc: stable@kernel.org --- fs/nfs/nfs4xdr.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 6bdef28efa33..65c8dae4b267 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -862,8 +862,8 @@ static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const bmval1 |= FATTR4_WORD1_TIME_ACCESS_SET; *p++ = cpu_to_be32(NFS4_SET_TO_CLIENT_TIME); *p++ = cpu_to_be32(0); - *p++ = cpu_to_be32(iap->ia_mtime.tv_sec); - *p++ = cpu_to_be32(iap->ia_mtime.tv_nsec); + *p++ = cpu_to_be32(iap->ia_atime.tv_sec); + *p++ = cpu_to_be32(iap->ia_atime.tv_nsec); } else if (iap->ia_valid & ATTR_ATIME) { bmval1 |= FATTR4_WORD1_TIME_ACCESS_SET; -- cgit v1.2.3 From d5f8d3fe72594f2e896b407f78daf24f37ef4d53 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Wed, 16 Jun 2010 09:52:25 -0400 Subject: NFSv41: Fix a memory leak in nfs41_proc_async_sequence() If the call to rpc_call_async() fails, then the arguments will not be freed, since there will be no call to nfs41_sequence_call_done Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) (limited to 'fs') diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 70015dd60a98..89be778a6543 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -5078,18 +5078,27 @@ static int nfs4_proc_sequence(struct nfs_client *clp, struct rpc_cred *cred) &res, args.sa_cache_this, 1); } +struct nfs4_sequence_data { + struct nfs_client *clp; + struct nfs4_sequence_args args; + struct nfs4_sequence_res res; +}; + static void nfs41_sequence_release(void *data) { - struct nfs_client *clp = (struct nfs_client *)data; + struct nfs4_sequence_data *calldata = data; + struct nfs_client *clp = calldata->clp; if (atomic_read(&clp->cl_count) > 1) nfs4_schedule_state_renewal(clp); nfs_put_client(clp); + kfree(calldata); } static void nfs41_sequence_call_done(struct rpc_task *task, void *data) { - struct nfs_client *clp = (struct nfs_client *)data; + struct nfs4_sequence_data *calldata = data; + struct nfs_client *clp = calldata->clp; nfs41_sequence_done(clp, task->tk_msg.rpc_resp, task->tk_status); @@ -5106,19 +5115,16 @@ static void nfs41_sequence_call_done(struct rpc_task *task, void *data) } dprintk("%s rpc_cred %p\n", __func__, task->tk_msg.rpc_cred); out: - kfree(task->tk_msg.rpc_argp); - kfree(task->tk_msg.rpc_resp); - dprintk("<-- %s\n", __func__); } static void nfs41_sequence_prepare(struct rpc_task *task, void *data) { - struct nfs_client *clp; + struct nfs4_sequence_data *calldata = data; + struct nfs_client *clp = calldata->clp; struct nfs4_sequence_args *args; struct nfs4_sequence_res *res; - clp = (struct nfs_client *)data; args = task->tk_msg.rpc_argp; res = task->tk_msg.rpc_resp; @@ -5136,8 +5142,7 @@ static const struct rpc_call_ops nfs41_sequence_ops = { static int nfs41_proc_async_sequence(struct nfs_client *clp, struct rpc_cred *cred) { - struct nfs4_sequence_args *args; - struct nfs4_sequence_res *res; + struct nfs4_sequence_data *calldata; struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SEQUENCE], .rpc_cred = cred, @@ -5145,20 +5150,18 @@ static int nfs41_proc_async_sequence(struct nfs_client *clp, if (!atomic_inc_not_zero(&clp->cl_count)) return -EIO; - args = kzalloc(sizeof(*args), GFP_NOFS); - res = kzalloc(sizeof(*res), GFP_NOFS); - if (!args || !res) { - kfree(args); - kfree(res); + calldata = kmalloc(sizeof(*calldata), GFP_NOFS); + if (calldata == NULL) { nfs_put_client(clp); return -ENOMEM; } - res->sr_slotid = NFS4_MAX_SLOT_TABLE; - msg.rpc_argp = args; - msg.rpc_resp = res; + calldata->res.sr_slotid = NFS4_MAX_SLOT_TABLE; + msg.rpc_argp = &calldata->args; + msg.rpc_resp = &calldata->res; + calldata->clp = clp; return rpc_call_async(clp->cl_rpcclient, &msg, RPC_TASK_SOFT, - &nfs41_sequence_ops, (void *)clp); + &nfs41_sequence_ops, calldata); } struct nfs4_reclaim_complete_data { -- cgit v1.2.3 From 2a6e26cdb8f17b1075c2dfd8f2f3c341bac4f441 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Wed, 16 Jun 2010 09:52:25 -0400 Subject: NFSv4.1: Clean up nfs4_setup_sequence Firstly, there is little point in first zeroing out the entire struct nfs4_sequence_res, and then initialising all fields save one. Just initialise the last field to zero... Secondly, nfs41_setup_sequence() has only 2 possible return values: 0, or -EAGAIN, so there is no 'terminate rpc task' case. Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'fs') diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 89be778a6543..4bfc0b7c428f 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -480,7 +480,6 @@ static int nfs41_setup_sequence(struct nfs4_session *session, if (res->sr_slotid != NFS4_MAX_SLOT_TABLE) return 0; - memset(res, 0, sizeof(*res)); res->sr_slotid = NFS4_MAX_SLOT_TABLE; tbl = &session->fc_slot_table; @@ -525,6 +524,7 @@ static int nfs41_setup_sequence(struct nfs4_session *session, res->sr_session = session; res->sr_slotid = slotid; res->sr_renewal_time = jiffies; + res->sr_status_flags = 0; /* * sr_status is only set in decode_sequence, and so will remain * set to 1 if an rpc level failure occurs. @@ -548,11 +548,6 @@ int nfs4_setup_sequence(struct nfs_client *clp, goto out; ret = nfs41_setup_sequence(clp->cl_session, args, res, cache_reply, task); - if (ret && ret != -EAGAIN) { - /* terminate rpc task */ - task->tk_status = ret; - task->tk_action = NULL; - } out: dprintk("<-- %s status=%d\n", __func__, ret); return ret; -- cgit v1.2.3 From d185a334c748b3ca9de1f3a293fd8a9cf68378ab Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Wed, 16 Jun 2010 09:52:25 -0400 Subject: NFSv4.1: Simplify nfs41_sequence_done() Nobody uses the rpc_status parameter. It is not obvious why we need the struct nfs_client argument either, when we already have that information in the session. Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) (limited to 'fs') diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 4bfc0b7c428f..fc653c3bc557 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -370,12 +370,11 @@ static void nfs41_check_drain_session_complete(struct nfs4_session *ses) complete(&ses->complete); } -static void nfs41_sequence_free_slot(const struct nfs_client *clp, - struct nfs4_sequence_res *res) +static void nfs41_sequence_free_slot(struct nfs4_sequence_res *res) { struct nfs4_slot_table *tbl; - tbl = &clp->cl_session->fc_slot_table; + tbl = &res->sr_session->fc_slot_table; if (res->sr_slotid == NFS4_MAX_SLOT_TABLE) { /* just wake up the next guy waiting since * we may have not consumed a slot after all */ @@ -385,14 +384,12 @@ static void nfs41_sequence_free_slot(const struct nfs_client *clp, spin_lock(&tbl->slot_tbl_lock); nfs4_free_slot(tbl, res->sr_slotid); - nfs41_check_drain_session_complete(clp->cl_session); + nfs41_check_drain_session_complete(res->sr_session); spin_unlock(&tbl->slot_tbl_lock); res->sr_slotid = NFS4_MAX_SLOT_TABLE; } -static void nfs41_sequence_done(struct nfs_client *clp, - struct nfs4_sequence_res *res, - int rpc_status) +static void nfs41_sequence_done(struct nfs4_sequence_res *res) { unsigned long timestamp; struct nfs4_slot_table *tbl; @@ -413,7 +410,8 @@ static void nfs41_sequence_done(struct nfs_client *clp, /* Check the SEQUENCE operation status */ if (res->sr_status == 0) { - tbl = &clp->cl_session->fc_slot_table; + struct nfs_client *clp = res->sr_session->clp; + tbl = &res->sr_session->fc_slot_table; slot = tbl->slots + res->sr_slotid; /* Update the slot's sequence and clientid lease timer */ ++slot->seq_nr; @@ -429,7 +427,7 @@ static void nfs41_sequence_done(struct nfs_client *clp, out: /* The session may be reset by one of the error handlers. */ dprintk("%s: Error %d free the slot \n", __func__, res->sr_status); - nfs41_sequence_free_slot(clp, res); + nfs41_sequence_free_slot(res); } /* @@ -582,7 +580,7 @@ static void nfs41_call_sync_done(struct rpc_task *task, void *calldata) { struct nfs41_call_sync_data *data = calldata; - nfs41_sequence_done(data->clp, data->seq_res, task->tk_status); + nfs41_sequence_done(data->seq_res); } struct rpc_call_ops nfs41_call_sync_ops = { @@ -662,7 +660,7 @@ static void nfs4_sequence_done(const struct nfs_server *server, { #ifdef CONFIG_NFS_V4_1 if (nfs4_has_session(server->nfs_client)) - nfs41_sequence_done(server->nfs_client, res, rpc_status); + nfs41_sequence_done(res); #endif /* CONFIG_NFS_V4_1 */ } @@ -4606,7 +4604,7 @@ static void nfs4_get_lease_time_done(struct rpc_task *task, void *calldata) (struct nfs4_get_lease_time_data *)calldata; dprintk("--> %s\n", __func__); - nfs41_sequence_done(data->clp, &data->res->lr_seq_res, task->tk_status); + nfs41_sequence_done(&data->res->lr_seq_res); switch (task->tk_status) { case -NFS4ERR_DELAY: case -NFS4ERR_GRACE: @@ -5095,7 +5093,7 @@ static void nfs41_sequence_call_done(struct rpc_task *task, void *data) struct nfs4_sequence_data *calldata = data; struct nfs_client *clp = calldata->clp; - nfs41_sequence_done(clp, task->tk_msg.rpc_resp, task->tk_status); + nfs41_sequence_done(task->tk_msg.rpc_resp); if (task->tk_status < 0) { dprintk("%s ERROR %d\n", __func__, task->tk_status); @@ -5184,7 +5182,7 @@ static void nfs4_reclaim_complete_done(struct rpc_task *task, void *data) struct nfs4_sequence_res *res = &calldata->res.seq_res; dprintk("--> %s\n", __func__); - nfs41_sequence_done(clp, res, task->tk_status); + nfs41_sequence_done(res); switch (task->tk_status) { case 0: case -NFS4ERR_COMPLETE_ALREADY: -- cgit v1.2.3 From aa5190d0ed7d042c6d7d89fe8101558a912eee73 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Wed, 16 Jun 2010 09:52:25 -0400 Subject: NFSv4: Kill nfs4_async_handle_error() abuses by NFSv4.1 Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 78 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 41 insertions(+), 37 deletions(-) (limited to 'fs') diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index fc653c3bc557..a00932c1215e 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -3457,9 +3457,11 @@ static int nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t buflen } static int -_nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server, struct nfs_client *clp, struct nfs4_state *state) +nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server, struct nfs4_state *state) { - if (!clp || task->tk_status >= 0) + struct nfs_client *clp = server->nfs_client; + + if (task->tk_status >= 0) return 0; switch(task->tk_status) { case -NFS4ERR_ADMIN_REVOKED: @@ -3491,8 +3493,7 @@ _nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server, return -EAGAIN; #endif /* CONFIG_NFS_V4_1 */ case -NFS4ERR_DELAY: - if (server) - nfs_inc_server_stats(server, NFSIOS_DELAY); + nfs_inc_server_stats(server, NFSIOS_DELAY); case -NFS4ERR_GRACE: case -EKEYEXPIRED: rpc_delay(task, NFS4_POLL_RETRY_MAX); @@ -3513,12 +3514,6 @@ do_state_recovery: return -EAGAIN; } -static int -nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server, struct nfs4_state *state) -{ - return _nfs4_async_handle_error(task, server, server->nfs_client, state); -} - int nfs4_proc_setclientid(struct nfs_client *clp, u32 program, unsigned short port, struct rpc_cred *cred, struct nfs4_setclientid_res *res) @@ -5088,6 +5083,19 @@ static void nfs41_sequence_release(void *data) kfree(calldata); } +static int nfs41_sequence_handle_errors(struct rpc_task *task, struct nfs_client *clp) +{ + switch(task->tk_status) { + case -NFS4ERR_DELAY: + case -EKEYEXPIRED: + rpc_delay(task, NFS4_POLL_RETRY_MAX); + return -EAGAIN; + default: + nfs4_schedule_state_recovery(clp); + } + return 0; +} + static void nfs41_sequence_call_done(struct rpc_task *task, void *data) { struct nfs4_sequence_data *calldata = data; @@ -5100,9 +5108,8 @@ static void nfs41_sequence_call_done(struct rpc_task *task, void *data) if (atomic_read(&clp->cl_count) == 1) goto out; - if (_nfs4_async_handle_error(task, NULL, clp, NULL) - == -EAGAIN) { - nfs_restart_rpc(task, clp); + if (nfs41_sequence_handle_errors(task, clp) == -EAGAIN) { + rpc_restart_call_prepare(task); return; } } @@ -5175,6 +5182,23 @@ static void nfs4_reclaim_complete_prepare(struct rpc_task *task, void *data) rpc_call_start(task); } +static int nfs41_reclaim_complete_handle_errors(struct rpc_task *task, struct nfs_client *clp) +{ + switch(task->tk_status) { + case 0: + case -NFS4ERR_COMPLETE_ALREADY: + case -NFS4ERR_WRONG_CRED: /* What to do here? */ + break; + case -NFS4ERR_DELAY: + case -EKEYEXPIRED: + rpc_delay(task, NFS4_POLL_RETRY_MAX); + return -EAGAIN; + default: + nfs4_schedule_state_recovery(clp); + } + return 0; +} + static void nfs4_reclaim_complete_done(struct rpc_task *task, void *data) { struct nfs4_reclaim_complete_data *calldata = data; @@ -5183,31 +5207,11 @@ static void nfs4_reclaim_complete_done(struct rpc_task *task, void *data) dprintk("--> %s\n", __func__); nfs41_sequence_done(res); - switch (task->tk_status) { - case 0: - case -NFS4ERR_COMPLETE_ALREADY: - break; - case -NFS4ERR_BADSESSION: - case -NFS4ERR_DEADSESSION: - /* - * Handle the session error, but do not retry the operation, as - * we have no way of telling whether the clientid had to be - * reset before we got our reply. If reset, a new wave of - * reclaim operations will follow, containing their own reclaim - * complete. We don't want our retry to get on the way of - * recovery by incorrectly indicating to the server that we're - * done reclaiming state since the process had to be restarted. - */ - _nfs4_async_handle_error(task, NULL, clp, NULL); - break; - default: - if (_nfs4_async_handle_error( - task, NULL, clp, NULL) == -EAGAIN) { - rpc_restart_call_prepare(task); - return; - } - } + if (nfs41_reclaim_complete_handle_errors(task, clp) == -EAGAIN) { + rpc_restart_call_prepare(task); + return; + } dprintk("<-- %s\n", __func__); } -- cgit v1.2.3 From 71ac6da9944e2c9ec73ca64ca7bca74428858585 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Wed, 16 Jun 2010 09:52:26 -0400 Subject: NFSv4.1: Merge the nfs41_proc_async_sequence() and nfs4_proc_sequence() Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 68 +++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 44 insertions(+), 24 deletions(-) (limited to 'fs') diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index a00932c1215e..75847e4898a0 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -5048,24 +5048,6 @@ int nfs4_init_session(struct nfs_server *server) /* * Renew the cl_session lease. */ -static int nfs4_proc_sequence(struct nfs_client *clp, struct rpc_cred *cred) -{ - struct nfs4_sequence_args args; - struct nfs4_sequence_res res; - - struct rpc_message msg = { - .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SEQUENCE], - .rpc_argp = &args, - .rpc_resp = &res, - .rpc_cred = cred, - }; - - args.sa_cache_this = 0; - - return nfs4_call_sync_sequence(clp, clp->cl_rpcclient, &msg, &args, - &res, args.sa_cache_this, 1); -} - struct nfs4_sequence_data { struct nfs_client *clp; struct nfs4_sequence_args args; @@ -5139,29 +5121,67 @@ static const struct rpc_call_ops nfs41_sequence_ops = { .rpc_release = nfs41_sequence_release, }; -static int nfs41_proc_async_sequence(struct nfs_client *clp, - struct rpc_cred *cred) +static struct rpc_task *_nfs41_proc_sequence(struct nfs_client *clp, struct rpc_cred *cred) { struct nfs4_sequence_data *calldata; struct rpc_message msg = { .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SEQUENCE], .rpc_cred = cred, }; + struct rpc_task_setup task_setup_data = { + .rpc_client = clp->cl_rpcclient, + .rpc_message = &msg, + .callback_ops = &nfs41_sequence_ops, + .flags = RPC_TASK_ASYNC | RPC_TASK_SOFT, + }; if (!atomic_inc_not_zero(&clp->cl_count)) - return -EIO; + return ERR_PTR(-EIO); calldata = kmalloc(sizeof(*calldata), GFP_NOFS); if (calldata == NULL) { nfs_put_client(clp); - return -ENOMEM; + return ERR_PTR(-ENOMEM); } calldata->res.sr_slotid = NFS4_MAX_SLOT_TABLE; msg.rpc_argp = &calldata->args; msg.rpc_resp = &calldata->res; calldata->clp = clp; + task_setup_data.callback_data = calldata; - return rpc_call_async(clp->cl_rpcclient, &msg, RPC_TASK_SOFT, - &nfs41_sequence_ops, calldata); + return rpc_run_task(&task_setup_data); +} + +static int nfs41_proc_async_sequence(struct nfs_client *clp, struct rpc_cred *cred) +{ + struct rpc_task *task; + int ret = 0; + + task = _nfs41_proc_sequence(clp, cred); + if (IS_ERR(task)) + ret = PTR_ERR(task); + else + rpc_put_task(task); + dprintk("<-- %s status=%d\n", __func__, ret); + return ret; +} + +static int nfs4_proc_sequence(struct nfs_client *clp, struct rpc_cred *cred) +{ + struct rpc_task *task; + int ret; + + task = _nfs41_proc_sequence(clp, cred); + if (IS_ERR(task)) { + ret = PTR_ERR(task); + goto out; + } + ret = rpc_wait_for_completion_task(task); + if (!ret) + ret = task->tk_status; + rpc_put_task(task); +out: + dprintk("<-- %s status=%d\n", __func__, ret); + return ret; } struct nfs4_reclaim_complete_data { -- cgit v1.2.3 From 035168ab39f66e4946d493f9ee20d11e154f332a Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Wed, 16 Jun 2010 09:52:26 -0400 Subject: NFSv4.1: Make nfs4_setup_sequence take a nfs_server argument In anticipation of the day when we have per-filesystem sessions, and also in order to allow the session to change in the event of a filesystem migration event. Signed-off-by: Trond Myklebust --- fs/nfs/nfs4_fs.h | 14 ++++++++++++-- fs/nfs/nfs4proc.c | 49 +++++++++++++++++++++++++++---------------------- fs/nfs/read.c | 2 +- fs/nfs/unlink.c | 2 +- fs/nfs/write.c | 4 ++-- 5 files changed, 43 insertions(+), 28 deletions(-) (limited to 'fs') diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index c538c6106e16..9c0aa818b41e 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -223,7 +223,12 @@ extern int nfs4_proc_fs_locations(struct inode *dir, const struct qstr *name, extern struct nfs4_state_recovery_ops *nfs4_reboot_recovery_ops[]; extern struct nfs4_state_recovery_ops *nfs4_nograce_recovery_ops[]; #if defined(CONFIG_NFS_V4_1) -extern int nfs4_setup_sequence(struct nfs_client *clp, +static inline struct nfs4_session *nfs4_get_session(const struct nfs_server *server) +{ + return server->nfs_client->cl_session; +} + +extern int nfs4_setup_sequence(const struct nfs_server *server, struct nfs4_sequence_args *args, struct nfs4_sequence_res *res, int cache_reply, struct rpc_task *task); extern void nfs4_destroy_session(struct nfs4_session *session); @@ -234,7 +239,12 @@ extern int nfs4_init_session(struct nfs_server *server); extern int nfs4_proc_get_lease_time(struct nfs_client *clp, struct nfs_fsinfo *fsinfo); #else /* CONFIG_NFS_v4_1 */ -static inline int nfs4_setup_sequence(struct nfs_client *clp, +static inline struct nfs4_session *nfs4_get_session(const struct nfs_server *server) +{ + return NULL; +} + +static inline int nfs4_setup_sequence(const struct nfs_server *server, struct nfs4_sequence_args *args, struct nfs4_sequence_res *res, int cache_reply, struct rpc_task *task) { diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 75847e4898a0..0168007b78d2 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -531,20 +531,25 @@ static int nfs41_setup_sequence(struct nfs4_session *session, return 0; } -int nfs4_setup_sequence(struct nfs_client *clp, +int nfs4_setup_sequence(const struct nfs_server *server, struct nfs4_sequence_args *args, struct nfs4_sequence_res *res, int cache_reply, struct rpc_task *task) { + struct nfs4_session *session = nfs4_get_session(server); int ret = 0; + if (session == NULL) { + args->sa_session = NULL; + res->sr_session = NULL; + goto out; + } + dprintk("--> %s clp %p session %p sr_slotid %d\n", - __func__, clp, clp->cl_session, res->sr_slotid); + __func__, session->clp, session, res->sr_slotid); - if (!nfs4_has_session(clp)) - goto out; - ret = nfs41_setup_sequence(clp->cl_session, args, res, cache_reply, + ret = nfs41_setup_sequence(session, args, res, cache_reply, task); out: dprintk("<-- %s status=%d\n", __func__, ret); @@ -552,7 +557,7 @@ out: } struct nfs41_call_sync_data { - struct nfs_client *clp; + const struct nfs_server *seq_server; struct nfs4_sequence_args *seq_args; struct nfs4_sequence_res *seq_res; int cache_reply; @@ -562,9 +567,9 @@ static void nfs41_call_sync_prepare(struct rpc_task *task, void *calldata) { struct nfs41_call_sync_data *data = calldata; - dprintk("--> %s data->clp->cl_session %p\n", __func__, - data->clp->cl_session); - if (nfs4_setup_sequence(data->clp, data->seq_args, + dprintk("--> %s data->seq_server %p\n", __func__, data->seq_server); + + if (nfs4_setup_sequence(data->seq_server, data->seq_args, data->seq_res, data->cache_reply, task)) return; rpc_call_start(task); @@ -593,8 +598,7 @@ struct rpc_call_ops nfs41_call_priv_sync_ops = { .rpc_call_done = nfs41_call_sync_done, }; -static int nfs4_call_sync_sequence(struct nfs_client *clp, - struct rpc_clnt *clnt, +static int nfs4_call_sync_sequence(struct nfs_server *server, struct rpc_message *msg, struct nfs4_sequence_args *args, struct nfs4_sequence_res *res, @@ -604,13 +608,13 @@ static int nfs4_call_sync_sequence(struct nfs_client *clp, int ret; struct rpc_task *task; struct nfs41_call_sync_data data = { - .clp = clp, + .seq_server = server, .seq_args = args, .seq_res = res, .cache_reply = cache_reply, }; struct rpc_task_setup task_setup = { - .rpc_client = clnt, + .rpc_client = server->client, .rpc_message = msg, .callback_ops = &nfs41_call_sync_ops, .callback_data = &data @@ -635,8 +639,7 @@ int _nfs4_call_sync_session(struct nfs_server *server, struct nfs4_sequence_res *res, int cache_reply) { - return nfs4_call_sync_sequence(server->nfs_client, server->client, - msg, args, res, cache_reply, 0); + return nfs4_call_sync_sequence(server, msg, args, res, cache_reply, 0); } #endif /* CONFIG_NFS_V4_1 */ @@ -1355,7 +1358,7 @@ static void nfs4_open_prepare(struct rpc_task *task, void *calldata) nfs_copy_fh(&data->o_res.fh, data->o_arg.fh); } data->timestamp = jiffies; - if (nfs4_setup_sequence(data->o_arg.server->nfs_client, + if (nfs4_setup_sequence(data->o_arg.server, &data->o_arg.seq_args, &data->o_res.seq_res, 1, task)) return; @@ -1896,7 +1899,7 @@ static void nfs4_close_prepare(struct rpc_task *task, void *data) nfs_fattr_init(calldata->res.fattr); calldata->timestamp = jiffies; - if (nfs4_setup_sequence((NFS_SERVER(calldata->inode))->nfs_client, + if (nfs4_setup_sequence(NFS_SERVER(calldata->inode), &calldata->arg.seq_args, &calldata->res.seq_res, 1, task)) return; @@ -3660,7 +3663,7 @@ static void nfs4_delegreturn_prepare(struct rpc_task *task, void *data) d_data = (struct nfs4_delegreturndata *)data; - if (nfs4_setup_sequence(d_data->res.server->nfs_client, + if (nfs4_setup_sequence(d_data->res.server, &d_data->args.seq_args, &d_data->res.seq_res, 1, task)) return; @@ -3915,7 +3918,7 @@ static void nfs4_locku_prepare(struct rpc_task *task, void *data) return; } calldata->timestamp = jiffies; - if (nfs4_setup_sequence(calldata->server->nfs_client, + if (nfs4_setup_sequence(calldata->server, &calldata->arg.seq_args, &calldata->res.seq_res, 1, task)) return; @@ -4070,7 +4073,8 @@ static void nfs4_lock_prepare(struct rpc_task *task, void *calldata) } else data->arg.new_lock_owner = 0; data->timestamp = jiffies; - if (nfs4_setup_sequence(data->server->nfs_client, &data->arg.seq_args, + if (nfs4_setup_sequence(data->server, + &data->arg.seq_args, &data->res.seq_res, 1, task)) return; rpc_call_start(task); @@ -5110,7 +5114,7 @@ static void nfs41_sequence_prepare(struct rpc_task *task, void *data) args = task->tk_msg.rpc_argp; res = task->tk_msg.rpc_resp; - if (nfs4_setup_sequence(clp, args, res, 0, task)) + if (nfs41_setup_sequence(clp->cl_session, args, res, 0, task)) return; rpc_call_start(task); } @@ -5195,7 +5199,8 @@ static void nfs4_reclaim_complete_prepare(struct rpc_task *task, void *data) struct nfs4_reclaim_complete_data *calldata = data; rpc_task_set_priority(task, RPC_PRIORITY_PRIVILEGED); - if (nfs4_setup_sequence(calldata->clp, &calldata->arg.seq_args, + if (nfs41_setup_sequence(calldata->clp->cl_session, + &calldata->arg.seq_args, &calldata->res.seq_res, 0, task)) return; diff --git a/fs/nfs/read.c b/fs/nfs/read.c index 6e2b06e6ca79..5a33a92e8168 100644 --- a/fs/nfs/read.c +++ b/fs/nfs/read.c @@ -410,7 +410,7 @@ void nfs_read_prepare(struct rpc_task *task, void *calldata) { struct nfs_read_data *data = calldata; - if (nfs4_setup_sequence(NFS_SERVER(data->inode)->nfs_client, + if (nfs4_setup_sequence(NFS_SERVER(data->inode), &data->args.seq_args, &data->res.seq_res, 0, task)) return; diff --git a/fs/nfs/unlink.c b/fs/nfs/unlink.c index a2242af6a17d..2f84adaad427 100644 --- a/fs/nfs/unlink.c +++ b/fs/nfs/unlink.c @@ -110,7 +110,7 @@ void nfs_unlink_prepare(struct rpc_task *task, void *calldata) struct nfs_unlinkdata *data = calldata; struct nfs_server *server = NFS_SERVER(data->dir); - if (nfs4_setup_sequence(server->nfs_client, &data->args.seq_args, + if (nfs4_setup_sequence(server, &data->args.seq_args, &data->res.seq_res, 1, task)) return; rpc_call_start(task); diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 91679e2631ee..03df22822c4c 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -1036,9 +1036,9 @@ out: void nfs_write_prepare(struct rpc_task *task, void *calldata) { struct nfs_write_data *data = calldata; - struct nfs_client *clp = (NFS_SERVER(data->inode))->nfs_client; - if (nfs4_setup_sequence(clp, &data->args.seq_args, + if (nfs4_setup_sequence(NFS_SERVER(data->inode), + &data->args.seq_args, &data->res.seq_res, 1, task)) return; rpc_call_start(task); -- cgit v1.2.3 From df8964554a4e19c8ddcc4d9c642c4d267662d770 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Wed, 16 Jun 2010 09:52:26 -0400 Subject: NFSv41: Further cleanup for nfs4_sequence_done Instead of testing if the nfs_client has a session, we should be testing if the struct nfs4_sequence_res was set up with one. Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) (limited to 'fs') diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 0168007b78d2..a896bfbd520a 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -430,6 +430,13 @@ out: nfs41_sequence_free_slot(res); } +static void nfs4_sequence_done(const struct nfs_server *server, + struct nfs4_sequence_res *res, int rpc_status) +{ + if (res->sr_session != NULL) + nfs41_sequence_done(res); +} + /* * nfs4_find_slot - efficiently look for a free slot * @@ -642,6 +649,11 @@ int _nfs4_call_sync_session(struct nfs_server *server, return nfs4_call_sync_sequence(server, msg, args, res, cache_reply, 0); } +#else +static void nfs4_sequence_done(const struct nfs_server *server, + struct nfs4_sequence_res *res, int rpc_status) +{ +} #endif /* CONFIG_NFS_V4_1 */ int _nfs4_call_sync(struct nfs_server *server, @@ -658,15 +670,6 @@ int _nfs4_call_sync(struct nfs_server *server, (server)->nfs_client->cl_call_sync((server), (msg), &(args)->seq_args, \ &(res)->seq_res, (cache_reply)) -static void nfs4_sequence_done(const struct nfs_server *server, - struct nfs4_sequence_res *res, int rpc_status) -{ -#ifdef CONFIG_NFS_V4_1 - if (nfs4_has_session(server->nfs_client)) - nfs41_sequence_done(res); -#endif /* CONFIG_NFS_V4_1 */ -} - static void update_changeattr(struct inode *dir, struct nfs4_change_info *cinfo) { struct nfs_inode *nfsi = NFS_I(dir); -- cgit v1.2.3 From a2118c33aad6c447ad5e0a60cfaea3939b52ce0a Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Wed, 16 Jun 2010 09:52:26 -0400 Subject: NFSv41: Don't store session state in the nfs_client->cl_state Signed-off-by: Trond Myklebust --- fs/nfs/nfs4_fs.h | 5 ++++- fs/nfs/nfs4proc.c | 4 ++-- fs/nfs/nfs4state.c | 6 ++++-- 3 files changed, 10 insertions(+), 5 deletions(-) (limited to 'fs') diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 9c0aa818b41e..bb1e95530699 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -45,10 +45,13 @@ enum nfs4_client_state { NFS4CLNT_RECLAIM_NOGRACE, NFS4CLNT_DELEGRETURN, NFS4CLNT_SESSION_RESET, - NFS4CLNT_SESSION_DRAINING, NFS4CLNT_RECALL_SLOT, }; +enum nfs4_session_state { + NFS4_SESSION_DRAINING, +}; + /* * struct rpc_sequence ensures that RPC calls are sent in the exact * order that they appear on the list. diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index a896bfbd520a..fc972c6f1ce9 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -356,7 +356,7 @@ static void nfs41_check_drain_session_complete(struct nfs4_session *ses) { struct rpc_task *task; - if (!test_bit(NFS4CLNT_SESSION_DRAINING, &ses->clp->cl_state)) { + if (!test_bit(NFS4_SESSION_DRAINING, &ses->session_state)) { task = rpc_wake_up_next(&ses->fc_slot_table.slot_tbl_waitq); if (task) rpc_task_set_priority(task, RPC_PRIORITY_PRIVILEGED); @@ -489,7 +489,7 @@ static int nfs41_setup_sequence(struct nfs4_session *session, tbl = &session->fc_slot_table; spin_lock(&tbl->slot_tbl_lock); - if (test_bit(NFS4CLNT_SESSION_DRAINING, &session->clp->cl_state) && + if (test_bit(NFS4_SESSION_DRAINING, &session->session_state) && !rpc_task_has_priority(task, RPC_PRIORITY_PRIVILEGED)) { /* * The state manager will wait until the slot table is empty. diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 34acf5926fdc..4538c56a7f05 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -145,7 +145,9 @@ static void nfs4_end_drain_session(struct nfs_client *clp) struct nfs4_session *ses = clp->cl_session; int max_slots; - if (test_and_clear_bit(NFS4CLNT_SESSION_DRAINING, &clp->cl_state)) { + if (ses == NULL) + return; + if (test_and_clear_bit(NFS4_SESSION_DRAINING, &ses->session_state)) { spin_lock(&ses->fc_slot_table.slot_tbl_lock); max_slots = ses->fc_slot_table.max_slots; while (max_slots--) { @@ -167,7 +169,7 @@ static int nfs4_begin_drain_session(struct nfs_client *clp) struct nfs4_slot_table *tbl = &ses->fc_slot_table; spin_lock(&tbl->slot_tbl_lock); - set_bit(NFS4CLNT_SESSION_DRAINING, &clp->cl_state); + set_bit(NFS4_SESSION_DRAINING, &ses->session_state); if (tbl->highest_used_slotid != -1) { INIT_COMPLETION(ses->complete); spin_unlock(&tbl->slot_tbl_lock); -- cgit v1.2.3 From 97dc135947181a6670949a480da56c3ebf8d3715 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Wed, 16 Jun 2010 09:52:26 -0400 Subject: NFSv41: Clean up the NFSv4.1 minor version specific operations Signed-off-by: Trond Myklebust --- fs/nfs/client.c | 14 ++++++-------- fs/nfs/nfs4_fs.h | 11 +++++++++++ fs/nfs/nfs4proc.c | 21 ++++++++++++++++++++- include/linux/nfs_fs_sb.h | 7 ++----- 4 files changed, 39 insertions(+), 14 deletions(-) (limited to 'fs') diff --git a/fs/nfs/client.c b/fs/nfs/client.c index d25b5257b7a1..1df708fd4205 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -150,6 +150,7 @@ static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_ clp->cl_boot_time = CURRENT_TIME; clp->cl_state = 1 << NFS4CLNT_LEASE_EXPIRED; clp->cl_minorversion = cl_init->minorversion; + clp->cl_mvops = nfs_v4_minor_ops[cl_init->minorversion]; #endif cred = rpc_lookup_machine_cred(); if (!IS_ERR(cred)) @@ -178,7 +179,7 @@ static void nfs4_clear_client_minor_version(struct nfs_client *clp) clp->cl_session = NULL; } - clp->cl_call_sync = _nfs4_call_sync; + clp->cl_mvops = nfs_v4_minor_ops[0]; #endif /* CONFIG_NFS_V4_1 */ } @@ -188,7 +189,7 @@ static void nfs4_clear_client_minor_version(struct nfs_client *clp) static void nfs4_destroy_callback(struct nfs_client *clp) { if (__test_and_clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state)) - nfs_callback_down(clp->cl_minorversion); + nfs_callback_down(clp->cl_mvops->minor_version); } static void nfs4_shutdown_client(struct nfs_client *clp) @@ -1126,7 +1127,7 @@ static int nfs4_init_callback(struct nfs_client *clp) return error; } - error = nfs_callback_up(clp->cl_minorversion, + error = nfs_callback_up(clp->cl_mvops->minor_version, clp->cl_rpcclient->cl_xprt); if (error < 0) { dprintk("%s: failed to start callback. Error = %d\n", @@ -1143,10 +1144,8 @@ static int nfs4_init_callback(struct nfs_client *clp) */ static int nfs4_init_client_minor_version(struct nfs_client *clp) { - clp->cl_call_sync = _nfs4_call_sync; - #if defined(CONFIG_NFS_V4_1) - if (clp->cl_minorversion) { + if (clp->cl_mvops->minor_version) { struct nfs4_session *session = NULL; /* * Create the session and mark it expired. @@ -1158,7 +1157,6 @@ static int nfs4_init_client_minor_version(struct nfs_client *clp) return -ENOMEM; clp->cl_session = session; - clp->cl_call_sync = _nfs4_call_sync_session; } #endif /* CONFIG_NFS_V4_1 */ @@ -1454,7 +1452,7 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data, data->authflavor, parent_server->client->cl_xprt->prot, parent_server->client->cl_timeout, - parent_client->cl_minorversion); + parent_client->cl_mvops->minor_version); if (error < 0) goto error; diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index bb1e95530699..5b01705e30f9 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -52,6 +52,16 @@ enum nfs4_session_state { NFS4_SESSION_DRAINING, }; +struct nfs4_minor_version_ops { + u32 minor_version; + + int (*call_sync)(struct nfs_server *server, + struct rpc_message *msg, + struct nfs4_sequence_args *args, + struct nfs4_sequence_res *res, + int cache_reply); +}; + /* * struct rpc_sequence ensures that RPC calls are sent in the exact * order that they appear on the list. @@ -260,6 +270,7 @@ static inline int nfs4_init_session(struct nfs_server *server) } #endif /* CONFIG_NFS_V4_1 */ +extern const struct nfs4_minor_version_ops *nfs_v4_minor_ops[]; extern struct nfs4_state_maintenance_ops *nfs4_state_renewal_ops[]; extern const u32 nfs4_fattr_bitmap[2]; diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index fc972c6f1ce9..a938daf333da 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -667,7 +667,7 @@ int _nfs4_call_sync(struct nfs_server *server, } #define nfs4_call_sync(server, msg, args, res, cache_reply) \ - (server)->nfs_client->cl_call_sync((server), (msg), &(args)->seq_args, \ + (server)->nfs_client->cl_mvops->call_sync((server), (msg), &(args)->seq_args, \ &(res)->seq_res, (cache_reply)) static void update_changeattr(struct inode *dir, struct nfs4_change_info *cinfo) @@ -5353,6 +5353,18 @@ struct nfs4_state_maintenance_ops nfs41_state_renewal_ops = { }; #endif +static const struct nfs4_minor_version_ops nfs_v4_0_minor_ops = { + .minor_version = 0, + .call_sync = _nfs4_call_sync, +}; + +#if defined(CONFIG_NFS_V4_1) +static const struct nfs4_minor_version_ops nfs_v4_1_minor_ops = { + .minor_version = 1, + .call_sync = _nfs4_call_sync_session, +}; +#endif + /* * Per minor version reboot and network partition recovery ops */ @@ -5378,6 +5390,13 @@ struct nfs4_state_maintenance_ops *nfs4_state_renewal_ops[] = { #endif }; +const struct nfs4_minor_version_ops *nfs_v4_minor_ops[] = { + [0] = &nfs_v4_0_minor_ops, +#if defined(CONFIG_NFS_V4_1) + [1] = &nfs_v4_1_minor_ops, +#endif +}; + static const struct inode_operations nfs4_file_inode_operations = { .permission = nfs_permission, .getattr = nfs_getattr, diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h index d6e10a4c06e5..c82ee7cd6288 100644 --- a/include/linux/nfs_fs_sb.h +++ b/include/linux/nfs_fs_sb.h @@ -15,6 +15,7 @@ struct nlm_host; struct nfs4_sequence_args; struct nfs4_sequence_res; struct nfs_server; +struct nfs4_minor_version_ops; /* * The nfs_client identifies our client state to the server. @@ -70,11 +71,7 @@ struct nfs_client { */ char cl_ipaddr[48]; unsigned char cl_id_uniquifier; - int (* cl_call_sync)(struct nfs_server *server, - struct rpc_message *msg, - struct nfs4_sequence_args *args, - struct nfs4_sequence_res *res, - int cache_reply); + const struct nfs4_minor_version_ops *cl_mvops; #endif /* CONFIG_NFS_V4 */ #ifdef CONFIG_NFS_V4_1 -- cgit v1.2.3 From c48f4f3541e67881c9eb7c46e052f5ece48ef530 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Wed, 16 Jun 2010 09:52:27 -0400 Subject: NFSv41: Convert the various reboot recovery ops etc to minor version ops Signed-off-by: Trond Myklebust --- fs/nfs/nfs4_fs.h | 6 +++--- fs/nfs/nfs4proc.c | 31 ++++++------------------------- fs/nfs/nfs4renewd.c | 4 ++-- fs/nfs/nfs4state.c | 15 +++++++-------- 4 files changed, 18 insertions(+), 38 deletions(-) (limited to 'fs') diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 5b01705e30f9..1ff0ea2a8461 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -60,6 +60,9 @@ struct nfs4_minor_version_ops { struct nfs4_sequence_args *args, struct nfs4_sequence_res *res, int cache_reply); + const struct nfs4_state_recovery_ops *reboot_recovery_ops; + const struct nfs4_state_recovery_ops *nograce_recovery_ops; + const struct nfs4_state_maintenance_ops *state_renewal_ops; }; /* @@ -233,8 +236,6 @@ extern int nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fh extern int nfs4_proc_fs_locations(struct inode *dir, const struct qstr *name, struct nfs4_fs_locations *fs_locations, struct page *page); -extern struct nfs4_state_recovery_ops *nfs4_reboot_recovery_ops[]; -extern struct nfs4_state_recovery_ops *nfs4_nograce_recovery_ops[]; #if defined(CONFIG_NFS_V4_1) static inline struct nfs4_session *nfs4_get_session(const struct nfs_server *server) { @@ -271,7 +272,6 @@ static inline int nfs4_init_session(struct nfs_server *server) #endif /* CONFIG_NFS_V4_1 */ extern const struct nfs4_minor_version_ops *nfs_v4_minor_ops[]; -extern struct nfs4_state_maintenance_ops *nfs4_state_renewal_ops[]; extern const u32 nfs4_fattr_bitmap[2]; extern const u32 nfs4_statfs_bitmap[2]; diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index a938daf333da..9f222f57e75b 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -5356,40 +5356,21 @@ struct nfs4_state_maintenance_ops nfs41_state_renewal_ops = { static const struct nfs4_minor_version_ops nfs_v4_0_minor_ops = { .minor_version = 0, .call_sync = _nfs4_call_sync, + .reboot_recovery_ops = &nfs40_reboot_recovery_ops, + .nograce_recovery_ops = &nfs40_nograce_recovery_ops, + .state_renewal_ops = &nfs40_state_renewal_ops, }; #if defined(CONFIG_NFS_V4_1) static const struct nfs4_minor_version_ops nfs_v4_1_minor_ops = { .minor_version = 1, .call_sync = _nfs4_call_sync_session, + .reboot_recovery_ops = &nfs41_reboot_recovery_ops, + .nograce_recovery_ops = &nfs41_nograce_recovery_ops, + .state_renewal_ops = &nfs41_state_renewal_ops, }; #endif -/* - * Per minor version reboot and network partition recovery ops - */ - -struct nfs4_state_recovery_ops *nfs4_reboot_recovery_ops[] = { - &nfs40_reboot_recovery_ops, -#if defined(CONFIG_NFS_V4_1) - &nfs41_reboot_recovery_ops, -#endif -}; - -struct nfs4_state_recovery_ops *nfs4_nograce_recovery_ops[] = { - &nfs40_nograce_recovery_ops, -#if defined(CONFIG_NFS_V4_1) - &nfs41_nograce_recovery_ops, -#endif -}; - -struct nfs4_state_maintenance_ops *nfs4_state_renewal_ops[] = { - &nfs40_state_renewal_ops, -#if defined(CONFIG_NFS_V4_1) - &nfs41_state_renewal_ops, -#endif -}; - const struct nfs4_minor_version_ops *nfs_v4_minor_ops[] = { [0] = &nfs_v4_0_minor_ops, #if defined(CONFIG_NFS_V4_1) diff --git a/fs/nfs/nfs4renewd.c b/fs/nfs/nfs4renewd.c index d87f10327b72..72b6c580af13 100644 --- a/fs/nfs/nfs4renewd.c +++ b/fs/nfs/nfs4renewd.c @@ -54,14 +54,14 @@ void nfs4_renew_state(struct work_struct *work) { - struct nfs4_state_maintenance_ops *ops; + const struct nfs4_state_maintenance_ops *ops; struct nfs_client *clp = container_of(work, struct nfs_client, cl_renewd.work); struct rpc_cred *cred; long lease; unsigned long last, now; - ops = nfs4_state_renewal_ops[clp->cl_minorversion]; + ops = clp->cl_mvops->state_renewal_ops; dprintk("%s: start\n", __func__); /* Are there any active superblocks? */ if (list_empty(&clp->cl_superblocks)) diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 4538c56a7f05..c3c6b8aeb209 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -1122,8 +1122,7 @@ static void nfs4_state_end_reclaim_reboot(struct nfs_client *clp) if (!test_and_clear_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state)) return; - nfs4_reclaim_complete(clp, - nfs4_reboot_recovery_ops[clp->cl_minorversion]); + nfs4_reclaim_complete(clp, clp->cl_mvops->reboot_recovery_ops); for (pos = rb_first(&clp->cl_state_owners); pos != NULL; pos = rb_next(pos)) { sp = rb_entry(pos, struct nfs4_state_owner, so_client_node); @@ -1213,8 +1212,8 @@ restart: static int nfs4_check_lease(struct nfs_client *clp) { struct rpc_cred *cred; - struct nfs4_state_maintenance_ops *ops = - nfs4_state_renewal_ops[clp->cl_minorversion]; + const struct nfs4_state_maintenance_ops *ops = + clp->cl_mvops->state_renewal_ops; int status = -NFS4ERR_EXPIRED; /* Is the client already known to have an expired lease? */ @@ -1237,8 +1236,8 @@ out: static int nfs4_reclaim_lease(struct nfs_client *clp) { struct rpc_cred *cred; - struct nfs4_state_recovery_ops *ops = - nfs4_reboot_recovery_ops[clp->cl_minorversion]; + const struct nfs4_state_recovery_ops *ops = + clp->cl_mvops->reboot_recovery_ops; int status = -ENOENT; cred = ops->get_clid_cred(clp); @@ -1446,7 +1445,7 @@ static void nfs4_state_manager(struct nfs_client *clp) /* First recover reboot state... */ if (test_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state)) { status = nfs4_do_reclaim(clp, - nfs4_reboot_recovery_ops[clp->cl_minorversion]); + clp->cl_mvops->reboot_recovery_ops); if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) || test_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state)) continue; @@ -1460,7 +1459,7 @@ static void nfs4_state_manager(struct nfs_client *clp) /* Now recover expired state... */ if (test_and_clear_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state)) { status = nfs4_do_reclaim(clp, - nfs4_nograce_recovery_ops[clp->cl_minorversion]); + clp->cl_mvops->nograce_recovery_ops); if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) || test_bit(NFS4CLNT_SESSION_RESET, &clp->cl_state) || test_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state)) -- cgit v1.2.3 From e047a10c1293ee0ab20258154e7f3ebf8ad502d6 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Wed, 16 Jun 2010 09:52:27 -0400 Subject: NFSv41: Fix nfs_async_inode_return_delegation() ugliness Signed-off-by: Trond Myklebust --- fs/nfs/callback_proc.c | 13 +------------ fs/nfs/delegation.c | 6 ++---- fs/nfs/delegation.h | 4 +--- fs/nfs/nfs4_fs.h | 2 ++ fs/nfs/nfs4proc.c | 2 ++ 5 files changed, 8 insertions(+), 19 deletions(-) (limited to 'fs') diff --git a/fs/nfs/callback_proc.c b/fs/nfs/callback_proc.c index a08770a7e857..7445dd0ae3f3 100644 --- a/fs/nfs/callback_proc.c +++ b/fs/nfs/callback_proc.c @@ -62,16 +62,6 @@ out: return res->status; } -static int (*nfs_validate_delegation_stateid(struct nfs_client *clp))(struct nfs_delegation *, const nfs4_stateid *) -{ -#if defined(CONFIG_NFS_V4_1) - if (clp->cl_minorversion > 0) - return nfs41_validate_delegation_stateid; -#endif - return nfs4_validate_delegation_stateid; -} - - __be32 nfs4_callback_recall(struct cb_recallargs *args, void *dummy) { struct nfs_client *clp; @@ -92,8 +82,7 @@ __be32 nfs4_callback_recall(struct cb_recallargs *args, void *dummy) inode = nfs_delegation_find_inode(clp, &args->fh); if (inode != NULL) { /* Set up a helper thread to actually return the delegation */ - switch (nfs_async_inode_return_delegation(inode, &args->stateid, - nfs_validate_delegation_stateid(clp))) { + switch (nfs_async_inode_return_delegation(inode, &args->stateid)) { case 0: res = 0; break; diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index 301634543974..f34f4ac52b81 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c @@ -471,9 +471,7 @@ void nfs_expire_unreferenced_delegations(struct nfs_client *clp) /* * Asynchronous delegation recall! */ -int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *stateid, - int (*validate_stateid)(struct nfs_delegation *delegation, - const nfs4_stateid *stateid)) +int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *stateid) { struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; struct nfs_delegation *delegation; @@ -481,7 +479,7 @@ int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *s rcu_read_lock(); delegation = rcu_dereference(NFS_I(inode)->delegation); - if (!validate_stateid(delegation, stateid)) { + if (!clp->cl_mvops->validate_stateid(delegation, stateid)) { rcu_read_unlock(); return -ENOENT; } diff --git a/fs/nfs/delegation.h b/fs/nfs/delegation.h index 69e7b8140122..2026304bda19 100644 --- a/fs/nfs/delegation.h +++ b/fs/nfs/delegation.h @@ -34,9 +34,7 @@ enum { int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res); void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res); int nfs_inode_return_delegation(struct inode *inode); -int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *stateid, - int (*validate_stateid)(struct nfs_delegation *delegation, - const nfs4_stateid *stateid)); +int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *stateid); void nfs_inode_return_delegation_noreclaim(struct inode *inode); struct inode *nfs_delegation_find_inode(struct nfs_client *clp, const struct nfs_fh *fhandle); diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 1ff0ea2a8461..04eff16b34d3 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -60,6 +60,8 @@ struct nfs4_minor_version_ops { struct nfs4_sequence_args *args, struct nfs4_sequence_res *res, int cache_reply); + int (*validate_stateid)(struct nfs_delegation *, + const nfs4_stateid *); const struct nfs4_state_recovery_ops *reboot_recovery_ops; const struct nfs4_state_recovery_ops *nograce_recovery_ops; const struct nfs4_state_maintenance_ops *state_renewal_ops; diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 9f222f57e75b..d1ab0c36e939 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -5356,6 +5356,7 @@ struct nfs4_state_maintenance_ops nfs41_state_renewal_ops = { static const struct nfs4_minor_version_ops nfs_v4_0_minor_ops = { .minor_version = 0, .call_sync = _nfs4_call_sync, + .validate_stateid = nfs4_validate_delegation_stateid, .reboot_recovery_ops = &nfs40_reboot_recovery_ops, .nograce_recovery_ops = &nfs40_nograce_recovery_ops, .state_renewal_ops = &nfs40_state_renewal_ops, @@ -5365,6 +5366,7 @@ static const struct nfs4_minor_version_ops nfs_v4_0_minor_ops = { static const struct nfs4_minor_version_ops nfs_v4_1_minor_ops = { .minor_version = 1, .call_sync = _nfs4_call_sync_session, + .validate_stateid = nfs41_validate_delegation_stateid, .reboot_recovery_ops = &nfs41_reboot_recovery_ops, .nograce_recovery_ops = &nfs41_nograce_recovery_ops, .state_renewal_ops = &nfs41_state_renewal_ops, -- cgit v1.2.3 From a4432345352c2be157ed844603147ac2c82f209c Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Wed, 16 Jun 2010 09:52:27 -0400 Subject: NFSv41: Deprecate nfs_client->cl_minorversion Signed-off-by: Trond Myklebust --- fs/nfs/nfs4xdr.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'fs') diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 65c8dae4b267..1f7781d636ae 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -1172,7 +1172,7 @@ static inline void encode_createmode(struct xdr_stream *xdr, const struct nfs_op break; default: clp = arg->server->nfs_client; - if (clp->cl_minorversion > 0) { + if (clp->cl_mvops->minor_version > 0) { if (nfs4_has_persistent_session(clp)) { *p = cpu_to_be32(NFS4_CREATE_GUARDED); encode_attrs(xdr, arg->u.attrs, arg->server); @@ -1704,7 +1704,7 @@ static u32 nfs4_xdr_minorversion(const struct nfs4_sequence_args *args) { #if defined(CONFIG_NFS_V4_1) if (args->sa_session) - return args->sa_session->clp->cl_minorversion; + return args->sa_session->clp->cl_mvops->minor_version; #endif /* CONFIG_NFS_V4_1 */ return 0; } @@ -2395,7 +2395,7 @@ static int nfs4_xdr_enc_exchange_id(struct rpc_rqst *req, uint32_t *p, { struct xdr_stream xdr; struct compound_hdr hdr = { - .minorversion = args->client->cl_minorversion, + .minorversion = args->client->cl_mvops->minor_version, }; xdr_init_encode(&xdr, &req->rq_snd_buf, p); @@ -2413,7 +2413,7 @@ static int nfs4_xdr_enc_create_session(struct rpc_rqst *req, uint32_t *p, { struct xdr_stream xdr; struct compound_hdr hdr = { - .minorversion = args->client->cl_minorversion, + .minorversion = args->client->cl_mvops->minor_version, }; xdr_init_encode(&xdr, &req->rq_snd_buf, p); @@ -2431,7 +2431,7 @@ static int nfs4_xdr_enc_destroy_session(struct rpc_rqst *req, uint32_t *p, { struct xdr_stream xdr; struct compound_hdr hdr = { - .minorversion = session->clp->cl_minorversion, + .minorversion = session->clp->cl_mvops->minor_version, }; xdr_init_encode(&xdr, &req->rq_snd_buf, p); -- cgit v1.2.3 From d77d76ffb638bd013782138cca6d8f4918c5afd6 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Wed, 16 Jun 2010 09:52:27 -0400 Subject: NFSv41: Clean up exclusive create Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 17 ++++++----------- include/linux/nfs_xdr.h | 6 ++++-- 2 files changed, 10 insertions(+), 13 deletions(-) (limited to 'fs') diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index d1ab0c36e939..5d87563d0c1a 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -744,19 +744,14 @@ static struct nfs4_opendata *nfs4_opendata_alloc(struct path *path, p->o_arg.server = server; p->o_arg.bitmask = server->attr_bitmask; p->o_arg.claim = NFS4_OPEN_CLAIM_NULL; - if (flags & O_EXCL) { - if (nfs4_has_persistent_session(server->nfs_client)) { - /* GUARDED */ - p->o_arg.u.attrs = &p->attrs; - memcpy(&p->attrs, attrs, sizeof(p->attrs)); - } else { /* EXCLUSIVE4_1 */ - u32 *s = (u32 *) p->o_arg.u.verifier.data; - s[0] = jiffies; - s[1] = current->pid; - } - } else if (flags & O_CREAT) { + if (flags & O_CREAT) { + u32 *s; + p->o_arg.u.attrs = &p->attrs; memcpy(&p->attrs, attrs, sizeof(p->attrs)); + s = (u32 *) p->o_arg.u.verifier.data; + s[0] = jiffies; + s[1] = current->pid; } p->c_arg.fh = &p->o_res.fh; p->c_arg.stateid = &p->o_res.stateid; diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 51914d7d6cc4..a319cb926abf 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -196,8 +196,10 @@ struct nfs_openargs { __u64 clientid; __u64 id; union { - struct iattr * attrs; /* UNCHECKED, GUARDED */ - nfs4_verifier verifier; /* EXCLUSIVE */ + struct { + struct iattr * attrs; /* UNCHECKED, GUARDED */ + nfs4_verifier verifier; /* EXCLUSIVE */ + }; nfs4_stateid delegation; /* CLAIM_DELEGATE_CUR */ fmode_t delegation_type; /* CLAIM_PREVIOUS */ } u; -- cgit v1.2.3 From fe74ba3a8db003410e48863a5cafa6ac90674540 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Wed, 16 Jun 2010 09:52:27 -0400 Subject: NFSv41: Cleanup for nfs4_alloc_session. There is no reason to change the nfs_client state every time we allocate a new session. Move that line into nfs4_init_client_minor_version. Signed-off-by: Trond Myklebust --- fs/nfs/client.c | 7 +++++++ fs/nfs/nfs4proc.c | 7 ------- 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'fs') diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 1df708fd4205..4e7df2adb212 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -1157,6 +1157,13 @@ static int nfs4_init_client_minor_version(struct nfs_client *clp) return -ENOMEM; clp->cl_session = session; + /* + * The create session reply races with the server back + * channel probe. Mark the client NFS_CS_SESSION_INITING + * so that the client back channel can find the + * nfs_client struct + */ + clp->cl_cons_state = NFS_CS_SESSION_INITING; } #endif /* CONFIG_NFS_V4_1 */ diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 5d87563d0c1a..b4132fe14282 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -4795,13 +4795,6 @@ struct nfs4_session *nfs4_alloc_session(struct nfs_client *clp) if (!session) return NULL; - /* - * The create session reply races with the server back - * channel probe. Mark the client NFS_CS_SESSION_INITING - * so that the client back channel can find the - * nfs_client struct - */ - clp->cl_cons_state = NFS_CS_SESSION_INITING; init_completion(&session->complete); tbl = &session->fc_slot_table; -- cgit v1.2.3 From 1055d76d91e69c8ea9fb748db4d4a53b6384df31 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Wed, 16 Jun 2010 09:52:27 -0400 Subject: NFSv4.1: There is no need to init the session more than once... Set up a flag to ensure that is indeed the case. Signed-off-by: Trond Myklebust --- fs/nfs/nfs4_fs.h | 1 + fs/nfs/nfs4proc.c | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 04eff16b34d3..a986f13e8039 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -49,6 +49,7 @@ enum nfs4_client_state { }; enum nfs4_session_state { + NFS4_SESSION_INITING, NFS4_SESSION_DRAINING, }; diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index b4132fe14282..243d6c72cfa1 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -4807,6 +4807,8 @@ struct nfs4_session *nfs4_alloc_session(struct nfs_client *clp) spin_lock_init(&tbl->slot_tbl_lock); rpc_init_wait_queue(&tbl->slot_tbl_waitq, "BackChannel Slot table"); + session->session_state = 1<clp = clp; return session; } @@ -5023,6 +5025,10 @@ int nfs4_init_session(struct nfs_server *server) if (!nfs4_has_session(clp)) return 0; + session = clp->cl_session; + if (!test_and_clear_bit(NFS4_SESSION_INITING, &session->session_state)) + return 0; + rsize = server->rsize; if (rsize == 0) rsize = NFS_MAX_FILE_IO_SIZE; @@ -5030,7 +5036,6 @@ int nfs4_init_session(struct nfs_server *server) if (wsize == 0) wsize = NFS_MAX_FILE_IO_SIZE; - session = clp->cl_session; session->fc_attrs.max_rqst_sz = wsize + nfs41_maxwrite_overhead; session->fc_attrs.max_resp_sz = rsize + nfs41_maxread_overhead; -- cgit v1.2.3 From 1817176a86352f65210139d4c794ad2d19fc6b63 Mon Sep 17 00:00:00 2001 From: Dan Rosenberg Date: Thu, 24 Jun 2010 12:07:47 +1000 Subject: xfs: prevent swapext from operating on write-only files This patch prevents user "foo" from using the SWAPEXT ioctl to swap a write-only file owned by user "bar" into a file owned by "foo" and subsequently reading it. It does so by checking that the file descriptors passed to the ioctl are also opened for reading. Signed-off-by: Dan Rosenberg Reviewed-by: Christoph Hellwig --- fs/xfs/xfs_dfrag.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/xfs/xfs_dfrag.c b/fs/xfs/xfs_dfrag.c index 5bba29a07812..7f159d2a429a 100644 --- a/fs/xfs/xfs_dfrag.c +++ b/fs/xfs/xfs_dfrag.c @@ -69,7 +69,9 @@ xfs_swapext( goto out; } - if (!(file->f_mode & FMODE_WRITE) || (file->f_flags & O_APPEND)) { + if (!(file->f_mode & FMODE_WRITE) || + !(file->f_mode & FMODE_READ) || + (file->f_flags & O_APPEND)) { error = XFS_ERROR(EBADF); goto out_put_file; } @@ -81,6 +83,7 @@ xfs_swapext( } if (!(tmp_file->f_mode & FMODE_WRITE) || + !(tmp_file->f_mode & FMODE_READ) || (tmp_file->f_flags & O_APPEND)) { error = XFS_ERROR(EBADF); goto out_put_tmp_file; -- cgit v1.2.3 From 7dce11dbac54fce777eea0f5fb25b2694ccd7900 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 23 Jun 2010 18:11:11 +1000 Subject: xfs: always use iget in bulkstat The non-coherent bulkstat versionsthat look directly at the inode buffers causes various problems with performance optimizations that make increased use of just logging inodes. This patch makes bulkstat always use iget, which should be fast enough for normal use with the radix-tree based inode cache introduced a while ago. Signed-off-by: Christoph Hellwig Reviewed-by: Dave Chinner --- fs/xfs/linux-2.6/xfs_ioctl.c | 7 +- fs/xfs/linux-2.6/xfs_ioctl32.c | 12 +- fs/xfs/quota/xfs_qm.c | 11 +- fs/xfs/quota/xfs_qm_syscalls.c | 16 +-- fs/xfs/xfs_itable.c | 281 ++++++----------------------------------- fs/xfs/xfs_itable.h | 14 -- 6 files changed, 59 insertions(+), 282 deletions(-) (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_ioctl.c b/fs/xfs/linux-2.6/xfs_ioctl.c index 699b60cbab9c..e59a81062830 100644 --- a/fs/xfs/linux-2.6/xfs_ioctl.c +++ b/fs/xfs/linux-2.6/xfs_ioctl.c @@ -679,10 +679,9 @@ xfs_ioc_bulkstat( error = xfs_bulkstat_single(mp, &inlast, bulkreq.ubuffer, &done); else /* XFS_IOC_FSBULKSTAT */ - error = xfs_bulkstat(mp, &inlast, &count, - (bulkstat_one_pf)xfs_bulkstat_one, NULL, - sizeof(xfs_bstat_t), bulkreq.ubuffer, - BULKSTAT_FG_QUICK, &done); + error = xfs_bulkstat(mp, &inlast, &count, xfs_bulkstat_one, + sizeof(xfs_bstat_t), bulkreq.ubuffer, + &done); if (error) return -error; diff --git a/fs/xfs/linux-2.6/xfs_ioctl32.c b/fs/xfs/linux-2.6/xfs_ioctl32.c index 9287135e9bfc..e1d8380b049d 100644 --- a/fs/xfs/linux-2.6/xfs_ioctl32.c +++ b/fs/xfs/linux-2.6/xfs_ioctl32.c @@ -237,15 +237,13 @@ xfs_bulkstat_one_compat( xfs_ino_t ino, /* inode number to get data for */ void __user *buffer, /* buffer to place output in */ int ubsize, /* size of buffer */ - void *private_data, /* my private data */ xfs_daddr_t bno, /* starting bno of inode cluster */ int *ubused, /* bytes used by me */ - void *dibuff, /* on-disk inode buffer */ int *stat) /* BULKSTAT_RV_... */ { return xfs_bulkstat_one_int(mp, ino, buffer, ubsize, xfs_bulkstat_one_fmt_compat, bno, - ubused, dibuff, stat); + ubused, stat); } /* copied from xfs_ioctl.c */ @@ -298,13 +296,11 @@ xfs_compat_ioc_bulkstat( int res; error = xfs_bulkstat_one_compat(mp, inlast, bulkreq.ubuffer, - sizeof(compat_xfs_bstat_t), - NULL, 0, NULL, NULL, &res); + sizeof(compat_xfs_bstat_t), 0, NULL, &res); } else if (cmd == XFS_IOC_FSBULKSTAT_32) { error = xfs_bulkstat(mp, &inlast, &count, - xfs_bulkstat_one_compat, NULL, - sizeof(compat_xfs_bstat_t), bulkreq.ubuffer, - BULKSTAT_FG_QUICK, &done); + xfs_bulkstat_one_compat, sizeof(compat_xfs_bstat_t), + bulkreq.ubuffer, &done); } else error = XFS_ERROR(EINVAL); if (error) diff --git a/fs/xfs/quota/xfs_qm.c b/fs/xfs/quota/xfs_qm.c index 2d8b7bc792c9..f19f94c4cb9f 100644 --- a/fs/xfs/quota/xfs_qm.c +++ b/fs/xfs/quota/xfs_qm.c @@ -1632,10 +1632,8 @@ xfs_qm_dqusage_adjust( xfs_ino_t ino, /* inode number to get data for */ void __user *buffer, /* not used */ int ubsize, /* not used */ - void *private_data, /* not used */ xfs_daddr_t bno, /* starting block of inode cluster */ int *ubused, /* not used */ - void *dip, /* on-disk inode pointer (not used) */ int *res) /* result code value */ { xfs_inode_t *ip; @@ -1796,12 +1794,13 @@ xfs_qm_quotacheck( * Iterate thru all the inodes in the file system, * adjusting the corresponding dquot counters in core. */ - if ((error = xfs_bulkstat(mp, &lastino, &count, - xfs_qm_dqusage_adjust, NULL, - structsz, NULL, BULKSTAT_FG_IGET, &done))) + error = xfs_bulkstat(mp, &lastino, &count, + xfs_qm_dqusage_adjust, + structsz, NULL, &done); + if (error) break; - } while (! done); + } while (!done); /* * We've made all the changes that we need to make incore. diff --git a/fs/xfs/quota/xfs_qm_syscalls.c b/fs/xfs/quota/xfs_qm_syscalls.c index 92b002f1805f..99a2d8e0f173 100644 --- a/fs/xfs/quota/xfs_qm_syscalls.c +++ b/fs/xfs/quota/xfs_qm_syscalls.c @@ -1109,10 +1109,8 @@ xfs_qm_internalqcheck_adjust( xfs_ino_t ino, /* inode number to get data for */ void __user *buffer, /* not used */ int ubsize, /* not used */ - void *private_data, /* not used */ xfs_daddr_t bno, /* starting block of inode cluster */ int *ubused, /* not used */ - void *dip, /* not used */ int *res) /* bulkstat result code */ { xfs_inode_t *ip; @@ -1205,15 +1203,15 @@ xfs_qm_internalqcheck( * Iterate thru all the inodes in the file system, * adjusting the corresponding dquot counters */ - if ((error = xfs_bulkstat(mp, &lastino, &count, - xfs_qm_internalqcheck_adjust, NULL, - 0, NULL, BULKSTAT_FG_IGET, &done))) { + error = xfs_bulkstat(mp, &lastino, &count, + xfs_qm_internalqcheck_adjust, + 0, NULL, &done); + if (error) { + cmn_err(CE_DEBUG, "Bulkstat returned error 0x%x", error); break; } - } while (! done); - if (error) { - cmn_err(CE_DEBUG, "Bulkstat returned error 0x%x", error); - } + } while (!done); + cmn_err(CE_DEBUG, "Checking results against system dquots"); for (i = 0; i < qmtest_hashmask; i++) { xfs_dqtest_t *d, *n; diff --git a/fs/xfs/xfs_itable.c b/fs/xfs/xfs_itable.c index b1b801e4a28e..83d7827793e4 100644 --- a/fs/xfs/xfs_itable.c +++ b/fs/xfs/xfs_itable.c @@ -49,24 +49,41 @@ xfs_internal_inum( (ino == mp->m_sb.sb_uquotino || ino == mp->m_sb.sb_gquotino))); } -STATIC int -xfs_bulkstat_one_iget( - xfs_mount_t *mp, /* mount point for filesystem */ - xfs_ino_t ino, /* inode number to get data for */ - xfs_daddr_t bno, /* starting bno of inode cluster */ - xfs_bstat_t *buf, /* return buffer */ - int *stat) /* BULKSTAT_RV_... */ +/* + * Return stat information for one inode. + * Return 0 if ok, else errno. + */ +int +xfs_bulkstat_one_int( + struct xfs_mount *mp, /* mount point for filesystem */ + xfs_ino_t ino, /* inode to get data for */ + void __user *buffer, /* buffer to place output in */ + int ubsize, /* size of buffer */ + bulkstat_one_fmt_pf formatter, /* formatter, copy to user */ + xfs_daddr_t bno, /* starting bno of cluster */ + int *ubused, /* bytes used by me */ + int *stat) /* BULKSTAT_RV_... */ { - xfs_icdinode_t *dic; /* dinode core info pointer */ - xfs_inode_t *ip; /* incore inode pointer */ - struct inode *inode; - int error; + struct xfs_icdinode *dic; /* dinode core info pointer */ + struct xfs_inode *ip; /* incore inode pointer */ + struct inode *inode; + struct xfs_bstat *buf; /* return buffer */ + int error = 0; /* error value */ + + *stat = BULKSTAT_RV_NOTHING; + + if (!buffer || xfs_internal_inum(mp, ino)) + return XFS_ERROR(EINVAL); + + buf = kmem_alloc(sizeof(*buf), KM_SLEEP | KM_MAYFAIL); + if (!buf) + return XFS_ERROR(ENOMEM); error = xfs_iget(mp, NULL, ino, XFS_IGET_BULKSTAT, XFS_ILOCK_SHARED, &ip, bno); if (error) { *stat = BULKSTAT_RV_NOTHING; - return error; + goto out_free; } ASSERT(ip != NULL); @@ -127,77 +144,16 @@ xfs_bulkstat_one_iget( buf->bs_blocks = dic->di_nblocks + ip->i_delayed_blks; break; } - xfs_iput(ip, XFS_ILOCK_SHARED); - return error; -} -STATIC void -xfs_bulkstat_one_dinode( - xfs_mount_t *mp, /* mount point for filesystem */ - xfs_ino_t ino, /* inode number to get data for */ - xfs_dinode_t *dic, /* dinode inode pointer */ - xfs_bstat_t *buf) /* return buffer */ -{ - /* - * The inode format changed when we moved the link count and - * made it 32 bits long. If this is an old format inode, - * convert it in memory to look like a new one. If it gets - * flushed to disk we will convert back before flushing or - * logging it. We zero out the new projid field and the old link - * count field. We'll handle clearing the pad field (the remains - * of the old uuid field) when we actually convert the inode to - * the new format. We don't change the version number so that we - * can distinguish this from a real new format inode. - */ - if (dic->di_version == 1) { - buf->bs_nlink = be16_to_cpu(dic->di_onlink); - buf->bs_projid = 0; - } else { - buf->bs_nlink = be32_to_cpu(dic->di_nlink); - buf->bs_projid = be16_to_cpu(dic->di_projid); - } + error = formatter(buffer, ubsize, ubused, buf); - buf->bs_ino = ino; - buf->bs_mode = be16_to_cpu(dic->di_mode); - buf->bs_uid = be32_to_cpu(dic->di_uid); - buf->bs_gid = be32_to_cpu(dic->di_gid); - buf->bs_size = be64_to_cpu(dic->di_size); - buf->bs_atime.tv_sec = be32_to_cpu(dic->di_atime.t_sec); - buf->bs_atime.tv_nsec = be32_to_cpu(dic->di_atime.t_nsec); - buf->bs_mtime.tv_sec = be32_to_cpu(dic->di_mtime.t_sec); - buf->bs_mtime.tv_nsec = be32_to_cpu(dic->di_mtime.t_nsec); - buf->bs_ctime.tv_sec = be32_to_cpu(dic->di_ctime.t_sec); - buf->bs_ctime.tv_nsec = be32_to_cpu(dic->di_ctime.t_nsec); - buf->bs_xflags = xfs_dic2xflags(dic); - buf->bs_extsize = be32_to_cpu(dic->di_extsize) << mp->m_sb.sb_blocklog; - buf->bs_extents = be32_to_cpu(dic->di_nextents); - buf->bs_gen = be32_to_cpu(dic->di_gen); - memset(buf->bs_pad, 0, sizeof(buf->bs_pad)); - buf->bs_dmevmask = be32_to_cpu(dic->di_dmevmask); - buf->bs_dmstate = be16_to_cpu(dic->di_dmstate); - buf->bs_aextents = be16_to_cpu(dic->di_anextents); - buf->bs_forkoff = XFS_DFORK_BOFF(dic); + if (!error) + *stat = BULKSTAT_RV_DIDONE; - switch (dic->di_format) { - case XFS_DINODE_FMT_DEV: - buf->bs_rdev = xfs_dinode_get_rdev(dic); - buf->bs_blksize = BLKDEV_IOSIZE; - buf->bs_blocks = 0; - break; - case XFS_DINODE_FMT_LOCAL: - case XFS_DINODE_FMT_UUID: - buf->bs_rdev = 0; - buf->bs_blksize = mp->m_sb.sb_blocksize; - buf->bs_blocks = 0; - break; - case XFS_DINODE_FMT_EXTENTS: - case XFS_DINODE_FMT_BTREE: - buf->bs_rdev = 0; - buf->bs_blksize = mp->m_sb.sb_blocksize; - buf->bs_blocks = be64_to_cpu(dic->di_nblocks); - break; - } + out_free: + kmem_free(buf); + return error; } /* Return 0 on success or positive error */ @@ -217,118 +173,19 @@ xfs_bulkstat_one_fmt( return 0; } -/* - * Return stat information for one inode. - * Return 0 if ok, else errno. - */ -int /* error status */ -xfs_bulkstat_one_int( - xfs_mount_t *mp, /* mount point for filesystem */ - xfs_ino_t ino, /* inode number to get data for */ - void __user *buffer, /* buffer to place output in */ - int ubsize, /* size of buffer */ - bulkstat_one_fmt_pf formatter, /* formatter, copy to user */ - xfs_daddr_t bno, /* starting bno of inode cluster */ - int *ubused, /* bytes used by me */ - void *dibuff, /* on-disk inode buffer */ - int *stat) /* BULKSTAT_RV_... */ -{ - xfs_bstat_t *buf; /* return buffer */ - int error = 0; /* error value */ - xfs_dinode_t *dip; /* dinode inode pointer */ - - dip = (xfs_dinode_t *)dibuff; - *stat = BULKSTAT_RV_NOTHING; - - if (!buffer || xfs_internal_inum(mp, ino)) - return XFS_ERROR(EINVAL); - - buf = kmem_alloc(sizeof(*buf), KM_SLEEP); - - if (dip == NULL) { - /* We're not being passed a pointer to a dinode. This happens - * if BULKSTAT_FG_IGET is selected. Do the iget. - */ - error = xfs_bulkstat_one_iget(mp, ino, bno, buf, stat); - if (error) - goto out_free; - } else { - xfs_bulkstat_one_dinode(mp, ino, dip, buf); - } - - error = formatter(buffer, ubsize, ubused, buf); - if (error) - goto out_free; - - *stat = BULKSTAT_RV_DIDONE; - - out_free: - kmem_free(buf); - return error; -} - int xfs_bulkstat_one( xfs_mount_t *mp, /* mount point for filesystem */ xfs_ino_t ino, /* inode number to get data for */ void __user *buffer, /* buffer to place output in */ int ubsize, /* size of buffer */ - void *private_data, /* my private data */ xfs_daddr_t bno, /* starting bno of inode cluster */ int *ubused, /* bytes used by me */ - void *dibuff, /* on-disk inode buffer */ int *stat) /* BULKSTAT_RV_... */ { return xfs_bulkstat_one_int(mp, ino, buffer, ubsize, xfs_bulkstat_one_fmt, bno, - ubused, dibuff, stat); -} - -/* - * Test to see whether we can use the ondisk inode directly, based - * on the given bulkstat flags, filling in dipp accordingly. - * Returns zero if the inode is dodgey. - */ -STATIC int -xfs_bulkstat_use_dinode( - xfs_mount_t *mp, - int flags, - xfs_buf_t *bp, - int clustidx, - xfs_dinode_t **dipp) -{ - xfs_dinode_t *dip; - unsigned int aformat; - - *dipp = NULL; - if (!bp || (flags & BULKSTAT_FG_IGET)) - return 1; - dip = (xfs_dinode_t *) - xfs_buf_offset(bp, clustidx << mp->m_sb.sb_inodelog); - /* - * Check the buffer containing the on-disk inode for di_mode == 0. - * This is to prevent xfs_bulkstat from picking up just reclaimed - * inodes that have their in-core state initialized but not flushed - * to disk yet. This is a temporary hack that would require a proper - * fix in the future. - */ - if (be16_to_cpu(dip->di_magic) != XFS_DINODE_MAGIC || - !XFS_DINODE_GOOD_VERSION(dip->di_version) || - !dip->di_mode) - return 0; - if (flags & BULKSTAT_FG_QUICK) { - *dipp = dip; - return 1; - } - /* BULKSTAT_FG_INLINE: if attr fork is local, or not there, use it */ - aformat = dip->di_aformat; - if ((XFS_DFORK_Q(dip) == 0) || - (aformat == XFS_DINODE_FMT_LOCAL) || - (aformat == XFS_DINODE_FMT_EXTENTS && !dip->di_anextents)) { - *dipp = dip; - return 1; - } - return 1; + ubused, stat); } #define XFS_BULKSTAT_UBLEFT(ubleft) ((ubleft) >= statstruct_size) @@ -342,10 +199,8 @@ xfs_bulkstat( xfs_ino_t *lastinop, /* last inode returned */ int *ubcountp, /* size of buffer/count returned */ bulkstat_one_pf formatter, /* func that'd fill a single buf */ - void *private_data,/* private data for formatter */ size_t statstruct_size, /* sizeof struct filling */ char __user *ubuffer, /* buffer with inode stats */ - int flags, /* defined in xfs_itable.h */ int *done) /* 1 if there are more stats to get */ { xfs_agblock_t agbno=0;/* allocation group block number */ @@ -380,14 +235,12 @@ xfs_bulkstat( int ubelem; /* spaces used in user's buffer */ int ubused; /* bytes used by formatter */ xfs_buf_t *bp; /* ptr to on-disk inode cluster buf */ - xfs_dinode_t *dip; /* ptr into bp for specific inode */ /* * Get the last inode value, see if there's nothing to do. */ ino = (xfs_ino_t)*lastinop; lastino = ino; - dip = NULL; agno = XFS_INO_TO_AGNO(mp, ino); agino = XFS_INO_TO_AGINO(mp, ino); if (agno >= mp->m_sb.sb_agcount || @@ -612,37 +465,6 @@ xfs_bulkstat( irbp->ir_startino) + ((chunkidx & nimask) >> mp->m_sb.sb_inopblog); - - if (flags & (BULKSTAT_FG_QUICK | - BULKSTAT_FG_INLINE)) { - int offset; - - ino = XFS_AGINO_TO_INO(mp, agno, - agino); - bno = XFS_AGB_TO_DADDR(mp, agno, - agbno); - - /* - * Get the inode cluster buffer - */ - if (bp) - xfs_buf_relse(bp); - - error = xfs_inotobp(mp, NULL, ino, &dip, - &bp, &offset, - XFS_IGET_BULKSTAT); - - if (!error) - clustidx = offset / mp->m_sb.sb_inodesize; - if (XFS_TEST_ERROR(error != 0, - mp, XFS_ERRTAG_BULKSTAT_READ_CHUNK, - XFS_RANDOM_BULKSTAT_READ_CHUNK)) { - bp = NULL; - ubleft = 0; - rval = error; - break; - } - } } ino = XFS_AGINO_TO_INO(mp, agno, agino); bno = XFS_AGB_TO_DADDR(mp, agno, agbno); @@ -658,35 +480,13 @@ xfs_bulkstat( * when the chunk is used up. */ irbp->ir_freecount++; - if (!xfs_bulkstat_use_dinode(mp, flags, bp, - clustidx, &dip)) { - lastino = ino; - continue; - } - /* - * If we need to do an iget, cannot hold bp. - * Drop it, until starting the next cluster. - */ - if ((flags & BULKSTAT_FG_INLINE) && !dip) { - if (bp) - xfs_buf_relse(bp); - bp = NULL; - } /* * Get the inode and fill in a single buffer. - * BULKSTAT_FG_QUICK uses dip to fill it in. - * BULKSTAT_FG_IGET uses igets. - * BULKSTAT_FG_INLINE uses dip if we have an - * inline attr fork, else igets. - * See: xfs_bulkstat_one & xfs_dm_bulkstat_one. - * This is also used to count inodes/blks, etc - * in xfs_qm_quotacheck. */ ubused = statstruct_size; - error = formatter(mp, ino, ubufp, - ubleft, private_data, - bno, &ubused, dip, &fmterror); + error = formatter(mp, ino, ubufp, ubleft, bno, + &ubused, &fmterror); if (fmterror == BULKSTAT_RV_NOTHING) { if (error && error != ENOENT && error != EINVAL) { @@ -779,7 +579,7 @@ xfs_bulkstat_single( ino = (xfs_ino_t)*lastinop; error = xfs_bulkstat_one(mp, ino, buffer, sizeof(xfs_bstat_t), - NULL, 0, NULL, NULL, &res); + 0, NULL, &res); if (error) { /* * Special case way failed, do it the "long" way @@ -788,8 +588,7 @@ xfs_bulkstat_single( (*lastinop)--; count = 1; if (xfs_bulkstat(mp, lastinop, &count, xfs_bulkstat_one, - NULL, sizeof(xfs_bstat_t), buffer, - BULKSTAT_FG_IGET, done)) + sizeof(xfs_bstat_t), buffer, done)) return error; if (count == 0 || (xfs_ino_t)*lastinop != ino) return error == EFSCORRUPTED ? diff --git a/fs/xfs/xfs_itable.h b/fs/xfs/xfs_itable.h index 20792bf45946..fea03397a3ab 100644 --- a/fs/xfs/xfs_itable.h +++ b/fs/xfs/xfs_itable.h @@ -27,10 +27,8 @@ typedef int (*bulkstat_one_pf)(struct xfs_mount *mp, xfs_ino_t ino, void __user *buffer, int ubsize, - void *private_data, xfs_daddr_t bno, int *ubused, - void *dip, int *stat); /* @@ -40,13 +38,6 @@ typedef int (*bulkstat_one_pf)(struct xfs_mount *mp, #define BULKSTAT_RV_DIDONE 1 #define BULKSTAT_RV_GIVEUP 2 -/* - * Values for bulkstat flag argument. - */ -#define BULKSTAT_FG_IGET 0x1 /* Go through the buffer cache */ -#define BULKSTAT_FG_QUICK 0x2 /* No iget, walk the dinode cluster */ -#define BULKSTAT_FG_INLINE 0x4 /* No iget if inline attrs */ - /* * Return stat information in bulk (by-inode) for the filesystem. */ @@ -56,10 +47,8 @@ xfs_bulkstat( xfs_ino_t *lastino, /* last inode returned */ int *count, /* size of buffer/count returned */ bulkstat_one_pf formatter, /* func that'd fill a single buf */ - void *private_data, /* private data for formatter */ size_t statstruct_size,/* sizeof struct that we're filling */ char __user *ubuffer,/* buffer with inode stats */ - int flags, /* flag to control access method */ int *done); /* 1 if there are more stats to get */ int @@ -84,7 +73,6 @@ xfs_bulkstat_one_int( bulkstat_one_fmt_pf formatter, xfs_daddr_t bno, int *ubused, - void *dibuff, int *stat); int @@ -93,10 +81,8 @@ xfs_bulkstat_one( xfs_ino_t ino, void __user *buffer, int ubsize, - void *private_data, xfs_daddr_t bno, int *ubused, - void *dibuff, int *stat); typedef int (*inumbers_fmt_pf)( -- cgit v1.2.3 From 7124fe0a5b619d65b739477b3b55a20bf805b06d Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Thu, 24 Jun 2010 11:15:33 +1000 Subject: xfs: validate untrusted inode numbers during lookup When we decode a handle or do a bulkstat lookup, we are using an inode number we cannot trust to be valid. If we are deleting inode chunks from disk (default noikeep mode), then we cannot trust the on disk inode buffer for any given inode number to correctly reflect whether the inode has been unlinked as the di_mode nor the generation number may have been updated on disk. This is due to the fact that when we delete an inode chunk, we do not write the clusters back to disk when they are removed - instead we mark them stale to avoid them being written back potentially over the top of something that has been subsequently allocated at that location. The result is that we can have locations of disk that look like they contain valid inodes but in reality do not. Hence we cannot simply convert the inode number to a block number and read the location from disk to determine if the inode is valid or not. As a result, and XFS_IGET_BULKSTAT lookup needs to actually look the inode up in the inode allocation btree to determine if the inode number is valid or not. It should be noted even on ikeep filesystems, there is the possibility that blocks on disk may look like valid inode clusters. e.g. if there are filesystem images hosted on the filesystem. Hence even for ikeep filesystems we really need to validate that the inode number is valid before issuing the inode buffer read. Signed-off-by: Dave Chinner Reviewed-by: Christoph Hellwig --- fs/xfs/xfs_ialloc.c | 121 +++++++++++++++++++++++++++++++++------------------- 1 file changed, 78 insertions(+), 43 deletions(-) (limited to 'fs') diff --git a/fs/xfs/xfs_ialloc.c b/fs/xfs/xfs_ialloc.c index 9d884c127bb9..0c946c8e05da 100644 --- a/fs/xfs/xfs_ialloc.c +++ b/fs/xfs/xfs_ialloc.c @@ -1203,6 +1203,63 @@ error0: return error; } +STATIC int +xfs_imap_lookup( + struct xfs_mount *mp, + struct xfs_trans *tp, + xfs_agnumber_t agno, + xfs_agino_t agino, + xfs_agblock_t agbno, + xfs_agblock_t *chunk_agbno, + xfs_agblock_t *offset_agbno, + int flags) +{ + struct xfs_inobt_rec_incore rec; + struct xfs_btree_cur *cur; + struct xfs_buf *agbp; + xfs_agino_t startino; + int error; + int i; + + error = xfs_ialloc_read_agi(mp, tp, agno, &agbp); + if (error) { + xfs_fs_cmn_err(CE_ALERT, mp, "xfs_imap: " + "xfs_ialloc_read_agi() returned " + "error %d, agno %d", + error, agno); + return error; + } + + /* + * derive and lookup the exact inode record for the given agino. If the + * record cannot be found, then it's an invalid inode number and we + * should abort. + */ + cur = xfs_inobt_init_cursor(mp, tp, agbp, agno); + startino = agino & ~(XFS_IALLOC_INODES(mp) - 1); + error = xfs_inobt_lookup(cur, startino, XFS_LOOKUP_EQ, &i); + if (!error) { + if (i) + error = xfs_inobt_get_rec(cur, &rec, &i); + if (!error && i == 0) + error = EINVAL; + } + + xfs_trans_brelse(tp, agbp); + xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR); + if (error) + return error; + + /* for untrusted inodes check it is allocated first */ + if ((flags & XFS_IGET_BULKSTAT) && + (rec.ir_free & XFS_INOBT_MASK(agino - rec.ir_startino))) + return EINVAL; + + *chunk_agbno = XFS_AGINO_TO_AGBNO(mp, rec.ir_startino); + *offset_agbno = agbno - *chunk_agbno; + return 0; +} + /* * Return the location of the inode in imap, for mapping it into a buffer. */ @@ -1263,6 +1320,23 @@ xfs_imap( return XFS_ERROR(EINVAL); } + blks_per_cluster = XFS_INODE_CLUSTER_SIZE(mp) >> mp->m_sb.sb_blocklog; + + /* + * For bulkstat and handle lookups, we have an untrusted inode number + * that we have to verify is valid. We cannot do this just by reading + * the inode buffer as it may have been unlinked and removed leaving + * inodes in stale state on disk. Hence we have to do a btree lookup + * in all cases where an untrusted inode number is passed. + */ + if (flags & XFS_IGET_BULKSTAT) { + error = xfs_imap_lookup(mp, tp, agno, agino, agbno, + &chunk_agbno, &offset_agbno, flags); + if (error) + return error; + goto out_map; + } + /* * If the inode cluster size is the same as the blocksize or * smaller we get to the buffer by simple arithmetics. @@ -1277,10 +1351,8 @@ xfs_imap( return 0; } - blks_per_cluster = XFS_INODE_CLUSTER_SIZE(mp) >> mp->m_sb.sb_blocklog; - /* - * If we get a block number passed from bulkstat we can use it to + * If we get a block number passed we can use it to * find the buffer easily. */ if (imap->im_blkno) { @@ -1304,50 +1376,13 @@ xfs_imap( offset_agbno = agbno & mp->m_inoalign_mask; chunk_agbno = agbno - offset_agbno; } else { - xfs_btree_cur_t *cur; /* inode btree cursor */ - xfs_inobt_rec_incore_t chunk_rec; - xfs_buf_t *agbp; /* agi buffer */ - int i; /* temp state */ - - error = xfs_ialloc_read_agi(mp, tp, agno, &agbp); - if (error) { - xfs_fs_cmn_err(CE_ALERT, mp, "xfs_imap: " - "xfs_ialloc_read_agi() returned " - "error %d, agno %d", - error, agno); - return error; - } - - cur = xfs_inobt_init_cursor(mp, tp, agbp, agno); - error = xfs_inobt_lookup(cur, agino, XFS_LOOKUP_LE, &i); - if (error) { - xfs_fs_cmn_err(CE_ALERT, mp, "xfs_imap: " - "xfs_inobt_lookup() failed"); - goto error0; - } - - error = xfs_inobt_get_rec(cur, &chunk_rec, &i); - if (error) { - xfs_fs_cmn_err(CE_ALERT, mp, "xfs_imap: " - "xfs_inobt_get_rec() failed"); - goto error0; - } - if (i == 0) { -#ifdef DEBUG - xfs_fs_cmn_err(CE_ALERT, mp, "xfs_imap: " - "xfs_inobt_get_rec() failed"); -#endif /* DEBUG */ - error = XFS_ERROR(EINVAL); - } - error0: - xfs_trans_brelse(tp, agbp); - xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR); + error = xfs_imap_lookup(mp, tp, agno, agino, agbno, + &chunk_agbno, &offset_agbno, flags); if (error) return error; - chunk_agbno = XFS_AGINO_TO_AGBNO(mp, chunk_rec.ir_startino); - offset_agbno = agbno - chunk_agbno; } +out_map: ASSERT(agbno >= chunk_agbno); cluster_agbno = chunk_agbno + ((offset_agbno / blks_per_cluster) * blks_per_cluster); -- cgit v1.2.3 From 1920779e67cbf5ea8afef317777c5bf2b8096188 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Thu, 24 Jun 2010 11:15:47 +1000 Subject: xfs: rename XFS_IGET_BULKSTAT to XFS_IGET_UNTRUSTED Inode numbers may come from somewhere external to the filesystem (e.g. file handles, bulkstat information) and so are inherently untrusted. Rename the flag we use for these lookups to make it obvious we are doing a lookup of an untrusted inode number and need to verify it completely before trying to read it from disk. Signed-off-by: Dave Chinner Reviewed-by: Christoph Hellwig --- fs/xfs/linux-2.6/xfs_export.c | 9 ++++----- fs/xfs/xfs_ialloc.c | 11 +++++++---- fs/xfs/xfs_inode.c | 2 +- fs/xfs/xfs_inode.h | 2 +- fs/xfs/xfs_itable.c | 2 +- 5 files changed, 14 insertions(+), 12 deletions(-) (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_export.c b/fs/xfs/linux-2.6/xfs_export.c index 846b75aeb2ab..92c84a19cc9e 100644 --- a/fs/xfs/linux-2.6/xfs_export.c +++ b/fs/xfs/linux-2.6/xfs_export.c @@ -128,12 +128,11 @@ xfs_nfs_get_inode( return ERR_PTR(-ESTALE); /* - * The XFS_IGET_BULKSTAT means that an invalid inode number is just - * fine and not an indication of a corrupted filesystem. Because - * clients can send any kind of invalid file handle, e.g. after - * a restore on the server we have to deal with this case gracefully. + * The XFS_IGET_UNTRUSTED means that an invalid inode number is just + * fine and not an indication of a corrupted filesystem as clients can + * send invalid file handles and we have to handle it gracefully.. */ - error = xfs_iget(mp, NULL, ino, XFS_IGET_BULKSTAT, + error = xfs_iget(mp, NULL, ino, XFS_IGET_UNTRUSTED, XFS_ILOCK_SHARED, &ip, 0); if (error) { /* diff --git a/fs/xfs/xfs_ialloc.c b/fs/xfs/xfs_ialloc.c index 0c946c8e05da..d8fd36685eb9 100644 --- a/fs/xfs/xfs_ialloc.c +++ b/fs/xfs/xfs_ialloc.c @@ -1251,7 +1251,7 @@ xfs_imap_lookup( return error; /* for untrusted inodes check it is allocated first */ - if ((flags & XFS_IGET_BULKSTAT) && + if ((flags & XFS_IGET_UNTRUSTED) && (rec.ir_free & XFS_INOBT_MASK(agino - rec.ir_startino))) return EINVAL; @@ -1292,8 +1292,11 @@ xfs_imap( if (agno >= mp->m_sb.sb_agcount || agbno >= mp->m_sb.sb_agblocks || ino != XFS_AGINO_TO_INO(mp, agno, agino)) { #ifdef DEBUG - /* no diagnostics for bulkstat, ino comes from userspace */ - if (flags & XFS_IGET_BULKSTAT) + /* + * Don't output diagnostic information for untrusted inodes + * as they can be invalid without implying corruption. + */ + if (flags & XFS_IGET_UNTRUSTED) return XFS_ERROR(EINVAL); if (agno >= mp->m_sb.sb_agcount) { xfs_fs_cmn_err(CE_ALERT, mp, @@ -1329,7 +1332,7 @@ xfs_imap( * inodes in stale state on disk. Hence we have to do a btree lookup * in all cases where an untrusted inode number is passed. */ - if (flags & XFS_IGET_BULKSTAT) { + if (flags & XFS_IGET_UNTRUSTED) { error = xfs_imap_lookup(mp, tp, agno, agino, agbno, &chunk_agbno, &offset_agbno, flags); if (error) diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index d53c39de7d05..12c277a5e98a 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -177,7 +177,7 @@ xfs_imap_to_bp( if (unlikely(XFS_TEST_ERROR(!di_ok, mp, XFS_ERRTAG_ITOBP_INOTOBP, XFS_RANDOM_ITOBP_INOTOBP))) { - if (iget_flags & XFS_IGET_BULKSTAT) { + if (iget_flags & XFS_IGET_UNTRUSTED) { xfs_trans_brelse(tp, bp); return XFS_ERROR(EINVAL); } diff --git a/fs/xfs/xfs_inode.h b/fs/xfs/xfs_inode.h index 9965e40a4615..6b31b38244ab 100644 --- a/fs/xfs/xfs_inode.h +++ b/fs/xfs/xfs_inode.h @@ -500,7 +500,7 @@ do { \ * Flags for xfs_iget() */ #define XFS_IGET_CREATE 0x1 -#define XFS_IGET_BULKSTAT 0x2 +#define XFS_IGET_UNTRUSTED 0x2 int xfs_inotobp(struct xfs_mount *, struct xfs_trans *, xfs_ino_t, struct xfs_dinode **, diff --git a/fs/xfs/xfs_itable.c b/fs/xfs/xfs_itable.c index 83d7827793e4..2acd12fd3f25 100644 --- a/fs/xfs/xfs_itable.c +++ b/fs/xfs/xfs_itable.c @@ -80,7 +80,7 @@ xfs_bulkstat_one_int( return XFS_ERROR(ENOMEM); error = xfs_iget(mp, NULL, ino, - XFS_IGET_BULKSTAT, XFS_ILOCK_SHARED, &ip, bno); + XFS_IGET_UNTRUSTED, XFS_ILOCK_SHARED, &ip, bno); if (error) { *stat = BULKSTAT_RV_NOTHING; goto out_free; -- cgit v1.2.3 From 7b6259e7a83647948fa33a736cc832310c8d85aa Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Thu, 24 Jun 2010 11:35:17 +1000 Subject: xfs: remove block number from inode lookup code The block number comes from bulkstat based inode lookups to shortcut the mapping calculations. We ar enot able to trust anything from bulkstat, so drop the block number as well so that the correct lookups and mappings are always done. Signed-off-by: Dave Chinner Reviewed-by: Christoph Hellwig --- fs/xfs/linux-2.6/xfs_export.c | 2 +- fs/xfs/linux-2.6/xfs_ioctl32.c | 5 ++--- fs/xfs/quota/xfs_qm.c | 7 +++---- fs/xfs/quota/xfs_qm_syscalls.c | 11 +++++------ fs/xfs/xfs_ialloc.c | 16 ---------------- fs/xfs/xfs_iget.c | 10 +++------- fs/xfs/xfs_inode.c | 3 --- fs/xfs/xfs_inode.h | 4 ++-- fs/xfs/xfs_itable.c | 12 ++++-------- fs/xfs/xfs_itable.h | 3 --- fs/xfs/xfs_log_recover.c | 2 +- fs/xfs/xfs_mount.c | 2 +- fs/xfs/xfs_rtalloc.c | 4 ++-- fs/xfs/xfs_trans_inode.c | 2 +- fs/xfs/xfs_vnodeops.c | 2 +- 15 files changed, 26 insertions(+), 59 deletions(-) (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_export.c b/fs/xfs/linux-2.6/xfs_export.c index 92c84a19cc9e..e7839ee49e43 100644 --- a/fs/xfs/linux-2.6/xfs_export.c +++ b/fs/xfs/linux-2.6/xfs_export.c @@ -133,7 +133,7 @@ xfs_nfs_get_inode( * send invalid file handles and we have to handle it gracefully.. */ error = xfs_iget(mp, NULL, ino, XFS_IGET_UNTRUSTED, - XFS_ILOCK_SHARED, &ip, 0); + XFS_ILOCK_SHARED, &ip); if (error) { /* * EINVAL means the inode cluster doesn't exist anymore. diff --git a/fs/xfs/linux-2.6/xfs_ioctl32.c b/fs/xfs/linux-2.6/xfs_ioctl32.c index e1d8380b049d..52ed49e6465c 100644 --- a/fs/xfs/linux-2.6/xfs_ioctl32.c +++ b/fs/xfs/linux-2.6/xfs_ioctl32.c @@ -237,12 +237,11 @@ xfs_bulkstat_one_compat( xfs_ino_t ino, /* inode number to get data for */ void __user *buffer, /* buffer to place output in */ int ubsize, /* size of buffer */ - xfs_daddr_t bno, /* starting bno of inode cluster */ int *ubused, /* bytes used by me */ int *stat) /* BULKSTAT_RV_... */ { return xfs_bulkstat_one_int(mp, ino, buffer, ubsize, - xfs_bulkstat_one_fmt_compat, bno, + xfs_bulkstat_one_fmt_compat, ubused, stat); } @@ -296,7 +295,7 @@ xfs_compat_ioc_bulkstat( int res; error = xfs_bulkstat_one_compat(mp, inlast, bulkreq.ubuffer, - sizeof(compat_xfs_bstat_t), 0, NULL, &res); + sizeof(compat_xfs_bstat_t), 0, &res); } else if (cmd == XFS_IOC_FSBULKSTAT_32) { error = xfs_bulkstat(mp, &inlast, &count, xfs_bulkstat_one_compat, sizeof(compat_xfs_bstat_t), diff --git a/fs/xfs/quota/xfs_qm.c b/fs/xfs/quota/xfs_qm.c index f19f94c4cb9f..8c117ff2e3ab 100644 --- a/fs/xfs/quota/xfs_qm.c +++ b/fs/xfs/quota/xfs_qm.c @@ -1632,7 +1632,6 @@ xfs_qm_dqusage_adjust( xfs_ino_t ino, /* inode number to get data for */ void __user *buffer, /* not used */ int ubsize, /* not used */ - xfs_daddr_t bno, /* starting block of inode cluster */ int *ubused, /* not used */ int *res) /* result code value */ { @@ -1658,7 +1657,7 @@ xfs_qm_dqusage_adjust( * the case in all other instances. It's OK that we do this because * quotacheck is done only at mount time. */ - if ((error = xfs_iget(mp, NULL, ino, 0, XFS_ILOCK_EXCL, &ip, bno))) { + if ((error = xfs_iget(mp, NULL, ino, 0, XFS_ILOCK_EXCL, &ip))) { *res = BULKSTAT_RV_NOTHING; return error; } @@ -1888,14 +1887,14 @@ xfs_qm_init_quotainos( mp->m_sb.sb_uquotino != NULLFSINO) { ASSERT(mp->m_sb.sb_uquotino > 0); if ((error = xfs_iget(mp, NULL, mp->m_sb.sb_uquotino, - 0, 0, &uip, 0))) + 0, 0, &uip))) return XFS_ERROR(error); } if (XFS_IS_OQUOTA_ON(mp) && mp->m_sb.sb_gquotino != NULLFSINO) { ASSERT(mp->m_sb.sb_gquotino > 0); if ((error = xfs_iget(mp, NULL, mp->m_sb.sb_gquotino, - 0, 0, &gip, 0))) { + 0, 0, &gip))) { if (uip) IRELE(uip); return XFS_ERROR(error); diff --git a/fs/xfs/quota/xfs_qm_syscalls.c b/fs/xfs/quota/xfs_qm_syscalls.c index 99a2d8e0f173..b4487764e923 100644 --- a/fs/xfs/quota/xfs_qm_syscalls.c +++ b/fs/xfs/quota/xfs_qm_syscalls.c @@ -262,7 +262,7 @@ xfs_qm_scall_trunc_qfiles( } if ((flags & XFS_DQ_USER) && mp->m_sb.sb_uquotino != NULLFSINO) { - error = xfs_iget(mp, NULL, mp->m_sb.sb_uquotino, 0, 0, &qip, 0); + error = xfs_iget(mp, NULL, mp->m_sb.sb_uquotino, 0, 0, &qip); if (!error) { error = xfs_truncate_file(mp, qip); IRELE(qip); @@ -271,7 +271,7 @@ xfs_qm_scall_trunc_qfiles( if ((flags & (XFS_DQ_GROUP|XFS_DQ_PROJ)) && mp->m_sb.sb_gquotino != NULLFSINO) { - error2 = xfs_iget(mp, NULL, mp->m_sb.sb_gquotino, 0, 0, &qip, 0); + error2 = xfs_iget(mp, NULL, mp->m_sb.sb_gquotino, 0, 0, &qip); if (!error2) { error2 = xfs_truncate_file(mp, qip); IRELE(qip); @@ -417,12 +417,12 @@ xfs_qm_scall_getqstat( } if (!uip && mp->m_sb.sb_uquotino != NULLFSINO) { if (xfs_iget(mp, NULL, mp->m_sb.sb_uquotino, - 0, 0, &uip, 0) == 0) + 0, 0, &uip) == 0) tempuqip = B_TRUE; } if (!gip && mp->m_sb.sb_gquotino != NULLFSINO) { if (xfs_iget(mp, NULL, mp->m_sb.sb_gquotino, - 0, 0, &gip, 0) == 0) + 0, 0, &gip) == 0) tempgqip = B_TRUE; } if (uip) { @@ -1109,7 +1109,6 @@ xfs_qm_internalqcheck_adjust( xfs_ino_t ino, /* inode number to get data for */ void __user *buffer, /* not used */ int ubsize, /* not used */ - xfs_daddr_t bno, /* starting block of inode cluster */ int *ubused, /* not used */ int *res) /* bulkstat result code */ { @@ -1132,7 +1131,7 @@ xfs_qm_internalqcheck_adjust( ipreleased = B_FALSE; again: lock_flags = XFS_ILOCK_SHARED; - if ((error = xfs_iget(mp, NULL, ino, 0, lock_flags, &ip, bno))) { + if ((error = xfs_iget(mp, NULL, ino, 0, lock_flags, &ip))) { *res = BULKSTAT_RV_NOTHING; return (error); } diff --git a/fs/xfs/xfs_ialloc.c b/fs/xfs/xfs_ialloc.c index d8fd36685eb9..c7142a064c48 100644 --- a/fs/xfs/xfs_ialloc.c +++ b/fs/xfs/xfs_ialloc.c @@ -1354,22 +1354,6 @@ xfs_imap( return 0; } - /* - * If we get a block number passed we can use it to - * find the buffer easily. - */ - if (imap->im_blkno) { - offset = XFS_INO_TO_OFFSET(mp, ino); - ASSERT(offset < mp->m_sb.sb_inopblock); - - cluster_agbno = xfs_daddr_to_agbno(mp, imap->im_blkno); - offset += (agbno - cluster_agbno) * mp->m_sb.sb_inopblock; - - imap->im_len = XFS_FSB_TO_BB(mp, blks_per_cluster); - imap->im_boffset = (ushort)(offset << mp->m_sb.sb_inodelog); - return 0; - } - /* * If the inode chunks are aligned then use simple maths to * find the location. Otherwise we have to do a btree diff --git a/fs/xfs/xfs_iget.c b/fs/xfs/xfs_iget.c index 75df75f43d48..8f8b91be2c99 100644 --- a/fs/xfs/xfs_iget.c +++ b/fs/xfs/xfs_iget.c @@ -259,7 +259,6 @@ xfs_iget_cache_miss( xfs_trans_t *tp, xfs_ino_t ino, struct xfs_inode **ipp, - xfs_daddr_t bno, int flags, int lock_flags) { @@ -272,7 +271,7 @@ xfs_iget_cache_miss( if (!ip) return ENOMEM; - error = xfs_iread(mp, tp, ip, bno, flags); + error = xfs_iread(mp, tp, ip, flags); if (error) goto out_destroy; @@ -358,8 +357,6 @@ out_destroy: * within the file system for the inode being requested. * lock_flags -- flags indicating how to lock the inode. See the comment * for xfs_ilock() for a list of valid values. - * bno -- the block number starting the buffer containing the inode, - * if known (as by bulkstat), else 0. */ int xfs_iget( @@ -368,8 +365,7 @@ xfs_iget( xfs_ino_t ino, uint flags, uint lock_flags, - xfs_inode_t **ipp, - xfs_daddr_t bno) + xfs_inode_t **ipp) { xfs_inode_t *ip; int error; @@ -397,7 +393,7 @@ again: read_unlock(&pag->pag_ici_lock); XFS_STATS_INC(xs_ig_missed); - error = xfs_iget_cache_miss(mp, pag, tp, ino, &ip, bno, + error = xfs_iget_cache_miss(mp, pag, tp, ino, &ip, flags, lock_flags); if (error) goto out_error_or_again; diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index 12c277a5e98a..b76a829d7e20 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -787,7 +787,6 @@ xfs_iread( xfs_mount_t *mp, xfs_trans_t *tp, xfs_inode_t *ip, - xfs_daddr_t bno, uint iget_flags) { xfs_buf_t *bp; @@ -797,11 +796,9 @@ xfs_iread( /* * Fill in the location information in the in-core inode. */ - ip->i_imap.im_blkno = bno; error = xfs_imap(mp, tp, ip->i_ino, &ip->i_imap, iget_flags); if (error) return error; - ASSERT(bno == 0 || bno == ip->i_imap.im_blkno); /* * Get pointers to the on-disk inode and the buffer containing it. diff --git a/fs/xfs/xfs_inode.h b/fs/xfs/xfs_inode.h index 6b31b38244ab..78550df13cd6 100644 --- a/fs/xfs/xfs_inode.h +++ b/fs/xfs/xfs_inode.h @@ -442,7 +442,7 @@ static inline void xfs_ifunlock(xfs_inode_t *ip) * xfs_iget.c prototypes. */ int xfs_iget(struct xfs_mount *, struct xfs_trans *, xfs_ino_t, - uint, uint, xfs_inode_t **, xfs_daddr_t); + uint, uint, xfs_inode_t **); void xfs_iput(xfs_inode_t *, uint); void xfs_iput_new(xfs_inode_t *, uint); void xfs_ilock(xfs_inode_t *, uint); @@ -509,7 +509,7 @@ int xfs_itobp(struct xfs_mount *, struct xfs_trans *, struct xfs_inode *, struct xfs_dinode **, struct xfs_buf **, uint); int xfs_iread(struct xfs_mount *, struct xfs_trans *, - struct xfs_inode *, xfs_daddr_t, uint); + struct xfs_inode *, uint); void xfs_dinode_to_disk(struct xfs_dinode *, struct xfs_icdinode *); void xfs_idestroy_fork(struct xfs_inode *, int); diff --git a/fs/xfs/xfs_itable.c b/fs/xfs/xfs_itable.c index 2acd12fd3f25..2b86f8610512 100644 --- a/fs/xfs/xfs_itable.c +++ b/fs/xfs/xfs_itable.c @@ -60,7 +60,6 @@ xfs_bulkstat_one_int( void __user *buffer, /* buffer to place output in */ int ubsize, /* size of buffer */ bulkstat_one_fmt_pf formatter, /* formatter, copy to user */ - xfs_daddr_t bno, /* starting bno of cluster */ int *ubused, /* bytes used by me */ int *stat) /* BULKSTAT_RV_... */ { @@ -80,7 +79,7 @@ xfs_bulkstat_one_int( return XFS_ERROR(ENOMEM); error = xfs_iget(mp, NULL, ino, - XFS_IGET_UNTRUSTED, XFS_ILOCK_SHARED, &ip, bno); + XFS_IGET_UNTRUSTED, XFS_ILOCK_SHARED, &ip); if (error) { *stat = BULKSTAT_RV_NOTHING; goto out_free; @@ -179,13 +178,11 @@ xfs_bulkstat_one( xfs_ino_t ino, /* inode number to get data for */ void __user *buffer, /* buffer to place output in */ int ubsize, /* size of buffer */ - xfs_daddr_t bno, /* starting bno of inode cluster */ int *ubused, /* bytes used by me */ int *stat) /* BULKSTAT_RV_... */ { return xfs_bulkstat_one_int(mp, ino, buffer, ubsize, - xfs_bulkstat_one_fmt, bno, - ubused, stat); + xfs_bulkstat_one_fmt, ubused, stat); } #define XFS_BULKSTAT_UBLEFT(ubleft) ((ubleft) >= statstruct_size) @@ -485,7 +482,7 @@ xfs_bulkstat( * Get the inode and fill in a single buffer. */ ubused = statstruct_size; - error = formatter(mp, ino, ubufp, ubleft, bno, + error = formatter(mp, ino, ubufp, ubleft, &ubused, &fmterror); if (fmterror == BULKSTAT_RV_NOTHING) { if (error && error != ENOENT && @@ -578,8 +575,7 @@ xfs_bulkstat_single( */ ino = (xfs_ino_t)*lastinop; - error = xfs_bulkstat_one(mp, ino, buffer, sizeof(xfs_bstat_t), - 0, NULL, &res); + error = xfs_bulkstat_one(mp, ino, buffer, sizeof(xfs_bstat_t), 0, &res); if (error) { /* * Special case way failed, do it the "long" way diff --git a/fs/xfs/xfs_itable.h b/fs/xfs/xfs_itable.h index fea03397a3ab..97295d91d170 100644 --- a/fs/xfs/xfs_itable.h +++ b/fs/xfs/xfs_itable.h @@ -27,7 +27,6 @@ typedef int (*bulkstat_one_pf)(struct xfs_mount *mp, xfs_ino_t ino, void __user *buffer, int ubsize, - xfs_daddr_t bno, int *ubused, int *stat); @@ -71,7 +70,6 @@ xfs_bulkstat_one_int( void __user *buffer, int ubsize, bulkstat_one_fmt_pf formatter, - xfs_daddr_t bno, int *ubused, int *stat); @@ -81,7 +79,6 @@ xfs_bulkstat_one( xfs_ino_t ino, void __user *buffer, int ubsize, - xfs_daddr_t bno, int *ubused, int *stat); diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c index ed0684cc50ee..9ac5cfab27b9 100644 --- a/fs/xfs/xfs_log_recover.c +++ b/fs/xfs/xfs_log_recover.c @@ -3198,7 +3198,7 @@ xlog_recover_process_one_iunlink( int error; ino = XFS_AGINO_TO_INO(mp, agno, agino); - error = xfs_iget(mp, NULL, ino, 0, 0, &ip, 0); + error = xfs_iget(mp, NULL, ino, 0, 0, &ip); if (error) goto fail; diff --git a/fs/xfs/xfs_mount.c b/fs/xfs/xfs_mount.c index d59f4e8bedcf..69f62d8b2816 100644 --- a/fs/xfs/xfs_mount.c +++ b/fs/xfs/xfs_mount.c @@ -1300,7 +1300,7 @@ xfs_mountfs( * Get and sanity-check the root inode. * Save the pointer to it in the mount structure. */ - error = xfs_iget(mp, NULL, sbp->sb_rootino, 0, XFS_ILOCK_EXCL, &rip, 0); + error = xfs_iget(mp, NULL, sbp->sb_rootino, 0, XFS_ILOCK_EXCL, &rip); if (error) { cmn_err(CE_WARN, "XFS: failed to read root inode"); goto out_log_dealloc; diff --git a/fs/xfs/xfs_rtalloc.c b/fs/xfs/xfs_rtalloc.c index 16445518506d..a2d32ce335aa 100644 --- a/fs/xfs/xfs_rtalloc.c +++ b/fs/xfs/xfs_rtalloc.c @@ -2277,12 +2277,12 @@ xfs_rtmount_inodes( sbp = &mp->m_sb; if (sbp->sb_rbmino == NULLFSINO) return 0; - error = xfs_iget(mp, NULL, sbp->sb_rbmino, 0, 0, &mp->m_rbmip, 0); + error = xfs_iget(mp, NULL, sbp->sb_rbmino, 0, 0, &mp->m_rbmip); if (error) return error; ASSERT(mp->m_rbmip != NULL); ASSERT(sbp->sb_rsumino != NULLFSINO); - error = xfs_iget(mp, NULL, sbp->sb_rsumino, 0, 0, &mp->m_rsumip, 0); + error = xfs_iget(mp, NULL, sbp->sb_rsumino, 0, 0, &mp->m_rsumip); if (error) { IRELE(mp->m_rbmip); return error; diff --git a/fs/xfs/xfs_trans_inode.c b/fs/xfs/xfs_trans_inode.c index 785ff101da0a..2559dfec946b 100644 --- a/fs/xfs/xfs_trans_inode.c +++ b/fs/xfs/xfs_trans_inode.c @@ -62,7 +62,7 @@ xfs_trans_iget( { int error; - error = xfs_iget(mp, tp, ino, flags, lock_flags, ipp, 0); + error = xfs_iget(mp, tp, ino, flags, lock_flags, ipp); if (!error && tp) xfs_trans_ijoin(tp, *ipp, lock_flags); return error; diff --git a/fs/xfs/xfs_vnodeops.c b/fs/xfs/xfs_vnodeops.c index a06bd62504fc..c1646838898f 100644 --- a/fs/xfs/xfs_vnodeops.c +++ b/fs/xfs/xfs_vnodeops.c @@ -1269,7 +1269,7 @@ xfs_lookup( if (error) goto out; - error = xfs_iget(dp->i_mount, NULL, inum, 0, 0, ipp, 0); + error = xfs_iget(dp->i_mount, NULL, inum, 0, 0, ipp); if (error) goto out_free_name; -- cgit v1.2.3 From bfaf148eb2e42c00f1c79b2163f0804068ea0c5e Mon Sep 17 00:00:00 2001 From: Yehuda Sadeh Date: Wed, 23 Jun 2010 15:52:27 -0700 Subject: ceph: fix caps debugfs entry The ceph client structure was not set correctly. Signed-off-by: Yehuda Sadeh Signed-off-by: Sage Weil --- fs/ceph/debugfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/ceph/debugfs.c b/fs/ceph/debugfs.c index 3be33fb066cc..f2f5332ddbba 100644 --- a/fs/ceph/debugfs.c +++ b/fs/ceph/debugfs.c @@ -261,7 +261,7 @@ static int osdc_show(struct seq_file *s, void *pp) static int caps_show(struct seq_file *s, void *p) { - struct ceph_client *client = p; + struct ceph_client *client = s->private; int total, avail, used, reserved, min; ceph_reservation_status(client, &total, &avail, &used, &reserved, &min); -- cgit v1.2.3 From 1f0e890dba5b0f543fea47732116b1c65d55614e Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Thu, 24 Jun 2010 15:11:43 -0400 Subject: NFSv4: Clean up struct nfs4_state_owner The 'so_delegations' list appears to be unused. Also eliminate so_client. If we already have so_server, we can get to the nfs_client structure. Signed-off-by: Trond Myklebust --- fs/nfs/nfs4_fs.h | 2 -- fs/nfs/nfs4proc.c | 2 +- fs/nfs/nfs4state.c | 14 ++++++-------- 3 files changed, 7 insertions(+), 11 deletions(-) (limited to 'fs') diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index a986f13e8039..cee871471e8d 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -108,7 +108,6 @@ struct nfs_unique_id { */ struct nfs4_state_owner { struct nfs_unique_id so_owner_id; - struct nfs_client *so_client; struct nfs_server *so_server; struct rb_node so_client_node; @@ -118,7 +117,6 @@ struct nfs4_state_owner { atomic_t so_count; unsigned long so_flags; struct list_head so_states; - struct list_head so_delegations; struct nfs_seqid_counter so_seqid; struct rpc_sequence so_sequence; }; diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 243d6c72cfa1..de9ff1505a24 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -1350,7 +1350,7 @@ static void nfs4_open_prepare(struct rpc_task *task, void *calldata) } /* Update sequence id. */ data->o_arg.id = sp->so_owner_id.id; - data->o_arg.clientid = sp->so_client->cl_clientid; + data->o_arg.clientid = sp->so_server->nfs_client->cl_clientid; if (data->o_arg.claim == NFS4_OPEN_CLAIM_PREVIOUS) { task->tk_msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_NOATTR]; nfs_copy_fh(&data->o_res.fh, data->o_arg.fh); diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index c3c6b8aeb209..13e17e32e3e4 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -373,7 +373,6 @@ nfs4_alloc_state_owner(void) return NULL; spin_lock_init(&sp->so_lock); INIT_LIST_HEAD(&sp->so_states); - INIT_LIST_HEAD(&sp->so_delegations); rpc_init_wait_queue(&sp->so_sequence.wait, "Seqid_waitqueue"); sp->so_seqid.sequence = &sp->so_sequence; spin_lock_init(&sp->so_sequence.lock); @@ -386,7 +385,7 @@ static void nfs4_drop_state_owner(struct nfs4_state_owner *sp) { if (!RB_EMPTY_NODE(&sp->so_client_node)) { - struct nfs_client *clp = sp->so_client; + struct nfs_client *clp = sp->so_server->nfs_client; spin_lock(&clp->cl_lock); rb_erase(&sp->so_client_node, &clp->cl_state_owners); @@ -408,7 +407,6 @@ struct nfs4_state_owner *nfs4_get_state_owner(struct nfs_server *server, struct new = nfs4_alloc_state_owner(); if (new == NULL) return NULL; - new->so_client = clp; new->so_server = server; new->so_cred = cred; spin_lock(&clp->cl_lock); @@ -425,7 +423,7 @@ struct nfs4_state_owner *nfs4_get_state_owner(struct nfs_server *server, struct void nfs4_put_state_owner(struct nfs4_state_owner *sp) { - struct nfs_client *clp = sp->so_client; + struct nfs_client *clp = sp->so_server->nfs_client; struct rpc_cred *cred = sp->so_cred; if (!atomic_dec_and_lock(&sp->so_count, &clp->cl_lock)) @@ -624,7 +622,7 @@ __nfs4_find_lock_state(struct nfs4_state *state, fl_owner_t fl_owner) static struct nfs4_lock_state *nfs4_alloc_lock_state(struct nfs4_state *state, fl_owner_t fl_owner) { struct nfs4_lock_state *lsp; - struct nfs_client *clp = state->owner->so_client; + struct nfs_client *clp = state->owner->so_server->nfs_client; lsp = kzalloc(sizeof(*lsp), GFP_NOFS); if (lsp == NULL) @@ -645,7 +643,7 @@ static struct nfs4_lock_state *nfs4_alloc_lock_state(struct nfs4_state *state, f static void nfs4_free_lock_state(struct nfs4_lock_state *lsp) { - struct nfs_client *clp = lsp->ls_state->owner->so_client; + struct nfs_client *clp = lsp->ls_state->owner->so_server->nfs_client; spin_lock(&clp->cl_lock); nfs_free_unique_id(&clp->cl_lockowner_id, &lsp->ls_id); @@ -1043,11 +1041,11 @@ restart: case -NFS4ERR_BAD_STATEID: case -NFS4ERR_RECLAIM_BAD: case -NFS4ERR_RECLAIM_CONFLICT: - nfs4_state_mark_reclaim_nograce(sp->so_client, state); + nfs4_state_mark_reclaim_nograce(sp->so_server->nfs_client, state); break; case -NFS4ERR_EXPIRED: case -NFS4ERR_NO_GRACE: - nfs4_state_mark_reclaim_nograce(sp->so_client, state); + nfs4_state_mark_reclaim_nograce(sp->so_server->nfs_client, state); case -NFS4ERR_STALE_CLIENTID: case -NFS4ERR_BADSESSION: case -NFS4ERR_BADSLOT: -- cgit v1.2.3 From 55bda7aacd13f5fdfeaafc16934953171405c692 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Thu, 24 Jun 2010 12:55:48 -0700 Subject: ceph: fix crush recursion There was a longstanding problem with recursion through intervening bucket types on complex hierarchies. Signed-off-by: Sage Weil --- fs/ceph/crush/mapper.c | 1 + 1 file changed, 1 insertion(+) (limited to 'fs') diff --git a/fs/ceph/crush/mapper.c b/fs/ceph/crush/mapper.c index 9ba54efb6543..804e6d53b77c 100644 --- a/fs/ceph/crush/mapper.c +++ b/fs/ceph/crush/mapper.c @@ -366,6 +366,7 @@ static int crush_choose(struct crush_map *map, BUG_ON(item >= 0 || (-1-item) >= map->max_buckets); in = map->buckets[-1-item]; + retry_bucket = 1; continue; } -- cgit v1.2.3 From a1a31e734241aefcb2b30fb0cc0376977b6d2ba8 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Thu, 24 Jun 2010 12:58:14 -0700 Subject: ceph: fix crush CHOOSE_LEAF when type is already a leaf We may not recurse for CHOOSE_LEAF if we start with a leaf node. When that happens, the out2 vector needs to be filled in with the result. Signed-off-by: Sage Weil --- fs/ceph/crush/mapper.c | 38 +++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-) (limited to 'fs') diff --git a/fs/ceph/crush/mapper.c b/fs/ceph/crush/mapper.c index 804e6d53b77c..374266bddd9c 100644 --- a/fs/ceph/crush/mapper.c +++ b/fs/ceph/crush/mapper.c @@ -238,7 +238,7 @@ static int bucket_straw_choose(struct crush_bucket_straw *bucket, static int crush_bucket_choose(struct crush_bucket *in, int x, int r) { - dprintk("choose %d x=%d r=%d\n", in->id, x, r); + dprintk(" crush_bucket_choose %d x=%d r=%d\n", in->id, x, r); switch (in->alg) { case CRUSH_BUCKET_UNIFORM: return bucket_uniform_choose((struct crush_bucket_uniform *)in, @@ -305,7 +305,9 @@ static int crush_choose(struct crush_map *map, int itemtype; int collide, reject; const int orig_tries = 5; /* attempts before we fall back to search */ - dprintk("choose bucket %d x %d outpos %d\n", bucket->id, x, outpos); + + dprintk("CHOOSE%s bucket %d x %d outpos %d numrep %d\n", recurse_to_leaf ? "_LEAF" : "", + bucket->id, x, outpos, numrep); for (rep = outpos; rep < numrep; rep++) { /* keep trying until we get a non-out, non-colliding item */ @@ -378,15 +380,25 @@ static int crush_choose(struct crush_map *map, } } - if (recurse_to_leaf && - item < 0 && - crush_choose(map, map->buckets[-1-item], - weight, - x, outpos+1, 0, - out2, outpos, - firstn, 0, NULL) <= outpos) { - reject = 1; - } else { + reject = 0; + if (recurse_to_leaf) { + if (item < 0) { + if (crush_choose(map, + map->buckets[-1-item], + weight, + x, outpos+1, 0, + out2, outpos, + firstn, 0, + NULL) <= outpos) + /* didn't get leaf */ + reject = 1; + } else { + /* we already have a leaf! */ + out2[outpos] = item; + } + } + + if (!reject) { /* out? */ if (itemtype == 0) reject = is_out(map, weight, @@ -425,12 +437,12 @@ reject: continue; } - dprintk("choose got %d\n", item); + dprintk("CHOOSE got %d\n", item); out[outpos] = item; outpos++; } - dprintk("choose returns %d\n", outpos); + dprintk("CHOOSE returns %d\n", outpos); return outpos; } -- cgit v1.2.3 From 523825bc586d19e0fbcfc5db717f5bb90108bbc3 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Wed, 2 Jun 2010 16:26:51 +0200 Subject: ext2: update ctime when changing the file's permission by setfacl ext2 didn't update the ctime of the file when its permission was changed. Steps to reproduce: # touch aaa # stat -c %Z aaa 1275289822 # setfacl -m 'u::x,g::x,o::x' aaa # stat -c %Z aaa 1275289822 <- unchanged But, according to the spec of the ctime, ext2 must update it. Port of ext3 patch by Miao Xie . Signed-off-by: Jan Kara --- fs/ext2/acl.c | 1 + 1 file changed, 1 insertion(+) (limited to 'fs') diff --git a/fs/ext2/acl.c b/fs/ext2/acl.c index ca7e2a0ed98a..2bcc0431bada 100644 --- a/fs/ext2/acl.c +++ b/fs/ext2/acl.c @@ -200,6 +200,7 @@ ext2_set_acl(struct inode *inode, int type, struct posix_acl *acl) return error; else { inode->i_mode = mode; + inode->i_ctime = CURRENT_TIME_SEC; mark_inode_dirty(inode); if (error == 0) acl = NULL; -- cgit v1.2.3 From 30e2bab2d6e22188c6d36a09cdcffb4748d2dbe5 Mon Sep 17 00:00:00 2001 From: Miao Xie Date: Thu, 27 May 2010 16:28:40 +0800 Subject: ext3: update ctime when changing the file's permission by setfacl ext3 didn't update the ctime of the file when its permission was changed. Steps to reproduce: # touch aaa # stat -c %Z aaa 1275289822 # setfacl -m 'u::x,g::x,o::x' aaa # stat -c %Z aaa 1275289822 <- unchanged But, according to the spec of the ctime, ext3 must update it. Signed-off-by: Miao Xie Signed-off-by: Jan Kara --- fs/ext3/acl.c | 1 + 1 file changed, 1 insertion(+) (limited to 'fs') diff --git a/fs/ext3/acl.c b/fs/ext3/acl.c index 01552abbca3c..8a11fe212183 100644 --- a/fs/ext3/acl.c +++ b/fs/ext3/acl.c @@ -205,6 +205,7 @@ ext3_set_acl(handle_t *handle, struct inode *inode, int type, return error; else { inode->i_mode = mode; + inode->i_ctime = CURRENT_TIME_SEC; ext3_mark_inode_dirty(handle, inode); if (error == 0) acl = NULL; -- cgit v1.2.3 From 327f935a9ef644c0ec3d050c94bce753756d60c0 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 30 Mar 2010 02:52:32 +0900 Subject: ocfs2: update gfp/slab.h includes Implicit slab.h inclusion via percpu.h is about to go away. Make sure gfp.h or slab.h is included as necessary. Signed-off-by: Tejun Heo Cc: Stephen Rothwell Cc: Joel Becker Signed-off-by: Stephen Rothwell --- fs/ocfs2/reservations.c | 1 - 1 file changed, 1 deletion(-) (limited to 'fs') diff --git a/fs/ocfs2/reservations.c b/fs/ocfs2/reservations.c index 40650021fc24..d8b6e4259b80 100644 --- a/fs/ocfs2/reservations.c +++ b/fs/ocfs2/reservations.c @@ -26,7 +26,6 @@ #include #include -#include #include #include #include -- cgit v1.2.3 From e956b4b7e342c24ca754e0276f4f5fb1e890dfd9 Mon Sep 17 00:00:00 2001 From: Matthew Whitworth Date: Sun, 27 Jun 2010 14:34:30 +0100 Subject: Fix comment spelling errors in ncpfs/inode.c Signed-off-by: Jiri Kosina --- fs/ncpfs/inode.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/ncpfs/inode.c b/fs/ncpfs/inode.c index fa3385154023..1e634deff941 100644 --- a/fs/ncpfs/inode.c +++ b/fs/ncpfs/inode.c @@ -728,8 +728,8 @@ out_fput: out_bdi: /* 23/12/1998 Marcin Dalecki : * - * The previously used put_filp(ncp_filp); was bogous, since - * it doesn't proper unlocking. + * The previously used put_filp(ncp_filp); was bogus, since + * it doesn't perform proper unlocking. */ fput(ncp_filp); out: -- cgit v1.2.3 From 4a9cdec73f79b2858e9ecf0b6cfac7f6b200bf3a Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Tue, 29 Jun 2010 11:00:23 -0400 Subject: ext4: Add new superblock fields reserved for the Next3 snapshot feature Signed-off-by: "Theodore Ts'o" --- fs/ext4/ext4.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 8b6d297c8c73..3864a2775458 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -1002,7 +1002,13 @@ struct ext4_super_block { __u8 s_reserved_char_pad2; __le16 s_reserved_pad; __le64 s_kbytes_written; /* nr of lifetime kilobytes written */ - __u32 s_reserved[160]; /* Padding to the end of the block */ + __le32 s_snapshot_inum; /* Inode number of active snapshot */ + __le32 s_snapshot_id; /* sequential ID of active snapshot */ + __le64 s_snapshot_r_blocks_count; /* reserved blocks for active + snapshot's future use */ + __le32 s_snapshot_list; /* inode number of the head of the + on-disk snapshot list */ + __u32 s_reserved[155]; /* Padding to the end of the block */ }; #ifdef __KERNEL__ -- cgit v1.2.3 From c67d859e39896e4286249da89c4ca0ef8bd949cb Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Tue, 29 Jun 2010 11:07:07 -0400 Subject: ext4: clean up ext4_abort() so __func__ is now implicit Use a macro definition for ext4_abort() to clean up the .c files a wee bit. Signed-off-by: "Theodore Ts'o" --- fs/ext4/ext4.h | 4 +++- fs/ext4/ext4_jbd2.c | 4 ++-- fs/ext4/super.c | 11 +++++------ 3 files changed, 10 insertions(+), 9 deletions(-) (limited to 'fs') diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 3864a2775458..d8ec824f5a25 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -1629,8 +1629,10 @@ extern void ext4_error_inode(const char *, struct inode *, const char *, ...) extern void ext4_error_file(const char *, struct file *, const char *, ...) __attribute__ ((format (printf, 3, 4))); extern void __ext4_std_error(struct super_block *, const char *, int); -extern void ext4_abort(struct super_block *, const char *, const char *, ...) +extern void __ext4_abort(struct super_block *, const char *, const char *, ...) __attribute__ ((format (printf, 3, 4))); +#define ext4_abort(sb, message...) __ext4_abort(sb, __func__, \ + ## message) extern void __ext4_warning(struct super_block *, const char *, const char *, ...) __attribute__ ((format (printf, 3, 4))); diff --git a/fs/ext4/ext4_jbd2.c b/fs/ext4/ext4_jbd2.c index cfd27b38fa15..df26a015d043 100644 --- a/fs/ext4/ext4_jbd2.c +++ b/fs/ext4/ext4_jbd2.c @@ -93,8 +93,8 @@ int __ext4_forget(const char *where, handle_t *handle, int is_metadata, err = jbd2_journal_revoke(handle, blocknr, bh); if (err) { ext4_journal_abort_handle(where, __func__, bh, handle, err); - ext4_abort(inode->i_sb, __func__, - "error %d when attempting revoke", err); + __ext4_abort(inode->i_sb, where, + "error %d when attempting revoke", err); } BUFFER_TRACE(bh, "exit"); return err; diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 422a4ce66778..11441e483b62 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -248,7 +248,7 @@ handle_t *ext4_journal_start_sb(struct super_block *sb, int nblocks) journal = EXT4_SB(sb)->s_journal; if (journal) { if (is_journal_aborted(journal)) { - ext4_abort(sb, __func__, "Detected aborted journal"); + ext4_abort(sb, "Detected aborted journal"); return ERR_PTR(-EROFS); } return jbd2_journal_start(journal, nblocks); @@ -464,8 +464,8 @@ void __ext4_std_error(struct super_block *sb, const char *function, int errno) * case we take the easy way out and panic immediately. */ -void ext4_abort(struct super_block *sb, const char *function, - const char *fmt, ...) +void __ext4_abort(struct super_block *sb, const char *function, + const char *fmt, ...) { va_list args; @@ -660,8 +660,7 @@ static void ext4_put_super(struct super_block *sb) err = jbd2_journal_destroy(sbi->s_journal); sbi->s_journal = NULL; if (err < 0) - ext4_abort(sb, __func__, - "Couldn't clean up the journal"); + ext4_abort(sb, "Couldn't clean up the journal"); } ext4_release_system_zone(sb); @@ -3605,7 +3604,7 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data) } if (sbi->s_mount_flags & EXT4_MF_FS_ABORTED) - ext4_abort(sb, __func__, "Abort forced by user"); + ext4_abort(sb, "Abort forced by user"); sb->s_flags = (sb->s_flags & ~MS_POSIXACL) | (test_opt(sb, POSIX_ACL) ? MS_POSIXACL : 0); -- cgit v1.2.3 From ec97f88ba6d4256927fde516033ee76d5d85b54a Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Thu, 24 Jun 2010 15:12:37 -0700 Subject: ceph: only release clean, unused caps with mds requests We can drop caps with an mds request. Ensure we only drop unused AND clean caps, since the MDS doesn't support cap writeback in that context, nor do we track it. If caps are dirty, and the MDS needs them back, we it will revoke and we will flush in the normal fashion. This fixes a possibly loss of metadata. Signed-off-by: Sage Weil --- fs/ceph/caps.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'fs') diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index 619b61655ee5..d4fcdda7676c 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -2886,18 +2886,19 @@ int ceph_encode_inode_release(void **p, struct inode *inode, struct ceph_inode_info *ci = ceph_inode(inode); struct ceph_cap *cap; struct ceph_mds_request_release *rel = *p; + int used, dirty; int ret = 0; - int used = 0; spin_lock(&inode->i_lock); used = __ceph_caps_used(ci); + dirty = __ceph_caps_dirty(ci); - dout("encode_inode_release %p mds%d used %s drop %s unless %s\n", inode, - mds, ceph_cap_string(used), ceph_cap_string(drop), + dout("encode_inode_release %p mds%d used|dirty %s drop %s unless %s\n", + inode, mds, ceph_cap_string(used|dirty), ceph_cap_string(drop), ceph_cap_string(unless)); - /* only drop unused caps */ - drop &= ~used; + /* only drop unused, clean caps */ + drop &= ~(used | dirty); cap = __get_cap_for_mds(ci, mds); if (cap && __cap_is_valid(cap)) { -- cgit v1.2.3 From 443b3760a06860187f135c1ecd56c2c7d4ad1022 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Tue, 29 Jun 2010 09:28:39 -0700 Subject: ceph: fix caps usage accounting for import (non-reserved) case We need to increase the total and used counters when allocating a new cap in the non-reserved (cap import) case. Signed-off-by: Sage Weil --- fs/ceph/caps.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index d4fcdda7676c..74144d6389f0 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -244,8 +244,14 @@ static struct ceph_cap *get_cap(struct ceph_cap_reservation *ctx) struct ceph_cap *cap = NULL; /* temporary, until we do something about cap import/export */ - if (!ctx) - return kmem_cache_alloc(ceph_cap_cachep, GFP_NOFS); + if (!ctx) { + cap = kmem_cache_alloc(ceph_cap_cachep, GFP_NOFS); + if (cap) { + caps_use_count++; + caps_total_count++; + } + return cap; + } spin_lock(&caps_list_lock); dout("get_cap ctx=%p (%d) %d = %d used + %d resv + %d avail\n", -- cgit v1.2.3 From e29136f80e775b0310273932b4297a62f5574a29 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Tue, 29 Jun 2010 12:54:28 -0400 Subject: ext4: Enhance ext4_grp_locked_error() to take block and function numbers Also use a macro definition so that __func__ and __LINE__ is implicit. Signed-off-by: "Theodore Ts'o" --- fs/ext4/ext4.h | 10 +++++++--- fs/ext4/inode.c | 13 +++++++------ fs/ext4/mballoc.c | 41 +++++++++++++++++++++-------------------- fs/ext4/super.c | 13 ++++++++++--- 4 files changed, 45 insertions(+), 32 deletions(-) (limited to 'fs') diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index d8ec824f5a25..5a41881cafca 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -1639,9 +1639,13 @@ extern void __ext4_warning(struct super_block *, const char *, #define ext4_warning(sb, message...) __ext4_warning(sb, __func__, ## message) extern void ext4_msg(struct super_block *, const char *, const char *, ...) __attribute__ ((format (printf, 3, 4))); -extern void ext4_grp_locked_error(struct super_block *, ext4_group_t, - const char *, const char *, ...) - __attribute__ ((format (printf, 4, 5))); +extern void __ext4_grp_locked_error(const char *, unsigned int, \ + struct super_block *, ext4_group_t, \ + unsigned long, ext4_fsblk_t, \ + const char *, ...) + __attribute__ ((format (printf, 7, 8))); +#define ext4_grp_locked_error(sb, grp, message...) \ + __ext4_grp_locked_error(__func__, __LINE__, (sb), (grp), ## message) extern void ext4_update_dynamic_rev(struct super_block *sb); extern int ext4_update_compat_feature(handle_t *handle, struct super_block *sb, __u32 compat); diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index b485987f0146..64baadb4956d 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -1128,8 +1128,8 @@ void ext4_da_update_reserve_space(struct inode *inode, ext4_discard_preallocations(inode); } -static int check_block_validity(struct inode *inode, const char *func, - struct ext4_map_blocks *map) +static int __check_block_validity(struct inode *inode, const char *func, + struct ext4_map_blocks *map) { if (!ext4_data_block_valid(EXT4_SB(inode->i_sb), map->m_pblk, map->m_len)) { @@ -1142,6 +1142,9 @@ static int check_block_validity(struct inode *inode, const char *func, return 0; } +#define check_block_validity(inode, map) \ + __check_block_validity((inode), __func__, (map)) + /* * Return the number of contiguous dirty pages in a given inode * starting at page frame idx. @@ -1244,7 +1247,7 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode, up_read((&EXT4_I(inode)->i_data_sem)); if (retval > 0 && map->m_flags & EXT4_MAP_MAPPED) { - int ret = check_block_validity(inode, __func__, map); + int ret = check_block_validity(inode, map); if (ret != 0) return ret; } @@ -1324,9 +1327,7 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode, up_write((&EXT4_I(inode)->i_data_sem)); if (retval > 0 && map->m_flags & EXT4_MAP_MAPPED) { - int ret = check_block_validity(inode, - "ext4_map_blocks_after_alloc", - map); + int ret = check_block_validity(inode, map); if (ret != 0) return ret; } diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c index b2948b047973..3dfad95f0f98 100644 --- a/fs/ext4/mballoc.c +++ b/fs/ext4/mballoc.c @@ -446,10 +446,11 @@ static void mb_free_blocks_double(struct inode *inode, struct ext4_buddy *e4b, blocknr = ext4_group_first_block_no(sb, e4b->bd_group); blocknr += first + i; ext4_grp_locked_error(sb, e4b->bd_group, - __func__, "double-free of inode" - " %lu's block %llu(bit %u in group %u)", - inode ? inode->i_ino : 0, blocknr, - first + i, e4b->bd_group); + inode ? inode->i_ino : 0, + blocknr, + "freeing block already freed " + "(bit %u)", + first + i); } mb_clear_bit(first + i, e4b->bd_info->bb_bitmap); } @@ -712,9 +713,9 @@ void ext4_mb_generate_buddy(struct super_block *sb, grp->bb_fragments = fragments; if (free != grp->bb_free) { - ext4_grp_locked_error(sb, group, __func__, - "EXT4-fs: group %u: %u blocks in bitmap, %u in gd", - group, free, grp->bb_free); + ext4_grp_locked_error(sb, group, 0, 0, + "%u blocks in bitmap, %u in gd", + free, grp->bb_free); /* * If we intent to continue, we consider group descritor * corrupt and update bb_free using bitmap value @@ -1296,10 +1297,10 @@ static void mb_free_blocks(struct inode *inode, struct ext4_buddy *e4b, blocknr = ext4_group_first_block_no(sb, e4b->bd_group); blocknr += block; ext4_grp_locked_error(sb, e4b->bd_group, - __func__, "double-free of inode" - " %lu's block %llu(bit %u in group %u)", - inode ? inode->i_ino : 0, blocknr, block, - e4b->bd_group); + inode ? inode->i_ino : 0, + blocknr, + "freeing already freed block " + "(bit %u)", block); } mb_clear_bit(block, EXT4_MB_BITMAP(e4b)); e4b->bd_info->bb_counters[order]++; @@ -1788,8 +1789,8 @@ void ext4_mb_complex_scan_group(struct ext4_allocation_context *ac, * free blocks even though group info says we * we have free blocks */ - ext4_grp_locked_error(sb, e4b->bd_group, - __func__, "%d free blocks as per " + ext4_grp_locked_error(sb, e4b->bd_group, 0, 0, + "%d free blocks as per " "group info. But bitmap says 0", free); break; @@ -1798,8 +1799,8 @@ void ext4_mb_complex_scan_group(struct ext4_allocation_context *ac, mb_find_extent(e4b, 0, i, ac->ac_g_ex.fe_len, &ex); BUG_ON(ex.fe_len <= 0); if (free < ex.fe_len) { - ext4_grp_locked_error(sb, e4b->bd_group, - __func__, "%d free blocks as per " + ext4_grp_locked_error(sb, e4b->bd_group, 0, 0, + "%d free blocks as per " "group info. But got %d blocks", free, ex.fe_len); /* @@ -3584,8 +3585,7 @@ ext4_mb_release_inode_pa(struct ext4_buddy *e4b, struct buffer_head *bitmap_bh, pa, (unsigned long) pa->pa_lstart, (unsigned long) pa->pa_pstart, (unsigned long) pa->pa_len); - ext4_grp_locked_error(sb, group, - __func__, "free %u, pa_free %u", + ext4_grp_locked_error(sb, group, 0, 0, "free %u, pa_free %u", free, pa->pa_free); /* * pa is already deleted so we use the value obtained @@ -4395,6 +4395,7 @@ static noinline_for_stack int ext4_mb_free_metadata(handle_t *handle, struct ext4_buddy *e4b, struct ext4_free_data *new_entry) { + ext4_group_t group = e4b->bd_group; ext4_grpblk_t block; struct ext4_free_data *entry; struct ext4_group_info *db = e4b->bd_info; @@ -4427,9 +4428,9 @@ ext4_mb_free_metadata(handle_t *handle, struct ext4_buddy *e4b, else if (block >= (entry->start_blk + entry->count)) n = &(*n)->rb_right; else { - ext4_grp_locked_error(sb, e4b->bd_group, __func__, - "Double free of blocks %d (%d %d)", - block, entry->start_blk, entry->count); + ext4_grp_locked_error(sb, group, 0, + ext4_group_first_block_no(sb, group) + block, + "Block already on to-be-freed list"); return 0; } } diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 11441e483b62..39aeb454bf2c 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -514,8 +514,10 @@ void __ext4_warning(struct super_block *sb, const char *function, va_end(args); } -void ext4_grp_locked_error(struct super_block *sb, ext4_group_t grp, - const char *function, const char *fmt, ...) +void __ext4_grp_locked_error(const char *function, unsigned int line, + struct super_block *sb, ext4_group_t grp, + unsigned long ino, ext4_fsblk_t block, + const char *fmt, ...) __releases(bitlock) __acquires(bitlock) { @@ -523,7 +525,12 @@ __acquires(bitlock) struct ext4_super_block *es = EXT4_SB(sb)->s_es; va_start(args, fmt); - printk(KERN_CRIT "EXT4-fs error (device %s): %s: ", sb->s_id, function); + printk(KERN_CRIT "EXT4-fs error (device %s): %s:%d: group %u", + sb->s_id, function, line, grp); + if (ino) + printk("inode %lu: ", ino); + if (block) + printk("block %llu:", (unsigned long long) block); vprintk(fmt, args); printk("\n"); va_end(args); -- cgit v1.2.3 From 57439f878afafefad8836ebf5c49da2a0a746105 Mon Sep 17 00:00:00 2001 From: "npiggin@suse.de" Date: Thu, 24 Jun 2010 13:02:14 +1000 Subject: fs: fix superblock iteration race list_for_each_entry_safe is not suitable to protect against concurrent modification of the list. 6754af6 introduced a race in sb walking. list_for_each_entry can use the trick of pinning the current entry in the list before we drop and retake the lock because it subsequently follows cur->next. However list_for_each_entry_safe saves n=cur->next for following before entering the loop body, so when the lock is dropped, n may be deleted. Signed-off-by: Nick Piggin Cc: Christoph Hellwig Cc: John Stultz Cc: Frank Mayhar Cc: Al Viro Signed-off-by: Linus Torvalds --- fs/dcache.c | 2 ++ fs/super.c | 6 ++++++ include/linux/list.h | 15 +++++++++++++++ 3 files changed, 23 insertions(+) (limited to 'fs') diff --git a/fs/dcache.c b/fs/dcache.c index d96047b4a633..c8c78ba07827 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -590,6 +590,8 @@ static void prune_dcache(int count) up_read(&sb->s_umount); } spin_lock(&sb_lock); + /* lock was dropped, must reset next */ + list_safe_reset_next(sb, n, s_list); count -= pruned; __put_super(sb); /* more work left to do? */ diff --git a/fs/super.c b/fs/super.c index 5c35bc7a499e..938119ab8dcb 100644 --- a/fs/super.c +++ b/fs/super.c @@ -374,6 +374,8 @@ void sync_supers(void) up_read(&sb->s_umount); spin_lock(&sb_lock); + /* lock was dropped, must reset next */ + list_safe_reset_next(sb, n, s_list); __put_super(sb); } } @@ -405,6 +407,8 @@ void iterate_supers(void (*f)(struct super_block *, void *), void *arg) up_read(&sb->s_umount); spin_lock(&sb_lock); + /* lock was dropped, must reset next */ + list_safe_reset_next(sb, n, s_list); __put_super(sb); } spin_unlock(&sb_lock); @@ -585,6 +589,8 @@ static void do_emergency_remount(struct work_struct *work) } up_write(&sb->s_umount); spin_lock(&sb_lock); + /* lock was dropped, must reset next */ + list_safe_reset_next(sb, n, s_list); __put_super(sb); } spin_unlock(&sb_lock); diff --git a/include/linux/list.h b/include/linux/list.h index 8392884a2977..5d57a3a1fa1b 100644 --- a/include/linux/list.h +++ b/include/linux/list.h @@ -544,6 +544,21 @@ static inline void list_splice_tail_init(struct list_head *list, &pos->member != (head); \ pos = n, n = list_entry(n->member.prev, typeof(*n), member)) +/** + * list_safe_reset_next - reset a stale list_for_each_entry_safe loop + * @pos: the loop cursor used in the list_for_each_entry_safe loop + * @n: temporary storage used in list_for_each_entry_safe + * @member: the name of the list_struct within the struct. + * + * list_safe_reset_next is not safe to use in general if the list may be + * modified concurrently (eg. the lock is dropped in the loop body). An + * exception to this is if the cursor element (pos) is pinned in the list, + * and list_safe_reset_next is called after re-taking the lock and before + * completing the current iteration of the loop body. + */ +#define list_safe_reset_next(pos, n, member) \ + n = list_entry(pos->member.next, typeof(*pos), member) + /* * Double linked lists with a single pointer list head. * Mostly useful for hash tables where the two pointer list head is -- cgit v1.2.3 From 90c7201b97bb7ac5a4e2605abc0efb5fdfb957f0 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Tue, 29 Jun 2010 14:53:24 -0400 Subject: ext4: Pass line number to ext4_journal_abort_handle() This allows the error messages to include the line number Signed-off-by: "Theodore Ts'o" --- fs/ext4/ext4_jbd2.c | 48 +++++++++++++++++++++++++----------------------- fs/ext4/ext4_jbd2.h | 43 +++++++++++++++++++++++-------------------- fs/ext4/super.c | 9 +++++---- 3 files changed, 53 insertions(+), 47 deletions(-) (limited to 'fs') diff --git a/fs/ext4/ext4_jbd2.c b/fs/ext4/ext4_jbd2.c index df26a015d043..9de37b9e177a 100644 --- a/fs/ext4/ext4_jbd2.c +++ b/fs/ext4/ext4_jbd2.c @@ -6,29 +6,29 @@ #include -int __ext4_journal_get_undo_access(const char *where, handle_t *handle, - struct buffer_head *bh) +int __ext4_journal_get_undo_access(const char *where, unsigned int line, + handle_t *handle, struct buffer_head *bh) { int err = 0; if (ext4_handle_valid(handle)) { err = jbd2_journal_get_undo_access(handle, bh); if (err) - ext4_journal_abort_handle(where, __func__, bh, + ext4_journal_abort_handle(where, line, __func__, bh, handle, err); } return err; } -int __ext4_journal_get_write_access(const char *where, handle_t *handle, - struct buffer_head *bh) +int __ext4_journal_get_write_access(const char *where, unsigned int line, + handle_t *handle, struct buffer_head *bh) { int err = 0; if (ext4_handle_valid(handle)) { err = jbd2_journal_get_write_access(handle, bh); if (err) - ext4_journal_abort_handle(where, __func__, bh, + ext4_journal_abort_handle(where, line, __func__, bh, handle, err); } return err; @@ -46,9 +46,9 @@ int __ext4_journal_get_write_access(const char *where, handle_t *handle, * If the handle isn't valid we're not journaling, but we still need to * call into ext4_journal_revoke() to put the buffer head. */ -int __ext4_forget(const char *where, handle_t *handle, int is_metadata, - struct inode *inode, struct buffer_head *bh, - ext4_fsblk_t blocknr) +int __ext4_forget(const char *where, unsigned int line, handle_t *handle, + int is_metadata, struct inode *inode, + struct buffer_head *bh, ext4_fsblk_t blocknr) { int err; @@ -79,8 +79,8 @@ int __ext4_forget(const char *where, handle_t *handle, int is_metadata, BUFFER_TRACE(bh, "call jbd2_journal_forget"); err = jbd2_journal_forget(handle, bh); if (err) - ext4_journal_abort_handle(where, __func__, bh, - handle, err); + ext4_journal_abort_handle(where, line, __func__, + bh, handle, err); return err; } return 0; @@ -92,7 +92,8 @@ int __ext4_forget(const char *where, handle_t *handle, int is_metadata, BUFFER_TRACE(bh, "call jbd2_journal_revoke"); err = jbd2_journal_revoke(handle, blocknr, bh); if (err) { - ext4_journal_abort_handle(where, __func__, bh, handle, err); + ext4_journal_abort_handle(where, line, __func__, + bh, handle, err); __ext4_abort(inode->i_sb, where, "error %d when attempting revoke", err); } @@ -100,7 +101,7 @@ int __ext4_forget(const char *where, handle_t *handle, int is_metadata, return err; } -int __ext4_journal_get_create_access(const char *where, +int __ext4_journal_get_create_access(const char *where, unsigned int line, handle_t *handle, struct buffer_head *bh) { int err = 0; @@ -108,22 +109,23 @@ int __ext4_journal_get_create_access(const char *where, if (ext4_handle_valid(handle)) { err = jbd2_journal_get_create_access(handle, bh); if (err) - ext4_journal_abort_handle(where, __func__, bh, - handle, err); + ext4_journal_abort_handle(where, line, __func__, + bh, handle, err); } return err; } -int __ext4_handle_dirty_metadata(const char *where, handle_t *handle, - struct inode *inode, struct buffer_head *bh) +int __ext4_handle_dirty_metadata(const char *where, unsigned int line, + handle_t *handle, struct inode *inode, + struct buffer_head *bh) { int err = 0; if (ext4_handle_valid(handle)) { err = jbd2_journal_dirty_metadata(handle, bh); if (err) - ext4_journal_abort_handle(where, __func__, bh, - handle, err); + ext4_journal_abort_handle(where, line, __func__, + bh, handle, err); } else { if (inode) mark_buffer_dirty_inode(bh, inode); @@ -144,8 +146,8 @@ int __ext4_handle_dirty_metadata(const char *where, handle_t *handle, return err; } -int __ext4_handle_dirty_super(const char *where, handle_t *handle, - struct super_block *sb) +int __ext4_handle_dirty_super(const char *where, unsigned int line, + handle_t *handle, struct super_block *sb) { struct buffer_head *bh = EXT4_SB(sb)->s_sbh; int err = 0; @@ -153,8 +155,8 @@ int __ext4_handle_dirty_super(const char *where, handle_t *handle, if (ext4_handle_valid(handle)) { err = jbd2_journal_dirty_metadata(handle, bh); if (err) - ext4_journal_abort_handle(where, __func__, bh, - handle, err); + ext4_journal_abort_handle(where, line, __func__, + bh, handle, err); } else sb->s_dirt = 1; return err; diff --git a/fs/ext4/ext4_jbd2.h b/fs/ext4/ext4_jbd2.h index 38d1e66e5843..6883c6be5b8d 100644 --- a/fs/ext4/ext4_jbd2.h +++ b/fs/ext4/ext4_jbd2.h @@ -122,41 +122,44 @@ int ext4_mark_inode_dirty(handle_t *handle, struct inode *inode); /* * Wrapper functions with which ext4 calls into JBD. */ -void ext4_journal_abort_handle(const char *caller, const char *err_fn, +void ext4_journal_abort_handle(const char *caller, unsigned int line, + const char *err_fn, struct buffer_head *bh, handle_t *handle, int err); -int __ext4_journal_get_undo_access(const char *where, handle_t *handle, - struct buffer_head *bh); +int __ext4_journal_get_undo_access(const char *where, unsigned int line, + handle_t *handle, struct buffer_head *bh); -int __ext4_journal_get_write_access(const char *where, handle_t *handle, - struct buffer_head *bh); +int __ext4_journal_get_write_access(const char *where, unsigned int line, + handle_t *handle, struct buffer_head *bh); -int __ext4_forget(const char *where, handle_t *handle, int is_metadata, - struct inode *inode, struct buffer_head *bh, - ext4_fsblk_t blocknr); +int __ext4_forget(const char *where, unsigned int line, handle_t *handle, + int is_metadata, struct inode *inode, + struct buffer_head *bh, ext4_fsblk_t blocknr); -int __ext4_journal_get_create_access(const char *where, +int __ext4_journal_get_create_access(const char *where, unsigned int line, handle_t *handle, struct buffer_head *bh); -int __ext4_handle_dirty_metadata(const char *where, handle_t *handle, - struct inode *inode, struct buffer_head *bh); +int __ext4_handle_dirty_metadata(const char *where, unsigned int line, + handle_t *handle, struct inode *inode, + struct buffer_head *bh); -int __ext4_handle_dirty_super(const char *where, handle_t *handle, - struct super_block *sb); +int __ext4_handle_dirty_super(const char *where, unsigned int line, + handle_t *handle, struct super_block *sb); #define ext4_journal_get_undo_access(handle, bh) \ - __ext4_journal_get_undo_access(__func__, (handle), (bh)) + __ext4_journal_get_undo_access(__func__, __LINE__, (handle), (bh)) #define ext4_journal_get_write_access(handle, bh) \ - __ext4_journal_get_write_access(__func__, (handle), (bh)) + __ext4_journal_get_write_access(__func__, __LINE__, (handle), (bh)) #define ext4_forget(handle, is_metadata, inode, bh, block_nr) \ - __ext4_forget(__func__, (handle), (is_metadata), (inode), (bh),\ - (block_nr)) + __ext4_forget(__func__, __LINE__, (handle), (is_metadata), (inode), \ + (bh), (block_nr)) #define ext4_journal_get_create_access(handle, bh) \ - __ext4_journal_get_create_access(__func__, (handle), (bh)) + __ext4_journal_get_create_access(__func__, __LINE__, (handle), (bh)) #define ext4_handle_dirty_metadata(handle, inode, bh) \ - __ext4_handle_dirty_metadata(__func__, (handle), (inode), (bh)) + __ext4_handle_dirty_metadata(__func__, __LINE__, (handle), (inode), \ + (bh)) #define ext4_handle_dirty_super(handle, sb) \ - __ext4_handle_dirty_super(__func__, (handle), (sb)) + __ext4_handle_dirty_super(__func__, __LINE__, (handle), (sb)) handle_t *ext4_journal_start_sb(struct super_block *sb, int nblocks); int __ext4_journal_stop(const char *where, handle_t *handle); diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 39aeb454bf2c..87db5ecfccb4 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -283,8 +283,9 @@ int __ext4_journal_stop(const char *where, handle_t *handle) return err; } -void ext4_journal_abort_handle(const char *caller, const char *err_fn, - struct buffer_head *bh, handle_t *handle, int err) +void ext4_journal_abort_handle(const char *caller, unsigned int line, + const char *err_fn, struct buffer_head *bh, + handle_t *handle, int err) { char nbuf[16]; const char *errstr = ext4_decode_error(NULL, err, nbuf); @@ -300,8 +301,8 @@ void ext4_journal_abort_handle(const char *caller, const char *err_fn, if (is_handle_aborted(handle)) return; - printk(KERN_ERR "%s: aborting transaction: %s in %s\n", - caller, errstr, err_fn); + printk(KERN_ERR "%s:%d: aborting transaction: %s in %s\n", + caller, line, errstr, err_fn); jbd2_journal_abort_handle(handle); } -- cgit v1.2.3 From 3c26c9d9597f982973b9b3a32364230096ab0d78 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Tue, 29 Jun 2010 15:05:17 -0700 Subject: nommu: add '[stack]' label to /proc/pid/maps output Add support to the NOMMU /proc/pid/maps file to show which mapping is the stack of the original thread after execve. This is largely based on the MMU code. Subsidiary thread stacks are not indicated. For FDPIC, we now get: root:/> cat /proc/self/maps 02064000-02067ccc rw-p 0004d000 00:01 22 /bin/busybox 0206e000-0206f35c rw-p 00006000 00:01 295 /lib/ld-uClibc.so.0 025f0000-025f6f0c r-xs 00000000 00:01 295 /lib/ld-uClibc.so.0 02680000-026ba6b0 r-xs 00000000 00:01 297 /lib/libc.so.0 02700000-0274d384 r-xs 00000000 00:01 22 /bin/busybox 02816000-02817000 rw-p 00000000 00:00 0 02848000-0284c0d8 rw-p 00000000 00:00 0 02860000-02880000 rw-p 00000000 00:00 0 [stack] The semi-downside here is that for FLAT, we get: root:/> cat /proc/155/maps 029f0000-029f9000 rwxp 00000000 00:00 0 [stack] The reason being that FLAT combines a whole lot of stuff into one map (including the stack). But this isn't any worse than the current output (which is nothing), so screw it. Signed-off-by: Mike Frysinger Signed-off-by: David Howells Cc: Greg Ungerer Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/proc/task_nommu.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/proc/task_nommu.c b/fs/proc/task_nommu.c index 46d4b5d72bd3..cb6306e63843 100644 --- a/fs/proc/task_nommu.c +++ b/fs/proc/task_nommu.c @@ -122,11 +122,20 @@ int task_statm(struct mm_struct *mm, int *shared, int *text, return size; } +static void pad_len_spaces(struct seq_file *m, int len) +{ + len = 25 + sizeof(void*) * 6 - len; + if (len < 1) + len = 1; + seq_printf(m, "%*c", len, ' '); +} + /* * display a single VMA to a sequenced file */ static int nommu_vma_show(struct seq_file *m, struct vm_area_struct *vma) { + struct mm_struct *mm = vma->vm_mm; unsigned long ino = 0; struct file *file; dev_t dev = 0; @@ -155,11 +164,14 @@ static int nommu_vma_show(struct seq_file *m, struct vm_area_struct *vma) MAJOR(dev), MINOR(dev), ino, &len); if (file) { - len = 25 + sizeof(void *) * 6 - len; - if (len < 1) - len = 1; - seq_printf(m, "%*c", len, ' '); + pad_len_spaces(m, len); seq_path(m, &file->f_path, ""); + } else if (mm) { + if (vma->vm_start <= mm->start_stack && + vma->vm_end >= mm->start_stack) { + pad_len_spaces(m, len); + seq_puts(m, "[stack]"); + } } seq_putc(m, '\n'); -- cgit v1.2.3 From 2952095c6b2eefd068dda0dee6317cf95155a304 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Tue, 29 Jun 2010 15:05:21 -0700 Subject: flat: tweak default stack alignment The recent commit 1f0ce8b3dd667dca7 ("mm: Move ARCH_SLAB_MINALIGN and ARCH_KMALLOC_MINALIGN to ") which moved the ARCH_SLAB_MINALIGN default into the global header inadvertently broke FLAT for a bunch of systems. Blackfin systems now fail on any FLAT exec with: Unable to read code+data+bss, errno 14 When your /init is a FLAT binary, obviously this can be annoying ;). This stems from the alignment usage in the FLAT loader. The behavior before was that FLAT would default to ARCH_SLAB_MINALIGN only if it was defined, and this was only defined by arches when they wanted a larger alignment value. Otherwise it'd default to pointer alignment. Arguably, this is kind of hokey that the FLAT is semi-abusing defines it shouldn't. So let's merge the two alignment requirements so the floor is never 0. Signed-off-by: Mike Frysinger Cc: David McCullough Cc: Greg Ungerer Cc: Paul Mundt Cc: Michal Simek Cc: Hirokazu Takata Cc: Geert Uytterhoeven Cc: David Howells Cc: David Woodhouse Cc: Pekka Enberg Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/binfmt_flat.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'fs') diff --git a/fs/binfmt_flat.c b/fs/binfmt_flat.c index b6ab27ccf214..811384bec8de 100644 --- a/fs/binfmt_flat.c +++ b/fs/binfmt_flat.c @@ -68,11 +68,7 @@ * Here we can be a bit looser than the data sections since this * needs to only meet arch ABI requirements. */ -#ifdef ARCH_SLAB_MINALIGN -#define FLAT_STACK_ALIGN (ARCH_SLAB_MINALIGN) -#else -#define FLAT_STACK_ALIGN (sizeof(void *)) -#endif +#define FLAT_STACK_ALIGN max_t(unsigned long, sizeof(void *), ARCH_SLAB_MINALIGN) #define RELOC_FAILED 0xff00ff01 /* Relocation incorrect somewhere */ #define UNLOADED_LIB 0x7ff000ff /* Placeholder for unused library */ -- cgit v1.2.3 From 46c23d7f520e315dde86881b38ba92ebdf34ced5 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Tue, 29 Jun 2010 15:05:38 -0700 Subject: sysvfs: fix NULL deref. when allocating new inode A call to sysv_write_inode() in sysv_new_inode() to its new interface that replaced wait flag with writeback structure. This was broken by a9185b41a4f84971b930c519f0c63bd450c4810d ("pass writeback_control to ->write_inode"). Signed-off-by: Lubomir Rintel Cc: Christoph Hellwig Cc: Al Viro Cc: [2.6.34.x] Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/sysv/ialloc.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/sysv/ialloc.c b/fs/sysv/ialloc.c index bbd69bdb0fa8..fcc498ec9b33 100644 --- a/fs/sysv/ialloc.c +++ b/fs/sysv/ialloc.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "sysv.h" /* We don't trust the value of @@ -139,6 +140,9 @@ struct inode * sysv_new_inode(const struct inode * dir, mode_t mode) struct inode *inode; sysv_ino_t ino; unsigned count; + struct writeback_control wbc = { + .sync_mode = WB_SYNC_NONE + }; inode = new_inode(sb); if (!inode) @@ -168,7 +172,7 @@ struct inode * sysv_new_inode(const struct inode * dir, mode_t mode) insert_inode_hash(inode); mark_inode_dirty(inode); - sysv_write_inode(inode, 0); /* ensure inode not allocated again */ + sysv_write_inode(inode, &wbc); /* ensure inode not allocated again */ mark_inode_dirty(inode); /* cleared by sysv_write_inode() */ /* That's it. */ unlock_super(sb); -- cgit v1.2.3 From f4985dc714d7ab1920c5aa502b7f4073fa1b4177 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Tue, 29 Jun 2010 15:05:42 -0700 Subject: fs/fcntl.c:kill_fasync_rcu() fa_lock must be IRQ-safe Fix a lockdep-splat-causing regression introduced by commit 989a2979205d ("fasync: RCU and fine grained locking"). kill_fasync() can be called from both process and hard-irq context, so fa_lock must be taken with IRQs disabled. Addresses https://bugzilla.kernel.org/show_bug.cgi?id=16230 Reported-by: Sergey Senozhatsky Reported-by: Dominik Brodowski Tested-by: Dominik Brodowski Cc: Maciej Rutecki Acked-by: Eric Dumazet Cc: Paul E. McKenney Cc: Lai Jiangshan Cc: "David S. Miller" Acked-by: Peter Zijlstra Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/fcntl.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/fcntl.c b/fs/fcntl.c index 51e11bf5708f..9d175d623aab 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c @@ -733,12 +733,14 @@ static void kill_fasync_rcu(struct fasync_struct *fa, int sig, int band) { while (fa) { struct fown_struct *fown; + unsigned long flags; + if (fa->magic != FASYNC_MAGIC) { printk(KERN_ERR "kill_fasync: bad magic number in " "fasync_struct!\n"); return; } - spin_lock(&fa->fa_lock); + spin_lock_irqsave(&fa->fa_lock, flags); if (fa->fa_file) { fown = &fa->fa_file->f_owner; /* Don't send SIGURG to processes which have not set a @@ -747,7 +749,7 @@ static void kill_fasync_rcu(struct fasync_struct *fa, int sig, int band) if (!(sig == SIGURG && fown->signum == 0)) send_sigio(fown, fa->fa_fd, band); } - spin_unlock(&fa->fa_lock); + spin_unlock_irqrestore(&fa->fa_lock, flags); fa = rcu_dereference(fa->fa_next); } } -- cgit v1.2.3 From 2cb4b05e7647891b46b91c07c9a60304803d1688 Mon Sep 17 00:00:00 2001 From: Changli Gao Date: Tue, 29 Jun 2010 13:09:18 +0200 Subject: splice: direct_splice_actor() should not use pos in sd direct_splice_actor() shouldn't use sd->pos, as sd->pos is for file reading, file->f_pos should be used instead. Signed-off-by: Changli Gao Signed-off-by: Miklos Szeredi ---- fs/splice.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) Signed-off-by: Jens Axboe --- fs/splice.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/splice.c b/fs/splice.c index 740e6b9faf7a..41900496d3bb 100644 --- a/fs/splice.c +++ b/fs/splice.c @@ -1282,7 +1282,8 @@ static int direct_splice_actor(struct pipe_inode_info *pipe, { struct file *file = sd->u.file; - return do_splice_from(pipe, file, &sd->pos, sd->total_len, sd->flags); + return do_splice_from(pipe, file, &file->f_pos, sd->total_len, + sd->flags); } /** -- cgit v1.2.3 From 19c9a49b432f245c6293508d164a4350f1f2c601 Mon Sep 17 00:00:00 2001 From: Changli Gao Date: Tue, 29 Jun 2010 13:10:36 +0200 Subject: splice: check f_mode for seekable file check f_mode for seekable file As a seekable file is allowed without a llseek function, so the old way isn't work any more. Signed-off-by: Changli Gao Signed-off-by: Miklos Szeredi ---- fs/splice.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) Signed-off-by: Jens Axboe --- fs/splice.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/splice.c b/fs/splice.c index 41900496d3bb..efdbfece9932 100644 --- a/fs/splice.c +++ b/fs/splice.c @@ -1372,8 +1372,7 @@ static long do_splice(struct file *in, loff_t __user *off_in, if (off_in) return -ESPIPE; if (off_out) { - if (!out->f_op || !out->f_op->llseek || - out->f_op->llseek == no_llseek) + if (!(out->f_mode & FMODE_PWRITE)) return -EINVAL; if (copy_from_user(&offset, off_out, sizeof(loff_t))) return -EFAULT; @@ -1393,8 +1392,7 @@ static long do_splice(struct file *in, loff_t __user *off_in, if (off_out) return -ESPIPE; if (off_in) { - if (!in->f_op || !in->f_op->llseek || - in->f_op->llseek == no_llseek) + if (!(in->f_mode & FMODE_PREAD)) return -EINVAL; if (copy_from_user(&offset, off_in, sizeof(loff_t))) return -EFAULT; -- cgit v1.2.3 From 06d738fa9155ff16dba3d7e501ba4581d01a98cb Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Thu, 1 Jul 2010 08:26:34 +0200 Subject: fs-writeback: fix kernel-doc warnings Fix kernel-doc to match the function's changed args. Warning(fs/fs-writeback.c:190): No description found for parameter 'args' Warning(fs/fs-writeback.c:190): Excess function parameter 'sb' description in 'bdi_queue_work_onstack' Signed-off-by: Randy Dunlap Cc: Christoph Hellwig Signed-off-by: Jens Axboe --- fs/fs-writeback.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 0609607d3955..6981e4b7c148 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -179,7 +179,7 @@ static void bdi_alloc_queue_work(struct backing_dev_info *bdi, /** * bdi_queue_work_onstack - start and wait for writeback - * @sb: write inodes from this super_block + * @args: parameters to control the work queue writeback * * Description: * This function initiates writeback and waits for the operation to -- cgit v1.2.3 From 153a10939ea6e42e9c0115b0645060d0d7bb4697 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Mon, 5 Jul 2010 09:44:17 -0700 Subject: ceph: fix crush device 'out' threshold to 1.0, not 0.1 Fix a typo that made any OSD weighted between 0.1 and 1.0 effectively weighted as 1.0 (fully in). Signed-off-by: Sage Weil --- fs/ceph/crush/mapper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/ceph/crush/mapper.c b/fs/ceph/crush/mapper.c index 374266bddd9c..a4eec133258e 100644 --- a/fs/ceph/crush/mapper.c +++ b/fs/ceph/crush/mapper.c @@ -264,7 +264,7 @@ static int crush_bucket_choose(struct crush_bucket *in, int x, int r) */ static int is_out(struct crush_map *map, __u32 *weight, int item, int x) { - if (weight[item] >= 0x1000) + if (weight[item] >= 0x10000) return 0; if (weight[item] == 0) return 1; -- cgit v1.2.3 From e3559997ac5dbd45a27805ed7951c7252b2b8ad1 Mon Sep 17 00:00:00 2001 From: Huang Weiyi Date: Tue, 25 May 2010 22:07:53 +0800 Subject: ncpfs: Remove duplicated #include Remove duplicated #include('s) in fs/ncpfs/ioctl.c Signed-off-by: Huang Weiyi Signed-off-by: Frederic Weisbecker --- fs/ncpfs/ioctl.c | 1 - 1 file changed, 1 deletion(-) (limited to 'fs') diff --git a/fs/ncpfs/ioctl.c b/fs/ncpfs/ioctl.c index 023c03d02070..84a8cfc4e38e 100644 --- a/fs/ncpfs/ioctl.c +++ b/fs/ncpfs/ioctl.c @@ -20,7 +20,6 @@ #include #include #include -#include #include -- cgit v1.2.3 From ed98adad3d87594c55347824e85137d1829c9e70 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Mon, 5 Jul 2010 12:15:14 -0700 Subject: ceph: fix message revocation A message can be on a queue (pending or sent), or out_msg (sending), or both. We were assuming that if it's not on a queue it couldn't be out_msg, but that was false in the case of lossy connections like the OSD. Fix ceph_con_revoke() to treat these cases independently. Also, fix the out_kvec_is_message check to only trigger if we are currently sending _this_ message. This fixes a GPF in tcp_sendpage, triggered by OSD restarts. Signed-off-by: Sage Weil --- fs/ceph/messenger.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'fs') diff --git a/fs/ceph/messenger.c b/fs/ceph/messenger.c index 9ad43a310a41..9692d08e2f88 100644 --- a/fs/ceph/messenger.c +++ b/fs/ceph/messenger.c @@ -2015,20 +2015,20 @@ void ceph_con_revoke(struct ceph_connection *con, struct ceph_msg *msg) { mutex_lock(&con->mutex); if (!list_empty(&msg->list_head)) { - dout("con_revoke %p msg %p\n", con, msg); + dout("con_revoke %p msg %p - was on queue\n", con, msg); list_del_init(&msg->list_head); ceph_msg_put(msg); msg->hdr.seq = 0; - if (con->out_msg == msg) { - ceph_msg_put(con->out_msg); - con->out_msg = NULL; - } + } + if (con->out_msg == msg) { + dout("con_revoke %p msg %p - was sending\n", con, msg); + con->out_msg = NULL; if (con->out_kvec_is_msg) { con->out_skip = con->out_kvec_bytes; con->out_kvec_is_msg = false; } - } else { - dout("con_revoke %p msg %p - not queued (sent?)\n", con, msg); + ceph_msg_put(msg); + msg->hdr.seq = 0; } mutex_unlock(&con->mutex); } -- cgit v1.2.3 From 22b1de06c9fe128ca3de72560c3e8c2cabf2927a Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Mon, 5 Jul 2010 15:36:49 -0700 Subject: ceph: fix leak of mon authorizer Fix leak of a struct ceph_buffer on umount. Signed-off-by: Sage Weil --- fs/ceph/auth_x.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'fs') diff --git a/fs/ceph/auth_x.c b/fs/ceph/auth_x.c index 3fe49042d8ad..6d44053ecff1 100644 --- a/fs/ceph/auth_x.c +++ b/fs/ceph/auth_x.c @@ -613,6 +613,9 @@ static void ceph_x_destroy(struct ceph_auth_client *ac) remove_ticket_handler(ac, th); } + if (xi->auth_authorizer.buf) + ceph_buffer_put(xi->auth_authorizer.buf); + kfree(ac->private); ac->private = NULL; } -- cgit v1.2.3 From 9c3a8ee8a1d72c5c0d7fbdf426d80e270ddfa54c Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 10 Jun 2010 12:07:27 +0200 Subject: writeback: remove writeback_inodes_wbc This was just an odd wrapper around writeback_inodes_wb. Removing this also allows to get rid of the bdi member of struct writeback_control which was rather out of place there. Signed-off-by: Christoph Hellwig Signed-off-by: Jens Axboe --- fs/afs/write.c | 1 - fs/btrfs/extent_io.c | 2 -- fs/fs-writeback.c | 12 ++---------- include/linux/writeback.h | 5 ++--- mm/backing-dev.c | 3 +-- mm/page-writeback.c | 3 +-- 6 files changed, 6 insertions(+), 20 deletions(-) (limited to 'fs') diff --git a/fs/afs/write.c b/fs/afs/write.c index 3dab9e9948d0..722743b152d8 100644 --- a/fs/afs/write.c +++ b/fs/afs/write.c @@ -680,7 +680,6 @@ int afs_writeback_all(struct afs_vnode *vnode) { struct address_space *mapping = vnode->vfs_inode.i_mapping; struct writeback_control wbc = { - .bdi = mapping->backing_dev_info, .sync_mode = WB_SYNC_ALL, .nr_to_write = LONG_MAX, .range_cyclic = 1, diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index a4080c21ec55..d74e6af9b53a 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -2594,7 +2594,6 @@ int extent_write_full_page(struct extent_io_tree *tree, struct page *page, .sync_io = wbc->sync_mode == WB_SYNC_ALL, }; struct writeback_control wbc_writepages = { - .bdi = wbc->bdi, .sync_mode = wbc->sync_mode, .older_than_this = NULL, .nr_to_write = 64, @@ -2628,7 +2627,6 @@ int extent_write_locked_range(struct extent_io_tree *tree, struct inode *inode, .sync_io = mode == WB_SYNC_ALL, }; struct writeback_control wbc_writepages = { - .bdi = inode->i_mapping->backing_dev_info, .sync_mode = mode, .older_than_this = NULL, .nr_to_write = nr_pages * 2, diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 6981e4b7c148..94a602e98bb5 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -614,8 +614,8 @@ static int writeback_sb_inodes(struct super_block *sb, return 1; } -static void writeback_inodes_wb(struct bdi_writeback *wb, - struct writeback_control *wbc) +void writeback_inodes_wb(struct bdi_writeback *wb, + struct writeback_control *wbc) { int ret = 0; @@ -660,13 +660,6 @@ static void writeback_inodes_wb(struct bdi_writeback *wb, /* Leave any unwritten inodes on b_io */ } -void writeback_inodes_wbc(struct writeback_control *wbc) -{ - struct backing_dev_info *bdi = wbc->bdi; - - writeback_inodes_wb(&bdi->wb, wbc); -} - /* * The maximum number of pages to writeout in a single bdi flush/kupdate * operation. We do this so we don't hold I_SYNC against an inode for @@ -705,7 +698,6 @@ static long wb_writeback(struct bdi_writeback *wb, struct wb_writeback_args *args) { struct writeback_control wbc = { - .bdi = wb->bdi, .sb = args->sb, .sync_mode = args->sync_mode, .older_than_this = NULL, diff --git a/include/linux/writeback.h b/include/linux/writeback.h index d63ef8f9609f..f6756f6a610c 100644 --- a/include/linux/writeback.h +++ b/include/linux/writeback.h @@ -27,8 +27,6 @@ enum writeback_sync_modes { * in a manner such that unspecified fields are set to zero. */ struct writeback_control { - struct backing_dev_info *bdi; /* If !NULL, only write back this - queue */ struct super_block *sb; /* if !NULL, only write inodes from this super_block */ enum writeback_sync_modes sync_mode; @@ -66,7 +64,8 @@ int inode_wait(void *); void writeback_inodes_sb(struct super_block *); int writeback_inodes_sb_if_idle(struct super_block *); void sync_inodes_sb(struct super_block *); -void writeback_inodes_wbc(struct writeback_control *wbc); +void writeback_inodes_wb(struct bdi_writeback *wb, + struct writeback_control *wbc); long wb_do_writeback(struct bdi_writeback *wb, int force_wait); void wakeup_flusher_threads(long nr_pages); diff --git a/mm/backing-dev.c b/mm/backing-dev.c index 660a87a22511..6e0b09a1ec2c 100644 --- a/mm/backing-dev.c +++ b/mm/backing-dev.c @@ -340,14 +340,13 @@ int bdi_has_dirty_io(struct backing_dev_info *bdi) static void bdi_flush_io(struct backing_dev_info *bdi) { struct writeback_control wbc = { - .bdi = bdi, .sync_mode = WB_SYNC_NONE, .older_than_this = NULL, .range_cyclic = 1, .nr_to_write = 1024, }; - writeback_inodes_wbc(&wbc); + writeback_inodes_wb(&bdi->wb, &wbc); } /* diff --git a/mm/page-writeback.c b/mm/page-writeback.c index 54f28bd493d3..37498ef61548 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c @@ -495,7 +495,6 @@ static void balance_dirty_pages(struct address_space *mapping, for (;;) { struct writeback_control wbc = { - .bdi = bdi, .sync_mode = WB_SYNC_NONE, .older_than_this = NULL, .nr_to_write = write_chunk, @@ -537,7 +536,7 @@ static void balance_dirty_pages(struct address_space *mapping, * up. */ if (bdi_nr_reclaimable > bdi_thresh) { - writeback_inodes_wbc(&wbc); + writeback_inodes_wb(&bdi->wb, &wbc); pages_written += write_chunk - wbc.nr_to_write; get_dirty_limits(&background_thresh, &dirty_thresh, &bdi_thresh, bdi); -- cgit v1.2.3 From edadfb10ba35da7253541e4155aa92eff758ebe6 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 10 Jun 2010 12:07:54 +0200 Subject: writeback: split writeback_inodes_wb The case where we have a superblock doesn't require a loop here as we scan over all inodes in writeback_sb_inodes. Split it out into a separate helper to make the code simpler. This also allows to get rid of the sb member in struct writeback_control, which was rather out of place there. Also update the comments in writeback_sb_inodes that explain the handling of inodes from wrong superblocks. Signed-off-by: Christoph Hellwig Signed-off-by: Jens Axboe --- fs/fs-writeback.c | 82 ++++++++++++++++++++++++++--------------------- include/linux/writeback.h | 2 -- 2 files changed, 46 insertions(+), 38 deletions(-) (limited to 'fs') diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 94a602e98bb5..8cc06d5432b5 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -554,29 +554,41 @@ static bool pin_sb_for_writeback(struct super_block *sb) /* * Write a portion of b_io inodes which belong to @sb. - * If @wbc->sb != NULL, then find and write all such + * + * If @only_this_sb is true, then find and write all such * inodes. Otherwise write only ones which go sequentially * in reverse order. + * * Return 1, if the caller writeback routine should be * interrupted. Otherwise return 0. */ -static int writeback_sb_inodes(struct super_block *sb, - struct bdi_writeback *wb, - struct writeback_control *wbc) +static int writeback_sb_inodes(struct super_block *sb, struct bdi_writeback *wb, + struct writeback_control *wbc, bool only_this_sb) { while (!list_empty(&wb->b_io)) { long pages_skipped; struct inode *inode = list_entry(wb->b_io.prev, struct inode, i_list); - if (wbc->sb && sb != inode->i_sb) { - /* super block given and doesn't - match, skip this inode */ - redirty_tail(inode); - continue; - } - if (sb != inode->i_sb) - /* finish with this superblock */ + + if (inode->i_sb != sb) { + if (only_this_sb) { + /* + * We only want to write back data for this + * superblock, move all inodes not belonging + * to it back onto the dirty list. + */ + redirty_tail(inode); + continue; + } + + /* + * The inode belongs to a different superblock. + * Bounce back to the caller to unpin this and + * pin the next superblock. + */ return 0; + } + if (inode->i_state & (I_NEW | I_WILL_FREE)) { requeue_io(inode); continue; @@ -629,29 +641,12 @@ void writeback_inodes_wb(struct bdi_writeback *wb, struct inode, i_list); struct super_block *sb = inode->i_sb; - if (wbc->sb) { - /* - * We are requested to write out inodes for a specific - * superblock. This means we already have s_umount - * taken by the caller which also waits for us to - * complete the writeout. - */ - if (sb != wbc->sb) { - redirty_tail(inode); - continue; - } - - WARN_ON(!rwsem_is_locked(&sb->s_umount)); - - ret = writeback_sb_inodes(sb, wb, wbc); - } else { - if (!pin_sb_for_writeback(sb)) { - requeue_io(inode); - continue; - } - ret = writeback_sb_inodes(sb, wb, wbc); - drop_super(sb); + if (!pin_sb_for_writeback(sb)) { + requeue_io(inode); + continue; } + ret = writeback_sb_inodes(sb, wb, wbc, false); + drop_super(sb); if (ret) break; @@ -660,6 +655,19 @@ void writeback_inodes_wb(struct bdi_writeback *wb, /* Leave any unwritten inodes on b_io */ } +static void __writeback_inodes_sb(struct super_block *sb, + struct bdi_writeback *wb, struct writeback_control *wbc) +{ + WARN_ON(!rwsem_is_locked(&sb->s_umount)); + + wbc->wb_start = jiffies; /* livelock avoidance */ + spin_lock(&inode_lock); + if (!wbc->for_kupdate || list_empty(&wb->b_io)) + queue_io(wb, wbc->older_than_this); + writeback_sb_inodes(sb, wb, wbc, true); + spin_unlock(&inode_lock); +} + /* * The maximum number of pages to writeout in a single bdi flush/kupdate * operation. We do this so we don't hold I_SYNC against an inode for @@ -698,7 +706,6 @@ static long wb_writeback(struct bdi_writeback *wb, struct wb_writeback_args *args) { struct writeback_control wbc = { - .sb = args->sb, .sync_mode = args->sync_mode, .older_than_this = NULL, .for_kupdate = args->for_kupdate, @@ -736,7 +743,10 @@ static long wb_writeback(struct bdi_writeback *wb, wbc.more_io = 0; wbc.nr_to_write = MAX_WRITEBACK_PAGES; wbc.pages_skipped = 0; - writeback_inodes_wb(wb, &wbc); + if (args->sb) + __writeback_inodes_sb(args->sb, wb, &wbc); + else + writeback_inodes_wb(wb, &wbc); args->nr_pages -= MAX_WRITEBACK_PAGES - wbc.nr_to_write; wrote += MAX_WRITEBACK_PAGES - wbc.nr_to_write; diff --git a/include/linux/writeback.h b/include/linux/writeback.h index f6756f6a610c..c24eca71e80c 100644 --- a/include/linux/writeback.h +++ b/include/linux/writeback.h @@ -27,8 +27,6 @@ enum writeback_sync_modes { * in a manner such that unspecified fields are set to zero. */ struct writeback_control { - struct super_block *sb; /* if !NULL, only write inodes from - this super_block */ enum writeback_sync_modes sync_mode; unsigned long *older_than_this; /* If !NULL, only write back inodes older than this */ -- cgit v1.2.3 From 83ba7b071f30f7c01f72518ad72d5cd203c27502 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 6 Jul 2010 08:59:53 +0200 Subject: writeback: simplify the write back thread queue First remove items from work_list as soon as we start working on them. This means we don't have to track any pending or visited state and can get rid of all the RCU magic freeing the work items - we can simply free them once the operation has finished. Second use a real completion for tracking synchronous requests - if the caller sets the completion pointer we complete it, otherwise use it as a boolean indicator that we can free the work item directly. Third unify struct wb_writeback_args and struct bdi_work into a single data structure, wb_writeback_work. Previous we set all parameters into a struct wb_writeback_args, copied it into struct bdi_work, copied it again on the stack to use it there. Instead of just allocate one structure dynamically or on the stack and use it all the way through the stack. Signed-off-by: Christoph Hellwig Signed-off-by: Jens Axboe --- fs/fs-writeback.c | 253 ++++++++++++-------------------------------- include/linux/backing-dev.h | 2 - mm/backing-dev.c | 14 +-- 3 files changed, 72 insertions(+), 197 deletions(-) (limited to 'fs') diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 8cc06d5432b5..d5be1693ac93 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -38,43 +38,18 @@ int nr_pdflush_threads; /* * Passed into wb_writeback(), essentially a subset of writeback_control */ -struct wb_writeback_args { +struct wb_writeback_work { long nr_pages; struct super_block *sb; enum writeback_sync_modes sync_mode; unsigned int for_kupdate:1; unsigned int range_cyclic:1; unsigned int for_background:1; -}; -/* - * Work items for the bdi_writeback threads - */ -struct bdi_work { struct list_head list; /* pending work list */ - struct rcu_head rcu_head; /* for RCU free/clear of work */ - - unsigned long seen; /* threads that have seen this work */ - atomic_t pending; /* number of threads still to do work */ - - struct wb_writeback_args args; /* writeback arguments */ - - unsigned long state; /* flag bits, see WS_* */ -}; - -enum { - WS_INPROGRESS = 0, - WS_ONSTACK, + struct completion *done; /* set if the caller waits */ }; -static inline void bdi_work_init(struct bdi_work *work, - struct wb_writeback_args *args) -{ - INIT_RCU_HEAD(&work->rcu_head); - work->args = *args; - __set_bit(WS_INPROGRESS, &work->state); -} - /** * writeback_in_progress - determine whether there is writeback in progress * @bdi: the device's backing_dev_info structure. @@ -87,49 +62,11 @@ int writeback_in_progress(struct backing_dev_info *bdi) return !list_empty(&bdi->work_list); } -static void bdi_work_free(struct rcu_head *head) -{ - struct bdi_work *work = container_of(head, struct bdi_work, rcu_head); - - clear_bit(WS_INPROGRESS, &work->state); - smp_mb__after_clear_bit(); - wake_up_bit(&work->state, WS_INPROGRESS); - - if (!test_bit(WS_ONSTACK, &work->state)) - kfree(work); -} - -static void wb_clear_pending(struct bdi_writeback *wb, struct bdi_work *work) -{ - /* - * The caller has retrieved the work arguments from this work, - * drop our reference. If this is the last ref, delete and free it - */ - if (atomic_dec_and_test(&work->pending)) { - struct backing_dev_info *bdi = wb->bdi; - - spin_lock(&bdi->wb_lock); - list_del_rcu(&work->list); - spin_unlock(&bdi->wb_lock); - - call_rcu(&work->rcu_head, bdi_work_free); - } -} - -static void bdi_queue_work(struct backing_dev_info *bdi, struct bdi_work *work) +static void bdi_queue_work(struct backing_dev_info *bdi, + struct wb_writeback_work *work) { - work->seen = bdi->wb_mask; - BUG_ON(!work->seen); - atomic_set(&work->pending, bdi->wb_cnt); - BUG_ON(!bdi->wb_cnt); - - /* - * list_add_tail_rcu() contains the necessary barriers to - * make sure the above stores are seen before the item is - * noticed on the list - */ spin_lock(&bdi->wb_lock); - list_add_tail_rcu(&work->list, &bdi->work_list); + list_add_tail(&work->list, &bdi->work_list); spin_unlock(&bdi->wb_lock); /* @@ -146,55 +83,29 @@ static void bdi_queue_work(struct backing_dev_info *bdi, struct bdi_work *work) } } -/* - * Used for on-stack allocated work items. The caller needs to wait until - * the wb threads have acked the work before it's safe to continue. - */ -static void bdi_wait_on_work_done(struct bdi_work *work) -{ - wait_on_bit(&work->state, WS_INPROGRESS, bdi_sched_wait, - TASK_UNINTERRUPTIBLE); -} - -static void bdi_alloc_queue_work(struct backing_dev_info *bdi, - struct wb_writeback_args *args) +static void +__bdi_start_writeback(struct backing_dev_info *bdi, long nr_pages, + bool range_cyclic, bool for_background) { - struct bdi_work *work; + struct wb_writeback_work *work; /* * This is WB_SYNC_NONE writeback, so if allocation fails just * wakeup the thread for old dirty data writeback */ - work = kmalloc(sizeof(*work), GFP_ATOMIC); - if (work) { - bdi_work_init(work, args); - bdi_queue_work(bdi, work); - } else { - struct bdi_writeback *wb = &bdi->wb; - - if (wb->task) - wake_up_process(wb->task); + work = kzalloc(sizeof(*work), GFP_ATOMIC); + if (!work) { + if (bdi->wb.task) + wake_up_process(bdi->wb.task); + return; } -} -/** - * bdi_queue_work_onstack - start and wait for writeback - * @args: parameters to control the work queue writeback - * - * Description: - * This function initiates writeback and waits for the operation to - * complete. Callers must hold the sb s_umount semaphore for - * reading, to avoid having the super disappear before we are done. - */ -static void bdi_queue_work_onstack(struct wb_writeback_args *args) -{ - struct bdi_work work; + work->sync_mode = WB_SYNC_NONE; + work->nr_pages = nr_pages; + work->range_cyclic = range_cyclic; + work->for_background = for_background; - bdi_work_init(&work, args); - __set_bit(WS_ONSTACK, &work.state); - - bdi_queue_work(args->sb->s_bdi, &work); - bdi_wait_on_work_done(&work); + bdi_queue_work(bdi, work); } /** @@ -210,13 +121,7 @@ static void bdi_queue_work_onstack(struct wb_writeback_args *args) */ void bdi_start_writeback(struct backing_dev_info *bdi, long nr_pages) { - struct wb_writeback_args args = { - .sync_mode = WB_SYNC_NONE, - .nr_pages = nr_pages, - .range_cyclic = 1, - }; - - bdi_alloc_queue_work(bdi, &args); + __bdi_start_writeback(bdi, nr_pages, true, false); } /** @@ -230,13 +135,7 @@ void bdi_start_writeback(struct backing_dev_info *bdi, long nr_pages) */ void bdi_start_background_writeback(struct backing_dev_info *bdi) { - struct wb_writeback_args args = { - .sync_mode = WB_SYNC_NONE, - .nr_pages = LONG_MAX, - .for_background = 1, - .range_cyclic = 1, - }; - bdi_alloc_queue_work(bdi, &args); + __bdi_start_writeback(bdi, LONG_MAX, true, true); } /* @@ -703,14 +602,14 @@ static inline bool over_bground_thresh(void) * all dirty pages if they are all attached to "old" mappings. */ static long wb_writeback(struct bdi_writeback *wb, - struct wb_writeback_args *args) + struct wb_writeback_work *work) { struct writeback_control wbc = { - .sync_mode = args->sync_mode, + .sync_mode = work->sync_mode, .older_than_this = NULL, - .for_kupdate = args->for_kupdate, - .for_background = args->for_background, - .range_cyclic = args->range_cyclic, + .for_kupdate = work->for_kupdate, + .for_background = work->for_background, + .range_cyclic = work->range_cyclic, }; unsigned long oldest_jif; long wrote = 0; @@ -730,24 +629,24 @@ static long wb_writeback(struct bdi_writeback *wb, /* * Stop writeback when nr_pages has been consumed */ - if (args->nr_pages <= 0) + if (work->nr_pages <= 0) break; /* * For background writeout, stop when we are below the * background dirty threshold */ - if (args->for_background && !over_bground_thresh()) + if (work->for_background && !over_bground_thresh()) break; wbc.more_io = 0; wbc.nr_to_write = MAX_WRITEBACK_PAGES; wbc.pages_skipped = 0; - if (args->sb) - __writeback_inodes_sb(args->sb, wb, &wbc); + if (work->sb) + __writeback_inodes_sb(work->sb, wb, &wbc); else writeback_inodes_wb(wb, &wbc); - args->nr_pages -= MAX_WRITEBACK_PAGES - wbc.nr_to_write; + work->nr_pages -= MAX_WRITEBACK_PAGES - wbc.nr_to_write; wrote += MAX_WRITEBACK_PAGES - wbc.nr_to_write; /* @@ -783,31 +682,21 @@ static long wb_writeback(struct bdi_writeback *wb, } /* - * Return the next bdi_work struct that hasn't been processed by this - * wb thread yet. ->seen is initially set for each thread that exists - * for this device, when a thread first notices a piece of work it - * clears its bit. Depending on writeback type, the thread will notify - * completion on either receiving the work (WB_SYNC_NONE) or after - * it is done (WB_SYNC_ALL). + * Return the next wb_writeback_work struct that hasn't been processed yet. */ -static struct bdi_work *get_next_work_item(struct backing_dev_info *bdi, - struct bdi_writeback *wb) +static struct wb_writeback_work * +get_next_work_item(struct backing_dev_info *bdi, struct bdi_writeback *wb) { - struct bdi_work *work, *ret = NULL; - - rcu_read_lock(); - - list_for_each_entry_rcu(work, &bdi->work_list, list) { - if (!test_bit(wb->nr, &work->seen)) - continue; - clear_bit(wb->nr, &work->seen); + struct wb_writeback_work *work = NULL; - ret = work; - break; + spin_lock(&bdi->wb_lock); + if (!list_empty(&bdi->work_list)) { + work = list_entry(bdi->work_list.next, + struct wb_writeback_work, list); + list_del_init(&work->list); } - - rcu_read_unlock(); - return ret; + spin_unlock(&bdi->wb_lock); + return work; } static long wb_check_old_data_flush(struct bdi_writeback *wb) @@ -832,14 +721,14 @@ static long wb_check_old_data_flush(struct bdi_writeback *wb) (inodes_stat.nr_inodes - inodes_stat.nr_unused); if (nr_pages) { - struct wb_writeback_args args = { + struct wb_writeback_work work = { .nr_pages = nr_pages, .sync_mode = WB_SYNC_NONE, .for_kupdate = 1, .range_cyclic = 1, }; - return wb_writeback(wb, &args); + return wb_writeback(wb, &work); } return 0; @@ -851,33 +740,27 @@ static long wb_check_old_data_flush(struct bdi_writeback *wb) long wb_do_writeback(struct bdi_writeback *wb, int force_wait) { struct backing_dev_info *bdi = wb->bdi; - struct bdi_work *work; + struct wb_writeback_work *work; long wrote = 0; while ((work = get_next_work_item(bdi, wb)) != NULL) { - struct wb_writeback_args args = work->args; - /* * Override sync mode, in case we must wait for completion + * because this thread is exiting now. */ if (force_wait) - work->args.sync_mode = args.sync_mode = WB_SYNC_ALL; + work->sync_mode = WB_SYNC_ALL; - /* - * If this isn't a data integrity operation, just notify - * that we have seen this work and we are now starting it. - */ - if (!test_bit(WS_ONSTACK, &work->state)) - wb_clear_pending(wb, work); - - wrote += wb_writeback(wb, &args); + wrote += wb_writeback(wb, work); /* - * This is a data integrity writeback, so only do the - * notification when we have completed the work. + * Notify the caller of completion if this is a synchronous + * work item, otherwise just free it. */ - if (test_bit(WS_ONSTACK, &work->state)) - wb_clear_pending(wb, work); + if (work->done) + complete(work->done); + else + kfree(work); } /* @@ -940,14 +823,9 @@ int bdi_writeback_task(struct bdi_writeback *wb) void wakeup_flusher_threads(long nr_pages) { struct backing_dev_info *bdi; - struct wb_writeback_args args = { - .sync_mode = WB_SYNC_NONE, - }; - if (nr_pages) { - args.nr_pages = nr_pages; - } else { - args.nr_pages = global_page_state(NR_FILE_DIRTY) + + if (!nr_pages) { + nr_pages = global_page_state(NR_FILE_DIRTY) + global_page_state(NR_UNSTABLE_NFS); } @@ -955,7 +833,7 @@ void wakeup_flusher_threads(long nr_pages) list_for_each_entry_rcu(bdi, &bdi_list, bdi_list) { if (!bdi_has_dirty_io(bdi)) continue; - bdi_alloc_queue_work(bdi, &args); + __bdi_start_writeback(bdi, nr_pages, false, false); } rcu_read_unlock(); } @@ -1164,17 +1042,20 @@ void writeback_inodes_sb(struct super_block *sb) { unsigned long nr_dirty = global_page_state(NR_FILE_DIRTY); unsigned long nr_unstable = global_page_state(NR_UNSTABLE_NFS); - struct wb_writeback_args args = { + DECLARE_COMPLETION_ONSTACK(done); + struct wb_writeback_work work = { .sb = sb, .sync_mode = WB_SYNC_NONE, + .done = &done, }; WARN_ON(!rwsem_is_locked(&sb->s_umount)); - args.nr_pages = nr_dirty + nr_unstable + + work.nr_pages = nr_dirty + nr_unstable + (inodes_stat.nr_inodes - inodes_stat.nr_unused); - bdi_queue_work_onstack(&args); + bdi_queue_work(sb->s_bdi, &work); + wait_for_completion(&done); } EXPORT_SYMBOL(writeback_inodes_sb); @@ -1206,16 +1087,20 @@ EXPORT_SYMBOL(writeback_inodes_sb_if_idle); */ void sync_inodes_sb(struct super_block *sb) { - struct wb_writeback_args args = { + DECLARE_COMPLETION_ONSTACK(done); + struct wb_writeback_work work = { .sb = sb, .sync_mode = WB_SYNC_ALL, .nr_pages = LONG_MAX, .range_cyclic = 0, + .done = &done, }; WARN_ON(!rwsem_is_locked(&sb->s_umount)); - bdi_queue_work_onstack(&args); + bdi_queue_work(sb->s_bdi, &work); + wait_for_completion(&done); + wait_sb_inodes(sb); } EXPORT_SYMBOL(sync_inodes_sb); diff --git a/include/linux/backing-dev.h b/include/linux/backing-dev.h index 9ae2889096b6..e9aec0d099df 100644 --- a/include/linux/backing-dev.h +++ b/include/linux/backing-dev.h @@ -82,8 +82,6 @@ struct backing_dev_info { struct bdi_writeback wb; /* default writeback info for this bdi */ spinlock_t wb_lock; /* protects update side of wb_list */ struct list_head wb_list; /* the flusher threads hanging off this bdi */ - unsigned long wb_mask; /* bitmask of registered tasks */ - unsigned int wb_cnt; /* number of registered tasks */ struct list_head work_list; diff --git a/mm/backing-dev.c b/mm/backing-dev.c index 6e0b09a1ec2c..123bcef13e51 100644 --- a/mm/backing-dev.c +++ b/mm/backing-dev.c @@ -104,15 +104,13 @@ static int bdi_debug_stats_show(struct seq_file *m, void *v) "b_more_io: %8lu\n" "bdi_list: %8u\n" "state: %8lx\n" - "wb_mask: %8lx\n" - "wb_list: %8u\n" - "wb_cnt: %8u\n", + "wb_list: %8u\n", (unsigned long) K(bdi_stat(bdi, BDI_WRITEBACK)), (unsigned long) K(bdi_stat(bdi, BDI_RECLAIMABLE)), K(bdi_thresh), K(dirty_thresh), K(background_thresh), nr_wb, nr_dirty, nr_io, nr_more_io, - !list_empty(&bdi->bdi_list), bdi->state, bdi->wb_mask, - !list_empty(&bdi->wb_list), bdi->wb_cnt); + !list_empty(&bdi->bdi_list), bdi->state, + !list_empty(&bdi->wb_list)); #undef K return 0; @@ -674,12 +672,6 @@ int bdi_init(struct backing_dev_info *bdi) bdi_wb_init(&bdi->wb, bdi); - /* - * Just one thread support for now, hard code mask and count - */ - bdi->wb_mask = 1; - bdi->wb_cnt = 1; - for (i = 0; i < NR_BDI_STAT_ITEMS; i++) { err = percpu_counter_init(&bdi->bdi_stat[i], 0); if (err) -- cgit v1.2.3 From 70d9e384aa7df681cfffd65947af72b22e86690e Mon Sep 17 00:00:00 2001 From: Davidlohr Bueso Date: Tue, 6 Jul 2010 00:50:58 -0400 Subject: omfs: fix memory leak In the error path of omfs_fill_super(), the FS super block info (sbi) is not being freed. Correct this. Signed-off-by: Davidlohr Bueso Signed-off-by: Bob Copeland --- fs/omfs/inode.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'fs') diff --git a/fs/omfs/inode.c b/fs/omfs/inode.c index 089839a6cc64..b5d6380e03fb 100644 --- a/fs/omfs/inode.c +++ b/fs/omfs/inode.c @@ -529,6 +529,8 @@ out_brelse_bh2: out_brelse_bh: brelse(bh); end: + if (ret) + kfree(sbi); return ret; } -- cgit v1.2.3 From b0bbb0be8f7fbf6d366b359e034c78a96c4e274d Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 8 Jul 2010 14:49:38 +0200 Subject: ceph: add kfree() to error path We leak a "pi" on this error path. Signed-off-by: Dan Carpenter Signed-off-by: Sage Weil --- fs/ceph/osdmap.c | 1 + 1 file changed, 1 insertion(+) (limited to 'fs') diff --git a/fs/ceph/osdmap.c b/fs/ceph/osdmap.c index 50ce64ebd330..277f8b339577 100644 --- a/fs/ceph/osdmap.c +++ b/fs/ceph/osdmap.c @@ -568,6 +568,7 @@ struct ceph_osdmap *osdmap_decode(void **p, void *end) if (ev > CEPH_PG_POOL_VERSION) { pr_warning("got unknown v %d > %d of ceph_pg_pool\n", ev, CEPH_PG_POOL_VERSION); + kfree(pi); goto bad; } __decode_pool(p, pi); -- cgit v1.2.3 From a4bfb4cf11fd2211b788af59dc8a8b4394bca227 Mon Sep 17 00:00:00 2001 From: Joel Becker Date: Tue, 6 Jul 2010 14:36:06 -0700 Subject: ocfs2: When zero extending, do it by page. ocfs2_zero_extend() does its zeroing block by block, but it calls a function named ocfs2_write_zero_page(). Let's have ocfs2_write_zero_page() handle the page level. From ocfs2_zero_extend()'s perspective, it is now page-at-a-time. Signed-off-by: Joel Becker Cc: stable@kernel.org --- fs/ocfs2/aops.c | 30 -------------- fs/ocfs2/file.c | 118 ++++++++++++++++++++++++++++++++++++++++---------------- 2 files changed, 84 insertions(+), 64 deletions(-) (limited to 'fs') diff --git a/fs/ocfs2/aops.c b/fs/ocfs2/aops.c index 3623ca20cc18..9a5c931439bd 100644 --- a/fs/ocfs2/aops.c +++ b/fs/ocfs2/aops.c @@ -459,36 +459,6 @@ int walk_page_buffers( handle_t *handle, return ret; } -handle_t *ocfs2_start_walk_page_trans(struct inode *inode, - struct page *page, - unsigned from, - unsigned to) -{ - struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); - handle_t *handle; - int ret = 0; - - handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS); - if (IS_ERR(handle)) { - ret = -ENOMEM; - mlog_errno(ret); - goto out; - } - - if (ocfs2_should_order_data(inode)) { - ret = ocfs2_jbd2_file_inode(handle, inode); - if (ret < 0) - mlog_errno(ret); - } -out: - if (ret) { - if (!IS_ERR(handle)) - ocfs2_commit_trans(osb, handle); - handle = ERR_PTR(ret); - } - return handle; -} - static sector_t ocfs2_bmap(struct address_space *mapping, sector_t block) { sector_t status; diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index 6a13ea64c447..4cfc976a9067 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c @@ -724,28 +724,55 @@ leave: return status; } +/* + * While a write will already be ordering the data, a truncate will not. + * Thus, we need to explicitly order the zeroed pages. + */ +static handle_t *ocfs2_zero_start_ordered_transaction(struct inode *inode) +{ + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); + handle_t *handle = NULL; + int ret = 0; + + if (!ocfs2_should_order_data(inode)) + goto out; + + handle = ocfs2_start_trans(osb, OCFS2_INODE_UPDATE_CREDITS); + if (IS_ERR(handle)) { + ret = -ENOMEM; + mlog_errno(ret); + goto out; + } + + ret = ocfs2_jbd2_file_inode(handle, inode); + if (ret < 0) + mlog_errno(ret); + +out: + if (ret) { + if (!IS_ERR(handle)) + ocfs2_commit_trans(osb, handle); + handle = ERR_PTR(ret); + } + return handle; +} + /* Some parts of this taken from generic_cont_expand, which turned out * to be too fragile to do exactly what we need without us having to * worry about recursive locking in ->write_begin() and ->write_end(). */ -static int ocfs2_write_zero_page(struct inode *inode, - u64 size) +static int ocfs2_write_zero_page(struct inode *inode, u64 abs_from, + u64 abs_to) { struct address_space *mapping = inode->i_mapping; struct page *page; - unsigned long index; - unsigned int offset; + unsigned long index = abs_from >> PAGE_CACHE_SHIFT; handle_t *handle = NULL; int ret; + unsigned zero_from, zero_to, block_start, block_end; - offset = (size & (PAGE_CACHE_SIZE-1)); /* Within page */ - /* ugh. in prepare/commit_write, if from==to==start of block, we - ** skip the prepare. make sure we never send an offset for the start - ** of a block - */ - if ((offset & (inode->i_sb->s_blocksize - 1)) == 0) { - offset++; - } - index = size >> PAGE_CACHE_SHIFT; + BUG_ON(abs_from >= abs_to); + BUG_ON(abs_to > (((u64)index + 1) << PAGE_CACHE_SHIFT)); + BUG_ON(abs_from & (inode->i_blkbits - 1)); page = grab_cache_page(mapping, index); if (!page) { @@ -754,31 +781,51 @@ static int ocfs2_write_zero_page(struct inode *inode, goto out; } - ret = ocfs2_prepare_write_nolock(inode, page, offset, offset); - if (ret < 0) { - mlog_errno(ret); - goto out_unlock; - } + /* Get the offsets within the page that we want to zero */ + zero_from = abs_from & (PAGE_CACHE_SIZE - 1); + zero_to = abs_to & (PAGE_CACHE_SIZE - 1); + if (!zero_to) + zero_to = PAGE_CACHE_SIZE; - if (ocfs2_should_order_data(inode)) { - handle = ocfs2_start_walk_page_trans(inode, page, offset, - offset); - if (IS_ERR(handle)) { - ret = PTR_ERR(handle); - handle = NULL; + /* We know that zero_from is block aligned */ + for (block_start = zero_from; block_start < zero_to; + block_start = block_end) { + block_end = block_start + (1 << inode->i_blkbits); + + /* + * block_start is block-aligned. Bump it by one to + * force ocfs2_{prepare,commit}_write() to zero the + * whole block. + */ + ret = ocfs2_prepare_write_nolock(inode, page, + block_start + 1, + block_start + 1); + if (ret < 0) { + mlog_errno(ret); goto out_unlock; } - } - /* must not update i_size! */ - ret = block_commit_write(page, offset, offset); - if (ret < 0) - mlog_errno(ret); - else - ret = 0; + if (!handle) { + handle = ocfs2_zero_start_ordered_transaction(inode); + if (IS_ERR(handle)) { + ret = PTR_ERR(handle); + handle = NULL; + break; + } + } + + /* must not update i_size! */ + ret = block_commit_write(page, block_start + 1, + block_start + 1); + if (ret < 0) + mlog_errno(ret); + else + ret = 0; + } if (handle) ocfs2_commit_trans(OCFS2_SB(inode->i_sb), handle); + out_unlock: unlock_page(page); page_cache_release(page); @@ -790,18 +837,21 @@ static int ocfs2_zero_extend(struct inode *inode, u64 zero_to_size) { int ret = 0; - u64 start_off; + u64 start_off, next_off; struct super_block *sb = inode->i_sb; start_off = ocfs2_align_bytes_to_blocks(sb, i_size_read(inode)); while (start_off < zero_to_size) { - ret = ocfs2_write_zero_page(inode, start_off); + next_off = (start_off & PAGE_CACHE_MASK) + PAGE_CACHE_SIZE; + if (next_off > zero_to_size) + next_off = zero_to_size; + ret = ocfs2_write_zero_page(inode, start_off, next_off); if (ret < 0) { mlog_errno(ret); goto out; } - start_off += sb->s_blocksize; + start_off = next_off; /* * Very large extends have the potential to lock up -- cgit v1.2.3 From 5693486bad2bc2ac585a2c24f7e2f3964b478df9 Mon Sep 17 00:00:00 2001 From: Joel Becker Date: Thu, 1 Jul 2010 15:13:31 -0700 Subject: ocfs2: Zero the tail cluster when extending past i_size. ocfs2's allocation unit is the cluster. This can be larger than a block or even a memory page. This means that a file may have many blocks in its last extent that are beyond the block containing i_size. There also may be more unwritten extents after that. When ocfs2 grows a file, it zeros the entire cluster in order to ensure future i_size growth will see cleared blocks. Unfortunately, block_write_full_page() drops the pages past i_size. This means that ocfs2 is actually leaking garbage data into the tail end of that last cluster. This is a bug. We adjust ocfs2_write_begin_nolock() and ocfs2_extend_file() to detect when a write or truncate is past i_size. They will use ocfs2_zero_extend() to ensure the data is properly zeroed. Older versions of ocfs2_zero_extend() simply zeroed every block between i_size and the zeroing position. This presumes three things: 1) There is allocation for all of these blocks. 2) The extents are not unwritten. 3) The extents are not refcounted. (1) and (2) hold true for non-sparse filesystems, which used to be the only users of ocfs2_zero_extend(). (3) is another bug. Since we're now using ocfs2_zero_extend() for sparse filesystems as well, we teach ocfs2_zero_extend() to check every extent between i_size and the zeroing position. If the extent is unwritten, it is ignored. If it is refcounted, it is CoWed. Then it is zeroed. Signed-off-by: Joel Becker Cc: stable@kernel.org --- fs/ocfs2/aops.c | 42 ++++++---- fs/ocfs2/file.c | 201 +++++++++++++++++++++++++++++++++++++++--------- fs/ocfs2/file.h | 6 +- fs/ocfs2/quota_global.c | 2 +- fs/ocfs2/quota_local.c | 4 +- fs/ocfs2/refcounttree.c | 6 ++ 6 files changed, 207 insertions(+), 54 deletions(-) (limited to 'fs') diff --git a/fs/ocfs2/aops.c b/fs/ocfs2/aops.c index 9a5c931439bd..742893ea7390 100644 --- a/fs/ocfs2/aops.c +++ b/fs/ocfs2/aops.c @@ -196,15 +196,14 @@ int ocfs2_get_block(struct inode *inode, sector_t iblock, dump_stack(); goto bail; } - - past_eof = ocfs2_blocks_for_bytes(inode->i_sb, i_size_read(inode)); - mlog(0, "Inode %lu, past_eof = %llu\n", inode->i_ino, - (unsigned long long)past_eof); - - if (create && (iblock >= past_eof)) - set_buffer_new(bh_result); } + past_eof = ocfs2_blocks_for_bytes(inode->i_sb, i_size_read(inode)); + mlog(0, "Inode %lu, past_eof = %llu\n", inode->i_ino, + (unsigned long long)past_eof); + if (create && (iblock >= past_eof)) + set_buffer_new(bh_result); + bail: if (err < 0) err = -EIO; @@ -1590,21 +1589,20 @@ out: * write path can treat it as an non-allocating write, which has no * special case code for sparse/nonsparse files. */ -static int ocfs2_expand_nonsparse_inode(struct inode *inode, loff_t pos, - unsigned len, +static int ocfs2_expand_nonsparse_inode(struct inode *inode, + struct buffer_head *di_bh, + loff_t pos, unsigned len, struct ocfs2_write_ctxt *wc) { int ret; - struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); loff_t newsize = pos + len; - if (ocfs2_sparse_alloc(osb)) - return 0; + BUG_ON(ocfs2_sparse_alloc(OCFS2_SB(inode->i_sb))); if (newsize <= i_size_read(inode)) return 0; - ret = ocfs2_extend_no_holes(inode, newsize, pos); + ret = ocfs2_extend_no_holes(inode, di_bh, newsize, pos); if (ret) mlog_errno(ret); @@ -1614,6 +1612,18 @@ static int ocfs2_expand_nonsparse_inode(struct inode *inode, loff_t pos, return ret; } +static int ocfs2_zero_tail(struct inode *inode, struct buffer_head *di_bh, + loff_t pos) +{ + int ret = 0; + + BUG_ON(!ocfs2_sparse_alloc(OCFS2_SB(inode->i_sb))); + if (pos > i_size_read(inode)) + ret = ocfs2_zero_extend(inode, di_bh, pos); + + return ret; +} + int ocfs2_write_begin_nolock(struct address_space *mapping, loff_t pos, unsigned len, unsigned flags, struct page **pagep, void **fsdata, @@ -1649,7 +1659,11 @@ int ocfs2_write_begin_nolock(struct address_space *mapping, } } - ret = ocfs2_expand_nonsparse_inode(inode, pos, len, wc); + if (ocfs2_sparse_alloc(osb)) + ret = ocfs2_zero_tail(inode, di_bh, pos); + else + ret = ocfs2_expand_nonsparse_inode(inode, di_bh, pos, len, + wc); if (ret) { mlog_errno(ret); goto out; diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index 4cfc976a9067..ac15911b31c4 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c @@ -787,6 +787,11 @@ static int ocfs2_write_zero_page(struct inode *inode, u64 abs_from, if (!zero_to) zero_to = PAGE_CACHE_SIZE; + mlog(0, + "abs_from = %llu, abs_to = %llu, index = %lu, zero_from = %u, zero_to = %u\n", + (unsigned long long)abs_from, (unsigned long long)abs_to, + index, zero_from, zero_to); + /* We know that zero_from is block aligned */ for (block_start = zero_from; block_start < zero_to; block_start = block_end) { @@ -833,25 +838,114 @@ out: return ret; } -static int ocfs2_zero_extend(struct inode *inode, - u64 zero_to_size) +/* + * Find the next range to zero. We do this in terms of bytes because + * that's what ocfs2_zero_extend() wants, and it is dealing with the + * pagecache. We may return multiple extents. + * + * zero_start and zero_end are ocfs2_zero_extend()s current idea of what + * needs to be zeroed. range_start and range_end return the next zeroing + * range. A subsequent call should pass the previous range_end as its + * zero_start. If range_end is 0, there's nothing to do. + * + * Unwritten extents are skipped over. Refcounted extents are CoWd. + */ +static int ocfs2_zero_extend_get_range(struct inode *inode, + struct buffer_head *di_bh, + u64 zero_start, u64 zero_end, + u64 *range_start, u64 *range_end) { - int ret = 0; - u64 start_off, next_off; - struct super_block *sb = inode->i_sb; + int rc = 0, needs_cow = 0; + u32 p_cpos, zero_clusters = 0; + u32 zero_cpos = + zero_start >> OCFS2_SB(inode->i_sb)->s_clustersize_bits; + u32 last_cpos = ocfs2_clusters_for_bytes(inode->i_sb, zero_end); + unsigned int num_clusters = 0; + unsigned int ext_flags = 0; - start_off = ocfs2_align_bytes_to_blocks(sb, i_size_read(inode)); - while (start_off < zero_to_size) { - next_off = (start_off & PAGE_CACHE_MASK) + PAGE_CACHE_SIZE; - if (next_off > zero_to_size) - next_off = zero_to_size; - ret = ocfs2_write_zero_page(inode, start_off, next_off); - if (ret < 0) { - mlog_errno(ret); + while (zero_cpos < last_cpos) { + rc = ocfs2_get_clusters(inode, zero_cpos, &p_cpos, + &num_clusters, &ext_flags); + if (rc) { + mlog_errno(rc); + goto out; + } + + if (p_cpos && !(ext_flags & OCFS2_EXT_UNWRITTEN)) { + zero_clusters = num_clusters; + if (ext_flags & OCFS2_EXT_REFCOUNTED) + needs_cow = 1; + break; + } + + zero_cpos += num_clusters; + } + if (!zero_clusters) { + *range_end = 0; + goto out; + } + + while ((zero_cpos + zero_clusters) < last_cpos) { + rc = ocfs2_get_clusters(inode, zero_cpos + zero_clusters, + &p_cpos, &num_clusters, + &ext_flags); + if (rc) { + mlog_errno(rc); + goto out; + } + + if (!p_cpos || (ext_flags & OCFS2_EXT_UNWRITTEN)) + break; + if (ext_flags & OCFS2_EXT_REFCOUNTED) + needs_cow = 1; + zero_clusters += num_clusters; + } + if ((zero_cpos + zero_clusters) > last_cpos) + zero_clusters = last_cpos - zero_cpos; + + if (needs_cow) { + rc = ocfs2_refcount_cow(inode, di_bh, zero_cpos, zero_clusters, + UINT_MAX); + if (rc) { + mlog_errno(rc); goto out; } + } - start_off = next_off; + *range_start = ocfs2_clusters_to_bytes(inode->i_sb, zero_cpos); + *range_end = ocfs2_clusters_to_bytes(inode->i_sb, + zero_cpos + zero_clusters); + +out: + return rc; +} + +/* + * Zero one range returned from ocfs2_zero_extend_get_range(). The caller + * has made sure that the entire range needs zeroing. + */ +static int ocfs2_zero_extend_range(struct inode *inode, u64 range_start, + u64 range_end) +{ + int rc = 0; + u64 next_pos; + u64 zero_pos = range_start; + + mlog(0, "range_start = %llu, range_end = %llu\n", + (unsigned long long)range_start, + (unsigned long long)range_end); + BUG_ON(range_start >= range_end); + + while (zero_pos < range_end) { + next_pos = (zero_pos & PAGE_CACHE_MASK) + PAGE_CACHE_SIZE; + if (next_pos > range_end) + next_pos = range_end; + rc = ocfs2_write_zero_page(inode, zero_pos, next_pos); + if (rc < 0) { + mlog_errno(rc); + break; + } + zero_pos = next_pos; /* * Very large extends have the potential to lock up @@ -860,16 +954,63 @@ static int ocfs2_zero_extend(struct inode *inode, cond_resched(); } -out: + return rc; +} + +int ocfs2_zero_extend(struct inode *inode, struct buffer_head *di_bh, + loff_t zero_to_size) +{ + int ret = 0; + u64 zero_start, range_start = 0, range_end = 0; + struct super_block *sb = inode->i_sb; + + zero_start = ocfs2_align_bytes_to_blocks(sb, i_size_read(inode)); + mlog(0, "zero_start %llu for i_size %llu\n", + (unsigned long long)zero_start, + (unsigned long long)i_size_read(inode)); + while (zero_start < zero_to_size) { + ret = ocfs2_zero_extend_get_range(inode, di_bh, zero_start, + zero_to_size, + &range_start, + &range_end); + if (ret) { + mlog_errno(ret); + break; + } + if (!range_end) + break; + /* Trim the ends */ + if (range_start < zero_start) + range_start = zero_start; + if (range_end > zero_to_size) + range_end = zero_to_size; + + ret = ocfs2_zero_extend_range(inode, range_start, + range_end); + if (ret) { + mlog_errno(ret); + break; + } + zero_start = range_end; + } + return ret; } -int ocfs2_extend_no_holes(struct inode *inode, u64 new_i_size, u64 zero_to) +int ocfs2_extend_no_holes(struct inode *inode, struct buffer_head *di_bh, + u64 new_i_size, u64 zero_to) { int ret; u32 clusters_to_add; struct ocfs2_inode_info *oi = OCFS2_I(inode); + /* + * Only quota files call this without a bh, and they can't be + * refcounted. + */ + BUG_ON(!di_bh && (oi->ip_dyn_features & OCFS2_HAS_REFCOUNT_FL)); + BUG_ON(!di_bh && !(oi->ip_flags & OCFS2_INODE_SYSTEM_FILE)); + clusters_to_add = ocfs2_clusters_for_bytes(inode->i_sb, new_i_size); if (clusters_to_add < oi->ip_clusters) clusters_to_add = 0; @@ -890,7 +1031,7 @@ int ocfs2_extend_no_holes(struct inode *inode, u64 new_i_size, u64 zero_to) * still need to zero the area between the old i_size and the * new i_size. */ - ret = ocfs2_zero_extend(inode, zero_to); + ret = ocfs2_zero_extend(inode, di_bh, zero_to); if (ret < 0) mlog_errno(ret); @@ -912,27 +1053,15 @@ static int ocfs2_extend_file(struct inode *inode, goto out; if (i_size_read(inode) == new_i_size) - goto out; + goto out; BUG_ON(new_i_size < i_size_read(inode)); - /* - * Fall through for converting inline data, even if the fs - * supports sparse files. - * - * The check for inline data here is legal - nobody can add - * the feature since we have i_mutex. We must check it again - * after acquiring ip_alloc_sem though, as paths like mmap - * might have raced us to converting the inode to extents. - */ - if (!(oi->ip_dyn_features & OCFS2_INLINE_DATA_FL) - && ocfs2_sparse_alloc(OCFS2_SB(inode->i_sb))) - goto out_update_size; - /* * The alloc sem blocks people in read/write from reading our * allocation until we're done changing it. We depend on * i_mutex to block other extend/truncate calls while we're - * here. + * here. We even have to hold it for sparse files because there + * might be some tail zeroing. */ down_write(&oi->ip_alloc_sem); @@ -949,14 +1078,16 @@ static int ocfs2_extend_file(struct inode *inode, ret = ocfs2_convert_inline_data_to_extents(inode, di_bh); if (ret) { up_write(&oi->ip_alloc_sem); - mlog_errno(ret); goto out; } } - if (!ocfs2_sparse_alloc(OCFS2_SB(inode->i_sb))) - ret = ocfs2_extend_no_holes(inode, new_i_size, new_i_size); + if (ocfs2_sparse_alloc(OCFS2_SB(inode->i_sb))) + ret = ocfs2_zero_extend(inode, di_bh, new_i_size); + else + ret = ocfs2_extend_no_holes(inode, di_bh, new_i_size, + new_i_size); up_write(&oi->ip_alloc_sem); diff --git a/fs/ocfs2/file.h b/fs/ocfs2/file.h index d66cf4f7c70e..97bf761c9e7c 100644 --- a/fs/ocfs2/file.h +++ b/fs/ocfs2/file.h @@ -54,8 +54,10 @@ int ocfs2_add_inode_data(struct ocfs2_super *osb, int ocfs2_simple_size_update(struct inode *inode, struct buffer_head *di_bh, u64 new_i_size); -int ocfs2_extend_no_holes(struct inode *inode, u64 new_i_size, - u64 zero_to); +int ocfs2_extend_no_holes(struct inode *inode, struct buffer_head *di_bh, + u64 new_i_size, u64 zero_to); +int ocfs2_zero_extend(struct inode *inode, struct buffer_head *di_bh, + loff_t zero_to); int ocfs2_setattr(struct dentry *dentry, struct iattr *attr); int ocfs2_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat); diff --git a/fs/ocfs2/quota_global.c b/fs/ocfs2/quota_global.c index 2bb35fe00511..4607923eb24c 100644 --- a/fs/ocfs2/quota_global.c +++ b/fs/ocfs2/quota_global.c @@ -775,7 +775,7 @@ static int ocfs2_acquire_dquot(struct dquot *dquot) * locking allocators ranks above a transaction start */ WARN_ON(journal_current_handle()); - status = ocfs2_extend_no_holes(gqinode, + status = ocfs2_extend_no_holes(gqinode, NULL, gqinode->i_size + (need_alloc << sb->s_blocksize_bits), gqinode->i_size); if (status < 0) diff --git a/fs/ocfs2/quota_local.c b/fs/ocfs2/quota_local.c index 8bd70d4d184d..dc78764ccc4c 100644 --- a/fs/ocfs2/quota_local.c +++ b/fs/ocfs2/quota_local.c @@ -971,7 +971,7 @@ static struct ocfs2_quota_chunk *ocfs2_local_quota_add_chunk( u64 p_blkno; /* We are protected by dqio_sem so no locking needed */ - status = ocfs2_extend_no_holes(lqinode, + status = ocfs2_extend_no_holes(lqinode, NULL, lqinode->i_size + 2 * sb->s_blocksize, lqinode->i_size); if (status < 0) { @@ -1114,7 +1114,7 @@ static struct ocfs2_quota_chunk *ocfs2_extend_local_quota_file( return ocfs2_local_quota_add_chunk(sb, type, offset); /* We are protected by dqio_sem so no locking needed */ - status = ocfs2_extend_no_holes(lqinode, + status = ocfs2_extend_no_holes(lqinode, NULL, lqinode->i_size + sb->s_blocksize, lqinode->i_size); if (status < 0) { diff --git a/fs/ocfs2/refcounttree.c b/fs/ocfs2/refcounttree.c index 4793f36f6518..32949df10694 100644 --- a/fs/ocfs2/refcounttree.c +++ b/fs/ocfs2/refcounttree.c @@ -4166,6 +4166,12 @@ static int __ocfs2_reflink(struct dentry *old_dentry, struct inode *inode = old_dentry->d_inode; struct buffer_head *new_bh = NULL; + if (OCFS2_I(inode)->ip_flags & OCFS2_INODE_SYSTEM_FILE) { + ret = -EINVAL; + mlog_errno(ret); + goto out; + } + ret = filemap_fdatawrite(inode->i_mapping); if (ret) { mlog_errno(ret); -- cgit v1.2.3 From d06dbaf6c2c7187938f3f6745d9e4938a2d0ec47 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Thu, 8 Jul 2010 10:47:16 -0700 Subject: ceph: fix printing of ipv6 addrs The buffer was too small. Make it bigger, use snprintf(), put brackets around the ipv6 address to avoid mixing it up with the :port, and use the ever-so-handy %pI[46] formats. Signed-off-by: Sage Weil --- fs/ceph/messenger.c | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) (limited to 'fs') diff --git a/fs/ceph/messenger.c b/fs/ceph/messenger.c index 9692d08e2f88..e8c5a2d0e88f 100644 --- a/fs/ceph/messenger.c +++ b/fs/ceph/messenger.c @@ -43,7 +43,8 @@ static void ceph_fault(struct ceph_connection *con); * nicely render a sockaddr as a string. */ #define MAX_ADDR_STR 20 -static char addr_str[MAX_ADDR_STR][40]; +#define MAX_ADDR_STR_LEN 60 +static char addr_str[MAX_ADDR_STR][MAX_ADDR_STR_LEN]; static DEFINE_SPINLOCK(addr_str_lock); static int last_addr_str; @@ -52,7 +53,6 @@ const char *pr_addr(const struct sockaddr_storage *ss) int i; char *s; struct sockaddr_in *in4 = (void *)ss; - unsigned char *quad = (void *)&in4->sin_addr.s_addr; struct sockaddr_in6 *in6 = (void *)ss; spin_lock(&addr_str_lock); @@ -64,25 +64,13 @@ const char *pr_addr(const struct sockaddr_storage *ss) switch (ss->ss_family) { case AF_INET: - sprintf(s, "%u.%u.%u.%u:%u", - (unsigned int)quad[0], - (unsigned int)quad[1], - (unsigned int)quad[2], - (unsigned int)quad[3], - (unsigned int)ntohs(in4->sin_port)); + snprintf(s, MAX_ADDR_STR_LEN, "%pI4:%u", &in4->sin_addr, + (unsigned int)ntohs(in4->sin_port)); break; case AF_INET6: - sprintf(s, "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x:%u", - in6->sin6_addr.s6_addr16[0], - in6->sin6_addr.s6_addr16[1], - in6->sin6_addr.s6_addr16[2], - in6->sin6_addr.s6_addr16[3], - in6->sin6_addr.s6_addr16[4], - in6->sin6_addr.s6_addr16[5], - in6->sin6_addr.s6_addr16[6], - in6->sin6_addr.s6_addr16[7], - (unsigned int)ntohs(in6->sin6_port)); + snprintf(s, MAX_ADDR_STR_LEN, "[%pI6c]:%u", &in6->sin6_addr, + (unsigned int)ntohs(in6->sin6_port)); break; default: -- cgit v1.2.3 From 39139f64e14684cf2370770deb79d929d27cfd9b Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Thu, 8 Jul 2010 09:54:52 -0700 Subject: ceph: fix parsing of ipv6 addresses Check for brackets around the ipv6 address to avoid ambiguity with the port number. Signed-off-by: Sage Weil --- fs/ceph/messenger.c | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) (limited to 'fs') diff --git a/fs/ceph/messenger.c b/fs/ceph/messenger.c index e8c5a2d0e88f..3ddef1556457 100644 --- a/fs/ceph/messenger.c +++ b/fs/ceph/messenger.c @@ -997,19 +997,32 @@ int ceph_parse_ips(const char *c, const char *end, struct sockaddr_in *in4 = (void *)ss; struct sockaddr_in6 *in6 = (void *)ss; int port; + char delim = ','; + + if (*p == '[') { + delim = ']'; + p++; + } memset(ss, 0, sizeof(*ss)); if (in4_pton(p, end - p, (u8 *)&in4->sin_addr.s_addr, - ',', &ipend)) { + delim, &ipend)) ss->ss_family = AF_INET; - } else if (in6_pton(p, end - p, (u8 *)&in6->sin6_addr.s6_addr, - ',', &ipend)) { + else if (in6_pton(p, end - p, (u8 *)&in6->sin6_addr.s6_addr, + delim, &ipend)) ss->ss_family = AF_INET6; - } else { + else goto bad; - } p = ipend; + if (delim == ']') { + if (*p != ']') { + dout("missing matching ']'\n"); + goto bad; + } + p++; + } + /* port? */ if (p < end && *p == ':') { port = 0; @@ -1043,7 +1056,7 @@ int ceph_parse_ips(const char *c, const char *end, return 0; bad: - pr_err("parse_ips bad ip '%s'\n", c); + pr_err("parse_ips bad ip '%.*s'\n", (int)(end - c), c); return -EINVAL; } -- cgit v1.2.3 From f91d3471ccf1ca9a795f46c94b1ded8dd219940c Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Thu, 1 Jul 2010 15:18:31 -0700 Subject: ceph: fix creation of ipv6 sockets Use the address family from the peer address instead of assuming IPv4. Signed-off-by: Sage Weil --- fs/ceph/messenger.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/ceph/messenger.c b/fs/ceph/messenger.c index 3ddef1556457..15167b2daa55 100644 --- a/fs/ceph/messenger.c +++ b/fs/ceph/messenger.c @@ -203,12 +203,13 @@ static void set_sock_callbacks(struct socket *sock, */ static struct socket *ceph_tcp_connect(struct ceph_connection *con) { - struct sockaddr *paddr = (struct sockaddr *)&con->peer_addr.in_addr; + struct sockaddr_storage *paddr = &con->peer_addr.in_addr; struct socket *sock; int ret; BUG_ON(con->sock); - ret = sock_create_kern(AF_INET, SOCK_STREAM, IPPROTO_TCP, &sock); + ret = sock_create_kern(con->peer_addr.in_addr.ss_family, SOCK_STREAM, + IPPROTO_TCP, &sock); if (ret) return ERR_PTR(ret); con->sock = sock; @@ -222,7 +223,8 @@ static struct socket *ceph_tcp_connect(struct ceph_connection *con) dout("connect %s\n", pr_addr(&con->peer_addr.in_addr)); - ret = sock->ops->connect(sock, paddr, sizeof(*paddr), O_NONBLOCK); + ret = sock->ops->connect(sock, (struct sockaddr *)paddr, sizeof(*paddr), + O_NONBLOCK); if (ret == -EINPROGRESS) { dout("connect %s EINPROGRESS sk_state = %u\n", pr_addr(&con->peer_addr.in_addr), -- cgit v1.2.3 From 5f202bd5ca64132cdd7f186656bc0221f257733d Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Sun, 4 Jul 2010 00:02:25 +0200 Subject: do_coredump: Do not take BKL core_pattern is not actually protected and hasn't been ever since we introduced procfs support for sysctl -- a _long_ time. Don't take it here either. Also nothing inside do_coredump appears to require bkl protection. Signed-off-by: Arnd Bergmann [ remove smp_lock.h headers ] Signed-off-by: Frederic Weisbecker --- fs/exec.c | 7 ------- 1 file changed, 7 deletions(-) (limited to 'fs') diff --git a/fs/exec.c b/fs/exec.c index e19de6a80339..2619c767c089 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -28,7 +28,6 @@ #include #include #include -#include #include #include #include @@ -1891,13 +1890,7 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs) */ clear_thread_flag(TIF_SIGPENDING); - /* - * lock_kernel() because format_corename() is controlled by sysctl, which - * uses lock_kernel() - */ - lock_kernel(); ispipe = format_corename(corename, signr); - unlock_kernel(); if (ispipe) { int dump_count; -- cgit v1.2.3 From f068272cb2f134a194b93e94a8e0672bfce48cd8 Mon Sep 17 00:00:00 2001 From: Bob Copeland Date: Sat, 6 Sep 2008 17:51:53 -0400 Subject: omfs: check bounds on block numbers before passing to sb_bread In case of filesystem corruption, passing unchecked block numbers into sb_bread can result in an infinite loop in __getblk(). Introduce a wrapper function omfs_sbread() to check the block numbers and to also perform the clus_to_blk() scaling. Signed-off-by: Bob Copeland --- fs/omfs/dir.c | 22 ++++++++-------------- fs/omfs/file.c | 8 ++++---- fs/omfs/inode.c | 27 ++++++++++++++++----------- fs/omfs/omfs.h | 1 + 4 files changed, 29 insertions(+), 29 deletions(-) (limited to 'fs') diff --git a/fs/omfs/dir.c b/fs/omfs/dir.c index b42d62419034..393f3f659da7 100644 --- a/fs/omfs/dir.c +++ b/fs/omfs/dir.c @@ -25,11 +25,10 @@ static struct buffer_head *omfs_get_bucket(struct inode *dir, const char *name, int namelen, int *ofs) { int nbuckets = (dir->i_size - OMFS_DIR_START)/8; - int block = clus_to_blk(OMFS_SB(dir->i_sb), dir->i_ino); int bucket = omfs_hash(name, namelen, nbuckets); *ofs = OMFS_DIR_START + bucket * 8; - return sb_bread(dir->i_sb, block); + return omfs_bread(dir->i_sb, dir->i_ino); } static struct buffer_head *omfs_scan_list(struct inode *dir, u64 block, @@ -42,8 +41,7 @@ static struct buffer_head *omfs_scan_list(struct inode *dir, u64 block, *prev_block = ~0; while (block != ~0) { - bh = sb_bread(dir->i_sb, - clus_to_blk(OMFS_SB(dir->i_sb), block)); + bh = omfs_bread(dir->i_sb, block); if (!bh) { err = -EIO; goto err; @@ -86,11 +84,10 @@ static struct buffer_head *omfs_find_entry(struct inode *dir, int omfs_make_empty(struct inode *inode, struct super_block *sb) { struct omfs_sb_info *sbi = OMFS_SB(sb); - int block = clus_to_blk(sbi, inode->i_ino); struct buffer_head *bh; struct omfs_inode *oi; - bh = sb_bread(sb, block); + bh = omfs_bread(sb, inode->i_ino); if (!bh) return -ENOMEM; @@ -134,7 +131,7 @@ static int omfs_add_link(struct dentry *dentry, struct inode *inode) brelse(bh); /* now set the sibling and parent pointers on the new inode */ - bh = sb_bread(dir->i_sb, clus_to_blk(OMFS_SB(dir->i_sb), inode->i_ino)); + bh = omfs_bread(dir->i_sb, inode->i_ino); if (!bh) goto out; @@ -190,8 +187,7 @@ static int omfs_delete_entry(struct dentry *dentry) if (prev != ~0) { /* found in middle of list, get list ptr */ brelse(bh); - bh = sb_bread(dir->i_sb, - clus_to_blk(OMFS_SB(dir->i_sb), prev)); + bh = omfs_bread(dir->i_sb, prev); if (!bh) goto out; @@ -224,8 +220,7 @@ static int omfs_dir_is_empty(struct inode *inode) u64 *ptr; int i; - bh = sb_bread(inode->i_sb, clus_to_blk(OMFS_SB(inode->i_sb), - inode->i_ino)); + bh = omfs_bread(inode->i_sb, inode->i_ino); if (!bh) return 0; @@ -353,8 +348,7 @@ static int omfs_fill_chain(struct file *filp, void *dirent, filldir_t filldir, /* follow chain in this bucket */ while (fsblock != ~0) { - bh = sb_bread(dir->i_sb, clus_to_blk(OMFS_SB(dir->i_sb), - fsblock)); + bh = omfs_bread(dir->i_sb, fsblock); if (!bh) goto out; @@ -466,7 +460,7 @@ static int omfs_readdir(struct file *filp, void *dirent, filldir_t filldir) hchain = (filp->f_pos >> 20) - 1; hindex = filp->f_pos & 0xfffff; - bh = sb_bread(dir->i_sb, clus_to_blk(OMFS_SB(dir->i_sb), dir->i_ino)); + bh = omfs_bread(dir->i_sb, dir->i_ino); if (!bh) goto out; diff --git a/fs/omfs/file.c b/fs/omfs/file.c index 6e7a3291bbe8..76bc21b91a8a 100644 --- a/fs/omfs/file.c +++ b/fs/omfs/file.c @@ -50,7 +50,7 @@ int omfs_shrink_inode(struct inode *inode) if (inode->i_size != 0) goto out; - bh = sb_bread(inode->i_sb, clus_to_blk(sbi, next)); + bh = omfs_bread(inode->i_sb, next); if (!bh) goto out; @@ -90,7 +90,7 @@ int omfs_shrink_inode(struct inode *inode) if (next == ~0) break; - bh = sb_bread(inode->i_sb, clus_to_blk(sbi, next)); + bh = omfs_bread(inode->i_sb, next); if (!bh) goto out; oe = (struct omfs_extent *) (&bh->b_data[OMFS_EXTENT_CONT]); @@ -232,7 +232,7 @@ static int omfs_get_block(struct inode *inode, sector_t block, int remain; ret = -EIO; - bh = sb_bread(inode->i_sb, clus_to_blk(sbi, inode->i_ino)); + bh = omfs_bread(inode->i_sb, inode->i_ino); if (!bh) goto out; @@ -265,7 +265,7 @@ static int omfs_get_block(struct inode *inode, sector_t block, break; brelse(bh); - bh = sb_bread(inode->i_sb, clus_to_blk(sbi, next)); + bh = omfs_bread(inode->i_sb, next); if (!bh) goto out; oe = (struct omfs_extent *) (&bh->b_data[OMFS_EXTENT_CONT]); diff --git a/fs/omfs/inode.c b/fs/omfs/inode.c index b5d6380e03fb..bd4bf753a63b 100644 --- a/fs/omfs/inode.c +++ b/fs/omfs/inode.c @@ -19,6 +19,15 @@ MODULE_AUTHOR("Bob Copeland "); MODULE_DESCRIPTION("OMFS (ReplayTV/Karma) Filesystem for Linux"); MODULE_LICENSE("GPL"); +struct buffer_head *omfs_bread(struct super_block *sb, sector_t block) +{ + struct omfs_sb_info *sbi = OMFS_SB(sb); + if (block >= sbi->s_num_blocks) + return NULL; + + return sb_bread(sb, clus_to_blk(sbi, block)); +} + struct inode *omfs_new_inode(struct inode *dir, int mode) { struct inode *inode; @@ -93,15 +102,13 @@ static int __omfs_write_inode(struct inode *inode, int wait) struct omfs_inode *oi; struct omfs_sb_info *sbi = OMFS_SB(inode->i_sb); struct buffer_head *bh, *bh2; - unsigned int block; u64 ctime; int i; int ret = -EIO; int sync_failed = 0; /* get current inode since we may have written sibling ptrs etc. */ - block = clus_to_blk(sbi, inode->i_ino); - bh = sb_bread(inode->i_sb, block); + bh = omfs_bread(inode->i_sb, inode->i_ino); if (!bh) goto out; @@ -140,8 +147,7 @@ static int __omfs_write_inode(struct inode *inode, int wait) /* if mirroring writes, copy to next fsblock */ for (i = 1; i < sbi->s_mirrors; i++) { - bh2 = sb_bread(inode->i_sb, block + i * - (sbi->s_blocksize / sbi->s_sys_blocksize)); + bh2 = omfs_bread(inode->i_sb, inode->i_ino + i); if (!bh2) goto out_brelse; @@ -193,7 +199,6 @@ struct inode *omfs_iget(struct super_block *sb, ino_t ino) struct omfs_sb_info *sbi = OMFS_SB(sb); struct omfs_inode *oi; struct buffer_head *bh; - unsigned int block; u64 ctime; unsigned long nsecs; struct inode *inode; @@ -204,8 +209,7 @@ struct inode *omfs_iget(struct super_block *sb, ino_t ino) if (!(inode->i_state & I_NEW)) return inode; - block = clus_to_blk(sbi, ino); - bh = sb_bread(inode->i_sb, block); + bh = omfs_bread(inode->i_sb, ino); if (!bh) goto iget_failed; @@ -319,6 +323,9 @@ static int omfs_get_imap(struct super_block *sb) goto nomem; block = clus_to_blk(sbi, sbi->s_bitmap_ino); + if (block >= sbi->s_num_blocks) + goto nomem; + ptr = sbi->s_imap; for (count = bitmap_size; count > 0; count -= sb->s_blocksize) { bh = sb_bread(sb, block++); @@ -417,7 +424,6 @@ static int omfs_fill_super(struct super_block *sb, void *data, int silent) struct omfs_root_block *omfs_rb; struct omfs_sb_info *sbi; struct inode *root; - sector_t start; int ret = -EINVAL; save_mount_options(sb, (char *) data); @@ -486,8 +492,7 @@ static int omfs_fill_super(struct super_block *sb, void *data, int silent) sbi->s_block_shift = get_bitmask_order(sbi->s_blocksize) - get_bitmask_order(sbi->s_sys_blocksize); - start = clus_to_blk(sbi, be64_to_cpu(omfs_sb->s_root_block)); - bh2 = sb_bread(sb, start); + bh2 = omfs_bread(sb, be64_to_cpu(omfs_sb->s_root_block)); if (!bh2) goto out_brelse_bh; diff --git a/fs/omfs/omfs.h b/fs/omfs/omfs.h index ebe2fdbe535e..7d414fef501a 100644 --- a/fs/omfs/omfs.h +++ b/fs/omfs/omfs.h @@ -58,6 +58,7 @@ extern void omfs_make_empty_table(struct buffer_head *bh, int offset); extern int omfs_shrink_inode(struct inode *inode); /* inode.c */ +extern struct buffer_head *omfs_bread(struct super_block *sb, sector_t block); extern struct inode *omfs_iget(struct super_block *sb, ino_t inode); extern struct inode *omfs_new_inode(struct inode *dir, int mode); extern int omfs_reserve_block(struct super_block *sb, sector_t block); -- cgit v1.2.3 From 9442e54f433eff9b6fbd0836611df4c1919df370 Mon Sep 17 00:00:00 2001 From: Bob Copeland Date: Thu, 14 Aug 2008 18:43:59 -0400 Subject: omfs: refuse to mount if bitmap pointer is obviously wrong If the free space bitmap pointer is corrupted such that it lies outside of the number of blocks in the filesystem, print a message and fail the mount so the user can fix it offline. Signed-off-by: Bob Copeland --- fs/omfs/inode.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'fs') diff --git a/fs/omfs/inode.c b/fs/omfs/inode.c index bd4bf753a63b..0af5d0af9f32 100644 --- a/fs/omfs/inode.c +++ b/fs/omfs/inode.c @@ -509,6 +509,15 @@ static int omfs_fill_super(struct super_block *sb, void *data, int silent) goto out_brelse_bh2; } + if (sbi->s_bitmap_ino != ~0ULL && + sbi->s_bitmap_ino > sbi->s_num_blocks) { + printk(KERN_ERR "omfs: free space bitmap location is corrupt " + "(%llx, total blocks %llx)\n", + (unsigned long long) sbi->s_bitmap_ino, + (unsigned long long) sbi->s_num_blocks); + goto out_brelse_bh2; + } + ret = omfs_get_imap(sb); if (ret) goto out_brelse_bh2; -- cgit v1.2.3 From 8800a044c71a128633cf3febaf4780531a991334 Mon Sep 17 00:00:00 2001 From: Bob Copeland Date: Tue, 6 Jul 2010 11:16:46 -0400 Subject: omfs: sanity check cluster size A corrupt filesystem could have a bad cluster size; this could result in the filesystem allocating too much space for files if too large, or getting stuck in omfs_allocate_block if too small. The proper range is 1-8 blocks. Reported-by: Eric Sesterhenn Signed-off-by: Bob Copeland --- fs/omfs/inode.c | 6 ++++++ fs/omfs/omfs_fs.h | 1 + 2 files changed, 7 insertions(+) (limited to 'fs') diff --git a/fs/omfs/inode.c b/fs/omfs/inode.c index 0af5d0af9f32..579d33fedddd 100644 --- a/fs/omfs/inode.c +++ b/fs/omfs/inode.c @@ -517,6 +517,12 @@ static int omfs_fill_super(struct super_block *sb, void *data, int silent) (unsigned long long) sbi->s_num_blocks); goto out_brelse_bh2; } + if (sbi->s_clustersize < 1 || + sbi->s_clustersize > OMFS_MAX_CLUSTER_SIZE) { + printk(KERN_ERR "omfs: cluster size out of range (%d)", + sbi->s_clustersize); + goto out_brelse_bh2; + } ret = omfs_get_imap(sb); if (ret) diff --git a/fs/omfs/omfs_fs.h b/fs/omfs/omfs_fs.h index 12cca245d6e8..ee5e4327de92 100644 --- a/fs/omfs/omfs_fs.h +++ b/fs/omfs/omfs_fs.h @@ -17,6 +17,7 @@ #define OMFS_EXTENT_CONT 0x40 #define OMFS_XOR_COUNT 19 #define OMFS_MAX_BLOCK_SIZE 8192 +#define OMFS_MAX_CLUSTER_SIZE 8 struct omfs_super_block { char s_fill1[256]; -- cgit v1.2.3 From 7909b1c64078087ac153fb47a2f50793fe3ee7d0 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Mon, 12 Jul 2010 14:41:40 +0200 Subject: fuse: don't use atomic kmap Don't use atomic kmap for mapping userspace buffers in device read/write/splice. This is necessary because the next patch (adding store notify) requires that caller of fuse_copy_page() may sleep between invocations. The simplest way to ensure this is to change the atomic kmaps to non-atomic ones. Thankfully architectures where kmap() is not a no-op are going out of fashion, so we can ignore the (probably negligible) performance impact of this change. Signed-off-by: Miklos Szeredi --- fs/fuse/dev.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'fs') diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 9424796d6634..7eb80d33c4f3 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -535,13 +535,13 @@ static void fuse_copy_finish(struct fuse_copy_state *cs) if (!cs->write) { buf->ops->unmap(cs->pipe, buf, cs->mapaddr); } else { - kunmap_atomic(cs->mapaddr, KM_USER0); + kunmap(buf->page); buf->len = PAGE_SIZE - cs->len; } cs->currbuf = NULL; cs->mapaddr = NULL; } else if (cs->mapaddr) { - kunmap_atomic(cs->mapaddr, KM_USER0); + kunmap(cs->pg); if (cs->write) { flush_dcache_page(cs->pg); set_page_dirty_lock(cs->pg); @@ -572,7 +572,7 @@ static int fuse_copy_fill(struct fuse_copy_state *cs) BUG_ON(!cs->nr_segs); cs->currbuf = buf; - cs->mapaddr = buf->ops->map(cs->pipe, buf, 1); + cs->mapaddr = buf->ops->map(cs->pipe, buf, 0); cs->len = buf->len; cs->buf = cs->mapaddr + buf->offset; cs->pipebufs++; @@ -592,7 +592,7 @@ static int fuse_copy_fill(struct fuse_copy_state *cs) buf->len = 0; cs->currbuf = buf; - cs->mapaddr = kmap_atomic(page, KM_USER0); + cs->mapaddr = kmap(page); cs->buf = cs->mapaddr; cs->len = PAGE_SIZE; cs->pipebufs++; @@ -611,7 +611,7 @@ static int fuse_copy_fill(struct fuse_copy_state *cs) return err; BUG_ON(err != 1); offset = cs->addr % PAGE_SIZE; - cs->mapaddr = kmap_atomic(cs->pg, KM_USER0); + cs->mapaddr = kmap(cs->pg); cs->buf = cs->mapaddr + offset; cs->len = min(PAGE_SIZE - offset, cs->seglen); cs->seglen -= cs->len; -- cgit v1.2.3 From a1d75f258230b75d46aecdf28b2e732413028863 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Mon, 12 Jul 2010 14:41:40 +0200 Subject: fuse: add store request Userspace filesystem can request data to be stored in the inode's mapping. This request is synchronous and has no reply. If the write to the fuse device returns an error then the store request was not fully completed (but may have updated some pages). If the stored data overflows the current file size, then the size is extended, similarly to a write(2) on the filesystem. Pages which have been completely stored are marked uptodate. Signed-off-by: Miklos Szeredi --- fs/fuse/dev.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++++ fs/fuse/file.c | 2 +- fs/fuse/fuse_i.h | 2 ++ include/linux/fuse.h | 13 +++++++- 4 files changed, 103 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 7eb80d33c4f3..8e01c865586e 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -1231,6 +1231,91 @@ err: return err; } +static int fuse_notify_store(struct fuse_conn *fc, unsigned int size, + struct fuse_copy_state *cs) +{ + struct fuse_notify_store_out outarg; + struct inode *inode; + struct address_space *mapping; + u64 nodeid; + int err; + pgoff_t index; + unsigned int offset; + unsigned int num; + loff_t file_size; + loff_t end; + + err = -EINVAL; + if (size < sizeof(outarg)) + goto out_finish; + + err = fuse_copy_one(cs, &outarg, sizeof(outarg)); + if (err) + goto out_finish; + + err = -EINVAL; + if (size - sizeof(outarg) != outarg.size) + goto out_finish; + + nodeid = outarg.nodeid; + + down_read(&fc->killsb); + + err = -ENOENT; + if (!fc->sb) + goto out_up_killsb; + + inode = ilookup5(fc->sb, nodeid, fuse_inode_eq, &nodeid); + if (!inode) + goto out_up_killsb; + + mapping = inode->i_mapping; + index = outarg.offset >> PAGE_CACHE_SHIFT; + offset = outarg.offset & ~PAGE_CACHE_MASK; + file_size = i_size_read(inode); + end = outarg.offset + outarg.size; + if (end > file_size) { + file_size = end; + fuse_write_update_size(inode, file_size); + } + + num = outarg.size; + while (num) { + struct page *page; + unsigned int this_num; + + err = -ENOMEM; + page = find_or_create_page(mapping, index, + mapping_gfp_mask(mapping)); + if (!page) + goto out_iput; + + this_num = min_t(unsigned, num, PAGE_CACHE_SIZE - offset); + err = fuse_copy_page(cs, &page, offset, this_num, 0); + if (!err && offset == 0 && (num != 0 || file_size == end)) + SetPageUptodate(page); + unlock_page(page); + page_cache_release(page); + + if (err) + goto out_iput; + + num -= this_num; + offset = 0; + index++; + } + + err = 0; + +out_iput: + iput(inode); +out_up_killsb: + up_read(&fc->killsb); +out_finish: + fuse_copy_finish(cs); + return err; +} + static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code, unsigned int size, struct fuse_copy_state *cs) { @@ -1244,6 +1329,9 @@ static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code, case FUSE_NOTIFY_INVAL_ENTRY: return fuse_notify_inval_entry(fc, size, cs); + case FUSE_NOTIFY_STORE: + return fuse_notify_store(fc, size, cs); + default: fuse_copy_finish(cs); return -EINVAL; diff --git a/fs/fuse/file.c b/fs/fuse/file.c index ada0adeb3bb5..147c1f71bdb9 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -706,7 +706,7 @@ static int fuse_write_begin(struct file *file, struct address_space *mapping, return 0; } -static void fuse_write_update_size(struct inode *inode, loff_t pos) +void fuse_write_update_size(struct inode *inode, loff_t pos) { struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_inode *fi = get_fuse_inode(inode); diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 8f309f04064e..61267d8d527b 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -748,4 +748,6 @@ long fuse_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg, unsigned fuse_file_poll(struct file *file, poll_table *wait); int fuse_dev_release(struct inode *inode, struct file *file); +void fuse_write_update_size(struct inode *inode, loff_t pos); + #endif /* _FS_FUSE_I_H */ diff --git a/include/linux/fuse.h b/include/linux/fuse.h index 88e0eb596919..a90bd49834aa 100644 --- a/include/linux/fuse.h +++ b/include/linux/fuse.h @@ -37,6 +37,9 @@ * * 7.14 * - add splice support to fuse device + * + * 7.15 + * - add store notify */ #ifndef _LINUX_FUSE_H @@ -68,7 +71,7 @@ #define FUSE_KERNEL_VERSION 7 /** Minor version number of this interface */ -#define FUSE_KERNEL_MINOR_VERSION 14 +#define FUSE_KERNEL_MINOR_VERSION 15 /** The node ID of the root inode */ #define FUSE_ROOT_ID 1 @@ -260,6 +263,7 @@ enum fuse_notify_code { FUSE_NOTIFY_POLL = 1, FUSE_NOTIFY_INVAL_INODE = 2, FUSE_NOTIFY_INVAL_ENTRY = 3, + FUSE_NOTIFY_STORE = 4, FUSE_NOTIFY_CODE_MAX, }; @@ -568,4 +572,11 @@ struct fuse_notify_inval_entry_out { __u32 padding; }; +struct fuse_notify_store_out { + __u64 nodeid; + __u64 offset; + __u32 size; + __u32 padding; +}; + #endif /* _LINUX_FUSE_H */ -- cgit v1.2.3 From 2d45ba381a74a743eeaa2b06c7c5c0d2bf73ba1a Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Mon, 12 Jul 2010 14:41:40 +0200 Subject: fuse: add retrieve request Userspace filesystem can request data to be retrieved from the inode's mapping. This request is synchronous and the retrieved data is queued as a new request. If the write to the fuse device returns an error then the retrieve request was not completed and a reply will not be sent. Only present pages are returned in the retrieve reply. Retrieving stops when it finds a non-present page and only data prior to that is returned. This request doesn't change the dirty state of pages. Signed-off-by: Miklos Szeredi --- fs/fuse/dev.c | 131 ++++++++++++++++++++++++++++++++++++++++++++++++++- fs/fuse/fuse_i.h | 1 + include/linux/fuse.h | 21 +++++++++ 3 files changed, 152 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 8e01c865586e..69ad053ffd78 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -239,7 +239,6 @@ static u64 fuse_get_unique(struct fuse_conn *fc) static void queue_request(struct fuse_conn *fc, struct fuse_req *req) { - req->in.h.unique = fuse_get_unique(fc); req->in.h.len = sizeof(struct fuse_in_header) + len_args(req->in.numargs, (struct fuse_arg *) req->in.args); list_add_tail(&req->list, &fc->pending); @@ -261,6 +260,7 @@ static void flush_bg_queue(struct fuse_conn *fc) req = list_entry(fc->bg_queue.next, struct fuse_req, list); list_del(&req->list); fc->active_background++; + req->in.h.unique = fuse_get_unique(fc); queue_request(fc, req); } } @@ -398,6 +398,7 @@ void fuse_request_send(struct fuse_conn *fc, struct fuse_req *req) else if (fc->conn_error) req->out.h.error = -ECONNREFUSED; else { + req->in.h.unique = fuse_get_unique(fc); queue_request(fc, req); /* acquire extra reference, since request is still needed after request_end() */ @@ -450,6 +451,23 @@ void fuse_request_send_background(struct fuse_conn *fc, struct fuse_req *req) } EXPORT_SYMBOL_GPL(fuse_request_send_background); +static int fuse_request_send_notify_reply(struct fuse_conn *fc, + struct fuse_req *req, u64 unique) +{ + int err = -ENODEV; + + req->isreply = 0; + req->in.h.unique = unique; + spin_lock(&fc->lock); + if (fc->connected) { + queue_request(fc, req); + err = 0; + } + spin_unlock(&fc->lock); + + return err; +} + /* * Called under fc->lock * @@ -1316,6 +1334,114 @@ out_finish: return err; } +static void fuse_retrieve_end(struct fuse_conn *fc, struct fuse_req *req) +{ + int i; + + for (i = 0; i < req->num_pages; i++) { + struct page *page = req->pages[i]; + page_cache_release(page); + } +} + +static int fuse_retrieve(struct fuse_conn *fc, struct inode *inode, + struct fuse_notify_retrieve_out *outarg) +{ + int err; + struct address_space *mapping = inode->i_mapping; + struct fuse_req *req; + pgoff_t index; + loff_t file_size; + unsigned int num; + unsigned int offset; + size_t total_len; + + req = fuse_get_req(fc); + if (IS_ERR(req)) + return PTR_ERR(req); + + offset = outarg->offset & ~PAGE_CACHE_MASK; + + req->in.h.opcode = FUSE_NOTIFY_REPLY; + req->in.h.nodeid = outarg->nodeid; + req->in.numargs = 2; + req->in.argpages = 1; + req->page_offset = offset; + req->end = fuse_retrieve_end; + + index = outarg->offset >> PAGE_CACHE_SHIFT; + file_size = i_size_read(inode); + num = outarg->size; + if (outarg->offset > file_size) + num = 0; + else if (outarg->offset + num > file_size) + num = file_size - outarg->offset; + + while (num) { + struct page *page; + unsigned int this_num; + + page = find_get_page(mapping, index); + if (!page) + break; + + this_num = min_t(unsigned, num, PAGE_CACHE_SIZE - offset); + req->pages[req->num_pages] = page; + req->num_pages++; + + num -= this_num; + total_len += this_num; + } + req->misc.retrieve_in.offset = outarg->offset; + req->misc.retrieve_in.size = total_len; + req->in.args[0].size = sizeof(req->misc.retrieve_in); + req->in.args[0].value = &req->misc.retrieve_in; + req->in.args[1].size = total_len; + + err = fuse_request_send_notify_reply(fc, req, outarg->notify_unique); + if (err) + fuse_retrieve_end(fc, req); + + return err; +} + +static int fuse_notify_retrieve(struct fuse_conn *fc, unsigned int size, + struct fuse_copy_state *cs) +{ + struct fuse_notify_retrieve_out outarg; + struct inode *inode; + int err; + + err = -EINVAL; + if (size != sizeof(outarg)) + goto copy_finish; + + err = fuse_copy_one(cs, &outarg, sizeof(outarg)); + if (err) + goto copy_finish; + + fuse_copy_finish(cs); + + down_read(&fc->killsb); + err = -ENOENT; + if (fc->sb) { + u64 nodeid = outarg.nodeid; + + inode = ilookup5(fc->sb, nodeid, fuse_inode_eq, &nodeid); + if (inode) { + err = fuse_retrieve(fc, inode, &outarg); + iput(inode); + } + } + up_read(&fc->killsb); + + return err; + +copy_finish: + fuse_copy_finish(cs); + return err; +} + static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code, unsigned int size, struct fuse_copy_state *cs) { @@ -1332,6 +1458,9 @@ static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code, case FUSE_NOTIFY_STORE: return fuse_notify_store(fc, size, cs); + case FUSE_NOTIFY_RETRIEVE: + return fuse_notify_retrieve(fc, size, cs); + default: fuse_copy_finish(cs); return -EINVAL; diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 61267d8d527b..57d4a3a0f102 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -272,6 +272,7 @@ struct fuse_req { struct fuse_write_in in; struct fuse_write_out out; } write; + struct fuse_notify_retrieve_in retrieve_in; struct fuse_lk_in lk_in; } misc; diff --git a/include/linux/fuse.h b/include/linux/fuse.h index a90bd49834aa..c3c578e09833 100644 --- a/include/linux/fuse.h +++ b/include/linux/fuse.h @@ -40,6 +40,7 @@ * * 7.15 * - add store notify + * - add retrieve notify */ #ifndef _LINUX_FUSE_H @@ -254,6 +255,7 @@ enum fuse_opcode { FUSE_DESTROY = 38, FUSE_IOCTL = 39, FUSE_POLL = 40, + FUSE_NOTIFY_REPLY = 41, /* CUSE specific operations */ CUSE_INIT = 4096, @@ -264,6 +266,7 @@ enum fuse_notify_code { FUSE_NOTIFY_INVAL_INODE = 2, FUSE_NOTIFY_INVAL_ENTRY = 3, FUSE_NOTIFY_STORE = 4, + FUSE_NOTIFY_RETRIEVE = 5, FUSE_NOTIFY_CODE_MAX, }; @@ -579,4 +582,22 @@ struct fuse_notify_store_out { __u32 padding; }; +struct fuse_notify_retrieve_out { + __u64 notify_unique; + __u64 nodeid; + __u64 offset; + __u32 size; + __u32 padding; +}; + +/* Matches the size of fuse_write_in */ +struct fuse_notify_retrieve_in { + __u64 dummy1; + __u64 offset; + __u32 size; + __u32 dummy2; + __u64 dummy3; + __u64 dummy4; +}; + #endif /* _LINUX_FUSE_H */ -- cgit v1.2.3 From 693c241a5f6aa01417f5f4caf9f82e60e316398d Mon Sep 17 00:00:00 2001 From: Joel Becker Date: Fri, 2 Jul 2010 17:20:27 -0700 Subject: ocfs2: No need to zero pages past i_size. When ocfs2 fills a hole, it does so by allocating clusters. When a cluster is larger than the write, ocfs2 must zero the portions of the cluster outside of the write. If the clustersize is smaller than a pagecache page, this is handled by the normal pagecache mechanisms, but when the clustersize is larger than a page, ocfs2's write code will zero the pages adjacent to the write. This makes sure the entire cluster is zeroed correctly. Currently ocfs2 behaves exactly the same when writing past i_size. However, this means ocfs2 is writing zeroed pages for portions of a new cluster that are beyond i_size. The page writeback code isn't expecting this. It treats all pages past the one containing i_size as left behind due to a previous truncate operation. Thankfully, ocfs2 calculates the number of pages it will be working on up front. The rest of the write code merely honors the original calculation. We can simply trim the number of pages to only cover the actual file data. Signed-off-by: Joel Becker Cc: stable@kernel.org --- fs/ocfs2/aops.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/ocfs2/aops.c b/fs/ocfs2/aops.c index 742893ea7390..356e976772bf 100644 --- a/fs/ocfs2/aops.c +++ b/fs/ocfs2/aops.c @@ -1100,23 +1100,37 @@ out: */ static int ocfs2_grab_pages_for_write(struct address_space *mapping, struct ocfs2_write_ctxt *wc, - u32 cpos, loff_t user_pos, int new, + u32 cpos, loff_t user_pos, + unsigned user_len, int new, struct page *mmap_page) { int ret = 0, i; - unsigned long start, target_index, index; + unsigned long start, target_index, end_index, index; struct inode *inode = mapping->host; + loff_t last_byte; target_index = user_pos >> PAGE_CACHE_SHIFT; /* * Figure out how many pages we'll be manipulating here. For * non allocating write, we just change the one - * page. Otherwise, we'll need a whole clusters worth. + * page. Otherwise, we'll need a whole clusters worth. If we're + * writing past i_size, we only need enough pages to cover the + * last page of the write. */ if (new) { wc->w_num_pages = ocfs2_pages_per_cluster(inode->i_sb); start = ocfs2_align_clusters_to_page_index(inode->i_sb, cpos); + /* + * We need the index *past* the last page we could possibly + * touch. This is the page past the end of the write or + * i_size, whichever is greater. + */ + last_byte = max(user_pos + user_len, i_size_read(inode)); + BUG_ON(last_byte < 1); + end_index = ((last_byte - 1) >> PAGE_CACHE_SHIFT) + 1; + if ((start + wc->w_num_pages) > end_index) + wc->w_num_pages = end_index - start; } else { wc->w_num_pages = 1; start = target_index; @@ -1773,7 +1787,7 @@ int ocfs2_write_begin_nolock(struct address_space *mapping, * that we can zero and flush if we error after adding the * extent. */ - ret = ocfs2_grab_pages_for_write(mapping, wc, wc->w_cpos, pos, + ret = ocfs2_grab_pages_for_write(mapping, wc, wc->w_cpos, pos, len, cluster_of_pages, mmap_page); if (ret) { mlog_errno(ret); -- cgit v1.2.3 From f471c9df922a80ca9af1d9a490b4aab3f990ec19 Mon Sep 17 00:00:00 2001 From: Wengang Wang Date: Wed, 30 Jun 2010 20:23:30 +0800 Subject: ocfs2/dlm: don't access beyond bitmap size dlm->recovery_map is defined as unsigned long recovery_map[BITS_TO_LONGS(O2NM_MAX_NODES)]; We should treat O2NM_MAX_NODES as the bit map size in bits. This patches fixes a bit operation that takes O2NM_MAX_NODES + 1 as bitmap size. Signed-off-by: Wengang Wang Signed-off-by: Joel Becker --- fs/ocfs2/dlm/dlmrecovery.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/ocfs2/dlm/dlmrecovery.c b/fs/ocfs2/dlm/dlmrecovery.c index f8b75ce4be70..9dfaac73b36d 100644 --- a/fs/ocfs2/dlm/dlmrecovery.c +++ b/fs/ocfs2/dlm/dlmrecovery.c @@ -463,7 +463,7 @@ static int dlm_do_recovery(struct dlm_ctxt *dlm) if (dlm->reco.dead_node == O2NM_INVALID_NODE_NUM) { int bit; - bit = find_next_bit (dlm->recovery_map, O2NM_MAX_NODES+1, 0); + bit = find_next_bit (dlm->recovery_map, O2NM_MAX_NODES, 0); if (bit >= O2NM_MAX_NODES || bit < 0) dlm_set_reco_dead_node(dlm, O2NM_INVALID_NODE_NUM); else -- cgit v1.2.3 From 0a463b74e7e6856b24e613de2b85237c6e11890b Mon Sep 17 00:00:00 2001 From: Tao Ma Date: Thu, 8 Jul 2010 11:11:11 +0800 Subject: ocfs2: Remove the redundant cpu_to_le64. In ocfs2_block_group_alloc, we set c_blkno by bg->bg_blkno. But actually bg->bg_blkno is already changed to little endian in ocfs2_block_group_fill. So remove the extra cpu_to_le64. Reported-by: Marcos Matsunaga Signed-off-by: Tao Ma Signed-off-by: Joel Becker --- fs/ocfs2/suballoc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/ocfs2/suballoc.c b/fs/ocfs2/suballoc.c index f4c2a9eb8c4d..a8e6a95a353f 100644 --- a/fs/ocfs2/suballoc.c +++ b/fs/ocfs2/suballoc.c @@ -741,7 +741,7 @@ static int ocfs2_block_group_alloc(struct ocfs2_super *osb, le16_to_cpu(bg->bg_free_bits_count)); le32_add_cpu(&cl->cl_recs[alloc_rec].c_total, le16_to_cpu(bg->bg_bits)); - cl->cl_recs[alloc_rec].c_blkno = cpu_to_le64(bg->bg_blkno); + cl->cl_recs[alloc_rec].c_blkno = bg->bg_blkno; if (le16_to_cpu(cl->cl_next_free_rec) < le16_to_cpu(cl->cl_count)) le16_add_cpu(&cl->cl_next_free_rec, 1); -- cgit v1.2.3 From a78f9f4668949a6588b8872f162e86685c63d023 Mon Sep 17 00:00:00 2001 From: Tao Ma Date: Fri, 9 Jul 2010 14:53:11 +0800 Subject: ocfs2: make xattr extension work with new local alloc reservation. The old ocfs2_xattr_extent_allocation is too optimistic about the clusters we can get. So actually if the file system is too fragmented, ocfs2_add_clusters_in_btree will return us with EGAIN and we need to allocate clusters once again. So this patch change it to a while loop so that we can allocate clusters until we reach clusters_to_add. Signed-off-by: Tao Ma Signed-off-by: Joel Becker Cc: stable@kernel.org --- fs/ocfs2/xattr.c | 74 ++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 45 insertions(+), 29 deletions(-) (limited to 'fs') diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c index e97b34842cfe..2be19083713f 100644 --- a/fs/ocfs2/xattr.c +++ b/fs/ocfs2/xattr.c @@ -709,7 +709,7 @@ static int ocfs2_xattr_extend_allocation(struct inode *inode, struct ocfs2_xattr_value_buf *vb, struct ocfs2_xattr_set_ctxt *ctxt) { - int status = 0; + int status = 0, credits; handle_t *handle = ctxt->handle; enum ocfs2_alloc_restarted why; u32 prev_clusters, logical_start = le32_to_cpu(vb->vb_xv->xr_clusters); @@ -719,38 +719,54 @@ static int ocfs2_xattr_extend_allocation(struct inode *inode, ocfs2_init_xattr_value_extent_tree(&et, INODE_CACHE(inode), vb); - status = vb->vb_access(handle, INODE_CACHE(inode), vb->vb_bh, - OCFS2_JOURNAL_ACCESS_WRITE); - if (status < 0) { - mlog_errno(status); - goto leave; - } + while (clusters_to_add) { + status = vb->vb_access(handle, INODE_CACHE(inode), vb->vb_bh, + OCFS2_JOURNAL_ACCESS_WRITE); + if (status < 0) { + mlog_errno(status); + break; + } - prev_clusters = le32_to_cpu(vb->vb_xv->xr_clusters); - status = ocfs2_add_clusters_in_btree(handle, - &et, - &logical_start, - clusters_to_add, - 0, - ctxt->data_ac, - ctxt->meta_ac, - &why); - if (status < 0) { - mlog_errno(status); - goto leave; - } + prev_clusters = le32_to_cpu(vb->vb_xv->xr_clusters); + status = ocfs2_add_clusters_in_btree(handle, + &et, + &logical_start, + clusters_to_add, + 0, + ctxt->data_ac, + ctxt->meta_ac, + &why); + if ((status < 0) && (status != -EAGAIN)) { + if (status != -ENOSPC) + mlog_errno(status); + break; + } - ocfs2_journal_dirty(handle, vb->vb_bh); + ocfs2_journal_dirty(handle, vb->vb_bh); - clusters_to_add -= le32_to_cpu(vb->vb_xv->xr_clusters) - prev_clusters; + clusters_to_add -= le32_to_cpu(vb->vb_xv->xr_clusters) - + prev_clusters; - /* - * We should have already allocated enough space before the transaction, - * so no need to restart. - */ - BUG_ON(why != RESTART_NONE || clusters_to_add); - -leave: + if (why != RESTART_NONE && clusters_to_add) { + /* + * We can only fail in case the alloc file doesn't give + * up enough clusters. + */ + BUG_ON(why == RESTART_META); + + mlog(0, "restarting xattr value extension for %u" + " clusters,.\n", clusters_to_add); + credits = ocfs2_calc_extend_credits(inode->i_sb, + &vb->vb_xv->xr_list, + clusters_to_add); + status = ocfs2_extend_trans(handle, credits); + if (status < 0) { + status = -ENOMEM; + mlog_errno(status); + break; + } + } + } return status; } -- cgit v1.2.3 From 121a39bb00b421211f4f590c440a8f636d3ae807 Mon Sep 17 00:00:00 2001 From: Tao Ma Date: Fri, 9 Jul 2010 14:53:12 +0800 Subject: ocfs2: Make xattr reflink work with new local alloc reservation. The new reservation code in local alloc has add the limitation that the caller should handle the case that the local alloc doesn't give use enough contiguous clusters. It make the old xattr reflink code broken. So this patch udpate the xattr reflink code so that it can handle the case that local alloc give us one cluster at a time. Signed-off-by: Tao Ma Signed-off-by: Joel Becker --- fs/ocfs2/xattr.c | 126 +++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 90 insertions(+), 36 deletions(-) (limited to 'fs') diff --git a/fs/ocfs2/xattr.c b/fs/ocfs2/xattr.c index 2be19083713f..d03469f61801 100644 --- a/fs/ocfs2/xattr.c +++ b/fs/ocfs2/xattr.c @@ -6804,16 +6804,15 @@ out: return ret; } -static int ocfs2_reflink_xattr_buckets(handle_t *handle, +static int ocfs2_reflink_xattr_bucket(handle_t *handle, u64 blkno, u64 new_blkno, u32 clusters, + u32 *cpos, int num_buckets, struct ocfs2_alloc_context *meta_ac, struct ocfs2_alloc_context *data_ac, struct ocfs2_reflink_xattr_tree_args *args) { int i, j, ret = 0; struct super_block *sb = args->reflink->old_inode->i_sb; - u32 bpc = ocfs2_xattr_buckets_per_cluster(OCFS2_SB(sb)); - u32 num_buckets = clusters * bpc; int bpb = args->old_bucket->bu_blocks; struct ocfs2_xattr_value_buf vb = { .vb_access = ocfs2_journal_access, @@ -6832,14 +6831,6 @@ static int ocfs2_reflink_xattr_buckets(handle_t *handle, break; } - /* - * The real bucket num in this series of blocks is stored - * in the 1st bucket. - */ - if (i == 0) - num_buckets = le16_to_cpu( - bucket_xh(args->old_bucket)->xh_num_buckets); - ret = ocfs2_xattr_bucket_journal_access(handle, args->new_bucket, OCFS2_JOURNAL_ACCESS_CREATE); @@ -6853,6 +6844,18 @@ static int ocfs2_reflink_xattr_buckets(handle_t *handle, bucket_block(args->old_bucket, j), sb->s_blocksize); + /* + * Record the start cpos so that we can use it to initialize + * our xattr tree we also set the xh_num_bucket for the new + * bucket. + */ + if (i == 0) { + *cpos = le32_to_cpu(bucket_xh(args->new_bucket)-> + xh_entries[0].xe_name_hash); + bucket_xh(args->new_bucket)->xh_num_buckets = + cpu_to_le16(num_buckets); + } + ocfs2_xattr_bucket_journal_dirty(handle, args->new_bucket); ret = ocfs2_reflink_xattr_header(handle, args->reflink, @@ -6882,6 +6885,7 @@ static int ocfs2_reflink_xattr_buckets(handle_t *handle, } ocfs2_xattr_bucket_journal_dirty(handle, args->new_bucket); + ocfs2_xattr_bucket_relse(args->old_bucket); ocfs2_xattr_bucket_relse(args->new_bucket); } @@ -6890,6 +6894,75 @@ static int ocfs2_reflink_xattr_buckets(handle_t *handle, ocfs2_xattr_bucket_relse(args->new_bucket); return ret; } + +static int ocfs2_reflink_xattr_buckets(handle_t *handle, + struct inode *inode, + struct ocfs2_reflink_xattr_tree_args *args, + struct ocfs2_extent_tree *et, + struct ocfs2_alloc_context *meta_ac, + struct ocfs2_alloc_context *data_ac, + u64 blkno, u32 cpos, u32 len) +{ + int ret, first_inserted = 0; + u32 p_cluster, num_clusters, reflink_cpos = 0; + u64 new_blkno; + unsigned int num_buckets, reflink_buckets; + unsigned int bpc = + ocfs2_xattr_buckets_per_cluster(OCFS2_SB(inode->i_sb)); + + ret = ocfs2_read_xattr_bucket(args->old_bucket, blkno); + if (ret) { + mlog_errno(ret); + goto out; + } + num_buckets = le16_to_cpu(bucket_xh(args->old_bucket)->xh_num_buckets); + ocfs2_xattr_bucket_relse(args->old_bucket); + + while (len && num_buckets) { + ret = ocfs2_claim_clusters(handle, data_ac, + 1, &p_cluster, &num_clusters); + if (ret) { + mlog_errno(ret); + goto out; + } + + new_blkno = ocfs2_clusters_to_blocks(inode->i_sb, p_cluster); + reflink_buckets = min(num_buckets, bpc * num_clusters); + + ret = ocfs2_reflink_xattr_bucket(handle, blkno, + new_blkno, num_clusters, + &reflink_cpos, reflink_buckets, + meta_ac, data_ac, args); + if (ret) { + mlog_errno(ret); + goto out; + } + + /* + * For the 1st allocated cluster, we make it use the same cpos + * so that the xattr tree looks the same as the original one + * in the most case. + */ + if (!first_inserted) { + reflink_cpos = cpos; + first_inserted = 1; + } + ret = ocfs2_insert_extent(handle, et, reflink_cpos, new_blkno, + num_clusters, 0, meta_ac); + if (ret) + mlog_errno(ret); + + mlog(0, "insert new xattr extent rec start %llu len %u to %u\n", + (unsigned long long)new_blkno, num_clusters, reflink_cpos); + + len -= num_clusters; + blkno += ocfs2_clusters_to_blocks(inode->i_sb, num_clusters); + num_buckets -= reflink_buckets; + } +out: + return ret; +} + /* * Create the same xattr extent record in the new inode's xattr tree. */ @@ -6901,8 +6974,6 @@ static int ocfs2_reflink_xattr_rec(struct inode *inode, void *para) { int ret, credits = 0; - u32 p_cluster, num_clusters; - u64 new_blkno; handle_t *handle; struct ocfs2_reflink_xattr_tree_args *args = (struct ocfs2_reflink_xattr_tree_args *)para; @@ -6911,6 +6982,9 @@ static int ocfs2_reflink_xattr_rec(struct inode *inode, struct ocfs2_alloc_context *data_ac = NULL; struct ocfs2_extent_tree et; + mlog(0, "reflink xattr buckets %llu len %u\n", + (unsigned long long)blkno, len); + ocfs2_init_xattr_tree_extent_tree(&et, INODE_CACHE(args->reflink->new_inode), args->new_blk_bh); @@ -6930,32 +7004,12 @@ static int ocfs2_reflink_xattr_rec(struct inode *inode, goto out; } - ret = ocfs2_claim_clusters(handle, data_ac, - len, &p_cluster, &num_clusters); - if (ret) { - mlog_errno(ret); - goto out_commit; - } - - new_blkno = ocfs2_clusters_to_blocks(osb->sb, p_cluster); - - mlog(0, "reflink xattr buckets %llu to %llu, len %u\n", - (unsigned long long)blkno, (unsigned long long)new_blkno, len); - ret = ocfs2_reflink_xattr_buckets(handle, blkno, new_blkno, len, - meta_ac, data_ac, args); - if (ret) { - mlog_errno(ret); - goto out_commit; - } - - mlog(0, "insert new xattr extent rec start %llu len %u to %u\n", - (unsigned long long)new_blkno, len, cpos); - ret = ocfs2_insert_extent(handle, &et, cpos, new_blkno, - len, 0, meta_ac); + ret = ocfs2_reflink_xattr_buckets(handle, inode, args, &et, + meta_ac, data_ac, + blkno, cpos, len); if (ret) mlog_errno(ret); -out_commit: ocfs2_commit_trans(osb, handle); out: -- cgit v1.2.3 From e372357ba55ae89307af15cd680467d8f0db4f01 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sat, 10 Jul 2010 16:33:36 +0200 Subject: ocfs2: tighten up strlen() checking This function is only called from one place and it's like this: dlm_register_domain(conn->cc_name, dlm_key, &fs_version); The "conn->cc_name" is 64 characters long. If strlen(conn->cc_name) were equal to O2NM_MAX_NAME_LEN (64) that would be a bug because strlen() doesn't count the NULL character. In fact, if you look how O2NM_MAX_NAME_LEN is used, it mostly describes 64 character buffers. The only exception is nd_name from struct o2nm_node. Anyway I looked into it and in this case the domain string comes from osb->uuid_str in ocfs2_setup_osb_uuid(). That's 32 characters and NULL which easily fits into O2NM_MAX_NAME_LEN. This patch doesn't change how the code works, but I think it makes the code a little cleaner. Signed-off-by: Dan Carpenter Signed-off-by: Joel Becker --- fs/ocfs2/dlm/dlmdomain.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/ocfs2/dlm/dlmdomain.c b/fs/ocfs2/dlm/dlmdomain.c index 2ccad86fb590..153abb5abef0 100644 --- a/fs/ocfs2/dlm/dlmdomain.c +++ b/fs/ocfs2/dlm/dlmdomain.c @@ -1671,7 +1671,7 @@ struct dlm_ctxt * dlm_register_domain(const char *domain, struct dlm_ctxt *dlm = NULL; struct dlm_ctxt *new_ctxt = NULL; - if (strlen(domain) > O2NM_MAX_NAME_LEN) { + if (strlen(domain) >= O2NM_MAX_NAME_LEN) { ret = -ENAMETOOLONG; mlog(ML_ERROR, "domain name length too long\n"); goto leave; -- cgit v1.2.3 From 6fb4374f6b1b3932f3acfe9d353568d3d8599cad Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Sun, 23 May 2010 15:20:21 +0300 Subject: UBIFS: fix GC LEB recovery UBIFS tries to alway have an LEB reserved for GC, and stores it in c->gc_lnum. Besides, there is GC head which points to the current GC head LEB. In case of an unclean power cut, what may happen is that the GC head was switched to the reserved GC LEB (c->gc_lnum), but a new reserved GC LEB was not created yet. So, after an unclean reboot we may have no reserved GC LEB, and we need to find a new LEB for this. To do this, we find a dirty LEB which can fit the current GC head, move the data, unmap this dirty LEB, and it becomes our reserved GC LEB. However, if we cannot find a dirty enough LEB, we return failure, which is wrong, because we still can have free LEBs to use for the reserved GC LEB. This patch fixes the issue. This patch also fixes few typos in comments, which were spotted by aspell. Note, this patch fixes a real issue [ 14.328117] UBIFS: recovery needed [ 53.941378] UBIFS error (pid 462): ubifs_rcvry_gc_commit: could not find a dirty LEB [ 89.606399] UBIFS: recovery completed [ 89.609329] UBIFS assert failed in mount_ubifs at 1358 (pid 462) [ 89.616165] [] (unwind_backtrace+0x0/0xe4) from [] (ubifs_fill_super+0x11d0/0x1c4c) [ 89.625930] [] (ubifs_fill_super+0x11d0/0x1c4c) from [] (ubifs_get_sb+0x1b0/0x354) [ 89.635696] [] (ubifs_get_sb+0x1b0/0x354) from [] (vfs_kern_mount+0x50/0xe0) [ 89.644485] [] (vfs_kern_mount+0x50/0xe0) from [] (do_kern_mount+0x34/0xdc) [ 89.653274] [] (do_kern_mount+0x34/0xdc) from [] (do_mount+0x148/0x7cc) [ 89.662063] [] (do_mount+0x148/0x7cc) from [] (sys_mount+0x98/0xc8) [ 89.670852] [] (sys_mount+0x98/0xc8) from [] (ret_fast_syscall+0x0/0x28) which was reported here: http://article.gmane.org/gmane.linux.drivers.mtd/29923 by Alexander Pazdnikov Reported-by: Alexander Pazdnikov Signed-off-by: Artem Bityutskiy Reviewed-by: Adrian Hunter --- fs/ubifs/recovery.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) (limited to 'fs') diff --git a/fs/ubifs/recovery.c b/fs/ubifs/recovery.c index 109c6ea03bb5..daae9e1f5382 100644 --- a/fs/ubifs/recovery.c +++ b/fs/ubifs/recovery.c @@ -24,7 +24,7 @@ * This file implements functions needed to recover from unclean un-mounts. * When UBIFS is mounted, it checks a flag on the master node to determine if * an un-mount was completed successfully. If not, the process of mounting - * incorparates additional checking and fixing of on-flash data structures. + * incorporates additional checking and fixing of on-flash data structures. * UBIFS always cleans away all remnants of an unclean un-mount, so that * errors do not accumulate. However UBIFS defers recovery if it is mounted * read-only, and the flash is not modified in that case. @@ -1063,8 +1063,21 @@ int ubifs_rcvry_gc_commit(struct ubifs_info *c) } err = ubifs_find_dirty_leb(c, &lp, wbuf->offs, 2); if (err) { - if (err == -ENOSPC) - dbg_err("could not find a dirty LEB"); + /* + * There are no dirty or empty LEBs subject to here being + * enough for the index. Try to use + * 'ubifs_find_free_leb_for_idx()', which will return any empty + * LEBs (ignoring index requirements). If the index then + * doesn't have enough LEBs the recovery commit will fail - + * which is the same result anyway i.e. recovery fails. So + * there is no problem ignoring index requirements and just + * grabbing a free LEB since we have already established there + * is not a dirty LEB we could have used instead. + */ + if (err == -ENOSPC) { + dbg_rcvry("could not find a dirty LEB"); + goto find_free; + } return err; } ubifs_assert(!(lp.flags & LPROPS_INDEX)); @@ -1139,8 +1152,8 @@ int ubifs_rcvry_gc_commit(struct ubifs_info *c) find_free: /* * There is no GC head LEB or the free space in the GC head LEB is too - * small. Allocate gc_lnum by calling 'ubifs_find_free_leb_for_idx()' so - * GC is not run. + * small, or there are not dirty LEBs. Allocate gc_lnum by calling + * 'ubifs_find_free_leb_for_idx()' so GC is not run. */ lnum = ubifs_find_free_leb_for_idx(c); if (lnum < 0) { -- cgit v1.2.3 From a8bf2bc212e129dd59a8b06cdbc15079cc3bd876 Mon Sep 17 00:00:00 2001 From: Bob Peterson Date: Thu, 24 Jun 2010 19:15:09 -0400 Subject: GFS2: O_TRUNC not working on stuffed files across cluster This patch replaces a statement that got dropped out by accident. Without the patch, truncates on stuffed (very small) files cause those files to have an unpredictable size. Signed-off-by: Bob Peterson Signed-off-by: Steven Whitehouse --- fs/gfs2/bmap.c | 1 + 1 file changed, 1 insertion(+) (limited to 'fs') diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c index 4a48c0f4b402..84da64b551b2 100644 --- a/fs/gfs2/bmap.c +++ b/fs/gfs2/bmap.c @@ -1041,6 +1041,7 @@ static int trunc_start(struct gfs2_inode *ip, u64 size) if (gfs2_is_stuffed(ip)) { u64 dsize = size + sizeof(struct gfs2_inode); + ip->i_disksize = size; ip->i_inode.i_mtime = ip->i_inode.i_ctime = CURRENT_TIME; gfs2_trans_add_bh(ip->i_gl, dibh, 1); gfs2_dinode_out(ip, dibh->b_data); -- cgit v1.2.3 From b7dc2df5725fe7355fd76000ead7e39728e1b8a9 Mon Sep 17 00:00:00 2001 From: Bob Peterson Date: Wed, 23 Jun 2010 11:44:47 -0400 Subject: GFS2: recovery stuck on transaction lock This patch fixes bugzilla bug #590878: GFS2: recovery stuck on transaction lock. We set the frozen flag on the glock when we receive a completion that cannot be delivered due to blocked locks. At that point we check to see whether the first waiting holder has the noexp flag set. If the noexp lock is queued later, then we need to unfreeze the glock at that point in time, namely, in the glock work function. This patch was originally written by Steve Whitehouse, but since he's on holiday, I'm submitting it. It's been well tested with a complex recovery test called revolver. Signed-off-by: Steve Whitehouse Signed-off-by: Bob Peterson --- fs/gfs2/glock.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'fs') diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c index ddcdbf493536..dbab3fdc2582 100644 --- a/fs/gfs2/glock.c +++ b/fs/gfs2/glock.c @@ -706,8 +706,18 @@ static void glock_work_func(struct work_struct *work) { unsigned long delay = 0; struct gfs2_glock *gl = container_of(work, struct gfs2_glock, gl_work.work); + struct gfs2_holder *gh; int drop_ref = 0; + if (unlikely(test_bit(GLF_FROZEN, &gl->gl_flags))) { + spin_lock(&gl->gl_spin); + gh = find_first_waiter(gl); + if (gh && (gh->gh_flags & LM_FLAG_NOEXP) && + test_and_clear_bit(GLF_FROZEN, &gl->gl_flags)) + set_bit(GLF_REPLY_PENDING, &gl->gl_flags); + spin_unlock(&gl->gl_spin); + } + if (test_and_clear_bit(GLF_REPLY_PENDING, &gl->gl_flags)) { finish_xmote(gl, gl->gl_reply); drop_ref = 1; -- cgit v1.2.3 From b1becbdee776b447f203aa8da9a40488d5a75e1d Mon Sep 17 00:00:00 2001 From: Bob Peterson Date: Thu, 17 Jun 2010 16:45:37 -0400 Subject: GFS2: Fix kernel NULL pointer dereference by dlm_astd This patch fixes a problem in an error path when looking up dinodes. There are two sister-functions, gfs2_inode_lookup and gfs2_process_unlinked_inode. Both functions acquire and hold the i_iopen glock for the dinode being looked up. The last thing they try to do is hold the i_gl glock for the dinode. If that glock fails for some reason, the error path was incorrectly calling gfs2_glock_put for the i_iopen glock twice. This resulted in the glock being prematurely freed. The "minimum hold time" usually kept the glock in memory, but the lock interface to dlm (aka lock_dlm) freed its memory for the glock. In some circumstances, it would cause dlm's dlm_astd daemon to try to call the bast function for the freed lock_dlm memory, which resulted in a NULL pointer dereference. Signed-off-by: Bob Peterson Signed-off-by: Steven Whitehouse --- fs/gfs2/inode.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c index b5612cbb62a5..f03afd9c44bc 100644 --- a/fs/gfs2/inode.c +++ b/fs/gfs2/inode.c @@ -169,7 +169,7 @@ struct inode *gfs2_inode_lookup(struct super_block *sb, { struct inode *inode; struct gfs2_inode *ip; - struct gfs2_glock *io_gl; + struct gfs2_glock *io_gl = NULL; int error; inode = gfs2_iget(sb, no_addr); @@ -198,6 +198,7 @@ struct inode *gfs2_inode_lookup(struct super_block *sb, ip->i_iopen_gh.gh_gl->gl_object = ip; gfs2_glock_put(io_gl); + io_gl = NULL; if ((type == DT_UNKNOWN) && (no_formal_ino == 0)) goto gfs2_nfsbypass; @@ -228,7 +229,8 @@ gfs2_nfsbypass: fail_glock: gfs2_glock_dq(&ip->i_iopen_gh); fail_iopen: - gfs2_glock_put(io_gl); + if (io_gl) + gfs2_glock_put(io_gl); fail_put: if (inode->i_state & I_NEW) ip->i_gl->gl_object = NULL; @@ -256,7 +258,7 @@ void gfs2_process_unlinked_inode(struct super_block *sb, u64 no_addr) { struct gfs2_sbd *sdp; struct gfs2_inode *ip; - struct gfs2_glock *io_gl; + struct gfs2_glock *io_gl = NULL; int error; struct gfs2_holder gh; struct inode *inode; @@ -293,6 +295,7 @@ void gfs2_process_unlinked_inode(struct super_block *sb, u64 no_addr) ip->i_iopen_gh.gh_gl->gl_object = ip; gfs2_glock_put(io_gl); + io_gl = NULL; inode->i_mode = DT2IF(DT_UNKNOWN); @@ -319,7 +322,8 @@ void gfs2_process_unlinked_inode(struct super_block *sb, u64 no_addr) fail_glock: gfs2_glock_dq(&ip->i_iopen_gh); fail_iopen: - gfs2_glock_put(io_gl); + if (io_gl) + gfs2_glock_put(io_gl); fail_put: ip->i_gl->gl_object = NULL; gfs2_glock_put(ip->i_gl); -- cgit v1.2.3 From 8b4216018bdbfbb1b76150d202b15ee68c38e991 Mon Sep 17 00:00:00 2001 From: Abhijith Das Date: Sun, 4 Jul 2010 01:33:24 -0400 Subject: GFS2: BUG in gfs2_adjust_quota HighMem pages on i686 do not get mapped to the buffer_heads and this was causing a NULL pointer dereference when we were trying to memset page buffers to zero. We now use zero_user() that kmaps the page and directly manipulates page data. This patch also fixes a boundary condition that was incorrect. Signed-off-by: Abhi Das Signed-off-by: Steven Whitehouse --- fs/gfs2/quota.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'fs') diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index 49667d68769e..b256d6f24288 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -694,10 +694,8 @@ get_a_page: if (!buffer_mapped(bh)) goto unlock_out; /* If it's a newly allocated disk block for quota, zero it */ - if (buffer_new(bh)) { - memset(bh->b_data, 0, bh->b_size); - set_buffer_uptodate(bh); - } + if (buffer_new(bh)) + zero_user(page, pos - blocksize, bh->b_size); } if (PageUptodate(page)) @@ -723,7 +721,7 @@ get_a_page: /* If quota straddles page boundary, we need to update the rest of the * quota at the beginning of the next page */ - if (offset != 0) { /* first page, offset is closer to PAGE_CACHE_SIZE */ + if ((offset + sizeof(struct gfs2_quota)) > PAGE_CACHE_SIZE) { ptr = ptr + nbytes; nbytes = sizeof(struct gfs2_quota) - nbytes; offset = 0; -- cgit v1.2.3 From 728a756b8fcd22d80e2dbba8117a8a3aafd3f203 Mon Sep 17 00:00:00 2001 From: Bob Peterson Date: Wed, 14 Jul 2010 18:12:26 -0400 Subject: GFS2: rename causes kernel Oops This patch fixes a kernel Oops in the GFS2 rename code. The problem was in the way the gfs2 directory code was trying to re-use sentinel directory entries. In the failing case, gfs2's rename function was renaming a file to another name that had the same non-trivial length. The file being renamed happened to be the first directory entry on the leaf block. First, the rename code (gfs2_rename in ops_inode.c) found the original directory entry and decided it could do its job by simply replacing the directory entry with another. Therefore it determined correctly that no block allocations were needed. Next, the rename code deleted the old directory entry prior to replacing it with the new name. Therefore, the soon-to-be replaced directory entry was temporarily made into a directory entry "sentinel" or a place holder at the start of a leaf block. Lastly, it went to re-add the replacement directory entry in that leaf block. However, when gfs2_dirent_find_space was looking for space in the leaf block, it used the wrong value for the sentinel. That threw off its calculations so later it decides it can't really re-use the sentinel and therefore must allocate a new leaf block. But because it previously decided to re-use the directory entry, it didn't waste the time to grab a new block allocation for the inode. Therefore, the inode's i_alloc pointer was still NULL and it crashes trying to reference it. In the case of sentinel directory entries, the entire dirent is reused, not just the "free space" portion of it, and therefore the function gfs2_dirent_find_space should use the value 0 rather than GFS2_DIRENT_SIZE(0) for the actual dirent size. Fixing this calculation enables the reproducer programs to work properly. Signed-off-by: Bob Peterson Signed-off-by: Steven Whitehouse --- fs/gfs2/dir.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/gfs2/dir.c b/fs/gfs2/dir.c index 8295c5b5d4a9..26ca3361a8bc 100644 --- a/fs/gfs2/dir.c +++ b/fs/gfs2/dir.c @@ -392,7 +392,7 @@ static int gfs2_dirent_find_space(const struct gfs2_dirent *dent, unsigned totlen = be16_to_cpu(dent->de_rec_len); if (gfs2_dirent_sentinel(dent)) - actual = GFS2_DIRENT_SIZE(0); + actual = 0; if (totlen - actual >= required) return 1; return 0; -- cgit v1.2.3 From f5e27b6ddfbafdd9c9c2f06bbf28af12581409bc Mon Sep 17 00:00:00 2001 From: Tao Ma Date: Wed, 14 Jul 2010 11:19:32 +0800 Subject: ocfs2: Don't duplicate pages past i_size during CoW. During CoW, the pages after i_size don't contain valid data, so there's no need to read and duplicate them. Signed-off-by: Tao Ma Signed-off-by: Joel Becker --- fs/ocfs2/refcounttree.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'fs') diff --git a/fs/ocfs2/refcounttree.c b/fs/ocfs2/refcounttree.c index 32949df10694..3ac5aa733e9c 100644 --- a/fs/ocfs2/refcounttree.c +++ b/fs/ocfs2/refcounttree.c @@ -2931,6 +2931,12 @@ static int ocfs2_duplicate_clusters_by_page(handle_t *handle, offset = ((loff_t)cpos) << OCFS2_SB(sb)->s_clustersize_bits; end = offset + (new_len << OCFS2_SB(sb)->s_clustersize_bits); + /* + * We only duplicate pages until we reach the page contains i_size - 1. + * So trim 'end' to i_size. + */ + if (end > i_size_read(context->inode)) + end = i_size_read(context->inode); while (offset < end) { page_index = offset >> PAGE_CACHE_SHIFT; -- cgit v1.2.3 From a39953dd95ff10e311083d94f4f95c348cb22464 Mon Sep 17 00:00:00 2001 From: Wengang Wang Date: Wed, 14 Jul 2010 22:38:21 +0800 Subject: ocfs2/dlm: Remove BUG_ON from migration in the rare case of a down node For migration, we are waiting for DLM_LOCK_RES_MIGRATING flag to be set before sending DLM_MIG_LOCKRES_MSG message to the target. We are using dlm_migration_can_proceed() for that purpose. However, if the node is down, dlm_migration_can_proceed() will also return "go ahead". In this rare case, the DLM_LOCK_RES_MIGRATING flag might not be set yet. Remove the BUG_ON() that trips over this condition. Signed-off-by: Wengang Wang Signed-off-by: Joel Becker --- fs/ocfs2/dlm/dlmmaster.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) (limited to 'fs') diff --git a/fs/ocfs2/dlm/dlmmaster.c b/fs/ocfs2/dlm/dlmmaster.c index 4a7506a4e314..94b97fc6a88e 100644 --- a/fs/ocfs2/dlm/dlmmaster.c +++ b/fs/ocfs2/dlm/dlmmaster.c @@ -2808,14 +2808,8 @@ again: mlog(0, "trying again...\n"); goto again; } - /* now that we are sure the MIGRATING state is there, drop - * the unneded state which blocked threads trying to DIRTY */ - spin_lock(&res->spinlock); - BUG_ON(!(res->state & DLM_LOCK_RES_BLOCK_DIRTY)); - BUG_ON(!(res->state & DLM_LOCK_RES_MIGRATING)); - res->state &= ~DLM_LOCK_RES_BLOCK_DIRTY; - spin_unlock(&res->spinlock); + ret = 0; /* did the target go down or die? */ spin_lock(&dlm->spinlock); if (!test_bit(target, dlm->domain_map)) { @@ -2825,10 +2819,22 @@ again: } spin_unlock(&dlm->spinlock); + /* + * if target is down, we need to clear DLM_LOCK_RES_BLOCK_DIRTY for + * another try; otherwise, we are sure the MIGRATING state is there, + * drop the unneded state which blocked threads trying to DIRTY + */ + spin_lock(&res->spinlock); + BUG_ON(!(res->state & DLM_LOCK_RES_BLOCK_DIRTY)); + res->state &= ~DLM_LOCK_RES_BLOCK_DIRTY; + if (!ret) + BUG_ON(!(res->state & DLM_LOCK_RES_MIGRATING)); + spin_unlock(&res->spinlock); + /* * at this point: * - * o the DLM_LOCK_RES_MIGRATING flag is set + * o the DLM_LOCK_RES_MIGRATING flag is set if target not down * o there are no pending asts on this lockres * o all processes trying to reserve an ast on this * lockres must wait for the MIGRATING flag to clear -- cgit v1.2.3 From 13ceef099edd2b70c5a6f3a9ef5d6d97cda2e096 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Wed, 14 Jul 2010 07:56:33 +0200 Subject: jbd2/ocfs2: Fix block checksumming when a buffer is used in several transactions OCFS2 uses t_commit trigger to compute and store checksum of the just committed blocks. When a buffer has b_frozen_data, checksum is computed for it instead of b_data but this can result in an old checksum being written to the filesystem in the following scenario: 1) transaction1 is opened 2) handle1 is opened 3) journal_access(handle1, bh) - This sets jh->b_transaction to transaction1 4) modify(bh) 5) journal_dirty(handle1, bh) 6) handle1 is closed 7) start committing transaction1, opening transaction2 8) handle2 is opened 9) journal_access(handle2, bh) - This copies off b_frozen_data to make it safe for transaction1 to commit. jh->b_next_transaction is set to transaction2. 10) jbd2_journal_write_metadata() checksums b_frozen_data 11) the journal correctly writes b_frozen_data to the disk journal 12) handle2 is closed - There was no dirty call for the bh on handle2, so it is never queued for any more journal operation 13) Checkpointing finally happens, and it just spools the bh via normal buffer writeback. This will write b_data, which was never triggered on and thus contains a wrong (old) checksum. This patch fixes the problem by calling the trigger at the moment data is frozen for journal commit - i.e., either when b_frozen_data is created by do_get_write_access or just before we write a buffer to the log if b_frozen_data does not exist. We also rename the trigger to t_frozen as that better describes when it is called. Signed-off-by: Jan Kara Signed-off-by: Mark Fasheh Signed-off-by: Joel Becker --- fs/jbd2/journal.c | 15 +++++++-------- fs/jbd2/transaction.c | 9 ++++++--- fs/ocfs2/journal.c | 24 ++++++++++++------------ include/linux/jbd2.h | 11 ++++++----- 4 files changed, 31 insertions(+), 28 deletions(-) (limited to 'fs') diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c index bc2ff5932769..036880895bfc 100644 --- a/fs/jbd2/journal.c +++ b/fs/jbd2/journal.c @@ -297,7 +297,6 @@ int jbd2_journal_write_metadata_buffer(transaction_t *transaction, struct page *new_page; unsigned int new_offset; struct buffer_head *bh_in = jh2bh(jh_in); - struct jbd2_buffer_trigger_type *triggers; journal_t *journal = transaction->t_journal; /* @@ -328,21 +327,21 @@ repeat: done_copy_out = 1; new_page = virt_to_page(jh_in->b_frozen_data); new_offset = offset_in_page(jh_in->b_frozen_data); - triggers = jh_in->b_frozen_triggers; } else { new_page = jh2bh(jh_in)->b_page; new_offset = offset_in_page(jh2bh(jh_in)->b_data); - triggers = jh_in->b_triggers; } mapped_data = kmap_atomic(new_page, KM_USER0); /* - * Fire any commit trigger. Do this before checking for escaping, - * as the trigger may modify the magic offset. If a copy-out - * happens afterwards, it will have the correct data in the buffer. + * Fire data frozen trigger if data already wasn't frozen. Do this + * before checking for escaping, as the trigger may modify the magic + * offset. If a copy-out happens afterwards, it will have the correct + * data in the buffer. */ - jbd2_buffer_commit_trigger(jh_in, mapped_data + new_offset, - triggers); + if (!done_copy_out) + jbd2_buffer_frozen_trigger(jh_in, mapped_data + new_offset, + jh_in->b_triggers); /* * Check for escaping diff --git a/fs/jbd2/transaction.c b/fs/jbd2/transaction.c index e214d68620ac..b8e0806681bb 100644 --- a/fs/jbd2/transaction.c +++ b/fs/jbd2/transaction.c @@ -725,6 +725,9 @@ done: page = jh2bh(jh)->b_page; offset = ((unsigned long) jh2bh(jh)->b_data) & ~PAGE_MASK; source = kmap_atomic(page, KM_USER0); + /* Fire data frozen trigger just before we copy the data */ + jbd2_buffer_frozen_trigger(jh, source + offset, + jh->b_triggers); memcpy(jh->b_frozen_data, source+offset, jh2bh(jh)->b_size); kunmap_atomic(source, KM_USER0); @@ -963,15 +966,15 @@ void jbd2_journal_set_triggers(struct buffer_head *bh, jh->b_triggers = type; } -void jbd2_buffer_commit_trigger(struct journal_head *jh, void *mapped_data, +void jbd2_buffer_frozen_trigger(struct journal_head *jh, void *mapped_data, struct jbd2_buffer_trigger_type *triggers) { struct buffer_head *bh = jh2bh(jh); - if (!triggers || !triggers->t_commit) + if (!triggers || !triggers->t_frozen) return; - triggers->t_commit(triggers, bh, mapped_data, bh->b_size); + triggers->t_frozen(triggers, bh, mapped_data, bh->b_size); } void jbd2_buffer_abort_trigger(struct journal_head *jh, diff --git a/fs/ocfs2/journal.c b/fs/ocfs2/journal.c index 39113b5e79e7..625de9d7088c 100644 --- a/fs/ocfs2/journal.c +++ b/fs/ocfs2/journal.c @@ -472,7 +472,7 @@ static inline struct ocfs2_triggers *to_ocfs2_trigger(struct jbd2_buffer_trigger return container_of(triggers, struct ocfs2_triggers, ot_triggers); } -static void ocfs2_commit_trigger(struct jbd2_buffer_trigger_type *triggers, +static void ocfs2_frozen_trigger(struct jbd2_buffer_trigger_type *triggers, struct buffer_head *bh, void *data, size_t size) { @@ -491,7 +491,7 @@ static void ocfs2_commit_trigger(struct jbd2_buffer_trigger_type *triggers, * Quota blocks have their own trigger because the struct ocfs2_block_check * offset depends on the blocksize. */ -static void ocfs2_dq_commit_trigger(struct jbd2_buffer_trigger_type *triggers, +static void ocfs2_dq_frozen_trigger(struct jbd2_buffer_trigger_type *triggers, struct buffer_head *bh, void *data, size_t size) { @@ -511,7 +511,7 @@ static void ocfs2_dq_commit_trigger(struct jbd2_buffer_trigger_type *triggers, * Directory blocks also have their own trigger because the * struct ocfs2_block_check offset depends on the blocksize. */ -static void ocfs2_db_commit_trigger(struct jbd2_buffer_trigger_type *triggers, +static void ocfs2_db_frozen_trigger(struct jbd2_buffer_trigger_type *triggers, struct buffer_head *bh, void *data, size_t size) { @@ -544,7 +544,7 @@ static void ocfs2_abort_trigger(struct jbd2_buffer_trigger_type *triggers, static struct ocfs2_triggers di_triggers = { .ot_triggers = { - .t_commit = ocfs2_commit_trigger, + .t_frozen = ocfs2_frozen_trigger, .t_abort = ocfs2_abort_trigger, }, .ot_offset = offsetof(struct ocfs2_dinode, i_check), @@ -552,7 +552,7 @@ static struct ocfs2_triggers di_triggers = { static struct ocfs2_triggers eb_triggers = { .ot_triggers = { - .t_commit = ocfs2_commit_trigger, + .t_frozen = ocfs2_frozen_trigger, .t_abort = ocfs2_abort_trigger, }, .ot_offset = offsetof(struct ocfs2_extent_block, h_check), @@ -560,7 +560,7 @@ static struct ocfs2_triggers eb_triggers = { static struct ocfs2_triggers rb_triggers = { .ot_triggers = { - .t_commit = ocfs2_commit_trigger, + .t_frozen = ocfs2_frozen_trigger, .t_abort = ocfs2_abort_trigger, }, .ot_offset = offsetof(struct ocfs2_refcount_block, rf_check), @@ -568,7 +568,7 @@ static struct ocfs2_triggers rb_triggers = { static struct ocfs2_triggers gd_triggers = { .ot_triggers = { - .t_commit = ocfs2_commit_trigger, + .t_frozen = ocfs2_frozen_trigger, .t_abort = ocfs2_abort_trigger, }, .ot_offset = offsetof(struct ocfs2_group_desc, bg_check), @@ -576,14 +576,14 @@ static struct ocfs2_triggers gd_triggers = { static struct ocfs2_triggers db_triggers = { .ot_triggers = { - .t_commit = ocfs2_db_commit_trigger, + .t_frozen = ocfs2_db_frozen_trigger, .t_abort = ocfs2_abort_trigger, }, }; static struct ocfs2_triggers xb_triggers = { .ot_triggers = { - .t_commit = ocfs2_commit_trigger, + .t_frozen = ocfs2_frozen_trigger, .t_abort = ocfs2_abort_trigger, }, .ot_offset = offsetof(struct ocfs2_xattr_block, xb_check), @@ -591,14 +591,14 @@ static struct ocfs2_triggers xb_triggers = { static struct ocfs2_triggers dq_triggers = { .ot_triggers = { - .t_commit = ocfs2_dq_commit_trigger, + .t_frozen = ocfs2_dq_frozen_trigger, .t_abort = ocfs2_abort_trigger, }, }; static struct ocfs2_triggers dr_triggers = { .ot_triggers = { - .t_commit = ocfs2_commit_trigger, + .t_frozen = ocfs2_frozen_trigger, .t_abort = ocfs2_abort_trigger, }, .ot_offset = offsetof(struct ocfs2_dx_root_block, dr_check), @@ -606,7 +606,7 @@ static struct ocfs2_triggers dr_triggers = { static struct ocfs2_triggers dl_triggers = { .ot_triggers = { - .t_commit = ocfs2_commit_trigger, + .t_frozen = ocfs2_frozen_trigger, .t_abort = ocfs2_abort_trigger, }, .ot_offset = offsetof(struct ocfs2_dx_leaf, dl_check), diff --git a/include/linux/jbd2.h b/include/linux/jbd2.h index a4d2e9f7088a..adf832dec3f3 100644 --- a/include/linux/jbd2.h +++ b/include/linux/jbd2.h @@ -1026,11 +1026,12 @@ void __jbd2_journal_insert_checkpoint(struct journal_head *, transaction_t *); struct jbd2_buffer_trigger_type { /* - * Fired just before a buffer is written to the journal. - * mapped_data is a mapped buffer that is the frozen data for - * commit. + * Fired a the moment data to write to the journal are known to be + * stable - so either at the moment b_frozen_data is created or just + * before a buffer is written to the journal. mapped_data is a mapped + * buffer that is the frozen data for commit. */ - void (*t_commit)(struct jbd2_buffer_trigger_type *type, + void (*t_frozen)(struct jbd2_buffer_trigger_type *type, struct buffer_head *bh, void *mapped_data, size_t size); @@ -1042,7 +1043,7 @@ struct jbd2_buffer_trigger_type { struct buffer_head *bh); }; -extern void jbd2_buffer_commit_trigger(struct journal_head *jh, +extern void jbd2_buffer_frozen_trigger(struct journal_head *jh, void *mapped_data, struct jbd2_buffer_trigger_type *triggers); extern void jbd2_buffer_abort_trigger(struct journal_head *jh, -- cgit v1.2.3 From 01a92f174f8a3b99dbb5e02c86e7ee1e576737af Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Thu, 15 Jul 2010 13:24:32 -0700 Subject: ceph: reuse request message when replaying against recovering mds Replayed rename operations (after an mds failure/recovery) were broken because the request paths were regenerated from the dentry names, which get mangled when d_move() is called. Instead, resend the previous request message when replaying completed operations. Just make sure the REPLAY flag is set and the target ino is filled in. This fixes problems with workloads doing renames when the MDS restarts, where the rename operation appears to succeed, but on mds restart then fails (leading to client confusion, app breakage, etc.). Signed-off-by: Sage Weil --- fs/ceph/mds_client.c | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) (limited to 'fs') diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 3ab79f6c4ce8..23332bc44515 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -1580,6 +1580,27 @@ static int __prepare_send_request(struct ceph_mds_client *mdsc, dout("prepare_send_request %p tid %lld %s (attempt %d)\n", req, req->r_tid, ceph_mds_op_name(req->r_op), req->r_attempts); + if (req->r_got_unsafe) { + /* + * Replay. Do not regenerate message (and rebuild + * paths, etc.); just use the original message. + * Rebuilding paths will break for renames because + * d_move mangles the src name. + */ + msg = req->r_request; + rhead = msg->front.iov_base; + + flags = le32_to_cpu(rhead->flags); + flags |= CEPH_MDS_FLAG_REPLAY; + rhead->flags = cpu_to_le32(flags); + + if (req->r_target_inode) + rhead->ino = cpu_to_le64(ceph_ino(req->r_target_inode)); + + rhead->num_retry = req->r_attempts - 1; + return 0; + } + if (req->r_request) { ceph_msg_put(req->r_request); req->r_request = NULL; @@ -1601,13 +1622,9 @@ static int __prepare_send_request(struct ceph_mds_client *mdsc, rhead->flags = cpu_to_le32(flags); rhead->num_fwd = req->r_num_fwd; rhead->num_retry = req->r_attempts - 1; + rhead->ino = 0; dout(" r_locked_dir = %p\n", req->r_locked_dir); - - if (req->r_target_inode && req->r_got_unsafe) - rhead->ino = cpu_to_le64(ceph_ino(req->r_target_inode)); - else - rhead->ino = 0; return 0; } -- cgit v1.2.3 From e979cf50395e24c4bdd489f60e2d5dd5ae66d255 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Thu, 15 Jul 2010 14:58:39 -0700 Subject: ceph: do not include cap/dentry releases in replayed messages Strip the cap and dentry releases from replayed messages. They can cause the shared state to get out of sync because they were generated (with the request message) earlier, and no longer reflect the current client state. Signed-off-by: Sage Weil --- fs/ceph/mds_client.c | 8 ++++++++ fs/ceph/mds_client.h | 1 + 2 files changed, 9 insertions(+) (limited to 'fs') diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 23332bc44515..416c08d315db 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -1514,6 +1514,9 @@ static struct ceph_msg *create_request_message(struct ceph_mds_client *mdsc, ceph_encode_filepath(&p, end, ino1, path1); ceph_encode_filepath(&p, end, ino2, path2); + /* make note of release offset, in case we need to replay */ + req->r_request_release_offset = p - msg->front.iov_base; + /* cap releases */ releases = 0; if (req->r_inode_drop) @@ -1598,6 +1601,11 @@ static int __prepare_send_request(struct ceph_mds_client *mdsc, rhead->ino = cpu_to_le64(ceph_ino(req->r_target_inode)); rhead->num_retry = req->r_attempts - 1; + + /* remove cap/dentry releases from message */ + rhead->num_releases = 0; + msg->hdr.front_len = cpu_to_le32(req->r_request_release_offset); + msg->front.iov_len = req->r_request_release_offset; return 0; } diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h index b292fa42a66d..952410c60d09 100644 --- a/fs/ceph/mds_client.h +++ b/fs/ceph/mds_client.h @@ -188,6 +188,7 @@ struct ceph_mds_request { int r_old_inode_drop, r_old_inode_unless; struct ceph_msg *r_request; /* original request */ + int r_request_release_offset; struct ceph_msg *r_reply; struct ceph_mds_reply_info_parsed r_reply_info; int r_err; -- cgit v1.2.3 From 5453258d532e72731b0829e4fefd36dd611a2fff Mon Sep 17 00:00:00 2001 From: Joel Becker Date: Fri, 16 Jul 2010 13:32:33 -0700 Subject: ocfs2: Silence gcc warning in ocfs2_write_zero_page(). ocfs2_write_zero_page() has a loop that won't ever be skipped, but gcc doesn't know that. Set ret=0 just to make gcc happy. Signed-off-by: Joel Becker --- fs/ocfs2/file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index ac15911b31c4..2b10b36d1577 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c @@ -767,7 +767,7 @@ static int ocfs2_write_zero_page(struct inode *inode, u64 abs_from, struct page *page; unsigned long index = abs_from >> PAGE_CACHE_SHIFT; handle_t *handle = NULL; - int ret; + int ret = 0; unsigned zero_from, zero_to, block_start, block_end; BUG_ON(abs_from >= abs_to); -- cgit v1.2.3 From 7f8275d0d660c146de6ee3017e1e2e594c49e820 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Mon, 19 Jul 2010 14:56:17 +1000 Subject: mm: add context argument to shrinker callback The current shrinker implementation requires the registered callback to have global state to work from. This makes it difficult to shrink caches that are not global (e.g. per-filesystem caches). Pass the shrinker structure to the callback so that users can embed the shrinker structure in the context the shrinker needs to operate on and get back to it in the callback via container_of(). Signed-off-by: Dave Chinner Reviewed-by: Christoph Hellwig --- arch/x86/kvm/mmu.c | 2 +- drivers/gpu/drm/i915/i915_gem.c | 2 +- fs/dcache.c | 2 +- fs/gfs2/glock.c | 2 +- fs/gfs2/quota.c | 2 +- fs/gfs2/quota.h | 2 +- fs/inode.c | 2 +- fs/mbcache.c | 5 +++-- fs/nfs/dir.c | 2 +- fs/nfs/internal.h | 3 ++- fs/quota/dquot.c | 2 +- fs/ubifs/shrinker.c | 2 +- fs/ubifs/ubifs.h | 2 +- fs/xfs/linux-2.6/xfs_buf.c | 5 +++-- fs/xfs/linux-2.6/xfs_sync.c | 1 + fs/xfs/quota/xfs_qm.c | 7 +++++-- include/linux/mm.h | 2 +- mm/vmscan.c | 8 +++++--- 18 files changed, 31 insertions(+), 22 deletions(-) (limited to 'fs') diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c index 3699613e8830..b1ed0a1a5913 100644 --- a/arch/x86/kvm/mmu.c +++ b/arch/x86/kvm/mmu.c @@ -2926,7 +2926,7 @@ static int kvm_mmu_remove_some_alloc_mmu_pages(struct kvm *kvm) return kvm_mmu_zap_page(kvm, page) + 1; } -static int mmu_shrink(int nr_to_scan, gfp_t gfp_mask) +static int mmu_shrink(struct shrinker *shrink, int nr_to_scan, gfp_t gfp_mask) { struct kvm *kvm; struct kvm *kvm_freed = NULL; diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 8757ecf6e96b..e7018708cc31 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -4978,7 +4978,7 @@ i915_gpu_is_active(struct drm_device *dev) } static int -i915_gem_shrink(int nr_to_scan, gfp_t gfp_mask) +i915_gem_shrink(struct shrinker *shrink, int nr_to_scan, gfp_t gfp_mask) { drm_i915_private_t *dev_priv, *next_dev; struct drm_i915_gem_object *obj_priv, *next_obj; diff --git a/fs/dcache.c b/fs/dcache.c index c8c78ba07827..86d4db15473e 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -896,7 +896,7 @@ EXPORT_SYMBOL(shrink_dcache_parent); * * In this case we return -1 to tell the caller that we baled. */ -static int shrink_dcache_memory(int nr, gfp_t gfp_mask) +static int shrink_dcache_memory(struct shrinker *shrink, int nr, gfp_t gfp_mask) { if (nr) { if (!(gfp_mask & __GFP_FS)) diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c index dbab3fdc2582..0898f3ec8212 100644 --- a/fs/gfs2/glock.c +++ b/fs/gfs2/glock.c @@ -1358,7 +1358,7 @@ void gfs2_glock_complete(struct gfs2_glock *gl, int ret) } -static int gfs2_shrink_glock_memory(int nr, gfp_t gfp_mask) +static int gfs2_shrink_glock_memory(struct shrinker *shrink, int nr, gfp_t gfp_mask) { struct gfs2_glock *gl; int may_demote; diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index b256d6f24288..8f02d3db8f42 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -77,7 +77,7 @@ static LIST_HEAD(qd_lru_list); static atomic_t qd_lru_count = ATOMIC_INIT(0); static DEFINE_SPINLOCK(qd_lru_lock); -int gfs2_shrink_qd_memory(int nr, gfp_t gfp_mask) +int gfs2_shrink_qd_memory(struct shrinker *shrink, int nr, gfp_t gfp_mask) { struct gfs2_quota_data *qd; struct gfs2_sbd *sdp; diff --git a/fs/gfs2/quota.h b/fs/gfs2/quota.h index 195f60c8bd14..e7d236ca48bd 100644 --- a/fs/gfs2/quota.h +++ b/fs/gfs2/quota.h @@ -51,7 +51,7 @@ static inline int gfs2_quota_lock_check(struct gfs2_inode *ip) return ret; } -extern int gfs2_shrink_qd_memory(int nr, gfp_t gfp_mask); +extern int gfs2_shrink_qd_memory(struct shrinker *shrink, int nr, gfp_t gfp_mask); extern const struct quotactl_ops gfs2_quotactl_ops; #endif /* __QUOTA_DOT_H__ */ diff --git a/fs/inode.c b/fs/inode.c index 2bee20ae3d65..722860b323a9 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -512,7 +512,7 @@ static void prune_icache(int nr_to_scan) * This function is passed the number of inodes to scan, and it returns the * total number of remaining possibly-reclaimable inodes. */ -static int shrink_icache_memory(int nr, gfp_t gfp_mask) +static int shrink_icache_memory(struct shrinker *shrink, int nr, gfp_t gfp_mask) { if (nr) { /* diff --git a/fs/mbcache.c b/fs/mbcache.c index ec88ff3d04a9..e28f21b95344 100644 --- a/fs/mbcache.c +++ b/fs/mbcache.c @@ -115,7 +115,7 @@ mb_cache_indexes(struct mb_cache *cache) * What the mbcache registers as to get shrunk dynamically. */ -static int mb_cache_shrink_fn(int nr_to_scan, gfp_t gfp_mask); +static int mb_cache_shrink_fn(struct shrinker *shrink, int nr_to_scan, gfp_t gfp_mask); static struct shrinker mb_cache_shrinker = { .shrink = mb_cache_shrink_fn, @@ -191,13 +191,14 @@ forget: * This function is called by the kernel memory management when memory * gets low. * + * @shrink: (ignored) * @nr_to_scan: Number of objects to scan * @gfp_mask: (ignored) * * Returns the number of objects which are present in the cache. */ static int -mb_cache_shrink_fn(int nr_to_scan, gfp_t gfp_mask) +mb_cache_shrink_fn(struct shrinker *shrink, int nr_to_scan, gfp_t gfp_mask) { LIST_HEAD(free_list); struct list_head *l, *ltmp; diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 782b431ef91c..e60416d3f818 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -1710,7 +1710,7 @@ static void nfs_access_free_list(struct list_head *head) } } -int nfs_access_cache_shrinker(int nr_to_scan, gfp_t gfp_mask) +int nfs_access_cache_shrinker(struct shrinker *shrink, int nr_to_scan, gfp_t gfp_mask) { LIST_HEAD(head); struct nfs_inode *nfsi; diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index d8bd619e386c..e70f44b9b3f4 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -205,7 +205,8 @@ extern struct rpc_procinfo nfs4_procedures[]; void nfs_close_context(struct nfs_open_context *ctx, int is_sync); /* dir.c */ -extern int nfs_access_cache_shrinker(int nr_to_scan, gfp_t gfp_mask); +extern int nfs_access_cache_shrinker(struct shrinker *shrink, + int nr_to_scan, gfp_t gfp_mask); /* inode.c */ extern struct workqueue_struct *nfsiod_workqueue; diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c index 12c233da1b6b..437d2ca2de97 100644 --- a/fs/quota/dquot.c +++ b/fs/quota/dquot.c @@ -676,7 +676,7 @@ static void prune_dqcache(int count) * This is called from kswapd when we think we need some * more memory */ -static int shrink_dqcache_memory(int nr, gfp_t gfp_mask) +static int shrink_dqcache_memory(struct shrinker *shrink, int nr, gfp_t gfp_mask) { if (nr) { spin_lock(&dq_list_lock); diff --git a/fs/ubifs/shrinker.c b/fs/ubifs/shrinker.c index 02feb59cefca..0b201114a5ad 100644 --- a/fs/ubifs/shrinker.c +++ b/fs/ubifs/shrinker.c @@ -277,7 +277,7 @@ static int kick_a_thread(void) return 0; } -int ubifs_shrinker(int nr, gfp_t gfp_mask) +int ubifs_shrinker(struct shrinker *shrink, int nr, gfp_t gfp_mask) { int freed, contention = 0; long clean_zn_cnt = atomic_long_read(&ubifs_clean_zn_cnt); diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h index 2eef553d50c8..04310878f449 100644 --- a/fs/ubifs/ubifs.h +++ b/fs/ubifs/ubifs.h @@ -1575,7 +1575,7 @@ int ubifs_tnc_start_commit(struct ubifs_info *c, struct ubifs_zbranch *zroot); int ubifs_tnc_end_commit(struct ubifs_info *c); /* shrinker.c */ -int ubifs_shrinker(int nr_to_scan, gfp_t gfp_mask); +int ubifs_shrinker(struct shrinker *shrink, int nr_to_scan, gfp_t gfp_mask); /* commit.c */ int ubifs_bg_thread(void *info); diff --git a/fs/xfs/linux-2.6/xfs_buf.c b/fs/xfs/linux-2.6/xfs_buf.c index 649ade8ef598..2ee3f7a60163 100644 --- a/fs/xfs/linux-2.6/xfs_buf.c +++ b/fs/xfs/linux-2.6/xfs_buf.c @@ -45,7 +45,7 @@ static kmem_zone_t *xfs_buf_zone; STATIC int xfsbufd(void *); -STATIC int xfsbufd_wakeup(int, gfp_t); +STATIC int xfsbufd_wakeup(struct shrinker *, int, gfp_t); STATIC void xfs_buf_delwri_queue(xfs_buf_t *, int); static struct shrinker xfs_buf_shake = { .shrink = xfsbufd_wakeup, @@ -340,7 +340,7 @@ _xfs_buf_lookup_pages( __func__, gfp_mask); XFS_STATS_INC(xb_page_retries); - xfsbufd_wakeup(0, gfp_mask); + xfsbufd_wakeup(NULL, 0, gfp_mask); congestion_wait(BLK_RW_ASYNC, HZ/50); goto retry; } @@ -1762,6 +1762,7 @@ xfs_buf_runall_queues( STATIC int xfsbufd_wakeup( + struct shrinker *shrink, int priority, gfp_t mask) { diff --git a/fs/xfs/linux-2.6/xfs_sync.c b/fs/xfs/linux-2.6/xfs_sync.c index ef7f0218bccb..be375827af98 100644 --- a/fs/xfs/linux-2.6/xfs_sync.c +++ b/fs/xfs/linux-2.6/xfs_sync.c @@ -838,6 +838,7 @@ static struct rw_semaphore xfs_mount_list_lock; static int xfs_reclaim_inode_shrink( + struct shrinker *shrink, int nr_to_scan, gfp_t gfp_mask) { diff --git a/fs/xfs/quota/xfs_qm.c b/fs/xfs/quota/xfs_qm.c index 8c117ff2e3ab..67c018392d62 100644 --- a/fs/xfs/quota/xfs_qm.c +++ b/fs/xfs/quota/xfs_qm.c @@ -69,7 +69,7 @@ STATIC void xfs_qm_list_destroy(xfs_dqlist_t *); STATIC int xfs_qm_init_quotainos(xfs_mount_t *); STATIC int xfs_qm_init_quotainfo(xfs_mount_t *); -STATIC int xfs_qm_shake(int, gfp_t); +STATIC int xfs_qm_shake(struct shrinker *, int, gfp_t); static struct shrinker xfs_qm_shaker = { .shrink = xfs_qm_shake, @@ -2117,7 +2117,10 @@ xfs_qm_shake_freelist( */ /* ARGSUSED */ STATIC int -xfs_qm_shake(int nr_to_scan, gfp_t gfp_mask) +xfs_qm_shake( + struct shrinker *shrink, + int nr_to_scan, + gfp_t gfp_mask) { int ndqused, nfree, n; diff --git a/include/linux/mm.h b/include/linux/mm.h index b969efb03787..a2b48041b910 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -999,7 +999,7 @@ static inline void sync_mm_rss(struct task_struct *task, struct mm_struct *mm) * querying the cache size, so a fastpath for that case is appropriate. */ struct shrinker { - int (*shrink)(int nr_to_scan, gfp_t gfp_mask); + int (*shrink)(struct shrinker *, int nr_to_scan, gfp_t gfp_mask); int seeks; /* seeks to recreate an obj */ /* These are for internal use */ diff --git a/mm/vmscan.c b/mm/vmscan.c index 9c7e57cc63a3..199fa436c0dd 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -213,8 +213,9 @@ unsigned long shrink_slab(unsigned long scanned, gfp_t gfp_mask, list_for_each_entry(shrinker, &shrinker_list, list) { unsigned long long delta; unsigned long total_scan; - unsigned long max_pass = (*shrinker->shrink)(0, gfp_mask); + unsigned long max_pass; + max_pass = (*shrinker->shrink)(shrinker, 0, gfp_mask); delta = (4 * scanned) / shrinker->seeks; delta *= max_pass; do_div(delta, lru_pages + 1); @@ -242,8 +243,9 @@ unsigned long shrink_slab(unsigned long scanned, gfp_t gfp_mask, int shrink_ret; int nr_before; - nr_before = (*shrinker->shrink)(0, gfp_mask); - shrink_ret = (*shrinker->shrink)(this_scan, gfp_mask); + nr_before = (*shrinker->shrink)(shrinker, 0, gfp_mask); + shrink_ret = (*shrinker->shrink)(shrinker, this_scan, + gfp_mask); if (shrink_ret == -1) break; if (shrink_ret < nr_before) -- cgit v1.2.3 From cffab6bc5511cd6f67a60bf16b62de4267b68c4c Mon Sep 17 00:00:00 2001 From: Peter Oberparleiter Date: Mon, 19 Jul 2010 09:22:35 +0200 Subject: [S390] dasd: use correct label location for diag fba disks Partition boundary calculation fails for DASD FBA disks under the following conditions: - disk is formatted with CMS FORMAT with a blocksize of more than 512 bytes - all of the disk is reserved to a single CMS file using CMS RESERVE - the disk is accessed using the DIAG mode of the DASD driver Under these circumstances, the partition detection code tries to read the CMS label block containing partition-relevant information from logical block offset 1, while it is in fact located at physical block offset 1. Fix this problem by using the correct CMS label block location depending on the device type as determined by the DASD SENSE ID information. Signed-off-by: Peter Oberparleiter Signed-off-by: Martin Schwidefsky --- fs/partitions/ibm.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/partitions/ibm.c b/fs/partitions/ibm.c index 3e73de5967ff..fc8497643fd0 100644 --- a/fs/partitions/ibm.c +++ b/fs/partitions/ibm.c @@ -74,6 +74,7 @@ int ibm_partition(struct parsed_partitions *state) } *label; unsigned char *data; Sector sect; + sector_t labelsect; res = 0; blocksize = bdev_logical_block_size(bdev); @@ -97,11 +98,20 @@ int ibm_partition(struct parsed_partitions *state) ioctl_by_bdev(bdev, HDIO_GETGEO, (unsigned long)geo) != 0) goto out_freeall; + /* + * Special case for FBA disks: label sector does not depend on + * blocksize. + */ + if ((info->cu_type == 0x6310 && info->dev_type == 0x9336) || + (info->cu_type == 0x3880 && info->dev_type == 0x3370)) + labelsect = info->label_block; + else + labelsect = info->label_block * (blocksize >> 9); + /* * Get volume label, extract name and type. */ - data = read_part_sector(state, info->label_block*(blocksize/512), - §); + data = read_part_sector(state, labelsect, §); if (data == NULL) goto out_readerr; -- cgit v1.2.3 From a2531293dbb7608fa672ff28efe3ab4027917a2f Mon Sep 17 00:00:00 2001 From: Pavel Machek Date: Sun, 18 Jul 2010 14:27:13 +0200 Subject: update email address pavel@suse.cz no longer works, replace it with working address. Signed-off-by: Pavel Machek Signed-off-by: Jiri Kosina --- Documentation/feature-removal-schedule.txt | 2 +- Documentation/hwmon/hpfall.c | 2 +- Documentation/power/tricks.txt | 2 +- Documentation/sparse.txt | 2 +- Documentation/zh_CN/sparse.txt | 2 +- arch/arm/mach-sa1100/collie.c | 2 +- arch/powerpc/kernel/suspend.c | 2 +- arch/x86/kernel/acpi/sleep.c | 2 +- arch/x86/kernel/apm_32.c | 2 +- arch/x86/kernel/cpu/cpufreq/powernow-k8.c | 2 +- arch/x86/mm/init_64.c | 2 +- arch/x86/power/cpu.c | 2 +- arch/x86/power/hibernate_64.c | 2 +- drivers/block/nbd.c | 2 +- drivers/media/video/usbvideo/vicam.c | 2 +- drivers/media/video/v4l2-compat-ioctl32.c | 2 +- drivers/staging/winbond/wbusb.c | 2 +- drivers/usb/class/cdc-acm.c | 2 +- drivers/usb/class/usblp.c | 2 +- drivers/video/backlight/locomolcd.c | 4 ++-- fs/compat.c | 2 +- fs/compat_ioctl.c | 2 +- kernel/debug/debug_core.c | 2 +- kernel/debug/gdbstub.c | 2 +- kernel/power/hibernate.c | 2 +- kernel/power/snapshot.c | 2 +- kernel/power/swap.c | 2 +- 27 files changed, 28 insertions(+), 28 deletions(-) (limited to 'fs') diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index c268783bc4e7..1a0fc32bc205 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -93,7 +93,7 @@ Why: Broken design for runtime control over driver power states, confusing inputs. This framework was never widely used, and most attempts to use it were broken. Drivers should instead be exposing domain-specific interfaces either to kernel or to userspace. -Who: Pavel Machek +Who: Pavel Machek --------------------------- diff --git a/Documentation/hwmon/hpfall.c b/Documentation/hwmon/hpfall.c index 681ec22b9d0e..a4a8fc5d05d4 100644 --- a/Documentation/hwmon/hpfall.c +++ b/Documentation/hwmon/hpfall.c @@ -1,7 +1,7 @@ /* Disk protection for HP machines. * * Copyright 2008 Eric Piel - * Copyright 2009 Pavel Machek + * Copyright 2009 Pavel Machek * * GPLv2. */ diff --git a/Documentation/power/tricks.txt b/Documentation/power/tricks.txt index 3b26bb502a4a..a1b8f7249f4c 100644 --- a/Documentation/power/tricks.txt +++ b/Documentation/power/tricks.txt @@ -1,6 +1,6 @@ swsusp/S3 tricks ~~~~~~~~~~~~~~~~ -Pavel Machek +Pavel Machek If you want to trick swsusp/S3 into working, you might want to try: diff --git a/Documentation/sparse.txt b/Documentation/sparse.txt index 9b659c79a547..4909d4116356 100644 --- a/Documentation/sparse.txt +++ b/Documentation/sparse.txt @@ -1,5 +1,5 @@ Copyright 2004 Linus Torvalds -Copyright 2004 Pavel Machek +Copyright 2004 Pavel Machek Copyright 2006 Bob Copeland Using sparse for typechecking diff --git a/Documentation/zh_CN/sparse.txt b/Documentation/zh_CN/sparse.txt index 75992a603ae3..cc144e581515 100644 --- a/Documentation/zh_CN/sparse.txt +++ b/Documentation/zh_CN/sparse.txt @@ -22,7 +22,7 @@ Documentation/sparse.txt 的中文翻译 --------------------------------------------------------------------- Copyright 2004 Linus Torvalds -Copyright 2004 Pavel Machek +Copyright 2004 Pavel Machek Copyright 2006 Bob Copeland 使用 sparse 工具做类型检查 diff --git a/arch/arm/mach-sa1100/collie.c b/arch/arm/mach-sa1100/collie.c index 5d5f330c5d94..16e682d5dbb7 100644 --- a/arch/arm/mach-sa1100/collie.c +++ b/arch/arm/mach-sa1100/collie.c @@ -11,7 +11,7 @@ * published by the Free Software Foundation. * * ChangeLog: - * 2006 Pavel Machek + * 2006 Pavel Machek * 03-06-2004 John Lenz * 06-04-2002 Chris Larson * 04-16-2001 Lineo Japan,Inc. ... diff --git a/arch/powerpc/kernel/suspend.c b/arch/powerpc/kernel/suspend.c index 6fc6328dc626..0167d53da30c 100644 --- a/arch/powerpc/kernel/suspend.c +++ b/arch/powerpc/kernel/suspend.c @@ -3,7 +3,7 @@ * * Distribute under GPLv2 * - * Copyright (c) 2002 Pavel Machek + * Copyright (c) 2002 Pavel Machek * Copyright (c) 2001 Patrick Mochel */ diff --git a/arch/x86/kernel/acpi/sleep.c b/arch/x86/kernel/acpi/sleep.c index 82e508677b91..f51cc55aced6 100644 --- a/arch/x86/kernel/acpi/sleep.c +++ b/arch/x86/kernel/acpi/sleep.c @@ -2,7 +2,7 @@ * sleep.c - x86-specific ACPI sleep support. * * Copyright (C) 2001-2003 Patrick Mochel - * Copyright (C) 2001-2003 Pavel Machek + * Copyright (C) 2001-2003 Pavel Machek */ #include diff --git a/arch/x86/kernel/apm_32.c b/arch/x86/kernel/apm_32.c index c4f9182ca3ac..4c9c67bf09b7 100644 --- a/arch/x86/kernel/apm_32.c +++ b/arch/x86/kernel/apm_32.c @@ -140,7 +140,7 @@ * is now the way life works). * Fix thinko in suspend() (wrong return). * Notify drivers on critical suspend. - * Make kapmd absorb more idle time (Pavel Machek + * Make kapmd absorb more idle time (Pavel Machek * modified by sfr). * Disable interrupts while we are suspended (Andy Henroid * fixed by sfr). diff --git a/arch/x86/kernel/cpu/cpufreq/powernow-k8.c b/arch/x86/kernel/cpu/cpufreq/powernow-k8.c index 7ec2123838e6..0af9aa20fce1 100644 --- a/arch/x86/kernel/cpu/cpufreq/powernow-k8.c +++ b/arch/x86/kernel/cpu/cpufreq/powernow-k8.c @@ -9,7 +9,7 @@ * Based on the powernow-k7.c module written by Dave Jones. * (C) 2003 Dave Jones on behalf of SuSE Labs * (C) 2004 Dominik Brodowski - * (C) 2004 Pavel Machek + * (C) 2004 Pavel Machek * Licensed under the terms of the GNU GPL License version 2. * Based upon datasheets & sample CPUs kindly provided by AMD. * diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c index ee41bba315d1..9a6674689a20 100644 --- a/arch/x86/mm/init_64.c +++ b/arch/x86/mm/init_64.c @@ -2,7 +2,7 @@ * linux/arch/x86_64/mm/init.c * * Copyright (C) 1995 Linus Torvalds - * Copyright (C) 2000 Pavel Machek + * Copyright (C) 2000 Pavel Machek * Copyright (C) 2002,2003 Andi Kleen */ diff --git a/arch/x86/power/cpu.c b/arch/x86/power/cpu.c index 1290ba54b350..e7e8c5f54956 100644 --- a/arch/x86/power/cpu.c +++ b/arch/x86/power/cpu.c @@ -4,7 +4,7 @@ * Distribute under GPLv2 * * Copyright (c) 2007 Rafael J. Wysocki - * Copyright (c) 2002 Pavel Machek + * Copyright (c) 2002 Pavel Machek * Copyright (c) 2001 Patrick Mochel */ diff --git a/arch/x86/power/hibernate_64.c b/arch/x86/power/hibernate_64.c index d24f983ba1e5..460f314d13e5 100644 --- a/arch/x86/power/hibernate_64.c +++ b/arch/x86/power/hibernate_64.c @@ -4,7 +4,7 @@ * Distribute under GPLv2 * * Copyright (c) 2007 Rafael J. Wysocki - * Copyright (c) 2002 Pavel Machek + * Copyright (c) 2002 Pavel Machek * Copyright (c) 2001 Patrick Mochel */ diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index 218d091f3c52..16c3c8613cd3 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -4,7 +4,7 @@ * Note that you can not swap over this thing, yet. Seems to work but * deadlocks sometimes - you can not swap over TCP in general. * - * Copyright 1997-2000, 2008 Pavel Machek + * Copyright 1997-2000, 2008 Pavel Machek * Parts copyright 2001 Steven Whitehouse * * This file is released under GPLv2 or later. diff --git a/drivers/media/video/usbvideo/vicam.c b/drivers/media/video/usbvideo/vicam.c index 6030410c6677..5d6fd01f918a 100644 --- a/drivers/media/video/usbvideo/vicam.c +++ b/drivers/media/video/usbvideo/vicam.c @@ -2,7 +2,7 @@ * USB ViCam WebCam driver * Copyright (c) 2002 Joe Burks (jburks@wavicle.org), * Christopher L Cheney (ccheney@cheney.cx), - * Pavel Machek (pavel@suse.cz), + * Pavel Machek (pavel@ucw.cz), * John Tyner (jtyner@cs.ucr.edu), * Monroe Williams (monroe@pobox.com) * diff --git a/drivers/media/video/v4l2-compat-ioctl32.c b/drivers/media/video/v4l2-compat-ioctl32.c index 9004a5fe7643..d2f20c2acae2 100644 --- a/drivers/media/video/v4l2-compat-ioctl32.c +++ b/drivers/media/video/v4l2-compat-ioctl32.c @@ -5,7 +5,7 @@ * Copyright (C) 1997-2000 Jakub Jelinek (jakub@redhat.com) * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be) * Copyright (C) 2001,2002 Andi Kleen, SuSE Labs - * Copyright (C) 2003 Pavel Machek (pavel@suse.cz) + * Copyright (C) 2003 Pavel Machek (pavel@ucw.cz) * Copyright (C) 2005 Philippe De Muyter (phdm@macqel.be) * Copyright (C) 2008 Hans Verkuil * diff --git a/drivers/staging/winbond/wbusb.c b/drivers/staging/winbond/wbusb.c index 681419d6856e..251caa052eee 100644 --- a/drivers/staging/winbond/wbusb.c +++ b/drivers/staging/winbond/wbusb.c @@ -1,5 +1,5 @@ /* - * Copyright 2008 Pavel Machek + * Copyright 2008 Pavel Machek * * Distribute under GPLv2. * diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 61d75507d5d0..8413a567c12d 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -2,7 +2,7 @@ * cdc-acm.c * * Copyright (c) 1999 Armin Fuerst - * Copyright (c) 1999 Pavel Machek + * Copyright (c) 1999 Pavel Machek * Copyright (c) 1999 Johannes Erdfelt * Copyright (c) 2000 Vojtech Pavlik * Copyright (c) 2004 Oliver Neukum diff --git a/drivers/usb/class/usblp.c b/drivers/usb/class/usblp.c index 2250095db0a0..84f9e52327f2 100644 --- a/drivers/usb/class/usblp.c +++ b/drivers/usb/class/usblp.c @@ -2,7 +2,7 @@ * usblp.c * * Copyright (c) 1999 Michael Gee - * Copyright (c) 1999 Pavel Machek + * Copyright (c) 1999 Pavel Machek * Copyright (c) 2000 Randy Dunlap * Copyright (c) 2000 Vojtech Pavlik # Copyright (c) 2001 Pete Zaitcev diff --git a/drivers/video/backlight/locomolcd.c b/drivers/video/backlight/locomolcd.c index 7571bc26071e..d2f59015d517 100644 --- a/drivers/video/backlight/locomolcd.c +++ b/drivers/video/backlight/locomolcd.c @@ -2,7 +2,7 @@ * Backlight control code for Sharp Zaurus SL-5500 * * Copyright 2005 John Lenz - * Maintainer: Pavel Machek (unless John wants to :-) + * Maintainer: Pavel Machek (unless John wants to :-) * GPL v2 * * This driver assumes single CPU. That's okay, because collie is @@ -246,6 +246,6 @@ static void __exit locomolcd_exit(void) module_init(locomolcd_init); module_exit(locomolcd_exit); -MODULE_AUTHOR("John Lenz , Pavel Machek "); +MODULE_AUTHOR("John Lenz , Pavel Machek "); MODULE_DESCRIPTION("Collie LCD driver"); MODULE_LICENSE("GPL"); diff --git a/fs/compat.c b/fs/compat.c index 6490d2134ff3..c6fda9aeb864 100644 --- a/fs/compat.c +++ b/fs/compat.c @@ -8,7 +8,7 @@ * Copyright (C) 1997-2000 Jakub Jelinek (jakub@redhat.com) * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be) * Copyright (C) 2001,2002 Andi Kleen, SuSE Labs - * Copyright (C) 2003 Pavel Machek (pavel@suse.cz) + * Copyright (C) 2003 Pavel Machek (pavel@ucw.cz) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c index 641640dc7ae5..5ead3763bba5 100644 --- a/fs/compat_ioctl.c +++ b/fs/compat_ioctl.c @@ -4,7 +4,7 @@ * Copyright (C) 1997-2000 Jakub Jelinek (jakub@redhat.com) * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be) * Copyright (C) 2001,2002 Andi Kleen, SuSE Labs - * Copyright (C) 2003 Pavel Machek (pavel@suse.cz) + * Copyright (C) 2003 Pavel Machek (pavel@ucw.cz) * * These routines maintain argument size conversion between 32bit and 64bit * ioctls. diff --git a/kernel/debug/debug_core.c b/kernel/debug/debug_core.c index 5cb7cd1de10c..568efbce80f7 100644 --- a/kernel/debug/debug_core.c +++ b/kernel/debug/debug_core.c @@ -6,7 +6,7 @@ * Copyright (C) 2000-2001 VERITAS Software Corporation. * Copyright (C) 2002-2004 Timesys Corporation * Copyright (C) 2003-2004 Amit S. Kale - * Copyright (C) 2004 Pavel Machek + * Copyright (C) 2004 Pavel Machek * Copyright (C) 2004-2006 Tom Rini * Copyright (C) 2004-2006 LinSysSoft Technologies Pvt. Ltd. * Copyright (C) 2005-2009 Wind River Systems, Inc. diff --git a/kernel/debug/gdbstub.c b/kernel/debug/gdbstub.c index 4b17b3269525..4e584721bcbb 100644 --- a/kernel/debug/gdbstub.c +++ b/kernel/debug/gdbstub.c @@ -6,7 +6,7 @@ * Copyright (C) 2000-2001 VERITAS Software Corporation. * Copyright (C) 2002-2004 Timesys Corporation * Copyright (C) 2003-2004 Amit S. Kale - * Copyright (C) 2004 Pavel Machek + * Copyright (C) 2004 Pavel Machek * Copyright (C) 2004-2006 Tom Rini * Copyright (C) 2004-2006 LinSysSoft Technologies Pvt. Ltd. * Copyright (C) 2005-2009 Wind River Systems, Inc. diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c index aa9e916da4d5..6b202e7f8b53 100644 --- a/kernel/power/hibernate.c +++ b/kernel/power/hibernate.c @@ -3,7 +3,7 @@ * * Copyright (c) 2003 Patrick Mochel * Copyright (c) 2003 Open Source Development Lab - * Copyright (c) 2004 Pavel Machek + * Copyright (c) 2004 Pavel Machek * Copyright (c) 2009 Rafael J. Wysocki, Novell Inc. * * This file is released under the GPLv2. diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c index 25ce010e9f8b..f6cd6faf84fd 100644 --- a/kernel/power/snapshot.c +++ b/kernel/power/snapshot.c @@ -3,7 +3,7 @@ * * This file provides system snapshot/restore functionality for swsusp. * - * Copyright (C) 1998-2005 Pavel Machek + * Copyright (C) 1998-2005 Pavel Machek * Copyright (C) 2006 Rafael J. Wysocki * * This file is released under the GPLv2. diff --git a/kernel/power/swap.c b/kernel/power/swap.c index b0bb21778391..48a0aa9da162 100644 --- a/kernel/power/swap.c +++ b/kernel/power/swap.c @@ -4,7 +4,7 @@ * This file provides functions for reading the suspend image from * and writing it to a swap partition. * - * Copyright (C) 1998,2001-2005 Pavel Machek + * Copyright (C) 1998,2001-2005 Pavel Machek * Copyright (C) 2006 Rafael J. Wysocki * * This file is released under the GPLv2. -- cgit v1.2.3 From 99d8f83c98930100cd70437b0c81a935e7a14b0b Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Wed, 7 Jul 2010 10:51:48 -0400 Subject: Btrfs: fix split_leaf double split corner case split_leaf was not properly balancing leaves when it was forced to split a leaf twice. This commit adds an extra push left and right before forcing the double split in hopes of getting the slot where we want to insert at either the start or end of the leaf. If the extra pushes do work, then we are able to avoid splitting twice and we keep the tree properly balanced. Signed-off-by: Chris Mason --- fs/btrfs/ctree.c | 129 +++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 111 insertions(+), 18 deletions(-) (limited to 'fs') diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c index 0d1d966b0fe4..c3df14ce2cc2 100644 --- a/fs/btrfs/ctree.c +++ b/fs/btrfs/ctree.c @@ -2304,12 +2304,17 @@ noinline int btrfs_leaf_free_space(struct btrfs_root *root, return ret; } +/* + * min slot controls the lowest index we're willing to push to the + * right. We'll push up to and including min_slot, but no lower + */ static noinline int __push_leaf_right(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct btrfs_path *path, int data_size, int empty, struct extent_buffer *right, - int free_space, u32 left_nritems) + int free_space, u32 left_nritems, + u32 min_slot) { struct extent_buffer *left = path->nodes[0]; struct extent_buffer *upper = path->nodes[1]; @@ -2327,7 +2332,7 @@ static noinline int __push_leaf_right(struct btrfs_trans_handle *trans, if (empty) nr = 0; else - nr = 1; + nr = max_t(u32, 1, min_slot); if (path->slots[0] >= left_nritems) push_space += data_size; @@ -2469,10 +2474,14 @@ out_unlock: * * returns 1 if the push failed because the other node didn't have enough * room, 0 if everything worked out and < 0 if there were major errors. + * + * this will push starting from min_slot to the end of the leaf. It won't + * push any slot lower than min_slot */ static int push_leaf_right(struct btrfs_trans_handle *trans, struct btrfs_root - *root, struct btrfs_path *path, int data_size, - int empty) + *root, struct btrfs_path *path, + int min_data_size, int data_size, + int empty, u32 min_slot) { struct extent_buffer *left = path->nodes[0]; struct extent_buffer *right; @@ -2514,8 +2523,8 @@ static int push_leaf_right(struct btrfs_trans_handle *trans, struct btrfs_root if (left_nritems == 0) goto out_unlock; - return __push_leaf_right(trans, root, path, data_size, empty, - right, free_space, left_nritems); + return __push_leaf_right(trans, root, path, min_data_size, empty, + right, free_space, left_nritems, min_slot); out_unlock: btrfs_tree_unlock(right); free_extent_buffer(right); @@ -2525,12 +2534,17 @@ out_unlock: /* * push some data in the path leaf to the left, trying to free up at * least data_size bytes. returns zero if the push worked, nonzero otherwise + * + * max_slot can put a limit on how far into the leaf we'll push items. The + * item at 'max_slot' won't be touched. Use (u32)-1 to make us do all the + * items */ static noinline int __push_leaf_left(struct btrfs_trans_handle *trans, struct btrfs_root *root, struct btrfs_path *path, int data_size, int empty, struct extent_buffer *left, - int free_space, int right_nritems) + int free_space, u32 right_nritems, + u32 max_slot) { struct btrfs_disk_key disk_key; struct extent_buffer *right = path->nodes[0]; @@ -2549,9 +2563,9 @@ static noinline int __push_leaf_left(struct btrfs_trans_handle *trans, slot = path->slots[1]; if (empty) - nr = right_nritems; + nr = min(right_nritems, max_slot); else - nr = right_nritems - 1; + nr = min(right_nritems - 1, max_slot); for (i = 0; i < nr; i++) { item = btrfs_item_nr(right, i); @@ -2712,10 +2726,14 @@ out: /* * push some data in the path leaf to the left, trying to free up at * least data_size bytes. returns zero if the push worked, nonzero otherwise + * + * max_slot can put a limit on how far into the leaf we'll push items. The + * item at 'max_slot' won't be touched. Use (u32)-1 to make us push all the + * items */ static int push_leaf_left(struct btrfs_trans_handle *trans, struct btrfs_root - *root, struct btrfs_path *path, int data_size, - int empty) + *root, struct btrfs_path *path, int min_data_size, + int data_size, int empty, u32 max_slot) { struct extent_buffer *right = path->nodes[0]; struct extent_buffer *left; @@ -2761,8 +2779,9 @@ static int push_leaf_left(struct btrfs_trans_handle *trans, struct btrfs_root goto out; } - return __push_leaf_left(trans, root, path, data_size, - empty, left, free_space, right_nritems); + return __push_leaf_left(trans, root, path, min_data_size, + empty, left, free_space, right_nritems, + max_slot); out: btrfs_tree_unlock(left); free_extent_buffer(left); @@ -2854,6 +2873,64 @@ static noinline int copy_for_split(struct btrfs_trans_handle *trans, return ret; } +/* + * double splits happen when we need to insert a big item in the middle + * of a leaf. A double split can leave us with 3 mostly empty leaves: + * leaf: [ slots 0 - N] [ our target ] [ N + 1 - total in leaf ] + * A B C + * + * We avoid this by trying to push the items on either side of our target + * into the adjacent leaves. If all goes well we can avoid the double split + * completely. + */ +static noinline int push_for_double_split(struct btrfs_trans_handle *trans, + struct btrfs_root *root, + struct btrfs_path *path, + int data_size) +{ + int ret; + int progress = 0; + int slot; + u32 nritems; + + slot = path->slots[0]; + + /* + * try to push all the items after our slot into the + * right leaf + */ + ret = push_leaf_right(trans, root, path, 1, data_size, 0, slot); + if (ret < 0) + return ret; + + if (ret == 0) + progress++; + + nritems = btrfs_header_nritems(path->nodes[0]); + /* + * our goal is to get our slot at the start or end of a leaf. If + * we've done so we're done + */ + if (path->slots[0] == 0 || path->slots[0] == nritems) + return 0; + + if (btrfs_leaf_free_space(root, path->nodes[0]) >= data_size) + return 0; + + /* try to push all the items before our slot into the next leaf */ + slot = path->slots[0]; + ret = push_leaf_left(trans, root, path, 1, data_size, 0, slot); + if (ret < 0) + return ret; + + if (ret == 0) + progress++; + + if (progress) + return 0; + return 1; +} + /* * split the path's leaf in two, making sure there is at least data_size * available for the resulting leaf level of the path. @@ -2876,6 +2953,7 @@ static noinline int split_leaf(struct btrfs_trans_handle *trans, int wret; int split; int num_doubles = 0; + int tried_avoid_double = 0; l = path->nodes[0]; slot = path->slots[0]; @@ -2884,12 +2962,14 @@ static noinline int split_leaf(struct btrfs_trans_handle *trans, return -EOVERFLOW; /* first try to make some room by pushing left and right */ - if (data_size && ins_key->type != BTRFS_DIR_ITEM_KEY) { - wret = push_leaf_right(trans, root, path, data_size, 0); + if (data_size) { + wret = push_leaf_right(trans, root, path, data_size, + data_size, 0, 0); if (wret < 0) return wret; if (wret) { - wret = push_leaf_left(trans, root, path, data_size, 0); + wret = push_leaf_left(trans, root, path, data_size, + data_size, 0, (u32)-1); if (wret < 0) return wret; } @@ -2923,6 +3003,8 @@ again: if (mid != nritems && leaf_space_used(l, mid, nritems - mid) + data_size > BTRFS_LEAF_DATA_SIZE(root)) { + if (data_size && !tried_avoid_double) + goto push_for_double; split = 2; } } @@ -2939,6 +3021,8 @@ again: if (mid != nritems && leaf_space_used(l, mid, nritems - mid) + data_size > BTRFS_LEAF_DATA_SIZE(root)) { + if (data_size && !tried_avoid_double) + goto push_for_double; split = 2 ; } } @@ -3019,6 +3103,13 @@ again: } return ret; + +push_for_double: + push_for_double_split(trans, root, path, data_size); + tried_avoid_double = 1; + if (btrfs_leaf_free_space(root, path->nodes[0]) >= data_size) + return 0; + goto again; } static noinline int setup_leaf_for_split(struct btrfs_trans_handle *trans, @@ -3915,13 +4006,15 @@ int btrfs_del_items(struct btrfs_trans_handle *trans, struct btrfs_root *root, extent_buffer_get(leaf); btrfs_set_path_blocking(path); - wret = push_leaf_left(trans, root, path, 1, 1); + wret = push_leaf_left(trans, root, path, 1, 1, + 1, (u32)-1); if (wret < 0 && wret != -ENOSPC) ret = wret; if (path->nodes[0] == leaf && btrfs_header_nritems(leaf)) { - wret = push_leaf_right(trans, root, path, 1, 1); + wret = push_leaf_right(trans, root, path, 1, + 1, 1, 0); if (wret < 0 && wret != -ENOSPC) ret = wret; } -- cgit v1.2.3 From b5384d48f4e74edec3ca1887cb65e378a72af9a1 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Sat, 12 Jun 2010 22:31:14 +0000 Subject: Btrfs: fix CLONE ioctl destination file size expansion to block boundary The CLONE and CLONE_RANGE ioctls round up the range of extents being cloned to the block size when the range to clone extends to the end of file (this is always the case with CLONE). It was then using that offset when extending the destination file's i_size. Fix this by not setting i_size beyond the originally requested ending offset. This bug was introduced by a22285a6 (2.6.35-rc1). Signed-off-by: Sage Weil Signed-off-by: Chris Mason --- fs/btrfs/ioctl.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 4dbaf89b1337..2a8b3a7568ad 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -1578,6 +1578,7 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd, u64 disko = 0, diskl = 0; u64 datao = 0, datal = 0; u8 comp; + u64 endoff; size = btrfs_item_size_nr(leaf, slot); read_extent_buffer(leaf, buf, @@ -1712,9 +1713,18 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd, btrfs_release_path(root, path); inode->i_mtime = inode->i_ctime = CURRENT_TIME; - if (new_key.offset + datal > inode->i_size) - btrfs_i_size_write(inode, - new_key.offset + datal); + + /* + * we round up to the block size at eof when + * determining which extents to clone above, + * but shouldn't round up the file size + */ + endoff = new_key.offset + datal; + if (endoff > off+olen) + endoff = off+olen; + if (endoff > inode->i_size) + btrfs_i_size_write(inode, endoff); + BTRFS_I(inode)->flags = BTRFS_I(src)->flags; ret = btrfs_update_inode(trans, root, inode); BUG_ON(ret); -- cgit v1.2.3 From 2ebc3464781ad24474abcbd2274e6254689853b5 Mon Sep 17 00:00:00 2001 From: Dan Rosenberg Date: Mon, 19 Jul 2010 16:58:20 -0400 Subject: Btrfs: fix checks in BTRFS_IOC_CLONE_RANGE 1. The BTRFS_IOC_CLONE and BTRFS_IOC_CLONE_RANGE ioctls should check whether the donor file is append-only before writing to it. 2. The BTRFS_IOC_CLONE_RANGE ioctl appears to have an integer overflow that allows a user to specify an out-of-bounds range to copy from the source file (if off + len wraps around). I haven't been able to successfully exploit this, but I'd imagine that a clever attacker could use this to read things he shouldn't. Even if it's not exploitable, it couldn't hurt to be safe. Signed-off-by: Dan Rosenberg cc: stable@kernel.org Signed-off-by: Chris Mason --- fs/btrfs/ioctl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 2a8b3a7568ad..9254b3d58dbe 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -1458,7 +1458,7 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd, */ /* the destination must be opened for writing */ - if (!(file->f_mode & FMODE_WRITE)) + if (!(file->f_mode & FMODE_WRITE) || (file->f_flags & O_APPEND)) return -EINVAL; ret = mnt_want_write(file->f_path.mnt); @@ -1511,7 +1511,7 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd, /* determine range to clone */ ret = -EINVAL; - if (off >= src->i_size || off + len > src->i_size) + if (off + len > src->i_size || off + len < off) goto out_unlock; if (len == 0) olen = len = src->i_size - off; -- cgit v1.2.3 From 70e60ce71516c3a9e882edb70a09f696a05961db Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Tue, 20 Jul 2010 08:07:02 +1000 Subject: xfs: convert inode shrinker to per-filesystem contexts Now the shrinker passes us a context, wire up a shrinker context per filesystem. This allows us to remove the global mount list and the locking problems that introduced. It also means that a shrinker call does not need to traverse clean filesystems before finding a filesystem with reclaimable inodes. This significantly reduces scanning overhead when lots of filesystems are present. Signed-off-by: Dave Chinner Reviewed-by: Christoph Hellwig --- fs/xfs/linux-2.6/xfs_super.c | 2 -- fs/xfs/linux-2.6/xfs_sync.c | 62 ++++++++++---------------------------------- fs/xfs/linux-2.6/xfs_sync.h | 2 -- fs/xfs/xfs_mount.h | 2 +- 4 files changed, 15 insertions(+), 53 deletions(-) (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_super.c b/fs/xfs/linux-2.6/xfs_super.c index f2d1718c9165..80938c736c27 100644 --- a/fs/xfs/linux-2.6/xfs_super.c +++ b/fs/xfs/linux-2.6/xfs_super.c @@ -1883,7 +1883,6 @@ init_xfs_fs(void) goto out_cleanup_procfs; vfs_initquota(); - xfs_inode_shrinker_init(); error = register_filesystem(&xfs_fs_type); if (error) @@ -1911,7 +1910,6 @@ exit_xfs_fs(void) { vfs_exitquota(); unregister_filesystem(&xfs_fs_type); - xfs_inode_shrinker_destroy(); xfs_sysctl_unregister(); xfs_cleanup_procfs(); xfs_buf_terminate(); diff --git a/fs/xfs/linux-2.6/xfs_sync.c b/fs/xfs/linux-2.6/xfs_sync.c index be375827af98..f433819611cb 100644 --- a/fs/xfs/linux-2.6/xfs_sync.c +++ b/fs/xfs/linux-2.6/xfs_sync.c @@ -828,14 +828,7 @@ xfs_reclaim_inodes( /* * Shrinker infrastructure. - * - * This is all far more complex than it needs to be. It adds a global list of - * mounts because the shrinkers can only call a global context. We need to make - * the shrinkers pass a context to avoid the need for global state. */ -static LIST_HEAD(xfs_mount_list); -static struct rw_semaphore xfs_mount_list_lock; - static int xfs_reclaim_inode_shrink( struct shrinker *shrink, @@ -847,65 +840,38 @@ xfs_reclaim_inode_shrink( xfs_agnumber_t ag; int reclaimable = 0; + mp = container_of(shrink, struct xfs_mount, m_inode_shrink); if (nr_to_scan) { if (!(gfp_mask & __GFP_FS)) return -1; - down_read(&xfs_mount_list_lock); - list_for_each_entry(mp, &xfs_mount_list, m_mplist) { - xfs_inode_ag_iterator(mp, xfs_reclaim_inode, 0, + xfs_inode_ag_iterator(mp, xfs_reclaim_inode, 0, XFS_ICI_RECLAIM_TAG, 1, &nr_to_scan); - if (nr_to_scan <= 0) - break; - } - up_read(&xfs_mount_list_lock); - } + /* if we don't exhaust the scan, don't bother coming back */ + if (nr_to_scan > 0) + return -1; + } - down_read(&xfs_mount_list_lock); - list_for_each_entry(mp, &xfs_mount_list, m_mplist) { - for (ag = 0; ag < mp->m_sb.sb_agcount; ag++) { - pag = xfs_perag_get(mp, ag); - reclaimable += pag->pag_ici_reclaimable; - xfs_perag_put(pag); - } + for (ag = 0; ag < mp->m_sb.sb_agcount; ag++) { + pag = xfs_perag_get(mp, ag); + reclaimable += pag->pag_ici_reclaimable; + xfs_perag_put(pag); } - up_read(&xfs_mount_list_lock); return reclaimable; } -static struct shrinker xfs_inode_shrinker = { - .shrink = xfs_reclaim_inode_shrink, - .seeks = DEFAULT_SEEKS, -}; - -void __init -xfs_inode_shrinker_init(void) -{ - init_rwsem(&xfs_mount_list_lock); - register_shrinker(&xfs_inode_shrinker); -} - -void -xfs_inode_shrinker_destroy(void) -{ - ASSERT(list_empty(&xfs_mount_list)); - unregister_shrinker(&xfs_inode_shrinker); -} - void xfs_inode_shrinker_register( struct xfs_mount *mp) { - down_write(&xfs_mount_list_lock); - list_add_tail(&mp->m_mplist, &xfs_mount_list); - up_write(&xfs_mount_list_lock); + mp->m_inode_shrink.shrink = xfs_reclaim_inode_shrink; + mp->m_inode_shrink.seeks = DEFAULT_SEEKS; + register_shrinker(&mp->m_inode_shrink); } void xfs_inode_shrinker_unregister( struct xfs_mount *mp) { - down_write(&xfs_mount_list_lock); - list_del(&mp->m_mplist); - up_write(&xfs_mount_list_lock); + unregister_shrinker(&mp->m_inode_shrink); } diff --git a/fs/xfs/linux-2.6/xfs_sync.h b/fs/xfs/linux-2.6/xfs_sync.h index cdcbaaca9880..e28139aaa4aa 100644 --- a/fs/xfs/linux-2.6/xfs_sync.h +++ b/fs/xfs/linux-2.6/xfs_sync.h @@ -55,8 +55,6 @@ int xfs_inode_ag_iterator(struct xfs_mount *mp, int (*execute)(struct xfs_inode *ip, struct xfs_perag *pag, int flags), int flags, int tag, int write_lock, int *nr_to_scan); -void xfs_inode_shrinker_init(void); -void xfs_inode_shrinker_destroy(void); void xfs_inode_shrinker_register(struct xfs_mount *mp); void xfs_inode_shrinker_unregister(struct xfs_mount *mp); diff --git a/fs/xfs/xfs_mount.h b/fs/xfs/xfs_mount.h index 1d2c7eed4eda..5761087ee8ea 100644 --- a/fs/xfs/xfs_mount.h +++ b/fs/xfs/xfs_mount.h @@ -259,7 +259,7 @@ typedef struct xfs_mount { wait_queue_head_t m_wait_single_sync_task; __int64_t m_update_flags; /* sb flags we need to update on the next remount,rw */ - struct list_head m_mplist; /* inode shrinker mount list */ + struct shrinker m_inode_shrink; /* inode reclaim shrinker */ } xfs_mount_t; /* -- cgit v1.2.3 From 16fd5367370099b59d96e30bb7d9de8d419659f2 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Tue, 20 Jul 2010 09:43:39 +1000 Subject: xfs: track AGs with reclaimable inodes in per-ag radix tree https://bugzilla.kernel.org/show_bug.cgi?id=16348 When the filesystem grows to a large number of allocation groups, the summing of recalimable inodes gets expensive. In many cases, most AGs won't have any reclaimable inodes and so we are wasting CPU time aggregating over these AGs. This is particularly important for the inode shrinker that gets called frequently under memory pressure. To avoid the overhead, track AGs with reclaimable inodes in the per-ag radix tree so that we can find all the AGs with reclaimable inodes via a simple gang tag lookup. This involves setting the tag when the first reclaimable inode is tracked in the AG, and removing the tag when the last reclaimable inode is removed from the tree. Then the summation process becomes a loop walking the radix tree summing AGs with the reclaim tag set. This significantly reduces the overhead of scanning - a 6400 AG filesystea now only uses about 25% of a cpu in kswapd while slab reclaim progresses instead of being permanently stuck at 100% CPU and making little progress. Clean filesystems filesystems will see no overhead and the overhead only increases linearly with the number of dirty AGs. Signed-off-by: Dave Chinner Reviewed-by: Christoph Hellwig --- fs/xfs/linux-2.6/xfs_sync.c | 71 +++++++++++++++++++++++++++++++++++++++----- fs/xfs/linux-2.6/xfs_trace.h | 3 ++ 2 files changed, 67 insertions(+), 7 deletions(-) (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_sync.c b/fs/xfs/linux-2.6/xfs_sync.c index f433819611cb..a51a07c3a70c 100644 --- a/fs/xfs/linux-2.6/xfs_sync.c +++ b/fs/xfs/linux-2.6/xfs_sync.c @@ -144,6 +144,41 @@ restart: return last_error; } +/* + * Select the next per-ag structure to iterate during the walk. The reclaim + * walk is optimised only to walk AGs with reclaimable inodes in them. + */ +static struct xfs_perag * +xfs_inode_ag_iter_next_pag( + struct xfs_mount *mp, + xfs_agnumber_t *first, + int tag) +{ + struct xfs_perag *pag = NULL; + + if (tag == XFS_ICI_RECLAIM_TAG) { + int found; + int ref; + + spin_lock(&mp->m_perag_lock); + found = radix_tree_gang_lookup_tag(&mp->m_perag_tree, + (void **)&pag, *first, 1, tag); + if (found <= 0) { + spin_unlock(&mp->m_perag_lock); + return NULL; + } + *first = pag->pag_agno + 1; + /* open coded pag reference increment */ + ref = atomic_inc_return(&pag->pag_ref); + spin_unlock(&mp->m_perag_lock); + trace_xfs_perag_get_reclaim(mp, pag->pag_agno, ref, _RET_IP_); + } else { + pag = xfs_perag_get(mp, *first); + (*first)++; + } + return pag; +} + int xfs_inode_ag_iterator( struct xfs_mount *mp, @@ -154,16 +189,15 @@ xfs_inode_ag_iterator( int exclusive, int *nr_to_scan) { + struct xfs_perag *pag; int error = 0; int last_error = 0; xfs_agnumber_t ag; int nr; nr = nr_to_scan ? *nr_to_scan : INT_MAX; - for (ag = 0; ag < mp->m_sb.sb_agcount; ag++) { - struct xfs_perag *pag; - - pag = xfs_perag_get(mp, ag); + ag = 0; + while ((pag = xfs_inode_ag_iter_next_pag(mp, &ag, tag))) { error = xfs_inode_ag_walk(mp, pag, execute, flags, tag, exclusive, &nr); xfs_perag_put(pag); @@ -640,6 +674,17 @@ __xfs_inode_set_reclaim_tag( radix_tree_tag_set(&pag->pag_ici_root, XFS_INO_TO_AGINO(ip->i_mount, ip->i_ino), XFS_ICI_RECLAIM_TAG); + + if (!pag->pag_ici_reclaimable) { + /* propagate the reclaim tag up into the perag radix tree */ + spin_lock(&ip->i_mount->m_perag_lock); + radix_tree_tag_set(&ip->i_mount->m_perag_tree, + XFS_INO_TO_AGNO(ip->i_mount, ip->i_ino), + XFS_ICI_RECLAIM_TAG); + spin_unlock(&ip->i_mount->m_perag_lock); + trace_xfs_perag_set_reclaim(ip->i_mount, pag->pag_agno, + -1, _RET_IP_); + } pag->pag_ici_reclaimable++; } @@ -674,6 +719,16 @@ __xfs_inode_clear_reclaim_tag( radix_tree_tag_clear(&pag->pag_ici_root, XFS_INO_TO_AGINO(mp, ip->i_ino), XFS_ICI_RECLAIM_TAG); pag->pag_ici_reclaimable--; + if (!pag->pag_ici_reclaimable) { + /* clear the reclaim tag from the perag radix tree */ + spin_lock(&ip->i_mount->m_perag_lock); + radix_tree_tag_clear(&ip->i_mount->m_perag_tree, + XFS_INO_TO_AGNO(ip->i_mount, ip->i_ino), + XFS_ICI_RECLAIM_TAG); + spin_unlock(&ip->i_mount->m_perag_lock); + trace_xfs_perag_clear_reclaim(ip->i_mount, pag->pag_agno, + -1, _RET_IP_); + } } /* @@ -838,7 +893,7 @@ xfs_reclaim_inode_shrink( struct xfs_mount *mp; struct xfs_perag *pag; xfs_agnumber_t ag; - int reclaimable = 0; + int reclaimable; mp = container_of(shrink, struct xfs_mount, m_inode_shrink); if (nr_to_scan) { @@ -852,8 +907,10 @@ xfs_reclaim_inode_shrink( return -1; } - for (ag = 0; ag < mp->m_sb.sb_agcount; ag++) { - pag = xfs_perag_get(mp, ag); + reclaimable = 0; + ag = 0; + while ((pag = xfs_inode_ag_iter_next_pag(mp, &ag, + XFS_ICI_RECLAIM_TAG))) { reclaimable += pag->pag_ici_reclaimable; xfs_perag_put(pag); } diff --git a/fs/xfs/linux-2.6/xfs_trace.h b/fs/xfs/linux-2.6/xfs_trace.h index 73d5aa117384..302820690904 100644 --- a/fs/xfs/linux-2.6/xfs_trace.h +++ b/fs/xfs/linux-2.6/xfs_trace.h @@ -124,7 +124,10 @@ DEFINE_EVENT(xfs_perag_class, name, \ unsigned long caller_ip), \ TP_ARGS(mp, agno, refcount, caller_ip)) DEFINE_PERAG_REF_EVENT(xfs_perag_get); +DEFINE_PERAG_REF_EVENT(xfs_perag_get_reclaim); DEFINE_PERAG_REF_EVENT(xfs_perag_put); +DEFINE_PERAG_REF_EVENT(xfs_perag_set_reclaim); +DEFINE_PERAG_REF_EVENT(xfs_perag_clear_reclaim); TRACE_EVENT(xfs_attr_list_node_descend, TP_PROTO(struct xfs_attr_list_context *ctx, -- cgit v1.2.3 From 33fa1d909c7357be715aa0e9f9e24c3ef5714493 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Mon, 12 Jul 2010 13:50:19 -0700 Subject: fs/ocfs2: Remove unnecessary casts of private_data Signed-off-by: Joe Perches Acked-by: Joel Becker Signed-off-by: Jiri Kosina --- fs/ocfs2/dlm/dlmdebug.c | 6 +++--- fs/ocfs2/dlmfs/dlmfs.c | 3 +-- fs/ocfs2/dlmglue.c | 4 ++-- 3 files changed, 6 insertions(+), 7 deletions(-) (limited to 'fs') diff --git a/fs/ocfs2/dlm/dlmdebug.c b/fs/ocfs2/dlm/dlmdebug.c index 0cd24cf54396..5efdd37dfe48 100644 --- a/fs/ocfs2/dlm/dlmdebug.c +++ b/fs/ocfs2/dlm/dlmdebug.c @@ -419,7 +419,7 @@ static loff_t debug_buffer_llseek(struct file *file, loff_t off, int whence) static int debug_buffer_release(struct inode *inode, struct file *file) { - struct debug_buffer *db = (struct debug_buffer *)file->private_data; + struct debug_buffer *db = file->private_data; if (db) kfree(db->buf); @@ -715,7 +715,7 @@ static int debug_lockres_open(struct inode *inode, struct file *file) goto bail; } - seq = (struct seq_file *) file->private_data; + seq = file->private_data; seq->private = dl; dlm_grab(dlm); @@ -731,7 +731,7 @@ bail: static int debug_lockres_release(struct inode *inode, struct file *file) { - struct seq_file *seq = (struct seq_file *)file->private_data; + struct seq_file *seq = file->private_data; struct debug_lockres *dl = (struct debug_lockres *)seq->private; if (dl->dl_res) diff --git a/fs/ocfs2/dlmfs/dlmfs.c b/fs/ocfs2/dlmfs/dlmfs.c index b83d6107a1f5..bef34d0528d5 100644 --- a/fs/ocfs2/dlmfs/dlmfs.c +++ b/fs/ocfs2/dlmfs/dlmfs.c @@ -182,8 +182,7 @@ static int dlmfs_file_release(struct inode *inode, { int level, status; struct dlmfs_inode_private *ip = DLMFS_I(inode); - struct dlmfs_filp_private *fp = - (struct dlmfs_filp_private *) file->private_data; + struct dlmfs_filp_private *fp = file->private_data; if (S_ISDIR(inode->i_mode)) BUG(); diff --git a/fs/ocfs2/dlmglue.c b/fs/ocfs2/dlmglue.c index 39eb16ac5f98..5e02a893f46e 100644 --- a/fs/ocfs2/dlmglue.c +++ b/fs/ocfs2/dlmglue.c @@ -2966,7 +2966,7 @@ static const struct seq_operations ocfs2_dlm_seq_ops = { static int ocfs2_dlm_debug_release(struct inode *inode, struct file *file) { - struct seq_file *seq = (struct seq_file *) file->private_data; + struct seq_file *seq = file->private_data; struct ocfs2_dlm_seq_priv *priv = seq->private; struct ocfs2_lock_res *res = &priv->p_iter_res; @@ -3000,7 +3000,7 @@ static int ocfs2_dlm_debug_open(struct inode *inode, struct file *file) goto out; } - seq = (struct seq_file *) file->private_data; + seq = file->private_data; seq->private = priv; ocfs2_add_lockres_tracking(&priv->p_iter_res, -- cgit v1.2.3 From 59b48568312a262cbba814c683a320fb4e0dccae Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Fri, 16 Jul 2010 09:46:44 -0700 Subject: fs/Kconfig: Fix typo Userpace -> Userspace Signed-off-by: Stephen Boyd Signed-off-by: Jiri Kosina --- fs/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/Kconfig b/fs/Kconfig index 5f85b5947613..3d185308ec88 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -64,7 +64,7 @@ source "fs/autofs4/Kconfig" source "fs/fuse/Kconfig" config CUSE - tristate "Character device in Userpace support" + tristate "Character device in Userspace support" depends on FUSE_FS help This FUSE extension allows character devices to be -- cgit v1.2.3 From 7af9cce8ae467bb2fcf3b0b6be3898835bdb984c Mon Sep 17 00:00:00 2001 From: Dmitry Monakhov Date: Tue, 1 Jun 2010 11:39:48 +0400 Subject: quota: check quota reservation on remove_dquot_ref Reserved space must being claimed before remove_dquot_ref() for a given inode. Filesystem is responsible for performing force blocks allocation in case of dealloc in ->quota_off. Let's add sanity check for that case. Do it similar to add_dquot_ref(). Signed-off-by: Dmitry Monakhov Signed-off-by: Jan Kara --- fs/quota/dquot.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c index 12c233da1b6b..a5974c49a78b 100644 --- a/fs/quota/dquot.c +++ b/fs/quota/dquot.c @@ -986,6 +986,7 @@ static void remove_dquot_ref(struct super_block *sb, int type, struct list_head *tofree_head) { struct inode *inode; + int reserved = 0; spin_lock(&inode_lock); list_for_each_entry(inode, &sb->s_inodes, i_sb_list) { @@ -995,10 +996,20 @@ static void remove_dquot_ref(struct super_block *sb, int type, * only quota pointers and these have separate locking * (dqptr_sem). */ - if (!IS_NOQUOTA(inode)) + if (!IS_NOQUOTA(inode)) { + if (unlikely(inode_get_rsv_space(inode) > 0)) + reserved = 1; remove_inode_dquot_ref(inode, type, tofree_head); + } } spin_unlock(&inode_lock); +#ifdef CONFIG_QUOTA_DEBUG + if (reserved) { + printk(KERN_WARNING "VFS (%s): Writes happened after quota" + " was disabled thus quota information is probably " + "inconsistent. Please run quotacheck(8).\n", sb->s_id); + } +#endif } /* Gather all references from inodes and drop them */ -- cgit v1.2.3 From ade7ce31c22e961dfbe1a6d57fd362c90c187cbd Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 4 Jun 2010 10:56:01 +0200 Subject: quota: Clean up the namespace in dqblk_xfs.h Almost all identifiers use the FS_* namespace, so rename the missing few XFS_* ones to FS_* as well. Without this some people might get upset about having too many XFS names in generic code. Acked-by: Steven Whitehouse Signed-off-by: Christoph Hellwig Signed-off-by: Jan Kara --- fs/gfs2/quota.c | 10 +++++----- fs/quota/dquot.c | 2 +- fs/xfs/linux-2.6/xfs_quotaops.c | 10 +++++----- fs/xfs/quota/xfs_qm_syscalls.c | 32 ++++++++++++++++---------------- include/linux/dqblk_xfs.h | 24 ++++++++++++------------ 5 files changed, 39 insertions(+), 39 deletions(-) (limited to 'fs') diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index b256d6f24288..ce345f8c69c2 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -1455,10 +1455,10 @@ static int gfs2_quota_get_xstate(struct super_block *sb, switch (sdp->sd_args.ar_quota) { case GFS2_QUOTA_ON: - fqs->qs_flags |= (XFS_QUOTA_UDQ_ENFD | XFS_QUOTA_GDQ_ENFD); + fqs->qs_flags |= (FS_QUOTA_UDQ_ENFD | FS_QUOTA_GDQ_ENFD); /*FALLTHRU*/ case GFS2_QUOTA_ACCOUNT: - fqs->qs_flags |= (XFS_QUOTA_UDQ_ACCT | XFS_QUOTA_GDQ_ACCT); + fqs->qs_flags |= (FS_QUOTA_UDQ_ACCT | FS_QUOTA_GDQ_ACCT); break; case GFS2_QUOTA_OFF: break; @@ -1504,7 +1504,7 @@ static int gfs2_get_dqblk(struct super_block *sb, int type, qid_t id, qlvb = (struct gfs2_quota_lvb *)qd->qd_gl->gl_lvb; fdq->d_version = FS_DQUOT_VERSION; - fdq->d_flags = (type == QUOTA_USER) ? XFS_USER_QUOTA : XFS_GROUP_QUOTA; + fdq->d_flags = (type == QUOTA_USER) ? FS_USER_QUOTA : FS_GROUP_QUOTA; fdq->d_id = id; fdq->d_blk_hardlimit = be64_to_cpu(qlvb->qb_limit); fdq->d_blk_softlimit = be64_to_cpu(qlvb->qb_warn); @@ -1539,12 +1539,12 @@ static int gfs2_set_dqblk(struct super_block *sb, int type, qid_t id, switch(type) { case USRQUOTA: type = QUOTA_USER; - if (fdq->d_flags != XFS_USER_QUOTA) + if (fdq->d_flags != FS_USER_QUOTA) return -EINVAL; break; case GRPQUOTA: type = QUOTA_GROUP; - if (fdq->d_flags != XFS_GROUP_QUOTA) + if (fdq->d_flags != FS_GROUP_QUOTA) return -EINVAL; break; default: diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c index a5974c49a78b..2857fd67ff33 100644 --- a/fs/quota/dquot.c +++ b/fs/quota/dquot.c @@ -2281,7 +2281,7 @@ static void do_get_dqblk(struct dquot *dquot, struct fs_disk_quota *di) memset(di, 0, sizeof(*di)); di->d_version = FS_DQUOT_VERSION; di->d_flags = dquot->dq_type == USRQUOTA ? - XFS_USER_QUOTA : XFS_GROUP_QUOTA; + FS_USER_QUOTA : FS_GROUP_QUOTA; di->d_id = dquot->dq_id; spin_lock(&dq_data_lock); diff --git a/fs/xfs/linux-2.6/xfs_quotaops.c b/fs/xfs/linux-2.6/xfs_quotaops.c index 067cafbfc635..b9ba7536f4b4 100644 --- a/fs/xfs/linux-2.6/xfs_quotaops.c +++ b/fs/xfs/linux-2.6/xfs_quotaops.c @@ -69,15 +69,15 @@ xfs_fs_set_xstate( if (op != Q_XQUOTARM && !XFS_IS_QUOTA_RUNNING(mp)) return -ENOSYS; - if (uflags & XFS_QUOTA_UDQ_ACCT) + if (uflags & FS_QUOTA_UDQ_ACCT) flags |= XFS_UQUOTA_ACCT; - if (uflags & XFS_QUOTA_PDQ_ACCT) + if (uflags & FS_QUOTA_PDQ_ACCT) flags |= XFS_PQUOTA_ACCT; - if (uflags & XFS_QUOTA_GDQ_ACCT) + if (uflags & FS_QUOTA_GDQ_ACCT) flags |= XFS_GQUOTA_ACCT; - if (uflags & XFS_QUOTA_UDQ_ENFD) + if (uflags & FS_QUOTA_UDQ_ENFD) flags |= XFS_UQUOTA_ENFD; - if (uflags & (XFS_QUOTA_PDQ_ENFD|XFS_QUOTA_GDQ_ENFD)) + if (uflags & (FS_QUOTA_PDQ_ENFD|FS_QUOTA_GDQ_ENFD)) flags |= XFS_OQUOTA_ENFD; switch (op) { diff --git a/fs/xfs/quota/xfs_qm_syscalls.c b/fs/xfs/quota/xfs_qm_syscalls.c index b4487764e923..41b04b96975b 100644 --- a/fs/xfs/quota/xfs_qm_syscalls.c +++ b/fs/xfs/quota/xfs_qm_syscalls.c @@ -786,9 +786,9 @@ xfs_qm_export_dquot( } #ifdef DEBUG - if (((XFS_IS_UQUOTA_ENFORCED(mp) && dst->d_flags == XFS_USER_QUOTA) || + if (((XFS_IS_UQUOTA_ENFORCED(mp) && dst->d_flags == FS_USER_QUOTA) || (XFS_IS_OQUOTA_ENFORCED(mp) && - (dst->d_flags & (XFS_PROJ_QUOTA | XFS_GROUP_QUOTA)))) && + (dst->d_flags & (FS_PROJ_QUOTA | FS_GROUP_QUOTA)))) && dst->d_id != 0) { if (((int) dst->d_bcount >= (int) dst->d_blk_softlimit) && (dst->d_blk_softlimit > 0)) { @@ -809,17 +809,17 @@ xfs_qm_export_qtype_flags( /* * Can't be more than one, or none. */ - ASSERT((flags & (XFS_PROJ_QUOTA | XFS_USER_QUOTA)) != - (XFS_PROJ_QUOTA | XFS_USER_QUOTA)); - ASSERT((flags & (XFS_PROJ_QUOTA | XFS_GROUP_QUOTA)) != - (XFS_PROJ_QUOTA | XFS_GROUP_QUOTA)); - ASSERT((flags & (XFS_USER_QUOTA | XFS_GROUP_QUOTA)) != - (XFS_USER_QUOTA | XFS_GROUP_QUOTA)); - ASSERT((flags & (XFS_PROJ_QUOTA|XFS_USER_QUOTA|XFS_GROUP_QUOTA)) != 0); + ASSERT((flags & (FS_PROJ_QUOTA | FS_USER_QUOTA)) != + (FS_PROJ_QUOTA | FS_USER_QUOTA)); + ASSERT((flags & (FS_PROJ_QUOTA | FS_GROUP_QUOTA)) != + (FS_PROJ_QUOTA | FS_GROUP_QUOTA)); + ASSERT((flags & (FS_USER_QUOTA | FS_GROUP_QUOTA)) != + (FS_USER_QUOTA | FS_GROUP_QUOTA)); + ASSERT((flags & (FS_PROJ_QUOTA|FS_USER_QUOTA|FS_GROUP_QUOTA)) != 0); return (flags & XFS_DQ_USER) ? - XFS_USER_QUOTA : (flags & XFS_DQ_PROJ) ? - XFS_PROJ_QUOTA : XFS_GROUP_QUOTA; + FS_USER_QUOTA : (flags & XFS_DQ_PROJ) ? + FS_PROJ_QUOTA : FS_GROUP_QUOTA; } STATIC uint @@ -830,16 +830,16 @@ xfs_qm_export_flags( uflags = 0; if (flags & XFS_UQUOTA_ACCT) - uflags |= XFS_QUOTA_UDQ_ACCT; + uflags |= FS_QUOTA_UDQ_ACCT; if (flags & XFS_PQUOTA_ACCT) - uflags |= XFS_QUOTA_PDQ_ACCT; + uflags |= FS_QUOTA_PDQ_ACCT; if (flags & XFS_GQUOTA_ACCT) - uflags |= XFS_QUOTA_GDQ_ACCT; + uflags |= FS_QUOTA_GDQ_ACCT; if (flags & XFS_UQUOTA_ENFD) - uflags |= XFS_QUOTA_UDQ_ENFD; + uflags |= FS_QUOTA_UDQ_ENFD; if (flags & (XFS_OQUOTA_ENFD)) { uflags |= (flags & XFS_GQUOTA_ACCT) ? - XFS_QUOTA_GDQ_ENFD : XFS_QUOTA_PDQ_ENFD; + FS_QUOTA_GDQ_ENFD : FS_QUOTA_PDQ_ENFD; } return (uflags); } diff --git a/include/linux/dqblk_xfs.h b/include/linux/dqblk_xfs.h index 4389ae72024e..86552807aed9 100644 --- a/include/linux/dqblk_xfs.h +++ b/include/linux/dqblk_xfs.h @@ -49,7 +49,7 @@ #define FS_DQUOT_VERSION 1 /* fs_disk_quota.d_version */ typedef struct fs_disk_quota { __s8 d_version; /* version of this structure */ - __s8 d_flags; /* XFS_{USER,PROJ,GROUP}_QUOTA */ + __s8 d_flags; /* FS_{USER,PROJ,GROUP}_QUOTA */ __u16 d_fieldmask; /* field specifier */ __u32 d_id; /* user, project, or group ID */ __u64 d_blk_hardlimit;/* absolute limit on disk blks */ @@ -119,18 +119,18 @@ typedef struct fs_disk_quota { #define FS_DQ_ACCT_MASK (FS_DQ_BCOUNT | FS_DQ_ICOUNT | FS_DQ_RTBCOUNT) /* - * Various flags related to quotactl(2). Only relevant to XFS filesystems. + * Various flags related to quotactl(2). */ -#define XFS_QUOTA_UDQ_ACCT (1<<0) /* user quota accounting */ -#define XFS_QUOTA_UDQ_ENFD (1<<1) /* user quota limits enforcement */ -#define XFS_QUOTA_GDQ_ACCT (1<<2) /* group quota accounting */ -#define XFS_QUOTA_GDQ_ENFD (1<<3) /* group quota limits enforcement */ -#define XFS_QUOTA_PDQ_ACCT (1<<4) /* project quota accounting */ -#define XFS_QUOTA_PDQ_ENFD (1<<5) /* project quota limits enforcement */ +#define FS_QUOTA_UDQ_ACCT (1<<0) /* user quota accounting */ +#define FS_QUOTA_UDQ_ENFD (1<<1) /* user quota limits enforcement */ +#define FS_QUOTA_GDQ_ACCT (1<<2) /* group quota accounting */ +#define FS_QUOTA_GDQ_ENFD (1<<3) /* group quota limits enforcement */ +#define FS_QUOTA_PDQ_ACCT (1<<4) /* project quota accounting */ +#define FS_QUOTA_PDQ_ENFD (1<<5) /* project quota limits enforcement */ -#define XFS_USER_QUOTA (1<<0) /* user quota type */ -#define XFS_PROJ_QUOTA (1<<1) /* project quota type */ -#define XFS_GROUP_QUOTA (1<<2) /* group quota type */ +#define FS_USER_QUOTA (1<<0) /* user quota type */ +#define FS_PROJ_QUOTA (1<<1) /* project quota type */ +#define FS_GROUP_QUOTA (1<<2) /* group quota type */ /* * fs_quota_stat is the struct returned in Q_XGETQSTAT for a given file system. @@ -151,7 +151,7 @@ typedef struct fs_qfilestat { typedef struct fs_quota_stat { __s8 qs_version; /* version number for future changes */ - __u16 qs_flags; /* XFS_QUOTA_{U,P,G}DQ_{ACCT,ENFD} */ + __u16 qs_flags; /* FS_QUOTA_{U,P,G}DQ_{ACCT,ENFD} */ __s8 qs_pad; /* unused */ fs_qfilestat_t qs_uquota; /* user quota storage information */ fs_qfilestat_t qs_gquota; /* group quota storage information */ -- cgit v1.2.3 From 189eef59e70e3e56edf726864629f310d114eefb Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 4 Jun 2010 10:56:29 +0200 Subject: quota: clean up quota active checks The various quota operations check for any quota beeing active on a superblock, and the inode not having the noquota flag. Merge these two checks into a dquot_active check and move that into dquot.c as that's the only place where it's needed. Signed-off-by: Christoph Hellwig Signed-off-by: Jan Kara --- fs/quota/dquot.c | 23 ++++++++++++++++------- include/linux/quotaops.h | 10 ---------- 2 files changed, 16 insertions(+), 17 deletions(-) (limited to 'fs') diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c index 2857fd67ff33..2eebf72d07c8 100644 --- a/fs/quota/dquot.c +++ b/fs/quota/dquot.c @@ -1315,6 +1315,15 @@ static int info_bdq_free(struct dquot *dquot, qsize_t space) return QUOTA_NL_NOWARN; } +static int dquot_active(const struct inode *inode) +{ + struct super_block *sb = inode->i_sb; + + if (IS_NOQUOTA(inode)) + return 0; + return sb_any_quota_loaded(sb) & ~sb_any_quota_suspended(sb); +} + /* * Initialize quota pointers in inode * @@ -1334,7 +1343,7 @@ static void __dquot_initialize(struct inode *inode, int type) /* First test before acquiring mutex - solves deadlocks when we * re-enter the quota code and are already holding the mutex */ - if (!sb_any_quota_active(inode->i_sb) || IS_NOQUOTA(inode)) + if (!dquot_active(inode)) return; /* First get references to structures we might need. */ @@ -1518,7 +1527,7 @@ int __dquot_alloc_space(struct inode *inode, qsize_t number, int flags) * First test before acquiring mutex - solves deadlocks when we * re-enter the quota code and are already holding the mutex */ - if (!sb_any_quota_active(inode->i_sb) || IS_NOQUOTA(inode)) { + if (!dquot_active(inode)) { inode_incr_space(inode, number, reserve); goto out; } @@ -1570,7 +1579,7 @@ int dquot_alloc_inode(const struct inode *inode) /* First test before acquiring mutex - solves deadlocks when we * re-enter the quota code and are already holding the mutex */ - if (!sb_any_quota_active(inode->i_sb) || IS_NOQUOTA(inode)) + if (!dquot_active(inode)) return 0; for (cnt = 0; cnt < MAXQUOTAS; cnt++) warntype[cnt] = QUOTA_NL_NOWARN; @@ -1607,7 +1616,7 @@ int dquot_claim_space_nodirty(struct inode *inode, qsize_t number) { int cnt; - if (!sb_any_quota_active(inode->i_sb) || IS_NOQUOTA(inode)) { + if (!dquot_active(inode)) { inode_claim_rsv_space(inode, number); return 0; } @@ -1640,7 +1649,7 @@ void __dquot_free_space(struct inode *inode, qsize_t number, int flags) /* First test before acquiring mutex - solves deadlocks when we * re-enter the quota code and are already holding the mutex */ - if (!sb_any_quota_active(inode->i_sb) || IS_NOQUOTA(inode)) { + if (!dquot_active(inode)) { inode_decr_space(inode, number, reserve); return; } @@ -1678,7 +1687,7 @@ void dquot_free_inode(const struct inode *inode) /* First test before acquiring mutex - solves deadlocks when we * re-enter the quota code and are already holding the mutex */ - if (!sb_any_quota_active(inode->i_sb) || IS_NOQUOTA(inode)) + if (!dquot_active(inode)) return; down_read(&sb_dqopt(inode->i_sb)->dqptr_sem); @@ -1801,7 +1810,7 @@ int dquot_transfer(struct inode *inode, struct iattr *iattr) struct super_block *sb = inode->i_sb; int ret; - if (!sb_any_quota_active(sb) || IS_NOQUOTA(inode)) + if (!dquot_active(inode)) return 0; if (iattr->ia_valid & ATTR_UID && iattr->ia_uid != inode->i_uid) diff --git a/include/linux/quotaops.h b/include/linux/quotaops.h index aa36793b48bd..126193c1a5ce 100644 --- a/include/linux/quotaops.h +++ b/include/linux/quotaops.h @@ -145,11 +145,6 @@ static inline bool sb_has_quota_active(struct super_block *sb, int type) !sb_has_quota_suspended(sb, type); } -static inline unsigned sb_any_quota_active(struct super_block *sb) -{ - return sb_any_quota_loaded(sb) & ~sb_any_quota_suspended(sb); -} - /* * Operations supported for diskquotas. */ @@ -194,11 +189,6 @@ static inline int sb_has_quota_active(struct super_block *sb, int type) return 0; } -static inline int sb_any_quota_active(struct super_block *sb) -{ - return 0; -} - static inline void dquot_initialize(struct inode *inode) { } -- cgit v1.2.3 From 0411ba7902e09111d6f2041b4697a597d2cf7736 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Thu, 10 Jun 2010 13:10:53 +0200 Subject: ext3: Fix set but unused variables [tytso@mit.edu: Fix compilation with CONFIG_JBD_DEBUG enabled] Acked-by: tytso@mit.edu cc: linux-ext4@vger.kernel.org Signed-off-by: Andi Kleen Signed-off-by: Jan Kara --- fs/ext3/namei.c | 3 +-- fs/ext3/resize.c | 2 -- fs/jbd/journal.c | 7 ------- fs/jbd/recovery.c | 11 ++--------- 4 files changed, 3 insertions(+), 20 deletions(-) (limited to 'fs') diff --git a/fs/ext3/namei.c b/fs/ext3/namei.c index ee184084ca42..2b35ddb70d65 100644 --- a/fs/ext3/namei.c +++ b/fs/ext3/namei.c @@ -1447,7 +1447,6 @@ static int ext3_add_entry (handle_t *handle, struct dentry *dentry, struct inode *inode) { struct inode *dir = dentry->d_parent->d_inode; - unsigned long offset; struct buffer_head * bh; struct ext3_dir_entry_2 *de; struct super_block * sb; @@ -1469,7 +1468,7 @@ static int ext3_add_entry (handle_t *handle, struct dentry *dentry, ext3_mark_inode_dirty(handle, dir); } blocks = dir->i_size >> sb->s_blocksize_bits; - for (block = 0, offset = 0; block < blocks; block++) { + for (block = 0; block < blocks; block++) { bh = ext3_bread(handle, dir, block, 0, &retval); if(!bh) return retval; diff --git a/fs/ext3/resize.c b/fs/ext3/resize.c index 54351ac7cef9..0ccd7b12b73c 100644 --- a/fs/ext3/resize.c +++ b/fs/ext3/resize.c @@ -964,7 +964,6 @@ int ext3_group_extend(struct super_block *sb, struct ext3_super_block *es, ext3_fsblk_t n_blocks_count) { ext3_fsblk_t o_blocks_count; - unsigned long o_groups_count; ext3_grpblk_t last; ext3_grpblk_t add; struct buffer_head * bh; @@ -976,7 +975,6 @@ int ext3_group_extend(struct super_block *sb, struct ext3_super_block *es, * yet: we're going to revalidate es->s_blocks_count after * taking the s_resize_lock below. */ o_blocks_count = le32_to_cpu(es->s_blocks_count); - o_groups_count = EXT3_SB(sb)->s_groups_count; if (test_opt(sb, DEBUG)) printk(KERN_DEBUG "EXT3-fs: extending last group from "E3FSBLK" uto "E3FSBLK" blocks\n", diff --git a/fs/jbd/journal.c b/fs/jbd/journal.c index 93d1e47647bd..f19ce94693d8 100644 --- a/fs/jbd/journal.c +++ b/fs/jbd/journal.c @@ -1281,13 +1281,9 @@ int journal_check_used_features (journal_t *journal, unsigned long compat, int journal_check_available_features (journal_t *journal, unsigned long compat, unsigned long ro, unsigned long incompat) { - journal_superblock_t *sb; - if (!compat && !ro && !incompat) return 1; - sb = journal->j_superblock; - /* We can support any known requested features iff the * superblock is in version 2. Otherwise we fail to support any * extended sb features. */ @@ -1481,7 +1477,6 @@ int journal_flush(journal_t *journal) int journal_wipe(journal_t *journal, int write) { - journal_superblock_t *sb; int err = 0; J_ASSERT (!(journal->j_flags & JFS_LOADED)); @@ -1490,8 +1485,6 @@ int journal_wipe(journal_t *journal, int write) if (err) return err; - sb = journal->j_superblock; - if (!journal->j_tail) goto no_recovery; diff --git a/fs/jbd/recovery.c b/fs/jbd/recovery.c index 54c9bc9e1b17..81051dafebf5 100644 --- a/fs/jbd/recovery.c +++ b/fs/jbd/recovery.c @@ -283,12 +283,9 @@ int journal_recover(journal_t *journal) int journal_skip_recovery(journal_t *journal) { int err; - journal_superblock_t * sb; - struct recovery_info info; memset (&info, 0, sizeof(info)); - sb = journal->j_superblock; err = do_one_pass(journal, &info, PASS_SCAN); @@ -297,7 +294,8 @@ int journal_skip_recovery(journal_t *journal) ++journal->j_transaction_sequence; } else { #ifdef CONFIG_JBD_DEBUG - int dropped = info.end_transaction - be32_to_cpu(sb->s_sequence); + int dropped = info.end_transaction - + be32_to_cpu(journal->j_superblock->s_sequence); #endif jbd_debug(1, "JBD: ignoring %d transaction%s from the journal.\n", @@ -321,11 +319,6 @@ static int do_one_pass(journal_t *journal, unsigned int sequence; int blocktype; - /* Precompute the maximum metadata descriptors in a descriptor block */ - int MAX_BLOCKS_PER_DESC; - MAX_BLOCKS_PER_DESC = ((journal->j_blocksize-sizeof(journal_header_t)) - / sizeof(journal_block_tag_t)); - /* * First thing is to establish what we expect to find in the log * (in terms of transaction IDs), and where (in terms of log -- cgit v1.2.3 From 4c4d3901225518ed1a4c938ba15ba09842a00770 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 7 Jun 2010 10:20:39 +0200 Subject: ext3: remove vestiges of nobh support The nobh option was only supported for writeback mode, but given that all write paths (except mmapped writed) actually create buffer heads, it effectively was a no-op already. Signed-off-by: Christoph Hellwig Signed-off-by: Jan Kara --- fs/ext3/inode.c | 16 +--------------- fs/ext3/super.c | 17 ++++------------- include/linux/ext3_fs.h | 1 - 3 files changed, 5 insertions(+), 29 deletions(-) (limited to 'fs') diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c index 735f0190ec2a..a786db403efc 100644 --- a/fs/ext3/inode.c +++ b/fs/ext3/inode.c @@ -1625,10 +1625,7 @@ static int ext3_writeback_writepage(struct page *page, goto out_fail; } - if (test_opt(inode->i_sb, NOBH) && ext3_should_writeback_data(inode)) - ret = nobh_writepage(page, ext3_get_block, wbc); - else - ret = block_write_full_page(page, ext3_get_block, wbc); + ret = block_write_full_page(page, ext3_get_block, wbc); err = ext3_journal_stop(handle); if (!ret) @@ -1922,17 +1919,6 @@ static int ext3_block_truncate_page(handle_t *handle, struct page *page, length = blocksize - (offset & (blocksize - 1)); iblock = index << (PAGE_CACHE_SHIFT - inode->i_sb->s_blocksize_bits); - /* - * For "nobh" option, we can only work if we don't need to - * read-in the page - otherwise we create buffers to do the IO. - */ - if (!page_has_buffers(page) && test_opt(inode->i_sb, NOBH) && - ext3_should_writeback_data(inode) && PageUptodate(page)) { - zero_user(page, offset, length); - set_page_dirty(page); - goto unlock; - } - if (!page_has_buffers(page)) create_empty_buffers(page, blocksize, 0); diff --git a/fs/ext3/super.c b/fs/ext3/super.c index 6c953bb255e7..9650a956fd0e 100644 --- a/fs/ext3/super.c +++ b/fs/ext3/super.c @@ -661,9 +661,6 @@ static int ext3_show_options(struct seq_file *seq, struct vfsmount *vfs) */ seq_puts(seq, ",barrier="); seq_puts(seq, test_opt(sb, BARRIER) ? "1" : "0"); - if (test_opt(sb, NOBH)) - seq_puts(seq, ",nobh"); - seq_printf(seq, ",data=%s", data_mode_string(test_opt(sb, DATA_FLAGS))); if (test_opt(sb, DATA_ERR_ABORT)) seq_puts(seq, ",data_err=abort"); @@ -1255,10 +1252,12 @@ set_qf_format: *n_blocks_count = option; break; case Opt_nobh: - set_opt(sbi->s_mount_opt, NOBH); + ext3_msg(sb, KERN_WARNING, + "warning: ignoring deprecated nobh option"); break; case Opt_bh: - clear_opt(sbi->s_mount_opt, NOBH); + ext3_msg(sb, KERN_WARNING, + "warning: ignoring deprecated bh option"); break; default: ext3_msg(sb, KERN_ERR, @@ -2001,14 +2000,6 @@ static int ext3_fill_super (struct super_block *sb, void *data, int silent) break; } - if (test_opt(sb, NOBH)) { - if (!(test_opt(sb, DATA_FLAGS) == EXT3_MOUNT_WRITEBACK_DATA)) { - ext3_msg(sb, KERN_WARNING, - "warning: ignoring nobh option - " - "it is supported only with writeback mode"); - clear_opt(sbi->s_mount_opt, NOBH); - } - } /* * The journal_load will have done any necessary log recovery, * so we can safely mount the rest of the filesystem now. diff --git a/include/linux/ext3_fs.h b/include/linux/ext3_fs.h index 7fc62d4550b2..3d3a9915dde2 100644 --- a/include/linux/ext3_fs.h +++ b/include/linux/ext3_fs.h @@ -400,7 +400,6 @@ struct ext3_inode { #define EXT3_MOUNT_POSIX_ACL 0x08000 /* POSIX Access Control Lists */ #define EXT3_MOUNT_RESERVATION 0x10000 /* Preallocation */ #define EXT3_MOUNT_BARRIER 0x20000 /* Use block barriers */ -#define EXT3_MOUNT_NOBH 0x40000 /* No bufferheads */ #define EXT3_MOUNT_QUOTA 0x80000 /* Some quota option set */ #define EXT3_MOUNT_USRQUOTA 0x100000 /* "old" user quota */ #define EXT3_MOUNT_GRPQUOTA 0x200000 /* "old" group quota */ -- cgit v1.2.3 From f25f624263445785b94f39739a6339ba9ed3275d Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Mon, 12 Jul 2010 21:04:31 +0200 Subject: ext3: Avoid filesystem corruption after a crash under heavy delete load It can happen that ext3_free_branches calls ext3_forget() for an indirect block in an earlier transaction than a transaction in which we clear pointer to this indirect block. Thus if we crash before a transaction clearing the block pointer is committed, we will see indirect block pointing to already freed blocks and complain during orphan list cleanup. The fix is simple: Make sure ext3_forget() is called in the transaction doing block pointer clearing. This is a backport of an ext4 fix by Amir G. Signed-off-by: Jan Kara --- fs/ext3/inode.c | 46 +++++++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 21 deletions(-) (limited to 'fs') diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c index a786db403efc..436e5bbccbc2 100644 --- a/fs/ext3/inode.c +++ b/fs/ext3/inode.c @@ -2269,27 +2269,6 @@ static void ext3_free_branches(handle_t *handle, struct inode *inode, (__le32*)bh->b_data + addr_per_block, depth); - /* - * We've probably journalled the indirect block several - * times during the truncate. But it's no longer - * needed and we now drop it from the transaction via - * journal_revoke(). - * - * That's easy if it's exclusively part of this - * transaction. But if it's part of the committing - * transaction then journal_forget() will simply - * brelse() it. That means that if the underlying - * block is reallocated in ext3_get_block(), - * unmap_underlying_metadata() will find this block - * and will try to get rid of it. damn, damn. - * - * If this block has already been committed to the - * journal, a revoke record will be written. And - * revoke records must be emitted *before* clearing - * this block's bit in the bitmaps. - */ - ext3_forget(handle, 1, inode, bh, bh->b_blocknr); - /* * Everything below this this pointer has been * released. Now let this top-of-subtree go. @@ -2313,6 +2292,31 @@ static void ext3_free_branches(handle_t *handle, struct inode *inode, truncate_restart_transaction(handle, inode); } + /* + * We've probably journalled the indirect block several + * times during the truncate. But it's no longer + * needed and we now drop it from the transaction via + * journal_revoke(). + * + * That's easy if it's exclusively part of this + * transaction. But if it's part of the committing + * transaction then journal_forget() will simply + * brelse() it. That means that if the underlying + * block is reallocated in ext3_get_block(), + * unmap_underlying_metadata() will find this block + * and will try to get rid of it. damn, damn. Thus + * we don't allow a block to be reallocated until + * a transaction freeing it has fully committed. + * + * We also have to make sure journal replay after a + * crash does not overwrite non-journaled data blocks + * with old metadata when the block got reallocated for + * data. Thus we have to store a revoke record for a + * block in the same transaction in which we free the + * block. + */ + ext3_forget(handle, 1, inode, bh, bh->b_blocknr); + ext3_free_blocks(handle, inode, nr, 1); if (parent_bh) { -- cgit v1.2.3 From fb5ffb0e160c93c3fe08ab83845eb9a2768af812 Mon Sep 17 00:00:00 2001 From: Jiaying Zhang Date: Tue, 20 Jul 2010 16:54:43 +0200 Subject: quota: Change quota error message to print out disk and function name The current quota error message doesn't always print the disk name, so it is hard to identify the "bad" disk when quota error happens. This patch changes the standardized quota error message to print out disk name and function name. It also uses a combination of cpp macro and inline function to provide better type checking and to lower the text size of the message. [Jan Kara: Export __quota_error] Signed-off-by: Jiaying Zhang Signed-off-by: Jan Kara --- fs/quota/dquot.c | 39 +++++++++++++++------- fs/quota/quota_tree.c | 85 ++++++++++++++++++++++++------------------------ fs/quota/quota_tree.h | 6 ---- fs/quota/quota_v1.c | 3 +- fs/quota/quota_v2.c | 11 +++---- include/linux/quotaops.h | 6 ++++ 6 files changed, 80 insertions(+), 70 deletions(-) (limited to 'fs') diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c index 2eebf72d07c8..b171221000fa 100644 --- a/fs/quota/dquot.c +++ b/fs/quota/dquot.c @@ -132,6 +132,22 @@ static __cacheline_aligned_in_smp DEFINE_SPINLOCK(dq_state_lock); __cacheline_aligned_in_smp DEFINE_SPINLOCK(dq_data_lock); EXPORT_SYMBOL(dq_data_lock); +void __quota_error(struct super_block *sb, const char *func, + const char *fmt, ...) +{ + va_list args; + + if (printk_ratelimit()) { + va_start(args, fmt); + printk(KERN_ERR "Quota error (device %s): %s: ", + sb->s_id, func); + vprintk(fmt, args); + printk("\n"); + va_end(args); + } +} +EXPORT_SYMBOL(__quota_error); + #if defined(CONFIG_QUOTA_DEBUG) || defined(CONFIG_PRINT_QUOTA_WARNING) static char *quotatypes[] = INITQFNAMES; #endif @@ -705,11 +721,8 @@ void dqput(struct dquot *dquot) return; #ifdef CONFIG_QUOTA_DEBUG if (!atomic_read(&dquot->dq_count)) { - printk("VFS: dqput: trying to free free dquot\n"); - printk("VFS: device %s, dquot of %s %d\n", - dquot->dq_sb->s_id, - quotatypes[dquot->dq_type], - dquot->dq_id); + quota_error(dquot->dq_sb, "trying to free free dquot of %s %d", + quotatypes[dquot->dq_type], dquot->dq_id); BUG(); } #endif @@ -732,9 +745,9 @@ we_slept: /* Commit dquot before releasing */ ret = dquot->dq_sb->dq_op->write_dquot(dquot); if (ret < 0) { - printk(KERN_ERR "VFS: cannot write quota structure on " - "device %s (error %d). Quota may get out of " - "sync!\n", dquot->dq_sb->s_id, ret); + quota_error(dquot->dq_sb, "Can't write quota structure" + " (error %d). Quota may get out of sync!", + ret); /* * We clear dirty bit anyway, so that we avoid * infinite loop here @@ -914,9 +927,9 @@ static void add_dquot_ref(struct super_block *sb, int type) #ifdef CONFIG_QUOTA_DEBUG if (reserved) { - printk(KERN_WARNING "VFS (%s): Writes happened before quota" - " was turned on thus quota information is probably " - "inconsistent. Please run quotacheck(8).\n", sb->s_id); + quota_error(sb, "Writes happened before quota was turned on " + "thus quota information is probably inconsistent. " + "Please run quotacheck(8)"); } #endif } @@ -947,7 +960,9 @@ static int remove_inode_dquot_ref(struct inode *inode, int type, if (dqput_blocks(dquot)) { #ifdef CONFIG_QUOTA_DEBUG if (atomic_read(&dquot->dq_count) != 1) - printk(KERN_WARNING "VFS: Adding dquot with dq_count %d to dispose list.\n", atomic_read(&dquot->dq_count)); + quota_error(inode->i_sb, "Adding dquot with " + "dq_count %d to dispose list", + atomic_read(&dquot->dq_count)); #endif spin_lock(&dq_list_lock); /* As dquot must have currently users it can't be on diff --git a/fs/quota/quota_tree.c b/fs/quota/quota_tree.c index 24f03407eeb5..9e48874eabcc 100644 --- a/fs/quota/quota_tree.c +++ b/fs/quota/quota_tree.c @@ -65,8 +65,7 @@ static ssize_t write_blk(struct qtree_mem_dqinfo *info, uint blk, char *buf) ret = sb->s_op->quota_write(sb, info->dqi_type, buf, info->dqi_usable_bs, blk << info->dqi_blocksize_bits); if (ret != info->dqi_usable_bs) { - q_warn(KERN_WARNING "VFS: dquota write failed on " - "dev %s\n", sb->s_id); + quota_error(sb, "dquota write failed"); if (ret >= 0) ret = -EIO; } @@ -160,9 +159,8 @@ static int remove_free_dqentry(struct qtree_mem_dqinfo *info, char *buf, dh->dqdh_next_free = dh->dqdh_prev_free = cpu_to_le32(0); /* No matter whether write succeeds block is out of list */ if (write_blk(info, blk, buf) < 0) - q_warn(KERN_ERR - "VFS: Can't write block (%u) with free entries.\n", - blk); + quota_error(info->dqi_sb, "Can't write block (%u) " + "with free entries", blk); return 0; out_buf: kfree(tmpbuf); @@ -252,9 +250,8 @@ static uint find_free_dqentry(struct qtree_mem_dqinfo *info, if (le16_to_cpu(dh->dqdh_entries) + 1 >= qtree_dqstr_in_blk(info)) { *err = remove_free_dqentry(info, buf, blk); if (*err < 0) { - q_warn(KERN_ERR "VFS: find_free_dqentry(): Can't " - "remove block (%u) from entry free list.\n", - blk); + quota_error(dquot->dq_sb, "Can't remove block (%u) " + "from entry free list", blk); goto out_buf; } } @@ -268,16 +265,15 @@ static uint find_free_dqentry(struct qtree_mem_dqinfo *info, } #ifdef __QUOTA_QT_PARANOIA if (i == qtree_dqstr_in_blk(info)) { - printk(KERN_ERR "VFS: find_free_dqentry(): Data block full " - "but it shouldn't.\n"); + quota_error(dquot->dq_sb, "Data block full but it shouldn't"); *err = -EIO; goto out_buf; } #endif *err = write_blk(info, blk, buf); if (*err < 0) { - q_warn(KERN_ERR "VFS: find_free_dqentry(): Can't write quota " - "data block %u.\n", blk); + quota_error(dquot->dq_sb, "Can't write quota data block %u", + blk); goto out_buf; } dquot->dq_off = (blk << info->dqi_blocksize_bits) + @@ -311,8 +307,8 @@ static int do_insert_tree(struct qtree_mem_dqinfo *info, struct dquot *dquot, } else { ret = read_blk(info, *treeblk, buf); if (ret < 0) { - q_warn(KERN_ERR "VFS: Can't read tree quota block " - "%u.\n", *treeblk); + quota_error(dquot->dq_sb, "Can't read tree quota " + "block %u", *treeblk); goto out_buf; } } @@ -323,9 +319,9 @@ static int do_insert_tree(struct qtree_mem_dqinfo *info, struct dquot *dquot, if (depth == info->dqi_qtree_depth - 1) { #ifdef __QUOTA_QT_PARANOIA if (newblk) { - printk(KERN_ERR "VFS: Inserting already present quota " - "entry (block %u).\n", - le32_to_cpu(ref[get_index(info, + quota_error(dquot->dq_sb, "Inserting already present " + "quota entry (block %u)", + le32_to_cpu(ref[get_index(info, dquot->dq_id, depth)])); ret = -EIO; goto out_buf; @@ -373,8 +369,8 @@ int qtree_write_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot) if (!dquot->dq_off) { ret = dq_insert_tree(info, dquot); if (ret < 0) { - q_warn(KERN_ERR "VFS: Error %zd occurred while " - "creating quota.\n", ret); + quota_error(sb, "Error %zd occurred while creating " + "quota", ret); kfree(ddquot); return ret; } @@ -385,8 +381,7 @@ int qtree_write_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot) ret = sb->s_op->quota_write(sb, type, ddquot, info->dqi_entry_size, dquot->dq_off); if (ret != info->dqi_entry_size) { - q_warn(KERN_WARNING "VFS: dquota write failed on dev %s\n", - sb->s_id); + quota_error(sb, "dquota write failed"); if (ret >= 0) ret = -ENOSPC; } else { @@ -410,14 +405,15 @@ static int free_dqentry(struct qtree_mem_dqinfo *info, struct dquot *dquot, if (!buf) return -ENOMEM; if (dquot->dq_off >> info->dqi_blocksize_bits != blk) { - q_warn(KERN_ERR "VFS: Quota structure has offset to other " - "block (%u) than it should (%u).\n", blk, - (uint)(dquot->dq_off >> info->dqi_blocksize_bits)); + quota_error(dquot->dq_sb, "Quota structure has offset to " + "other block (%u) than it should (%u)", blk, + (uint)(dquot->dq_off >> info->dqi_blocksize_bits)); goto out_buf; } ret = read_blk(info, blk, buf); if (ret < 0) { - q_warn(KERN_ERR "VFS: Can't read quota data block %u\n", blk); + quota_error(dquot->dq_sb, "Can't read quota data block %u", + blk); goto out_buf; } dh = (struct qt_disk_dqdbheader *)buf; @@ -427,8 +423,8 @@ static int free_dqentry(struct qtree_mem_dqinfo *info, struct dquot *dquot, if (ret >= 0) ret = put_free_dqblk(info, buf, blk); if (ret < 0) { - q_warn(KERN_ERR "VFS: Can't move quota data block (%u) " - "to free list.\n", blk); + quota_error(dquot->dq_sb, "Can't move quota data block " + "(%u) to free list", blk); goto out_buf; } } else { @@ -440,15 +436,15 @@ static int free_dqentry(struct qtree_mem_dqinfo *info, struct dquot *dquot, /* Insert will write block itself */ ret = insert_free_dqentry(info, buf, blk); if (ret < 0) { - q_warn(KERN_ERR "VFS: Can't insert quota data " - "block (%u) to free entry list.\n", blk); + quota_error(dquot->dq_sb, "Can't insert quota " + "data block (%u) to free entry list", blk); goto out_buf; } } else { ret = write_blk(info, blk, buf); if (ret < 0) { - q_warn(KERN_ERR "VFS: Can't write quota data " - "block %u\n", blk); + quota_error(dquot->dq_sb, "Can't write quota " + "data block %u", blk); goto out_buf; } } @@ -472,7 +468,8 @@ static int remove_tree(struct qtree_mem_dqinfo *info, struct dquot *dquot, return -ENOMEM; ret = read_blk(info, *blk, buf); if (ret < 0) { - q_warn(KERN_ERR "VFS: Can't read quota data block %u\n", *blk); + quota_error(dquot->dq_sb, "Can't read quota data " + "block %u", blk); goto out_buf; } newblk = le32_to_cpu(ref[get_index(info, dquot->dq_id, depth)]); @@ -496,8 +493,8 @@ static int remove_tree(struct qtree_mem_dqinfo *info, struct dquot *dquot, } else { ret = write_blk(info, *blk, buf); if (ret < 0) - q_warn(KERN_ERR "VFS: Can't write quota tree " - "block %u.\n", *blk); + quota_error(dquot->dq_sb, "Can't write quota " + "tree block %u", blk); } } out_buf: @@ -529,7 +526,8 @@ static loff_t find_block_dqentry(struct qtree_mem_dqinfo *info, return -ENOMEM; ret = read_blk(info, blk, buf); if (ret < 0) { - q_warn(KERN_ERR "VFS: Can't read quota tree block %u.\n", blk); + quota_error(dquot->dq_sb, "Can't read quota tree " + "block %u", blk); goto out_buf; } ddquot = buf + sizeof(struct qt_disk_dqdbheader); @@ -539,8 +537,8 @@ static loff_t find_block_dqentry(struct qtree_mem_dqinfo *info, ddquot += info->dqi_entry_size; } if (i == qtree_dqstr_in_blk(info)) { - q_warn(KERN_ERR "VFS: Quota for id %u referenced " - "but not present.\n", dquot->dq_id); + quota_error(dquot->dq_sb, "Quota for id %u referenced " + "but not present", dquot->dq_id); ret = -EIO; goto out_buf; } else { @@ -564,7 +562,8 @@ static loff_t find_tree_dqentry(struct qtree_mem_dqinfo *info, return -ENOMEM; ret = read_blk(info, blk, buf); if (ret < 0) { - q_warn(KERN_ERR "VFS: Can't read quota tree block %u.\n", blk); + quota_error(dquot->dq_sb, "Can't read quota tree block %u", + blk); goto out_buf; } ret = 0; @@ -598,7 +597,7 @@ int qtree_read_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot) #ifdef __QUOTA_QT_PARANOIA /* Invalidated quota? */ if (!sb_dqopt(dquot->dq_sb)->files[type]) { - printk(KERN_ERR "VFS: Quota invalidated while reading!\n"); + quota_error(sb, "Quota invalidated while reading!"); return -EIO; } #endif @@ -607,8 +606,8 @@ int qtree_read_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot) offset = find_dqentry(info, dquot); if (offset <= 0) { /* Entry not present? */ if (offset < 0) - q_warn(KERN_ERR "VFS: Can't read quota " - "structure for id %u.\n", dquot->dq_id); + quota_error(sb, "Can't read quota structure " + "for id %u", dquot->dq_id); dquot->dq_off = 0; set_bit(DQ_FAKE_B, &dquot->dq_flags); memset(&dquot->dq_dqb, 0, sizeof(struct mem_dqblk)); @@ -625,8 +624,8 @@ int qtree_read_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot) if (ret != info->dqi_entry_size) { if (ret >= 0) ret = -EIO; - q_warn(KERN_ERR "VFS: Error while reading quota " - "structure for id %u.\n", dquot->dq_id); + quota_error(sb, "Error while reading quota structure for id %u", + dquot->dq_id); set_bit(DQ_FAKE_B, &dquot->dq_flags); memset(&dquot->dq_dqb, 0, sizeof(struct mem_dqblk)); kfree(ddquot); diff --git a/fs/quota/quota_tree.h b/fs/quota/quota_tree.h index ccc3e71fb1d8..a1ab8db81a51 100644 --- a/fs/quota/quota_tree.h +++ b/fs/quota/quota_tree.h @@ -22,10 +22,4 @@ struct qt_disk_dqdbheader { #define QT_TREEOFF 1 /* Offset of tree in file in blocks */ -#define q_warn(fmt, args...) \ -do { \ - if (printk_ratelimit()) \ - printk(fmt, ## args); \ -} while(0) - #endif /* _LINUX_QUOTAIO_TREE_H */ diff --git a/fs/quota/quota_v1.c b/fs/quota/quota_v1.c index 4af344c5852a..34b37a67bb16 100644 --- a/fs/quota/quota_v1.c +++ b/fs/quota/quota_v1.c @@ -95,8 +95,7 @@ static int v1_commit_dqblk(struct dquot *dquot) (char *)&dqblk, sizeof(struct v1_disk_dqblk), v1_dqoff(dquot->dq_id)); if (ret != sizeof(struct v1_disk_dqblk)) { - printk(KERN_WARNING "VFS: dquota write failed on dev %s\n", - dquot->dq_sb->s_id); + quota_error(dquot->dq_sb, "dquota write failed"); if (ret >= 0) ret = -EIO; goto out; diff --git a/fs/quota/quota_v2.c b/fs/quota/quota_v2.c index 135206af1458..65444d29406b 100644 --- a/fs/quota/quota_v2.c +++ b/fs/quota/quota_v2.c @@ -63,9 +63,8 @@ static int v2_read_header(struct super_block *sb, int type, size = sb->s_op->quota_read(sb, type, (char *)dqhead, sizeof(struct v2_disk_dqheader), 0); if (size != sizeof(struct v2_disk_dqheader)) { - q_warn(KERN_WARNING "quota_v2: Failed header read:" - " expected=%zd got=%zd\n", - sizeof(struct v2_disk_dqheader), size); + quota_error(sb, "Failed header read: expected=%zd got=%zd", + sizeof(struct v2_disk_dqheader), size); return 0; } return 1; @@ -106,8 +105,7 @@ static int v2_read_file_info(struct super_block *sb, int type) size = sb->s_op->quota_read(sb, type, (char *)&dinfo, sizeof(struct v2_disk_dqinfo), V2_DQINFOOFF); if (size != sizeof(struct v2_disk_dqinfo)) { - q_warn(KERN_WARNING "quota_v2: Can't read info structure on device %s.\n", - sb->s_id); + quota_error(sb, "Can't read info structure"); return -1; } info->dqi_priv = kmalloc(sizeof(struct qtree_mem_dqinfo), GFP_NOFS); @@ -167,8 +165,7 @@ static int v2_write_file_info(struct super_block *sb, int type) size = sb->s_op->quota_write(sb, type, (char *)&dinfo, sizeof(struct v2_disk_dqinfo), V2_DQINFOOFF); if (size != sizeof(struct v2_disk_dqinfo)) { - q_warn(KERN_WARNING "Can't write info structure on device %s.\n", - sb->s_id); + quota_error(sb, "Can't write info structure"); return -1; } return 0; diff --git a/include/linux/quotaops.h b/include/linux/quotaops.h index 126193c1a5ce..4881b49b1a9a 100644 --- a/include/linux/quotaops.h +++ b/include/linux/quotaops.h @@ -28,6 +28,12 @@ static inline bool is_quota_modification(struct inode *inode, struct iattr *ia) #if defined(CONFIG_QUOTA) +#define quota_error(sb, fmt, args...) \ + __quota_error((sb), __func__, fmt , ## args) + +extern void __quota_error(struct super_block *sb, const char *func, + const char *fmt, ...); + /* * declaration of quota_function calls in kernel. */ -- cgit v1.2.3 From a4ce96ac356e7024a7724ade9d18ba1bdf3c5c06 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Wed, 21 Jul 2010 09:25:42 -0700 Subject: Fix up trivial spelling errors ('taht' -> 'that') Pointed out by Lucas who found the new one in a comment in setup_percpu.c. And then I fixed the others that I grepped for. Reported-by: Lucas Signed-off-by: Linus Torvalds --- arch/x86/kernel/setup_percpu.c | 2 +- drivers/usb/gadget/f_fs.c | 2 +- drivers/video/aty/radeon_pm.c | 2 +- fs/jffs2/xattr.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/arch/x86/kernel/setup_percpu.c b/arch/x86/kernel/setup_percpu.c index 690c2c09faf3..a60df9ae6454 100644 --- a/arch/x86/kernel/setup_percpu.c +++ b/arch/x86/kernel/setup_percpu.c @@ -239,7 +239,7 @@ void __init setup_per_cpu_areas(void) per_cpu(x86_cpu_to_node_map, cpu) = early_per_cpu_map(x86_cpu_to_node_map, cpu); /* - * Ensure taht the boot cpu numa_node is correct when the boot + * Ensure that the boot cpu numa_node is correct when the boot * cpu is on a node that doesn't have memory installed. * Also cpu_up() will call cpu_to_node() for APs when * MEMORY_HOTPLUG is defined, before per_cpu(numa_node) is set diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c index d69eccf5f197..2aaa0f75c6cf 100644 --- a/drivers/usb/gadget/f_fs.c +++ b/drivers/usb/gadget/f_fs.c @@ -136,7 +136,7 @@ struct ffs_data { * handling setup requests immidiatelly user space may be so * slow that another setup will be sent to the gadget but this * time not to us but another function and then there could be - * a race. Is taht the case? Or maybe we can use cdev->req + * a race. Is that the case? Or maybe we can use cdev->req * after all, maybe we just need some spinlock for that? */ struct usb_request *ep0req; /* P: mutex */ struct completion ep0req_completion; /* P: mutex */ diff --git a/drivers/video/aty/radeon_pm.c b/drivers/video/aty/radeon_pm.c index 515cf1978d19..c4e17642d9c5 100644 --- a/drivers/video/aty/radeon_pm.c +++ b/drivers/video/aty/radeon_pm.c @@ -2872,7 +2872,7 @@ void radeonfb_pm_init(struct radeonfb_info *rinfo, int dynclk, int ignore_devlis } #if 0 - /* Power down TV DAC, taht saves a significant amount of power, + /* Power down TV DAC, that saves a significant amount of power, * we'll have something better once we actually have some TVOut * support */ diff --git a/fs/jffs2/xattr.c b/fs/jffs2/xattr.c index a2d58c96f1b4..d258e261bdc7 100644 --- a/fs/jffs2/xattr.c +++ b/fs/jffs2/xattr.c @@ -626,7 +626,7 @@ void jffs2_xattr_free_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *i static int check_xattr_ref_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic) { - /* success of check_xattr_ref_inode() means taht inode (ic) dose not have + /* success of check_xattr_ref_inode() means that inode (ic) dose not have * duplicate name/value pairs. If duplicate name/value pair would be found, * one will be removed. */ -- cgit v1.2.3 From f03585689fdff4ae256edd45a35bc2dd83d3684a Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 18 May 2010 13:20:32 +0200 Subject: Bluetooth: Add blacklist support for incoming connections In some circumstances it could be desirable to reject incoming connections on the baseband level. This patch adds this feature through two new ioctl's: HCIBLOCKADDR and HCIUNBLOCKADDR. Both take a simple Bluetooth address as a parameter. BDADDR_ANY can be used with HCIUNBLOCKADDR to remove all devices from the blacklist. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- fs/compat_ioctl.c | 2 + include/net/bluetooth/hci.h | 3 ++ include/net/bluetooth/hci_core.h | 9 ++++ net/bluetooth/hci_core.c | 3 ++ net/bluetooth/hci_event.c | 2 +- net/bluetooth/hci_sock.c | 90 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 108 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c index 641640dc7ae5..18638969a659 100644 --- a/fs/compat_ioctl.c +++ b/fs/compat_ioctl.c @@ -1328,6 +1328,8 @@ COMPATIBLE_IOCTL(HCISETLINKPOL) COMPATIBLE_IOCTL(HCISETLINKMODE) COMPATIBLE_IOCTL(HCISETACLMTU) COMPATIBLE_IOCTL(HCISETSCOMTU) +COMPATIBLE_IOCTL(HCIBLOCKADDR) +COMPATIBLE_IOCTL(HCIUNBLOCKADDR) COMPATIBLE_IOCTL(HCIINQUIRY) COMPATIBLE_IOCTL(HCIUARTSETPROTO) COMPATIBLE_IOCTL(HCIUARTGETPROTO) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index fc0c502d9fd1..ca2518e0574e 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -100,6 +100,9 @@ enum { #define HCISETACLMTU _IOW('H', 227, int) #define HCISETSCOMTU _IOW('H', 228, int) +#define HCIBLOCKADDR _IOW('H', 230, int) +#define HCIUNBLOCKADDR _IOW('H', 231, int) + #define HCIINQUIRY _IOR('H', 240, int) /* HCI timeouts */ diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index e42f6ed5421c..ffc637748b87 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -62,6 +62,11 @@ struct hci_conn_hash { unsigned int sco_num; }; +struct bdaddr_list { + struct list_head list; + bdaddr_t bdaddr; +}; + struct hci_dev { struct list_head list; spinlock_t lock; @@ -127,6 +132,7 @@ struct hci_dev { struct inquiry_cache inq_cache; struct hci_conn_hash conn_hash; + struct bdaddr_list blacklist; struct hci_dev_stats stat; @@ -424,6 +430,9 @@ int hci_get_conn_info(struct hci_dev *hdev, void __user *arg); int hci_get_auth_info(struct hci_dev *hdev, void __user *arg); int hci_inquiry(void __user *arg); +struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr); +int hci_blacklist_clear(struct hci_dev *hdev); + void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb); int hci_recv_frame(struct sk_buff *skb); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 2f768de87011..aeb2982310a0 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -562,6 +562,7 @@ static int hci_dev_do_close(struct hci_dev *hdev) hci_dev_lock_bh(hdev); inquiry_cache_flush(hdev); hci_conn_hash_flush(hdev); + hci_blacklist_clear(hdev); hci_dev_unlock_bh(hdev); hci_notify(hdev, HCI_DEV_DOWN); @@ -923,6 +924,8 @@ int hci_register_dev(struct hci_dev *hdev) hci_conn_hash_init(hdev); + INIT_LIST_HEAD(&hdev->blacklist.list); + memset(&hdev->stat, 0, sizeof(struct hci_dev_stats)); atomic_set(&hdev->promisc, 0); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 786b5de0bac4..43feeef3c498 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -952,7 +952,7 @@ static inline void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *sk mask |= hci_proto_connect_ind(hdev, &ev->bdaddr, ev->link_type); - if (mask & HCI_LM_ACCEPT) { + if ((mask & HCI_LM_ACCEPT) && !hci_blacklist_lookup(hdev, &ev->bdaddr)) { /* Connection accepted */ struct inquiry_entry *ie; struct hci_conn *conn; diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index 38f08f6b86f6..4f170a595934 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -165,6 +165,86 @@ static int hci_sock_release(struct socket *sock) return 0; } +struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr) +{ + struct list_head *p; + struct bdaddr_list *blacklist = &hdev->blacklist; + + list_for_each(p, &blacklist->list) { + struct bdaddr_list *b; + + b = list_entry(p, struct bdaddr_list, list); + + if (bacmp(bdaddr, &b->bdaddr) == 0) + return b; + } + + return NULL; +} + +static int hci_blacklist_add(struct hci_dev *hdev, void __user *arg) +{ + bdaddr_t bdaddr; + struct bdaddr_list *entry; + + if (copy_from_user(&bdaddr, arg, sizeof(bdaddr))) + return -EFAULT; + + if (bacmp(&bdaddr, BDADDR_ANY) == 0) + return -EBADF; + + if (hci_blacklist_lookup(hdev, &bdaddr)) + return -EEXIST; + + entry = kzalloc(sizeof(struct bdaddr_list), GFP_KERNEL); + if (!entry) + return -ENOMEM; + + bacpy(&entry->bdaddr, &bdaddr); + + list_add(&entry->list, &hdev->blacklist.list); + + return 0; +} + +int hci_blacklist_clear(struct hci_dev *hdev) +{ + struct list_head *p, *n; + struct bdaddr_list *blacklist = &hdev->blacklist; + + list_for_each_safe(p, n, &blacklist->list) { + struct bdaddr_list *b; + + b = list_entry(p, struct bdaddr_list, list); + + list_del(p); + kfree(b); + } + + return 0; +} + +static int hci_blacklist_del(struct hci_dev *hdev, void __user *arg) +{ + bdaddr_t bdaddr; + struct bdaddr_list *entry; + + if (copy_from_user(&bdaddr, arg, sizeof(bdaddr))) + return -EFAULT; + + if (bacmp(&bdaddr, BDADDR_ANY) == 0) + return hci_blacklist_clear(hdev); + + entry = hci_blacklist_lookup(hdev, &bdaddr); + if (!entry) + return -ENOENT; + + list_del(&entry->list); + kfree(entry); + + return 0; +} + /* Ioctls that require bound socket */ static inline int hci_sock_bound_ioctl(struct sock *sk, unsigned int cmd, unsigned long arg) { @@ -194,6 +274,16 @@ static inline int hci_sock_bound_ioctl(struct sock *sk, unsigned int cmd, unsign case HCIGETAUTHINFO: return hci_get_auth_info(hdev, (void __user *) arg); + case HCIBLOCKADDR: + if (!capable(CAP_NET_ADMIN)) + return -EACCES; + return hci_blacklist_add(hdev, (void __user *) arg); + + case HCIUNBLOCKADDR: + if (!capable(CAP_NET_ADMIN)) + return -EACCES; + return hci_blacklist_del(hdev, (void __user *) arg); + default: if (hdev->ioctl) return hdev->ioctl(hdev, cmd, arg); -- cgit v1.2.3 From 2cdf096fffaa257e7449611295a38c46dc921c8b Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 9 Jul 2010 16:28:36 -0300 Subject: Bluetooth: Add missing HCIUARTGETDEVICE ioctl to compat_ioctl.c Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- fs/compat_ioctl.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c index 18638969a659..547452de5ca2 100644 --- a/fs/compat_ioctl.c +++ b/fs/compat_ioctl.c @@ -601,8 +601,9 @@ static int ioc_settimeout(unsigned int fd, unsigned int cmd, } /* Bluetooth ioctls */ -#define HCIUARTSETPROTO _IOW('U', 200, int) -#define HCIUARTGETPROTO _IOR('U', 201, int) +#define HCIUARTSETPROTO _IOW('U', 200, int) +#define HCIUARTGETPROTO _IOR('U', 201, int) +#define HCIUARTGETDEVICE _IOR('U', 202, int) #define BNEPCONNADD _IOW('B', 200, int) #define BNEPCONNDEL _IOW('B', 201, int) -- cgit v1.2.3 From 63c7d09cd52fe23ad2baee26bcc10a590944cfa4 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 12 Jul 2010 11:37:04 -0300 Subject: Bluetooth: Add HCIUARTSETFLAGS and HCIUARTGETFLAGS ioctls This patch introduces two new ioctls: HCIUARTSETFLAGS and HCIUARTGETFLAGS. The only flag available for now is HCI_UART_RAW_DEVICE which allows to initialize a UART device into RAW mode from userspace. This is particularly useful for experimenting with Bluetooth controllers that don't yet have proper support in BlueZ. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_ldisc.c | 12 ++++++++++++ drivers/bluetooth/hci_uart.h | 7 ++++++- fs/compat_ioctl.c | 2 ++ 3 files changed, 20 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index e8beffe80b31..a57dbfccb3fb 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -395,6 +395,9 @@ static int hci_uart_register_dev(struct hci_uart *hu) if (!reset) set_bit(HCI_QUIRK_NO_RESET, &hdev->quirks); + if (test_bit(HCI_UART_RAW_DEVICE, &hu->hdev_flags)) + set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks); + if (hci_register_dev(hdev) < 0) { BT_ERR("Can't register HCI device"); hci_free_dev(hdev); @@ -475,6 +478,15 @@ static int hci_uart_tty_ioctl(struct tty_struct *tty, struct file * file, return hu->hdev->id; return -EUNATCH; + case HCIUARTSETFLAGS: + if (test_bit(HCI_UART_PROTO_SET, &hu->flags)) + return -EBUSY; + hu->hdev_flags = arg; + break; + + case HCIUARTGETFLAGS: + return hu->hdev_flags; + default: err = n_tty_ioctl_helper(tty, file, cmd, arg); break; diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h index 50113db06b9f..9694d9d60905 100644 --- a/drivers/bluetooth/hci_uart.h +++ b/drivers/bluetooth/hci_uart.h @@ -31,6 +31,8 @@ #define HCIUARTSETPROTO _IOW('U', 200, int) #define HCIUARTGETPROTO _IOR('U', 201, int) #define HCIUARTGETDEVICE _IOR('U', 202, int) +#define HCIUARTSETFLAGS _IOW('U', 203, int) +#define HCIUARTGETFLAGS _IOR('U', 204, int) /* UART protocols */ #define HCI_UART_MAX_PROTO 5 @@ -41,6 +43,8 @@ #define HCI_UART_H4DS 3 #define HCI_UART_LL 4 +#define HCI_UART_RAW_DEVICE 0 + struct hci_uart; struct hci_uart_proto { @@ -57,6 +61,7 @@ struct hci_uart { struct tty_struct *tty; struct hci_dev *hdev; unsigned long flags; + unsigned long hdev_flags; struct hci_uart_proto *proto; void *priv; @@ -66,7 +71,7 @@ struct hci_uart { spinlock_t rx_lock; }; -/* HCI_UART flag bits */ +/* HCI_UART proto flag bits */ #define HCI_UART_PROTO_SET 0 /* TX states */ diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c index 547452de5ca2..8ea5e3374507 100644 --- a/fs/compat_ioctl.c +++ b/fs/compat_ioctl.c @@ -604,6 +604,8 @@ static int ioc_settimeout(unsigned int fd, unsigned int cmd, #define HCIUARTSETPROTO _IOW('U', 200, int) #define HCIUARTGETPROTO _IOR('U', 201, int) #define HCIUARTGETDEVICE _IOR('U', 202, int) +#define HCIUARTSETFLAGS _IOW('U', 203, int) +#define HCIUARTGETFLAGS _IOR('U', 204, int) #define BNEPCONNADD _IOW('B', 200, int) #define BNEPCONNDEL _IOW('B', 201, int) -- cgit v1.2.3 From 4c0c03ca54f72fdd5912516ad0a23ec5cf01bda7 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 22 Jul 2010 12:53:18 +0100 Subject: CIFS: Fix a malicious redirect problem in the DNS lookup code Fix the security problem in the CIFS filesystem DNS lookup code in which a malicious redirect could be installed by a random user by simply adding a result record into one of their keyrings with add_key() and then invoking a CIFS CFS lookup [CVE-2010-2524]. This is done by creating an internal keyring specifically for the caching of DNS lookups. To enforce the use of this keyring, the module init routine creates a set of override credentials with the keyring installed as the thread keyring and instructs request_key() to only install lookup result keys in that keyring. The override is then applied around the call to request_key(). This has some additional benefits when a kernel service uses this module to request a key: (1) The result keys are owned by root, not the user that caused the lookup. (2) The result keys don't pop up in the user's keyrings. (3) The result keys don't come out of the quota of the user that caused the lookup. The keyring can be viewed as root by doing cat /proc/keys: 2a0ca6c3 I----- 1 perm 1f030000 0 0 keyring .dns_resolver: 1/4 It can then be listed with 'keyctl list' by root. # keyctl list 0x2a0ca6c3 1 key in keyring: 726766307: --alswrv 0 0 dns_resolver: foo.bar.com Signed-off-by: David Howells Reviewed-and-Tested-by: Jeff Layton Acked-by: Steve French Signed-off-by: Linus Torvalds --- fs/cifs/cifsfs.c | 6 ++--- fs/cifs/dns_resolve.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++ fs/cifs/dns_resolve.h | 4 +-- 3 files changed, 74 insertions(+), 5 deletions(-) (limited to 'fs') diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 484e52bb40bb..2cb1a70214d7 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -923,7 +923,7 @@ init_cifs(void) goto out_unregister_filesystem; #endif #ifdef CONFIG_CIFS_DFS_UPCALL - rc = register_key_type(&key_type_dns_resolver); + rc = cifs_init_dns_resolver(); if (rc) goto out_unregister_key_type; #endif @@ -935,7 +935,7 @@ init_cifs(void) out_unregister_resolver_key: #ifdef CONFIG_CIFS_DFS_UPCALL - unregister_key_type(&key_type_dns_resolver); + cifs_exit_dns_resolver(); out_unregister_key_type: #endif #ifdef CONFIG_CIFS_UPCALL @@ -961,7 +961,7 @@ exit_cifs(void) cifs_proc_clean(); #ifdef CONFIG_CIFS_DFS_UPCALL cifs_dfs_release_automount_timer(); - unregister_key_type(&key_type_dns_resolver); + cifs_exit_dns_resolver(); #endif #ifdef CONFIG_CIFS_UPCALL unregister_key_type(&cifs_spnego_key_type); diff --git a/fs/cifs/dns_resolve.c b/fs/cifs/dns_resolve.c index 4db2c5e7283f..49315cbf742d 100644 --- a/fs/cifs/dns_resolve.c +++ b/fs/cifs/dns_resolve.c @@ -24,12 +24,16 @@ */ #include +#include +#include #include #include "dns_resolve.h" #include "cifsglob.h" #include "cifsproto.h" #include "cifs_debug.h" +static const struct cred *dns_resolver_cache; + /* Checks if supplied name is IP address * returns: * 1 - name is IP @@ -94,6 +98,7 @@ struct key_type key_type_dns_resolver = { int dns_resolve_server_name_to_ip(const char *unc, char **ip_addr) { + const struct cred *saved_cred; int rc = -EAGAIN; struct key *rkey = ERR_PTR(-EAGAIN); char *name; @@ -133,8 +138,15 @@ dns_resolve_server_name_to_ip(const char *unc, char **ip_addr) goto skip_upcall; } + saved_cred = override_creds(dns_resolver_cache); rkey = request_key(&key_type_dns_resolver, name, ""); + revert_creds(saved_cred); if (!IS_ERR(rkey)) { + if (!(rkey->perm & KEY_USR_VIEW)) { + down_read(&rkey->sem); + rkey->perm |= KEY_USR_VIEW; + up_read(&rkey->sem); + } len = rkey->type_data.x[0]; data = rkey->payload.data; } else { @@ -165,4 +177,61 @@ out: return rc; } +int __init cifs_init_dns_resolver(void) +{ + struct cred *cred; + struct key *keyring; + int ret; + + printk(KERN_NOTICE "Registering the %s key type\n", + key_type_dns_resolver.name); + + /* create an override credential set with a special thread keyring in + * which DNS requests are cached + * + * this is used to prevent malicious redirections from being installed + * with add_key(). + */ + cred = prepare_kernel_cred(NULL); + if (!cred) + return -ENOMEM; + + keyring = key_alloc(&key_type_keyring, ".dns_resolver", 0, 0, cred, + (KEY_POS_ALL & ~KEY_POS_SETATTR) | + KEY_USR_VIEW | KEY_USR_READ, + KEY_ALLOC_NOT_IN_QUOTA); + if (IS_ERR(keyring)) { + ret = PTR_ERR(keyring); + goto failed_put_cred; + } + + ret = key_instantiate_and_link(keyring, NULL, 0, NULL, NULL); + if (ret < 0) + goto failed_put_key; + + ret = register_key_type(&key_type_dns_resolver); + if (ret < 0) + goto failed_put_key; + + /* instruct request_key() to use this special keyring as a cache for + * the results it looks up */ + cred->thread_keyring = keyring; + cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING; + dns_resolver_cache = cred; + return 0; + +failed_put_key: + key_put(keyring); +failed_put_cred: + put_cred(cred); + return ret; +} +void __exit cifs_exit_dns_resolver(void) +{ + key_revoke(dns_resolver_cache->thread_keyring); + unregister_key_type(&key_type_dns_resolver); + put_cred(dns_resolver_cache); + printk(KERN_NOTICE "Unregistered %s key type\n", + key_type_dns_resolver.name); +} diff --git a/fs/cifs/dns_resolve.h b/fs/cifs/dns_resolve.h index 966e9288930b..26b9eaa9f5ee 100644 --- a/fs/cifs/dns_resolve.h +++ b/fs/cifs/dns_resolve.h @@ -24,8 +24,8 @@ #define _DNS_RESOLVE_H #ifdef __KERNEL__ -#include -extern struct key_type key_type_dns_resolver; +extern int __init cifs_init_dns_resolver(void); +extern void __exit cifs_exit_dns_resolver(void); extern int dns_resolve_server_name_to_ip(const char *unc, char **ip_addr); #endif /* KERNEL */ -- cgit v1.2.3 From a0dff78dab0ff8d78bd5c9e33c105cf1292f2282 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Thu, 22 Jul 2010 13:47:21 -0700 Subject: ceph: avoid dcache readdir for snapdir We should always go to the MDS for readdir on the hidden snapdir. The set of snapshots can change at any time; the client can't trust its cache for that. Signed-off-by: Sage Weil --- fs/ceph/dir.c | 1 + 1 file changed, 1 insertion(+) (limited to 'fs') diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c index f85719310db2..ea36ba9960d3 100644 --- a/fs/ceph/dir.c +++ b/fs/ceph/dir.c @@ -266,6 +266,7 @@ static int ceph_readdir(struct file *filp, void *dirent, filldir_t filldir) spin_lock(&inode->i_lock); if ((filp->f_pos == 2 || fi->dentry) && !ceph_test_opt(client, NOASYNCREADDIR) && + ceph_snap(inode) != CEPH_SNAPDIR && (ci->i_ceph_flags & CEPH_I_COMPLETE) && __ceph_caps_issued_mask(ci, CEPH_CAP_FILE_SHARED, 1)) { err = __dcache_readdir(filp, dirent, filldir); -- cgit v1.2.3 From 8b8edefa2fffbff97f9eec8b70e78ae23abad1a0 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 20 Jul 2010 22:09:01 +0200 Subject: fscache: convert object to use workqueue instead of slow-work Make fscache object state transition callbacks use workqueue instead of slow-work. New dedicated unbound CPU workqueue fscache_object_wq is created. get/put callbacks are renamed and modified to take @object and called directly from the enqueue wrapper and the work function. While at it, make all open coded instances of get/put to use fscache_get/put_object(). * Unbound workqueue is used. * work_busy() output is printed instead of slow-work flags in object debugging outputs. They mean basically the same thing bit-for-bit. * sysctl fscache.object_max_active added to control concurrency. The default value is nr_cpus clamped between 4 and WQ_UNBOUND_MAX_ACTIVE. * slow_work_sleep_till_thread_needed() is replaced with fscache private implementation fscache_object_sleep_till_congested() which waits on fscache_object_wq congestion. * debugfs support is dropped for now. Tracing API based debug facility is planned to be added. Signed-off-by: Tejun Heo Acked-by: David Howells --- Documentation/filesystems/caching/fscache.txt | 10 +-- fs/cachefiles/namei.c | 13 ++-- fs/fscache/internal.h | 7 ++ fs/fscache/main.c | 76 ++++++++++++++++++ fs/fscache/object-list.c | 11 ++- fs/fscache/object.c | 106 +++++++++++++------------- include/linux/fscache-cache.h | 9 ++- 7 files changed, 158 insertions(+), 74 deletions(-) (limited to 'fs') diff --git a/Documentation/filesystems/caching/fscache.txt b/Documentation/filesystems/caching/fscache.txt index a91e2e2095b0..770267af5b3e 100644 --- a/Documentation/filesystems/caching/fscache.txt +++ b/Documentation/filesystems/caching/fscache.txt @@ -343,8 +343,8 @@ This will look something like: [root@andromeda ~]# head /proc/fs/fscache/objects OBJECT PARENT STAT CHLDN OPS OOP IPR EX READS EM EV F S | NETFS_COOKIE_DEF TY FL NETFS_DATA OBJECT_KEY, AUX_DATA ======== ======== ==== ===== === === === == ===== == == = = | ================ == == ================ ================ - 17e4b 2 ACTV 0 0 0 0 0 0 7b 4 0 8 | NFS.fh DT 0 ffff88001dd82820 010006017edcf8bbc93b43298fdfbe71e50b57b13a172c0117f38472, e567634700000000000000000000000063f2404a000000000000000000000000c9030000000000000000000063f2404a - 1693a 2 ACTV 0 0 0 0 0 0 7b 4 0 8 | NFS.fh DT 0 ffff88002db23380 010006017edcf8bbc93b43298fdfbe71e50b57b1e0162c01a2df0ea6, 420ebc4a000000000000000000000000420ebc4a0000000000000000000000000e1801000000000000000000420ebc4a + 17e4b 2 ACTV 0 0 0 0 0 0 7b 4 0 0 | NFS.fh DT 0 ffff88001dd82820 010006017edcf8bbc93b43298fdfbe71e50b57b13a172c0117f38472, e567634700000000000000000000000063f2404a000000000000000000000000c9030000000000000000000063f2404a + 1693a 2 ACTV 0 0 0 0 0 0 7b 4 0 0 | NFS.fh DT 0 ffff88002db23380 010006017edcf8bbc93b43298fdfbe71e50b57b1e0162c01a2df0ea6, 420ebc4a000000000000000000000000420ebc4a0000000000000000000000000e1801000000000000000000420ebc4a where the first set of columns before the '|' describe the object: @@ -362,7 +362,7 @@ where the first set of columns before the '|' describe the object: EM Object's event mask EV Events raised on this object F Object flags - S Object slow-work work item flags + S Object work item busy state mask (1:pending 2:running) and the second set of columns describe the object's cookie, if present: @@ -395,8 +395,8 @@ and the following paired letters: w Show objects that don't have pending writes R Show objects that have outstanding reads r Show objects that don't have outstanding reads - S Show objects that have slow work queued - s Show objects that don't have slow work queued + S Show objects that have work queued + s Show objects that don't have work queued If neither side of a letter pair is given, then both are implied. For example: diff --git a/fs/cachefiles/namei.c b/fs/cachefiles/namei.c index f4a7840bf42c..42c7fafc8bfe 100644 --- a/fs/cachefiles/namei.c +++ b/fs/cachefiles/namei.c @@ -37,9 +37,9 @@ void __cachefiles_printk_object(struct cachefiles_object *object, printk(KERN_ERR "%sobject: OBJ%x\n", prefix, object->fscache.debug_id); - printk(KERN_ERR "%sobjstate=%s fl=%lx swfl=%lx ev=%lx[%lx]\n", + printk(KERN_ERR "%sobjstate=%s fl=%lx wbusy=%x ev=%lx[%lx]\n", prefix, fscache_object_states[object->fscache.state], - object->fscache.flags, object->fscache.work.flags, + object->fscache.flags, work_busy(&object->fscache.work), object->fscache.events, object->fscache.event_mask & FSCACHE_OBJECT_EVENTS_MASK); printk(KERN_ERR "%sops=%u inp=%u exc=%u\n", @@ -212,7 +212,7 @@ wait_for_old_object: /* if the object we're waiting for is queued for processing, * then just put ourselves on the queue behind it */ - if (slow_work_is_queued(&xobject->fscache.work)) { + if (work_pending(&xobject->fscache.work)) { _debug("queue OBJ%x behind OBJ%x immediately", object->fscache.debug_id, xobject->fscache.debug_id); @@ -220,8 +220,7 @@ wait_for_old_object: } /* otherwise we sleep until either the object we're waiting for - * is done, or the slow-work facility wants the thread back to - * do other work */ + * is done, or the fscache_object is congested */ wq = bit_waitqueue(&xobject->flags, CACHEFILES_OBJECT_ACTIVE); init_wait(&wait); requeue = false; @@ -229,8 +228,8 @@ wait_for_old_object: prepare_to_wait(wq, &wait, TASK_UNINTERRUPTIBLE); if (!test_bit(CACHEFILES_OBJECT_ACTIVE, &xobject->flags)) break; - requeue = slow_work_sleep_till_thread_needed( - &object->fscache.work, &timeout); + + requeue = fscache_object_sleep_till_congested(&timeout); } while (timeout > 0 && !requeue); finish_wait(wq, &wait); diff --git a/fs/fscache/internal.h b/fs/fscache/internal.h index edd7434ab6e5..6e0b5fb25231 100644 --- a/fs/fscache/internal.h +++ b/fs/fscache/internal.h @@ -82,6 +82,13 @@ extern unsigned fscache_defer_lookup; extern unsigned fscache_defer_create; extern unsigned fscache_debug; extern struct kobject *fscache_root; +extern struct workqueue_struct *fscache_object_wq; +DECLARE_PER_CPU(wait_queue_head_t, fscache_object_cong_wait); + +static inline bool fscache_object_congested(void) +{ + return workqueue_congested(WORK_CPU_UNBOUND, fscache_object_wq); +} extern int fscache_wait_bit(void *); extern int fscache_wait_bit_interruptible(void *); diff --git a/fs/fscache/main.c b/fs/fscache/main.c index add6bdb53f04..bb8d4c35c7a2 100644 --- a/fs/fscache/main.c +++ b/fs/fscache/main.c @@ -15,6 +15,7 @@ #include #include #include +#include #include "internal.h" MODULE_DESCRIPTION("FS Cache Manager"); @@ -40,22 +41,89 @@ MODULE_PARM_DESC(fscache_debug, "FS-Cache debugging mask"); struct kobject *fscache_root; +struct workqueue_struct *fscache_object_wq; + +DEFINE_PER_CPU(wait_queue_head_t, fscache_object_cong_wait); + +/* these values serve as lower bounds, will be adjusted in fscache_init() */ +static unsigned fscache_object_max_active = 4; + +#ifdef CONFIG_SYSCTL +static struct ctl_table_header *fscache_sysctl_header; + +static int fscache_max_active_sysctl(struct ctl_table *table, int write, + void __user *buffer, + size_t *lenp, loff_t *ppos) +{ + struct workqueue_struct **wqp = table->extra1; + unsigned int *datap = table->data; + int ret; + + ret = proc_dointvec(table, write, buffer, lenp, ppos); + if (ret == 0) + workqueue_set_max_active(*wqp, *datap); + return ret; +} + +ctl_table fscache_sysctls[] = { + { + .procname = "object_max_active", + .data = &fscache_object_max_active, + .maxlen = sizeof(unsigned), + .mode = 0644, + .proc_handler = fscache_max_active_sysctl, + .extra1 = &fscache_object_wq, + }, + {} +}; + +ctl_table fscache_sysctls_root[] = { + { + .procname = "fscache", + .mode = 0555, + .child = fscache_sysctls, + }, + {} +}; +#endif /* * initialise the fs caching module */ static int __init fscache_init(void) { + unsigned int nr_cpus = num_possible_cpus(); + unsigned int cpu; int ret; ret = slow_work_register_user(THIS_MODULE); if (ret < 0) goto error_slow_work; + fscache_object_max_active = + clamp_val(nr_cpus, + fscache_object_max_active, WQ_UNBOUND_MAX_ACTIVE); + + ret = -ENOMEM; + fscache_object_wq = alloc_workqueue("fscache_object", WQ_UNBOUND, + fscache_object_max_active); + if (!fscache_object_wq) + goto error_object_wq; + + for_each_possible_cpu(cpu) + init_waitqueue_head(&per_cpu(fscache_object_cong_wait, cpu)); + ret = fscache_proc_init(); if (ret < 0) goto error_proc; +#ifdef CONFIG_SYSCTL + ret = -ENOMEM; + fscache_sysctl_header = register_sysctl_table(fscache_sysctls_root); + if (!fscache_sysctl_header) + goto error_sysctl; +#endif + fscache_cookie_jar = kmem_cache_create("fscache_cookie_jar", sizeof(struct fscache_cookie), 0, @@ -78,8 +146,14 @@ static int __init fscache_init(void) error_kobj: kmem_cache_destroy(fscache_cookie_jar); error_cookie_jar: +#ifdef CONFIG_SYSCTL + unregister_sysctl_table(fscache_sysctl_header); +error_sysctl: +#endif fscache_proc_cleanup(); error_proc: + destroy_workqueue(fscache_object_wq); +error_object_wq: slow_work_unregister_user(THIS_MODULE); error_slow_work: return ret; @@ -96,7 +170,9 @@ static void __exit fscache_exit(void) kobject_put(fscache_root); kmem_cache_destroy(fscache_cookie_jar); + unregister_sysctl_table(fscache_sysctl_header); fscache_proc_cleanup(); + destroy_workqueue(fscache_object_wq); slow_work_unregister_user(THIS_MODULE); printk(KERN_NOTICE "FS-Cache: Unloaded\n"); } diff --git a/fs/fscache/object-list.c b/fs/fscache/object-list.c index 4a8eb31c5338..ebe29c581380 100644 --- a/fs/fscache/object-list.c +++ b/fs/fscache/object-list.c @@ -34,8 +34,8 @@ struct fscache_objlist_data { #define FSCACHE_OBJLIST_CONFIG_NOREADS 0x00000200 /* show objects without active reads */ #define FSCACHE_OBJLIST_CONFIG_EVENTS 0x00000400 /* show objects with events */ #define FSCACHE_OBJLIST_CONFIG_NOEVENTS 0x00000800 /* show objects without no events */ -#define FSCACHE_OBJLIST_CONFIG_WORK 0x00001000 /* show objects with slow work */ -#define FSCACHE_OBJLIST_CONFIG_NOWORK 0x00002000 /* show objects without slow work */ +#define FSCACHE_OBJLIST_CONFIG_WORK 0x00001000 /* show objects with work */ +#define FSCACHE_OBJLIST_CONFIG_NOWORK 0x00002000 /* show objects without work */ u8 buf[512]; /* key and aux data buffer */ }; @@ -231,12 +231,11 @@ static int fscache_objlist_show(struct seq_file *m, void *v) READS, NOREADS); FILTER(obj->events & obj->event_mask, EVENTS, NOEVENTS); - FILTER(obj->work.flags & ~(1UL << SLOW_WORK_VERY_SLOW), - WORK, NOWORK); + FILTER(work_busy(&obj->work), WORK, NOWORK); } seq_printf(m, - "%8x %8x %s %5u %3u %3u %3u %2u %5u %2lx %2lx %1lx %1lx | ", + "%8x %8x %s %5u %3u %3u %3u %2u %5u %2lx %2lx %1lx %1x | ", obj->debug_id, obj->parent ? obj->parent->debug_id : -1, fscache_object_states_short[obj->state], @@ -249,7 +248,7 @@ static int fscache_objlist_show(struct seq_file *m, void *v) obj->event_mask & FSCACHE_OBJECT_EVENTS_MASK, obj->events, obj->flags, - obj->work.flags); + work_busy(&obj->work)); no_cookie = true; keylen = auxlen = 0; diff --git a/fs/fscache/object.c b/fs/fscache/object.c index 0b589a9b4ffc..b6b897c550ac 100644 --- a/fs/fscache/object.c +++ b/fs/fscache/object.c @@ -14,7 +14,6 @@ #define FSCACHE_DEBUG_LEVEL COOKIE #include -#include #include "internal.h" const char *fscache_object_states[FSCACHE_OBJECT__NSTATES] = { @@ -50,12 +49,8 @@ const char fscache_object_states_short[FSCACHE_OBJECT__NSTATES][5] = { [FSCACHE_OBJECT_DEAD] = "DEAD", }; -static void fscache_object_slow_work_put_ref(struct slow_work *); -static int fscache_object_slow_work_get_ref(struct slow_work *); -static void fscache_object_slow_work_execute(struct slow_work *); -#ifdef CONFIG_SLOW_WORK_DEBUG -static void fscache_object_slow_work_desc(struct slow_work *, struct seq_file *); -#endif +static int fscache_get_object(struct fscache_object *); +static void fscache_put_object(struct fscache_object *); static void fscache_initialise_object(struct fscache_object *); static void fscache_lookup_object(struct fscache_object *); static void fscache_object_available(struct fscache_object *); @@ -64,17 +59,6 @@ static void fscache_withdraw_object(struct fscache_object *); static void fscache_enqueue_dependents(struct fscache_object *); static void fscache_dequeue_object(struct fscache_object *); -const struct slow_work_ops fscache_object_slow_work_ops = { - .owner = THIS_MODULE, - .get_ref = fscache_object_slow_work_get_ref, - .put_ref = fscache_object_slow_work_put_ref, - .execute = fscache_object_slow_work_execute, -#ifdef CONFIG_SLOW_WORK_DEBUG - .desc = fscache_object_slow_work_desc, -#endif -}; -EXPORT_SYMBOL(fscache_object_slow_work_ops); - /* * we need to notify the parent when an op completes that we had outstanding * upon it @@ -345,7 +329,7 @@ unsupported_event: /* * execute an object */ -static void fscache_object_slow_work_execute(struct slow_work *work) +void fscache_object_work_func(struct work_struct *work) { struct fscache_object *object = container_of(work, struct fscache_object, work); @@ -359,23 +343,9 @@ static void fscache_object_slow_work_execute(struct slow_work *work) if (object->events & object->event_mask) fscache_enqueue_object(object); clear_bit(FSCACHE_OBJECT_EV_REQUEUE, &object->events); + fscache_put_object(object); } - -/* - * describe an object for slow-work debugging - */ -#ifdef CONFIG_SLOW_WORK_DEBUG -static void fscache_object_slow_work_desc(struct slow_work *work, - struct seq_file *m) -{ - struct fscache_object *object = - container_of(work, struct fscache_object, work); - - seq_printf(m, "FSC: OBJ%x: %s", - object->debug_id, - fscache_object_states_short[object->state]); -} -#endif +EXPORT_SYMBOL(fscache_object_work_func); /* * initialise an object @@ -393,7 +363,6 @@ static void fscache_initialise_object(struct fscache_object *object) _enter(""); ASSERT(object->cookie != NULL); ASSERT(object->cookie->parent != NULL); - ASSERT(list_empty(&object->work.link)); if (object->events & ((1 << FSCACHE_OBJECT_EV_ERROR) | (1 << FSCACHE_OBJECT_EV_RELEASE) | @@ -671,10 +640,8 @@ static void fscache_drop_object(struct fscache_object *object) object->parent = NULL; } - /* this just shifts the object release to the slow work processor */ - fscache_stat(&fscache_n_cop_put_object); - object->cache->ops->put_object(object); - fscache_stat_d(&fscache_n_cop_put_object); + /* this just shifts the object release to the work processor */ + fscache_put_object(object); _leave(""); } @@ -758,12 +725,10 @@ void fscache_withdrawing_object(struct fscache_cache *cache, } /* - * allow the slow work item processor to get a ref on an object + * get a ref on an object */ -static int fscache_object_slow_work_get_ref(struct slow_work *work) +static int fscache_get_object(struct fscache_object *object) { - struct fscache_object *object = - container_of(work, struct fscache_object, work); int ret; fscache_stat(&fscache_n_cop_grab_object); @@ -773,13 +738,10 @@ static int fscache_object_slow_work_get_ref(struct slow_work *work) } /* - * allow the slow work item processor to discard a ref on a work item + * discard a ref on a work item */ -static void fscache_object_slow_work_put_ref(struct slow_work *work) +static void fscache_put_object(struct fscache_object *object) { - struct fscache_object *object = - container_of(work, struct fscache_object, work); - fscache_stat(&fscache_n_cop_put_object); object->cache->ops->put_object(object); fscache_stat_d(&fscache_n_cop_put_object); @@ -792,8 +754,48 @@ void fscache_enqueue_object(struct fscache_object *object) { _enter("{OBJ%x}", object->debug_id); - slow_work_enqueue(&object->work); + if (fscache_get_object(object) >= 0) { + wait_queue_head_t *cong_wq = + &get_cpu_var(fscache_object_cong_wait); + + if (queue_work(fscache_object_wq, &object->work)) { + if (fscache_object_congested()) + wake_up(cong_wq); + } else + fscache_put_object(object); + + put_cpu_var(fscache_object_cong_wait); + } +} + +/** + * fscache_object_sleep_till_congested - Sleep until object wq is congested + * @timoutp: Scheduler sleep timeout + * + * Allow an object handler to sleep until the object workqueue is congested. + * + * The caller must set up a wake up event before calling this and must have set + * the appropriate sleep mode (such as TASK_UNINTERRUPTIBLE) and tested its own + * condition before calling this function as no test is made here. + * + * %true is returned if the object wq is congested, %false otherwise. + */ +bool fscache_object_sleep_till_congested(signed long *timeoutp) +{ + wait_queue_head_t *cong_wq = &__get_cpu_var(fscache_object_cong_wait); + DEFINE_WAIT(wait); + + if (fscache_object_congested()) + return true; + + add_wait_queue_exclusive(cong_wq, &wait); + if (!fscache_object_congested()) + *timeoutp = schedule_timeout(*timeoutp); + finish_wait(cong_wq, &wait); + + return fscache_object_congested(); } +EXPORT_SYMBOL_GPL(fscache_object_sleep_till_congested); /* * enqueue the dependents of an object for metadata-type processing @@ -819,9 +821,7 @@ static void fscache_enqueue_dependents(struct fscache_object *object) /* sort onto appropriate lists */ fscache_enqueue_object(dep); - fscache_stat(&fscache_n_cop_put_object); - dep->cache->ops->put_object(dep); - fscache_stat_d(&fscache_n_cop_put_object); + fscache_put_object(dep); if (!list_empty(&object->dependents)) cond_resched_lock(&object->lock); diff --git a/include/linux/fscache-cache.h b/include/linux/fscache-cache.h index c57db27ac861..27c8df503152 100644 --- a/include/linux/fscache-cache.h +++ b/include/linux/fscache-cache.h @@ -21,6 +21,7 @@ #include #include #include +#include #define NR_MAXCACHES BITS_PER_LONG @@ -389,7 +390,7 @@ struct fscache_object { struct fscache_cache *cache; /* cache that supplied this object */ struct fscache_cookie *cookie; /* netfs's file/index object */ struct fscache_object *parent; /* parent object */ - struct slow_work work; /* attention scheduling record */ + struct work_struct work; /* attention scheduling record */ struct list_head dependents; /* FIFO of dependent objects */ struct list_head dep_link; /* link in parent's dependents list */ struct list_head pending_ops; /* unstarted operations on this object */ @@ -411,7 +412,7 @@ extern const char *fscache_object_states[]; (test_bit(FSCACHE_IOERROR, &(obj)->cache->flags) && \ (obj)->state >= FSCACHE_OBJECT_DYING) -extern const struct slow_work_ops fscache_object_slow_work_ops; +extern void fscache_object_work_func(struct work_struct *work); /** * fscache_object_init - Initialise a cache object description @@ -433,7 +434,7 @@ void fscache_object_init(struct fscache_object *object, spin_lock_init(&object->lock); INIT_LIST_HEAD(&object->cache_link); INIT_HLIST_NODE(&object->cookie_link); - vslow_work_init(&object->work, &fscache_object_slow_work_ops); + INIT_WORK(&object->work, fscache_object_work_func); INIT_LIST_HEAD(&object->dependents); INIT_LIST_HEAD(&object->dep_link); INIT_LIST_HEAD(&object->pending_ops); @@ -534,6 +535,8 @@ extern void fscache_io_error(struct fscache_cache *cache); extern void fscache_mark_pages_cached(struct fscache_retrieval *op, struct pagevec *pagevec); +extern bool fscache_object_sleep_till_congested(signed long *timeoutp); + extern enum fscache_checkaux fscache_check_aux(struct fscache_object *object, const void *data, uint16_t datalen); -- cgit v1.2.3 From 8af7c12436803291c90295259db23d371a7ad9cc Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 20 Jul 2010 22:09:01 +0200 Subject: fscache: convert operation to use workqueue instead of slow-work Make fscache operation to use only workqueue instead of combination of workqueue and slow-work. FSCACHE_OP_SLOW is dropped and FSCACHE_OP_FAST is renamed to FSCACHE_OP_ASYNC and uses newly added fscache_op_wq workqueue to execute op->processor(). fscache_operation_init_slow() is dropped and fscache_operation_init() now takes @processor argument directly. * Unbound workqueue is used. * fscache_retrieval_work() is no longer necessary as OP_ASYNC now does the equivalent thing. * sysctl fscache.operation_max_active added to control concurrency. The default value is nr_cpus clamped between 2 and WQ_UNBOUND_MAX_ACTIVE. * debugfs support is dropped for now. Tracing API based debug facility is planned to be added. Signed-off-by: Tejun Heo Acked-by: David Howells --- fs/cachefiles/rdwr.c | 4 +-- fs/fscache/internal.h | 1 + fs/fscache/main.c | 23 +++++++++++++++ fs/fscache/operation.c | 67 ++++++------------------------------------- fs/fscache/page.c | 36 ++++++----------------- include/linux/fscache-cache.h | 37 +++++++----------------- 6 files changed, 53 insertions(+), 115 deletions(-) (limited to 'fs') diff --git a/fs/cachefiles/rdwr.c b/fs/cachefiles/rdwr.c index 0f0d41fbb03f..0e3c0924cc3a 100644 --- a/fs/cachefiles/rdwr.c +++ b/fs/cachefiles/rdwr.c @@ -422,7 +422,7 @@ int cachefiles_read_or_alloc_page(struct fscache_retrieval *op, shift = PAGE_SHIFT - inode->i_sb->s_blocksize_bits; op->op.flags &= FSCACHE_OP_KEEP_FLAGS; - op->op.flags |= FSCACHE_OP_FAST; + op->op.flags |= FSCACHE_OP_ASYNC; op->op.processor = cachefiles_read_copier; pagevec_init(&pagevec, 0); @@ -729,7 +729,7 @@ int cachefiles_read_or_alloc_pages(struct fscache_retrieval *op, pagevec_init(&pagevec, 0); op->op.flags &= FSCACHE_OP_KEEP_FLAGS; - op->op.flags |= FSCACHE_OP_FAST; + op->op.flags |= FSCACHE_OP_ASYNC; op->op.processor = cachefiles_read_copier; INIT_LIST_HEAD(&backpages); diff --git a/fs/fscache/internal.h b/fs/fscache/internal.h index 6e0b5fb25231..6a026441c5a6 100644 --- a/fs/fscache/internal.h +++ b/fs/fscache/internal.h @@ -83,6 +83,7 @@ extern unsigned fscache_defer_create; extern unsigned fscache_debug; extern struct kobject *fscache_root; extern struct workqueue_struct *fscache_object_wq; +extern struct workqueue_struct *fscache_op_wq; DECLARE_PER_CPU(wait_queue_head_t, fscache_object_cong_wait); static inline bool fscache_object_congested(void) diff --git a/fs/fscache/main.c b/fs/fscache/main.c index bb8d4c35c7a2..44d13ddab2cc 100644 --- a/fs/fscache/main.c +++ b/fs/fscache/main.c @@ -42,11 +42,13 @@ MODULE_PARM_DESC(fscache_debug, struct kobject *fscache_root; struct workqueue_struct *fscache_object_wq; +struct workqueue_struct *fscache_op_wq; DEFINE_PER_CPU(wait_queue_head_t, fscache_object_cong_wait); /* these values serve as lower bounds, will be adjusted in fscache_init() */ static unsigned fscache_object_max_active = 4; +static unsigned fscache_op_max_active = 2; #ifdef CONFIG_SYSCTL static struct ctl_table_header *fscache_sysctl_header; @@ -74,6 +76,14 @@ ctl_table fscache_sysctls[] = { .proc_handler = fscache_max_active_sysctl, .extra1 = &fscache_object_wq, }, + { + .procname = "operation_max_active", + .data = &fscache_op_max_active, + .maxlen = sizeof(unsigned), + .mode = 0644, + .proc_handler = fscache_max_active_sysctl, + .extra1 = &fscache_op_wq, + }, {} }; @@ -110,6 +120,16 @@ static int __init fscache_init(void) if (!fscache_object_wq) goto error_object_wq; + fscache_op_max_active = + clamp_val(fscache_object_max_active / 2, + fscache_op_max_active, WQ_UNBOUND_MAX_ACTIVE); + + ret = -ENOMEM; + fscache_op_wq = alloc_workqueue("fscache_operation", WQ_UNBOUND, + fscache_op_max_active); + if (!fscache_op_wq) + goto error_op_wq; + for_each_possible_cpu(cpu) init_waitqueue_head(&per_cpu(fscache_object_cong_wait, cpu)); @@ -152,6 +172,8 @@ error_sysctl: #endif fscache_proc_cleanup(); error_proc: + destroy_workqueue(fscache_op_wq); +error_op_wq: destroy_workqueue(fscache_object_wq); error_object_wq: slow_work_unregister_user(THIS_MODULE); @@ -172,6 +194,7 @@ static void __exit fscache_exit(void) kmem_cache_destroy(fscache_cookie_jar); unregister_sysctl_table(fscache_sysctl_header); fscache_proc_cleanup(); + destroy_workqueue(fscache_op_wq); destroy_workqueue(fscache_object_wq); slow_work_unregister_user(THIS_MODULE); printk(KERN_NOTICE "FS-Cache: Unloaded\n"); diff --git a/fs/fscache/operation.c b/fs/fscache/operation.c index f17cecafae44..b9f34eaede09 100644 --- a/fs/fscache/operation.c +++ b/fs/fscache/operation.c @@ -42,16 +42,12 @@ void fscache_enqueue_operation(struct fscache_operation *op) fscache_stat(&fscache_n_op_enqueue); switch (op->flags & FSCACHE_OP_TYPE) { - case FSCACHE_OP_FAST: - _debug("queue fast"); + case FSCACHE_OP_ASYNC: + _debug("queue async"); atomic_inc(&op->usage); - if (!schedule_work(&op->fast_work)) + if (!queue_work(fscache_op_wq, &op->work)) fscache_put_operation(op); break; - case FSCACHE_OP_SLOW: - _debug("queue slow"); - slow_work_enqueue(&op->slow_work); - break; case FSCACHE_OP_MYTHREAD: _debug("queue for caller's attention"); break; @@ -455,36 +451,13 @@ void fscache_operation_gc(struct work_struct *work) } /* - * allow the slow work item processor to get a ref on an operation - */ -static int fscache_op_get_ref(struct slow_work *work) -{ - struct fscache_operation *op = - container_of(work, struct fscache_operation, slow_work); - - atomic_inc(&op->usage); - return 0; -} - -/* - * allow the slow work item processor to discard a ref on an operation - */ -static void fscache_op_put_ref(struct slow_work *work) -{ - struct fscache_operation *op = - container_of(work, struct fscache_operation, slow_work); - - fscache_put_operation(op); -} - -/* - * execute an operation using the slow thread pool to provide processing context - * - the caller holds a ref to this object, so we don't need to hold one + * execute an operation using fs_op_wq to provide processing context - + * the caller holds a ref to this object, so we don't need to hold one */ -static void fscache_op_execute(struct slow_work *work) +void fscache_op_work_func(struct work_struct *work) { struct fscache_operation *op = - container_of(work, struct fscache_operation, slow_work); + container_of(work, struct fscache_operation, work); unsigned long start; _enter("{OBJ%x OP%x,%d}", @@ -494,31 +467,7 @@ static void fscache_op_execute(struct slow_work *work) start = jiffies; op->processor(op); fscache_hist(fscache_ops_histogram, start); + fscache_put_operation(op); _leave(""); } - -/* - * describe an operation for slow-work debugging - */ -#ifdef CONFIG_SLOW_WORK_DEBUG -static void fscache_op_desc(struct slow_work *work, struct seq_file *m) -{ - struct fscache_operation *op = - container_of(work, struct fscache_operation, slow_work); - - seq_printf(m, "FSC: OBJ%x OP%x: %s/%s fl=%lx", - op->object->debug_id, op->debug_id, - op->name, op->state, op->flags); -} -#endif - -const struct slow_work_ops fscache_op_slow_work_ops = { - .owner = THIS_MODULE, - .get_ref = fscache_op_get_ref, - .put_ref = fscache_op_put_ref, - .execute = fscache_op_execute, -#ifdef CONFIG_SLOW_WORK_DEBUG - .desc = fscache_op_desc, -#endif -}; diff --git a/fs/fscache/page.c b/fs/fscache/page.c index 723b889fd219..41c441c2058d 100644 --- a/fs/fscache/page.c +++ b/fs/fscache/page.c @@ -105,7 +105,7 @@ bool __fscache_maybe_release_page(struct fscache_cookie *cookie, page_busy: /* we might want to wait here, but that could deadlock the allocator as - * the slow-work threads writing to the cache may all end up sleeping + * the work threads writing to the cache may all end up sleeping * on memory allocation */ fscache_stat(&fscache_n_store_vmscan_busy); return false; @@ -188,9 +188,8 @@ int __fscache_attr_changed(struct fscache_cookie *cookie) return -ENOMEM; } - fscache_operation_init(op, NULL); - fscache_operation_init_slow(op, fscache_attr_changed_op); - op->flags = FSCACHE_OP_SLOW | (1 << FSCACHE_OP_EXCLUSIVE); + fscache_operation_init(op, fscache_attr_changed_op, NULL); + op->flags = FSCACHE_OP_ASYNC | (1 << FSCACHE_OP_EXCLUSIVE); fscache_set_op_name(op, "Attr"); spin_lock(&cookie->lock); @@ -217,24 +216,6 @@ nobufs: } EXPORT_SYMBOL(__fscache_attr_changed); -/* - * handle secondary execution given to a retrieval op on behalf of the - * cache - */ -static void fscache_retrieval_work(struct work_struct *work) -{ - struct fscache_retrieval *op = - container_of(work, struct fscache_retrieval, op.fast_work); - unsigned long start; - - _enter("{OP%x}", op->op.debug_id); - - start = jiffies; - op->op.processor(&op->op); - fscache_hist(fscache_ops_histogram, start); - fscache_put_operation(&op->op); -} - /* * release a retrieval op reference */ @@ -269,13 +250,12 @@ static struct fscache_retrieval *fscache_alloc_retrieval( return NULL; } - fscache_operation_init(&op->op, fscache_release_retrieval_op); + fscache_operation_init(&op->op, NULL, fscache_release_retrieval_op); op->op.flags = FSCACHE_OP_MYTHREAD | (1 << FSCACHE_OP_WAITING); op->mapping = mapping; op->end_io_func = end_io_func; op->context = context; op->start_time = jiffies; - INIT_WORK(&op->op.fast_work, fscache_retrieval_work); INIT_LIST_HEAD(&op->to_do); fscache_set_op_name(&op->op, "Retr"); return op; @@ -795,9 +775,9 @@ int __fscache_write_page(struct fscache_cookie *cookie, if (!op) goto nomem; - fscache_operation_init(&op->op, fscache_release_write_op); - fscache_operation_init_slow(&op->op, fscache_write_op); - op->op.flags = FSCACHE_OP_SLOW | (1 << FSCACHE_OP_WAITING); + fscache_operation_init(&op->op, fscache_write_op, + fscache_release_write_op); + op->op.flags = FSCACHE_OP_ASYNC | (1 << FSCACHE_OP_WAITING); fscache_set_op_name(&op->op, "Write1"); ret = radix_tree_preload(gfp & ~__GFP_HIGHMEM); @@ -852,7 +832,7 @@ int __fscache_write_page(struct fscache_cookie *cookie, fscache_stat(&fscache_n_store_ops); fscache_stat(&fscache_n_stores_ok); - /* the slow work queue now carries its own ref on the object */ + /* the work queue now carries its own ref on the object */ fscache_put_operation(&op->op); _leave(" = 0"); return 0; diff --git a/include/linux/fscache-cache.h b/include/linux/fscache-cache.h index 27c8df503152..17ed9c1dbfbe 100644 --- a/include/linux/fscache-cache.h +++ b/include/linux/fscache-cache.h @@ -77,18 +77,14 @@ typedef void (*fscache_operation_release_t)(struct fscache_operation *op); typedef void (*fscache_operation_processor_t)(struct fscache_operation *op); struct fscache_operation { - union { - struct work_struct fast_work; /* record for fast ops */ - struct slow_work slow_work; /* record for (very) slow ops */ - }; + struct work_struct work; /* record for async ops */ struct list_head pend_link; /* link in object->pending_ops */ struct fscache_object *object; /* object to be operated upon */ unsigned long flags; #define FSCACHE_OP_TYPE 0x000f /* operation type */ -#define FSCACHE_OP_FAST 0x0001 /* - fast op, processor may not sleep for disk */ -#define FSCACHE_OP_SLOW 0x0002 /* - (very) slow op, processor may sleep for disk */ -#define FSCACHE_OP_MYTHREAD 0x0003 /* - processing is done be issuing thread, not pool */ +#define FSCACHE_OP_ASYNC 0x0001 /* - async op, processor may sleep for disk */ +#define FSCACHE_OP_MYTHREAD 0x0002 /* - processing is done be issuing thread, not pool */ #define FSCACHE_OP_WAITING 4 /* cleared when op is woken */ #define FSCACHE_OP_EXCLUSIVE 5 /* exclusive op, other ops must wait */ #define FSCACHE_OP_DEAD 6 /* op is now dead */ @@ -106,7 +102,8 @@ struct fscache_operation { /* operation releaser */ fscache_operation_release_t release; -#ifdef CONFIG_SLOW_WORK_DEBUG +#ifdef CONFIG_WORKQUEUE_DEBUGFS + struct work_struct put_work; /* work to delay operation put */ const char *name; /* operation name */ const char *state; /* operation state */ #define fscache_set_op_name(OP, N) do { (OP)->name = (N); } while(0) @@ -118,7 +115,7 @@ struct fscache_operation { }; extern atomic_t fscache_op_debug_id; -extern const struct slow_work_ops fscache_op_slow_work_ops; +extern void fscache_op_work_func(struct work_struct *work); extern void fscache_enqueue_operation(struct fscache_operation *); extern void fscache_put_operation(struct fscache_operation *); @@ -129,33 +126,21 @@ extern void fscache_put_operation(struct fscache_operation *); * @release: The release function to assign * * Do basic initialisation of an operation. The caller must still set flags, - * object, either fast_work or slow_work if necessary, and processor if needed. + * object and processor if needed. */ static inline void fscache_operation_init(struct fscache_operation *op, - fscache_operation_release_t release) + fscache_operation_processor_t processor, + fscache_operation_release_t release) { + INIT_WORK(&op->work, fscache_op_work_func); atomic_set(&op->usage, 1); op->debug_id = atomic_inc_return(&fscache_op_debug_id); + op->processor = processor; op->release = release; INIT_LIST_HEAD(&op->pend_link); fscache_set_op_state(op, "Init"); } -/** - * fscache_operation_init_slow - Do additional initialisation of a slow op - * @op: The operation to initialise - * @processor: The processor function to assign - * - * Do additional initialisation of an operation as required for slow work. - */ -static inline -void fscache_operation_init_slow(struct fscache_operation *op, - fscache_operation_processor_t processor) -{ - op->processor = processor; - slow_work_init(&op->slow_work, &fscache_op_slow_work_ops); -} - /* * data read operation */ -- cgit v1.2.3 From d098adfb7d281258173a43151483e52e21761021 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 20 Jul 2010 22:09:01 +0200 Subject: fscache: drop references to slow-work fscache no longer uses slow-work. Drop references to it. Signed-off-by: Tejun Heo Acked-by: David Howells --- fs/fscache/Kconfig | 1 - fs/fscache/main.c | 7 ------- include/linux/fscache-cache.h | 1 - 3 files changed, 9 deletions(-) (limited to 'fs') diff --git a/fs/fscache/Kconfig b/fs/fscache/Kconfig index cc94bb9563f2..3f6dfa989881 100644 --- a/fs/fscache/Kconfig +++ b/fs/fscache/Kconfig @@ -1,7 +1,6 @@ config FSCACHE tristate "General filesystem local caching manager" - select SLOW_WORK help This option enables a generic filesystem caching manager that can be used by various network and other filesystems to cache data locally. diff --git a/fs/fscache/main.c b/fs/fscache/main.c index 44d13ddab2cc..500936d9fff2 100644 --- a/fs/fscache/main.c +++ b/fs/fscache/main.c @@ -106,10 +106,6 @@ static int __init fscache_init(void) unsigned int cpu; int ret; - ret = slow_work_register_user(THIS_MODULE); - if (ret < 0) - goto error_slow_work; - fscache_object_max_active = clamp_val(nr_cpus, fscache_object_max_active, WQ_UNBOUND_MAX_ACTIVE); @@ -176,8 +172,6 @@ error_proc: error_op_wq: destroy_workqueue(fscache_object_wq); error_object_wq: - slow_work_unregister_user(THIS_MODULE); -error_slow_work: return ret; } @@ -196,7 +190,6 @@ static void __exit fscache_exit(void) fscache_proc_cleanup(); destroy_workqueue(fscache_op_wq); destroy_workqueue(fscache_object_wq); - slow_work_unregister_user(THIS_MODULE); printk(KERN_NOTICE "FS-Cache: Unloaded\n"); } diff --git a/include/linux/fscache-cache.h b/include/linux/fscache-cache.h index 17ed9c1dbfbe..b8581c09d19f 100644 --- a/include/linux/fscache-cache.h +++ b/include/linux/fscache-cache.h @@ -20,7 +20,6 @@ #include #include -#include #include #define NR_MAXCACHES BITS_PER_LONG -- cgit v1.2.3 From 9b646972467fb5fdc677f9e4251875db20bdbb64 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 20 Jul 2010 22:09:02 +0200 Subject: cifs: use workqueue instead of slow-work Workqueue can now handle high concurrency. Use system_nrt_wq instead of slow-work. * Updated is_valid_oplock_break() to not call cifs_oplock_break_put() as advised by Steve French. It might cause deadlock. Instead, reference is increased after queueing succeeded and cifs_oplock_break() briefly grabs GlobalSMBSeslock before putting the cfile to make sure it doesn't put before the matching get is finished. * Anton Blanchard reported that cifs conversion was using now gone system_single_wq. Use system_nrt_wq which provides non-reentrance guarantee which is enough and much better. Signed-off-by: Tejun Heo Acked-by: Steve French Cc: Anton Blanchard --- fs/cifs/Kconfig | 1 - fs/cifs/cifsfs.c | 5 ----- fs/cifs/cifsglob.h | 8 +++++--- fs/cifs/dir.c | 2 +- fs/cifs/file.c | 30 +++++++++++++----------------- fs/cifs/misc.c | 20 ++++++++++++-------- 6 files changed, 31 insertions(+), 35 deletions(-) (limited to 'fs') diff --git a/fs/cifs/Kconfig b/fs/cifs/Kconfig index 80f352596807..6994a0f54f02 100644 --- a/fs/cifs/Kconfig +++ b/fs/cifs/Kconfig @@ -2,7 +2,6 @@ config CIFS tristate "CIFS support (advanced network filesystem, SMBFS successor)" depends on INET select NLS - select SLOW_WORK help This is the client VFS module for the Common Internet File System (CIFS) protocol which is the successor to the Server Message Block diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 78c02eb4cb1f..4c075177c8b6 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -917,15 +917,10 @@ init_cifs(void) if (rc) goto out_unregister_key_type; #endif - rc = slow_work_register_user(THIS_MODULE); - if (rc) - goto out_unregister_resolver_key; return 0; - out_unregister_resolver_key: #ifdef CONFIG_CIFS_DFS_UPCALL - unregister_key_type(&key_type_dns_resolver); out_unregister_key_type: #endif #ifdef CONFIG_CIFS_UPCALL diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index a88479ceaad5..f5a1f9bb3a9f 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -19,7 +19,7 @@ #include #include #include -#include +#include #include "cifs_fs_sb.h" #include "cifsacl.h" /* @@ -363,7 +363,7 @@ struct cifsFileInfo { atomic_t count; /* reference count */ struct mutex fh_mutex; /* prevents reopen race after dead ses*/ struct cifs_search_info srch_inf; - struct slow_work oplock_break; /* slow_work job for oplock breaks */ + struct work_struct oplock_break; /* work for oplock breaks */ }; /* Take a reference on the file private data */ @@ -732,4 +732,6 @@ GLOBAL_EXTERN unsigned int cifs_min_rcv; /* min size of big ntwrk buf pool */ GLOBAL_EXTERN unsigned int cifs_min_small; /* min size of small buf pool */ GLOBAL_EXTERN unsigned int cifs_max_pending; /* MAX requests at once to server*/ -extern const struct slow_work_ops cifs_oplock_break_ops; +void cifs_oplock_break(struct work_struct *work); +void cifs_oplock_break_get(struct cifsFileInfo *cfile); +void cifs_oplock_break_put(struct cifsFileInfo *cfile); diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index 391816b461ca..b066e73c4153 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -162,7 +162,7 @@ cifs_new_fileinfo(struct inode *newinode, __u16 fileHandle, mutex_init(&pCifsFile->lock_mutex); INIT_LIST_HEAD(&pCifsFile->llist); atomic_set(&pCifsFile->count, 1); - slow_work_init(&pCifsFile->oplock_break, &cifs_oplock_break_ops); + INIT_WORK(&pCifsFile->oplock_break, cifs_oplock_break); write_lock(&GlobalSMBSeslock); list_add(&pCifsFile->tlist, &cifs_sb->tcon->openFileList); diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 75541af4b3db..e767bfa7978b 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -2295,8 +2295,7 @@ out: return rc; } -static void -cifs_oplock_break(struct slow_work *work) +void cifs_oplock_break(struct work_struct *work) { struct cifsFileInfo *cfile = container_of(work, struct cifsFileInfo, oplock_break); @@ -2333,33 +2332,30 @@ cifs_oplock_break(struct slow_work *work) LOCKING_ANDX_OPLOCK_RELEASE, false); cFYI(1, "Oplock release rc = %d", rc); } + + /* + * We might have kicked in before is_valid_oplock_break() + * finished grabbing reference for us. Make sure it's done by + * waiting for GlobalSMSSeslock. + */ + write_lock(&GlobalSMBSeslock); + write_unlock(&GlobalSMBSeslock); + + cifs_oplock_break_put(cfile); } -static int -cifs_oplock_break_get(struct slow_work *work) +void cifs_oplock_break_get(struct cifsFileInfo *cfile) { - struct cifsFileInfo *cfile = container_of(work, struct cifsFileInfo, - oplock_break); mntget(cfile->mnt); cifsFileInfo_get(cfile); - return 0; } -static void -cifs_oplock_break_put(struct slow_work *work) +void cifs_oplock_break_put(struct cifsFileInfo *cfile) { - struct cifsFileInfo *cfile = container_of(work, struct cifsFileInfo, - oplock_break); mntput(cfile->mnt); cifsFileInfo_put(cfile); } -const struct slow_work_ops cifs_oplock_break_ops = { - .get_ref = cifs_oplock_break_get, - .put_ref = cifs_oplock_break_put, - .execute = cifs_oplock_break, -}; - const struct address_space_operations cifs_addr_ops = { .readpage = cifs_readpage, .readpages = cifs_readpages, diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index 1394aa37f26c..3ccadc1326d6 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -498,7 +498,6 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv) struct cifsTconInfo *tcon; struct cifsInodeInfo *pCifsInode; struct cifsFileInfo *netfile; - int rc; cFYI(1, "Checking for oplock break or dnotify response"); if ((pSMB->hdr.Command == SMB_COM_NT_TRANSACT) && @@ -583,13 +582,18 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv) pCifsInode->clientCanCacheAll = false; if (pSMB->OplockLevel == 0) pCifsInode->clientCanCacheRead = false; - rc = slow_work_enqueue(&netfile->oplock_break); - if (rc) { - cERROR(1, "failed to enqueue oplock " - "break: %d\n", rc); - } else { - netfile->oplock_break_cancelled = false; - } + + /* + * cifs_oplock_break_put() can't be called + * from here. Get reference after queueing + * succeeded. cifs_oplock_break() will + * synchronize using GlobalSMSSeslock. + */ + if (queue_work(system_nrt_wq, + &netfile->oplock_break)) + cifs_oplock_break_get(netfile); + netfile->oplock_break_cancelled = false; + read_unlock(&GlobalSMBSeslock); read_unlock(&cifs_tcp_ses_lock); return true; -- cgit v1.2.3 From 92c60ccaf3c15a06d859682b980de1066641b4d0 Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Sun, 23 May 2010 00:17:48 +0900 Subject: nilfs2: add blocksize member to nilfs object This stores blocksize in nilfs objects for the successive refactoring of recovery logic. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/the_nilfs.c | 1 + fs/nilfs2/the_nilfs.h | 2 ++ 2 files changed, 3 insertions(+) (limited to 'fs') diff --git a/fs/nilfs2/the_nilfs.c b/fs/nilfs2/the_nilfs.c index 8c1097327abc..870a1273e9b0 100644 --- a/fs/nilfs2/the_nilfs.c +++ b/fs/nilfs2/the_nilfs.c @@ -604,6 +604,7 @@ int init_nilfs(struct the_nilfs *nilfs, struct nilfs_sb_info *sbi, char *data) when reloading fails. */ } nilfs->ns_blocksize_bits = sb->s_blocksize_bits; + nilfs->ns_blocksize = blocksize; err = nilfs_store_disk_layout(nilfs, sbp); if (err) diff --git a/fs/nilfs2/the_nilfs.h b/fs/nilfs2/the_nilfs.h index 1ab974533697..85df47f0730f 100644 --- a/fs/nilfs2/the_nilfs.h +++ b/fs/nilfs2/the_nilfs.h @@ -82,6 +82,7 @@ enum { * @ns_gc_inodes: dummy inodes to keep live blocks * @ns_gc_inodes_h: hash list to keep dummy inode holding live blocks * @ns_blocksize_bits: bit length of block size + * @ns_blocksize: block size * @ns_nsegments: number of segments in filesystem * @ns_blocks_per_segment: number of blocks per segment * @ns_r_segments_percentage: reserved segments percentage @@ -168,6 +169,7 @@ struct the_nilfs { /* Disk layout information (static) */ unsigned int ns_blocksize_bits; + unsigned int ns_blocksize; unsigned long ns_nsegments; unsigned long ns_blocks_per_segment; unsigned long ns_r_segments_percentage; -- cgit v1.2.3 From 8b94025c00f9171b41ba9c1696943f5c935b62ef Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Sun, 23 May 2010 01:39:02 +0900 Subject: nilfs2: refactor recovery logic routines Most functions in recovery code take an argument of a super block instance or a nilfs_sb_info struct for convenience sake. This replaces them aggressively with a nilfs object by applying __bread and __breadahead against routines using sb_bread and sb_breadahead. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/recovery.c | 160 +++++++++++++++++++++++++++++--------------------- fs/nilfs2/segment.h | 4 +- fs/nilfs2/the_nilfs.c | 9 ++- 3 files changed, 100 insertions(+), 73 deletions(-) (limited to 'fs') diff --git a/fs/nilfs2/recovery.c b/fs/nilfs2/recovery.c index bae2a516b4ee..1c883b1074eb 100644 --- a/fs/nilfs2/recovery.c +++ b/fs/nilfs2/recovery.c @@ -110,8 +110,8 @@ static void store_segsum_info(struct nilfs_segsum_info *ssi, } /** - * calc_crc_cont - check CRC of blocks continuously - * @sbi: nilfs_sb_info + * nilfs_compute_checksum - compute checksum of blocks continuously + * @nilfs: nilfs object * @bhs: buffer head of start block * @sum: place to store result * @offset: offset bytes in the first block @@ -119,23 +119,25 @@ static void store_segsum_info(struct nilfs_segsum_info *ssi, * @start: DBN of start block * @nblock: number of blocks to be checked */ -static int calc_crc_cont(struct nilfs_sb_info *sbi, struct buffer_head *bhs, - u32 *sum, unsigned long offset, u64 check_bytes, - sector_t start, unsigned long nblock) +static int nilfs_compute_checksum(struct the_nilfs *nilfs, + struct buffer_head *bhs, u32 *sum, + unsigned long offset, u64 check_bytes, + sector_t start, unsigned long nblock) { - unsigned long blocksize = sbi->s_super->s_blocksize; + unsigned int blocksize = nilfs->ns_blocksize; unsigned long size; u32 crc; BUG_ON(offset >= blocksize); check_bytes -= offset; size = min_t(u64, check_bytes, blocksize - offset); - crc = crc32_le(sbi->s_nilfs->ns_crc_seed, + crc = crc32_le(nilfs->ns_crc_seed, (unsigned char *)bhs->b_data + offset, size); if (--nblock > 0) { do { - struct buffer_head *bh - = sb_bread(sbi->s_super, ++start); + struct buffer_head *bh; + + bh = __bread(nilfs->ns_bdev, ++start, blocksize); if (!bh) return -EIO; check_bytes -= size; @@ -150,12 +152,12 @@ static int calc_crc_cont(struct nilfs_sb_info *sbi, struct buffer_head *bhs, /** * nilfs_read_super_root_block - read super root block - * @sb: super_block + * @nilfs: nilfs object * @sr_block: disk block number of the super root block * @pbh: address of a buffer_head pointer to return super root buffer * @check: CRC check flag */ -int nilfs_read_super_root_block(struct super_block *sb, sector_t sr_block, +int nilfs_read_super_root_block(struct the_nilfs *nilfs, sector_t sr_block, struct buffer_head **pbh, int check) { struct buffer_head *bh_sr; @@ -164,7 +166,7 @@ int nilfs_read_super_root_block(struct super_block *sb, sector_t sr_block, int ret; *pbh = NULL; - bh_sr = sb_bread(sb, sr_block); + bh_sr = __bread(nilfs->ns_bdev, sr_block, nilfs->ns_blocksize); if (unlikely(!bh_sr)) { ret = NILFS_SEG_FAIL_IO; goto failed; @@ -174,12 +176,13 @@ int nilfs_read_super_root_block(struct super_block *sb, sector_t sr_block, if (check) { unsigned bytes = le16_to_cpu(sr->sr_bytes); - if (bytes == 0 || bytes > sb->s_blocksize) { + if (bytes == 0 || bytes > nilfs->ns_blocksize) { ret = NILFS_SEG_FAIL_CHECKSUM_SUPER_ROOT; goto failed_bh; } - if (calc_crc_cont(NILFS_SB(sb), bh_sr, &crc, - sizeof(sr->sr_sum), bytes, sr_block, 1)) { + if (nilfs_compute_checksum( + nilfs, bh_sr, &crc, sizeof(sr->sr_sum), bytes, + sr_block, 1)) { ret = NILFS_SEG_FAIL_IO; goto failed_bh; } @@ -200,13 +203,13 @@ int nilfs_read_super_root_block(struct super_block *sb, sector_t sr_block, /** * load_segment_summary - read segment summary of the specified partial segment - * @sbi: nilfs_sb_info + * @nilfs: nilfs object * @pseg_start: start disk block number of partial segment * @seg_seq: sequence number requested * @ssi: pointer to nilfs_segsum_info struct to store information */ static int -load_segment_summary(struct nilfs_sb_info *sbi, sector_t pseg_start, +load_segment_summary(struct the_nilfs *nilfs, sector_t pseg_start, u64 seg_seq, struct nilfs_segsum_info *ssi) { struct buffer_head *bh_sum; @@ -215,7 +218,7 @@ load_segment_summary(struct nilfs_sb_info *sbi, sector_t pseg_start, u32 crc; int ret = NILFS_SEG_FAIL_IO; - bh_sum = sb_bread(sbi->s_super, pseg_start); + bh_sum = __bread(nilfs->ns_bdev, pseg_start, nilfs->ns_blocksize); if (!bh_sum) goto out; @@ -226,22 +229,21 @@ load_segment_summary(struct nilfs_sb_info *sbi, sector_t pseg_start, ret = NILFS_SEG_FAIL_MAGIC; goto failed; } - store_segsum_info(ssi, sum, sbi->s_super->s_blocksize); + store_segsum_info(ssi, sum, nilfs->ns_blocksize); if (seg_seq != ssi->seg_seq) { ret = NILFS_SEG_FAIL_SEQ; goto failed; } nblock = ssi->nblocks; - if (unlikely(nblock == 0 || - nblock > sbi->s_nilfs->ns_blocks_per_segment)) { + if (unlikely(nblock == 0 || nblock > nilfs->ns_blocks_per_segment)) { /* This limits the number of blocks read in the CRC check */ ret = NILFS_SEG_FAIL_CONSISTENCY; goto failed; } - if (calc_crc_cont(sbi, bh_sum, &crc, sizeof(sum->ss_datasum), - ((u64)nblock << sbi->s_super->s_blocksize_bits), - pseg_start, nblock)) { + if (nilfs_compute_checksum(nilfs, bh_sum, &crc, sizeof(sum->ss_datasum), + ((u64)nblock << nilfs->ns_blocksize_bits), + pseg_start, nblock)) { ret = NILFS_SEG_FAIL_IO; goto failed; } @@ -255,8 +257,16 @@ load_segment_summary(struct nilfs_sb_info *sbi, sector_t pseg_start, return ret; } -static void *segsum_get(struct super_block *sb, struct buffer_head **pbh, - unsigned int *offset, unsigned int bytes) +/** + * nilfs_read_summary_info - read an item on summary blocks of a log + * @nilfs: nilfs object + * @pbh: the current buffer head on summary blocks [in, out] + * @offset: the current byte offset on summary blocks [in, out] + * @bytes: byte size of the item to be read + */ +static void *nilfs_read_summary_info(struct the_nilfs *nilfs, + struct buffer_head **pbh, + unsigned int *offset, unsigned int bytes) { void *ptr; sector_t blocknr; @@ -265,7 +275,8 @@ static void *segsum_get(struct super_block *sb, struct buffer_head **pbh, if (bytes > (*pbh)->b_size - *offset) { blocknr = (*pbh)->b_blocknr; brelse(*pbh); - *pbh = sb_bread(sb, blocknr + 1); + *pbh = __bread(nilfs->ns_bdev, blocknr + 1, + nilfs->ns_blocksize); if (unlikely(!*pbh)) return NULL; *offset = 0; @@ -275,9 +286,18 @@ static void *segsum_get(struct super_block *sb, struct buffer_head **pbh, return ptr; } -static void segsum_skip(struct super_block *sb, struct buffer_head **pbh, - unsigned int *offset, unsigned int bytes, - unsigned long count) +/** + * nilfs_skip_summary_info - skip items on summary blocks of a log + * @nilfs: nilfs object + * @pbh: the current buffer head on summary blocks [in, out] + * @offset: the current byte offset on summary blocks [in, out] + * @bytes: byte size of the item to be skipped + * @count: number of items to be skipped + */ +static void nilfs_skip_summary_info(struct the_nilfs *nilfs, + struct buffer_head **pbh, + unsigned int *offset, unsigned int bytes, + unsigned long count) { unsigned int rest_item_in_current_block = ((*pbh)->b_size - *offset) / bytes; @@ -294,26 +314,33 @@ static void segsum_skip(struct super_block *sb, struct buffer_head **pbh, *offset = bytes * (count - (bcnt - 1) * nitem_per_block); brelse(*pbh); - *pbh = sb_bread(sb, blocknr + bcnt); + *pbh = __bread(nilfs->ns_bdev, blocknr + bcnt, + nilfs->ns_blocksize); } } -static int -collect_blocks_from_segsum(struct nilfs_sb_info *sbi, sector_t sum_blocknr, - struct nilfs_segsum_info *ssi, - struct list_head *head) +/** + * nilfs_scan_dsync_log - get block information of a log written for data sync + * @nilfs: nilfs object + * @start_blocknr: start block number of the log + * @ssi: log summary information + * @head: list head to add nilfs_recovery_block struct + */ +static int nilfs_scan_dsync_log(struct the_nilfs *nilfs, sector_t start_blocknr, + struct nilfs_segsum_info *ssi, + struct list_head *head) { struct buffer_head *bh; unsigned int offset; unsigned long nfinfo = ssi->nfinfo; - sector_t blocknr = sum_blocknr + ssi->nsumblk; + sector_t blocknr = start_blocknr + ssi->nsumblk; ino_t ino; int err = -EIO; if (!nfinfo) return 0; - bh = sb_bread(sbi->s_super, sum_blocknr); + bh = __bread(nilfs->ns_bdev, start_blocknr, nilfs->ns_blocksize); if (unlikely(!bh)) goto out; @@ -323,7 +350,8 @@ collect_blocks_from_segsum(struct nilfs_sb_info *sbi, sector_t sum_blocknr, unsigned long nblocks, ndatablk, nnodeblk; struct nilfs_finfo *finfo; - finfo = segsum_get(sbi->s_super, &bh, &offset, sizeof(*finfo)); + finfo = nilfs_read_summary_info(nilfs, &bh, &offset, + sizeof(*finfo)); if (unlikely(!finfo)) goto out; @@ -336,8 +364,8 @@ collect_blocks_from_segsum(struct nilfs_sb_info *sbi, sector_t sum_blocknr, struct nilfs_recovery_block *rb; struct nilfs_binfo_v *binfo; - binfo = segsum_get(sbi->s_super, &bh, &offset, - sizeof(*binfo)); + binfo = nilfs_read_summary_info(nilfs, &bh, &offset, + sizeof(*binfo)); if (unlikely(!binfo)) goto out; @@ -355,9 +383,9 @@ collect_blocks_from_segsum(struct nilfs_sb_info *sbi, sector_t sum_blocknr, } if (--nfinfo == 0) break; - blocknr += nnodeblk; /* always 0 for the data sync segments */ - segsum_skip(sbi->s_super, &bh, &offset, sizeof(__le64), - nnodeblk); + blocknr += nnodeblk; /* always 0 for data sync logs */ + nilfs_skip_summary_info(nilfs, &bh, &offset, sizeof(__le64), + nnodeblk); if (unlikely(!bh)) goto out; } @@ -467,14 +495,14 @@ static int nilfs_prepare_segment_for_recovery(struct the_nilfs *nilfs, return err; } -static int nilfs_recovery_copy_block(struct nilfs_sb_info *sbi, +static int nilfs_recovery_copy_block(struct the_nilfs *nilfs, struct nilfs_recovery_block *rb, struct page *page) { struct buffer_head *bh_org; void *kaddr; - bh_org = sb_bread(sbi->s_super, rb->blocknr); + bh_org = __bread(nilfs->ns_bdev, rb->blocknr, nilfs->ns_blocksize); if (unlikely(!bh_org)) return -EIO; @@ -485,13 +513,14 @@ static int nilfs_recovery_copy_block(struct nilfs_sb_info *sbi, return 0; } -static int recover_dsync_blocks(struct nilfs_sb_info *sbi, - struct list_head *head, - unsigned long *nr_salvaged_blocks) +static int nilfs_recover_dsync_blocks(struct the_nilfs *nilfs, + struct nilfs_sb_info *sbi, + struct list_head *head, + unsigned long *nr_salvaged_blocks) { struct inode *inode; struct nilfs_recovery_block *rb, *n; - unsigned blocksize = sbi->s_super->s_blocksize; + unsigned blocksize = nilfs->ns_blocksize; struct page *page; loff_t pos; int err = 0, err2 = 0; @@ -511,7 +540,7 @@ static int recover_dsync_blocks(struct nilfs_sb_info *sbi, if (unlikely(err)) goto failed_inode; - err = nilfs_recovery_copy_block(sbi, rb, page); + err = nilfs_recovery_copy_block(nilfs, rb, page); if (unlikely(err)) goto failed_page; @@ -551,8 +580,8 @@ static int recover_dsync_blocks(struct nilfs_sb_info *sbi, /** * nilfs_do_roll_forward - salvage logical segments newer than the latest * checkpoint + * @nilfs: nilfs object * @sbi: nilfs_sb_info - * @nilfs: the_nilfs * @ri: pointer to a nilfs_recovery_info */ static int nilfs_do_roll_forward(struct the_nilfs *nilfs, @@ -582,7 +611,7 @@ static int nilfs_do_roll_forward(struct the_nilfs *nilfs, while (segnum != ri->ri_segnum || pseg_start <= ri->ri_pseg_start) { - ret = load_segment_summary(sbi, pseg_start, seg_seq, &ssi); + ret = load_segment_summary(nilfs, pseg_start, seg_seq, &ssi); if (ret) { if (ret == NILFS_SEG_FAIL_IO) { err = -EIO; @@ -610,13 +639,14 @@ static int nilfs_do_roll_forward(struct the_nilfs *nilfs, if (!NILFS_SEG_DSYNC(&ssi)) goto confused; - err = collect_blocks_from_segsum( - sbi, pseg_start, &ssi, &dsync_blocks); + err = nilfs_scan_dsync_log(nilfs, pseg_start, &ssi, + &dsync_blocks); if (unlikely(err)) goto failed; if (NILFS_SEG_LOGEND(&ssi)) { - err = recover_dsync_blocks( - sbi, &dsync_blocks, &nsalvaged_blocks); + err = nilfs_recover_dsync_blocks( + nilfs, sbi, &dsync_blocks, + &nsalvaged_blocks); if (unlikely(err)) goto failed; state = RF_INIT_ST; @@ -653,7 +683,7 @@ static int nilfs_do_roll_forward(struct the_nilfs *nilfs, } out: dispose_recovery_list(&dsync_blocks); - nilfs_detach_writer(sbi->s_nilfs, sbi); + nilfs_detach_writer(nilfs, sbi); return err; confused: @@ -667,7 +697,6 @@ static int nilfs_do_roll_forward(struct the_nilfs *nilfs, } static void nilfs_finish_roll_forward(struct the_nilfs *nilfs, - struct nilfs_sb_info *sbi, struct nilfs_recovery_info *ri) { struct buffer_head *bh; @@ -677,7 +706,7 @@ static void nilfs_finish_roll_forward(struct the_nilfs *nilfs, nilfs_get_segnum_of_block(nilfs, ri->ri_super_root)) return; - bh = sb_getblk(sbi->s_super, ri->ri_lsegs_start); + bh = __getblk(nilfs->ns_bdev, ri->ri_lsegs_start, nilfs->ns_blocksize); BUG_ON(!bh); memset(bh->b_data, 0, bh->b_size); set_buffer_dirty(bh); @@ -751,7 +780,7 @@ int nilfs_recover_logical_segments(struct the_nilfs *nilfs, goto failed; } - nilfs_finish_roll_forward(nilfs, sbi, ri); + nilfs_finish_roll_forward(nilfs, ri); } failed: @@ -762,7 +791,6 @@ int nilfs_recover_logical_segments(struct the_nilfs *nilfs, /** * nilfs_search_super_root - search the latest valid super root * @nilfs: the_nilfs - * @sbi: nilfs_sb_info * @ri: pointer to a nilfs_recovery_info struct to store search results. * * nilfs_search_super_root() looks for the latest super-root from a partial @@ -776,7 +804,7 @@ int nilfs_recover_logical_segments(struct the_nilfs *nilfs, * * %-EIO - I/O error */ -int nilfs_search_super_root(struct the_nilfs *nilfs, struct nilfs_sb_info *sbi, +int nilfs_search_super_root(struct the_nilfs *nilfs, struct nilfs_recovery_info *ri) { struct nilfs_segsum_info ssi; @@ -801,11 +829,10 @@ int nilfs_search_super_root(struct the_nilfs *nilfs, struct nilfs_sb_info *sbi, /* Read ahead segment */ b = seg_start; while (b <= seg_end) - sb_breadahead(sbi->s_super, b++); + __breadahead(nilfs->ns_bdev, b++, nilfs->ns_blocksize); for (;;) { - /* Load segment summary */ - ret = load_segment_summary(sbi, pseg_start, seg_seq, &ssi); + ret = load_segment_summary(nilfs, pseg_start, seg_seq, &ssi); if (ret) { if (ret == NILFS_SEG_FAIL_IO) goto failed; @@ -836,7 +863,8 @@ int nilfs_search_super_root(struct the_nilfs *nilfs, struct nilfs_sb_info *sbi, if (pseg_start == seg_start) { nilfs_get_segment_range(nilfs, nextnum, &b, &end); while (b <= end) - sb_breadahead(sbi->s_super, b++); + __breadahead(nilfs->ns_bdev, b++, + nilfs->ns_blocksize); } if (!NILFS_SEG_HAS_SR(&ssi)) { if (!ri->ri_lsegs_start && NILFS_SEG_LOGBGN(&ssi)) { diff --git a/fs/nilfs2/segment.h b/fs/nilfs2/segment.h index 01e20dbb217d..d6bb67ee7bc8 100644 --- a/fs/nilfs2/segment.h +++ b/fs/nilfs2/segment.h @@ -234,9 +234,9 @@ extern int nilfs_attach_segment_constructor(struct nilfs_sb_info *); extern void nilfs_detach_segment_constructor(struct nilfs_sb_info *); /* recovery.c */ -extern int nilfs_read_super_root_block(struct super_block *, sector_t, +extern int nilfs_read_super_root_block(struct the_nilfs *, sector_t, struct buffer_head **, int); -extern int nilfs_search_super_root(struct the_nilfs *, struct nilfs_sb_info *, +extern int nilfs_search_super_root(struct the_nilfs *, struct nilfs_recovery_info *); extern int nilfs_recover_logical_segments(struct the_nilfs *, struct nilfs_sb_info *, diff --git a/fs/nilfs2/the_nilfs.c b/fs/nilfs2/the_nilfs.c index 870a1273e9b0..a94908e4d6d7 100644 --- a/fs/nilfs2/the_nilfs.c +++ b/fs/nilfs2/the_nilfs.c @@ -159,8 +159,7 @@ void put_nilfs(struct the_nilfs *nilfs) kfree(nilfs); } -static int nilfs_load_super_root(struct the_nilfs *nilfs, - struct nilfs_sb_info *sbi, sector_t sr_block) +static int nilfs_load_super_root(struct the_nilfs *nilfs, sector_t sr_block) { struct buffer_head *bh_sr; struct nilfs_super_root *raw_sr; @@ -169,7 +168,7 @@ static int nilfs_load_super_root(struct the_nilfs *nilfs, unsigned inode_size; int err; - err = nilfs_read_super_root_block(sbi->s_super, sr_block, &bh_sr, 1); + err = nilfs_read_super_root_block(nilfs, sr_block, &bh_sr, 1); if (unlikely(err)) return err; @@ -285,13 +284,13 @@ int load_nilfs(struct the_nilfs *nilfs, struct nilfs_sb_info *sbi) nilfs_init_recovery_info(&ri); - err = nilfs_search_super_root(nilfs, sbi, &ri); + err = nilfs_search_super_root(nilfs, &ri); if (unlikely(err)) { printk(KERN_ERR "NILFS: error searching super root.\n"); goto failed; } - err = nilfs_load_super_root(nilfs, sbi, ri.ri_super_root); + err = nilfs_load_super_root(nilfs, ri.ri_super_root); if (unlikely(err)) { printk(KERN_ERR "NILFS: error loading super root.\n"); goto failed; -- cgit v1.2.3 From aee5ce2f578b98eba16e59cb84d39a95682a836b Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Sun, 23 May 2010 12:21:57 +0900 Subject: nilfs2: rename nilfs_recover_logical_segments function The function name of nilfs_recover_logical_segments makes no sense. This changes the name into nilfs_salvage_orphan_logs to clarify the role of the function. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/recovery.c | 11 +++++------ fs/nilfs2/segment.h | 6 +++--- fs/nilfs2/the_nilfs.c | 2 +- 3 files changed, 9 insertions(+), 10 deletions(-) (limited to 'fs') diff --git a/fs/nilfs2/recovery.c b/fs/nilfs2/recovery.c index 1c883b1074eb..fd7fb4149aa7 100644 --- a/fs/nilfs2/recovery.c +++ b/fs/nilfs2/recovery.c @@ -719,9 +719,8 @@ static void nilfs_finish_roll_forward(struct the_nilfs *nilfs, } /** - * nilfs_recover_logical_segments - salvage logical segments written after - * the latest super root - * @nilfs: the_nilfs + * nilfs_salvage_orphan_logs - salvage logs written after the latest checkpoint + * @nilfs: nilfs object * @sbi: nilfs_sb_info * @ri: pointer to a nilfs_recovery_info struct to store search results. * @@ -738,9 +737,9 @@ static void nilfs_finish_roll_forward(struct the_nilfs *nilfs, * * %-ENOMEM - Insufficient memory available. */ -int nilfs_recover_logical_segments(struct the_nilfs *nilfs, - struct nilfs_sb_info *sbi, - struct nilfs_recovery_info *ri) +int nilfs_salvage_orphan_logs(struct the_nilfs *nilfs, + struct nilfs_sb_info *sbi, + struct nilfs_recovery_info *ri) { int err; diff --git a/fs/nilfs2/segment.h b/fs/nilfs2/segment.h index d6bb67ee7bc8..17c487bd8152 100644 --- a/fs/nilfs2/segment.h +++ b/fs/nilfs2/segment.h @@ -238,9 +238,9 @@ extern int nilfs_read_super_root_block(struct the_nilfs *, sector_t, struct buffer_head **, int); extern int nilfs_search_super_root(struct the_nilfs *, struct nilfs_recovery_info *); -extern int nilfs_recover_logical_segments(struct the_nilfs *, - struct nilfs_sb_info *, - struct nilfs_recovery_info *); +extern int nilfs_salvage_orphan_logs(struct the_nilfs *, + struct nilfs_sb_info *, + struct nilfs_recovery_info *); extern void nilfs_dispose_segment_list(struct list_head *); #endif /* _NILFS_SEGMENT_H */ diff --git a/fs/nilfs2/the_nilfs.c b/fs/nilfs2/the_nilfs.c index a94908e4d6d7..9f2cb01994d0 100644 --- a/fs/nilfs2/the_nilfs.c +++ b/fs/nilfs2/the_nilfs.c @@ -319,7 +319,7 @@ int load_nilfs(struct the_nilfs *nilfs, struct nilfs_sb_info *sbi) goto failed_unload; } - err = nilfs_recover_logical_segments(nilfs, sbi, &ri); + err = nilfs_salvage_orphan_logs(nilfs, sbi, &ri); if (err) goto failed_unload; -- cgit v1.2.3 From 354fa8be280ce81c88b6b236d62d23ebcade2d3f Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Sun, 23 May 2010 19:21:49 +0900 Subject: nilfs2: divide load_segment_summary function load_segment_summary function has two distinct roles: getting summary header of a log, and verifying consistencies of the log. This divide it into two corresponding functions, nilfs_read_log_header and nilfs_validate_log to clarify the meaning. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/recovery.c | 110 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 69 insertions(+), 41 deletions(-) (limited to 'fs') diff --git a/fs/nilfs2/recovery.c b/fs/nilfs2/recovery.c index fd7fb4149aa7..35506b1704d1 100644 --- a/fs/nilfs2/recovery.c +++ b/fs/nilfs2/recovery.c @@ -202,58 +202,63 @@ int nilfs_read_super_root_block(struct the_nilfs *nilfs, sector_t sr_block, } /** - * load_segment_summary - read segment summary of the specified partial segment + * nilfs_read_log_header - read summary header of the specified log * @nilfs: nilfs object - * @pseg_start: start disk block number of partial segment - * @seg_seq: sequence number requested - * @ssi: pointer to nilfs_segsum_info struct to store information + * @start_blocknr: start block number of the log + * @sum: pointer to return segment summary structure */ -static int -load_segment_summary(struct the_nilfs *nilfs, sector_t pseg_start, - u64 seg_seq, struct nilfs_segsum_info *ssi) +static struct buffer_head * +nilfs_read_log_header(struct the_nilfs *nilfs, sector_t start_blocknr, + struct nilfs_segment_summary **sum) { struct buffer_head *bh_sum; - struct nilfs_segment_summary *sum; + + bh_sum = __bread(nilfs->ns_bdev, start_blocknr, nilfs->ns_blocksize); + if (bh_sum) + *sum = (struct nilfs_segment_summary *)bh_sum->b_data; + return bh_sum; +} + +/** + * nilfs_validate_log - verify consistency of log + * @nilfs: nilfs object + * @seg_seq: sequence number of segment + * @bh_sum: buffer head of summary block + * @sum: segment summary struct + */ +static int nilfs_validate_log(struct the_nilfs *nilfs, u64 seg_seq, + struct buffer_head *bh_sum, + struct nilfs_segment_summary *sum) +{ unsigned long nblock; u32 crc; - int ret = NILFS_SEG_FAIL_IO; + int ret; - bh_sum = __bread(nilfs->ns_bdev, pseg_start, nilfs->ns_blocksize); - if (!bh_sum) + ret = NILFS_SEG_FAIL_MAGIC; + if (le32_to_cpu(sum->ss_magic) != NILFS_SEGSUM_MAGIC) goto out; - sum = (struct nilfs_segment_summary *)bh_sum->b_data; - - /* Check consistency of segment summary */ - if (le32_to_cpu(sum->ss_magic) != NILFS_SEGSUM_MAGIC) { - ret = NILFS_SEG_FAIL_MAGIC; - goto failed; - } - store_segsum_info(ssi, sum, nilfs->ns_blocksize); - if (seg_seq != ssi->seg_seq) { - ret = NILFS_SEG_FAIL_SEQ; - goto failed; - } + ret = NILFS_SEG_FAIL_SEQ; + if (le64_to_cpu(sum->ss_seq) != seg_seq) + goto out; - nblock = ssi->nblocks; - if (unlikely(nblock == 0 || nblock > nilfs->ns_blocks_per_segment)) { + nblock = le32_to_cpu(sum->ss_nblocks); + ret = NILFS_SEG_FAIL_CONSISTENCY; + if (unlikely(nblock == 0 || nblock > nilfs->ns_blocks_per_segment)) /* This limits the number of blocks read in the CRC check */ - ret = NILFS_SEG_FAIL_CONSISTENCY; - goto failed; - } + goto out; + + ret = NILFS_SEG_FAIL_IO; if (nilfs_compute_checksum(nilfs, bh_sum, &crc, sizeof(sum->ss_datasum), ((u64)nblock << nilfs->ns_blocksize_bits), - pseg_start, nblock)) { - ret = NILFS_SEG_FAIL_IO; - goto failed; - } - if (crc == le32_to_cpu(sum->ss_datasum)) - ret = 0; - else - ret = NILFS_SEG_FAIL_CHECKSUM_FULL; - failed: - brelse(bh_sum); - out: + bh_sum->b_blocknr, nblock)) + goto out; + + ret = NILFS_SEG_FAIL_CHECKSUM_FULL; + if (crc != le32_to_cpu(sum->ss_datasum)) + goto out; + ret = 0; +out: return ret; } @@ -589,6 +594,8 @@ static int nilfs_do_roll_forward(struct the_nilfs *nilfs, struct nilfs_recovery_info *ri) { struct nilfs_segsum_info ssi; + struct buffer_head *bh_sum = NULL; + struct nilfs_segment_summary *sum; sector_t pseg_start; sector_t seg_start, seg_end; /* Starting/ending DBN of full segment */ unsigned long nsalvaged_blocks = 0; @@ -610,8 +617,14 @@ static int nilfs_do_roll_forward(struct the_nilfs *nilfs, nilfs_get_segment_range(nilfs, segnum, &seg_start, &seg_end); while (segnum != ri->ri_segnum || pseg_start <= ri->ri_pseg_start) { + brelse(bh_sum); + bh_sum = nilfs_read_log_header(nilfs, pseg_start, &sum); + if (!bh_sum) { + err = -EIO; + goto failed; + } - ret = load_segment_summary(nilfs, pseg_start, seg_seq, &ssi); + ret = nilfs_validate_log(nilfs, seg_seq, bh_sum, sum); if (ret) { if (ret == NILFS_SEG_FAIL_IO) { err = -EIO; @@ -619,6 +632,8 @@ static int nilfs_do_roll_forward(struct the_nilfs *nilfs, } goto strayed; } + + store_segsum_info(&ssi, sum, nilfs->ns_blocksize); if (unlikely(NILFS_SEG_HAS_SR(&ssi))) goto confused; @@ -682,6 +697,7 @@ static int nilfs_do_roll_forward(struct the_nilfs *nilfs, ri->ri_need_recovery = NILFS_RECOVERY_ROLLFORWARD_DONE; } out: + brelse(bh_sum); dispose_recovery_list(&dsync_blocks); nilfs_detach_writer(nilfs, sbi); return err; @@ -807,6 +823,8 @@ int nilfs_search_super_root(struct the_nilfs *nilfs, struct nilfs_recovery_info *ri) { struct nilfs_segsum_info ssi; + struct buffer_head *bh_sum = NULL; + struct nilfs_segment_summary *sum; sector_t pseg_start, pseg_end, sr_pseg_start = 0; sector_t seg_start, seg_end; /* range of full segment (block number) */ sector_t b, end; @@ -831,12 +849,20 @@ int nilfs_search_super_root(struct the_nilfs *nilfs, __breadahead(nilfs->ns_bdev, b++, nilfs->ns_blocksize); for (;;) { - ret = load_segment_summary(nilfs, pseg_start, seg_seq, &ssi); + brelse(bh_sum); + ret = NILFS_SEG_FAIL_IO; + bh_sum = nilfs_read_log_header(nilfs, pseg_start, &sum); + if (!bh_sum) + goto failed; + + ret = nilfs_validate_log(nilfs, seg_seq, bh_sum, sum); if (ret) { if (ret == NILFS_SEG_FAIL_IO) goto failed; goto strayed; } + + store_segsum_info(&ssi, sum, nilfs->ns_blocksize); pseg_end = pseg_start + ssi.nblocks - 1; if (unlikely(pseg_end > seg_end)) { ret = NILFS_SEG_FAIL_CONSISTENCY; @@ -936,6 +962,7 @@ int nilfs_search_super_root(struct the_nilfs *nilfs, super_root_found: /* Updating pointers relating to the latest checkpoint */ + brelse(bh_sum); list_splice_tail(&segments, &ri->ri_used_segments); nilfs->ns_last_pseg = sr_pseg_start; nilfs->ns_last_seq = nilfs->ns_seg_seq; @@ -943,6 +970,7 @@ int nilfs_search_super_root(struct the_nilfs *nilfs, return 0; failed: + brelse(bh_sum); nilfs_dispose_segment_list(&segments); return (ret < 0) ? ret : nilfs_warn_segment_error(ret); } -- cgit v1.2.3 From 85655484f896d078d310221475b90ea27f76e5f2 Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Sun, 23 May 2010 19:46:44 +0900 Subject: nilfs2: do not use nilfs_segsum_info structure in recovery code This will get rid of nilfs_segsum_info use from recovery functions for simplicity. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/recovery.c | 91 +++++++++++++++++++++------------------------------- 1 file changed, 37 insertions(+), 54 deletions(-) (limited to 'fs') diff --git a/fs/nilfs2/recovery.c b/fs/nilfs2/recovery.c index 35506b1704d1..f5d9c3f954ae 100644 --- a/fs/nilfs2/recovery.c +++ b/fs/nilfs2/recovery.c @@ -91,24 +91,6 @@ static int nilfs_warn_segment_error(int err) return -EINVAL; } -static void store_segsum_info(struct nilfs_segsum_info *ssi, - struct nilfs_segment_summary *sum, - unsigned int blocksize) -{ - ssi->flags = le16_to_cpu(sum->ss_flags); - ssi->seg_seq = le64_to_cpu(sum->ss_seq); - ssi->ctime = le64_to_cpu(sum->ss_create); - ssi->next = le64_to_cpu(sum->ss_next); - ssi->nblocks = le32_to_cpu(sum->ss_nblocks); - ssi->nfinfo = le32_to_cpu(sum->ss_nfinfo); - ssi->sumbytes = le32_to_cpu(sum->ss_sumbytes); - - ssi->nsumblk = DIV_ROUND_UP(ssi->sumbytes, blocksize); - ssi->nfileblk = ssi->nblocks - ssi->nsumblk - !!NILFS_SEG_HAS_SR(ssi); - - /* need to verify ->ss_bytes field if read ->ss_cno */ -} - /** * nilfs_compute_checksum - compute checksum of blocks continuously * @nilfs: nilfs object @@ -328,29 +310,31 @@ static void nilfs_skip_summary_info(struct the_nilfs *nilfs, * nilfs_scan_dsync_log - get block information of a log written for data sync * @nilfs: nilfs object * @start_blocknr: start block number of the log - * @ssi: log summary information + * @sum: log summary information * @head: list head to add nilfs_recovery_block struct */ static int nilfs_scan_dsync_log(struct the_nilfs *nilfs, sector_t start_blocknr, - struct nilfs_segsum_info *ssi, + struct nilfs_segment_summary *sum, struct list_head *head) { struct buffer_head *bh; unsigned int offset; - unsigned long nfinfo = ssi->nfinfo; - sector_t blocknr = start_blocknr + ssi->nsumblk; + u32 nfinfo, sumbytes; + sector_t blocknr; ino_t ino; int err = -EIO; + nfinfo = le32_to_cpu(sum->ss_nfinfo); if (!nfinfo) return 0; + sumbytes = le32_to_cpu(sum->ss_sumbytes); + blocknr = start_blocknr + DIV_ROUND_UP(sumbytes, nilfs->ns_blocksize); bh = __bread(nilfs->ns_bdev, start_blocknr, nilfs->ns_blocksize); if (unlikely(!bh)) goto out; - offset = le16_to_cpu( - ((struct nilfs_segment_summary *)bh->b_data)->ss_bytes); + offset = le16_to_cpu(sum->ss_bytes); for (;;) { unsigned long nblocks, ndatablk, nnodeblk; struct nilfs_finfo *finfo; @@ -593,12 +577,12 @@ static int nilfs_do_roll_forward(struct the_nilfs *nilfs, struct nilfs_sb_info *sbi, struct nilfs_recovery_info *ri) { - struct nilfs_segsum_info ssi; struct buffer_head *bh_sum = NULL; struct nilfs_segment_summary *sum; sector_t pseg_start; sector_t seg_start, seg_end; /* Starting/ending DBN of full segment */ unsigned long nsalvaged_blocks = 0; + unsigned int flags; u64 seg_seq; __u64 segnum, nextnum = 0; int empty_seg = 0; @@ -633,32 +617,34 @@ static int nilfs_do_roll_forward(struct the_nilfs *nilfs, goto strayed; } - store_segsum_info(&ssi, sum, nilfs->ns_blocksize); - if (unlikely(NILFS_SEG_HAS_SR(&ssi))) + flags = le16_to_cpu(sum->ss_flags); + if (flags & NILFS_SS_SR) goto confused; /* Found a valid partial segment; do recovery actions */ - nextnum = nilfs_get_segnum_of_block(nilfs, ssi.next); + nextnum = nilfs_get_segnum_of_block(nilfs, + le64_to_cpu(sum->ss_next)); empty_seg = 0; - nilfs->ns_ctime = ssi.ctime; - if (!(ssi.flags & NILFS_SS_GC)) - nilfs->ns_nongc_ctime = ssi.ctime; + nilfs->ns_ctime = le64_to_cpu(sum->ss_create); + if (!(flags & NILFS_SS_GC)) + nilfs->ns_nongc_ctime = nilfs->ns_ctime; switch (state) { case RF_INIT_ST: - if (!NILFS_SEG_LOGBGN(&ssi) || !NILFS_SEG_DSYNC(&ssi)) + if (!(flags & NILFS_SS_LOGBGN) || + !(flags & NILFS_SS_SYNDT)) goto try_next_pseg; state = RF_DSYNC_ST; /* Fall through */ case RF_DSYNC_ST: - if (!NILFS_SEG_DSYNC(&ssi)) + if (!(flags & NILFS_SS_SYNDT)) goto confused; - err = nilfs_scan_dsync_log(nilfs, pseg_start, &ssi, + err = nilfs_scan_dsync_log(nilfs, pseg_start, sum, &dsync_blocks); if (unlikely(err)) goto failed; - if (NILFS_SEG_LOGEND(&ssi)) { + if (flags & NILFS_SS_LOGEND) { err = nilfs_recover_dsync_blocks( nilfs, sbi, &dsync_blocks, &nsalvaged_blocks); @@ -672,7 +658,7 @@ static int nilfs_do_roll_forward(struct the_nilfs *nilfs, try_next_pseg: if (pseg_start == ri->ri_lsegs_end) break; - pseg_start += ssi.nblocks; + pseg_start += le32_to_cpu(sum->ss_nblocks); if (pseg_start < seg_end) continue; goto feed_segment; @@ -822,12 +808,13 @@ int nilfs_salvage_orphan_logs(struct the_nilfs *nilfs, int nilfs_search_super_root(struct the_nilfs *nilfs, struct nilfs_recovery_info *ri) { - struct nilfs_segsum_info ssi; struct buffer_head *bh_sum = NULL; struct nilfs_segment_summary *sum; sector_t pseg_start, pseg_end, sr_pseg_start = 0; sector_t seg_start, seg_end; /* range of full segment (block number) */ sector_t b, end; + unsigned long nblocks; + unsigned int flags; u64 seg_seq; __u64 segnum, nextnum = 0; __u64 cno; @@ -862,8 +849,8 @@ int nilfs_search_super_root(struct the_nilfs *nilfs, goto strayed; } - store_segsum_info(&ssi, sum, nilfs->ns_blocksize); - pseg_end = pseg_start + ssi.nblocks - 1; + nblocks = le32_to_cpu(sum->ss_nblocks); + pseg_end = pseg_start + nblocks - 1; if (unlikely(pseg_end > seg_end)) { ret = NILFS_SEG_FAIL_CONSISTENCY; goto strayed; @@ -873,11 +860,13 @@ int nilfs_search_super_root(struct the_nilfs *nilfs, ri->ri_pseg_start = pseg_start; ri->ri_seq = seg_seq; ri->ri_segnum = segnum; - nextnum = nilfs_get_segnum_of_block(nilfs, ssi.next); + nextnum = nilfs_get_segnum_of_block(nilfs, + le64_to_cpu(sum->ss_next)); ri->ri_nextnum = nextnum; empty_seg = 0; - if (!NILFS_SEG_HAS_SR(&ssi) && !scan_newer) { + flags = le16_to_cpu(sum->ss_flags); + if (!(flags & NILFS_SS_SR) && !scan_newer) { /* This will never happen because a superblock (last_segment) always points to a pseg having a super root. */ @@ -891,12 +880,12 @@ int nilfs_search_super_root(struct the_nilfs *nilfs, __breadahead(nilfs->ns_bdev, b++, nilfs->ns_blocksize); } - if (!NILFS_SEG_HAS_SR(&ssi)) { - if (!ri->ri_lsegs_start && NILFS_SEG_LOGBGN(&ssi)) { + if (!(flags & NILFS_SS_SR)) { + if (!ri->ri_lsegs_start && (flags & NILFS_SS_LOGBGN)) { ri->ri_lsegs_start = pseg_start; ri->ri_lsegs_start_seq = seg_seq; } - if (NILFS_SEG_LOGEND(&ssi)) + if (flags & NILFS_SS_LOGEND) ri->ri_lsegs_end = pseg_start; goto try_next_pseg; } @@ -907,12 +896,12 @@ int nilfs_search_super_root(struct the_nilfs *nilfs, ri->ri_lsegs_start = ri->ri_lsegs_end = 0; nilfs_dispose_segment_list(&segments); - nilfs->ns_pseg_offset = (sr_pseg_start = pseg_start) - + ssi.nblocks - seg_start; + sr_pseg_start = pseg_start; + nilfs->ns_pseg_offset = pseg_start + nblocks - seg_start; nilfs->ns_seg_seq = seg_seq; nilfs->ns_segnum = segnum; nilfs->ns_cno = cno; /* nilfs->ns_cno = ri->ri_cno + 1 */ - nilfs->ns_ctime = ssi.ctime; + nilfs->ns_ctime = le64_to_cpu(sum->ss_create); nilfs->ns_nextnum = nextnum; if (scan_newer) @@ -923,15 +912,9 @@ int nilfs_search_super_root(struct the_nilfs *nilfs, scan_newer = 1; } - /* reset region for roll-forward */ - pseg_start += ssi.nblocks; - if (pseg_start < seg_end) - continue; - goto feed_segment; - try_next_pseg: /* Standing on a course, or met an inconsistent state */ - pseg_start += ssi.nblocks; + pseg_start += nblocks; if (pseg_start < seg_end) continue; goto feed_segment; -- cgit v1.2.3 From 4762077c7b93d35e0417f66702deae3ce3a9855e Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Sun, 23 May 2010 21:48:36 +0900 Subject: nilfs2: get rid of macros for segment summary information This removes macros to test segment summary flags and redefines a few relevant macros with inline functions. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/segbuf.h | 24 +++++++++++++----------- fs/nilfs2/segment.c | 8 ++++---- 2 files changed, 17 insertions(+), 15 deletions(-) (limited to 'fs') diff --git a/fs/nilfs2/segbuf.h b/fs/nilfs2/segbuf.h index 85fbb66455e2..b04f08cc2397 100644 --- a/fs/nilfs2/segbuf.h +++ b/fs/nilfs2/segbuf.h @@ -54,17 +54,6 @@ struct nilfs_segsum_info { sector_t next; }; -/* macro for the flags */ -#define NILFS_SEG_HAS_SR(sum) ((sum)->flags & NILFS_SS_SR) -#define NILFS_SEG_LOGBGN(sum) ((sum)->flags & NILFS_SS_LOGBGN) -#define NILFS_SEG_LOGEND(sum) ((sum)->flags & NILFS_SS_LOGEND) -#define NILFS_SEG_DSYNC(sum) ((sum)->flags & NILFS_SS_SYNDT) -#define NILFS_SEG_SIMPLEX(sum) \ - (((sum)->flags & (NILFS_SS_LOGBGN | NILFS_SS_LOGEND)) == \ - (NILFS_SS_LOGBGN | NILFS_SS_LOGEND)) - -#define NILFS_SEG_EMPTY(sum) ((sum)->nblocks == (sum)->nsumblk) - /** * struct nilfs_segment_buffer - Segment buffer * @sb_super: back pointer to a superblock struct @@ -141,6 +130,19 @@ int nilfs_segbuf_extend_payload(struct nilfs_segment_buffer *, struct buffer_head **); void nilfs_segbuf_fill_in_segsum(struct nilfs_segment_buffer *); +static inline int nilfs_segbuf_simplex(struct nilfs_segment_buffer *segbuf) +{ + unsigned int flags = segbuf->sb_sum.flags; + + return (flags & (NILFS_SS_LOGBGN | NILFS_SS_LOGEND)) == + (NILFS_SS_LOGBGN | NILFS_SS_LOGEND); +} + +static inline int nilfs_segbuf_empty(struct nilfs_segment_buffer *segbuf) +{ + return segbuf->sb_sum.nblocks == segbuf->sb_sum.nsumblk; +} + static inline void nilfs_segbuf_add_segsum_buffer(struct nilfs_segment_buffer *segbuf, struct buffer_head *bh) diff --git a/fs/nilfs2/segment.c b/fs/nilfs2/segment.c index c9201649cc49..1f7881ca01c4 100644 --- a/fs/nilfs2/segment.c +++ b/fs/nilfs2/segment.c @@ -1914,12 +1914,12 @@ static void nilfs_segctor_complete_write(struct nilfs_sc_info *sci) } } - if (!NILFS_SEG_SIMPLEX(&segbuf->sb_sum)) { - if (NILFS_SEG_LOGBGN(&segbuf->sb_sum)) { + if (!nilfs_segbuf_simplex(segbuf)) { + if (segbuf->sb_sum.flags & NILFS_SS_LOGBGN) { set_bit(NILFS_SC_UNCLOSED, &sci->sc_flags); sci->sc_lseg_stime = jiffies; } - if (NILFS_SEG_LOGEND(&segbuf->sb_sum)) + if (segbuf->sb_sum.flags & NILFS_SS_LOGEND) clear_bit(NILFS_SC_UNCLOSED, &sci->sc_flags); } } @@ -2082,7 +2082,7 @@ static int nilfs_segctor_do_construct(struct nilfs_sc_info *sci, int mode) /* Avoid empty segment */ if (sci->sc_stage.scnt == NILFS_ST_DONE && - NILFS_SEG_EMPTY(&sci->sc_curseg->sb_sum)) { + nilfs_segbuf_empty(sci->sc_curseg)) { nilfs_segctor_abort_construction(sci, nilfs, 1); goto out; } -- cgit v1.2.3 From 57a4bfc486727b68e4422031aeba427fb7262668 Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Sun, 20 Jun 2010 03:10:21 +0900 Subject: nilfs2: get rid of ns_free_segments_count This counter is unused. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/the_nilfs.c | 3 --- fs/nilfs2/the_nilfs.h | 2 -- 2 files changed, 5 deletions(-) (limited to 'fs') diff --git a/fs/nilfs2/the_nilfs.c b/fs/nilfs2/the_nilfs.c index 9f2cb01994d0..4a9e8a059638 100644 --- a/fs/nilfs2/the_nilfs.c +++ b/fs/nilfs2/the_nilfs.c @@ -630,9 +630,6 @@ int init_nilfs(struct the_nilfs *nilfs, struct nilfs_sb_info *sbi, char *data) err = -EINVAL; goto failed_sbh; } - /* Dummy values */ - nilfs->ns_free_segments_count = - nilfs->ns_nsegments - (nilfs->ns_segnum + 1); /* Initialize gcinode cache */ err = nilfs_init_gccache(nilfs); diff --git a/fs/nilfs2/the_nilfs.h b/fs/nilfs2/the_nilfs.h index 85df47f0730f..191560ec2e7f 100644 --- a/fs/nilfs2/the_nilfs.h +++ b/fs/nilfs2/the_nilfs.h @@ -73,7 +73,6 @@ enum { * @ns_last_seq: sequence value of the latest segment * @ns_last_cno: checkpoint number of the latest segment * @ns_prot_seq: least sequence number of segments which must not be reclaimed - * @ns_free_segments_count: counter of free segments * @ns_segctor_sem: segment constructor semaphore * @ns_dat: DAT file inode * @ns_cpfile: checkpoint file inode @@ -150,7 +149,6 @@ struct the_nilfs { u64 ns_last_seq; __u64 ns_last_cno; u64 ns_prot_seq; - unsigned long ns_free_segments_count; struct rw_semaphore ns_segctor_sem; -- cgit v1.2.3 From bde4e696e4a527c3cc579ed77e4844d11ca17e12 Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Sun, 27 Jun 2010 21:38:05 +0900 Subject: nilfs2: do not update mount time on rw->ro remount Mount time field in super block is wrongly updated when nilfs remounts the partition from read-write to read-only. This fixes the issue. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/super.c | 1 - 1 file changed, 1 deletion(-) (limited to 'fs') diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c index 414ef68931cf..39b28cf3cf8e 100644 --- a/fs/nilfs2/super.c +++ b/fs/nilfs2/super.c @@ -884,7 +884,6 @@ static int nilfs_remount(struct super_block *sb, int *flags, char *data) if (!(sbp->s_state & le16_to_cpu(NILFS_VALID_FS)) && (nilfs->ns_mount_state & NILFS_VALID_FS)) sbp->s_state = cpu_to_le16(nilfs->ns_mount_state); - sbp->s_mtime = cpu_to_le64(get_seconds()); nilfs_commit_super(sbi, 1); up_write(&nilfs->ns_sem); } else { -- cgit v1.2.3 From 7ecaa46cfea453238a369b3019739d50ff5d7c37 Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Mon, 28 Jun 2010 17:49:29 +0900 Subject: nilfs2: add nilfs_cleanup_super This function write out filesystem state to super blocks in order to share the same cleanup work. This is a preparation for making super block writeback alternately. Cc: Jiro SEKIBA Signed-off-by: Ryusuke Konishi --- fs/nilfs2/nilfs.h | 1 + fs/nilfs2/super.c | 28 ++++++++++++++++++++-------- fs/nilfs2/the_nilfs.c | 5 ++--- 3 files changed, 23 insertions(+), 11 deletions(-) (limited to 'fs') diff --git a/fs/nilfs2/nilfs.h b/fs/nilfs2/nilfs.h index 47d6d7928122..469541711a1f 100644 --- a/fs/nilfs2/nilfs.h +++ b/fs/nilfs2/nilfs.h @@ -271,6 +271,7 @@ nilfs_read_super_block(struct super_block *, u64, int, struct buffer_head **); extern int nilfs_store_magic_and_option(struct super_block *, struct nilfs_super_block *, char *); extern int nilfs_commit_super(struct nilfs_sb_info *, int); +extern int nilfs_cleanup_super(struct nilfs_sb_info *); extern int nilfs_attach_checkpoint(struct nilfs_sb_info *, __u64); extern void nilfs_detach_checkpoint(struct nilfs_sb_info *); diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c index 39b28cf3cf8e..f23a31b04e14 100644 --- a/fs/nilfs2/super.c +++ b/fs/nilfs2/super.c @@ -278,6 +278,24 @@ int nilfs_commit_super(struct nilfs_sb_info *sbi, int dupsb) return nilfs_sync_super(sbi, dupsb); } +/** + * nilfs_cleanup_super() - write filesystem state for cleanup + * @sbi: nilfs_sb_info to be unmounted or degraded to read-only + * + * This function restores state flags in the on-disk super block. + * This will set "clean" flag (i.e. NILFS_VALID_FS) unless the + * filesystem was not clean previously. + */ +int nilfs_cleanup_super(struct nilfs_sb_info *sbi) +{ + struct nilfs_super_block **sbp = sbi->s_nilfs->ns_sbp; + int ret; + + sbp[0]->s_state = cpu_to_le16(sbi->s_nilfs->ns_mount_state); + ret = nilfs_commit_super(sbi, 1); + return ret; +} + static void nilfs_put_super(struct super_block *sb) { struct nilfs_sb_info *sbi = NILFS_SB(sb); @@ -289,8 +307,7 @@ static void nilfs_put_super(struct super_block *sb) if (!(sb->s_flags & MS_RDONLY)) { down_write(&nilfs->ns_sem); - nilfs->ns_sbp[0]->s_state = cpu_to_le16(nilfs->ns_mount_state); - nilfs_commit_super(sbi, 1); + nilfs_cleanup_super(sbi); up_write(&nilfs->ns_sem); } down_write(&nilfs->ns_super_sem); @@ -819,7 +836,6 @@ nilfs_fill_super(struct super_block *sb, void *data, int silent, static int nilfs_remount(struct super_block *sb, int *flags, char *data) { struct nilfs_sb_info *sbi = NILFS_SB(sb); - struct nilfs_super_block *sbp; struct the_nilfs *nilfs = sbi->s_nilfs; unsigned long old_sb_flags; struct nilfs_mount_options old_opts; @@ -880,11 +896,7 @@ static int nilfs_remount(struct super_block *sb, int *flags, char *data) * the RDONLY flag and then mark the partition as valid again. */ down_write(&nilfs->ns_sem); - sbp = nilfs->ns_sbp[0]; - if (!(sbp->s_state & le16_to_cpu(NILFS_VALID_FS)) && - (nilfs->ns_mount_state & NILFS_VALID_FS)) - sbp->s_state = cpu_to_le16(nilfs->ns_mount_state); - nilfs_commit_super(sbi, 1); + nilfs_cleanup_super(sbi); up_write(&nilfs->ns_sem); } else { /* diff --git a/fs/nilfs2/the_nilfs.c b/fs/nilfs2/the_nilfs.c index 4a9e8a059638..ed58053b6f68 100644 --- a/fs/nilfs2/the_nilfs.c +++ b/fs/nilfs2/the_nilfs.c @@ -324,9 +324,8 @@ int load_nilfs(struct the_nilfs *nilfs, struct nilfs_sb_info *sbi) goto failed_unload; down_write(&nilfs->ns_sem); - nilfs->ns_mount_state |= NILFS_VALID_FS; - nilfs->ns_sbp[0]->s_state = cpu_to_le16(nilfs->ns_mount_state); - err = nilfs_commit_super(sbi, 1); + nilfs->ns_mount_state |= NILFS_VALID_FS; /* set "clean" flag */ + err = nilfs_cleanup_super(sbi); up_write(&nilfs->ns_sem); if (err) { -- cgit v1.2.3 From c8a11c8a1455c380286cfd3d3442e2b60edee49a Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Mon, 28 Jun 2010 17:49:30 +0900 Subject: nilfs2: add nilfs_set_error This function marks error state and write it on super blocks. This is a preparation for making super block writeback alternately. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/super.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) (limited to 'fs') diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c index f23a31b04e14..4a85dfb70b8e 100644 --- a/fs/nilfs2/super.c +++ b/fs/nilfs2/super.c @@ -74,6 +74,19 @@ struct kmem_cache *nilfs_btree_path_cache; static int nilfs_remount(struct super_block *sb, int *flags, char *data); +static void nilfs_set_error(struct nilfs_sb_info *sbi) +{ + struct the_nilfs *nilfs = sbi->s_nilfs; + + down_write(&nilfs->ns_sem); + if (!(nilfs->ns_mount_state & NILFS_ERROR_FS)) { + nilfs->ns_mount_state |= NILFS_ERROR_FS; + nilfs->ns_sbp[0]->s_state |= cpu_to_le16(NILFS_ERROR_FS); + nilfs_commit_super(sbi, 1); + } + up_write(&nilfs->ns_sem); +} + /** * nilfs_error() - report failure condition on a filesystem * @@ -99,16 +112,7 @@ void nilfs_error(struct super_block *sb, const char *function, va_end(args); if (!(sb->s_flags & MS_RDONLY)) { - struct the_nilfs *nilfs = sbi->s_nilfs; - - down_write(&nilfs->ns_sem); - if (!(nilfs->ns_mount_state & NILFS_ERROR_FS)) { - nilfs->ns_mount_state |= NILFS_ERROR_FS; - nilfs->ns_sbp[0]->s_state |= - cpu_to_le16(NILFS_ERROR_FS); - nilfs_commit_super(sbi, 1); - } - up_write(&nilfs->ns_sem); + nilfs_set_error(sbi); if (nilfs_test_opt(sbi, ERRORS_RO)) { printk(KERN_CRIT "Remounting filesystem read-only\n"); -- cgit v1.2.3 From 60f46b7efc1d6b980511c2644cb89903062f6e98 Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Mon, 28 Jun 2010 17:49:31 +0900 Subject: nilfs2: separate function that updates log position This moves out section that updates information of the recent log position stored in super blocks from nilfs_commit_super to a new routine named nilfs_set_log_cursor. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/nilfs.h | 2 ++ fs/nilfs2/super.c | 30 +++++++++++++++++------------- 2 files changed, 19 insertions(+), 13 deletions(-) (limited to 'fs') diff --git a/fs/nilfs2/nilfs.h b/fs/nilfs2/nilfs.h index 469541711a1f..6718616183b7 100644 --- a/fs/nilfs2/nilfs.h +++ b/fs/nilfs2/nilfs.h @@ -270,6 +270,8 @@ extern struct nilfs_super_block * nilfs_read_super_block(struct super_block *, u64, int, struct buffer_head **); extern int nilfs_store_magic_and_option(struct super_block *, struct nilfs_super_block *, char *); +extern void nilfs_set_log_cursor(struct nilfs_super_block *, + struct the_nilfs *); extern int nilfs_commit_super(struct nilfs_sb_info *, int); extern int nilfs_cleanup_super(struct nilfs_sb_info *); extern int nilfs_attach_checkpoint(struct nilfs_sb_info *, __u64); diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c index 4a85dfb70b8e..c5328c8ba1d2 100644 --- a/fs/nilfs2/super.c +++ b/fs/nilfs2/super.c @@ -237,13 +237,27 @@ static int nilfs_sync_super(struct nilfs_sb_info *sbi, int dupsb) return err; } +void nilfs_set_log_cursor(struct nilfs_super_block *sbp, + struct the_nilfs *nilfs) +{ + sector_t nfreeblocks; + + /* nilfs->ns_sem must be locked by the caller. */ + nilfs_count_free_blocks(nilfs, &nfreeblocks); + sbp->s_free_blocks_count = cpu_to_le64(nfreeblocks); + + spin_lock(&nilfs->ns_last_segment_lock); + sbp->s_last_seq = cpu_to_le64(nilfs->ns_last_seq); + sbp->s_last_pseg = cpu_to_le64(nilfs->ns_last_pseg); + sbp->s_last_cno = cpu_to_le64(nilfs->ns_last_cno); + spin_unlock(&nilfs->ns_last_segment_lock); +} + int nilfs_commit_super(struct nilfs_sb_info *sbi, int dupsb) { struct the_nilfs *nilfs = sbi->s_nilfs; struct nilfs_super_block **sbp = nilfs->ns_sbp; - sector_t nfreeblocks; time_t t; - int err; /* nilfs->sem must be locked by the caller. */ if (sbp[0]->s_magic != cpu_to_le16(NILFS_SUPER_MAGIC)) { @@ -255,20 +269,10 @@ int nilfs_commit_super(struct nilfs_sb_info *sbi, int dupsb) return -EIO; } } - err = nilfs_count_free_blocks(nilfs, &nfreeblocks); - if (unlikely(err)) { - printk(KERN_ERR "NILFS: failed to count free blocks\n"); - return err; - } - spin_lock(&nilfs->ns_last_segment_lock); - sbp[0]->s_last_seq = cpu_to_le64(nilfs->ns_last_seq); - sbp[0]->s_last_pseg = cpu_to_le64(nilfs->ns_last_pseg); - sbp[0]->s_last_cno = cpu_to_le64(nilfs->ns_last_cno); - spin_unlock(&nilfs->ns_last_segment_lock); + nilfs_set_log_cursor(sbp[0], nilfs); t = get_seconds(); nilfs->ns_sbwtime[0] = t; - sbp[0]->s_free_blocks_count = cpu_to_le64(nfreeblocks); sbp[0]->s_wtime = cpu_to_le64(t); sbp[0]->s_sum = 0; sbp[0]->s_sum = cpu_to_le32(crc32_le(nilfs->ns_crc_seed, -- cgit v1.2.3 From d26493b6f017c0b0063a15bf893411ddae85eee4 Mon Sep 17 00:00:00 2001 From: Jiro SEKIBA Date: Mon, 28 Jun 2010 17:49:32 +0900 Subject: nilfs2: introduce nilfs_prepare_super This function checks validity of super block pointers. If first super block is invalid, it will swap the super blocks. The function should be called before any super block information updates. Caller must obtain nilfs->ns_sem. Signed-off-by: Jiro SEKIBA Signed-off-by: Ryusuke Konishi --- fs/nilfs2/nilfs.h | 1 + fs/nilfs2/segment.c | 8 ++++-- fs/nilfs2/super.c | 73 +++++++++++++++++++++++++++++++++++++---------------- 3 files changed, 58 insertions(+), 24 deletions(-) (limited to 'fs') diff --git a/fs/nilfs2/nilfs.h b/fs/nilfs2/nilfs.h index 6718616183b7..462651061b03 100644 --- a/fs/nilfs2/nilfs.h +++ b/fs/nilfs2/nilfs.h @@ -272,6 +272,7 @@ extern int nilfs_store_magic_and_option(struct super_block *, struct nilfs_super_block *, char *); extern void nilfs_set_log_cursor(struct nilfs_super_block *, struct the_nilfs *); +extern struct nilfs_super_block **nilfs_prepare_super(struct nilfs_sb_info *); extern int nilfs_commit_super(struct nilfs_sb_info *, int); extern int nilfs_cleanup_super(struct nilfs_sb_info *); extern int nilfs_attach_checkpoint(struct nilfs_sb_info *, __u64); diff --git a/fs/nilfs2/segment.c b/fs/nilfs2/segment.c index 1f7881ca01c4..9e680a93b13a 100644 --- a/fs/nilfs2/segment.c +++ b/fs/nilfs2/segment.c @@ -2408,6 +2408,7 @@ static int nilfs_segctor_construct(struct nilfs_sc_info *sci, int mode) { struct nilfs_sb_info *sbi = sci->sc_sbi; struct the_nilfs *nilfs = sbi->s_nilfs; + struct nilfs_super_block **sbp; int err = 0; nilfs_segctor_accept(sci); @@ -2423,8 +2424,11 @@ static int nilfs_segctor_construct(struct nilfs_sc_info *sci, int mode) if (test_bit(NILFS_SC_SUPER_ROOT, &sci->sc_flags) && nilfs_discontinued(nilfs)) { down_write(&nilfs->ns_sem); - err = nilfs_commit_super( - sbi, nilfs_altsb_need_update(nilfs)); + err = -EIO; + sbp = nilfs_prepare_super(sbi); + if (likely(sbp)) + err = nilfs_commit_super( + sbi, nilfs_altsb_need_update(nilfs)); up_write(&nilfs->ns_sem); } } diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c index c5328c8ba1d2..eb7de40828c7 100644 --- a/fs/nilfs2/super.c +++ b/fs/nilfs2/super.c @@ -77,12 +77,16 @@ static int nilfs_remount(struct super_block *sb, int *flags, char *data); static void nilfs_set_error(struct nilfs_sb_info *sbi) { struct the_nilfs *nilfs = sbi->s_nilfs; + struct nilfs_super_block **sbp; down_write(&nilfs->ns_sem); if (!(nilfs->ns_mount_state & NILFS_ERROR_FS)) { nilfs->ns_mount_state |= NILFS_ERROR_FS; - nilfs->ns_sbp[0]->s_state |= cpu_to_le16(NILFS_ERROR_FS); - nilfs_commit_super(sbi, 1); + sbp = nilfs_prepare_super(sbi); + if (likely(sbp)) { + sbp[0]->s_state |= cpu_to_le16(NILFS_ERROR_FS); + nilfs_commit_super(sbi, 1); + } } up_write(&nilfs->ns_sem); } @@ -253,22 +257,32 @@ void nilfs_set_log_cursor(struct nilfs_super_block *sbp, spin_unlock(&nilfs->ns_last_segment_lock); } -int nilfs_commit_super(struct nilfs_sb_info *sbi, int dupsb) +struct nilfs_super_block **nilfs_prepare_super(struct nilfs_sb_info *sbi) { struct the_nilfs *nilfs = sbi->s_nilfs; struct nilfs_super_block **sbp = nilfs->ns_sbp; - time_t t; - /* nilfs->sem must be locked by the caller. */ + /* nilfs->ns_sem must be locked by the caller. */ if (sbp[0]->s_magic != cpu_to_le16(NILFS_SUPER_MAGIC)) { - if (sbp[1] && sbp[1]->s_magic == cpu_to_le16(NILFS_SUPER_MAGIC)) + if (sbp[1] && + sbp[1]->s_magic == cpu_to_le16(NILFS_SUPER_MAGIC)) { nilfs_swap_super_block(nilfs); - else { + } else { printk(KERN_CRIT "NILFS: superblock broke on dev %s\n", sbi->s_super->s_id); - return -EIO; + return NULL; } } + return sbp; +} + +int nilfs_commit_super(struct nilfs_sb_info *sbi, int dupsb) +{ + struct the_nilfs *nilfs = sbi->s_nilfs; + struct nilfs_super_block **sbp = nilfs->ns_sbp; + time_t t; + + /* nilfs->ns_sem must be locked by the caller. */ nilfs_set_log_cursor(sbp[0], nilfs); t = get_seconds(); @@ -296,11 +310,14 @@ int nilfs_commit_super(struct nilfs_sb_info *sbi, int dupsb) */ int nilfs_cleanup_super(struct nilfs_sb_info *sbi) { - struct nilfs_super_block **sbp = sbi->s_nilfs->ns_sbp; - int ret; + struct nilfs_super_block **sbp; + int ret = -EIO; - sbp[0]->s_state = cpu_to_le16(sbi->s_nilfs->ns_mount_state); - ret = nilfs_commit_super(sbi, 1); + sbp = nilfs_prepare_super(sbi); + if (sbp) { + sbp[0]->s_state = cpu_to_le16(sbi->s_nilfs->ns_mount_state); + ret = nilfs_commit_super(sbi, 1); + } return ret; } @@ -336,6 +353,7 @@ static int nilfs_sync_fs(struct super_block *sb, int wait) { struct nilfs_sb_info *sbi = NILFS_SB(sb); struct the_nilfs *nilfs = sbi->s_nilfs; + struct nilfs_super_block **sbp; int err = 0; /* This function is called when super block should be written back */ @@ -343,8 +361,11 @@ static int nilfs_sync_fs(struct super_block *sb, int wait) err = nilfs_construct_segment(sb); down_write(&nilfs->ns_sem); - if (nilfs_sb_dirty(nilfs)) - nilfs_commit_super(sbi, 1); + if (nilfs_sb_dirty(nilfs)) { + sbp = nilfs_prepare_super(sbi); + if (likely(sbp)) + nilfs_commit_super(sbi, 1); + } up_write(&nilfs->ns_sem); return err; @@ -638,11 +659,18 @@ nilfs_set_default_options(struct nilfs_sb_info *sbi, static int nilfs_setup_super(struct nilfs_sb_info *sbi) { struct the_nilfs *nilfs = sbi->s_nilfs; - struct nilfs_super_block *sbp = nilfs->ns_sbp[0]; - int max_mnt_count = le16_to_cpu(sbp->s_max_mnt_count); - int mnt_count = le16_to_cpu(sbp->s_mnt_count); + struct nilfs_super_block **sbp; + int max_mnt_count; + int mnt_count; + + /* nilfs->ns_sem must be locked by the caller. */ + sbp = nilfs_prepare_super(sbi); + if (!sbp) + return -EIO; + + max_mnt_count = le16_to_cpu(sbp[0]->s_max_mnt_count); + mnt_count = le16_to_cpu(sbp[0]->s_mnt_count); - /* nilfs->sem must be locked by the caller. */ if (nilfs->ns_mount_state & NILFS_ERROR_FS) { printk(KERN_WARNING "NILFS warning: mounting fs with errors\n"); @@ -653,11 +681,12 @@ static int nilfs_setup_super(struct nilfs_sb_info *sbi) #endif } if (!max_mnt_count) - sbp->s_max_mnt_count = cpu_to_le16(NILFS_DFL_MAX_MNT_COUNT); + sbp[0]->s_max_mnt_count = cpu_to_le16(NILFS_DFL_MAX_MNT_COUNT); - sbp->s_mnt_count = cpu_to_le16(mnt_count + 1); - sbp->s_state = cpu_to_le16(le16_to_cpu(sbp->s_state) & ~NILFS_VALID_FS); - sbp->s_mtime = cpu_to_le64(get_seconds()); + sbp[0]->s_mnt_count = cpu_to_le16(mnt_count + 1); + sbp[0]->s_state = + cpu_to_le16(le16_to_cpu(sbp[0]->s_state) & ~NILFS_VALID_FS); + sbp[0]->s_mtime = cpu_to_le64(get_seconds()); return nilfs_commit_super(sbi, 1); } -- cgit v1.2.3 From b2ac86e1a8e3a3b0ab4449d062c582f07a078e7b Mon Sep 17 00:00:00 2001 From: Jiro SEKIBA Date: Mon, 28 Jun 2010 17:49:33 +0900 Subject: nilfs2: sync super blocks in turns This will sync super blocks in turns instead of syncing duplicate super blocks at the time. This will help searching valid super root when super block is written into disk before log is written, which is happen when barrier-less block devices are unmounted uncleanly. In the situation, old super block likely points to valid log. This patch introduces ns_sbwcount member to the nilfs object and adds nilfs_sb_will_flip() function; ns_sbwcount counts how many times super blocks write back to the disk. And, nilfs_sb_will_flip() decides whether flipping required or not based on the count of ns_sbwcount to sync super blocks asymmetrically. The following functions are also changed: - nilfs_prepare_super(): flips super blocks according to the argument. The argument is calculated by nilfs_sb_will_flip() function. - nilfs_cleanup_super(): sets "clean" flag to both super blocks if they point to the same checkpoint. To update both of super block information, caller of nilfs_commit_super must set the information on both super blocks. Signed-off-by: Jiro SEKIBA Signed-off-by: Ryusuke Konishi --- fs/nilfs2/nilfs.h | 11 +++++- fs/nilfs2/segment.c | 10 +++--- fs/nilfs2/super.c | 95 +++++++++++++++++++++++++++++++++++---------------- fs/nilfs2/the_nilfs.c | 4 +-- fs/nilfs2/the_nilfs.h | 17 +++++---- 5 files changed, 91 insertions(+), 46 deletions(-) (limited to 'fs') diff --git a/fs/nilfs2/nilfs.h b/fs/nilfs2/nilfs.h index 462651061b03..36998eaab02f 100644 --- a/fs/nilfs2/nilfs.h +++ b/fs/nilfs2/nilfs.h @@ -106,6 +106,14 @@ enum { NILFS_I_GCDAT, /* shadow DAT, on memory only */ }; +/* + * commit flags for nilfs_commit_super and nilfs_sync_super + */ +enum { + NILFS_SB_COMMIT = 0, /* Commit a super block alternately */ + NILFS_SB_COMMIT_ALL /* Commit both super blocks */ +}; + /* * Macros to check inode numbers */ @@ -272,7 +280,8 @@ extern int nilfs_store_magic_and_option(struct super_block *, struct nilfs_super_block *, char *); extern void nilfs_set_log_cursor(struct nilfs_super_block *, struct the_nilfs *); -extern struct nilfs_super_block **nilfs_prepare_super(struct nilfs_sb_info *); +extern struct nilfs_super_block **nilfs_prepare_super(struct nilfs_sb_info *, + int flip); extern int nilfs_commit_super(struct nilfs_sb_info *, int); extern int nilfs_cleanup_super(struct nilfs_sb_info *); extern int nilfs_attach_checkpoint(struct nilfs_sb_info *, __u64); diff --git a/fs/nilfs2/segment.c b/fs/nilfs2/segment.c index 9e680a93b13a..04e04854a311 100644 --- a/fs/nilfs2/segment.c +++ b/fs/nilfs2/segment.c @@ -2425,10 +2425,12 @@ static int nilfs_segctor_construct(struct nilfs_sc_info *sci, int mode) nilfs_discontinued(nilfs)) { down_write(&nilfs->ns_sem); err = -EIO; - sbp = nilfs_prepare_super(sbi); - if (likely(sbp)) - err = nilfs_commit_super( - sbi, nilfs_altsb_need_update(nilfs)); + sbp = nilfs_prepare_super(sbi, + nilfs_sb_will_flip(nilfs)); + if (likely(sbp)) { + nilfs_set_log_cursor(sbp[0], nilfs); + err = nilfs_commit_super(sbi, NILFS_SB_COMMIT); + } up_write(&nilfs->ns_sem); } } diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c index eb7de40828c7..f2cfbbab2346 100644 --- a/fs/nilfs2/super.c +++ b/fs/nilfs2/super.c @@ -82,10 +82,12 @@ static void nilfs_set_error(struct nilfs_sb_info *sbi) down_write(&nilfs->ns_sem); if (!(nilfs->ns_mount_state & NILFS_ERROR_FS)) { nilfs->ns_mount_state |= NILFS_ERROR_FS; - sbp = nilfs_prepare_super(sbi); + sbp = nilfs_prepare_super(sbi, 0); if (likely(sbp)) { sbp[0]->s_state |= cpu_to_le16(NILFS_ERROR_FS); - nilfs_commit_super(sbi, 1); + if (sbp[1]) + sbp[1]->s_state |= cpu_to_le16(NILFS_ERROR_FS); + nilfs_commit_super(sbi, NILFS_SB_COMMIT_ALL); } } up_write(&nilfs->ns_sem); @@ -184,7 +186,7 @@ static void nilfs_clear_inode(struct inode *inode) nilfs_btnode_cache_clear(&ii->i_btnode_cache); } -static int nilfs_sync_super(struct nilfs_sb_info *sbi, int dupsb) +static int nilfs_sync_super(struct nilfs_sb_info *sbi, int flag) { struct the_nilfs *nilfs = sbi->s_nilfs; int err; @@ -210,12 +212,20 @@ static int nilfs_sync_super(struct nilfs_sb_info *sbi, int dupsb) printk(KERN_ERR "NILFS: unable to write superblock (err=%d)\n", err); if (err == -EIO && nilfs->ns_sbh[1]) { + /* + * sbp[0] points to newer log than sbp[1], + * so copy sbp[0] to sbp[1] to take over sbp[0]. + */ + memcpy(nilfs->ns_sbp[1], nilfs->ns_sbp[0], + nilfs->ns_sbsize); nilfs_fall_back_super_block(nilfs); goto retry; } } else { struct nilfs_super_block *sbp = nilfs->ns_sbp[0]; + nilfs->ns_sbwcount++; + /* * The latest segment becomes trailable from the position * written in superblock. @@ -224,20 +234,21 @@ static int nilfs_sync_super(struct nilfs_sb_info *sbi, int dupsb) /* update GC protection for recent segments */ if (nilfs->ns_sbh[1]) { - sbp = NULL; - if (dupsb) { + if (flag == NILFS_SB_COMMIT_ALL) { set_buffer_dirty(nilfs->ns_sbh[1]); - if (!sync_dirty_buffer(nilfs->ns_sbh[1])) - sbp = nilfs->ns_sbp[1]; + if (sync_dirty_buffer(nilfs->ns_sbh[1]) < 0) + goto out; } + if (le64_to_cpu(nilfs->ns_sbp[1]->s_last_cno) < + le64_to_cpu(nilfs->ns_sbp[0]->s_last_cno)) + sbp = nilfs->ns_sbp[1]; } - if (sbp) { - spin_lock(&nilfs->ns_last_segment_lock); - nilfs->ns_prot_seq = le64_to_cpu(sbp->s_last_seq); - spin_unlock(&nilfs->ns_last_segment_lock); - } - } + spin_lock(&nilfs->ns_last_segment_lock); + nilfs->ns_prot_seq = le64_to_cpu(sbp->s_last_seq); + spin_unlock(&nilfs->ns_last_segment_lock); + } + out: return err; } @@ -257,7 +268,8 @@ void nilfs_set_log_cursor(struct nilfs_super_block *sbp, spin_unlock(&nilfs->ns_last_segment_lock); } -struct nilfs_super_block **nilfs_prepare_super(struct nilfs_sb_info *sbi) +struct nilfs_super_block **nilfs_prepare_super(struct nilfs_sb_info *sbi, + int flip) { struct the_nilfs *nilfs = sbi->s_nilfs; struct nilfs_super_block **sbp = nilfs->ns_sbp; @@ -266,38 +278,46 @@ struct nilfs_super_block **nilfs_prepare_super(struct nilfs_sb_info *sbi) if (sbp[0]->s_magic != cpu_to_le16(NILFS_SUPER_MAGIC)) { if (sbp[1] && sbp[1]->s_magic == cpu_to_le16(NILFS_SUPER_MAGIC)) { - nilfs_swap_super_block(nilfs); + memcpy(sbp[0], sbp[1], nilfs->ns_sbsize); } else { printk(KERN_CRIT "NILFS: superblock broke on dev %s\n", sbi->s_super->s_id); return NULL; } + } else if (sbp[1] && + sbp[1]->s_magic != cpu_to_le16(NILFS_SUPER_MAGIC)) { + memcpy(sbp[1], sbp[0], nilfs->ns_sbsize); } + + if (flip && sbp[1]) + nilfs_swap_super_block(nilfs); + return sbp; } -int nilfs_commit_super(struct nilfs_sb_info *sbi, int dupsb) +int nilfs_commit_super(struct nilfs_sb_info *sbi, int flag) { struct the_nilfs *nilfs = sbi->s_nilfs; struct nilfs_super_block **sbp = nilfs->ns_sbp; time_t t; /* nilfs->ns_sem must be locked by the caller. */ - nilfs_set_log_cursor(sbp[0], nilfs); - t = get_seconds(); - nilfs->ns_sbwtime[0] = t; + nilfs->ns_sbwtime = t; sbp[0]->s_wtime = cpu_to_le64(t); sbp[0]->s_sum = 0; sbp[0]->s_sum = cpu_to_le32(crc32_le(nilfs->ns_crc_seed, (unsigned char *)sbp[0], nilfs->ns_sbsize)); - if (dupsb && sbp[1]) { - memcpy(sbp[1], sbp[0], nilfs->ns_sbsize); - nilfs->ns_sbwtime[1] = t; + if (flag == NILFS_SB_COMMIT_ALL && sbp[1]) { + sbp[1]->s_wtime = sbp[0]->s_wtime; + sbp[1]->s_sum = 0; + sbp[1]->s_sum = cpu_to_le32(crc32_le(nilfs->ns_crc_seed, + (unsigned char *)sbp[1], + nilfs->ns_sbsize)); } clear_nilfs_sb_dirty(nilfs); - return nilfs_sync_super(sbi, dupsb); + return nilfs_sync_super(sbi, flag); } /** @@ -311,12 +331,23 @@ int nilfs_commit_super(struct nilfs_sb_info *sbi, int dupsb) int nilfs_cleanup_super(struct nilfs_sb_info *sbi) { struct nilfs_super_block **sbp; + int flag = NILFS_SB_COMMIT; int ret = -EIO; - sbp = nilfs_prepare_super(sbi); + sbp = nilfs_prepare_super(sbi, 0); if (sbp) { sbp[0]->s_state = cpu_to_le16(sbi->s_nilfs->ns_mount_state); - ret = nilfs_commit_super(sbi, 1); + nilfs_set_log_cursor(sbp[0], sbi->s_nilfs); + if (sbp[1] && sbp[0]->s_last_cno == sbp[1]->s_last_cno) { + /* + * make the "clean" flag also to the opposite + * super block if both super blocks point to + * the same checkpoint. + */ + sbp[1]->s_state = sbp[0]->s_state; + flag = NILFS_SB_COMMIT_ALL; + } + ret = nilfs_commit_super(sbi, flag); } return ret; } @@ -362,9 +393,11 @@ static int nilfs_sync_fs(struct super_block *sb, int wait) down_write(&nilfs->ns_sem); if (nilfs_sb_dirty(nilfs)) { - sbp = nilfs_prepare_super(sbi); - if (likely(sbp)) - nilfs_commit_super(sbi, 1); + sbp = nilfs_prepare_super(sbi, nilfs_sb_will_flip(nilfs)); + if (likely(sbp)) { + nilfs_set_log_cursor(sbp[0], nilfs); + nilfs_commit_super(sbi, NILFS_SB_COMMIT); + } } up_write(&nilfs->ns_sem); @@ -664,7 +697,7 @@ static int nilfs_setup_super(struct nilfs_sb_info *sbi) int mnt_count; /* nilfs->ns_sem must be locked by the caller. */ - sbp = nilfs_prepare_super(sbi); + sbp = nilfs_prepare_super(sbi, 0); if (!sbp) return -EIO; @@ -687,7 +720,9 @@ static int nilfs_setup_super(struct nilfs_sb_info *sbi) sbp[0]->s_state = cpu_to_le16(le16_to_cpu(sbp[0]->s_state) & ~NILFS_VALID_FS); sbp[0]->s_mtime = cpu_to_le64(get_seconds()); - return nilfs_commit_super(sbi, 1); + /* synchronize sbp[1] with sbp[0] */ + memcpy(sbp[1], sbp[0], nilfs->ns_sbsize); + return nilfs_commit_super(sbi, NILFS_SB_COMMIT_ALL); } struct nilfs_super_block *nilfs_read_super_block(struct super_block *sb, diff --git a/fs/nilfs2/the_nilfs.c b/fs/nilfs2/the_nilfs.c index ed58053b6f68..530d2777b4c7 100644 --- a/fs/nilfs2/the_nilfs.c +++ b/fs/nilfs2/the_nilfs.c @@ -513,8 +513,8 @@ static int nilfs_load_super_block(struct the_nilfs *nilfs, nilfs_swap_super_block(nilfs); } - nilfs->ns_sbwtime[0] = le64_to_cpu(sbp[0]->s_wtime); - nilfs->ns_sbwtime[1] = valid[!swp] ? le64_to_cpu(sbp[1]->s_wtime) : 0; + nilfs->ns_sbwcount = 0; + nilfs->ns_sbwtime = le64_to_cpu(sbp[0]->s_wtime); nilfs->ns_prot_seq = le64_to_cpu(sbp[valid[1] & !swp]->s_last_seq); *sbpp = sbp[0]; return 0; diff --git a/fs/nilfs2/the_nilfs.h b/fs/nilfs2/the_nilfs.h index 191560ec2e7f..32b4983b7458 100644 --- a/fs/nilfs2/the_nilfs.h +++ b/fs/nilfs2/the_nilfs.h @@ -57,7 +57,8 @@ enum { * @ns_current: back pointer to current mount * @ns_sbh: buffer heads of on-disk super blocks * @ns_sbp: pointers to super block data - * @ns_sbwtime: previous write time of super blocks + * @ns_sbwtime: previous write time of super block + * @ns_sbwcount: write count of super block * @ns_sbsize: size of valid data in super block * @ns_supers: list of nilfs super block structs * @ns_seg_seq: segment sequence counter @@ -119,7 +120,8 @@ struct the_nilfs { */ struct buffer_head *ns_sbh[2]; struct nilfs_super_block *ns_sbp[2]; - time_t ns_sbwtime[2]; + time_t ns_sbwtime; + unsigned ns_sbwcount; unsigned ns_sbsize; unsigned ns_mount_state; @@ -203,20 +205,17 @@ THE_NILFS_FNS(SB_DIRTY, sb_dirty) /* Minimum interval of periodical update of superblocks (in seconds) */ #define NILFS_SB_FREQ 10 -#define NILFS_ALTSB_FREQ 60 /* spare superblock */ static inline int nilfs_sb_need_update(struct the_nilfs *nilfs) { u64 t = get_seconds(); - return t < nilfs->ns_sbwtime[0] || - t > nilfs->ns_sbwtime[0] + NILFS_SB_FREQ; + return t < nilfs->ns_sbwtime || t > nilfs->ns_sbwtime + NILFS_SB_FREQ; } -static inline int nilfs_altsb_need_update(struct the_nilfs *nilfs) +static inline int nilfs_sb_will_flip(struct the_nilfs *nilfs) { - u64 t = get_seconds(); - struct nilfs_super_block **sbp = nilfs->ns_sbp; - return sbp[1] && t > nilfs->ns_sbwtime[1] + NILFS_ALTSB_FREQ; + int flip_bits = nilfs->ns_sbwcount & 0x0FL; + return (flip_bits != 0x08 && flip_bits != 0x0F); } void nilfs_set_last_segment(struct the_nilfs *, sector_t, u64, __u64); -- cgit v1.2.3 From 843d63baa5babf3d8786f6a4377a2448525da7aa Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Mon, 28 Jun 2010 19:15:24 +0900 Subject: nilfs2: separate setup of log cursor from init_nilfs This separates a setup routine of log cursor from init_nilfs(). The routine, nilfs_store_log_cursor, reads the last position of the log containing a super root, and initializes relevant state on the nilfs object. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/the_nilfs.c | 45 ++++++++++++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 13 deletions(-) (limited to 'fs') diff --git a/fs/nilfs2/the_nilfs.c b/fs/nilfs2/the_nilfs.c index 530d2777b4c7..0d2a46cb75f8 100644 --- a/fs/nilfs2/the_nilfs.c +++ b/fs/nilfs2/the_nilfs.c @@ -246,6 +246,36 @@ static void nilfs_clear_recovery_info(struct nilfs_recovery_info *ri) nilfs_dispose_segment_list(&ri->ri_used_segments); } +/** + * nilfs_store_log_cursor - load log cursor from a super block + * @nilfs: nilfs object + * @sbp: buffer storing super block to be read + * + * nilfs_store_log_cursor() reads the last position of the log + * containing a super root from a given super block, and initializes + * relevant information on the nilfs object preparatory for log + * scanning and recovery. + */ +static int nilfs_store_log_cursor(struct the_nilfs *nilfs, + struct nilfs_super_block *sbp) +{ + int ret = 0; + + nilfs->ns_last_pseg = le64_to_cpu(sbp->s_last_pseg); + nilfs->ns_last_cno = le64_to_cpu(sbp->s_last_cno); + nilfs->ns_last_seq = le64_to_cpu(sbp->s_last_seq); + + nilfs->ns_seg_seq = nilfs->ns_last_seq; + nilfs->ns_segnum = + nilfs_get_segnum_of_block(nilfs, nilfs->ns_last_pseg); + nilfs->ns_cno = nilfs->ns_last_cno + 1; + if (nilfs->ns_segnum >= nilfs->ns_nsegments) { + printk(KERN_ERR "NILFS invalid last segment number.\n"); + ret = -EINVAL; + } + return ret; +} + /** * load_nilfs - load and recover the nilfs * @nilfs: the_nilfs structure to be released @@ -615,20 +645,9 @@ int init_nilfs(struct the_nilfs *nilfs, struct nilfs_sb_info *sbi, char *data) bdi = nilfs->ns_bdev->bd_inode->i_mapping->backing_dev_info; nilfs->ns_bdi = bdi ? : &default_backing_dev_info; - /* Finding last segment */ - nilfs->ns_last_pseg = le64_to_cpu(sbp->s_last_pseg); - nilfs->ns_last_cno = le64_to_cpu(sbp->s_last_cno); - nilfs->ns_last_seq = le64_to_cpu(sbp->s_last_seq); - - nilfs->ns_seg_seq = nilfs->ns_last_seq; - nilfs->ns_segnum = - nilfs_get_segnum_of_block(nilfs, nilfs->ns_last_pseg); - nilfs->ns_cno = nilfs->ns_last_cno + 1; - if (nilfs->ns_segnum >= nilfs->ns_nsegments) { - printk(KERN_ERR "NILFS invalid last segment number.\n"); - err = -EINVAL; + err = nilfs_store_log_cursor(nilfs, sbp); + if (err) goto failed_sbh; - } /* Initialize gcinode cache */ err = nilfs_init_gccache(nilfs); -- cgit v1.2.3 From 2d72b99ecdf8cbb5d9422c54b401d9d590b2faf5 Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Mon, 28 Jun 2010 19:15:25 +0900 Subject: nilfs2: add missing error code in comment of nilfs_search_super_root nilfs_search_super_root can return -ENOMEM, but this error code is not described in its kernel-doc comment. This fixes the discrepancy. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/recovery.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'fs') diff --git a/fs/nilfs2/recovery.c b/fs/nilfs2/recovery.c index f5d9c3f954ae..83e3d8c61a01 100644 --- a/fs/nilfs2/recovery.c +++ b/fs/nilfs2/recovery.c @@ -804,6 +804,8 @@ int nilfs_salvage_orphan_logs(struct the_nilfs *nilfs, * %-EINVAL - No valid segment found * * %-EIO - I/O error + * + * %-ENOMEM - Insufficient memory available. */ int nilfs_search_super_root(struct the_nilfs *nilfs, struct nilfs_recovery_info *ri) -- cgit v1.2.3 From 6c12516083cf51b6e576691ac6e20c4a32f4edb9 Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Mon, 28 Jun 2010 19:15:26 +0900 Subject: nilfs2: implement fallback for super root search Although nilfs redundantly uses two super blocks and each may point to different position on log, the current version of nilfs does not try fallback to the spare super block when it doesn't find any valid log at the position that the primary super block points to. This has been a cause of mount failures due to write order reversals on barrier less block devices. This inserts fallback code in error path of nilfs_search_super_root routine to resolve the mount failure problem. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/the_nilfs.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/nilfs2/the_nilfs.c b/fs/nilfs2/the_nilfs.c index 0d2a46cb75f8..88c8976c55a9 100644 --- a/fs/nilfs2/the_nilfs.c +++ b/fs/nilfs2/the_nilfs.c @@ -38,6 +38,8 @@ static LIST_HEAD(nilfs_objects); static DEFINE_SPINLOCK(nilfs_lock); +static int nilfs_valid_sb(struct nilfs_super_block *sbp); + void nilfs_set_last_segment(struct the_nilfs *nilfs, sector_t start_blocknr, u64 seq, __u64 cno) { @@ -316,8 +318,50 @@ int load_nilfs(struct the_nilfs *nilfs, struct nilfs_sb_info *sbi) err = nilfs_search_super_root(nilfs, &ri); if (unlikely(err)) { - printk(KERN_ERR "NILFS: error searching super root.\n"); - goto failed; + struct nilfs_super_block **sbp = nilfs->ns_sbp; + int blocksize; + + if (err != -EINVAL) + goto scan_error; + + if (!nilfs_valid_sb(sbp[1])) { + printk(KERN_WARNING + "NILFS warning: unable to fall back to spare" + "super block\n"); + goto scan_error; + } + printk(KERN_INFO + "NILFS: try rollback from an earlier position\n"); + + /* + * restore super block with its spare and reconfigure + * relevant states of the nilfs object. + */ + memcpy(sbp[0], sbp[1], nilfs->ns_sbsize); + nilfs->ns_crc_seed = le32_to_cpu(sbp[0]->s_crc_seed); + nilfs->ns_sbwtime = le64_to_cpu(sbp[0]->s_wtime); + + /* verify consistency between two super blocks */ + blocksize = BLOCK_SIZE << le32_to_cpu(sbp[0]->s_log_block_size); + if (blocksize != nilfs->ns_blocksize) { + printk(KERN_WARNING + "NILFS warning: blocksize differs between " + "two super blocks (%d != %d)\n", + blocksize, nilfs->ns_blocksize); + goto scan_error; + } + + err = nilfs_store_log_cursor(nilfs, sbp[0]); + if (err) + goto scan_error; + + /* drop clean flag to allow roll-forward and recovery */ + nilfs->ns_mount_state &= ~NILFS_VALID_FS; + valid_fs = 0; + + err = nilfs_search_super_root(nilfs, &ri); + if (err) + goto scan_error; } err = nilfs_load_super_root(nilfs, ri.ri_super_root); @@ -371,6 +415,10 @@ int load_nilfs(struct the_nilfs *nilfs, struct nilfs_sb_info *sbi) sbi->s_super->s_flags = s_flags; return 0; + scan_error: + printk(KERN_ERR "NILFS: error searching super root.\n"); + goto failed; + failed_unload: nilfs_mdt_destroy(nilfs->ns_cpfile); nilfs_mdt_destroy(nilfs->ns_sufile); -- cgit v1.2.3 From 325020477a51ffa849418b3e38189fd266f2ae20 Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Tue, 29 Jun 2010 14:42:13 +0900 Subject: nilfs2: do not update log cursor for small change Super blocks of nilfs are periodically overwritten in order to record the recent log position. This shortens recovery time after unclean unmount, but the current implementation performs the update even for a few blocks of change. If the filesystem gets small changes slowly and continually, super blocks may be updated excessively. This moderates the issue by skipping update of log cursor if it does not cross a segment boundary. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/segment.c | 1 - fs/nilfs2/the_nilfs.c | 11 +++++++++++ fs/nilfs2/the_nilfs.h | 2 ++ 3 files changed, 13 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/nilfs2/segment.c b/fs/nilfs2/segment.c index 04e04854a311..9fd051a33c4f 100644 --- a/fs/nilfs2/segment.c +++ b/fs/nilfs2/segment.c @@ -1951,7 +1951,6 @@ static void nilfs_segctor_complete_write(struct nilfs_sc_info *sci) if (update_sr) { nilfs_set_last_segment(nilfs, segbuf->sb_pseg_start, segbuf->sb_sum.seg_seq, nilfs->ns_cno++); - set_nilfs_sb_dirty(nilfs); clear_bit(NILFS_SC_HAVE_DELTA, &sci->sc_flags); clear_bit(NILFS_SC_DIRTY, &sci->sc_flags); diff --git a/fs/nilfs2/the_nilfs.c b/fs/nilfs2/the_nilfs.c index 88c8976c55a9..f2efc8c5be7f 100644 --- a/fs/nilfs2/the_nilfs.c +++ b/fs/nilfs2/the_nilfs.c @@ -47,6 +47,16 @@ void nilfs_set_last_segment(struct the_nilfs *nilfs, nilfs->ns_last_pseg = start_blocknr; nilfs->ns_last_seq = seq; nilfs->ns_last_cno = cno; + + if (!nilfs_sb_dirty(nilfs)) { + if (nilfs->ns_prev_seq == nilfs->ns_last_seq) + goto stay_cursor; + + set_nilfs_sb_dirty(nilfs); + } + nilfs->ns_prev_seq = nilfs->ns_last_seq; + + stay_cursor: spin_unlock(&nilfs->ns_last_segment_lock); } @@ -267,6 +277,7 @@ static int nilfs_store_log_cursor(struct the_nilfs *nilfs, nilfs->ns_last_cno = le64_to_cpu(sbp->s_last_cno); nilfs->ns_last_seq = le64_to_cpu(sbp->s_last_seq); + nilfs->ns_prev_seq = nilfs->ns_last_seq; nilfs->ns_seg_seq = nilfs->ns_last_seq; nilfs->ns_segnum = nilfs_get_segnum_of_block(nilfs, nilfs->ns_last_pseg); diff --git a/fs/nilfs2/the_nilfs.h b/fs/nilfs2/the_nilfs.h index 32b4983b7458..f785a7b0ab99 100644 --- a/fs/nilfs2/the_nilfs.h +++ b/fs/nilfs2/the_nilfs.h @@ -74,6 +74,7 @@ enum { * @ns_last_seq: sequence value of the latest segment * @ns_last_cno: checkpoint number of the latest segment * @ns_prot_seq: least sequence number of segments which must not be reclaimed + * @ns_prev_seq: base sequence number used to decide if advance log cursor * @ns_segctor_sem: segment constructor semaphore * @ns_dat: DAT file inode * @ns_cpfile: checkpoint file inode @@ -151,6 +152,7 @@ struct the_nilfs { u64 ns_last_seq; __u64 ns_last_cno; u64 ns_prot_seq; + u64 ns_prev_seq; struct rw_semaphore ns_segctor_sem; -- cgit v1.2.3 From 773bc4f3b6898634a80a41c72a1f34cb89992dcd Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Mon, 5 Jul 2010 13:00:08 +0900 Subject: nilfs2: add barrier mount option Nilfs enables write barriers by default and has "nobarrier" mount option to disable this feature. But it lacks the complementary option and has no way to re-enable the feature on remount. This adds "barrier" option to resolve this imbalance. Signed-off-by: Ryusuke Konishi --- Documentation/filesystems/nilfs2.txt | 5 ++++- fs/nilfs2/super.c | 6 +++++- 2 files changed, 9 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/Documentation/filesystems/nilfs2.txt b/Documentation/filesystems/nilfs2.txt index d3e7673995eb..54f61c0ff442 100644 --- a/Documentation/filesystems/nilfs2.txt +++ b/Documentation/filesystems/nilfs2.txt @@ -49,7 +49,10 @@ Mount options NILFS2 supports the following mount options: (*) == default -nobarrier Disables barriers. +barrier(*) This enables/disables the use of write barriers. This +nobarrier requires an IO stack which can support barriers, and + if nilfs gets an error on a barrier write, it will + disable again with a warning. errors=continue Keep going on a filesystem error. errors=remount-ro(*) Remount the filesystem read-only on an error. errors=panic Panic and halt the machine if an error occurs. diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c index f2cfbbab2346..13b0e955c028 100644 --- a/fs/nilfs2/super.c +++ b/fs/nilfs2/super.c @@ -603,7 +603,7 @@ static const struct export_operations nilfs_export_ops = { enum { Opt_err_cont, Opt_err_panic, Opt_err_ro, - Opt_nobarrier, Opt_snapshot, Opt_order, Opt_norecovery, + Opt_barrier, Opt_nobarrier, Opt_snapshot, Opt_order, Opt_norecovery, Opt_discard, Opt_err, }; @@ -611,6 +611,7 @@ static match_table_t tokens = { {Opt_err_cont, "errors=continue"}, {Opt_err_panic, "errors=panic"}, {Opt_err_ro, "errors=remount-ro"}, + {Opt_barrier, "barrier"}, {Opt_nobarrier, "nobarrier"}, {Opt_snapshot, "cp=%u"}, {Opt_order, "order=%s"}, @@ -636,6 +637,9 @@ static int parse_options(char *options, struct super_block *sb) token = match_token(p, tokens, args); switch (token) { + case Opt_barrier: + nilfs_set_opt(sbi, BARRIER); + break; case Opt_nobarrier: nilfs_clear_opt(sbi, BARRIER); break; -- cgit v1.2.3 From 802d31775404ee335ca1e97a82e1e706a4c843be Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Mon, 5 Jul 2010 14:27:04 +0900 Subject: nilfs2: add nodiscard mount option Nilfs has "discard" mount option which issues discard/TRIM commands to underlying block device, but it lacks a complementary option and has no way to disable the feature through remount. This adds "nodiscard" option to resolve this imbalance. Signed-off-by: Ryusuke Konishi --- Documentation/filesystems/nilfs2.txt | 7 ++++--- fs/nilfs2/super.c | 6 +++++- 2 files changed, 9 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/Documentation/filesystems/nilfs2.txt b/Documentation/filesystems/nilfs2.txt index 54f61c0ff442..d5c0cef38a71 100644 --- a/Documentation/filesystems/nilfs2.txt +++ b/Documentation/filesystems/nilfs2.txt @@ -77,9 +77,10 @@ norecovery Disable recovery of the filesystem on mount. This disables every write access on the device for read-only mounts or snapshots. This option will fail for r/w mounts on an unclean volume. -discard Issue discard/TRIM commands to the underlying block - device when blocks are freed. This is useful for SSD - devices and sparse/thinly-provisioned LUNs. +discard This enables/disables the use of discard/TRIM commands. +nodiscard(*) The discard/TRIM commands are sent to the underlying + block device when blocks are freed. This is useful + for SSD devices and sparse/thinly-provisioned LUNs. NILFS2 usage ============ diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c index 13b0e955c028..9da12211aac1 100644 --- a/fs/nilfs2/super.c +++ b/fs/nilfs2/super.c @@ -604,7 +604,7 @@ static const struct export_operations nilfs_export_ops = { enum { Opt_err_cont, Opt_err_panic, Opt_err_ro, Opt_barrier, Opt_nobarrier, Opt_snapshot, Opt_order, Opt_norecovery, - Opt_discard, Opt_err, + Opt_discard, Opt_nodiscard, Opt_err, }; static match_table_t tokens = { @@ -617,6 +617,7 @@ static match_table_t tokens = { {Opt_order, "order=%s"}, {Opt_norecovery, "norecovery"}, {Opt_discard, "discard"}, + {Opt_nodiscard, "nodiscard"}, {Opt_err, NULL} }; @@ -676,6 +677,9 @@ static int parse_options(char *options, struct super_block *sb) case Opt_discard: nilfs_set_opt(sbi, DISCARD); break; + case Opt_nodiscard: + nilfs_clear_opt(sbi, DISCARD); + break; default: printk(KERN_ERR "NILFS: Unrecognized mount option \"%s\"\n", p); -- cgit v1.2.3 From c6b4d57ddf12f3fd4d41d7b3b9181de46748418d Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Mon, 5 Jul 2010 14:40:27 +0900 Subject: nilfs2: use seq_puts to print mount options without argument This replaces seq_printf() with seq_puts() in nilfs_show_options for mount options which have no argument. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/super.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'fs') diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c index 9da12211aac1..6a11243ebc51 100644 --- a/fs/nilfs2/super.c +++ b/fs/nilfs2/super.c @@ -521,20 +521,20 @@ static int nilfs_show_options(struct seq_file *seq, struct vfsmount *vfs) struct nilfs_sb_info *sbi = NILFS_SB(sb); if (!nilfs_test_opt(sbi, BARRIER)) - seq_printf(seq, ",nobarrier"); + seq_puts(seq, ",nobarrier"); if (nilfs_test_opt(sbi, SNAPSHOT)) seq_printf(seq, ",cp=%llu", (unsigned long long int)sbi->s_snapshot_cno); if (nilfs_test_opt(sbi, ERRORS_PANIC)) - seq_printf(seq, ",errors=panic"); + seq_puts(seq, ",errors=panic"); if (nilfs_test_opt(sbi, ERRORS_CONT)) - seq_printf(seq, ",errors=continue"); + seq_puts(seq, ",errors=continue"); if (nilfs_test_opt(sbi, STRICT_ORDER)) - seq_printf(seq, ",order=strict"); + seq_puts(seq, ",order=strict"); if (nilfs_test_opt(sbi, NORECOVERY)) - seq_printf(seq, ",norecovery"); + seq_puts(seq, ",norecovery"); if (nilfs_test_opt(sbi, DISCARD)) - seq_printf(seq, ",discard"); + seq_puts(seq, ",discard"); return 0; } -- cgit v1.2.3 From 7c01745781177795e39f78b2c2c42c470a13833a Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Mon, 5 Jul 2010 20:08:33 +0900 Subject: nilfs2: pass remount flag to parse_options This adds is_remount argument to the parse_options() function that obtains mount options from strings. Previously, parse_options did not distinguish context whether it's called for a new mount or remount, so the caller needed additional verifications outside the function. This allows parse_options to verify options and print messages depending on the context. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/super.c | 49 ++++++++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 23 deletions(-) (limited to 'fs') diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c index 6a11243ebc51..952f4ccb18de 100644 --- a/fs/nilfs2/super.c +++ b/fs/nilfs2/super.c @@ -621,7 +621,7 @@ static match_table_t tokens = { {Opt_err, NULL} }; -static int parse_options(char *options, struct super_block *sb) +static int parse_options(char *options, struct super_block *sb, int is_remount) { struct nilfs_sb_info *sbi = NILFS_SB(sb); char *p; @@ -666,8 +666,26 @@ static int parse_options(char *options, struct super_block *sb) case Opt_snapshot: if (match_int(&args[0], &option) || option <= 0) return 0; - if (!(sb->s_flags & MS_RDONLY)) + if (is_remount) { + if (!nilfs_test_opt(sbi, SNAPSHOT)) { + printk(KERN_ERR + "NILFS: cannot change regular " + "mount to snapshot.\n"); + return 0; + } else if (option != sbi->s_snapshot_cno) { + printk(KERN_ERR + "NILFS: cannot remount to a " + "different snapshot.\n"); + return 0; + } + break; + } + if (!(sb->s_flags & MS_RDONLY)) { + printk(KERN_ERR "NILFS: cannot mount snapshot " + "read/write. A read-only option is " + "required.\n"); return 0; + } sbi->s_snapshot_cno = option; nilfs_set_opt(sbi, SNAPSHOT); break; @@ -767,7 +785,7 @@ int nilfs_store_magic_and_option(struct super_block *sb, sbi->s_interval = le32_to_cpu(sbp->s_c_interval); sbi->s_watermark = le32_to_cpu(sbp->s_c_block_max); - return !parse_options(data, sb) ? -EINVAL : 0 ; + return !parse_options(data, sb, 0) ? -EINVAL : 0 ; } /** @@ -929,32 +947,17 @@ static int nilfs_remount(struct super_block *sb, int *flags, char *data) old_opts.snapshot_cno = sbi->s_snapshot_cno; was_snapshot = nilfs_test_opt(sbi, SNAPSHOT); - if (!parse_options(data, sb)) { + if (!parse_options(data, sb, 1)) { err = -EINVAL; goto restore_opts; } sb->s_flags = (sb->s_flags & ~MS_POSIXACL); err = -EINVAL; - if (was_snapshot) { - if (!(*flags & MS_RDONLY)) { - printk(KERN_ERR "NILFS (device %s): cannot remount " - "snapshot read/write.\n", - sb->s_id); - goto restore_opts; - } else if (sbi->s_snapshot_cno != old_opts.snapshot_cno) { - printk(KERN_ERR "NILFS (device %s): cannot " - "remount to a different snapshot.\n", - sb->s_id); - goto restore_opts; - } - } else { - if (nilfs_test_opt(sbi, SNAPSHOT)) { - printk(KERN_ERR "NILFS (device %s): cannot change " - "a regular mount to a snapshot.\n", - sb->s_id); - goto restore_opts; - } + if (was_snapshot && !(*flags & MS_RDONLY)) { + printk(KERN_ERR "NILFS (device %s): cannot remount snapshot " + "read/write.\n", sb->s_id); + goto restore_opts; } if (!nilfs_valid_fs(nilfs)) { -- cgit v1.2.3 From cfa913a5077f7619869b2b4d1bf23ccb4f8b3d7b Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Wed, 7 Jul 2010 17:19:54 +0900 Subject: nilfs2: add sanity check in nilfs_btree_add_dirty_buffer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to the report titled "problem with nilfs_cleanerd" from Łukasz Wójcicki, nilfs_btree_lookup_dirty_buffers or nilfs_btree_add_dirty_buffer got memory violation during garbage collection. This could happen if a level field of given btree node buffer is incorrect, which is a crucial internal bug. This inserts a sanity check to figure out the problem. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/btree.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'fs') diff --git a/fs/nilfs2/btree.c b/fs/nilfs2/btree.c index b27a342c5af6..386356707f90 100644 --- a/fs/nilfs2/btree.c +++ b/fs/nilfs2/btree.c @@ -1920,6 +1920,18 @@ static void nilfs_btree_add_dirty_buffer(struct nilfs_btree *btree, node = (struct nilfs_btree_node *)bh->b_data; key = nilfs_btree_node_get_key(node, 0); level = nilfs_btree_node_get_level(node); + if (level < NILFS_BTREE_LEVEL_NODE_MIN || + level >= NILFS_BTREE_LEVEL_MAX) { + dump_stack(); + printk(KERN_WARNING + "%s: invalid btree level: %d (key=%llu, ino=%lu, " + "blocknr=%llu)\n", + __func__, level, (unsigned long long)key, + NILFS_BMAP_I(&btree->bt_bmap)->vfs_inode.i_ino, + (unsigned long long)bh->b_blocknr); + return; + } + list_for_each(head, &lists[level]) { cbh = list_entry(head, struct buffer_head, b_assoc_buffers); cnode = (struct nilfs_btree_node *)cbh->b_data; -- cgit v1.2.3 From 1d5385b9f30ae43209459db424416a3e1d8f2bde Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Fri, 16 Jul 2010 23:52:40 +0900 Subject: nilfs2: verify btree node after reading This inserts sanity checks soon after read btree node from disk. This allows early detection of broken btree nodes, and helps to narrow down problems due to file system corruption. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/btree.c | 50 +++++++++++++++++++++++++++++++++++++++++++++++--- fs/nilfs2/btree.h | 2 ++ fs/nilfs2/gcinode.c | 9 +++++++-- 3 files changed, 56 insertions(+), 5 deletions(-) (limited to 'fs') diff --git a/fs/nilfs2/btree.c b/fs/nilfs2/btree.c index 386356707f90..6c9ec566d000 100644 --- a/fs/nilfs2/btree.c +++ b/fs/nilfs2/btree.c @@ -71,17 +71,24 @@ static int nilfs_btree_get_block(const struct nilfs_btree *btree, __u64 ptr, { struct address_space *btnc = &NILFS_BMAP_I((struct nilfs_bmap *)btree)->i_btnode_cache; + struct buffer_head *bh; int err; err = nilfs_btnode_submit_block(btnc, ptr, 0, bhp); if (err) return err == -EEXIST ? 0 : err; - wait_on_buffer(*bhp); - if (!buffer_uptodate(*bhp)) { - brelse(*bhp); + bh = *bhp; + wait_on_buffer(bh); + if (!buffer_uptodate(bh)) { + brelse(bh); return -EIO; } + if (nilfs_btree_broken_node_block(bh)) { + clear_buffer_uptodate(bh); + brelse(bh); + return -EINVAL; + } return 0; } @@ -382,6 +389,43 @@ static int nilfs_btree_node_lookup(const struct nilfs_btree_node *node, return s == 0; } +/** + * nilfs_btree_node_broken - verify consistency of btree node + * @node: btree node block to be examined + * @size: node size (in bytes) + * @blocknr: block number + * + * Return Value: If node is broken, 1 is returned. Otherwise, 0 is returned. + */ +static int nilfs_btree_node_broken(const struct nilfs_btree_node *node, + size_t size, sector_t blocknr) +{ + int level, flags, nchildren; + int ret = 0; + + level = nilfs_btree_node_get_level(node); + flags = nilfs_btree_node_get_flags(node); + nchildren = nilfs_btree_node_get_nchildren(node); + + if (unlikely(level < NILFS_BTREE_LEVEL_NODE_MIN || + level >= NILFS_BTREE_LEVEL_MAX || + (flags & NILFS_BTREE_NODE_ROOT) || + nchildren < 0 || + nchildren > NILFS_BTREE_NODE_NCHILDREN_MAX(size))) { + printk(KERN_CRIT "NILFS: bad btree node (blocknr=%llu): " + "level = %d, flags = 0x%x, nchildren = %d\n", + (unsigned long long)blocknr, level, flags, nchildren); + ret = 1; + } + return ret; +} + +int nilfs_btree_broken_node_block(struct buffer_head *bh) +{ + return nilfs_btree_node_broken((struct nilfs_btree_node *)bh->b_data, + bh->b_size, bh->b_blocknr); +} + static inline struct nilfs_btree_node * nilfs_btree_get_root(const struct nilfs_btree *btree) { diff --git a/fs/nilfs2/btree.h b/fs/nilfs2/btree.h index 43c8c5b541fd..980e1e8ec53a 100644 --- a/fs/nilfs2/btree.h +++ b/fs/nilfs2/btree.h @@ -80,4 +80,6 @@ int nilfs_btree_convert_and_insert(struct nilfs_bmap *, __u64, __u64, const __u64 *, const __u64 *, int); void nilfs_btree_init_gc(struct nilfs_bmap *); +int nilfs_btree_broken_node_block(struct buffer_head *bh); + #endif /* _NILFS_BTREE_H */ diff --git a/fs/nilfs2/gcinode.c b/fs/nilfs2/gcinode.c index 145f03cd7d3e..edb53fcb7f83 100644 --- a/fs/nilfs2/gcinode.c +++ b/fs/nilfs2/gcinode.c @@ -164,10 +164,15 @@ int nilfs_gccache_wait_and_mark_dirty(struct buffer_head *bh) if (buffer_dirty(bh)) return -EEXIST; - if (buffer_nilfs_node(bh)) + if (buffer_nilfs_node(bh)) { + if (nilfs_btree_broken_node_block(bh)) { + clear_buffer_uptodate(bh); + return -EIO; + } nilfs_btnode_mark_dirty(bh); - else + } else { nilfs_mdt_mark_buffer_dirty(bh); + } return 0; } -- cgit v1.2.3 From 25b8d7ded0e4579bf152882249abfd351e65a17d Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Sat, 10 Jul 2010 16:50:41 +0900 Subject: nilfs2: get rid of private conversion macros on bmap key and pointer Will remove nilfs_bmap_key_to_dkey(), nilfs_bmap_dkey_to_key(), nilfs_bmap_ptr_to_dptr(), and nilfs_bmap_dptr_to_ptr() for simplicity. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/bmap.h | 5 ----- fs/nilfs2/btree.c | 34 ++++++++++++++++------------------ fs/nilfs2/direct.c | 12 ++++++------ 3 files changed, 22 insertions(+), 29 deletions(-) (limited to 'fs') diff --git a/fs/nilfs2/bmap.h b/fs/nilfs2/bmap.h index 9980d7dbab91..de88ddf3bf02 100644 --- a/fs/nilfs2/bmap.h +++ b/fs/nilfs2/bmap.h @@ -32,11 +32,6 @@ #define NILFS_BMAP_INVALID_PTR 0 -#define nilfs_bmap_dkey_to_key(dkey) le64_to_cpu(dkey) -#define nilfs_bmap_key_to_dkey(key) cpu_to_le64(key) -#define nilfs_bmap_dptr_to_ptr(dptr) le64_to_cpu(dptr) -#define nilfs_bmap_ptr_to_dptr(ptr) cpu_to_le64(ptr) - #define nilfs_bmap_keydiff_abs(diff) ((diff) < 0 ? -(diff) : (diff)) diff --git a/fs/nilfs2/btree.c b/fs/nilfs2/btree.c index 6c9ec566d000..b2347f793072 100644 --- a/fs/nilfs2/btree.c +++ b/fs/nilfs2/btree.c @@ -191,29 +191,27 @@ nilfs_btree_node_dptrs(const struct nilfs_btree_node *node, static inline __u64 nilfs_btree_node_get_key(const struct nilfs_btree_node *node, int index) { - return nilfs_bmap_dkey_to_key(*(nilfs_btree_node_dkeys(node) + index)); + return le64_to_cpu(*(nilfs_btree_node_dkeys(node) + index)); } static inline void nilfs_btree_node_set_key(struct nilfs_btree_node *node, int index, __u64 key) { - *(nilfs_btree_node_dkeys(node) + index) = nilfs_bmap_key_to_dkey(key); + *(nilfs_btree_node_dkeys(node) + index) = cpu_to_le64(key); } static inline __u64 nilfs_btree_node_get_ptr(const struct nilfs_btree *btree, const struct nilfs_btree_node *node, int index) { - return nilfs_bmap_dptr_to_ptr(*(nilfs_btree_node_dptrs(node, btree) + - index)); + return le64_to_cpu(*(nilfs_btree_node_dptrs(node, btree) + index)); } static inline void nilfs_btree_node_set_ptr(struct nilfs_btree *btree, struct nilfs_btree_node *node, int index, __u64 ptr) { - *(nilfs_btree_node_dptrs(node, btree) + index) = - nilfs_bmap_ptr_to_dptr(ptr); + *(nilfs_btree_node_dptrs(node, btree) + index) = cpu_to_le64(ptr); } static void nilfs_btree_node_init(struct nilfs_btree *btree, @@ -232,8 +230,8 @@ static void nilfs_btree_node_init(struct nilfs_btree *btree, dkeys = nilfs_btree_node_dkeys(node); dptrs = nilfs_btree_node_dptrs(node, btree); for (i = 0; i < nchildren; i++) { - dkeys[i] = nilfs_bmap_key_to_dkey(keys[i]); - dptrs[i] = nilfs_bmap_ptr_to_dptr(ptrs[i]); + dkeys[i] = cpu_to_le64(keys[i]); + dptrs[i] = cpu_to_le64(ptrs[i]); } } @@ -313,8 +311,8 @@ static void nilfs_btree_node_insert(struct nilfs_btree *btree, memmove(dptrs + index + 1, dptrs + index, (nchildren - index) * sizeof(*dptrs)); } - dkeys[index] = nilfs_bmap_key_to_dkey(key); - dptrs[index] = nilfs_bmap_ptr_to_dptr(ptr); + dkeys[index] = cpu_to_le64(key); + dptrs[index] = cpu_to_le64(ptr); nchildren++; nilfs_btree_node_set_nchildren(node, nchildren); } @@ -332,8 +330,8 @@ static void nilfs_btree_node_delete(struct nilfs_btree *btree, dkeys = nilfs_btree_node_dkeys(node); dptrs = nilfs_btree_node_dptrs(node, btree); - key = nilfs_bmap_dkey_to_key(dkeys[index]); - ptr = nilfs_bmap_dptr_to_ptr(dptrs[index]); + key = le64_to_cpu(dkeys[index]); + ptr = le64_to_cpu(dptrs[index]); nchildren = nilfs_btree_node_get_nchildren(node); if (keyp != NULL) *keyp = key; @@ -1569,8 +1567,8 @@ static int nilfs_btree_gather_data(struct nilfs_bmap *bmap, dkeys = nilfs_btree_node_dkeys(node); dptrs = nilfs_btree_node_dptrs(node, btree); for (i = 0; i < nitems; i++) { - keys[i] = nilfs_bmap_dkey_to_key(dkeys[i]); - ptrs[i] = nilfs_bmap_dptr_to_ptr(dptrs[i]); + keys[i] = le64_to_cpu(dkeys[i]); + ptrs[i] = le64_to_cpu(dptrs[i]); } if (bh != NULL) @@ -2059,7 +2057,7 @@ static int nilfs_btree_assign_p(struct nilfs_btree *btree, key = nilfs_btree_node_get_key(parent, path[level + 1].bp_index); /* on-disk format */ - binfo->bi_dat.bi_blkoff = nilfs_bmap_key_to_dkey(key); + binfo->bi_dat.bi_blkoff = cpu_to_le64(key); binfo->bi_dat.bi_level = level; return 0; @@ -2090,8 +2088,8 @@ static int nilfs_btree_assign_v(struct nilfs_btree *btree, key = nilfs_btree_node_get_key(parent, path[level + 1].bp_index); /* on-disk format */ - binfo->bi_v.bi_vblocknr = nilfs_bmap_ptr_to_dptr(ptr); - binfo->bi_v.bi_blkoff = nilfs_bmap_key_to_dkey(key); + binfo->bi_v.bi_vblocknr = cpu_to_le64(ptr); + binfo->bi_v.bi_blkoff = cpu_to_le64(key); return 0; } @@ -2159,7 +2157,7 @@ static int nilfs_btree_assign_gc(struct nilfs_bmap *bmap, /* on-disk format */ binfo->bi_v.bi_vblocknr = cpu_to_le64((*bh)->b_blocknr); - binfo->bi_v.bi_blkoff = nilfs_bmap_key_to_dkey(key); + binfo->bi_v.bi_blkoff = cpu_to_le64(key); return 0; } diff --git a/fs/nilfs2/direct.c b/fs/nilfs2/direct.c index 236753df5cdf..32f1746a74a7 100644 --- a/fs/nilfs2/direct.c +++ b/fs/nilfs2/direct.c @@ -36,13 +36,13 @@ static inline __le64 *nilfs_direct_dptrs(const struct nilfs_direct *direct) static inline __u64 nilfs_direct_get_ptr(const struct nilfs_direct *direct, __u64 key) { - return nilfs_bmap_dptr_to_ptr(*(nilfs_direct_dptrs(direct) + key)); + return le64_to_cpu(*(nilfs_direct_dptrs(direct) + key)); } static inline void nilfs_direct_set_ptr(struct nilfs_direct *direct, __u64 key, __u64 ptr) { - *(nilfs_direct_dptrs(direct) + key) = nilfs_bmap_ptr_to_dptr(ptr); + *(nilfs_direct_dptrs(direct) + key) = cpu_to_le64(ptr); } static int nilfs_direct_lookup(const struct nilfs_bmap *bmap, @@ -258,7 +258,7 @@ int nilfs_direct_delete_and_convert(struct nilfs_bmap *bmap, for (i = 0, j = 0; i < NILFS_DIRECT_NBLOCKS; i++) { if ((j < n) && (i == keys[j])) { dptrs[i] = (i != key) ? - nilfs_bmap_ptr_to_dptr(ptrs[j]) : + cpu_to_le64(ptrs[j]) : NILFS_BMAP_INVALID_PTR; j++; } else @@ -315,8 +315,8 @@ static int nilfs_direct_assign_v(struct nilfs_direct *direct, ret = nilfs_dat_prepare_start(dat, &req.bpr_req); if (!ret) { nilfs_dat_commit_start(dat, &req.bpr_req, blocknr); - binfo->bi_v.bi_vblocknr = nilfs_bmap_ptr_to_dptr(ptr); - binfo->bi_v.bi_blkoff = nilfs_bmap_key_to_dkey(key); + binfo->bi_v.bi_vblocknr = cpu_to_le64(ptr); + binfo->bi_v.bi_blkoff = cpu_to_le64(key); } return ret; } @@ -329,7 +329,7 @@ static int nilfs_direct_assign_p(struct nilfs_direct *direct, { nilfs_direct_set_ptr(direct, key, blocknr); - binfo->bi_dat.bi_blkoff = nilfs_bmap_key_to_dkey(key); + binfo->bi_dat.bi_blkoff = cpu_to_le64(key); binfo->bi_dat.bi_level = 0; return 0; -- cgit v1.2.3 From 583ada4761e18bb105ce5181b0b13cf55ead6201 Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Sat, 10 Jul 2010 21:37:47 +0900 Subject: nilfs2: remove constant qualifier from argument of bmap propagate The first argument of bops->bop_propagate operation takes a constant qualifier, and causes compilation error when removed cast to pointer of nilfs_btree structure type. This fixes the issue to prepare for succesive removal of nilfs_btree struct. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/bmap.h | 2 +- fs/nilfs2/btree.c | 4 ++-- fs/nilfs2/direct.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/nilfs2/bmap.h b/fs/nilfs2/bmap.h index de88ddf3bf02..379fda4c668c 100644 --- a/fs/nilfs2/bmap.h +++ b/fs/nilfs2/bmap.h @@ -66,7 +66,7 @@ struct nilfs_bmap_operations { int (*bop_delete)(struct nilfs_bmap *, __u64); void (*bop_clear)(struct nilfs_bmap *); - int (*bop_propagate)(const struct nilfs_bmap *, struct buffer_head *); + int (*bop_propagate)(struct nilfs_bmap *, struct buffer_head *); void (*bop_lookup_dirty_buffers)(struct nilfs_bmap *, struct list_head *); diff --git a/fs/nilfs2/btree.c b/fs/nilfs2/btree.c index b2347f793072..a2dc36cb6ef7 100644 --- a/fs/nilfs2/btree.c +++ b/fs/nilfs2/btree.c @@ -1899,7 +1899,7 @@ static int nilfs_btree_propagate_v(struct nilfs_btree *btree, return ret; } -static int nilfs_btree_propagate(const struct nilfs_bmap *bmap, +static int nilfs_btree_propagate(struct nilfs_bmap *bmap, struct buffer_head *bh) { struct nilfs_btree *btree; @@ -1942,7 +1942,7 @@ static int nilfs_btree_propagate(const struct nilfs_bmap *bmap, return ret; } -static int nilfs_btree_propagate_gc(const struct nilfs_bmap *bmap, +static int nilfs_btree_propagate_gc(struct nilfs_bmap *bmap, struct buffer_head *bh) { return nilfs_dat_mark_dirty(nilfs_bmap_get_dat(bmap), bh->b_blocknr); diff --git a/fs/nilfs2/direct.c b/fs/nilfs2/direct.c index 32f1746a74a7..fd006eefc254 100644 --- a/fs/nilfs2/direct.c +++ b/fs/nilfs2/direct.c @@ -269,7 +269,7 @@ int nilfs_direct_delete_and_convert(struct nilfs_bmap *bmap, return 0; } -static int nilfs_direct_propagate(const struct nilfs_bmap *bmap, +static int nilfs_direct_propagate(struct nilfs_bmap *bmap, struct buffer_head *bh) { struct nilfs_direct *direct = (struct nilfs_direct *)bmap; -- cgit v1.2.3 From 10ff885ba6f56bf7480ce3b5daf38c07600ecea3 Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Sat, 10 Jul 2010 18:07:04 +0900 Subject: nilfs2: get rid of nilfs_direct uses This replaces all uses of nilfs_direct struct in implementation of direct mapping with nilfs_bmap struct. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/direct.c | 78 ++++++++++++++++++++++-------------------------------- 1 file changed, 32 insertions(+), 46 deletions(-) (limited to 'fs') diff --git a/fs/nilfs2/direct.c b/fs/nilfs2/direct.c index fd006eefc254..cfc7218914d6 100644 --- a/fs/nilfs2/direct.c +++ b/fs/nilfs2/direct.c @@ -27,31 +27,29 @@ #include "alloc.h" #include "dat.h" -static inline __le64 *nilfs_direct_dptrs(const struct nilfs_direct *direct) +static inline __le64 *nilfs_direct_dptrs(const struct nilfs_bmap *direct) { return (__le64 *) - ((struct nilfs_direct_node *)direct->d_bmap.b_u.u_data + 1); + ((struct nilfs_direct_node *)direct->b_u.u_data + 1); } static inline __u64 -nilfs_direct_get_ptr(const struct nilfs_direct *direct, __u64 key) +nilfs_direct_get_ptr(const struct nilfs_bmap *direct, __u64 key) { return le64_to_cpu(*(nilfs_direct_dptrs(direct) + key)); } -static inline void nilfs_direct_set_ptr(struct nilfs_direct *direct, +static inline void nilfs_direct_set_ptr(struct nilfs_bmap *direct, __u64 key, __u64 ptr) { *(nilfs_direct_dptrs(direct) + key) = cpu_to_le64(ptr); } -static int nilfs_direct_lookup(const struct nilfs_bmap *bmap, +static int nilfs_direct_lookup(const struct nilfs_bmap *direct, __u64 key, int level, __u64 *ptrp) { - struct nilfs_direct *direct; __u64 ptr; - direct = (struct nilfs_direct *)bmap; /* XXX: use macro for level 1 */ if (key > NILFS_DIRECT_KEY_MAX || level != 1) return -ENOENT; ptr = nilfs_direct_get_ptr(direct, key); @@ -63,11 +61,10 @@ static int nilfs_direct_lookup(const struct nilfs_bmap *bmap, return 0; } -static int nilfs_direct_lookup_contig(const struct nilfs_bmap *bmap, +static int nilfs_direct_lookup_contig(const struct nilfs_bmap *direct, __u64 key, __u64 *ptrp, unsigned maxblocks) { - struct nilfs_direct *direct = (struct nilfs_direct *)bmap; struct inode *dat = NULL; __u64 ptr, ptr2; sector_t blocknr; @@ -79,8 +76,8 @@ static int nilfs_direct_lookup_contig(const struct nilfs_bmap *bmap, if (ptr == NILFS_BMAP_INVALID_PTR) return -ENOENT; - if (NILFS_BMAP_USE_VBN(bmap)) { - dat = nilfs_bmap_get_dat(bmap); + if (NILFS_BMAP_USE_VBN(direct)) { + dat = nilfs_bmap_get_dat(direct); ret = nilfs_dat_translate(dat, ptr, &blocknr); if (ret < 0) return ret; @@ -106,29 +103,28 @@ static int nilfs_direct_lookup_contig(const struct nilfs_bmap *bmap, } static __u64 -nilfs_direct_find_target_v(const struct nilfs_direct *direct, __u64 key) +nilfs_direct_find_target_v(const struct nilfs_bmap *direct, __u64 key) { __u64 ptr; - ptr = nilfs_bmap_find_target_seq(&direct->d_bmap, key); + ptr = nilfs_bmap_find_target_seq(direct, key); if (ptr != NILFS_BMAP_INVALID_PTR) /* sequential access */ return ptr; else /* block group */ - return nilfs_bmap_find_target_in_group(&direct->d_bmap); + return nilfs_bmap_find_target_in_group(direct); } -static void nilfs_direct_set_target_v(struct nilfs_direct *direct, +static void nilfs_direct_set_target_v(struct nilfs_bmap *direct, __u64 key, __u64 ptr) { - direct->d_bmap.b_last_allocated_key = key; - direct->d_bmap.b_last_allocated_ptr = ptr; + direct->b_last_allocated_key = key; + direct->b_last_allocated_ptr = ptr; } static int nilfs_direct_insert(struct nilfs_bmap *bmap, __u64 key, __u64 ptr) { - struct nilfs_direct *direct = (struct nilfs_direct *)bmap; union nilfs_bmap_ptr_req req; struct inode *dat = NULL; struct buffer_head *bh; @@ -136,11 +132,11 @@ static int nilfs_direct_insert(struct nilfs_bmap *bmap, __u64 key, __u64 ptr) if (key > NILFS_DIRECT_KEY_MAX) return -ENOENT; - if (nilfs_direct_get_ptr(direct, key) != NILFS_BMAP_INVALID_PTR) + if (nilfs_direct_get_ptr(bmap, key) != NILFS_BMAP_INVALID_PTR) return -EEXIST; if (NILFS_BMAP_USE_VBN(bmap)) { - req.bpr_ptr = nilfs_direct_find_target_v(direct, key); + req.bpr_ptr = nilfs_direct_find_target_v(bmap, key); dat = nilfs_bmap_get_dat(bmap); } ret = nilfs_bmap_prepare_alloc_ptr(bmap, &req, dat); @@ -150,13 +146,13 @@ static int nilfs_direct_insert(struct nilfs_bmap *bmap, __u64 key, __u64 ptr) set_buffer_nilfs_volatile(bh); nilfs_bmap_commit_alloc_ptr(bmap, &req, dat); - nilfs_direct_set_ptr(direct, key, req.bpr_ptr); + nilfs_direct_set_ptr(bmap, key, req.bpr_ptr); if (!nilfs_bmap_dirty(bmap)) nilfs_bmap_set_dirty(bmap); if (NILFS_BMAP_USE_VBN(bmap)) - nilfs_direct_set_target_v(direct, key, req.bpr_ptr); + nilfs_direct_set_target_v(bmap, key, req.bpr_ptr); nilfs_bmap_add_blocks(bmap, 1); } @@ -165,33 +161,30 @@ static int nilfs_direct_insert(struct nilfs_bmap *bmap, __u64 key, __u64 ptr) static int nilfs_direct_delete(struct nilfs_bmap *bmap, __u64 key) { - struct nilfs_direct *direct = (struct nilfs_direct *)bmap; union nilfs_bmap_ptr_req req; struct inode *dat; int ret; if (key > NILFS_DIRECT_KEY_MAX || - nilfs_direct_get_ptr(direct, key) == NILFS_BMAP_INVALID_PTR) + nilfs_direct_get_ptr(bmap, key) == NILFS_BMAP_INVALID_PTR) return -ENOENT; dat = NILFS_BMAP_USE_VBN(bmap) ? nilfs_bmap_get_dat(bmap) : NULL; - req.bpr_ptr = nilfs_direct_get_ptr(direct, key); + req.bpr_ptr = nilfs_direct_get_ptr(bmap, key); ret = nilfs_bmap_prepare_end_ptr(bmap, &req, dat); if (!ret) { nilfs_bmap_commit_end_ptr(bmap, &req, dat); - nilfs_direct_set_ptr(direct, key, NILFS_BMAP_INVALID_PTR); + nilfs_direct_set_ptr(bmap, key, NILFS_BMAP_INVALID_PTR); nilfs_bmap_sub_blocks(bmap, 1); } return ret; } -static int nilfs_direct_last_key(const struct nilfs_bmap *bmap, __u64 *keyp) +static int nilfs_direct_last_key(const struct nilfs_bmap *direct, __u64 *keyp) { - struct nilfs_direct *direct; __u64 key, lastkey; - direct = (struct nilfs_direct *)bmap; lastkey = NILFS_DIRECT_KEY_MAX + 1; for (key = NILFS_DIRECT_KEY_MIN; key <= NILFS_DIRECT_KEY_MAX; key++) if (nilfs_direct_get_ptr(direct, key) != @@ -211,15 +204,13 @@ static int nilfs_direct_check_insert(const struct nilfs_bmap *bmap, __u64 key) return key > NILFS_DIRECT_KEY_MAX; } -static int nilfs_direct_gather_data(struct nilfs_bmap *bmap, +static int nilfs_direct_gather_data(struct nilfs_bmap *direct, __u64 *keys, __u64 *ptrs, int nitems) { - struct nilfs_direct *direct; __u64 key; __u64 ptr; int n; - direct = (struct nilfs_direct *)bmap; if (nitems > NILFS_DIRECT_NBLOCKS) nitems = NILFS_DIRECT_NBLOCKS; n = 0; @@ -237,7 +228,6 @@ static int nilfs_direct_gather_data(struct nilfs_bmap *bmap, int nilfs_direct_delete_and_convert(struct nilfs_bmap *bmap, __u64 key, __u64 *keys, __u64 *ptrs, int n) { - struct nilfs_direct *direct; __le64 *dptrs; int ret, i, j; @@ -253,8 +243,7 @@ int nilfs_direct_delete_and_convert(struct nilfs_bmap *bmap, bmap->b_ops->bop_clear(bmap); /* convert */ - direct = (struct nilfs_direct *)bmap; - dptrs = nilfs_direct_dptrs(direct); + dptrs = nilfs_direct_dptrs(bmap); for (i = 0, j = 0; i < NILFS_DIRECT_NBLOCKS; i++) { if ((j < n) && (i == keys[j])) { dptrs[i] = (i != key) ? @@ -272,7 +261,6 @@ int nilfs_direct_delete_and_convert(struct nilfs_bmap *bmap, static int nilfs_direct_propagate(struct nilfs_bmap *bmap, struct buffer_head *bh) { - struct nilfs_direct *direct = (struct nilfs_direct *)bmap; struct nilfs_palloc_req oldreq, newreq; struct inode *dat; __u64 key; @@ -284,7 +272,7 @@ static int nilfs_direct_propagate(struct nilfs_bmap *bmap, dat = nilfs_bmap_get_dat(bmap); key = nilfs_bmap_data_get_key(bmap, bh); - ptr = nilfs_direct_get_ptr(direct, key); + ptr = nilfs_direct_get_ptr(bmap, key); if (!buffer_nilfs_volatile(bh)) { oldreq.pr_entry_nr = ptr; newreq.pr_entry_nr = ptr; @@ -294,20 +282,20 @@ static int nilfs_direct_propagate(struct nilfs_bmap *bmap, nilfs_dat_commit_update(dat, &oldreq, &newreq, bmap->b_ptr_type == NILFS_BMAP_PTR_VS); set_buffer_nilfs_volatile(bh); - nilfs_direct_set_ptr(direct, key, newreq.pr_entry_nr); + nilfs_direct_set_ptr(bmap, key, newreq.pr_entry_nr); } else ret = nilfs_dat_mark_dirty(dat, ptr); return ret; } -static int nilfs_direct_assign_v(struct nilfs_direct *direct, +static int nilfs_direct_assign_v(struct nilfs_bmap *direct, __u64 key, __u64 ptr, struct buffer_head **bh, sector_t blocknr, union nilfs_binfo *binfo) { - struct inode *dat = nilfs_bmap_get_dat(&direct->d_bmap); + struct inode *dat = nilfs_bmap_get_dat(direct); union nilfs_bmap_ptr_req req; int ret; @@ -321,7 +309,7 @@ static int nilfs_direct_assign_v(struct nilfs_direct *direct, return ret; } -static int nilfs_direct_assign_p(struct nilfs_direct *direct, +static int nilfs_direct_assign_p(struct nilfs_bmap *direct, __u64 key, __u64 ptr, struct buffer_head **bh, sector_t blocknr, @@ -340,18 +328,16 @@ static int nilfs_direct_assign(struct nilfs_bmap *bmap, sector_t blocknr, union nilfs_binfo *binfo) { - struct nilfs_direct *direct; __u64 key; __u64 ptr; - direct = (struct nilfs_direct *)bmap; key = nilfs_bmap_data_get_key(bmap, *bh); if (unlikely(key > NILFS_DIRECT_KEY_MAX)) { printk(KERN_CRIT "%s: invalid key: %llu\n", __func__, (unsigned long long)key); return -EINVAL; } - ptr = nilfs_direct_get_ptr(direct, key); + ptr = nilfs_direct_get_ptr(bmap, key); if (unlikely(ptr == NILFS_BMAP_INVALID_PTR)) { printk(KERN_CRIT "%s: invalid pointer: %llu\n", __func__, (unsigned long long)ptr); @@ -359,8 +345,8 @@ static int nilfs_direct_assign(struct nilfs_bmap *bmap, } return NILFS_BMAP_USE_VBN(bmap) ? - nilfs_direct_assign_v(direct, key, ptr, bh, blocknr, binfo) : - nilfs_direct_assign_p(direct, key, ptr, bh, blocknr, binfo); + nilfs_direct_assign_v(bmap, key, ptr, bh, blocknr, binfo) : + nilfs_direct_assign_p(bmap, key, ptr, bh, blocknr, binfo); } static const struct nilfs_bmap_operations nilfs_direct_ops = { -- cgit v1.2.3 From e7c274f8083793f8f861def63c02a0839b34d26d Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Sat, 10 Jul 2010 19:09:49 +0900 Subject: nilfs2: get rid of nilfs_btree uses This replaces all uses of nilfs_btree struct in implementation of btree mapping with nilfs_bmap struct. Name of local variable "btree" is kept not to bloat amount of change. And, a part of local variables "bmap" is renamed to "btree" to uniform naming rule. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/btree.c | 316 ++++++++++++++++++++++++------------------------------ fs/nilfs2/btree.h | 2 +- 2 files changed, 141 insertions(+), 177 deletions(-) (limited to 'fs') diff --git a/fs/nilfs2/btree.c b/fs/nilfs2/btree.c index a2dc36cb6ef7..81e871645b5f 100644 --- a/fs/nilfs2/btree.c +++ b/fs/nilfs2/btree.c @@ -66,11 +66,10 @@ static void nilfs_btree_free_path(struct nilfs_btree_path *path) /* * B-tree node operations */ -static int nilfs_btree_get_block(const struct nilfs_btree *btree, __u64 ptr, +static int nilfs_btree_get_block(const struct nilfs_bmap *btree, __u64 ptr, struct buffer_head **bhp) { - struct address_space *btnc = - &NILFS_BMAP_I((struct nilfs_bmap *)btree)->i_btnode_cache; + struct address_space *btnc = &NILFS_BMAP_I(btree)->i_btnode_cache; struct buffer_head *bh; int err; @@ -92,11 +91,10 @@ static int nilfs_btree_get_block(const struct nilfs_btree *btree, __u64 ptr, return 0; } -static int nilfs_btree_get_new_block(const struct nilfs_btree *btree, +static int nilfs_btree_get_new_block(const struct nilfs_bmap *btree, __u64 ptr, struct buffer_head **bhp) { - struct address_space *btnc = - &NILFS_BMAP_I((struct nilfs_bmap *)btree)->i_btnode_cache; + struct address_space *btnc = &NILFS_BMAP_I(btree)->i_btnode_cache; struct buffer_head *bh; bh = nilfs_btnode_create_block(btnc, ptr); @@ -149,14 +147,14 @@ nilfs_btree_node_set_nchildren(struct nilfs_btree_node *node, int nchildren) node->bn_nchildren = cpu_to_le16(nchildren); } -static inline int nilfs_btree_node_size(const struct nilfs_btree *btree) +static inline int nilfs_btree_node_size(const struct nilfs_bmap *btree) { - return 1 << btree->bt_bmap.b_inode->i_blkbits; + return 1 << btree->b_inode->i_blkbits; } static inline int nilfs_btree_node_nchildren_min(const struct nilfs_btree_node *node, - const struct nilfs_btree *btree) + const struct nilfs_bmap *btree) { return nilfs_btree_node_root(node) ? NILFS_BTREE_ROOT_NCHILDREN_MIN : @@ -165,7 +163,7 @@ nilfs_btree_node_nchildren_min(const struct nilfs_btree_node *node, static inline int nilfs_btree_node_nchildren_max(const struct nilfs_btree_node *node, - const struct nilfs_btree *btree) + const struct nilfs_bmap *btree) { return nilfs_btree_node_root(node) ? NILFS_BTREE_ROOT_NCHILDREN_MAX : @@ -182,7 +180,7 @@ nilfs_btree_node_dkeys(const struct nilfs_btree_node *node) static inline __le64 * nilfs_btree_node_dptrs(const struct nilfs_btree_node *node, - const struct nilfs_btree *btree) + const struct nilfs_bmap *btree) { return (__le64 *)(nilfs_btree_node_dkeys(node) + nilfs_btree_node_nchildren_max(node, btree)); @@ -201,20 +199,20 @@ nilfs_btree_node_set_key(struct nilfs_btree_node *node, int index, __u64 key) } static inline __u64 -nilfs_btree_node_get_ptr(const struct nilfs_btree *btree, +nilfs_btree_node_get_ptr(const struct nilfs_bmap *btree, const struct nilfs_btree_node *node, int index) { return le64_to_cpu(*(nilfs_btree_node_dptrs(node, btree) + index)); } static inline void -nilfs_btree_node_set_ptr(struct nilfs_btree *btree, +nilfs_btree_node_set_ptr(struct nilfs_bmap *btree, struct nilfs_btree_node *node, int index, __u64 ptr) { *(nilfs_btree_node_dptrs(node, btree) + index) = cpu_to_le64(ptr); } -static void nilfs_btree_node_init(struct nilfs_btree *btree, +static void nilfs_btree_node_init(struct nilfs_bmap *btree, struct nilfs_btree_node *node, int flags, int level, int nchildren, const __u64 *keys, const __u64 *ptrs) @@ -236,7 +234,7 @@ static void nilfs_btree_node_init(struct nilfs_btree *btree, } /* Assume the buffer heads corresponding to left and right are locked. */ -static void nilfs_btree_node_move_left(struct nilfs_btree *btree, +static void nilfs_btree_node_move_left(struct nilfs_bmap *btree, struct nilfs_btree_node *left, struct nilfs_btree_node *right, int n) @@ -265,7 +263,7 @@ static void nilfs_btree_node_move_left(struct nilfs_btree *btree, } /* Assume that the buffer heads corresponding to left and right are locked. */ -static void nilfs_btree_node_move_right(struct nilfs_btree *btree, +static void nilfs_btree_node_move_right(struct nilfs_bmap *btree, struct nilfs_btree_node *left, struct nilfs_btree_node *right, int n) @@ -294,7 +292,7 @@ static void nilfs_btree_node_move_right(struct nilfs_btree *btree, } /* Assume that the buffer head corresponding to node is locked. */ -static void nilfs_btree_node_insert(struct nilfs_btree *btree, +static void nilfs_btree_node_insert(struct nilfs_bmap *btree, struct nilfs_btree_node *node, __u64 key, __u64 ptr, int index) { @@ -318,7 +316,7 @@ static void nilfs_btree_node_insert(struct nilfs_btree *btree, } /* Assume that the buffer head corresponding to node is locked. */ -static void nilfs_btree_node_delete(struct nilfs_btree *btree, +static void nilfs_btree_node_delete(struct nilfs_bmap *btree, struct nilfs_btree_node *node, __u64 *keyp, __u64 *ptrp, int index) { @@ -425,9 +423,9 @@ int nilfs_btree_broken_node_block(struct buffer_head *bh) } static inline struct nilfs_btree_node * -nilfs_btree_get_root(const struct nilfs_btree *btree) +nilfs_btree_get_root(const struct nilfs_bmap *btree) { - return (struct nilfs_btree_node *)btree->bt_bmap.b_u.u_data; + return (struct nilfs_btree_node *)btree->b_u.u_data; } static inline struct nilfs_btree_node * @@ -442,13 +440,13 @@ nilfs_btree_get_sib_node(const struct nilfs_btree_path *path, int level) return (struct nilfs_btree_node *)path[level].bp_sib_bh->b_data; } -static inline int nilfs_btree_height(const struct nilfs_btree *btree) +static inline int nilfs_btree_height(const struct nilfs_bmap *btree) { return nilfs_btree_node_get_level(nilfs_btree_get_root(btree)) + 1; } static inline struct nilfs_btree_node * -nilfs_btree_get_node(const struct nilfs_btree *btree, +nilfs_btree_get_node(const struct nilfs_bmap *btree, const struct nilfs_btree_path *path, int level) { @@ -469,7 +467,7 @@ nilfs_btree_bad_node(struct nilfs_btree_node *node, int level) return 0; } -static int nilfs_btree_do_lookup(const struct nilfs_btree *btree, +static int nilfs_btree_do_lookup(const struct nilfs_bmap *btree, struct nilfs_btree_path *path, __u64 key, __u64 *ptrp, int minlevel) { @@ -516,7 +514,7 @@ static int nilfs_btree_do_lookup(const struct nilfs_btree *btree, return 0; } -static int nilfs_btree_do_lookup_last(const struct nilfs_btree *btree, +static int nilfs_btree_do_lookup_last(const struct nilfs_bmap *btree, struct nilfs_btree_path *path, __u64 *keyp, __u64 *ptrp) { @@ -553,15 +551,13 @@ static int nilfs_btree_do_lookup_last(const struct nilfs_btree *btree, return 0; } -static int nilfs_btree_lookup(const struct nilfs_bmap *bmap, +static int nilfs_btree_lookup(const struct nilfs_bmap *btree, __u64 key, int level, __u64 *ptrp) { - struct nilfs_btree *btree; struct nilfs_btree_path *path; __u64 ptr; int ret; - btree = (struct nilfs_btree *)bmap; path = nilfs_btree_alloc_path(); if (path == NULL) return -ENOMEM; @@ -576,10 +572,9 @@ static int nilfs_btree_lookup(const struct nilfs_bmap *bmap, return ret; } -static int nilfs_btree_lookup_contig(const struct nilfs_bmap *bmap, +static int nilfs_btree_lookup_contig(const struct nilfs_bmap *btree, __u64 key, __u64 *ptrp, unsigned maxblocks) { - struct nilfs_btree *btree = (struct nilfs_btree *)bmap; struct nilfs_btree_path *path; struct nilfs_btree_node *node; struct inode *dat = NULL; @@ -596,8 +591,8 @@ static int nilfs_btree_lookup_contig(const struct nilfs_bmap *bmap, if (ret < 0) goto out; - if (NILFS_BMAP_USE_VBN(bmap)) { - dat = nilfs_bmap_get_dat(bmap); + if (NILFS_BMAP_USE_VBN(btree)) { + dat = nilfs_bmap_get_dat(btree); ret = nilfs_dat_translate(dat, ptr, &blocknr); if (ret < 0) goto out; @@ -656,7 +651,7 @@ static int nilfs_btree_lookup_contig(const struct nilfs_bmap *bmap, return ret; } -static void nilfs_btree_promote_key(struct nilfs_btree *btree, +static void nilfs_btree_promote_key(struct nilfs_bmap *btree, struct nilfs_btree_path *path, int level, __u64 key) { @@ -678,7 +673,7 @@ static void nilfs_btree_promote_key(struct nilfs_btree *btree, } } -static void nilfs_btree_do_insert(struct nilfs_btree *btree, +static void nilfs_btree_do_insert(struct nilfs_bmap *btree, struct nilfs_btree_path *path, int level, __u64 *keyp, __u64 *ptrp) { @@ -702,7 +697,7 @@ static void nilfs_btree_do_insert(struct nilfs_btree *btree, } } -static void nilfs_btree_carry_left(struct nilfs_btree *btree, +static void nilfs_btree_carry_left(struct nilfs_bmap *btree, struct nilfs_btree_path *path, int level, __u64 *keyp, __u64 *ptrp) { @@ -747,7 +742,7 @@ static void nilfs_btree_carry_left(struct nilfs_btree *btree, nilfs_btree_do_insert(btree, path, level, keyp, ptrp); } -static void nilfs_btree_carry_right(struct nilfs_btree *btree, +static void nilfs_btree_carry_right(struct nilfs_bmap *btree, struct nilfs_btree_path *path, int level, __u64 *keyp, __u64 *ptrp) { @@ -793,7 +788,7 @@ static void nilfs_btree_carry_right(struct nilfs_btree *btree, nilfs_btree_do_insert(btree, path, level, keyp, ptrp); } -static void nilfs_btree_split(struct nilfs_btree *btree, +static void nilfs_btree_split(struct nilfs_bmap *btree, struct nilfs_btree_path *path, int level, __u64 *keyp, __u64 *ptrp) { @@ -847,7 +842,7 @@ static void nilfs_btree_split(struct nilfs_btree *btree, path[level + 1].bp_index++; } -static void nilfs_btree_grow(struct nilfs_btree *btree, +static void nilfs_btree_grow(struct nilfs_bmap *btree, struct nilfs_btree_path *path, int level, __u64 *keyp, __u64 *ptrp) { @@ -874,7 +869,7 @@ static void nilfs_btree_grow(struct nilfs_btree *btree, *ptrp = path[level].bp_newreq.bpr_ptr; } -static __u64 nilfs_btree_find_near(const struct nilfs_btree *btree, +static __u64 nilfs_btree_find_near(const struct nilfs_bmap *btree, const struct nilfs_btree_path *path) { struct nilfs_btree_node *node; @@ -902,13 +897,13 @@ static __u64 nilfs_btree_find_near(const struct nilfs_btree *btree, return NILFS_BMAP_INVALID_PTR; } -static __u64 nilfs_btree_find_target_v(const struct nilfs_btree *btree, +static __u64 nilfs_btree_find_target_v(const struct nilfs_bmap *btree, const struct nilfs_btree_path *path, __u64 key) { __u64 ptr; - ptr = nilfs_bmap_find_target_seq(&btree->bt_bmap, key); + ptr = nilfs_bmap_find_target_seq(btree, key); if (ptr != NILFS_BMAP_INVALID_PTR) /* sequential access */ return ptr; @@ -919,17 +914,17 @@ static __u64 nilfs_btree_find_target_v(const struct nilfs_btree *btree, return ptr; } /* block group */ - return nilfs_bmap_find_target_in_group(&btree->bt_bmap); + return nilfs_bmap_find_target_in_group(btree); } -static void nilfs_btree_set_target_v(struct nilfs_btree *btree, __u64 key, +static void nilfs_btree_set_target_v(struct nilfs_bmap *btree, __u64 key, __u64 ptr) { - btree->bt_bmap.b_last_allocated_key = key; - btree->bt_bmap.b_last_allocated_ptr = ptr; + btree->b_last_allocated_key = key; + btree->b_last_allocated_ptr = ptr; } -static int nilfs_btree_prepare_insert(struct nilfs_btree *btree, +static int nilfs_btree_prepare_insert(struct nilfs_bmap *btree, struct nilfs_btree_path *path, int *levelp, __u64 key, __u64 ptr, struct nilfs_bmap_stats *stats) @@ -944,14 +939,13 @@ static int nilfs_btree_prepare_insert(struct nilfs_btree *btree, level = NILFS_BTREE_LEVEL_DATA; /* allocate a new ptr for data block */ - if (NILFS_BMAP_USE_VBN(&btree->bt_bmap)) { + if (NILFS_BMAP_USE_VBN(btree)) { path[level].bp_newreq.bpr_ptr = nilfs_btree_find_target_v(btree, path, key); - dat = nilfs_bmap_get_dat(&btree->bt_bmap); + dat = nilfs_bmap_get_dat(btree); } - ret = nilfs_bmap_prepare_alloc_ptr(&btree->bt_bmap, - &path[level].bp_newreq, dat); + ret = nilfs_bmap_prepare_alloc_ptr(btree, &path[level].bp_newreq, dat); if (ret < 0) goto err_out_data; @@ -1009,7 +1003,7 @@ static int nilfs_btree_prepare_insert(struct nilfs_btree *btree, /* split */ path[level].bp_newreq.bpr_ptr = path[level - 1].bp_newreq.bpr_ptr + 1; - ret = nilfs_bmap_prepare_alloc_ptr(&btree->bt_bmap, + ret = nilfs_bmap_prepare_alloc_ptr(btree, &path[level].bp_newreq, dat); if (ret < 0) goto err_out_child_node; @@ -1039,8 +1033,7 @@ static int nilfs_btree_prepare_insert(struct nilfs_btree *btree, /* grow */ path[level].bp_newreq.bpr_ptr = path[level - 1].bp_newreq.bpr_ptr + 1; - ret = nilfs_bmap_prepare_alloc_ptr(&btree->bt_bmap, - &path[level].bp_newreq, dat); + ret = nilfs_bmap_prepare_alloc_ptr(btree, &path[level].bp_newreq, dat); if (ret < 0) goto err_out_child_node; ret = nilfs_btree_get_new_block(btree, path[level].bp_newreq.bpr_ptr, @@ -1066,25 +1059,22 @@ static int nilfs_btree_prepare_insert(struct nilfs_btree *btree, /* error */ err_out_curr_node: - nilfs_bmap_abort_alloc_ptr(&btree->bt_bmap, &path[level].bp_newreq, - dat); + nilfs_bmap_abort_alloc_ptr(btree, &path[level].bp_newreq, dat); err_out_child_node: for (level--; level > NILFS_BTREE_LEVEL_DATA; level--) { nilfs_btnode_delete(path[level].bp_sib_bh); - nilfs_bmap_abort_alloc_ptr(&btree->bt_bmap, - &path[level].bp_newreq, dat); + nilfs_bmap_abort_alloc_ptr(btree, &path[level].bp_newreq, dat); } - nilfs_bmap_abort_alloc_ptr(&btree->bt_bmap, &path[level].bp_newreq, - dat); + nilfs_bmap_abort_alloc_ptr(btree, &path[level].bp_newreq, dat); err_out_data: *levelp = level; stats->bs_nblocks = 0; return ret; } -static void nilfs_btree_commit_insert(struct nilfs_btree *btree, +static void nilfs_btree_commit_insert(struct nilfs_bmap *btree, struct nilfs_btree_path *path, int maxlevel, __u64 key, __u64 ptr) { @@ -1093,29 +1083,27 @@ static void nilfs_btree_commit_insert(struct nilfs_btree *btree, set_buffer_nilfs_volatile((struct buffer_head *)((unsigned long)ptr)); ptr = path[NILFS_BTREE_LEVEL_DATA].bp_newreq.bpr_ptr; - if (NILFS_BMAP_USE_VBN(&btree->bt_bmap)) { + if (NILFS_BMAP_USE_VBN(btree)) { nilfs_btree_set_target_v(btree, key, ptr); - dat = nilfs_bmap_get_dat(&btree->bt_bmap); + dat = nilfs_bmap_get_dat(btree); } for (level = NILFS_BTREE_LEVEL_NODE_MIN; level <= maxlevel; level++) { - nilfs_bmap_commit_alloc_ptr(&btree->bt_bmap, + nilfs_bmap_commit_alloc_ptr(btree, &path[level - 1].bp_newreq, dat); path[level].bp_op(btree, path, level, &key, &ptr); } - if (!nilfs_bmap_dirty(&btree->bt_bmap)) - nilfs_bmap_set_dirty(&btree->bt_bmap); + if (!nilfs_bmap_dirty(btree)) + nilfs_bmap_set_dirty(btree); } -static int nilfs_btree_insert(struct nilfs_bmap *bmap, __u64 key, __u64 ptr) +static int nilfs_btree_insert(struct nilfs_bmap *btree, __u64 key, __u64 ptr) { - struct nilfs_btree *btree; struct nilfs_btree_path *path; struct nilfs_bmap_stats stats; int level, ret; - btree = (struct nilfs_btree *)bmap; path = nilfs_btree_alloc_path(); if (path == NULL) return -ENOMEM; @@ -1132,14 +1120,14 @@ static int nilfs_btree_insert(struct nilfs_bmap *bmap, __u64 key, __u64 ptr) if (ret < 0) goto out; nilfs_btree_commit_insert(btree, path, level, key, ptr); - nilfs_bmap_add_blocks(bmap, stats.bs_nblocks); + nilfs_bmap_add_blocks(btree, stats.bs_nblocks); out: nilfs_btree_free_path(path); return ret; } -static void nilfs_btree_do_delete(struct nilfs_btree *btree, +static void nilfs_btree_do_delete(struct nilfs_bmap *btree, struct nilfs_btree_path *path, int level, __u64 *keyp, __u64 *ptrp) { @@ -1161,7 +1149,7 @@ static void nilfs_btree_do_delete(struct nilfs_btree *btree, } } -static void nilfs_btree_borrow_left(struct nilfs_btree *btree, +static void nilfs_btree_borrow_left(struct nilfs_bmap *btree, struct nilfs_btree_path *path, int level, __u64 *keyp, __u64 *ptrp) { @@ -1192,7 +1180,7 @@ static void nilfs_btree_borrow_left(struct nilfs_btree *btree, path[level].bp_index += n; } -static void nilfs_btree_borrow_right(struct nilfs_btree *btree, +static void nilfs_btree_borrow_right(struct nilfs_bmap *btree, struct nilfs_btree_path *path, int level, __u64 *keyp, __u64 *ptrp) { @@ -1224,7 +1212,7 @@ static void nilfs_btree_borrow_right(struct nilfs_btree *btree, path[level].bp_sib_bh = NULL; } -static void nilfs_btree_concat_left(struct nilfs_btree *btree, +static void nilfs_btree_concat_left(struct nilfs_bmap *btree, struct nilfs_btree_path *path, int level, __u64 *keyp, __u64 *ptrp) { @@ -1249,7 +1237,7 @@ static void nilfs_btree_concat_left(struct nilfs_btree *btree, path[level].bp_index += nilfs_btree_node_get_nchildren(left); } -static void nilfs_btree_concat_right(struct nilfs_btree *btree, +static void nilfs_btree_concat_right(struct nilfs_bmap *btree, struct nilfs_btree_path *path, int level, __u64 *keyp, __u64 *ptrp) { @@ -1273,7 +1261,7 @@ static void nilfs_btree_concat_right(struct nilfs_btree *btree, path[level + 1].bp_index++; } -static void nilfs_btree_shrink(struct nilfs_btree *btree, +static void nilfs_btree_shrink(struct nilfs_bmap *btree, struct nilfs_btree_path *path, int level, __u64 *keyp, __u64 *ptrp) { @@ -1295,7 +1283,7 @@ static void nilfs_btree_shrink(struct nilfs_btree *btree, } -static int nilfs_btree_prepare_delete(struct nilfs_btree *btree, +static int nilfs_btree_prepare_delete(struct nilfs_bmap *btree, struct nilfs_btree_path *path, int *levelp, struct nilfs_bmap_stats *stats, @@ -1315,7 +1303,7 @@ static int nilfs_btree_prepare_delete(struct nilfs_btree *btree, path[level].bp_oldreq.bpr_ptr = nilfs_btree_node_get_ptr(btree, node, path[level].bp_index); - ret = nilfs_bmap_prepare_end_ptr(&btree->bt_bmap, + ret = nilfs_bmap_prepare_end_ptr(btree, &path[level].bp_oldreq, dat); if (ret < 0) goto err_out_child_node; @@ -1393,8 +1381,7 @@ static int nilfs_btree_prepare_delete(struct nilfs_btree *btree, path[level].bp_oldreq.bpr_ptr = nilfs_btree_node_get_ptr(btree, node, path[level].bp_index); - ret = nilfs_bmap_prepare_end_ptr(&btree->bt_bmap, - &path[level].bp_oldreq, dat); + ret = nilfs_bmap_prepare_end_ptr(btree, &path[level].bp_oldreq, dat); if (ret < 0) goto err_out_child_node; @@ -1409,44 +1396,40 @@ static int nilfs_btree_prepare_delete(struct nilfs_btree *btree, /* error */ err_out_curr_node: - nilfs_bmap_abort_end_ptr(&btree->bt_bmap, &path[level].bp_oldreq, dat); + nilfs_bmap_abort_end_ptr(btree, &path[level].bp_oldreq, dat); err_out_child_node: for (level--; level >= NILFS_BTREE_LEVEL_NODE_MIN; level--) { brelse(path[level].bp_sib_bh); - nilfs_bmap_abort_end_ptr(&btree->bt_bmap, - &path[level].bp_oldreq, dat); + nilfs_bmap_abort_end_ptr(btree, &path[level].bp_oldreq, dat); } *levelp = level; stats->bs_nblocks = 0; return ret; } -static void nilfs_btree_commit_delete(struct nilfs_btree *btree, +static void nilfs_btree_commit_delete(struct nilfs_bmap *btree, struct nilfs_btree_path *path, int maxlevel, struct inode *dat) { int level; for (level = NILFS_BTREE_LEVEL_NODE_MIN; level <= maxlevel; level++) { - nilfs_bmap_commit_end_ptr(&btree->bt_bmap, - &path[level].bp_oldreq, dat); + nilfs_bmap_commit_end_ptr(btree, &path[level].bp_oldreq, dat); path[level].bp_op(btree, path, level, NULL, NULL); } - if (!nilfs_bmap_dirty(&btree->bt_bmap)) - nilfs_bmap_set_dirty(&btree->bt_bmap); + if (!nilfs_bmap_dirty(btree)) + nilfs_bmap_set_dirty(btree); } -static int nilfs_btree_delete(struct nilfs_bmap *bmap, __u64 key) +static int nilfs_btree_delete(struct nilfs_bmap *btree, __u64 key) { - struct nilfs_btree *btree; struct nilfs_btree_path *path; struct nilfs_bmap_stats stats; struct inode *dat; int level, ret; - btree = (struct nilfs_btree *)bmap; path = nilfs_btree_alloc_path(); if (path == NULL) return -ENOMEM; @@ -1457,27 +1440,24 @@ static int nilfs_btree_delete(struct nilfs_bmap *bmap, __u64 key) goto out; - dat = NILFS_BMAP_USE_VBN(&btree->bt_bmap) ? - nilfs_bmap_get_dat(&btree->bt_bmap) : NULL; + dat = NILFS_BMAP_USE_VBN(btree) ? nilfs_bmap_get_dat(btree) : NULL; ret = nilfs_btree_prepare_delete(btree, path, &level, &stats, dat); if (ret < 0) goto out; nilfs_btree_commit_delete(btree, path, level, dat); - nilfs_bmap_sub_blocks(bmap, stats.bs_nblocks); + nilfs_bmap_sub_blocks(btree, stats.bs_nblocks); out: nilfs_btree_free_path(path); return ret; } -static int nilfs_btree_last_key(const struct nilfs_bmap *bmap, __u64 *keyp) +static int nilfs_btree_last_key(const struct nilfs_bmap *btree, __u64 *keyp) { - struct nilfs_btree *btree; struct nilfs_btree_path *path; int ret; - btree = (struct nilfs_btree *)bmap; path = nilfs_btree_alloc_path(); if (path == NULL) return -ENOMEM; @@ -1489,16 +1469,14 @@ static int nilfs_btree_last_key(const struct nilfs_bmap *bmap, __u64 *keyp) return ret; } -static int nilfs_btree_check_delete(struct nilfs_bmap *bmap, __u64 key) +static int nilfs_btree_check_delete(struct nilfs_bmap *btree, __u64 key) { struct buffer_head *bh; - struct nilfs_btree *btree; struct nilfs_btree_node *root, *node; __u64 maxkey, nextmaxkey; __u64 ptr; int nchildren, ret; - btree = (struct nilfs_btree *)bmap; root = nilfs_btree_get_root(btree); switch (nilfs_btree_height(btree)) { case 2: @@ -1529,18 +1507,16 @@ static int nilfs_btree_check_delete(struct nilfs_bmap *bmap, __u64 key) return (maxkey == key) && (nextmaxkey < NILFS_BMAP_LARGE_LOW); } -static int nilfs_btree_gather_data(struct nilfs_bmap *bmap, +static int nilfs_btree_gather_data(struct nilfs_bmap *btree, __u64 *keys, __u64 *ptrs, int nitems) { struct buffer_head *bh; - struct nilfs_btree *btree; struct nilfs_btree_node *node, *root; __le64 *dkeys; __le64 *dptrs; __u64 ptr; int nchildren, i, ret; - btree = (struct nilfs_btree *)bmap; root = nilfs_btree_get_root(btree); switch (nilfs_btree_height(btree)) { case 2: @@ -1578,14 +1554,13 @@ static int nilfs_btree_gather_data(struct nilfs_bmap *bmap, } static int -nilfs_btree_prepare_convert_and_insert(struct nilfs_bmap *bmap, __u64 key, +nilfs_btree_prepare_convert_and_insert(struct nilfs_bmap *btree, __u64 key, union nilfs_bmap_ptr_req *dreq, union nilfs_bmap_ptr_req *nreq, struct buffer_head **bhp, struct nilfs_bmap_stats *stats) { struct buffer_head *bh; - struct nilfs_btree *btree = (struct nilfs_btree *)bmap; struct inode *dat = NULL; int ret; @@ -1593,12 +1568,12 @@ nilfs_btree_prepare_convert_and_insert(struct nilfs_bmap *bmap, __u64 key, /* for data */ /* cannot find near ptr */ - if (NILFS_BMAP_USE_VBN(bmap)) { + if (NILFS_BMAP_USE_VBN(btree)) { dreq->bpr_ptr = nilfs_btree_find_target_v(btree, NULL, key); - dat = nilfs_bmap_get_dat(bmap); + dat = nilfs_bmap_get_dat(btree); } - ret = nilfs_bmap_prepare_alloc_ptr(bmap, dreq, dat); + ret = nilfs_bmap_prepare_alloc_ptr(btree, dreq, dat); if (ret < 0) return ret; @@ -1606,7 +1581,7 @@ nilfs_btree_prepare_convert_and_insert(struct nilfs_bmap *bmap, __u64 key, stats->bs_nblocks++; if (nreq != NULL) { nreq->bpr_ptr = dreq->bpr_ptr + 1; - ret = nilfs_bmap_prepare_alloc_ptr(bmap, nreq, dat); + ret = nilfs_bmap_prepare_alloc_ptr(btree, nreq, dat); if (ret < 0) goto err_out_dreq; @@ -1623,16 +1598,16 @@ nilfs_btree_prepare_convert_and_insert(struct nilfs_bmap *bmap, __u64 key, /* error */ err_out_nreq: - nilfs_bmap_abort_alloc_ptr(bmap, nreq, dat); + nilfs_bmap_abort_alloc_ptr(btree, nreq, dat); err_out_dreq: - nilfs_bmap_abort_alloc_ptr(bmap, dreq, dat); + nilfs_bmap_abort_alloc_ptr(btree, dreq, dat); stats->bs_nblocks = 0; return ret; } static void -nilfs_btree_commit_convert_and_insert(struct nilfs_bmap *bmap, +nilfs_btree_commit_convert_and_insert(struct nilfs_bmap *btree, __u64 key, __u64 ptr, const __u64 *keys, const __u64 *ptrs, int n, @@ -1640,34 +1615,32 @@ nilfs_btree_commit_convert_and_insert(struct nilfs_bmap *bmap, union nilfs_bmap_ptr_req *nreq, struct buffer_head *bh) { - struct nilfs_btree *btree = (struct nilfs_btree *)bmap; struct nilfs_btree_node *node; struct inode *dat; __u64 tmpptr; /* free resources */ - if (bmap->b_ops->bop_clear != NULL) - bmap->b_ops->bop_clear(bmap); + if (btree->b_ops->bop_clear != NULL) + btree->b_ops->bop_clear(btree); /* ptr must be a pointer to a buffer head. */ set_buffer_nilfs_volatile((struct buffer_head *)((unsigned long)ptr)); /* convert and insert */ - dat = NILFS_BMAP_USE_VBN(bmap) ? nilfs_bmap_get_dat(bmap) : NULL; - nilfs_btree_init(bmap); + dat = NILFS_BMAP_USE_VBN(btree) ? nilfs_bmap_get_dat(btree) : NULL; + nilfs_btree_init(btree); if (nreq != NULL) { - nilfs_bmap_commit_alloc_ptr(bmap, dreq, dat); - nilfs_bmap_commit_alloc_ptr(bmap, nreq, dat); + nilfs_bmap_commit_alloc_ptr(btree, dreq, dat); + nilfs_bmap_commit_alloc_ptr(btree, nreq, dat); /* create child node at level 1 */ node = (struct nilfs_btree_node *)bh->b_data; nilfs_btree_node_init(btree, node, 0, 1, n, keys, ptrs); - nilfs_btree_node_insert(btree, node, - key, dreq->bpr_ptr, n); + nilfs_btree_node_insert(btree, node, key, dreq->bpr_ptr, n); if (!buffer_dirty(bh)) nilfs_btnode_mark_dirty(bh); - if (!nilfs_bmap_dirty(bmap)) - nilfs_bmap_set_dirty(bmap); + if (!nilfs_bmap_dirty(btree)) + nilfs_bmap_set_dirty(btree); brelse(bh); @@ -1677,19 +1650,18 @@ nilfs_btree_commit_convert_and_insert(struct nilfs_bmap *bmap, nilfs_btree_node_init(btree, node, NILFS_BTREE_NODE_ROOT, 2, 1, &keys[0], &tmpptr); } else { - nilfs_bmap_commit_alloc_ptr(bmap, dreq, dat); + nilfs_bmap_commit_alloc_ptr(btree, dreq, dat); /* create root node at level 1 */ node = nilfs_btree_get_root(btree); nilfs_btree_node_init(btree, node, NILFS_BTREE_NODE_ROOT, 1, n, keys, ptrs); - nilfs_btree_node_insert(btree, node, - key, dreq->bpr_ptr, n); - if (!nilfs_bmap_dirty(bmap)) - nilfs_bmap_set_dirty(bmap); + nilfs_btree_node_insert(btree, node, key, dreq->bpr_ptr, n); + if (!nilfs_bmap_dirty(btree)) + nilfs_bmap_set_dirty(btree); } - if (NILFS_BMAP_USE_VBN(bmap)) + if (NILFS_BMAP_USE_VBN(btree)) nilfs_btree_set_target_v(btree, key, dreq->bpr_ptr); } @@ -1702,7 +1674,7 @@ nilfs_btree_commit_convert_and_insert(struct nilfs_bmap *bmap, * @ptrs: * @n: */ -int nilfs_btree_convert_and_insert(struct nilfs_bmap *bmap, +int nilfs_btree_convert_and_insert(struct nilfs_bmap *btree, __u64 key, __u64 ptr, const __u64 *keys, const __u64 *ptrs, int n) { @@ -1715,7 +1687,7 @@ int nilfs_btree_convert_and_insert(struct nilfs_bmap *bmap, di = &dreq; ni = NULL; } else if ((n + 1) <= NILFS_BTREE_NODE_NCHILDREN_MAX( - 1 << bmap->b_inode->i_blkbits)) { + 1 << btree->b_inode->i_blkbits)) { di = &dreq; ni = &nreq; } else { @@ -1724,17 +1696,17 @@ int nilfs_btree_convert_and_insert(struct nilfs_bmap *bmap, BUG(); } - ret = nilfs_btree_prepare_convert_and_insert(bmap, key, di, ni, &bh, + ret = nilfs_btree_prepare_convert_and_insert(btree, key, di, ni, &bh, &stats); if (ret < 0) return ret; - nilfs_btree_commit_convert_and_insert(bmap, key, ptr, keys, ptrs, n, + nilfs_btree_commit_convert_and_insert(btree, key, ptr, keys, ptrs, n, di, ni, bh); - nilfs_bmap_add_blocks(bmap, stats.bs_nblocks); + nilfs_bmap_add_blocks(btree, stats.bs_nblocks); return 0; } -static int nilfs_btree_propagate_p(struct nilfs_btree *btree, +static int nilfs_btree_propagate_p(struct nilfs_bmap *btree, struct nilfs_btree_path *path, int level, struct buffer_head *bh) @@ -1746,7 +1718,7 @@ static int nilfs_btree_propagate_p(struct nilfs_btree *btree, return 0; } -static int nilfs_btree_prepare_update_v(struct nilfs_btree *btree, +static int nilfs_btree_prepare_update_v(struct nilfs_bmap *btree, struct nilfs_btree_path *path, int level, struct inode *dat) { @@ -1768,7 +1740,7 @@ static int nilfs_btree_prepare_update_v(struct nilfs_btree *btree, path[level].bp_ctxt.newkey = path[level].bp_newreq.bpr_ptr; path[level].bp_ctxt.bh = path[level].bp_bh; ret = nilfs_btnode_prepare_change_key( - &NILFS_BMAP_I(&btree->bt_bmap)->i_btnode_cache, + &NILFS_BMAP_I(btree)->i_btnode_cache, &path[level].bp_ctxt); if (ret < 0) { nilfs_dat_abort_update(dat, @@ -1781,7 +1753,7 @@ static int nilfs_btree_prepare_update_v(struct nilfs_btree *btree, return 0; } -static void nilfs_btree_commit_update_v(struct nilfs_btree *btree, +static void nilfs_btree_commit_update_v(struct nilfs_bmap *btree, struct nilfs_btree_path *path, int level, struct inode *dat) { @@ -1789,11 +1761,11 @@ static void nilfs_btree_commit_update_v(struct nilfs_btree *btree, nilfs_dat_commit_update(dat, &path[level].bp_oldreq.bpr_req, &path[level].bp_newreq.bpr_req, - btree->bt_bmap.b_ptr_type == NILFS_BMAP_PTR_VS); + btree->b_ptr_type == NILFS_BMAP_PTR_VS); if (buffer_nilfs_node(path[level].bp_bh)) { nilfs_btnode_commit_change_key( - &NILFS_BMAP_I(&btree->bt_bmap)->i_btnode_cache, + &NILFS_BMAP_I(btree)->i_btnode_cache, &path[level].bp_ctxt); path[level].bp_bh = path[level].bp_ctxt.bh; } @@ -1804,7 +1776,7 @@ static void nilfs_btree_commit_update_v(struct nilfs_btree *btree, path[level].bp_newreq.bpr_ptr); } -static void nilfs_btree_abort_update_v(struct nilfs_btree *btree, +static void nilfs_btree_abort_update_v(struct nilfs_bmap *btree, struct nilfs_btree_path *path, int level, struct inode *dat) { @@ -1812,11 +1784,11 @@ static void nilfs_btree_abort_update_v(struct nilfs_btree *btree, &path[level].bp_newreq.bpr_req); if (buffer_nilfs_node(path[level].bp_bh)) nilfs_btnode_abort_change_key( - &NILFS_BMAP_I(&btree->bt_bmap)->i_btnode_cache, + &NILFS_BMAP_I(btree)->i_btnode_cache, &path[level].bp_ctxt); } -static int nilfs_btree_prepare_propagate_v(struct nilfs_btree *btree, +static int nilfs_btree_prepare_propagate_v(struct nilfs_bmap *btree, struct nilfs_btree_path *path, int minlevel, int *maxlevelp, struct inode *dat) @@ -1851,7 +1823,7 @@ static int nilfs_btree_prepare_propagate_v(struct nilfs_btree *btree, return ret; } -static void nilfs_btree_commit_propagate_v(struct nilfs_btree *btree, +static void nilfs_btree_commit_propagate_v(struct nilfs_bmap *btree, struct nilfs_btree_path *path, int minlevel, int maxlevel, struct buffer_head *bh, @@ -1866,13 +1838,13 @@ static void nilfs_btree_commit_propagate_v(struct nilfs_btree *btree, nilfs_btree_commit_update_v(btree, path, level, dat); } -static int nilfs_btree_propagate_v(struct nilfs_btree *btree, +static int nilfs_btree_propagate_v(struct nilfs_bmap *btree, struct nilfs_btree_path *path, int level, struct buffer_head *bh) { int maxlevel = 0, ret; struct nilfs_btree_node *parent; - struct inode *dat = nilfs_bmap_get_dat(&btree->bt_bmap); + struct inode *dat = nilfs_bmap_get_dat(btree); __u64 ptr; get_bh(bh); @@ -1899,10 +1871,9 @@ static int nilfs_btree_propagate_v(struct nilfs_btree *btree, return ret; } -static int nilfs_btree_propagate(struct nilfs_bmap *bmap, +static int nilfs_btree_propagate(struct nilfs_bmap *btree, struct buffer_head *bh) { - struct nilfs_btree *btree; struct nilfs_btree_path *path; struct nilfs_btree_node *node; __u64 key; @@ -1910,7 +1881,6 @@ static int nilfs_btree_propagate(struct nilfs_bmap *bmap, WARN_ON(!buffer_dirty(bh)); - btree = (struct nilfs_btree *)bmap; path = nilfs_btree_alloc_path(); if (path == NULL) return -ENOMEM; @@ -1920,7 +1890,7 @@ static int nilfs_btree_propagate(struct nilfs_bmap *bmap, key = nilfs_btree_node_get_key(node, 0); level = nilfs_btree_node_get_level(node); } else { - key = nilfs_bmap_data_get_key(bmap, bh); + key = nilfs_bmap_data_get_key(btree, bh); level = NILFS_BTREE_LEVEL_DATA; } @@ -1932,7 +1902,7 @@ static int nilfs_btree_propagate(struct nilfs_bmap *bmap, goto out; } - ret = NILFS_BMAP_USE_VBN(bmap) ? + ret = NILFS_BMAP_USE_VBN(btree) ? nilfs_btree_propagate_v(btree, path, level, bh) : nilfs_btree_propagate_p(btree, path, level, bh); @@ -1942,13 +1912,13 @@ static int nilfs_btree_propagate(struct nilfs_bmap *bmap, return ret; } -static int nilfs_btree_propagate_gc(struct nilfs_bmap *bmap, +static int nilfs_btree_propagate_gc(struct nilfs_bmap *btree, struct buffer_head *bh) { - return nilfs_dat_mark_dirty(nilfs_bmap_get_dat(bmap), bh->b_blocknr); + return nilfs_dat_mark_dirty(nilfs_bmap_get_dat(btree), bh->b_blocknr); } -static void nilfs_btree_add_dirty_buffer(struct nilfs_btree *btree, +static void nilfs_btree_add_dirty_buffer(struct nilfs_bmap *btree, struct list_head *lists, struct buffer_head *bh) { @@ -1969,7 +1939,7 @@ static void nilfs_btree_add_dirty_buffer(struct nilfs_btree *btree, "%s: invalid btree level: %d (key=%llu, ino=%lu, " "blocknr=%llu)\n", __func__, level, (unsigned long long)key, - NILFS_BMAP_I(&btree->bt_bmap)->vfs_inode.i_ino, + NILFS_BMAP_I(btree)->vfs_inode.i_ino, (unsigned long long)bh->b_blocknr); return; } @@ -1984,11 +1954,10 @@ static void nilfs_btree_add_dirty_buffer(struct nilfs_btree *btree, list_add_tail(&bh->b_assoc_buffers, head); } -static void nilfs_btree_lookup_dirty_buffers(struct nilfs_bmap *bmap, +static void nilfs_btree_lookup_dirty_buffers(struct nilfs_bmap *btree, struct list_head *listp) { - struct nilfs_btree *btree = (struct nilfs_btree *)bmap; - struct address_space *btcache = &NILFS_BMAP_I(bmap)->i_btnode_cache; + struct address_space *btcache = &NILFS_BMAP_I(btree)->i_btnode_cache; struct list_head lists[NILFS_BTREE_LEVEL_MAX]; struct pagevec pvec; struct buffer_head *bh, *head; @@ -2022,7 +1991,7 @@ static void nilfs_btree_lookup_dirty_buffers(struct nilfs_bmap *bmap, list_splice_tail(&lists[level], listp); } -static int nilfs_btree_assign_p(struct nilfs_btree *btree, +static int nilfs_btree_assign_p(struct nilfs_bmap *btree, struct nilfs_btree_path *path, int level, struct buffer_head **bh, @@ -2042,12 +2011,12 @@ static int nilfs_btree_assign_p(struct nilfs_btree *btree, path[level].bp_ctxt.newkey = blocknr; path[level].bp_ctxt.bh = *bh; ret = nilfs_btnode_prepare_change_key( - &NILFS_BMAP_I(&btree->bt_bmap)->i_btnode_cache, + &NILFS_BMAP_I(btree)->i_btnode_cache, &path[level].bp_ctxt); if (ret < 0) return ret; nilfs_btnode_commit_change_key( - &NILFS_BMAP_I(&btree->bt_bmap)->i_btnode_cache, + &NILFS_BMAP_I(btree)->i_btnode_cache, &path[level].bp_ctxt); *bh = path[level].bp_ctxt.bh; } @@ -2063,7 +2032,7 @@ static int nilfs_btree_assign_p(struct nilfs_btree *btree, return 0; } -static int nilfs_btree_assign_v(struct nilfs_btree *btree, +static int nilfs_btree_assign_v(struct nilfs_bmap *btree, struct nilfs_btree_path *path, int level, struct buffer_head **bh, @@ -2071,15 +2040,14 @@ static int nilfs_btree_assign_v(struct nilfs_btree *btree, union nilfs_binfo *binfo) { struct nilfs_btree_node *parent; - struct inode *dat = nilfs_bmap_get_dat(&btree->bt_bmap); + struct inode *dat = nilfs_bmap_get_dat(btree); __u64 key; __u64 ptr; union nilfs_bmap_ptr_req req; int ret; parent = nilfs_btree_get_node(btree, path, level + 1); - ptr = nilfs_btree_node_get_ptr(btree, parent, - path[level + 1].bp_index); + ptr = nilfs_btree_node_get_ptr(btree, parent, path[level + 1].bp_index); req.bpr_ptr = ptr; ret = nilfs_dat_prepare_start(dat, &req.bpr_req); if (ret < 0) @@ -2094,18 +2062,16 @@ static int nilfs_btree_assign_v(struct nilfs_btree *btree, return 0; } -static int nilfs_btree_assign(struct nilfs_bmap *bmap, +static int nilfs_btree_assign(struct nilfs_bmap *btree, struct buffer_head **bh, sector_t blocknr, union nilfs_binfo *binfo) { - struct nilfs_btree *btree; struct nilfs_btree_path *path; struct nilfs_btree_node *node; __u64 key; int level, ret; - btree = (struct nilfs_btree *)bmap; path = nilfs_btree_alloc_path(); if (path == NULL) return -ENOMEM; @@ -2115,7 +2081,7 @@ static int nilfs_btree_assign(struct nilfs_bmap *bmap, key = nilfs_btree_node_get_key(node, 0); level = nilfs_btree_node_get_level(node); } else { - key = nilfs_bmap_data_get_key(bmap, *bh); + key = nilfs_bmap_data_get_key(btree, *bh); level = NILFS_BTREE_LEVEL_DATA; } @@ -2125,7 +2091,7 @@ static int nilfs_btree_assign(struct nilfs_bmap *bmap, goto out; } - ret = NILFS_BMAP_USE_VBN(bmap) ? + ret = NILFS_BMAP_USE_VBN(btree) ? nilfs_btree_assign_v(btree, path, level, bh, blocknr, binfo) : nilfs_btree_assign_p(btree, path, level, bh, blocknr, binfo); @@ -2135,7 +2101,7 @@ static int nilfs_btree_assign(struct nilfs_bmap *bmap, return ret; } -static int nilfs_btree_assign_gc(struct nilfs_bmap *bmap, +static int nilfs_btree_assign_gc(struct nilfs_bmap *btree, struct buffer_head **bh, sector_t blocknr, union nilfs_binfo *binfo) @@ -2144,7 +2110,7 @@ static int nilfs_btree_assign_gc(struct nilfs_bmap *bmap, __u64 key; int ret; - ret = nilfs_dat_move(nilfs_bmap_get_dat(bmap), (*bh)->b_blocknr, + ret = nilfs_dat_move(nilfs_bmap_get_dat(btree), (*bh)->b_blocknr, blocknr); if (ret < 0) return ret; @@ -2153,7 +2119,7 @@ static int nilfs_btree_assign_gc(struct nilfs_bmap *bmap, node = (struct nilfs_btree_node *)(*bh)->b_data; key = nilfs_btree_node_get_key(node, 0); } else - key = nilfs_bmap_data_get_key(bmap, *bh); + key = nilfs_bmap_data_get_key(btree, *bh); /* on-disk format */ binfo->bi_v.bi_vblocknr = cpu_to_le64((*bh)->b_blocknr); @@ -2162,15 +2128,13 @@ static int nilfs_btree_assign_gc(struct nilfs_bmap *bmap, return 0; } -static int nilfs_btree_mark(struct nilfs_bmap *bmap, __u64 key, int level) +static int nilfs_btree_mark(struct nilfs_bmap *btree, __u64 key, int level) { struct buffer_head *bh; - struct nilfs_btree *btree; struct nilfs_btree_path *path; __u64 ptr; int ret; - btree = (struct nilfs_btree *)bmap; path = nilfs_btree_alloc_path(); if (path == NULL) return -ENOMEM; @@ -2189,8 +2153,8 @@ static int nilfs_btree_mark(struct nilfs_bmap *bmap, __u64 key, int level) if (!buffer_dirty(bh)) nilfs_btnode_mark_dirty(bh); brelse(bh); - if (!nilfs_bmap_dirty(&btree->bt_bmap)) - nilfs_bmap_set_dirty(&btree->bt_bmap); + if (!nilfs_bmap_dirty(btree)) + nilfs_bmap_set_dirty(btree); out: nilfs_btree_free_path(path); diff --git a/fs/nilfs2/btree.h b/fs/nilfs2/btree.h index 980e1e8ec53a..cffbfbad0a6b 100644 --- a/fs/nilfs2/btree.h +++ b/fs/nilfs2/btree.h @@ -54,7 +54,7 @@ struct nilfs_btree_path { union nilfs_bmap_ptr_req bp_oldreq; union nilfs_bmap_ptr_req bp_newreq; struct nilfs_btnode_chkey_ctxt bp_ctxt; - void (*bp_op)(struct nilfs_btree *, struct nilfs_btree_path *, + void (*bp_op)(struct nilfs_bmap *, struct nilfs_btree_path *, int, __u64 *, __u64 *); }; -- cgit v1.2.3 From dc935be2a094087bc561d80f8cf9e66bbc1f7b18 Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Sat, 10 Jul 2010 22:21:54 +0900 Subject: nilfs2: unify bmap set_target_v operations This unifies two similar functions nilfs_btree_set_target_v and nilfs_direct_set_target_v into one, nilfs_bmap_set_target_v. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/bmap.h | 7 +++++++ fs/nilfs2/btree.c | 11 ++--------- fs/nilfs2/direct.c | 9 +-------- 3 files changed, 10 insertions(+), 17 deletions(-) (limited to 'fs') diff --git a/fs/nilfs2/bmap.h b/fs/nilfs2/bmap.h index 379fda4c668c..fae83cf9c009 100644 --- a/fs/nilfs2/bmap.h +++ b/fs/nilfs2/bmap.h @@ -219,6 +219,13 @@ static inline void nilfs_bmap_abort_end_ptr(struct nilfs_bmap *bmap, nilfs_dat_abort_end(dat, &req->bpr_req); } +static inline void nilfs_bmap_set_target_v(struct nilfs_bmap *bmap, __u64 key, + __u64 ptr) +{ + bmap->b_last_allocated_key = key; + bmap->b_last_allocated_ptr = ptr; +} + __u64 nilfs_bmap_data_get_key(const struct nilfs_bmap *, const struct buffer_head *); diff --git a/fs/nilfs2/btree.c b/fs/nilfs2/btree.c index 81e871645b5f..0543bf9f80ba 100644 --- a/fs/nilfs2/btree.c +++ b/fs/nilfs2/btree.c @@ -917,13 +917,6 @@ static __u64 nilfs_btree_find_target_v(const struct nilfs_bmap *btree, return nilfs_bmap_find_target_in_group(btree); } -static void nilfs_btree_set_target_v(struct nilfs_bmap *btree, __u64 key, - __u64 ptr) -{ - btree->b_last_allocated_key = key; - btree->b_last_allocated_ptr = ptr; -} - static int nilfs_btree_prepare_insert(struct nilfs_bmap *btree, struct nilfs_btree_path *path, int *levelp, __u64 key, __u64 ptr, @@ -1084,7 +1077,7 @@ static void nilfs_btree_commit_insert(struct nilfs_bmap *btree, set_buffer_nilfs_volatile((struct buffer_head *)((unsigned long)ptr)); ptr = path[NILFS_BTREE_LEVEL_DATA].bp_newreq.bpr_ptr; if (NILFS_BMAP_USE_VBN(btree)) { - nilfs_btree_set_target_v(btree, key, ptr); + nilfs_bmap_set_target_v(btree, key, ptr); dat = nilfs_bmap_get_dat(btree); } @@ -1662,7 +1655,7 @@ nilfs_btree_commit_convert_and_insert(struct nilfs_bmap *btree, } if (NILFS_BMAP_USE_VBN(btree)) - nilfs_btree_set_target_v(btree, key, dreq->bpr_ptr); + nilfs_bmap_set_target_v(btree, key, dreq->bpr_ptr); } /** diff --git a/fs/nilfs2/direct.c b/fs/nilfs2/direct.c index cfc7218914d6..318613010106 100644 --- a/fs/nilfs2/direct.c +++ b/fs/nilfs2/direct.c @@ -116,13 +116,6 @@ nilfs_direct_find_target_v(const struct nilfs_bmap *direct, __u64 key) return nilfs_bmap_find_target_in_group(direct); } -static void nilfs_direct_set_target_v(struct nilfs_bmap *direct, - __u64 key, __u64 ptr) -{ - direct->b_last_allocated_key = key; - direct->b_last_allocated_ptr = ptr; -} - static int nilfs_direct_insert(struct nilfs_bmap *bmap, __u64 key, __u64 ptr) { union nilfs_bmap_ptr_req req; @@ -152,7 +145,7 @@ static int nilfs_direct_insert(struct nilfs_bmap *bmap, __u64 key, __u64 ptr) nilfs_bmap_set_dirty(bmap); if (NILFS_BMAP_USE_VBN(bmap)) - nilfs_direct_set_target_v(bmap, key, req.bpr_ptr); + nilfs_bmap_set_target_v(bmap, key, req.bpr_ptr); nilfs_bmap_add_blocks(bmap, 1); } -- cgit v1.2.3 From 05d0e94b66dbdf9d90371b39dc7a6b390ba74d41 Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Sat, 10 Jul 2010 20:52:09 +0900 Subject: nilfs2: get rid of nilfs_bmap_union This removes nilfs_bmap_union and finally unifies three structures and the union in bmap/btree code into one. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/bmap.c | 6 ++++-- fs/nilfs2/bmap_union.h | 42 ------------------------------------------ fs/nilfs2/btree.h | 8 -------- fs/nilfs2/direct.h | 11 ----------- fs/nilfs2/gcinode.c | 2 ++ fs/nilfs2/mdt.c | 1 + fs/nilfs2/nilfs.h | 7 ++----- fs/nilfs2/super.c | 4 +++- 8 files changed, 12 insertions(+), 69 deletions(-) delete mode 100644 fs/nilfs2/bmap_union.h (limited to 'fs') diff --git a/fs/nilfs2/bmap.c b/fs/nilfs2/bmap.c index effdbdbe6c11..3dbdc1d356bf 100644 --- a/fs/nilfs2/bmap.c +++ b/fs/nilfs2/bmap.c @@ -26,6 +26,8 @@ #include "nilfs.h" #include "bmap.h" #include "sb.h" +#include "btree.h" +#include "direct.h" #include "btnode.h" #include "mdt.h" #include "dat.h" @@ -533,7 +535,7 @@ void nilfs_bmap_init_gc(struct nilfs_bmap *bmap) void nilfs_bmap_init_gcdat(struct nilfs_bmap *gcbmap, struct nilfs_bmap *bmap) { - memcpy(gcbmap, bmap, sizeof(union nilfs_bmap_union)); + memcpy(gcbmap, bmap, sizeof(*bmap)); init_rwsem(&gcbmap->b_sem); lockdep_set_class(&bmap->b_sem, &nilfs_bmap_dat_lock_key); gcbmap->b_inode = &NILFS_BMAP_I(gcbmap)->vfs_inode; @@ -541,7 +543,7 @@ void nilfs_bmap_init_gcdat(struct nilfs_bmap *gcbmap, struct nilfs_bmap *bmap) void nilfs_bmap_commit_gcdat(struct nilfs_bmap *gcbmap, struct nilfs_bmap *bmap) { - memcpy(bmap, gcbmap, sizeof(union nilfs_bmap_union)); + memcpy(bmap, gcbmap, sizeof(*bmap)); init_rwsem(&bmap->b_sem); lockdep_set_class(&bmap->b_sem, &nilfs_bmap_dat_lock_key); bmap->b_inode = &NILFS_BMAP_I(bmap)->vfs_inode; diff --git a/fs/nilfs2/bmap_union.h b/fs/nilfs2/bmap_union.h deleted file mode 100644 index d41509bff47b..000000000000 --- a/fs/nilfs2/bmap_union.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * bmap_union.h - NILFS block mapping. - * - * Copyright (C) 2006-2008 Nippon Telegraph and Telephone Corporation. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Written by Koji Sato . - */ - -#ifndef _NILFS_BMAP_UNION_H -#define _NILFS_BMAP_UNION_H - -#include "bmap.h" -#include "direct.h" -#include "btree.h" - -/** - * nilfs_bmap_union - - * @bi_bmap: bmap structure - * @bi_btree: direct map structure - * @bi_direct: B-tree structure - */ -union nilfs_bmap_union { - struct nilfs_bmap bi_bmap; - struct nilfs_direct bi_direct; - struct nilfs_btree bi_btree; -}; - -#endif /* _NILFS_BMAP_UNION_H */ diff --git a/fs/nilfs2/btree.h b/fs/nilfs2/btree.h index cffbfbad0a6b..22c02e35b6ef 100644 --- a/fs/nilfs2/btree.h +++ b/fs/nilfs2/btree.h @@ -30,14 +30,6 @@ #include "btnode.h" #include "bmap.h" -/** - * struct nilfs_btree - B-tree structure - * @bt_bmap: bmap base structure - */ -struct nilfs_btree { - struct nilfs_bmap bt_bmap; -}; - /** * struct nilfs_btree_path - A path on which B-tree operations are executed * @bp_bh: buffer head of node block diff --git a/fs/nilfs2/direct.h b/fs/nilfs2/direct.h index a5ffd66e25d0..dc643de20a25 100644 --- a/fs/nilfs2/direct.h +++ b/fs/nilfs2/direct.h @@ -28,8 +28,6 @@ #include "bmap.h" -struct nilfs_direct; - /** * struct nilfs_direct_node - direct node * @dn_flags: flags @@ -40,15 +38,6 @@ struct nilfs_direct_node { __u8 pad[7]; }; -/** - * struct nilfs_direct - direct mapping - * @d_bmap: bmap structure - */ -struct nilfs_direct { - struct nilfs_bmap d_bmap; -}; - - #define NILFS_DIRECT_NBLOCKS (NILFS_BMAP_SIZE / sizeof(__le64) - 1) #define NILFS_DIRECT_KEY_MIN 0 #define NILFS_DIRECT_KEY_MAX (NILFS_DIRECT_NBLOCKS - 1) diff --git a/fs/nilfs2/gcinode.c b/fs/nilfs2/gcinode.c index edb53fcb7f83..b6343825f91a 100644 --- a/fs/nilfs2/gcinode.c +++ b/fs/nilfs2/gcinode.c @@ -48,6 +48,8 @@ #include #include #include "nilfs.h" +#include "btree.h" +#include "btnode.h" #include "page.h" #include "mdt.h" #include "dat.h" diff --git a/fs/nilfs2/mdt.c b/fs/nilfs2/mdt.c index 024be8c35bb6..d01aff4957d9 100644 --- a/fs/nilfs2/mdt.c +++ b/fs/nilfs2/mdt.c @@ -28,6 +28,7 @@ #include #include #include "nilfs.h" +#include "btnode.h" #include "segment.h" #include "page.h" #include "mdt.h" diff --git a/fs/nilfs2/nilfs.h b/fs/nilfs2/nilfs.h index 36998eaab02f..cfedc48d78d9 100644 --- a/fs/nilfs2/nilfs.h +++ b/fs/nilfs2/nilfs.h @@ -32,7 +32,6 @@ #include "the_nilfs.h" #include "sb.h" #include "bmap.h" -#include "bmap_union.h" /* * nilfs inode data in memory @@ -41,7 +40,7 @@ struct nilfs_inode_info { __u32 i_flags; unsigned long i_state; /* Dynamic state flags */ struct nilfs_bmap *i_bmap; - union nilfs_bmap_union i_bmap_union; + struct nilfs_bmap i_bmap_data; __u64 i_xattr; /* sector_t ??? */ __u32 i_dir_start_lookup; __u64 i_cno; /* check point number for GC inode */ @@ -71,9 +70,7 @@ static inline struct nilfs_inode_info *NILFS_I(const struct inode *inode) static inline struct nilfs_inode_info * NILFS_BMAP_I(const struct nilfs_bmap *bmap) { - return container_of((union nilfs_bmap_union *)bmap, - struct nilfs_inode_info, - i_bmap_union); + return container_of(bmap, struct nilfs_inode_info, i_bmap_data); } static inline struct inode *NILFS_BTNC_I(struct address_space *btnc) diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c index 952f4ccb18de..164457316df1 100644 --- a/fs/nilfs2/super.c +++ b/fs/nilfs2/super.c @@ -55,6 +55,8 @@ #include "nilfs.h" #include "mdt.h" #include "alloc.h" +#include "btree.h" +#include "btnode.h" #include "page.h" #include "cpfile.h" #include "ifile.h" @@ -1213,7 +1215,7 @@ static void nilfs_inode_init_once(void *obj) init_rwsem(&ii->xattr_sem); #endif nilfs_btnode_cache_init_once(&ii->i_btnode_cache); - ii->i_bmap = (struct nilfs_bmap *)&ii->i_bmap_union; + ii->i_bmap = &ii->i_bmap_data; inode_init_once(&ii->vfs_inode); } -- cgit v1.2.3 From 364ec2d700223b965620ff4d5031a3665d195873 Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Tue, 13 Jul 2010 23:33:51 +0900 Subject: nilfs2: remove redundant pointer checks in bmap lookup functions nilfs_bmap_lookup and its variants are supposed to take a valid pointer argument to return a block address, thus pointer checks in nilfs_btree_lookup and nilfs_direct_lookup are needless. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/btree.c | 6 +----- fs/nilfs2/direct.c | 3 +-- 2 files changed, 2 insertions(+), 7 deletions(-) (limited to 'fs') diff --git a/fs/nilfs2/btree.c b/fs/nilfs2/btree.c index 0543bf9f80ba..18bb965c66b5 100644 --- a/fs/nilfs2/btree.c +++ b/fs/nilfs2/btree.c @@ -555,17 +555,13 @@ static int nilfs_btree_lookup(const struct nilfs_bmap *btree, __u64 key, int level, __u64 *ptrp) { struct nilfs_btree_path *path; - __u64 ptr; int ret; path = nilfs_btree_alloc_path(); if (path == NULL) return -ENOMEM; - ret = nilfs_btree_do_lookup(btree, path, key, &ptr, level); - - if (ptrp != NULL) - *ptrp = ptr; + ret = nilfs_btree_do_lookup(btree, path, key, ptrp, level); nilfs_btree_free_path(path); diff --git a/fs/nilfs2/direct.c b/fs/nilfs2/direct.c index 318613010106..324d80c57518 100644 --- a/fs/nilfs2/direct.c +++ b/fs/nilfs2/direct.c @@ -56,8 +56,7 @@ static int nilfs_direct_lookup(const struct nilfs_bmap *direct, if (ptr == NILFS_BMAP_INVALID_PTR) return -ENOENT; - if (ptrp != NULL) - *ptrp = ptr; + *ptrp = ptr; return 0; } -- cgit v1.2.3 From ea64ab87cdba9e1172392d247e6526359e301f12 Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Tue, 13 Jul 2010 23:33:52 +0900 Subject: nilfs2: optimize calculation of min/max number of btree node children nilfs_btree_node_nchildren_max() and nilfs_btree_node_nchildren_min() functions switch return value depending on whether target node is the root or a node block. In most uses of these functions, however, the node type is fixed, and moreover the same calculation is repeatedly performed in loop. This unfold these functions depending on context and move them outside loops wherever possible. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/btree.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) (limited to 'fs') diff --git a/fs/nilfs2/btree.c b/fs/nilfs2/btree.c index 18bb965c66b5..c0266f7f0b26 100644 --- a/fs/nilfs2/btree.c +++ b/fs/nilfs2/btree.c @@ -473,7 +473,7 @@ static int nilfs_btree_do_lookup(const struct nilfs_bmap *btree, { struct nilfs_btree_node *node; __u64 ptr; - int level, index, found, ret; + int level, index, found, ncmax, ret; node = nilfs_btree_get_root(btree); level = nilfs_btree_node_get_level(node); @@ -485,6 +485,8 @@ static int nilfs_btree_do_lookup(const struct nilfs_bmap *btree, path[level].bp_bh = NULL; path[level].bp_index = index; + ncmax = NILFS_BTREE_NODE_NCHILDREN_MAX(nilfs_btree_node_size(btree)); + for (level--; level >= minlevel; level--) { ret = nilfs_btree_get_block(btree, ptr, &path[level].bp_bh); if (ret < 0) @@ -496,9 +498,9 @@ static int nilfs_btree_do_lookup(const struct nilfs_bmap *btree, found = nilfs_btree_node_lookup(node, key, &index); else index = 0; - if (index < nilfs_btree_node_nchildren_max(node, btree)) + if (index < ncmax) { ptr = nilfs_btree_node_get_ptr(btree, node, index); - else { + } else { WARN_ON(found || level != NILFS_BTREE_LEVEL_NODE_MIN); /* insert */ ptr = NILFS_BMAP_INVALID_PTR; @@ -921,7 +923,7 @@ static int nilfs_btree_prepare_insert(struct nilfs_bmap *btree, struct buffer_head *bh; struct nilfs_btree_node *node, *parent, *sib; __u64 sibptr; - int pindex, level, ret; + int pindex, level, ncmax, ret; struct inode *dat = NULL; stats->bs_nblocks = 0; @@ -938,12 +940,13 @@ static int nilfs_btree_prepare_insert(struct nilfs_bmap *btree, if (ret < 0) goto err_out_data; + ncmax = NILFS_BTREE_NODE_NCHILDREN_MAX(nilfs_btree_node_size(btree)); + for (level = NILFS_BTREE_LEVEL_NODE_MIN; level < nilfs_btree_height(btree) - 1; level++) { node = nilfs_btree_get_nonroot_node(path, level); - if (nilfs_btree_node_get_nchildren(node) < - nilfs_btree_node_nchildren_max(node, btree)) { + if (nilfs_btree_node_get_nchildren(node) < ncmax) { path[level].bp_op = nilfs_btree_do_insert; stats->bs_nblocks++; goto out; @@ -960,8 +963,7 @@ static int nilfs_btree_prepare_insert(struct nilfs_bmap *btree, if (ret < 0) goto err_out_child_node; sib = (struct nilfs_btree_node *)bh->b_data; - if (nilfs_btree_node_get_nchildren(sib) < - nilfs_btree_node_nchildren_max(sib, btree)) { + if (nilfs_btree_node_get_nchildren(sib) < ncmax) { path[level].bp_sib_bh = bh; path[level].bp_op = nilfs_btree_carry_left; stats->bs_nblocks++; @@ -979,8 +981,7 @@ static int nilfs_btree_prepare_insert(struct nilfs_bmap *btree, if (ret < 0) goto err_out_child_node; sib = (struct nilfs_btree_node *)bh->b_data; - if (nilfs_btree_node_get_nchildren(sib) < - nilfs_btree_node_nchildren_max(sib, btree)) { + if (nilfs_btree_node_get_nchildren(sib) < ncmax) { path[level].bp_sib_bh = bh; path[level].bp_op = nilfs_btree_carry_right; stats->bs_nblocks++; @@ -1014,7 +1015,7 @@ static int nilfs_btree_prepare_insert(struct nilfs_bmap *btree, /* root */ node = nilfs_btree_get_root(btree); if (nilfs_btree_node_get_nchildren(node) < - nilfs_btree_node_nchildren_max(node, btree)) { + NILFS_BTREE_ROOT_NCHILDREN_MAX) { path[level].bp_op = nilfs_btree_do_insert; stats->bs_nblocks++; goto out; @@ -1281,10 +1282,12 @@ static int nilfs_btree_prepare_delete(struct nilfs_bmap *btree, struct buffer_head *bh; struct nilfs_btree_node *node, *parent, *sib; __u64 sibptr; - int pindex, level, ret; + int pindex, level, ncmin, ret; ret = 0; stats->bs_nblocks = 0; + ncmin = NILFS_BTREE_NODE_NCHILDREN_MIN(nilfs_btree_node_size(btree)); + for (level = NILFS_BTREE_LEVEL_NODE_MIN; level < nilfs_btree_height(btree) - 1; level++) { @@ -1297,8 +1300,7 @@ static int nilfs_btree_prepare_delete(struct nilfs_bmap *btree, if (ret < 0) goto err_out_child_node; - if (nilfs_btree_node_get_nchildren(node) > - nilfs_btree_node_nchildren_min(node, btree)) { + if (nilfs_btree_node_get_nchildren(node) > ncmin) { path[level].bp_op = nilfs_btree_do_delete; stats->bs_nblocks++; goto out; @@ -1315,8 +1317,7 @@ static int nilfs_btree_prepare_delete(struct nilfs_bmap *btree, if (ret < 0) goto err_out_curr_node; sib = (struct nilfs_btree_node *)bh->b_data; - if (nilfs_btree_node_get_nchildren(sib) > - nilfs_btree_node_nchildren_min(sib, btree)) { + if (nilfs_btree_node_get_nchildren(sib) > ncmin) { path[level].bp_sib_bh = bh; path[level].bp_op = nilfs_btree_borrow_left; stats->bs_nblocks++; @@ -1336,8 +1337,7 @@ static int nilfs_btree_prepare_delete(struct nilfs_bmap *btree, if (ret < 0) goto err_out_curr_node; sib = (struct nilfs_btree_node *)bh->b_data; - if (nilfs_btree_node_get_nchildren(sib) > - nilfs_btree_node_nchildren_min(sib, btree)) { + if (nilfs_btree_node_get_nchildren(sib) > ncmin) { path[level].bp_sib_bh = bh; path[level].bp_op = nilfs_btree_borrow_right; stats->bs_nblocks++; -- cgit v1.2.3 From 9b7b265c9ab67fcd1245d6b64fa5ca2eda43ac88 Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Tue, 13 Jul 2010 23:33:53 +0900 Subject: nilfs2: reduce repetitive calculation of max number of child nodes The current btree implementation repeats the same calculation on the maximum number of child nodes. This is because a few low level routines use the calculation for index addressing in a btree node block. This reduces the calculation by explicitly passing the maximum number of child nodes (ncmax) through their argument. This changes parameter passing of the following functions: - nilfs_btree_node_dptrs - nilfs_btree_node_get_ptr - nilfs_btree_node_set_ptr - nilfs_btree_node_init - nilfs_btree_node_move_left - nilfs_btree_node_move_right - nilfs_btree_node_insert - nilfs_btree_node_delete, and - nilfs_btree_get_node The following functions are removed: - nilfs_btree_node_nchildren_min - nilfs_btree_node_nchildren_max Most middle level btree operations are rewritten to pass a proper ncmax value depending on whether each occurrence of node is "root" or not. A constant NILFS_BTREE_ROOT_NCHILDREN_MAX is used for the root node, whereas nilfs_btree_nchildren_per_block() function is used for non-root nodes. If a node could be either root or a non-root node, an output argument of nilfs_btree_get_node() is used to set up ncmax. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/btree.c | 339 +++++++++++++++++++++++++++++------------------------- 1 file changed, 182 insertions(+), 157 deletions(-) (limited to 'fs') diff --git a/fs/nilfs2/btree.c b/fs/nilfs2/btree.c index c0266f7f0b26..829e145f1353 100644 --- a/fs/nilfs2/btree.c +++ b/fs/nilfs2/btree.c @@ -152,22 +152,9 @@ static inline int nilfs_btree_node_size(const struct nilfs_bmap *btree) return 1 << btree->b_inode->i_blkbits; } -static inline int -nilfs_btree_node_nchildren_min(const struct nilfs_btree_node *node, - const struct nilfs_bmap *btree) -{ - return nilfs_btree_node_root(node) ? - NILFS_BTREE_ROOT_NCHILDREN_MIN : - NILFS_BTREE_NODE_NCHILDREN_MIN(nilfs_btree_node_size(btree)); -} - -static inline int -nilfs_btree_node_nchildren_max(const struct nilfs_btree_node *node, - const struct nilfs_bmap *btree) +static int nilfs_btree_nchildren_per_block(const struct nilfs_bmap *btree) { - return nilfs_btree_node_root(node) ? - NILFS_BTREE_ROOT_NCHILDREN_MAX : - NILFS_BTREE_NODE_NCHILDREN_MAX(nilfs_btree_node_size(btree)); + return NILFS_BTREE_NODE_NCHILDREN_MAX(nilfs_btree_node_size(btree)); } static inline __le64 * @@ -179,11 +166,9 @@ nilfs_btree_node_dkeys(const struct nilfs_btree_node *node) } static inline __le64 * -nilfs_btree_node_dptrs(const struct nilfs_btree_node *node, - const struct nilfs_bmap *btree) +nilfs_btree_node_dptrs(const struct nilfs_btree_node *node, int ncmax) { - return (__le64 *)(nilfs_btree_node_dkeys(node) + - nilfs_btree_node_nchildren_max(node, btree)); + return (__le64 *)(nilfs_btree_node_dkeys(node) + ncmax); } static inline __u64 @@ -199,22 +184,21 @@ nilfs_btree_node_set_key(struct nilfs_btree_node *node, int index, __u64 key) } static inline __u64 -nilfs_btree_node_get_ptr(const struct nilfs_bmap *btree, - const struct nilfs_btree_node *node, int index) +nilfs_btree_node_get_ptr(const struct nilfs_btree_node *node, int index, + int ncmax) { - return le64_to_cpu(*(nilfs_btree_node_dptrs(node, btree) + index)); + return le64_to_cpu(*(nilfs_btree_node_dptrs(node, ncmax) + index)); } static inline void -nilfs_btree_node_set_ptr(struct nilfs_bmap *btree, - struct nilfs_btree_node *node, int index, __u64 ptr) +nilfs_btree_node_set_ptr(struct nilfs_btree_node *node, int index, __u64 ptr, + int ncmax) { - *(nilfs_btree_node_dptrs(node, btree) + index) = cpu_to_le64(ptr); + *(nilfs_btree_node_dptrs(node, ncmax) + index) = cpu_to_le64(ptr); } -static void nilfs_btree_node_init(struct nilfs_bmap *btree, - struct nilfs_btree_node *node, - int flags, int level, int nchildren, +static void nilfs_btree_node_init(struct nilfs_btree_node *node, int flags, + int level, int nchildren, int ncmax, const __u64 *keys, const __u64 *ptrs) { __le64 *dkeys; @@ -226,7 +210,7 @@ static void nilfs_btree_node_init(struct nilfs_bmap *btree, nilfs_btree_node_set_nchildren(node, nchildren); dkeys = nilfs_btree_node_dkeys(node); - dptrs = nilfs_btree_node_dptrs(node, btree); + dptrs = nilfs_btree_node_dptrs(node, ncmax); for (i = 0; i < nchildren; i++) { dkeys[i] = cpu_to_le64(keys[i]); dptrs[i] = cpu_to_le64(ptrs[i]); @@ -234,21 +218,20 @@ static void nilfs_btree_node_init(struct nilfs_bmap *btree, } /* Assume the buffer heads corresponding to left and right are locked. */ -static void nilfs_btree_node_move_left(struct nilfs_bmap *btree, - struct nilfs_btree_node *left, +static void nilfs_btree_node_move_left(struct nilfs_btree_node *left, struct nilfs_btree_node *right, - int n) + int n, int lncmax, int rncmax) { __le64 *ldkeys, *rdkeys; __le64 *ldptrs, *rdptrs; int lnchildren, rnchildren; ldkeys = nilfs_btree_node_dkeys(left); - ldptrs = nilfs_btree_node_dptrs(left, btree); + ldptrs = nilfs_btree_node_dptrs(left, lncmax); lnchildren = nilfs_btree_node_get_nchildren(left); rdkeys = nilfs_btree_node_dkeys(right); - rdptrs = nilfs_btree_node_dptrs(right, btree); + rdptrs = nilfs_btree_node_dptrs(right, rncmax); rnchildren = nilfs_btree_node_get_nchildren(right); memcpy(ldkeys + lnchildren, rdkeys, n * sizeof(*rdkeys)); @@ -263,21 +246,20 @@ static void nilfs_btree_node_move_left(struct nilfs_bmap *btree, } /* Assume that the buffer heads corresponding to left and right are locked. */ -static void nilfs_btree_node_move_right(struct nilfs_bmap *btree, - struct nilfs_btree_node *left, +static void nilfs_btree_node_move_right(struct nilfs_btree_node *left, struct nilfs_btree_node *right, - int n) + int n, int lncmax, int rncmax) { __le64 *ldkeys, *rdkeys; __le64 *ldptrs, *rdptrs; int lnchildren, rnchildren; ldkeys = nilfs_btree_node_dkeys(left); - ldptrs = nilfs_btree_node_dptrs(left, btree); + ldptrs = nilfs_btree_node_dptrs(left, lncmax); lnchildren = nilfs_btree_node_get_nchildren(left); rdkeys = nilfs_btree_node_dkeys(right); - rdptrs = nilfs_btree_node_dptrs(right, btree); + rdptrs = nilfs_btree_node_dptrs(right, rncmax); rnchildren = nilfs_btree_node_get_nchildren(right); memmove(rdkeys + n, rdkeys, rnchildren * sizeof(*rdkeys)); @@ -292,16 +274,15 @@ static void nilfs_btree_node_move_right(struct nilfs_bmap *btree, } /* Assume that the buffer head corresponding to node is locked. */ -static void nilfs_btree_node_insert(struct nilfs_bmap *btree, - struct nilfs_btree_node *node, - __u64 key, __u64 ptr, int index) +static void nilfs_btree_node_insert(struct nilfs_btree_node *node, int index, + __u64 key, __u64 ptr, int ncmax) { __le64 *dkeys; __le64 *dptrs; int nchildren; dkeys = nilfs_btree_node_dkeys(node); - dptrs = nilfs_btree_node_dptrs(node, btree); + dptrs = nilfs_btree_node_dptrs(node, ncmax); nchildren = nilfs_btree_node_get_nchildren(node); if (index < nchildren) { memmove(dkeys + index + 1, dkeys + index, @@ -316,9 +297,8 @@ static void nilfs_btree_node_insert(struct nilfs_bmap *btree, } /* Assume that the buffer head corresponding to node is locked. */ -static void nilfs_btree_node_delete(struct nilfs_bmap *btree, - struct nilfs_btree_node *node, - __u64 *keyp, __u64 *ptrp, int index) +static void nilfs_btree_node_delete(struct nilfs_btree_node *node, int index, + __u64 *keyp, __u64 *ptrp, int ncmax) { __u64 key; __u64 ptr; @@ -327,7 +307,7 @@ static void nilfs_btree_node_delete(struct nilfs_bmap *btree, int nchildren; dkeys = nilfs_btree_node_dkeys(node); - dptrs = nilfs_btree_node_dptrs(node, btree); + dptrs = nilfs_btree_node_dptrs(node, ncmax); key = le64_to_cpu(dkeys[index]); ptr = le64_to_cpu(dptrs[index]); nchildren = nilfs_btree_node_get_nchildren(node); @@ -445,14 +425,21 @@ static inline int nilfs_btree_height(const struct nilfs_bmap *btree) return nilfs_btree_node_get_level(nilfs_btree_get_root(btree)) + 1; } -static inline struct nilfs_btree_node * +static struct nilfs_btree_node * nilfs_btree_get_node(const struct nilfs_bmap *btree, const struct nilfs_btree_path *path, - int level) + int level, int *ncmaxp) { - return (level == nilfs_btree_height(btree) - 1) ? - nilfs_btree_get_root(btree) : - nilfs_btree_get_nonroot_node(path, level); + struct nilfs_btree_node *node; + + if (level == nilfs_btree_height(btree) - 1) { + node = nilfs_btree_get_root(btree); + *ncmaxp = NILFS_BTREE_ROOT_NCHILDREN_MAX; + } else { + node = nilfs_btree_get_nonroot_node(path, level); + *ncmaxp = nilfs_btree_nchildren_per_block(btree); + } + return node; } static inline int @@ -481,11 +468,12 @@ static int nilfs_btree_do_lookup(const struct nilfs_bmap *btree, return -ENOENT; found = nilfs_btree_node_lookup(node, key, &index); - ptr = nilfs_btree_node_get_ptr(btree, node, index); + ptr = nilfs_btree_node_get_ptr(node, index, + NILFS_BTREE_ROOT_NCHILDREN_MAX); path[level].bp_bh = NULL; path[level].bp_index = index; - ncmax = NILFS_BTREE_NODE_NCHILDREN_MAX(nilfs_btree_node_size(btree)); + ncmax = nilfs_btree_nchildren_per_block(btree); for (level--; level >= minlevel; level--) { ret = nilfs_btree_get_block(btree, ptr, &path[level].bp_bh); @@ -499,7 +487,7 @@ static int nilfs_btree_do_lookup(const struct nilfs_bmap *btree, else index = 0; if (index < ncmax) { - ptr = nilfs_btree_node_get_ptr(btree, node, index); + ptr = nilfs_btree_node_get_ptr(node, index, ncmax); } else { WARN_ON(found || level != NILFS_BTREE_LEVEL_NODE_MIN); /* insert */ @@ -522,16 +510,18 @@ static int nilfs_btree_do_lookup_last(const struct nilfs_bmap *btree, { struct nilfs_btree_node *node; __u64 ptr; - int index, level, ret; + int index, level, ncmax, ret; node = nilfs_btree_get_root(btree); index = nilfs_btree_node_get_nchildren(node) - 1; if (index < 0) return -ENOENT; level = nilfs_btree_node_get_level(node); - ptr = nilfs_btree_node_get_ptr(btree, node, index); + ptr = nilfs_btree_node_get_ptr(node, index, + NILFS_BTREE_ROOT_NCHILDREN_MAX); path[level].bp_bh = NULL; path[level].bp_index = index; + ncmax = nilfs_btree_nchildren_per_block(btree); for (level--; level > 0; level--) { ret = nilfs_btree_get_block(btree, ptr, &path[level].bp_bh); @@ -541,7 +531,7 @@ static int nilfs_btree_do_lookup_last(const struct nilfs_bmap *btree, if (nilfs_btree_bad_node(node, level)) return -EINVAL; index = nilfs_btree_node_get_nchildren(node) - 1; - ptr = nilfs_btree_node_get_ptr(btree, node, index); + ptr = nilfs_btree_node_get_ptr(node, index, ncmax); path[level].bp_index = index; } @@ -579,7 +569,7 @@ static int nilfs_btree_lookup_contig(const struct nilfs_bmap *btree, __u64 ptr, ptr2; sector_t blocknr; int level = NILFS_BTREE_LEVEL_NODE_MIN; - int ret, cnt, index, maxlevel; + int ret, cnt, index, maxlevel, ncmax; path = nilfs_btree_alloc_path(); if (path == NULL) @@ -601,14 +591,14 @@ static int nilfs_btree_lookup_contig(const struct nilfs_bmap *btree, goto end; maxlevel = nilfs_btree_height(btree) - 1; - node = nilfs_btree_get_node(btree, path, level); + node = nilfs_btree_get_node(btree, path, level, &ncmax); index = path[level].bp_index + 1; for (;;) { while (index < nilfs_btree_node_get_nchildren(node)) { if (nilfs_btree_node_get_key(node, index) != key + cnt) goto end; - ptr2 = nilfs_btree_node_get_ptr(btree, node, index); + ptr2 = nilfs_btree_node_get_ptr(node, index, ncmax); if (dat) { ret = nilfs_dat_translate(dat, ptr2, &blocknr); if (ret < 0) @@ -624,12 +614,12 @@ static int nilfs_btree_lookup_contig(const struct nilfs_bmap *btree, break; /* look-up right sibling node */ - node = nilfs_btree_get_node(btree, path, level + 1); + node = nilfs_btree_get_node(btree, path, level + 1, &ncmax); index = path[level + 1].bp_index + 1; if (index >= nilfs_btree_node_get_nchildren(node) || nilfs_btree_node_get_key(node, index) != key + cnt) break; - ptr2 = nilfs_btree_node_get_ptr(btree, node, index); + ptr2 = nilfs_btree_node_get_ptr(node, index, ncmax); path[level + 1].bp_index = index; brelse(path[level].bp_bh); @@ -638,6 +628,7 @@ static int nilfs_btree_lookup_contig(const struct nilfs_bmap *btree, if (ret < 0) goto out; node = nilfs_btree_get_nonroot_node(path, level); + ncmax = nilfs_btree_nchildren_per_block(btree); index = 0; path[level].bp_index = index; } @@ -676,11 +667,13 @@ static void nilfs_btree_do_insert(struct nilfs_bmap *btree, int level, __u64 *keyp, __u64 *ptrp) { struct nilfs_btree_node *node; + int ncblk; if (level < nilfs_btree_height(btree) - 1) { node = nilfs_btree_get_nonroot_node(path, level); - nilfs_btree_node_insert(btree, node, *keyp, *ptrp, - path[level].bp_index); + ncblk = nilfs_btree_nchildren_per_block(btree); + nilfs_btree_node_insert(node, path[level].bp_index, + *keyp, *ptrp, ncblk); if (!buffer_dirty(path[level].bp_bh)) nilfs_btnode_mark_dirty(path[level].bp_bh); @@ -690,8 +683,9 @@ static void nilfs_btree_do_insert(struct nilfs_bmap *btree, 0)); } else { node = nilfs_btree_get_root(btree); - nilfs_btree_node_insert(btree, node, *keyp, *ptrp, - path[level].bp_index); + nilfs_btree_node_insert(node, path[level].bp_index, + *keyp, *ptrp, + NILFS_BTREE_ROOT_NCHILDREN_MAX); } } @@ -700,12 +694,13 @@ static void nilfs_btree_carry_left(struct nilfs_bmap *btree, int level, __u64 *keyp, __u64 *ptrp) { struct nilfs_btree_node *node, *left; - int nchildren, lnchildren, n, move; + int nchildren, lnchildren, n, move, ncblk; node = nilfs_btree_get_nonroot_node(path, level); left = nilfs_btree_get_sib_node(path, level); nchildren = nilfs_btree_node_get_nchildren(node); lnchildren = nilfs_btree_node_get_nchildren(left); + ncblk = nilfs_btree_nchildren_per_block(btree); move = 0; n = (nchildren + lnchildren + 1) / 2 - lnchildren; @@ -715,7 +710,7 @@ static void nilfs_btree_carry_left(struct nilfs_bmap *btree, move = 1; } - nilfs_btree_node_move_left(btree, left, node, n); + nilfs_btree_node_move_left(left, node, n, ncblk, ncblk); if (!buffer_dirty(path[level].bp_bh)) nilfs_btnode_mark_dirty(path[level].bp_bh); @@ -745,12 +740,13 @@ static void nilfs_btree_carry_right(struct nilfs_bmap *btree, int level, __u64 *keyp, __u64 *ptrp) { struct nilfs_btree_node *node, *right; - int nchildren, rnchildren, n, move; + int nchildren, rnchildren, n, move, ncblk; node = nilfs_btree_get_nonroot_node(path, level); right = nilfs_btree_get_sib_node(path, level); nchildren = nilfs_btree_node_get_nchildren(node); rnchildren = nilfs_btree_node_get_nchildren(right); + ncblk = nilfs_btree_nchildren_per_block(btree); move = 0; n = (nchildren + rnchildren + 1) / 2 - rnchildren; @@ -760,7 +756,7 @@ static void nilfs_btree_carry_right(struct nilfs_bmap *btree, move = 1; } - nilfs_btree_node_move_right(btree, node, right, n); + nilfs_btree_node_move_right(node, right, n, ncblk, ncblk); if (!buffer_dirty(path[level].bp_bh)) nilfs_btnode_mark_dirty(path[level].bp_bh); @@ -793,11 +789,12 @@ static void nilfs_btree_split(struct nilfs_bmap *btree, struct nilfs_btree_node *node, *right; __u64 newkey; __u64 newptr; - int nchildren, n, move; + int nchildren, n, move, ncblk; node = nilfs_btree_get_nonroot_node(path, level); right = nilfs_btree_get_sib_node(path, level); nchildren = nilfs_btree_node_get_nchildren(node); + ncblk = nilfs_btree_nchildren_per_block(btree); move = 0; n = (nchildren + 1) / 2; @@ -806,7 +803,7 @@ static void nilfs_btree_split(struct nilfs_bmap *btree, move = 1; } - nilfs_btree_node_move_right(btree, node, right, n); + nilfs_btree_node_move_right(node, right, n, ncblk, ncblk); if (!buffer_dirty(path[level].bp_bh)) nilfs_btnode_mark_dirty(path[level].bp_bh); @@ -818,8 +815,8 @@ static void nilfs_btree_split(struct nilfs_bmap *btree, if (move) { path[level].bp_index -= nilfs_btree_node_get_nchildren(node); - nilfs_btree_node_insert(btree, right, *keyp, *ptrp, - path[level].bp_index); + nilfs_btree_node_insert(right, path[level].bp_index, + *keyp, *ptrp, ncblk); *keyp = nilfs_btree_node_get_key(right, 0); *ptrp = path[level].bp_newreq.bpr_ptr; @@ -845,14 +842,16 @@ static void nilfs_btree_grow(struct nilfs_bmap *btree, int level, __u64 *keyp, __u64 *ptrp) { struct nilfs_btree_node *root, *child; - int n; + int n, ncblk; root = nilfs_btree_get_root(btree); child = nilfs_btree_get_sib_node(path, level); + ncblk = nilfs_btree_nchildren_per_block(btree); n = nilfs_btree_node_get_nchildren(root); - nilfs_btree_node_move_right(btree, root, child, n); + nilfs_btree_node_move_right(root, child, n, + NILFS_BTREE_ROOT_NCHILDREN_MAX, ncblk); nilfs_btree_node_set_level(root, level + 1); if (!buffer_dirty(path[level].bp_sib_bh)) @@ -871,7 +870,7 @@ static __u64 nilfs_btree_find_near(const struct nilfs_bmap *btree, const struct nilfs_btree_path *path) { struct nilfs_btree_node *node; - int level; + int level, ncmax; if (path == NULL) return NILFS_BMAP_INVALID_PTR; @@ -879,17 +878,18 @@ static __u64 nilfs_btree_find_near(const struct nilfs_bmap *btree, /* left sibling */ level = NILFS_BTREE_LEVEL_NODE_MIN; if (path[level].bp_index > 0) { - node = nilfs_btree_get_node(btree, path, level); - return nilfs_btree_node_get_ptr(btree, node, - path[level].bp_index - 1); + node = nilfs_btree_get_node(btree, path, level, &ncmax); + return nilfs_btree_node_get_ptr(node, + path[level].bp_index - 1, + ncmax); } /* parent */ level = NILFS_BTREE_LEVEL_NODE_MIN + 1; if (level <= nilfs_btree_height(btree) - 1) { - node = nilfs_btree_get_node(btree, path, level); - return nilfs_btree_node_get_ptr(btree, node, - path[level].bp_index); + node = nilfs_btree_get_node(btree, path, level, &ncmax); + return nilfs_btree_node_get_ptr(node, path[level].bp_index, + ncmax); } return NILFS_BMAP_INVALID_PTR; @@ -923,7 +923,7 @@ static int nilfs_btree_prepare_insert(struct nilfs_bmap *btree, struct buffer_head *bh; struct nilfs_btree_node *node, *parent, *sib; __u64 sibptr; - int pindex, level, ncmax, ret; + int pindex, level, ncmax, ncblk, ret; struct inode *dat = NULL; stats->bs_nblocks = 0; @@ -940,54 +940,55 @@ static int nilfs_btree_prepare_insert(struct nilfs_bmap *btree, if (ret < 0) goto err_out_data; - ncmax = NILFS_BTREE_NODE_NCHILDREN_MAX(nilfs_btree_node_size(btree)); + ncblk = nilfs_btree_nchildren_per_block(btree); for (level = NILFS_BTREE_LEVEL_NODE_MIN; level < nilfs_btree_height(btree) - 1; level++) { node = nilfs_btree_get_nonroot_node(path, level); - if (nilfs_btree_node_get_nchildren(node) < ncmax) { + if (nilfs_btree_node_get_nchildren(node) < ncblk) { path[level].bp_op = nilfs_btree_do_insert; stats->bs_nblocks++; goto out; } - parent = nilfs_btree_get_node(btree, path, level + 1); + parent = nilfs_btree_get_node(btree, path, level + 1, &ncmax); pindex = path[level + 1].bp_index; /* left sibling */ if (pindex > 0) { - sibptr = nilfs_btree_node_get_ptr(btree, parent, - pindex - 1); + sibptr = nilfs_btree_node_get_ptr(parent, pindex - 1, + ncmax); ret = nilfs_btree_get_block(btree, sibptr, &bh); if (ret < 0) goto err_out_child_node; sib = (struct nilfs_btree_node *)bh->b_data; - if (nilfs_btree_node_get_nchildren(sib) < ncmax) { + if (nilfs_btree_node_get_nchildren(sib) < ncblk) { path[level].bp_sib_bh = bh; path[level].bp_op = nilfs_btree_carry_left; stats->bs_nblocks++; goto out; - } else + } else { brelse(bh); + } } /* right sibling */ - if (pindex < - nilfs_btree_node_get_nchildren(parent) - 1) { - sibptr = nilfs_btree_node_get_ptr(btree, parent, - pindex + 1); + if (pindex < nilfs_btree_node_get_nchildren(parent) - 1) { + sibptr = nilfs_btree_node_get_ptr(parent, pindex + 1, + ncmax); ret = nilfs_btree_get_block(btree, sibptr, &bh); if (ret < 0) goto err_out_child_node; sib = (struct nilfs_btree_node *)bh->b_data; - if (nilfs_btree_node_get_nchildren(sib) < ncmax) { + if (nilfs_btree_node_get_nchildren(sib) < ncblk) { path[level].bp_sib_bh = bh; path[level].bp_op = nilfs_btree_carry_right; stats->bs_nblocks++; goto out; - } else + } else { brelse(bh); + } } /* split */ @@ -1005,9 +1006,8 @@ static int nilfs_btree_prepare_insert(struct nilfs_bmap *btree, stats->bs_nblocks++; - nilfs_btree_node_init(btree, - (struct nilfs_btree_node *)bh->b_data, - 0, level, 0, NULL, NULL); + sib = (struct nilfs_btree_node *)bh->b_data; + nilfs_btree_node_init(sib, 0, level, 0, ncblk, NULL, NULL); path[level].bp_sib_bh = bh; path[level].bp_op = nilfs_btree_split; } @@ -1031,8 +1031,8 @@ static int nilfs_btree_prepare_insert(struct nilfs_bmap *btree, if (ret < 0) goto err_out_curr_node; - nilfs_btree_node_init(btree, (struct nilfs_btree_node *)bh->b_data, - 0, level, 0, NULL, NULL); + nilfs_btree_node_init((struct nilfs_btree_node *)bh->b_data, + 0, level, 0, ncblk, NULL, NULL); path[level].bp_sib_bh = bh; path[level].bp_op = nilfs_btree_grow; @@ -1122,11 +1122,13 @@ static void nilfs_btree_do_delete(struct nilfs_bmap *btree, int level, __u64 *keyp, __u64 *ptrp) { struct nilfs_btree_node *node; + int ncblk; if (level < nilfs_btree_height(btree) - 1) { node = nilfs_btree_get_nonroot_node(path, level); - nilfs_btree_node_delete(btree, node, keyp, ptrp, - path[level].bp_index); + ncblk = nilfs_btree_nchildren_per_block(btree); + nilfs_btree_node_delete(node, path[level].bp_index, + keyp, ptrp, ncblk); if (!buffer_dirty(path[level].bp_bh)) nilfs_btnode_mark_dirty(path[level].bp_bh); if (path[level].bp_index == 0) @@ -1134,8 +1136,9 @@ static void nilfs_btree_do_delete(struct nilfs_bmap *btree, nilfs_btree_node_get_key(node, 0)); } else { node = nilfs_btree_get_root(btree); - nilfs_btree_node_delete(btree, node, keyp, ptrp, - path[level].bp_index); + nilfs_btree_node_delete(node, path[level].bp_index, + keyp, ptrp, + NILFS_BTREE_ROOT_NCHILDREN_MAX); } } @@ -1144,7 +1147,7 @@ static void nilfs_btree_borrow_left(struct nilfs_bmap *btree, int level, __u64 *keyp, __u64 *ptrp) { struct nilfs_btree_node *node, *left; - int nchildren, lnchildren, n; + int nchildren, lnchildren, n, ncblk; nilfs_btree_do_delete(btree, path, level, keyp, ptrp); @@ -1152,10 +1155,11 @@ static void nilfs_btree_borrow_left(struct nilfs_bmap *btree, left = nilfs_btree_get_sib_node(path, level); nchildren = nilfs_btree_node_get_nchildren(node); lnchildren = nilfs_btree_node_get_nchildren(left); + ncblk = nilfs_btree_nchildren_per_block(btree); n = (nchildren + lnchildren) / 2 - nchildren; - nilfs_btree_node_move_right(btree, left, node, n); + nilfs_btree_node_move_right(left, node, n, ncblk, ncblk); if (!buffer_dirty(path[level].bp_bh)) nilfs_btnode_mark_dirty(path[level].bp_bh); @@ -1175,7 +1179,7 @@ static void nilfs_btree_borrow_right(struct nilfs_bmap *btree, int level, __u64 *keyp, __u64 *ptrp) { struct nilfs_btree_node *node, *right; - int nchildren, rnchildren, n; + int nchildren, rnchildren, n, ncblk; nilfs_btree_do_delete(btree, path, level, keyp, ptrp); @@ -1183,10 +1187,11 @@ static void nilfs_btree_borrow_right(struct nilfs_bmap *btree, right = nilfs_btree_get_sib_node(path, level); nchildren = nilfs_btree_node_get_nchildren(node); rnchildren = nilfs_btree_node_get_nchildren(right); + ncblk = nilfs_btree_nchildren_per_block(btree); n = (nchildren + rnchildren) / 2 - nchildren; - nilfs_btree_node_move_left(btree, node, right, n); + nilfs_btree_node_move_left(node, right, n, ncblk, ncblk); if (!buffer_dirty(path[level].bp_bh)) nilfs_btnode_mark_dirty(path[level].bp_bh); @@ -1207,16 +1212,17 @@ static void nilfs_btree_concat_left(struct nilfs_bmap *btree, int level, __u64 *keyp, __u64 *ptrp) { struct nilfs_btree_node *node, *left; - int n; + int n, ncblk; nilfs_btree_do_delete(btree, path, level, keyp, ptrp); node = nilfs_btree_get_nonroot_node(path, level); left = nilfs_btree_get_sib_node(path, level); + ncblk = nilfs_btree_nchildren_per_block(btree); n = nilfs_btree_node_get_nchildren(node); - nilfs_btree_node_move_left(btree, left, node, n); + nilfs_btree_node_move_left(left, node, n, ncblk, ncblk); if (!buffer_dirty(path[level].bp_sib_bh)) nilfs_btnode_mark_dirty(path[level].bp_sib_bh); @@ -1232,16 +1238,17 @@ static void nilfs_btree_concat_right(struct nilfs_bmap *btree, int level, __u64 *keyp, __u64 *ptrp) { struct nilfs_btree_node *node, *right; - int n; + int n, ncblk; nilfs_btree_do_delete(btree, path, level, keyp, ptrp); node = nilfs_btree_get_nonroot_node(path, level); right = nilfs_btree_get_sib_node(path, level); + ncblk = nilfs_btree_nchildren_per_block(btree); n = nilfs_btree_node_get_nchildren(right); - nilfs_btree_node_move_left(btree, node, right, n); + nilfs_btree_node_move_left(node, right, n, ncblk, ncblk); if (!buffer_dirty(path[level].bp_bh)) nilfs_btnode_mark_dirty(path[level].bp_bh); @@ -1256,17 +1263,20 @@ static void nilfs_btree_shrink(struct nilfs_bmap *btree, int level, __u64 *keyp, __u64 *ptrp) { struct nilfs_btree_node *root, *child; - int n; + int n, ncblk; nilfs_btree_do_delete(btree, path, level, keyp, ptrp); root = nilfs_btree_get_root(btree); child = nilfs_btree_get_nonroot_node(path, level); + ncblk = nilfs_btree_nchildren_per_block(btree); - nilfs_btree_node_delete(btree, root, NULL, NULL, 0); + nilfs_btree_node_delete(root, 0, NULL, NULL, + NILFS_BTREE_ROOT_NCHILDREN_MAX); nilfs_btree_node_set_level(root, level); n = nilfs_btree_node_get_nchildren(child); - nilfs_btree_node_move_left(btree, root, child, n); + nilfs_btree_node_move_left(root, child, n, + NILFS_BTREE_ROOT_NCHILDREN_MAX, ncblk); nilfs_btnode_delete(path[level].bp_bh); path[level].bp_bh = NULL; @@ -1282,19 +1292,20 @@ static int nilfs_btree_prepare_delete(struct nilfs_bmap *btree, struct buffer_head *bh; struct nilfs_btree_node *node, *parent, *sib; __u64 sibptr; - int pindex, level, ncmin, ret; + int pindex, level, ncmin, ncmax, ncblk, ret; ret = 0; stats->bs_nblocks = 0; ncmin = NILFS_BTREE_NODE_NCHILDREN_MIN(nilfs_btree_node_size(btree)); + ncblk = nilfs_btree_nchildren_per_block(btree); for (level = NILFS_BTREE_LEVEL_NODE_MIN; level < nilfs_btree_height(btree) - 1; level++) { node = nilfs_btree_get_nonroot_node(path, level); path[level].bp_oldreq.bpr_ptr = - nilfs_btree_node_get_ptr(btree, node, - path[level].bp_index); + nilfs_btree_node_get_ptr(node, path[level].bp_index, + ncblk); ret = nilfs_bmap_prepare_end_ptr(btree, &path[level].bp_oldreq, dat); if (ret < 0) @@ -1306,13 +1317,13 @@ static int nilfs_btree_prepare_delete(struct nilfs_bmap *btree, goto out; } - parent = nilfs_btree_get_node(btree, path, level + 1); + parent = nilfs_btree_get_node(btree, path, level + 1, &ncmax); pindex = path[level + 1].bp_index; if (pindex > 0) { /* left sibling */ - sibptr = nilfs_btree_node_get_ptr(btree, parent, - pindex - 1); + sibptr = nilfs_btree_node_get_ptr(parent, pindex - 1, + ncmax); ret = nilfs_btree_get_block(btree, sibptr, &bh); if (ret < 0) goto err_out_curr_node; @@ -1331,8 +1342,8 @@ static int nilfs_btree_prepare_delete(struct nilfs_bmap *btree, } else if (pindex < nilfs_btree_node_get_nchildren(parent) - 1) { /* right sibling */ - sibptr = nilfs_btree_node_get_ptr(btree, parent, - pindex + 1); + sibptr = nilfs_btree_node_get_ptr(parent, pindex + 1, + ncmax); ret = nilfs_btree_get_block(btree, sibptr, &bh); if (ret < 0) goto err_out_curr_node; @@ -1368,7 +1379,8 @@ static int nilfs_btree_prepare_delete(struct nilfs_bmap *btree, node = nilfs_btree_get_root(btree); path[level].bp_oldreq.bpr_ptr = - nilfs_btree_node_get_ptr(btree, node, path[level].bp_index); + nilfs_btree_node_get_ptr(node, path[level].bp_index, + NILFS_BTREE_ROOT_NCHILDREN_MAX); ret = nilfs_bmap_prepare_end_ptr(btree, &path[level].bp_oldreq, dat); if (ret < 0) @@ -1476,7 +1488,8 @@ static int nilfs_btree_check_delete(struct nilfs_bmap *btree, __u64 key) nchildren = nilfs_btree_node_get_nchildren(root); if (nchildren > 1) return 0; - ptr = nilfs_btree_node_get_ptr(btree, root, nchildren - 1); + ptr = nilfs_btree_node_get_ptr(root, nchildren - 1, + NILFS_BTREE_ROOT_NCHILDREN_MAX); ret = nilfs_btree_get_block(btree, ptr, &bh); if (ret < 0) return ret; @@ -1504,22 +1517,25 @@ static int nilfs_btree_gather_data(struct nilfs_bmap *btree, __le64 *dkeys; __le64 *dptrs; __u64 ptr; - int nchildren, i, ret; + int nchildren, ncmax, i, ret; root = nilfs_btree_get_root(btree); switch (nilfs_btree_height(btree)) { case 2: bh = NULL; node = root; + ncmax = NILFS_BTREE_ROOT_NCHILDREN_MAX; break; case 3: nchildren = nilfs_btree_node_get_nchildren(root); WARN_ON(nchildren > 1); - ptr = nilfs_btree_node_get_ptr(btree, root, nchildren - 1); + ptr = nilfs_btree_node_get_ptr(root, nchildren - 1, + NILFS_BTREE_ROOT_NCHILDREN_MAX); ret = nilfs_btree_get_block(btree, ptr, &bh); if (ret < 0) return ret; node = (struct nilfs_btree_node *)bh->b_data; + ncmax = nilfs_btree_nchildren_per_block(btree); break; default: node = NULL; @@ -1530,7 +1546,7 @@ static int nilfs_btree_gather_data(struct nilfs_bmap *btree, if (nchildren < nitems) nitems = nchildren; dkeys = nilfs_btree_node_dkeys(node); - dptrs = nilfs_btree_node_dptrs(node, btree); + dptrs = nilfs_btree_node_dptrs(node, ncmax); for (i = 0; i < nitems; i++) { keys[i] = le64_to_cpu(dkeys[i]); ptrs[i] = le64_to_cpu(dptrs[i]); @@ -1607,6 +1623,7 @@ nilfs_btree_commit_convert_and_insert(struct nilfs_bmap *btree, struct nilfs_btree_node *node; struct inode *dat; __u64 tmpptr; + int ncblk; /* free resources */ if (btree->b_ops->bop_clear != NULL) @@ -1624,8 +1641,9 @@ nilfs_btree_commit_convert_and_insert(struct nilfs_bmap *btree, /* create child node at level 1 */ node = (struct nilfs_btree_node *)bh->b_data; - nilfs_btree_node_init(btree, node, 0, 1, n, keys, ptrs); - nilfs_btree_node_insert(btree, node, key, dreq->bpr_ptr, n); + ncblk = nilfs_btree_nchildren_per_block(btree); + nilfs_btree_node_init(node, 0, 1, n, ncblk, keys, ptrs); + nilfs_btree_node_insert(node, n, key, dreq->bpr_ptr, ncblk); if (!buffer_dirty(bh)) nilfs_btnode_mark_dirty(bh); if (!nilfs_bmap_dirty(btree)) @@ -1636,16 +1654,19 @@ nilfs_btree_commit_convert_and_insert(struct nilfs_bmap *btree, /* create root node at level 2 */ node = nilfs_btree_get_root(btree); tmpptr = nreq->bpr_ptr; - nilfs_btree_node_init(btree, node, NILFS_BTREE_NODE_ROOT, - 2, 1, &keys[0], &tmpptr); + nilfs_btree_node_init(node, NILFS_BTREE_NODE_ROOT, 2, 1, + NILFS_BTREE_ROOT_NCHILDREN_MAX, + &keys[0], &tmpptr); } else { nilfs_bmap_commit_alloc_ptr(btree, dreq, dat); /* create root node at level 1 */ node = nilfs_btree_get_root(btree); - nilfs_btree_node_init(btree, node, NILFS_BTREE_NODE_ROOT, - 1, n, keys, ptrs); - nilfs_btree_node_insert(btree, node, key, dreq->bpr_ptr, n); + nilfs_btree_node_init(node, NILFS_BTREE_NODE_ROOT, 1, n, + NILFS_BTREE_ROOT_NCHILDREN_MAX, + keys, ptrs); + nilfs_btree_node_insert(node, n, key, dreq->bpr_ptr, + NILFS_BTREE_ROOT_NCHILDREN_MAX); if (!nilfs_bmap_dirty(btree)) nilfs_bmap_set_dirty(btree); } @@ -1712,12 +1733,12 @@ static int nilfs_btree_prepare_update_v(struct nilfs_bmap *btree, int level, struct inode *dat) { struct nilfs_btree_node *parent; - int ret; + int ncmax, ret; - parent = nilfs_btree_get_node(btree, path, level + 1); + parent = nilfs_btree_get_node(btree, path, level + 1, &ncmax); path[level].bp_oldreq.bpr_ptr = - nilfs_btree_node_get_ptr(btree, parent, - path[level + 1].bp_index); + nilfs_btree_node_get_ptr(parent, path[level + 1].bp_index, + ncmax); path[level].bp_newreq.bpr_ptr = path[level].bp_oldreq.bpr_ptr + 1; ret = nilfs_dat_prepare_update(dat, &path[level].bp_oldreq.bpr_req, &path[level].bp_newreq.bpr_req); @@ -1747,6 +1768,7 @@ static void nilfs_btree_commit_update_v(struct nilfs_bmap *btree, int level, struct inode *dat) { struct nilfs_btree_node *parent; + int ncmax; nilfs_dat_commit_update(dat, &path[level].bp_oldreq.bpr_req, &path[level].bp_newreq.bpr_req, @@ -1760,9 +1782,9 @@ static void nilfs_btree_commit_update_v(struct nilfs_bmap *btree, } set_buffer_nilfs_volatile(path[level].bp_bh); - parent = nilfs_btree_get_node(btree, path, level + 1); - nilfs_btree_node_set_ptr(btree, parent, path[level + 1].bp_index, - path[level].bp_newreq.bpr_ptr); + parent = nilfs_btree_get_node(btree, path, level + 1, &ncmax); + nilfs_btree_node_set_ptr(parent, path[level + 1].bp_index, + path[level].bp_newreq.bpr_ptr, ncmax); } static void nilfs_btree_abort_update_v(struct nilfs_bmap *btree, @@ -1835,6 +1857,7 @@ static int nilfs_btree_propagate_v(struct nilfs_bmap *btree, struct nilfs_btree_node *parent; struct inode *dat = nilfs_bmap_get_dat(btree); __u64 ptr; + int ncmax; get_bh(bh); path[level].bp_bh = bh; @@ -1844,9 +1867,10 @@ static int nilfs_btree_propagate_v(struct nilfs_bmap *btree, goto out; if (buffer_nilfs_volatile(path[level].bp_bh)) { - parent = nilfs_btree_get_node(btree, path, level + 1); - ptr = nilfs_btree_node_get_ptr(btree, parent, - path[level + 1].bp_index); + parent = nilfs_btree_get_node(btree, path, level + 1, &ncmax); + ptr = nilfs_btree_node_get_ptr(parent, + path[level + 1].bp_index, + ncmax); ret = nilfs_dat_mark_dirty(dat, ptr); if (ret < 0) goto out; @@ -1990,11 +2014,11 @@ static int nilfs_btree_assign_p(struct nilfs_bmap *btree, struct nilfs_btree_node *parent; __u64 key; __u64 ptr; - int ret; + int ncmax, ret; - parent = nilfs_btree_get_node(btree, path, level + 1); - ptr = nilfs_btree_node_get_ptr(btree, parent, - path[level + 1].bp_index); + parent = nilfs_btree_get_node(btree, path, level + 1, &ncmax); + ptr = nilfs_btree_node_get_ptr(parent, path[level + 1].bp_index, + ncmax); if (buffer_nilfs_node(*bh)) { path[level].bp_ctxt.oldkey = ptr; path[level].bp_ctxt.newkey = blocknr; @@ -2010,8 +2034,8 @@ static int nilfs_btree_assign_p(struct nilfs_bmap *btree, *bh = path[level].bp_ctxt.bh; } - nilfs_btree_node_set_ptr(btree, parent, - path[level + 1].bp_index, blocknr); + nilfs_btree_node_set_ptr(parent, path[level + 1].bp_index, blocknr, + ncmax); key = nilfs_btree_node_get_key(parent, path[level + 1].bp_index); /* on-disk format */ @@ -2033,10 +2057,11 @@ static int nilfs_btree_assign_v(struct nilfs_bmap *btree, __u64 key; __u64 ptr; union nilfs_bmap_ptr_req req; - int ret; + int ncmax, ret; - parent = nilfs_btree_get_node(btree, path, level + 1); - ptr = nilfs_btree_node_get_ptr(btree, parent, path[level + 1].bp_index); + parent = nilfs_btree_get_node(btree, path, level + 1, &ncmax); + ptr = nilfs_btree_node_get_ptr(parent, path[level + 1].bp_index, + ncmax); req.bpr_ptr = ptr; ret = nilfs_dat_prepare_start(dat, &req.bpr_req); if (ret < 0) -- cgit v1.2.3 From 5ad2686e9266f24a0bb76b01d5c3ae29b4e149fe Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Tue, 13 Jul 2010 23:33:54 +0900 Subject: nilfs2: get maximum number of child nodes from bmap object The patch "reduce repetitive calculation of max number of child nodes" gathered up the calculation of maximum number of child nodes into nilfs_btree_nchildren_per_block() function. This makes the function get resultant value from a private variable in bmap object instead of calculating it for each call. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/bmap.h | 2 ++ fs/nilfs2/btree.c | 6 +++++- 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/nilfs2/bmap.h b/fs/nilfs2/bmap.h index fae83cf9c009..a20569b19929 100644 --- a/fs/nilfs2/bmap.h +++ b/fs/nilfs2/bmap.h @@ -105,6 +105,7 @@ static inline int nilfs_bmap_is_new_ptr(unsigned long ptr) * @b_last_allocated_ptr: last allocated ptr for data block * @b_ptr_type: pointer type * @b_state: state + * @b_nchildren_per_block: maximum number of child nodes for non-root nodes */ struct nilfs_bmap { union { @@ -118,6 +119,7 @@ struct nilfs_bmap { __u64 b_last_allocated_ptr; int b_ptr_type; int b_state; + __u16 b_nchildren_per_block; }; /* pointer type */ diff --git a/fs/nilfs2/btree.c b/fs/nilfs2/btree.c index 829e145f1353..7089d9041146 100644 --- a/fs/nilfs2/btree.c +++ b/fs/nilfs2/btree.c @@ -154,7 +154,7 @@ static inline int nilfs_btree_node_size(const struct nilfs_bmap *btree) static int nilfs_btree_nchildren_per_block(const struct nilfs_bmap *btree) { - return NILFS_BTREE_NODE_NCHILDREN_MAX(nilfs_btree_node_size(btree)); + return btree->b_nchildren_per_block; } static inline __le64 * @@ -2218,10 +2218,14 @@ static const struct nilfs_bmap_operations nilfs_btree_ops_gc = { int nilfs_btree_init(struct nilfs_bmap *bmap) { bmap->b_ops = &nilfs_btree_ops; + bmap->b_nchildren_per_block = + NILFS_BTREE_NODE_NCHILDREN_MAX(nilfs_btree_node_size(bmap)); return 0; } void nilfs_btree_init_gc(struct nilfs_bmap *bmap) { bmap->b_ops = &nilfs_btree_ops_gc; + bmap->b_nchildren_per_block = + NILFS_BTREE_NODE_NCHILDREN_MAX(nilfs_btree_node_size(bmap)); } -- cgit v1.2.3 From 7c397a81fe90c0445df2873700d14e82cca5fbc8 Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Tue, 13 Jul 2010 23:33:55 +0900 Subject: nilfs2: eliminate inline keywords in btree implementation This removes all inline uses from btree.c. Gcc now agressively apply inline expansion even for the functions declared without the keyword; the inline use in btree.c looks excessive. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/btree.c | 41 +++++++++++++++++++---------------------- 1 file changed, 19 insertions(+), 22 deletions(-) (limited to 'fs') diff --git a/fs/nilfs2/btree.c b/fs/nilfs2/btree.c index 7089d9041146..6462c7026479 100644 --- a/fs/nilfs2/btree.c +++ b/fs/nilfs2/btree.c @@ -106,48 +106,45 @@ static int nilfs_btree_get_new_block(const struct nilfs_bmap *btree, return 0; } -static inline int -nilfs_btree_node_get_flags(const struct nilfs_btree_node *node) +static int nilfs_btree_node_get_flags(const struct nilfs_btree_node *node) { return node->bn_flags; } -static inline void +static void nilfs_btree_node_set_flags(struct nilfs_btree_node *node, int flags) { node->bn_flags = flags; } -static inline int nilfs_btree_node_root(const struct nilfs_btree_node *node) +static int nilfs_btree_node_root(const struct nilfs_btree_node *node) { return nilfs_btree_node_get_flags(node) & NILFS_BTREE_NODE_ROOT; } -static inline int -nilfs_btree_node_get_level(const struct nilfs_btree_node *node) +static int nilfs_btree_node_get_level(const struct nilfs_btree_node *node) { return node->bn_level; } -static inline void +static void nilfs_btree_node_set_level(struct nilfs_btree_node *node, int level) { node->bn_level = level; } -static inline int -nilfs_btree_node_get_nchildren(const struct nilfs_btree_node *node) +static int nilfs_btree_node_get_nchildren(const struct nilfs_btree_node *node) { return le16_to_cpu(node->bn_nchildren); } -static inline void +static void nilfs_btree_node_set_nchildren(struct nilfs_btree_node *node, int nchildren) { node->bn_nchildren = cpu_to_le16(nchildren); } -static inline int nilfs_btree_node_size(const struct nilfs_bmap *btree) +static int nilfs_btree_node_size(const struct nilfs_bmap *btree) { return 1 << btree->b_inode->i_blkbits; } @@ -157,7 +154,7 @@ static int nilfs_btree_nchildren_per_block(const struct nilfs_bmap *btree) return btree->b_nchildren_per_block; } -static inline __le64 * +static __le64 * nilfs_btree_node_dkeys(const struct nilfs_btree_node *node) { return (__le64 *)((char *)(node + 1) + @@ -165,32 +162,32 @@ nilfs_btree_node_dkeys(const struct nilfs_btree_node *node) 0 : NILFS_BTREE_NODE_EXTRA_PAD_SIZE)); } -static inline __le64 * +static __le64 * nilfs_btree_node_dptrs(const struct nilfs_btree_node *node, int ncmax) { return (__le64 *)(nilfs_btree_node_dkeys(node) + ncmax); } -static inline __u64 +static __u64 nilfs_btree_node_get_key(const struct nilfs_btree_node *node, int index) { return le64_to_cpu(*(nilfs_btree_node_dkeys(node) + index)); } -static inline void +static void nilfs_btree_node_set_key(struct nilfs_btree_node *node, int index, __u64 key) { *(nilfs_btree_node_dkeys(node) + index) = cpu_to_le64(key); } -static inline __u64 +static __u64 nilfs_btree_node_get_ptr(const struct nilfs_btree_node *node, int index, int ncmax) { return le64_to_cpu(*(nilfs_btree_node_dptrs(node, ncmax) + index)); } -static inline void +static void nilfs_btree_node_set_ptr(struct nilfs_btree_node *node, int index, __u64 ptr, int ncmax) { @@ -402,25 +399,25 @@ int nilfs_btree_broken_node_block(struct buffer_head *bh) bh->b_size, bh->b_blocknr); } -static inline struct nilfs_btree_node * +static struct nilfs_btree_node * nilfs_btree_get_root(const struct nilfs_bmap *btree) { return (struct nilfs_btree_node *)btree->b_u.u_data; } -static inline struct nilfs_btree_node * +static struct nilfs_btree_node * nilfs_btree_get_nonroot_node(const struct nilfs_btree_path *path, int level) { return (struct nilfs_btree_node *)path[level].bp_bh->b_data; } -static inline struct nilfs_btree_node * +static struct nilfs_btree_node * nilfs_btree_get_sib_node(const struct nilfs_btree_path *path, int level) { return (struct nilfs_btree_node *)path[level].bp_sib_bh->b_data; } -static inline int nilfs_btree_height(const struct nilfs_bmap *btree) +static int nilfs_btree_height(const struct nilfs_bmap *btree) { return nilfs_btree_node_get_level(nilfs_btree_get_root(btree)) + 1; } @@ -442,7 +439,7 @@ nilfs_btree_get_node(const struct nilfs_bmap *btree, return node; } -static inline int +static int nilfs_btree_bad_node(struct nilfs_btree_node *node, int level) { if (unlikely(nilfs_btree_node_get_level(node) != level)) { -- cgit v1.2.3 From f8e6cc013b896d75d6ce4ec9e168014af1257fd8 Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Thu, 15 Jul 2010 11:39:10 +0900 Subject: nilfs2: fix buffer head leak in nilfs_btnode_submit_block nilfs_btnode_submit_block() refers to buffer head just before returning from the function, but it releases the buffer head earlier than that if nilfs_dat_translate() gets an error. This has potential for oops in the erroneous case. This fixes the issue. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/btnode.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/nilfs2/btnode.c b/fs/nilfs2/btnode.c index 447ce47a3306..0a6834bb278e 100644 --- a/fs/nilfs2/btnode.c +++ b/fs/nilfs2/btnode.c @@ -100,6 +100,7 @@ int nilfs_btnode_submit_block(struct address_space *btnc, __u64 blocknr, { struct buffer_head *bh; struct inode *inode = NILFS_BTNC_I(btnc); + struct page *page; int err; bh = nilfs_grab_buffer(inode, btnc, blocknr, 1 << BH_NILFS_Node); @@ -107,6 +108,7 @@ int nilfs_btnode_submit_block(struct address_space *btnc, __u64 blocknr, return -ENOMEM; err = -EEXIST; /* internal code */ + page = bh->b_page; if (buffer_uptodate(bh) || buffer_dirty(bh)) goto found; @@ -143,8 +145,8 @@ found: *pbh = bh; out_locked: - unlock_page(bh->b_page); - page_cache_release(bh->b_page); + unlock_page(page); + page_cache_release(page); return err; } -- cgit v1.2.3 From 26dfdd8e29f28c08aa67861b3c83d0f3f7d30cee Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Sun, 18 Jul 2010 10:42:23 +0900 Subject: nilfs2: add read ahead mode to nilfs_btnode_submit_block This adds mode argument to nilfs_btnode_submit_block() function and allows it to issue a read-ahead request. An optional submit_ptr argument is also added to store the actual block address for which bio is sent. submit_ptr is used for a series of read-ahead requests, and helps to decide if each requested block is continous to the previous one on disk. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/btnode.c | 17 ++++++++++++++--- fs/nilfs2/btnode.h | 4 ++-- fs/nilfs2/btree.c | 3 ++- fs/nilfs2/gcinode.c | 6 ++++-- 4 files changed, 22 insertions(+), 8 deletions(-) (limited to 'fs') diff --git a/fs/nilfs2/btnode.c b/fs/nilfs2/btnode.c index 0a6834bb278e..f78ab1044d1d 100644 --- a/fs/nilfs2/btnode.c +++ b/fs/nilfs2/btnode.c @@ -96,7 +96,8 @@ nilfs_btnode_create_block(struct address_space *btnc, __u64 blocknr) } int nilfs_btnode_submit_block(struct address_space *btnc, __u64 blocknr, - sector_t pblocknr, struct buffer_head **pbh) + sector_t pblocknr, int mode, + struct buffer_head **pbh, sector_t *submit_ptr) { struct buffer_head *bh; struct inode *inode = NILFS_BTNC_I(btnc); @@ -127,7 +128,16 @@ int nilfs_btnode_submit_block(struct address_space *btnc, __u64 blocknr, } } } - lock_buffer(bh); + + if (mode == READA) { + if (pblocknr != *submit_ptr + 1 || !trylock_buffer(bh)) { + err = -EBUSY; /* internal code */ + brelse(bh); + goto out_locked; + } + } else { /* mode == READ */ + lock_buffer(bh); + } if (buffer_uptodate(bh)) { unlock_buffer(bh); err = -EEXIST; /* internal code */ @@ -138,8 +148,9 @@ int nilfs_btnode_submit_block(struct address_space *btnc, __u64 blocknr, bh->b_blocknr = pblocknr; /* set block address for read */ bh->b_end_io = end_buffer_read_sync; get_bh(bh); - submit_bh(READ, bh); + submit_bh(mode, bh); bh->b_blocknr = blocknr; /* set back to the given block address */ + *submit_ptr = pblocknr; err = 0; found: *pbh = bh; diff --git a/fs/nilfs2/btnode.h b/fs/nilfs2/btnode.h index 07da83f07712..79037494f1e0 100644 --- a/fs/nilfs2/btnode.h +++ b/fs/nilfs2/btnode.h @@ -42,8 +42,8 @@ void nilfs_btnode_cache_init(struct address_space *, struct backing_dev_info *); void nilfs_btnode_cache_clear(struct address_space *); struct buffer_head *nilfs_btnode_create_block(struct address_space *btnc, __u64 blocknr); -int nilfs_btnode_submit_block(struct address_space *, __u64, sector_t, - struct buffer_head **); +int nilfs_btnode_submit_block(struct address_space *, __u64, sector_t, int, + struct buffer_head **, sector_t *); void nilfs_btnode_delete(struct buffer_head *); int nilfs_btnode_prepare_change_key(struct address_space *, struct nilfs_btnode_chkey_ctxt *); diff --git a/fs/nilfs2/btree.c b/fs/nilfs2/btree.c index 6462c7026479..4669389bf686 100644 --- a/fs/nilfs2/btree.c +++ b/fs/nilfs2/btree.c @@ -71,9 +71,10 @@ static int nilfs_btree_get_block(const struct nilfs_bmap *btree, __u64 ptr, { struct address_space *btnc = &NILFS_BMAP_I(btree)->i_btnode_cache; struct buffer_head *bh; + sector_t pbn = 0; int err; - err = nilfs_btnode_submit_block(btnc, ptr, 0, bhp); + err = nilfs_btnode_submit_block(btnc, ptr, pbn, READ, bhp, &pbn); if (err) return err == -EEXIST ? 0 : err; diff --git a/fs/nilfs2/gcinode.c b/fs/nilfs2/gcinode.c index b6343825f91a..bed3a783129b 100644 --- a/fs/nilfs2/gcinode.c +++ b/fs/nilfs2/gcinode.c @@ -151,8 +151,10 @@ int nilfs_gccache_submit_read_data(struct inode *inode, sector_t blkoff, int nilfs_gccache_submit_read_node(struct inode *inode, sector_t pbn, __u64 vbn, struct buffer_head **out_bh) { - int ret = nilfs_btnode_submit_block(&NILFS_I(inode)->i_btnode_cache, - vbn ? : pbn, pbn, out_bh); + int ret; + + ret = nilfs_btnode_submit_block(&NILFS_I(inode)->i_btnode_cache, + vbn ? : pbn, pbn, READ, out_bh, &pbn); if (ret == -EEXIST) /* internal code (cache hit) */ ret = 0; return ret; -- cgit v1.2.3 From 464ece88630d0fb715ca942eabb1da825046a534 Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Sun, 18 Jul 2010 10:42:24 +0900 Subject: nilfs2: add btree get block function with readahead option This adds __nilfs_btree_get_block() function that can issue a series of read-ahead requests for sibling btree nodes. This read-ahead needs parent node block, so nilfs_btree_readahead_info structure is added to pass the information that __nilfs_btree_get_block() needs. This also replaces the previous nilfs_btree_get_block() implementation with a wrapper function of __nilfs_btree_get_block(). Signed-off-by: Ryusuke Konishi --- fs/nilfs2/btree.c | 94 ++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 68 insertions(+), 26 deletions(-) (limited to 'fs') diff --git a/fs/nilfs2/btree.c b/fs/nilfs2/btree.c index 4669389bf686..1b5321c0bcac 100644 --- a/fs/nilfs2/btree.c +++ b/fs/nilfs2/btree.c @@ -66,32 +66,6 @@ static void nilfs_btree_free_path(struct nilfs_btree_path *path) /* * B-tree node operations */ -static int nilfs_btree_get_block(const struct nilfs_bmap *btree, __u64 ptr, - struct buffer_head **bhp) -{ - struct address_space *btnc = &NILFS_BMAP_I(btree)->i_btnode_cache; - struct buffer_head *bh; - sector_t pbn = 0; - int err; - - err = nilfs_btnode_submit_block(btnc, ptr, pbn, READ, bhp, &pbn); - if (err) - return err == -EEXIST ? 0 : err; - - bh = *bhp; - wait_on_buffer(bh); - if (!buffer_uptodate(bh)) { - brelse(bh); - return -EIO; - } - if (nilfs_btree_broken_node_block(bh)) { - clear_buffer_uptodate(bh); - brelse(bh); - return -EINVAL; - } - return 0; -} - static int nilfs_btree_get_new_block(const struct nilfs_bmap *btree, __u64 ptr, struct buffer_head **bhp) { @@ -452,6 +426,74 @@ nilfs_btree_bad_node(struct nilfs_btree_node *node, int level) return 0; } +struct nilfs_btree_readahead_info { + struct nilfs_btree_node *node; /* parent node */ + int max_ra_blocks; /* max nof blocks to read ahead */ + int index; /* current index on the parent node */ + int ncmax; /* nof children in the parent node */ +}; + +static int __nilfs_btree_get_block(const struct nilfs_bmap *btree, __u64 ptr, + struct buffer_head **bhp, + const struct nilfs_btree_readahead_info *ra) +{ + struct address_space *btnc = &NILFS_BMAP_I(btree)->i_btnode_cache; + struct buffer_head *bh, *ra_bh; + sector_t submit_ptr = 0; + int ret; + + ret = nilfs_btnode_submit_block(btnc, ptr, 0, READ, &bh, &submit_ptr); + if (ret) { + if (ret != -EEXIST) + return ret; + goto out_check; + } + + if (ra) { + int i, n; + __u64 ptr2; + + /* read ahead sibling nodes */ + for (n = ra->max_ra_blocks, i = ra->index + 1; + n > 0 && i < ra->ncmax; n--, i++) { + ptr2 = nilfs_btree_node_get_ptr(ra->node, i, ra->ncmax); + + ret = nilfs_btnode_submit_block(btnc, ptr2, 0, READA, + &ra_bh, &submit_ptr); + if (likely(!ret || ret == -EEXIST)) + brelse(ra_bh); + else if (ret != -EBUSY) + break; + if (!buffer_locked(bh)) + goto out_no_wait; + } + } + + wait_on_buffer(bh); + + out_no_wait: + if (!buffer_uptodate(bh)) { + brelse(bh); + return -EIO; + } + + out_check: + if (nilfs_btree_broken_node_block(bh)) { + clear_buffer_uptodate(bh); + brelse(bh); + return -EINVAL; + } + + *bhp = bh; + return 0; +} + +static int nilfs_btree_get_block(const struct nilfs_bmap *btree, __u64 ptr, + struct buffer_head **bhp) +{ + return __nilfs_btree_get_block(btree, ptr, bhp, NULL); +} + static int nilfs_btree_do_lookup(const struct nilfs_bmap *btree, struct nilfs_btree_path *path, __u64 key, __u64 *ptrp, int minlevel) -- cgit v1.2.3 From 4e13e66bee2d792c1aae21797f16c181024834eb Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Sun, 18 Jul 2010 10:42:25 +0900 Subject: nilfs2: introduce check flag to btree node buffer nilfs_btree_get_block() now may return untested buffer due to read-ahead. This adds a new flag for buffer heads so that the btree code can check whether the buffer is already verified or not. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/btree.c | 10 +++++++++- fs/nilfs2/page.c | 5 ++++- fs/nilfs2/page.h | 2 ++ 3 files changed, 15 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/nilfs2/btree.c b/fs/nilfs2/btree.c index 1b5321c0bcac..d3faa0bba171 100644 --- a/fs/nilfs2/btree.c +++ b/fs/nilfs2/btree.c @@ -370,8 +370,16 @@ static int nilfs_btree_node_broken(const struct nilfs_btree_node *node, int nilfs_btree_broken_node_block(struct buffer_head *bh) { - return nilfs_btree_node_broken((struct nilfs_btree_node *)bh->b_data, + int ret; + + if (buffer_nilfs_checked(bh)) + return 0; + + ret = nilfs_btree_node_broken((struct nilfs_btree_node *)bh->b_data, bh->b_size, bh->b_blocknr); + if (likely(!ret)) + set_buffer_nilfs_checked(bh); + return ret; } static struct nilfs_btree_node * diff --git a/fs/nilfs2/page.c b/fs/nilfs2/page.c index 8de3e1e48130..aab11db2cb08 100644 --- a/fs/nilfs2/page.c +++ b/fs/nilfs2/page.c @@ -37,7 +37,8 @@ #define NILFS_BUFFER_INHERENT_BITS \ ((1UL << BH_Uptodate) | (1UL << BH_Mapped) | (1UL << BH_NILFS_Node) | \ - (1UL << BH_NILFS_Volatile) | (1UL << BH_NILFS_Allocated)) + (1UL << BH_NILFS_Volatile) | (1UL << BH_NILFS_Allocated) | \ + (1UL << BH_NILFS_Checked)) static struct buffer_head * __nilfs_get_page_block(struct page *page, unsigned long block, pgoff_t index, @@ -129,6 +130,7 @@ void nilfs_forget_buffer(struct buffer_head *bh) lock_buffer(bh); clear_buffer_nilfs_volatile(bh); + clear_buffer_nilfs_checked(bh); clear_buffer_dirty(bh); if (nilfs_page_buffers_clean(page)) __nilfs_clear_page_dirty(page); @@ -480,6 +482,7 @@ void nilfs_clear_dirty_pages(struct address_space *mapping) lock_buffer(bh); clear_buffer_dirty(bh); clear_buffer_nilfs_volatile(bh); + clear_buffer_nilfs_checked(bh); clear_buffer_uptodate(bh); clear_buffer_mapped(bh); unlock_buffer(bh); diff --git a/fs/nilfs2/page.h b/fs/nilfs2/page.h index 8abca4d1c1f8..f53d8da41ed7 100644 --- a/fs/nilfs2/page.h +++ b/fs/nilfs2/page.h @@ -34,11 +34,13 @@ enum { BH_NILFS_Allocated = BH_PrivateStart, BH_NILFS_Node, BH_NILFS_Volatile, + BH_NILFS_Checked, }; BUFFER_FNS(NILFS_Allocated, nilfs_allocated) /* nilfs private buffers */ BUFFER_FNS(NILFS_Node, nilfs_node) /* nilfs node buffers */ BUFFER_FNS(NILFS_Volatile, nilfs_volatile) +BUFFER_FNS(NILFS_Checked, nilfs_checked) /* buffer is verified */ void nilfs_mark_buffer_dirty(struct buffer_head *bh); -- cgit v1.2.3 From 03bdb5ac58a2144dfe8cfd73347fdb9f57e2e062 Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Sun, 18 Jul 2010 10:42:26 +0900 Subject: nilfs2: apply read-ahead for nilfs_btree_lookup_contig This applies read-ahead to nilfs_btree_do_lookup and nilfs_btree_lookup_contig functions and extends them to read ahead siblings of level 1 btree nodes that hold data blocks. At present, the read-ahead is not applied to most btree operations; only get_block() callback function, which is used during read of regular files or directories, receives the benefit. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/btree.c | 50 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 17 deletions(-) (limited to 'fs') diff --git a/fs/nilfs2/btree.c b/fs/nilfs2/btree.c index d3faa0bba171..300c2bc00c3f 100644 --- a/fs/nilfs2/btree.c +++ b/fs/nilfs2/btree.c @@ -504,9 +504,11 @@ static int nilfs_btree_get_block(const struct nilfs_bmap *btree, __u64 ptr, static int nilfs_btree_do_lookup(const struct nilfs_bmap *btree, struct nilfs_btree_path *path, - __u64 key, __u64 *ptrp, int minlevel) + __u64 key, __u64 *ptrp, int minlevel, + int readahead) { struct nilfs_btree_node *node; + struct nilfs_btree_readahead_info p, *ra; __u64 ptr; int level, index, found, ncmax, ret; @@ -523,10 +525,20 @@ static int nilfs_btree_do_lookup(const struct nilfs_bmap *btree, ncmax = nilfs_btree_nchildren_per_block(btree); - for (level--; level >= minlevel; level--) { - ret = nilfs_btree_get_block(btree, ptr, &path[level].bp_bh); + while (--level >= minlevel) { + ra = NULL; + if (level == NILFS_BTREE_LEVEL_NODE_MIN && readahead) { + p.node = nilfs_btree_get_node(btree, path, level + 1, + &p.ncmax); + p.index = index; + p.max_ra_blocks = 7; + ra = &p; + } + ret = __nilfs_btree_get_block(btree, ptr, &path[level].bp_bh, + ra); if (ret < 0) return ret; + node = nilfs_btree_get_nonroot_node(path, level); if (nilfs_btree_bad_node(node, level)) return -EINVAL; @@ -601,7 +613,7 @@ static int nilfs_btree_lookup(const struct nilfs_bmap *btree, if (path == NULL) return -ENOMEM; - ret = nilfs_btree_do_lookup(btree, path, key, ptrp, level); + ret = nilfs_btree_do_lookup(btree, path, key, ptrp, level, 0); nilfs_btree_free_path(path); @@ -618,12 +630,13 @@ static int nilfs_btree_lookup_contig(const struct nilfs_bmap *btree, sector_t blocknr; int level = NILFS_BTREE_LEVEL_NODE_MIN; int ret, cnt, index, maxlevel, ncmax; + struct nilfs_btree_readahead_info p; path = nilfs_btree_alloc_path(); if (path == NULL) return -ENOMEM; - ret = nilfs_btree_do_lookup(btree, path, key, &ptr, level); + ret = nilfs_btree_do_lookup(btree, path, key, &ptr, level, 1); if (ret < 0) goto out; @@ -662,17 +675,20 @@ static int nilfs_btree_lookup_contig(const struct nilfs_bmap *btree, break; /* look-up right sibling node */ - node = nilfs_btree_get_node(btree, path, level + 1, &ncmax); - index = path[level + 1].bp_index + 1; - if (index >= nilfs_btree_node_get_nchildren(node) || - nilfs_btree_node_get_key(node, index) != key + cnt) + p.node = nilfs_btree_get_node(btree, path, level + 1, &p.ncmax); + p.index = path[level + 1].bp_index + 1; + p.max_ra_blocks = 7; + if (p.index >= nilfs_btree_node_get_nchildren(p.node) || + nilfs_btree_node_get_key(p.node, p.index) != key + cnt) break; - ptr2 = nilfs_btree_node_get_ptr(node, index, ncmax); - path[level + 1].bp_index = index; + ptr2 = nilfs_btree_node_get_ptr(p.node, p.index, p.ncmax); + path[level + 1].bp_index = p.index; brelse(path[level].bp_bh); path[level].bp_bh = NULL; - ret = nilfs_btree_get_block(btree, ptr2, &path[level].bp_bh); + + ret = __nilfs_btree_get_block(btree, ptr2, &path[level].bp_bh, + &p); if (ret < 0) goto out; node = nilfs_btree_get_nonroot_node(path, level); @@ -1147,7 +1163,7 @@ static int nilfs_btree_insert(struct nilfs_bmap *btree, __u64 key, __u64 ptr) return -ENOMEM; ret = nilfs_btree_do_lookup(btree, path, key, NULL, - NILFS_BTREE_LEVEL_NODE_MIN); + NILFS_BTREE_LEVEL_NODE_MIN, 0); if (ret != -ENOENT) { if (ret == 0) ret = -EEXIST; @@ -1484,7 +1500,7 @@ static int nilfs_btree_delete(struct nilfs_bmap *btree, __u64 key) return -ENOMEM; ret = nilfs_btree_do_lookup(btree, path, key, NULL, - NILFS_BTREE_LEVEL_NODE_MIN); + NILFS_BTREE_LEVEL_NODE_MIN, 0); if (ret < 0) goto out; @@ -1955,7 +1971,7 @@ static int nilfs_btree_propagate(struct nilfs_bmap *btree, level = NILFS_BTREE_LEVEL_DATA; } - ret = nilfs_btree_do_lookup(btree, path, key, NULL, level + 1); + ret = nilfs_btree_do_lookup(btree, path, key, NULL, level + 1, 0); if (ret < 0) { if (unlikely(ret == -ENOENT)) printk(KERN_CRIT "%s: key = %llu, level == %d\n", @@ -2147,7 +2163,7 @@ static int nilfs_btree_assign(struct nilfs_bmap *btree, level = NILFS_BTREE_LEVEL_DATA; } - ret = nilfs_btree_do_lookup(btree, path, key, NULL, level + 1); + ret = nilfs_btree_do_lookup(btree, path, key, NULL, level + 1, 0); if (ret < 0) { WARN_ON(ret == -ENOENT); goto out; @@ -2201,7 +2217,7 @@ static int nilfs_btree_mark(struct nilfs_bmap *btree, __u64 key, int level) if (path == NULL) return -ENOMEM; - ret = nilfs_btree_do_lookup(btree, path, key, &ptr, level + 1); + ret = nilfs_btree_do_lookup(btree, path, key, &ptr, level + 1, 0); if (ret < 0) { WARN_ON(ret == -ENOENT); goto out; -- cgit v1.2.3 From c5ca48aabe8b11674bf1102abe52d17ecc053f9c Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Thu, 22 Jul 2010 03:22:20 +0900 Subject: nilfs2: reject incompatible filesystem This forces nilfs to check compatibility of feature flags so as to reject a filesystem with unknown features when it mounts or remounts the filesystem. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/nilfs.h | 2 ++ fs/nilfs2/super.c | 39 +++++++++++++++++++++++++++++++++++++++ fs/nilfs2/the_nilfs.c | 20 ++++++++++++++++++++ 3 files changed, 61 insertions(+) (limited to 'fs') diff --git a/fs/nilfs2/nilfs.h b/fs/nilfs2/nilfs.h index cfedc48d78d9..0842d775b3e0 100644 --- a/fs/nilfs2/nilfs.h +++ b/fs/nilfs2/nilfs.h @@ -275,6 +275,8 @@ extern struct nilfs_super_block * nilfs_read_super_block(struct super_block *, u64, int, struct buffer_head **); extern int nilfs_store_magic_and_option(struct super_block *, struct nilfs_super_block *, char *); +extern int nilfs_check_feature_compatibility(struct super_block *, + struct nilfs_super_block *); extern void nilfs_set_log_cursor(struct nilfs_super_block *, struct the_nilfs *); extern struct nilfs_super_block **nilfs_prepare_super(struct nilfs_sb_info *, diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c index 164457316df1..26078b3407c9 100644 --- a/fs/nilfs2/super.c +++ b/fs/nilfs2/super.c @@ -790,6 +790,30 @@ int nilfs_store_magic_and_option(struct super_block *sb, return !parse_options(data, sb, 0) ? -EINVAL : 0 ; } +int nilfs_check_feature_compatibility(struct super_block *sb, + struct nilfs_super_block *sbp) +{ + __u64 features; + + features = le64_to_cpu(sbp->s_feature_incompat) & + ~NILFS_FEATURE_INCOMPAT_SUPP; + if (features) { + printk(KERN_ERR "NILFS: couldn't mount because of unsupported " + "optional features (%llx)\n", + (unsigned long long)features); + return -EINVAL; + } + features = le64_to_cpu(sbp->s_feature_compat_ro) & + ~NILFS_FEATURE_COMPAT_RO_SUPP; + if (!(sb->s_flags & MS_RDONLY) && features) { + printk(KERN_ERR "NILFS: couldn't mount RDWR because of " + "unsupported optional features (%llx)\n", + (unsigned long long)features); + return -EINVAL; + } + return 0; +} + /** * nilfs_fill_super() - initialize a super block instance * @sb: super_block @@ -984,11 +1008,26 @@ static int nilfs_remount(struct super_block *sb, int *flags, char *data) nilfs_cleanup_super(sbi); up_write(&nilfs->ns_sem); } else { + __u64 features; + /* * Mounting a RDONLY partition read-write, so reread and * store the current valid flag. (It may have been changed * by fsck since we originally mounted the partition.) */ + down_read(&nilfs->ns_sem); + features = le64_to_cpu(nilfs->ns_sbp[0]->s_feature_compat_ro) & + ~NILFS_FEATURE_COMPAT_RO_SUPP; + up_read(&nilfs->ns_sem); + if (features) { + printk(KERN_WARNING "NILFS (device %s): couldn't " + "remount RDWR because of unsupported optional " + "features (%llx)\n", + sb->s_id, (unsigned long long)features); + err = -EROFS; + goto restore_opts; + } + sb->s_flags &= ~MS_RDONLY; err = nilfs_attach_segment_constructor(sbi); diff --git a/fs/nilfs2/the_nilfs.c b/fs/nilfs2/the_nilfs.c index f2efc8c5be7f..da67b560f3c3 100644 --- a/fs/nilfs2/the_nilfs.c +++ b/fs/nilfs2/the_nilfs.c @@ -385,11 +385,23 @@ int load_nilfs(struct the_nilfs *nilfs, struct nilfs_sb_info *sbi) goto skip_recovery; if (s_flags & MS_RDONLY) { + __u64 features; + if (nilfs_test_opt(sbi, NORECOVERY)) { printk(KERN_INFO "NILFS: norecovery option specified. " "skipping roll-forward recovery\n"); goto skip_recovery; } + features = le64_to_cpu(nilfs->ns_sbp[0]->s_feature_compat_ro) & + ~NILFS_FEATURE_COMPAT_RO_SUPP; + if (features) { + printk(KERN_ERR "NILFS: couldn't proceed with " + "recovery because of unsupported optional " + "features (%llx)\n", + (unsigned long long)features); + err = -EROFS; + goto failed_unload; + } if (really_read_only) { printk(KERN_ERR "NILFS: write access " "unavailable, cannot proceed.\n"); @@ -644,6 +656,10 @@ int init_nilfs(struct the_nilfs *nilfs, struct nilfs_sb_info *sbi, char *data) if (err) goto out; + err = nilfs_check_feature_compatibility(sb, sbp); + if (err) + goto out; + blocksize = BLOCK_SIZE << le32_to_cpu(sbp->s_log_block_size); if (sb->s_blocksize != blocksize && !sb_set_blocksize(sb, blocksize)) { @@ -669,6 +685,10 @@ int init_nilfs(struct the_nilfs *nilfs, struct nilfs_sb_info *sbi, char *data) if (err) goto failed_sbh; + err = nilfs_check_feature_compatibility(sb, sbp); + if (err) + goto failed_sbh; + blocksize = BLOCK_SIZE << le32_to_cpu(sbp->s_log_block_size); if (sb->s_blocksize != blocksize) { int hw_blocksize = bdev_logical_block_size(sb->s_bdev); -- cgit v1.2.3 From 43d2932d88e4ab776dd388c20b003ebd5e1d1f1f Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Wed, 21 Jul 2010 14:22:21 +0200 Subject: quota: Use mark_inode_dirty_sync instead of mark_inode_dirty Quota code never touches file data. It just modifies i_blocks + i_bytes of inodes and inode flags of quota files. So use mark_inode_dirty_sync instead of mark_inode_dirty. Signed-off-by: Jan Kara --- fs/quota/dquot.c | 2 +- include/linux/quotaops.h | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'fs') diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c index b171221000fa..a7023bcfae4f 100644 --- a/fs/quota/dquot.c +++ b/fs/quota/dquot.c @@ -1992,7 +1992,7 @@ int dquot_disable(struct super_block *sb, int type, unsigned int flags) truncate_inode_pages(&toputinode[cnt]->i_data, 0); mutex_unlock(&toputinode[cnt]->i_mutex); - mark_inode_dirty(toputinode[cnt]); + mark_inode_dirty_sync(toputinode[cnt]); } mutex_unlock(&dqopt->dqonoff_mutex); } diff --git a/include/linux/quotaops.h b/include/linux/quotaops.h index 4881b49b1a9a..d50ba858cfe0 100644 --- a/include/linux/quotaops.h +++ b/include/linux/quotaops.h @@ -266,7 +266,7 @@ static inline int dquot_alloc_space_nodirty(struct inode *inode, qsize_t nr) static inline void dquot_alloc_space_nofail(struct inode *inode, qsize_t nr) { __dquot_alloc_space(inode, nr, DQUOT_SPACE_WARN|DQUOT_SPACE_NOFAIL); - mark_inode_dirty(inode); + mark_inode_dirty_sync(inode); } static inline int dquot_alloc_space(struct inode *inode, qsize_t nr) @@ -275,7 +275,7 @@ static inline int dquot_alloc_space(struct inode *inode, qsize_t nr) ret = dquot_alloc_space_nodirty(inode, nr); if (!ret) - mark_inode_dirty(inode); + mark_inode_dirty_sync(inode); return ret; } @@ -305,7 +305,7 @@ static inline int dquot_prealloc_block(struct inode *inode, qsize_t nr) ret = dquot_prealloc_block_nodirty(inode, nr); if (!ret) - mark_inode_dirty(inode); + mark_inode_dirty_sync(inode); return ret; } @@ -321,7 +321,7 @@ static inline int dquot_claim_block(struct inode *inode, qsize_t nr) ret = dquot_claim_space_nodirty(inode, nr << inode->i_blkbits); if (!ret) - mark_inode_dirty(inode); + mark_inode_dirty_sync(inode); return ret; } @@ -333,7 +333,7 @@ static inline void dquot_free_space_nodirty(struct inode *inode, qsize_t nr) static inline void dquot_free_space(struct inode *inode, qsize_t nr) { dquot_free_space_nodirty(inode, nr); - mark_inode_dirty(inode); + mark_inode_dirty_sync(inode); } static inline void dquot_free_block_nodirty(struct inode *inode, qsize_t nr) -- cgit v1.2.3 From aa32a796389bedbcf1c7714385b18714a0743810 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Fri, 23 Jul 2010 12:49:41 +0200 Subject: ext3: default to ordered mode data=writeback mode is dangerous as it leads to higher data loss and stale data exposure when systems crash. It should not be the default, especially when all major distros ensure their ext3 filesystems default to ordered mode. Change the default mode to the safer data=ordered mode, because we should be caring far more about avoiding stale data exposure than performance. CC: linux-ext4@vger.kernel.org Signed-off-by: Dave Chinner Acked-by: Eric Sandeen Signed-off-by: Jan Kara --- fs/ext3/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'fs') diff --git a/fs/ext3/Kconfig b/fs/ext3/Kconfig index 522b15498f45..e8c6ba0e4a3e 100644 --- a/fs/ext3/Kconfig +++ b/fs/ext3/Kconfig @@ -31,6 +31,7 @@ config EXT3_FS config EXT3_DEFAULTS_TO_ORDERED bool "Default to 'data=ordered' in ext3" depends on EXT3_FS + default y help The journal mode options for ext3 have different tradeoffs between when data is guaranteed to be on disk and -- cgit v1.2.3 From 6ecd7c2dd9f5dd4f6e8f65c8027159f9c73b0e4c Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 20 Jul 2010 22:09:02 +0200 Subject: gfs2: use workqueue instead of slow-work Workqueue can now handle high concurrency. Convert gfs to use workqueue instead of slow-work. * Steven pointed out that recovery path might be run from allocation path and thus requires forward progress guarantee without memory allocation. Create and use gfs_recovery_wq with rescuer. Please note that forward progress wasn't guaranteed with slow-work. * Updated to use non-reentrant workqueue. Signed-off-by: Tejun Heo Acked-by: Steven Whitehouse --- fs/gfs2/Kconfig | 1 - fs/gfs2/incore.h | 3 +-- fs/gfs2/main.c | 14 ++++++++------ fs/gfs2/ops_fstype.c | 8 ++++---- fs/gfs2/recovery.c | 54 ++++++++++++++++++++-------------------------------- fs/gfs2/recovery.h | 6 ++++-- fs/gfs2/sys.c | 3 ++- 7 files changed, 40 insertions(+), 49 deletions(-) (limited to 'fs') diff --git a/fs/gfs2/Kconfig b/fs/gfs2/Kconfig index a47b43107112..cc9665522148 100644 --- a/fs/gfs2/Kconfig +++ b/fs/gfs2/Kconfig @@ -7,7 +7,6 @@ config GFS2_FS select IP_SCTP if DLM_SCTP select FS_POSIX_ACL select CRC32 - select SLOW_WORK select QUOTACTL help A cluster filesystem. diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h index b5d7363b22da..dd8f2e63d15a 100644 --- a/fs/gfs2/incore.h +++ b/fs/gfs2/incore.h @@ -12,7 +12,6 @@ #include #include -#include #include #include @@ -383,7 +382,7 @@ struct gfs2_journal_extent { struct gfs2_jdesc { struct list_head jd_list; struct list_head extent_list; - struct slow_work jd_work; + struct work_struct jd_work; struct inode *jd_inode; unsigned long jd_flags; #define JDF_RECOVERY 1 diff --git a/fs/gfs2/main.c b/fs/gfs2/main.c index fb2a5f93b7c3..b1e9630eb46a 100644 --- a/fs/gfs2/main.c +++ b/fs/gfs2/main.c @@ -15,7 +15,6 @@ #include #include #include -#include #include "gfs2.h" #include "incore.h" @@ -24,6 +23,7 @@ #include "util.h" #include "glock.h" #include "quota.h" +#include "recovery.h" static struct shrinker qd_shrinker = { .shrink = gfs2_shrink_qd_memory, @@ -138,9 +138,11 @@ static int __init init_gfs2_fs(void) if (error) goto fail_unregister; - error = slow_work_register_user(THIS_MODULE); - if (error) - goto fail_slow; + error = -ENOMEM; + gfs_recovery_wq = alloc_workqueue("gfs_recovery", + WQ_NON_REENTRANT | WQ_RESCUER, 0); + if (!gfs_recovery_wq) + goto fail_wq; gfs2_register_debugfs(); @@ -148,7 +150,7 @@ static int __init init_gfs2_fs(void) return 0; -fail_slow: +fail_wq: unregister_filesystem(&gfs2meta_fs_type); fail_unregister: unregister_filesystem(&gfs2_fs_type); @@ -190,7 +192,7 @@ static void __exit exit_gfs2_fs(void) gfs2_unregister_debugfs(); unregister_filesystem(&gfs2_fs_type); unregister_filesystem(&gfs2meta_fs_type); - slow_work_unregister_user(THIS_MODULE); + destroy_workqueue(gfs_recovery_wq); kmem_cache_destroy(gfs2_quotad_cachep); kmem_cache_destroy(gfs2_rgrpd_cachep); diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c index 3593b3a7290e..9a08e1bd6fbd 100644 --- a/fs/gfs2/ops_fstype.c +++ b/fs/gfs2/ops_fstype.c @@ -17,7 +17,6 @@ #include #include #include -#include #include #include "gfs2.h" @@ -673,7 +672,7 @@ static int gfs2_jindex_hold(struct gfs2_sbd *sdp, struct gfs2_holder *ji_gh) break; INIT_LIST_HEAD(&jd->extent_list); - slow_work_init(&jd->jd_work, &gfs2_recover_ops); + INIT_WORK(&jd->jd_work, gfs2_recover_func); jd->jd_inode = gfs2_lookupi(sdp->sd_jindex, &name, 1); if (!jd->jd_inode || IS_ERR(jd->jd_inode)) { if (!jd->jd_inode) @@ -782,7 +781,8 @@ static int init_journal(struct gfs2_sbd *sdp, int undo) if (sdp->sd_lockstruct.ls_first) { unsigned int x; for (x = 0; x < sdp->sd_journals; x++) { - error = gfs2_recover_journal(gfs2_jdesc_find(sdp, x)); + error = gfs2_recover_journal(gfs2_jdesc_find(sdp, x), + true); if (error) { fs_err(sdp, "error recovering journal %u: %d\n", x, error); @@ -792,7 +792,7 @@ static int init_journal(struct gfs2_sbd *sdp, int undo) gfs2_others_may_mount(sdp); } else if (!sdp->sd_args.ar_spectator) { - error = gfs2_recover_journal(sdp->sd_jdesc); + error = gfs2_recover_journal(sdp->sd_jdesc, true); if (error) { fs_err(sdp, "error recovering my journal: %d\n", error); goto fail_jinode_gh; diff --git a/fs/gfs2/recovery.c b/fs/gfs2/recovery.c index 4b9bece3d437..f7f89a94a5a4 100644 --- a/fs/gfs2/recovery.c +++ b/fs/gfs2/recovery.c @@ -14,7 +14,6 @@ #include #include #include -#include #include "gfs2.h" #include "incore.h" @@ -28,6 +27,8 @@ #include "util.h" #include "dir.h" +struct workqueue_struct *gfs_recovery_wq; + int gfs2_replay_read_block(struct gfs2_jdesc *jd, unsigned int blk, struct buffer_head **bh) { @@ -443,23 +444,7 @@ static void gfs2_recovery_done(struct gfs2_sbd *sdp, unsigned int jid, kobject_uevent_env(&sdp->sd_kobj, KOBJ_CHANGE, envp); } -static int gfs2_recover_get_ref(struct slow_work *work) -{ - struct gfs2_jdesc *jd = container_of(work, struct gfs2_jdesc, jd_work); - if (test_and_set_bit(JDF_RECOVERY, &jd->jd_flags)) - return -EBUSY; - return 0; -} - -static void gfs2_recover_put_ref(struct slow_work *work) -{ - struct gfs2_jdesc *jd = container_of(work, struct gfs2_jdesc, jd_work); - clear_bit(JDF_RECOVERY, &jd->jd_flags); - smp_mb__after_clear_bit(); - wake_up_bit(&jd->jd_flags, JDF_RECOVERY); -} - -static void gfs2_recover_work(struct slow_work *work) +void gfs2_recover_func(struct work_struct *work) { struct gfs2_jdesc *jd = container_of(work, struct gfs2_jdesc, jd_work); struct gfs2_inode *ip = GFS2_I(jd->jd_inode); @@ -578,7 +563,7 @@ static void gfs2_recover_work(struct slow_work *work) gfs2_glock_dq_uninit(&j_gh); fs_info(sdp, "jid=%u: Done\n", jd->jd_jid); - return; + goto done; fail_gunlock_tr: gfs2_glock_dq_uninit(&t_gh); @@ -590,32 +575,35 @@ fail_gunlock_j: } fs_info(sdp, "jid=%u: %s\n", jd->jd_jid, (error) ? "Failed" : "Done"); - fail: gfs2_recovery_done(sdp, jd->jd_jid, LM_RD_GAVEUP); +done: + clear_bit(JDF_RECOVERY, &jd->jd_flags); + smp_mb__after_clear_bit(); + wake_up_bit(&jd->jd_flags, JDF_RECOVERY); } -struct slow_work_ops gfs2_recover_ops = { - .owner = THIS_MODULE, - .get_ref = gfs2_recover_get_ref, - .put_ref = gfs2_recover_put_ref, - .execute = gfs2_recover_work, -}; - - static int gfs2_recovery_wait(void *word) { schedule(); return 0; } -int gfs2_recover_journal(struct gfs2_jdesc *jd) +int gfs2_recover_journal(struct gfs2_jdesc *jd, bool wait) { int rv; - rv = slow_work_enqueue(&jd->jd_work); - if (rv) - return rv; - wait_on_bit(&jd->jd_flags, JDF_RECOVERY, gfs2_recovery_wait, TASK_UNINTERRUPTIBLE); + + if (test_and_set_bit(JDF_RECOVERY, &jd->jd_flags)) + return -EBUSY; + + /* we have JDF_RECOVERY, queue should always succeed */ + rv = queue_work(gfs_recovery_wq, &jd->jd_work); + BUG_ON(!rv); + + if (wait) + wait_on_bit(&jd->jd_flags, JDF_RECOVERY, gfs2_recovery_wait, + TASK_UNINTERRUPTIBLE); + return 0; } diff --git a/fs/gfs2/recovery.h b/fs/gfs2/recovery.h index 1616ac22569a..2226136c7647 100644 --- a/fs/gfs2/recovery.h +++ b/fs/gfs2/recovery.h @@ -12,6 +12,8 @@ #include "incore.h" +extern struct workqueue_struct *gfs_recovery_wq; + static inline void gfs2_replay_incr_blk(struct gfs2_sbd *sdp, unsigned int *blk) { if (++*blk == sdp->sd_jdesc->jd_blocks) @@ -27,8 +29,8 @@ extern void gfs2_revoke_clean(struct gfs2_sbd *sdp); extern int gfs2_find_jhead(struct gfs2_jdesc *jd, struct gfs2_log_header_host *head); -extern int gfs2_recover_journal(struct gfs2_jdesc *gfs2_jd); -extern struct slow_work_ops gfs2_recover_ops; +extern int gfs2_recover_journal(struct gfs2_jdesc *gfs2_jd, bool wait); +extern void gfs2_recover_func(struct work_struct *work); #endif /* __RECOVERY_DOT_H__ */ diff --git a/fs/gfs2/sys.c b/fs/gfs2/sys.c index 37f5393e68e6..6b60316ae327 100644 --- a/fs/gfs2/sys.c +++ b/fs/gfs2/sys.c @@ -25,6 +25,7 @@ #include "quota.h" #include "util.h" #include "glops.h" +#include "recovery.h" struct gfs2_attr { struct attribute attr; @@ -352,7 +353,7 @@ static ssize_t recover_store(struct gfs2_sbd *sdp, const char *buf, size_t len) list_for_each_entry(jd, &sdp->sd_jindex_list, jd_list) { if (jd->jd_jid != jid) continue; - rv = slow_work_enqueue(&jd->jd_work); + rv = gfs2_recover_journal(jd, false); break; } out: -- cgit v1.2.3 From 252af5214682191e34e57204e1a31924fb82c207 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Thu, 22 Jul 2010 13:49:08 -0700 Subject: ceph: fix d_release dop for snapdir, snapped dentries We need to set the d_release dop for snapdir and snapped dentries so that the ceph_dentry_info struct gets released. We also use the dcache to cache readdir results when possible, which only works if we know when dentries are dropped from the cache. Since we don't use the dcache for readdir in the hidden snapdir, avoid that case in ceph_dentry_release. Signed-off-by: Sage Weil --- fs/ceph/dir.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c index ea36ba9960d3..f94ed3c7f6a5 100644 --- a/fs/ceph/dir.c +++ b/fs/ceph/dir.c @@ -1014,18 +1014,22 @@ out_touch: /* * When a dentry is released, clear the dir I_COMPLETE if it was part - * of the current dir gen. + * of the current dir gen or if this is in the snapshot namespace. */ static void ceph_dentry_release(struct dentry *dentry) { struct ceph_dentry_info *di = ceph_dentry(dentry); struct inode *parent_inode = dentry->d_parent->d_inode; + u64 snapid = ceph_snap(parent_inode); - if (parent_inode) { + dout("dentry_release %p parent %p\n", dentry, parent_inode); + + if (parent_inode && snapid != CEPH_SNAPDIR) { struct ceph_inode_info *ci = ceph_inode(parent_inode); spin_lock(&parent_inode->i_lock); - if (ci->i_shared_gen == di->lease_shared_gen) { + if (ci->i_shared_gen == di->lease_shared_gen || + snapid <= CEPH_MAXSNAP) { dout(" clearing %p complete (d_release)\n", parent_inode); ci->i_ceph_flags &= ~CEPH_I_COMPLETE; @@ -1242,7 +1246,9 @@ struct dentry_operations ceph_dentry_ops = { struct dentry_operations ceph_snapdir_dentry_ops = { .d_revalidate = ceph_snapdir_d_revalidate, + .d_release = ceph_dentry_release, }; struct dentry_operations ceph_snap_dentry_ops = { + .d_release = ceph_dentry_release, }; -- cgit v1.2.3 From bc4fdca85734d12cd2c7a25c52323ef6e6e5adef Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Tue, 20 Jul 2010 16:19:56 -0700 Subject: ceph: fix pg_mapping leak on pg_temp updates Free the ceph_pg_mapping structs when they are removed from the pg_temp rbtree. Also fix a leak in the __insert_pg_mapping() error path. Signed-off-by: Sage Weil --- fs/ceph/osdmap.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) (limited to 'fs') diff --git a/fs/ceph/osdmap.c b/fs/ceph/osdmap.c index 277f8b339577..416d46adbf87 100644 --- a/fs/ceph/osdmap.c +++ b/fs/ceph/osdmap.c @@ -831,12 +831,13 @@ struct ceph_osdmap *osdmap_apply_incremental(void **p, void *end, /* remove any? */ while (rbp && pgid_cmp(rb_entry(rbp, struct ceph_pg_mapping, node)->pgid, pgid) <= 0) { - struct rb_node *cur = rbp; + struct ceph_pg_mapping *cur = + rb_entry(rbp, struct ceph_pg_mapping, node); + rbp = rb_next(rbp); - dout(" removed pg_temp %llx\n", - *(u64 *)&rb_entry(cur, struct ceph_pg_mapping, - node)->pgid); - rb_erase(cur, &map->pg_temp); + dout(" removed pg_temp %llx\n", *(u64 *)&cur->pgid); + rb_erase(&cur->node, &map->pg_temp); + kfree(cur); } if (pglen) { @@ -852,19 +853,22 @@ struct ceph_osdmap *osdmap_apply_incremental(void **p, void *end, for (j = 0; j < pglen; j++) pg->osds[j] = ceph_decode_32(p); err = __insert_pg_mapping(pg, &map->pg_temp); - if (err) + if (err) { + kfree(pg); goto bad; + } dout(" added pg_temp %llx len %d\n", *(u64 *)&pgid, pglen); } } while (rbp) { - struct rb_node *cur = rbp; + struct ceph_pg_mapping *cur = + rb_entry(rbp, struct ceph_pg_mapping, node); + rbp = rb_next(rbp); - dout(" removed pg_temp %llx\n", - *(u64 *)&rb_entry(cur, struct ceph_pg_mapping, - node)->pgid); - rb_erase(cur, &map->pg_temp); + dout(" removed pg_temp %llx\n", *(u64 *)&cur->pgid); + rb_erase(&cur->node, &map->pg_temp); + kfree(cur); } /* ignore the rest */ -- cgit v1.2.3 From 8c696737aa61316a252c4514d09dd163f1464d33 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Thu, 22 Jul 2010 14:11:56 -0700 Subject: ceph: fix leak of dentry in ceph_init_dentry() error path If we fail to allocate a ceph_dentry_info, don't leak the dn reference. Signed-off-by: Sage Weil --- fs/ceph/inode.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c index 8f9b9fe8ef9f..3582e79f46e0 100644 --- a/fs/ceph/inode.c +++ b/fs/ceph/inode.c @@ -1199,8 +1199,10 @@ retry_lookup: goto out; } err = ceph_init_dentry(dn); - if (err < 0) + if (err < 0) { + dput(dn); goto out; + } } else if (dn->d_inode && (ceph_ino(dn->d_inode) != vino.ino || ceph_snap(dn->d_inode) != vino.snap)) { -- cgit v1.2.3 From 1dadcce358a4c4078e1ea0bc4365c3f67b8e373e Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Fri, 23 Jul 2010 13:54:21 -0700 Subject: ceph: fix dentry lease release When we embed a dentry lease release notification in a request, invalidate our lease so we don't think we still have it. Otherwise we can get all sorts of incorrect client behavior when multiple clients are interacting with the same part of the namespace. Signed-off-by: Sage Weil --- fs/ceph/caps.c | 1 + 1 file changed, 1 insertion(+) (limited to 'fs') diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index 74144d6389f0..6afc1affb50a 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -2984,6 +2984,7 @@ int ceph_encode_dentry_release(void **p, struct dentry *dentry, memcpy(*p, dentry->d_name.name, dentry->d_name.len); *p += dentry->d_name.len; rel->dname_seq = cpu_to_le32(di->lease_seq); + __ceph_mdsc_drop_dentry_lease(dentry); } spin_unlock(&dentry->d_lock); return ret; -- cgit v1.2.3 From c28e69d9332aab739920082a0a5677d861390824 Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Sat, 24 Jul 2010 17:09:10 +0900 Subject: nilfs2: simplify nilfs_get_page function Implementation of nilfs_get_page() is a bit old as below: - A common read_mapping_page inline function is now available instead of its read_cache_page use. - wait_on_page_locked() use in the function is eliminable since read_cache_page function does the same thing through wait_on_page_read(). - PageUptodate() check is eliminable for the same reason. This renews nilfs_get_page() based on these points. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/dir.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'fs') diff --git a/fs/nilfs2/dir.c b/fs/nilfs2/dir.c index 85c89dfc71f0..d8d183e6d095 100644 --- a/fs/nilfs2/dir.c +++ b/fs/nilfs2/dir.c @@ -199,13 +199,10 @@ fail: static struct page *nilfs_get_page(struct inode *dir, unsigned long n) { struct address_space *mapping = dir->i_mapping; - struct page *page = read_cache_page(mapping, n, - (filler_t *)mapping->a_ops->readpage, NULL); + struct page *page = read_mapping_page(mapping, n, NULL); + if (!IS_ERR(page)) { - wait_on_page_locked(page); kmap(page); - if (!PageUptodate(page)) - goto fail; if (!PageChecked(page)) nilfs_check_page(page); if (PageError(page)) -- cgit v1.2.3 From 40f2b6ffe525e975203c1621d4d4abaa7689b674 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Sat, 24 Jul 2010 11:10:09 +0200 Subject: fscache: fix build on !CONFIG_SYSCTL Commit 8b8edefa (fscache: convert object to use workqueue instead of slow-work) made fscache_exit() call unregister_sysctl_table() unconditionally breaking build when sysctl is disabled. Fix it by putting it inside CONFIG_SYSCTL. Signed-off-by: Tejun Heo Reported-by: Randy Dunlap Cc: David Howells --- fs/fscache/main.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'fs') diff --git a/fs/fscache/main.c b/fs/fscache/main.c index 500936d9fff2..f9d856773f79 100644 --- a/fs/fscache/main.c +++ b/fs/fscache/main.c @@ -186,7 +186,9 @@ static void __exit fscache_exit(void) kobject_put(fscache_root); kmem_cache_destroy(fscache_cookie_jar); +#ifdef CONFIG_SYSCTL unregister_sysctl_table(fscache_sysctl_header); +#endif fscache_proc_cleanup(); destroy_workqueue(fscache_op_wq); destroy_workqueue(fscache_object_wq); -- cgit v1.2.3 From 25848b3ec681c7018e3746dd850c1e8ed0a3dd6b Mon Sep 17 00:00:00 2001 From: "Robert P. J. Day" Date: Sat, 24 Jul 2010 06:41:18 -0400 Subject: ceph: Correct obvious typo of Kconfig variable "CRYPTO_AES" Signed-off-by: Robert P. J. Day Signed-off-by: Sage Weil --- fs/ceph/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/ceph/Kconfig b/fs/ceph/Kconfig index 04b8280582a9..bc87b9c1d27e 100644 --- a/fs/ceph/Kconfig +++ b/fs/ceph/Kconfig @@ -2,7 +2,7 @@ config CEPH_FS tristate "Ceph distributed file system (EXPERIMENTAL)" depends on INET && EXPERIMENTAL select LIBCRC32C - select CONFIG_CRYPTO_AES + select CRYPTO_AES help Choose Y or M here to include support for mounting the experimental Ceph distributed file system. Ceph is an extremely -- cgit v1.2.3 From 6cda9fa2575ec0869fe77b0bdf295c0e51868cab Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Sun, 25 Jul 2010 20:39:03 +0900 Subject: nilfs2: avoid rec_len overflow with 64KB block size With 64KB blocksize, a directory entry can have size 64KB which does not fit into 16 bits we have for entry length. So this patch stores 0xffff instead and converts value when read from / written to disk. Nilfs derives its directory implementation from ext2 filesystem, and this draws upon the corresponding change on ext2. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/dir.c | 26 ++++++++++++++------------ include/linux/nilfs2_fs.h | 18 ++++++++++++++++++ 2 files changed, 32 insertions(+), 12 deletions(-) (limited to 'fs') diff --git a/fs/nilfs2/dir.c b/fs/nilfs2/dir.c index d8d183e6d095..b60277b44468 100644 --- a/fs/nilfs2/dir.c +++ b/fs/nilfs2/dir.c @@ -141,7 +141,7 @@ static void nilfs_check_page(struct page *page) } for (offs = 0; offs <= limit - NILFS_DIR_REC_LEN(1); offs += rec_len) { p = (struct nilfs_dir_entry *)(kaddr + offs); - rec_len = le16_to_cpu(p->rec_len); + rec_len = nilfs_rec_len_from_disk(p->rec_len); if (rec_len < NILFS_DIR_REC_LEN(1)) goto Eshort; @@ -235,7 +235,8 @@ nilfs_match(int len, const unsigned char *name, struct nilfs_dir_entry *de) */ static struct nilfs_dir_entry *nilfs_next_entry(struct nilfs_dir_entry *p) { - return (struct nilfs_dir_entry *)((char *)p + le16_to_cpu(p->rec_len)); + return (struct nilfs_dir_entry *)((char *)p + + nilfs_rec_len_from_disk(p->rec_len)); } static unsigned char @@ -326,7 +327,7 @@ static int nilfs_readdir(struct file *filp, void *dirent, filldir_t filldir) goto success; } } - filp->f_pos += le16_to_cpu(de->rec_len); + filp->f_pos += nilfs_rec_len_from_disk(de->rec_len); } nilfs_put_page(page); } @@ -441,7 +442,7 @@ void nilfs_set_link(struct inode *dir, struct nilfs_dir_entry *de, struct page *page, struct inode *inode) { unsigned from = (char *) de - (char *) page_address(page); - unsigned to = from + le16_to_cpu(de->rec_len); + unsigned to = from + nilfs_rec_len_from_disk(de->rec_len); struct address_space *mapping = page->mapping; int err; @@ -497,7 +498,7 @@ int nilfs_add_link(struct dentry *dentry, struct inode *inode) /* We hit i_size */ name_len = 0; rec_len = chunk_size; - de->rec_len = cpu_to_le16(chunk_size); + de->rec_len = nilfs_rec_len_to_disk(chunk_size); de->inode = 0; goto got_it; } @@ -511,7 +512,7 @@ int nilfs_add_link(struct dentry *dentry, struct inode *inode) if (nilfs_match(namelen, name, de)) goto out_unlock; name_len = NILFS_DIR_REC_LEN(de->name_len); - rec_len = le16_to_cpu(de->rec_len); + rec_len = nilfs_rec_len_from_disk(de->rec_len); if (!de->inode && rec_len >= reclen) goto got_it; if (rec_len >= name_len + reclen) @@ -534,8 +535,8 @@ got_it: struct nilfs_dir_entry *de1; de1 = (struct nilfs_dir_entry *)((char *)de + name_len); - de1->rec_len = cpu_to_le16(rec_len - name_len); - de->rec_len = cpu_to_le16(name_len); + de1->rec_len = nilfs_rec_len_to_disk(rec_len - name_len); + de->rec_len = nilfs_rec_len_to_disk(name_len); de = de1; } de->name_len = namelen; @@ -566,7 +567,8 @@ int nilfs_delete_entry(struct nilfs_dir_entry *dir, struct page *page) struct inode *inode = mapping->host; char *kaddr = page_address(page); unsigned from = ((char *)dir - kaddr) & ~(nilfs_chunk_size(inode) - 1); - unsigned to = ((char *)dir - kaddr) + le16_to_cpu(dir->rec_len); + unsigned to = ((char *)dir - kaddr) + + nilfs_rec_len_from_disk(dir->rec_len); struct nilfs_dir_entry *pde = NULL; struct nilfs_dir_entry *de = (struct nilfs_dir_entry *)(kaddr + from); int err; @@ -587,7 +589,7 @@ int nilfs_delete_entry(struct nilfs_dir_entry *dir, struct page *page) err = nilfs_prepare_chunk(page, mapping, from, to); BUG_ON(err); if (pde) - pde->rec_len = cpu_to_le16(to - from); + pde->rec_len = nilfs_rec_len_to_disk(to - from); dir->inode = 0; nilfs_commit_chunk(page, mapping, from, to); inode->i_ctime = inode->i_mtime = CURRENT_TIME; @@ -621,14 +623,14 @@ int nilfs_make_empty(struct inode *inode, struct inode *parent) memset(kaddr, 0, chunk_size); de = (struct nilfs_dir_entry *)kaddr; de->name_len = 1; - de->rec_len = cpu_to_le16(NILFS_DIR_REC_LEN(1)); + de->rec_len = nilfs_rec_len_to_disk(NILFS_DIR_REC_LEN(1)); memcpy(de->name, ".\0\0", 4); de->inode = cpu_to_le64(inode->i_ino); nilfs_set_de_type(de, inode); de = (struct nilfs_dir_entry *)(kaddr + NILFS_DIR_REC_LEN(1)); de->name_len = 2; - de->rec_len = cpu_to_le16(chunk_size - NILFS_DIR_REC_LEN(1)); + de->rec_len = nilfs_rec_len_to_disk(chunk_size - NILFS_DIR_REC_LEN(1)); de->inode = cpu_to_le64(parent->i_ino); memcpy(de->name, "..\0", 4); nilfs_set_de_type(de, inode); diff --git a/include/linux/nilfs2_fs.h b/include/linux/nilfs2_fs.h index 7dd4cd494490..970828a5ffc5 100644 --- a/include/linux/nilfs2_fs.h +++ b/include/linux/nilfs2_fs.h @@ -326,7 +326,25 @@ enum { #define NILFS_DIR_ROUND (NILFS_DIR_PAD - 1) #define NILFS_DIR_REC_LEN(name_len) (((name_len) + 12 + NILFS_DIR_ROUND) & \ ~NILFS_DIR_ROUND) +#define NILFS_MAX_REC_LEN ((1<<16)-1) +static inline unsigned nilfs_rec_len_from_disk(__le16 dlen) +{ + unsigned len = le16_to_cpu(dlen); + + if (len == NILFS_MAX_REC_LEN) + return 1 << 16; + return len; +} + +static inline __le16 nilfs_rec_len_to_disk(unsigned len) +{ + if (len == (1 << 16)) + return cpu_to_le16(NILFS_MAX_REC_LEN); + else if (len > (1 << 16)) + BUG(); + return cpu_to_le16(len); +} /** * struct nilfs_finfo - file information -- cgit v1.2.3 From 89c0fd014d34d409a7b196667c2b9a4813b6c968 Mon Sep 17 00:00:00 2001 From: Ryusuke Konishi Date: Sun, 25 Jul 2010 22:44:53 +0900 Subject: nilfs2: reject filesystem with unsupported block size This inserts sanity check that refuses to mount a filesystem with unsupported block size. Previously, kernel code of nilfs was looking only limitation of devices though mkfs.nilfs2 limits the range of block sizes; there was no check that prevents rec_len overflow with larger block sizes. With this change, block sizes larger than 64KB or smaller than 1KB will get rejected explicitly by kernel. Signed-off-by: Ryusuke Konishi --- fs/nilfs2/the_nilfs.c | 9 ++++++++- include/linux/nilfs2_fs.h | 6 ++++++ 2 files changed, 14 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/nilfs2/the_nilfs.c b/fs/nilfs2/the_nilfs.c index da67b560f3c3..37de1f062d81 100644 --- a/fs/nilfs2/the_nilfs.c +++ b/fs/nilfs2/the_nilfs.c @@ -671,7 +671,7 @@ int init_nilfs(struct the_nilfs *nilfs, struct nilfs_sb_info *sbi, char *data) goto out; } - blocksize = sb_min_blocksize(sb, BLOCK_SIZE); + blocksize = sb_min_blocksize(sb, NILFS_MIN_BLOCK_SIZE); if (!blocksize) { printk(KERN_ERR "NILFS: unable to set blocksize\n"); err = -EINVAL; @@ -690,6 +690,13 @@ int init_nilfs(struct the_nilfs *nilfs, struct nilfs_sb_info *sbi, char *data) goto failed_sbh; blocksize = BLOCK_SIZE << le32_to_cpu(sbp->s_log_block_size); + if (blocksize < NILFS_MIN_BLOCK_SIZE || + blocksize > NILFS_MAX_BLOCK_SIZE) { + printk(KERN_ERR "NILFS: couldn't mount because of unsupported " + "filesystem blocksize %d\n", blocksize); + err = -EINVAL; + goto failed_sbh; + } if (sb->s_blocksize != blocksize) { int hw_blocksize = bdev_logical_block_size(sb->s_bdev); diff --git a/include/linux/nilfs2_fs.h b/include/linux/nilfs2_fs.h index 970828a5ffc5..f5487b6f91ed 100644 --- a/include/linux/nilfs2_fs.h +++ b/include/linux/nilfs2_fs.h @@ -286,6 +286,12 @@ struct nilfs_super_block { #define NILFS_NAME_LEN 255 +/* + * Block size limitations + */ +#define NILFS_MIN_BLOCK_SIZE 1024 +#define NILFS_MAX_BLOCK_SIZE 65536 + /* * The new version of the directory entry. Since V0 structures are * stored in intel byte order, and the name_len field could never be -- cgit v1.2.3 From 288699fecaffa1ef8f75f92020cbb593a772e487 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 23 Jun 2010 18:11:15 +1000 Subject: xfs: drop dmapi hooks Dmapi support was never merged upstream, but we still have a lot of hooks bloating XFS for it, all over the fast pathes of the filesystem. This patch drops over 700 lines of dmapi overhead. If we'll ever get HSM support in mainline at least the namespace events can be done much saner in the VFS instead of the individual filesystem, so it's not like this is much help for future work. Signed-off-by: Christoph Hellwig Reviewed-by: Dave Chinner --- fs/xfs/Makefile | 3 +- fs/xfs/linux-2.6/xfs_aops.c | 1 - fs/xfs/linux-2.6/xfs_buf.c | 1 - fs/xfs/linux-2.6/xfs_dmapi_priv.h | 28 ------- fs/xfs/linux-2.6/xfs_export.c | 1 - fs/xfs/linux-2.6/xfs_file.c | 85 ------------------- fs/xfs/linux-2.6/xfs_fs_subr.c | 4 - fs/xfs/linux-2.6/xfs_fs_subr.h | 25 ------ fs/xfs/linux-2.6/xfs_ioctl.c | 12 +-- fs/xfs/linux-2.6/xfs_ioctl32.c | 1 - fs/xfs/linux-2.6/xfs_iops.c | 1 - fs/xfs/linux-2.6/xfs_linux.h | 1 - fs/xfs/linux-2.6/xfs_quotaops.c | 1 - fs/xfs/linux-2.6/xfs_super.c | 67 ++------------- fs/xfs/linux-2.6/xfs_super.h | 7 -- fs/xfs/linux-2.6/xfs_sync.c | 1 - fs/xfs/linux-2.6/xfs_trace.c | 1 - fs/xfs/quota/xfs_dquot.c | 1 - fs/xfs/quota/xfs_dquot_item.c | 1 - fs/xfs/quota/xfs_qm.c | 1 - fs/xfs/quota/xfs_qm_bhv.c | 1 - fs/xfs/quota/xfs_qm_stats.c | 1 - fs/xfs/quota/xfs_qm_syscalls.c | 1 - fs/xfs/quota/xfs_trans_dquot.c | 1 - fs/xfs/support/debug.c | 1 - fs/xfs/xfs_alloc.c | 1 - fs/xfs/xfs_alloc_btree.c | 1 - fs/xfs/xfs_attr.c | 1 - fs/xfs/xfs_attr_leaf.c | 1 - fs/xfs/xfs_bmap.c | 23 ----- fs/xfs/xfs_bmap_btree.c | 1 - fs/xfs/xfs_btree.c | 1 - fs/xfs/xfs_buf_item.c | 1 - fs/xfs/xfs_da_btree.c | 1 - fs/xfs/xfs_dfrag.c | 1 - fs/xfs/xfs_dir2.c | 1 - fs/xfs/xfs_dir2_block.c | 1 - fs/xfs/xfs_dir2_data.c | 1 - fs/xfs/xfs_dir2_leaf.c | 1 - fs/xfs/xfs_dir2_node.c | 1 - fs/xfs/xfs_dir2_sf.c | 1 - fs/xfs/xfs_dmapi.h | 170 ------------------------------------- fs/xfs/xfs_dmops.c | 55 ------------ fs/xfs/xfs_error.c | 1 - fs/xfs/xfs_extfree_item.c | 1 - fs/xfs/xfs_filestream.c | 1 - fs/xfs/xfs_fsops.c | 1 - fs/xfs/xfs_ialloc.c | 1 - fs/xfs/xfs_ialloc_btree.c | 1 - fs/xfs/xfs_iget.c | 1 - fs/xfs/xfs_inode.c | 1 - fs/xfs/xfs_inode_item.c | 1 - fs/xfs/xfs_iomap.c | 1 - fs/xfs/xfs_itable.c | 1 - fs/xfs/xfs_log.c | 1 - fs/xfs/xfs_log_cil.c | 1 - fs/xfs/xfs_log_recover.c | 1 - fs/xfs/xfs_mount.c | 1 - fs/xfs/xfs_mount.h | 67 --------------- fs/xfs/xfs_rename.c | 32 +------ fs/xfs/xfs_rtalloc.c | 1 - fs/xfs/xfs_rw.c | 1 - fs/xfs/xfs_trans.c | 1 - fs/xfs/xfs_trans_ail.c | 1 - fs/xfs/xfs_trans_buf.c | 1 - fs/xfs/xfs_trans_extfree.c | 1 - fs/xfs/xfs_trans_inode.c | 1 - fs/xfs/xfs_trans_item.c | 1 - fs/xfs/xfs_utils.c | 1 - fs/xfs/xfs_vnodeops.c | 172 ++++---------------------------------- 70 files changed, 27 insertions(+), 779 deletions(-) delete mode 100644 fs/xfs/linux-2.6/xfs_dmapi_priv.h delete mode 100644 fs/xfs/linux-2.6/xfs_fs_subr.h delete mode 100644 fs/xfs/xfs_dmapi.h delete mode 100644 fs/xfs/xfs_dmops.c (limited to 'fs') diff --git a/fs/xfs/Makefile b/fs/xfs/Makefile index c8fb13f83b3f..a5239b1713be 100644 --- a/fs/xfs/Makefile +++ b/fs/xfs/Makefile @@ -90,8 +90,7 @@ xfs-y += xfs_alloc.o \ xfs_trans_item.o \ xfs_utils.o \ xfs_vnodeops.o \ - xfs_rw.o \ - xfs_dmops.o + xfs_rw.o xfs-$(CONFIG_XFS_TRACE) += xfs_btree_trace.o diff --git a/fs/xfs/linux-2.6/xfs_aops.c b/fs/xfs/linux-2.6/xfs_aops.c index 34640d6dbdcb..4cd5e00f0c5c 100644 --- a/fs/xfs/linux-2.6/xfs_aops.c +++ b/fs/xfs/linux-2.6/xfs_aops.c @@ -23,7 +23,6 @@ #include "xfs_ag.h" #include "xfs_dir2.h" #include "xfs_trans.h" -#include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" diff --git a/fs/xfs/linux-2.6/xfs_buf.c b/fs/xfs/linux-2.6/xfs_buf.c index 2ee3f7a60163..4b2177f5b223 100644 --- a/fs/xfs/linux-2.6/xfs_buf.c +++ b/fs/xfs/linux-2.6/xfs_buf.c @@ -39,7 +39,6 @@ #include "xfs_inum.h" #include "xfs_log.h" #include "xfs_ag.h" -#include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_trace.h" diff --git a/fs/xfs/linux-2.6/xfs_dmapi_priv.h b/fs/xfs/linux-2.6/xfs_dmapi_priv.h deleted file mode 100644 index a8b0b1685eed..000000000000 --- a/fs/xfs/linux-2.6/xfs_dmapi_priv.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2000-2006 Silicon Graphics, Inc. - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it would be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -#ifndef __XFS_DMAPI_PRIV_H__ -#define __XFS_DMAPI_PRIV_H__ - -/* - * Based on IO_ISDIRECT, decide which i_ flag is set. - */ -#define DM_SEM_FLAG_RD(ioflags) (((ioflags) & IO_ISDIRECT) ? \ - DM_FLAGS_IMUX : 0) -#define DM_SEM_FLAG_WR (DM_FLAGS_IALLOCSEM_WR | DM_FLAGS_IMUX) - -#endif /*__XFS_DMAPI_PRIV_H__*/ diff --git a/fs/xfs/linux-2.6/xfs_export.c b/fs/xfs/linux-2.6/xfs_export.c index e7839ee49e43..09c91325f727 100644 --- a/fs/xfs/linux-2.6/xfs_export.c +++ b/fs/xfs/linux-2.6/xfs_export.c @@ -23,7 +23,6 @@ #include "xfs_sb.h" #include "xfs_ag.h" #include "xfs_dir2.h" -#include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_export.h" #include "xfs_vnodeops.h" diff --git a/fs/xfs/linux-2.6/xfs_file.c b/fs/xfs/linux-2.6/xfs_file.c index 257a56b127cf..dca06131551a 100644 --- a/fs/xfs/linux-2.6/xfs_file.c +++ b/fs/xfs/linux-2.6/xfs_file.c @@ -24,7 +24,6 @@ #include "xfs_ag.h" #include "xfs_dir2.h" #include "xfs_trans.h" -#include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" @@ -275,20 +274,6 @@ xfs_file_aio_read( mutex_lock(&inode->i_mutex); xfs_ilock(ip, XFS_IOLOCK_SHARED); - if (DM_EVENT_ENABLED(ip, DM_EVENT_READ) && !(ioflags & IO_INVIS)) { - int dmflags = FILP_DELAY_FLAG(file) | DM_SEM_FLAG_RD(ioflags); - int iolock = XFS_IOLOCK_SHARED; - - ret = -XFS_SEND_DATA(mp, DM_EVENT_READ, ip, iocb->ki_pos, size, - dmflags, &iolock); - if (ret) { - xfs_iunlock(ip, XFS_IOLOCK_SHARED); - if (unlikely(ioflags & IO_ISDIRECT)) - mutex_unlock(&inode->i_mutex); - return ret; - } - } - if (unlikely(ioflags & IO_ISDIRECT)) { if (inode->i_mapping->nrpages) { ret = -xfs_flushinval_pages(ip, @@ -321,7 +306,6 @@ xfs_file_splice_read( unsigned int flags) { struct xfs_inode *ip = XFS_I(infilp->f_mapping->host); - struct xfs_mount *mp = ip->i_mount; int ioflags = 0; ssize_t ret; @@ -335,18 +319,6 @@ xfs_file_splice_read( xfs_ilock(ip, XFS_IOLOCK_SHARED); - if (DM_EVENT_ENABLED(ip, DM_EVENT_READ) && !(ioflags & IO_INVIS)) { - int iolock = XFS_IOLOCK_SHARED; - int error; - - error = XFS_SEND_DATA(mp, DM_EVENT_READ, ip, *ppos, count, - FILP_DELAY_FLAG(infilp), &iolock); - if (error) { - xfs_iunlock(ip, XFS_IOLOCK_SHARED); - return -error; - } - } - trace_xfs_file_splice_read(ip, count, *ppos, ioflags); ret = generic_file_splice_read(infilp, ppos, pipe, count, flags); @@ -367,7 +339,6 @@ xfs_file_splice_write( { struct inode *inode = outfilp->f_mapping->host; struct xfs_inode *ip = XFS_I(inode); - struct xfs_mount *mp = ip->i_mount; xfs_fsize_t isize, new_size; int ioflags = 0; ssize_t ret; @@ -382,18 +353,6 @@ xfs_file_splice_write( xfs_ilock(ip, XFS_IOLOCK_EXCL); - if (DM_EVENT_ENABLED(ip, DM_EVENT_WRITE) && !(ioflags & IO_INVIS)) { - int iolock = XFS_IOLOCK_EXCL; - int error; - - error = XFS_SEND_DATA(mp, DM_EVENT_WRITE, ip, *ppos, count, - FILP_DELAY_FLAG(outfilp), &iolock); - if (error) { - xfs_iunlock(ip, XFS_IOLOCK_EXCL); - return -error; - } - } - new_size = *ppos + count; xfs_ilock(ip, XFS_ILOCK_EXCL); @@ -627,7 +586,6 @@ xfs_file_aio_write( int ioflags = 0; xfs_fsize_t isize, new_size; int iolock; - int eventsent = 0; size_t ocount = 0, count; int need_i_mutex; @@ -673,33 +631,6 @@ start: goto out_unlock_mutex; } - if ((DM_EVENT_ENABLED(ip, DM_EVENT_WRITE) && - !(ioflags & IO_INVIS) && !eventsent)) { - int dmflags = FILP_DELAY_FLAG(file); - - if (need_i_mutex) - dmflags |= DM_FLAGS_IMUX; - - xfs_iunlock(ip, XFS_ILOCK_EXCL); - error = XFS_SEND_DATA(ip->i_mount, DM_EVENT_WRITE, ip, - pos, count, dmflags, &iolock); - if (error) { - goto out_unlock_internal; - } - xfs_ilock(ip, XFS_ILOCK_EXCL); - eventsent = 1; - - /* - * The iolock was dropped and reacquired in XFS_SEND_DATA - * so we have to recheck the size when appending. - * We will only "goto start;" once, since having sent the - * event prevents another call to XFS_SEND_DATA, which is - * what allows the size to change in the first place. - */ - if ((file->f_flags & O_APPEND) && pos != ip->i_size) - goto start; - } - if (ioflags & IO_ISDIRECT) { xfs_buftarg_t *target = XFS_IS_REALTIME_INODE(ip) ? @@ -830,22 +761,6 @@ write_retry: xfs_iunlock(ip, XFS_ILOCK_EXCL); } - if (ret == -ENOSPC && - DM_EVENT_ENABLED(ip, DM_EVENT_NOSPACE) && !(ioflags & IO_INVIS)) { - xfs_iunlock(ip, iolock); - if (need_i_mutex) - mutex_unlock(&inode->i_mutex); - error = XFS_SEND_NAMESP(ip->i_mount, DM_EVENT_NOSPACE, ip, - DM_RIGHT_NULL, ip, DM_RIGHT_NULL, NULL, NULL, - 0, 0, 0); /* Delay flag intentionally unused */ - if (need_i_mutex) - mutex_lock(&inode->i_mutex); - xfs_ilock(ip, iolock); - if (error) - goto out_unlock_internal; - goto start; - } - error = -ret; if (ret <= 0) goto out_unlock_internal; diff --git a/fs/xfs/linux-2.6/xfs_fs_subr.c b/fs/xfs/linux-2.6/xfs_fs_subr.c index b6918d76bc7b..1f279b012f94 100644 --- a/fs/xfs/linux-2.6/xfs_fs_subr.c +++ b/fs/xfs/linux-2.6/xfs_fs_subr.c @@ -21,10 +21,6 @@ #include "xfs_inode.h" #include "xfs_trace.h" -int fs_noerr(void) { return 0; } -int fs_nosys(void) { return ENOSYS; } -void fs_noval(void) { return; } - /* * note: all filemap functions return negative error codes. These * need to be inverted before returning to the xfs core functions. diff --git a/fs/xfs/linux-2.6/xfs_fs_subr.h b/fs/xfs/linux-2.6/xfs_fs_subr.h deleted file mode 100644 index 82bb19b2599e..000000000000 --- a/fs/xfs/linux-2.6/xfs_fs_subr.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2000,2002,2005 Silicon Graphics, Inc. - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it would be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -#ifndef __XFS_FS_SUBR_H__ -#define __XFS_FS_SUBR_H__ - -extern int fs_noerr(void); -extern int fs_nosys(void); -extern void fs_noval(void); - -#endif /* __XFS_FS_SUBR_H__ */ diff --git a/fs/xfs/linux-2.6/xfs_ioctl.c b/fs/xfs/linux-2.6/xfs_ioctl.c index e59a81062830..addd37051aa9 100644 --- a/fs/xfs/linux-2.6/xfs_ioctl.c +++ b/fs/xfs/linux-2.6/xfs_ioctl.c @@ -25,7 +25,6 @@ #include "xfs_ag.h" #include "xfs_dir2.h" #include "xfs_alloc.h" -#include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" @@ -1116,16 +1115,7 @@ xfs_ioctl_setattr( xfs_qm_dqrele(udqp); xfs_qm_dqrele(gdqp); - if (code) - return code; - - if (DM_EVENT_ENABLED(ip, DM_EVENT_ATTRIBUTE)) { - XFS_SEND_NAMESP(mp, DM_EVENT_ATTRIBUTE, ip, DM_RIGHT_NULL, - NULL, DM_RIGHT_NULL, NULL, NULL, 0, 0, - (mask & FSX_NONBLOCK) ? DM_FLAGS_NDELAY : 0); - } - - return 0; + return code; error_return: xfs_qm_dqrele(udqp); diff --git a/fs/xfs/linux-2.6/xfs_ioctl32.c b/fs/xfs/linux-2.6/xfs_ioctl32.c index 52ed49e6465c..e67be3ae3540 100644 --- a/fs/xfs/linux-2.6/xfs_ioctl32.c +++ b/fs/xfs/linux-2.6/xfs_ioctl32.c @@ -29,7 +29,6 @@ #include "xfs_sb.h" #include "xfs_ag.h" #include "xfs_dir2.h" -#include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_attr_sf.h" diff --git a/fs/xfs/linux-2.6/xfs_iops.c b/fs/xfs/linux-2.6/xfs_iops.c index 44f0b2de153e..ce3118477d9e 100644 --- a/fs/xfs/linux-2.6/xfs_iops.c +++ b/fs/xfs/linux-2.6/xfs_iops.c @@ -26,7 +26,6 @@ #include "xfs_ag.h" #include "xfs_dir2.h" #include "xfs_alloc.h" -#include "xfs_dmapi.h" #include "xfs_quota.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" diff --git a/fs/xfs/linux-2.6/xfs_linux.h b/fs/xfs/linux-2.6/xfs_linux.h index facfb323a706..998a9d7fb9c8 100644 --- a/fs/xfs/linux-2.6/xfs_linux.h +++ b/fs/xfs/linux-2.6/xfs_linux.h @@ -87,7 +87,6 @@ #include #include #include -#include #include /* diff --git a/fs/xfs/linux-2.6/xfs_quotaops.c b/fs/xfs/linux-2.6/xfs_quotaops.c index 067cafbfc635..bfd5ac9d1f6f 100644 --- a/fs/xfs/linux-2.6/xfs_quotaops.c +++ b/fs/xfs/linux-2.6/xfs_quotaops.c @@ -16,7 +16,6 @@ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "xfs.h" -#include "xfs_dmapi.h" #include "xfs_sb.h" #include "xfs_inum.h" #include "xfs_log.h" diff --git a/fs/xfs/linux-2.6/xfs_super.c b/fs/xfs/linux-2.6/xfs_super.c index 80938c736c27..4c7b8f9f4deb 100644 --- a/fs/xfs/linux-2.6/xfs_super.c +++ b/fs/xfs/linux-2.6/xfs_super.c @@ -25,7 +25,6 @@ #include "xfs_ag.h" #include "xfs_dir2.h" #include "xfs_alloc.h" -#include "xfs_dmapi.h" #include "xfs_quota.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" @@ -116,9 +115,6 @@ mempool_t *xfs_ioend_pool; #define MNTOPT_GQUOTANOENF "gqnoenforce"/* group quota limit enforcement */ #define MNTOPT_PQUOTANOENF "pqnoenforce"/* project quota limit enforcement */ #define MNTOPT_QUOTANOENF "qnoenforce" /* same as uqnoenforce */ -#define MNTOPT_DMAPI "dmapi" /* DMI enabled (DMAPI / XDSM) */ -#define MNTOPT_XDSM "xdsm" /* DMI enabled (DMAPI / XDSM) */ -#define MNTOPT_DMI "dmi" /* DMI enabled (DMAPI / XDSM) */ #define MNTOPT_DELAYLOG "delaylog" /* Delayed loging enabled */ #define MNTOPT_NODELAYLOG "nodelaylog" /* Delayed loging disabled */ @@ -172,15 +168,13 @@ suffix_strtoul(char *s, char **endp, unsigned int base) STATIC int xfs_parseargs( struct xfs_mount *mp, - char *options, - char **mtpt) + char *options) { struct super_block *sb = mp->m_super; char *this_char, *value, *eov; int dsunit = 0; int dswidth = 0; int iosize = 0; - int dmapi_implies_ikeep = 1; __uint8_t iosizelog = 0; /* @@ -243,15 +237,10 @@ xfs_parseargs( if (!mp->m_logname) return ENOMEM; } else if (!strcmp(this_char, MNTOPT_MTPT)) { - if (!value || !*value) { - cmn_err(CE_WARN, - "XFS: %s option requires an argument", - this_char); - return EINVAL; - } - *mtpt = kstrndup(value, MAXNAMELEN, GFP_KERNEL); - if (!*mtpt) - return ENOMEM; + cmn_err(CE_WARN, + "XFS: %s option not allowed on this system", + this_char); + return EINVAL; } else if (!strcmp(this_char, MNTOPT_RTDEV)) { if (!value || !*value) { cmn_err(CE_WARN, @@ -329,7 +318,6 @@ xfs_parseargs( } else if (!strcmp(this_char, MNTOPT_IKEEP)) { mp->m_flags |= XFS_MOUNT_IKEEP; } else if (!strcmp(this_char, MNTOPT_NOIKEEP)) { - dmapi_implies_ikeep = 0; mp->m_flags &= ~XFS_MOUNT_IKEEP; } else if (!strcmp(this_char, MNTOPT_LARGEIO)) { mp->m_flags &= ~XFS_MOUNT_COMPAT_IOSIZE; @@ -370,12 +358,6 @@ xfs_parseargs( } else if (!strcmp(this_char, MNTOPT_GQUOTANOENF)) { mp->m_qflags |= (XFS_GQUOTA_ACCT | XFS_GQUOTA_ACTIVE); mp->m_qflags &= ~XFS_OQUOTA_ENFD; - } else if (!strcmp(this_char, MNTOPT_DMAPI)) { - mp->m_flags |= XFS_MOUNT_DMAPI; - } else if (!strcmp(this_char, MNTOPT_XDSM)) { - mp->m_flags |= XFS_MOUNT_DMAPI; - } else if (!strcmp(this_char, MNTOPT_DMI)) { - mp->m_flags |= XFS_MOUNT_DMAPI; } else if (!strcmp(this_char, MNTOPT_DELAYLOG)) { mp->m_flags |= XFS_MOUNT_DELAYLOG; cmn_err(CE_WARN, @@ -430,12 +412,6 @@ xfs_parseargs( return EINVAL; } - if ((mp->m_flags & XFS_MOUNT_DMAPI) && (!*mtpt || *mtpt[0] == '\0')) { - printk("XFS: %s option needs the mount point option as well\n", - MNTOPT_DMAPI); - return EINVAL; - } - if ((dsunit && !dswidth) || (!dsunit && dswidth)) { cmn_err(CE_WARN, "XFS: sunit and swidth must be specified together"); @@ -449,18 +425,6 @@ xfs_parseargs( return EINVAL; } - /* - * Applications using DMI filesystems often expect the - * inode generation number to be monotonically increasing. - * If we delete inode chunks we break this assumption, so - * keep unused inode chunks on disk for DMI filesystems - * until we come up with a better solution. - * Note that if "ikeep" or "noikeep" mount options are - * supplied, then they are honored. - */ - if ((mp->m_flags & XFS_MOUNT_DMAPI) && dmapi_implies_ikeep) - mp->m_flags |= XFS_MOUNT_IKEEP; - done: if (!(mp->m_flags & XFS_MOUNT_NOALIGN)) { /* @@ -542,7 +506,6 @@ xfs_showargs( { XFS_MOUNT_OSYNCISOSYNC, "," MNTOPT_OSYNCISOSYNC }, { XFS_MOUNT_ATTR2, "," MNTOPT_ATTR2 }, { XFS_MOUNT_FILESTREAMS, "," MNTOPT_FILESTREAM }, - { XFS_MOUNT_DMAPI, "," MNTOPT_DMAPI }, { XFS_MOUNT_GRPID, "," MNTOPT_GRPID }, { XFS_MOUNT_DELAYLOG, "," MNTOPT_DELAYLOG }, { 0, NULL } @@ -1207,8 +1170,6 @@ xfs_fs_put_super( xfs_sync_attr(mp, 0); } - XFS_SEND_PREUNMOUNT(mp); - /* * Blow away any referenced inode in the filestreams cache. * This can and will cause log traffic as inodes go inactive @@ -1218,14 +1179,11 @@ xfs_fs_put_super( XFS_bflush(mp->m_ddev_targp); - XFS_SEND_UNMOUNT(mp); - xfs_unmountfs(mp); xfs_freesb(mp); xfs_inode_shrinker_unregister(mp); xfs_icsb_destroy_counters(mp); xfs_close_devices(mp); - xfs_dmops_put(mp); xfs_free_fsname(mp); kfree(mp); } @@ -1543,7 +1501,6 @@ xfs_fs_fill_super( struct inode *root; struct xfs_mount *mp = NULL; int flags = 0, error = ENOMEM; - char *mtpt = NULL; mp = kzalloc(sizeof(struct xfs_mount), GFP_KERNEL); if (!mp) @@ -1559,7 +1516,7 @@ xfs_fs_fill_super( mp->m_super = sb; sb->s_fs_info = mp; - error = xfs_parseargs(mp, (char *)data, &mtpt); + error = xfs_parseargs(mp, (char *)data); if (error) goto out_free_fsname; @@ -1571,16 +1528,12 @@ xfs_fs_fill_super( #endif sb->s_op = &xfs_super_operations; - error = xfs_dmops_get(mp); - if (error) - goto out_free_fsname; - if (silent) flags |= XFS_MFSI_QUIET; error = xfs_open_devices(mp); if (error) - goto out_put_dmops; + goto out_free_fsname; if (xfs_icsb_init_counters(mp)) mp->m_flags |= XFS_MOUNT_NO_PERCPU_SB; @@ -1608,8 +1561,6 @@ xfs_fs_fill_super( if (error) goto out_filestream_unmount; - XFS_SEND_MOUNT(mp, DM_RIGHT_NULL, mtpt, mp->m_fsname); - sb->s_magic = XFS_SB_MAGIC; sb->s_blocksize = mp->m_sb.sb_blocksize; sb->s_blocksize_bits = ffs(sb->s_blocksize) - 1; @@ -1638,7 +1589,6 @@ xfs_fs_fill_super( xfs_inode_shrinker_register(mp); - kfree(mtpt); return 0; out_filestream_unmount: @@ -1648,11 +1598,8 @@ xfs_fs_fill_super( out_destroy_counters: xfs_icsb_destroy_counters(mp); xfs_close_devices(mp); - out_put_dmops: - xfs_dmops_put(mp); out_free_fsname: xfs_free_fsname(mp); - kfree(mtpt); kfree(mp); out: return -error; diff --git a/fs/xfs/linux-2.6/xfs_super.h b/fs/xfs/linux-2.6/xfs_super.h index 519618e9279e..1ef4a4d2d997 100644 --- a/fs/xfs/linux-2.6/xfs_super.h +++ b/fs/xfs/linux-2.6/xfs_super.h @@ -56,12 +56,6 @@ extern void xfs_qm_exit(void); # define XFS_BIGFS_STRING #endif -#ifdef CONFIG_XFS_DMAPI -# define XFS_DMAPI_STRING "dmapi support, " -#else -# define XFS_DMAPI_STRING -#endif - #ifdef DEBUG # define XFS_DBG_STRING "debug" #else @@ -72,7 +66,6 @@ extern void xfs_qm_exit(void); XFS_SECURITY_STRING \ XFS_REALTIME_STRING \ XFS_BIGFS_STRING \ - XFS_DMAPI_STRING \ XFS_DBG_STRING /* DBG must be last */ struct xfs_inode; diff --git a/fs/xfs/linux-2.6/xfs_sync.c b/fs/xfs/linux-2.6/xfs_sync.c index a51a07c3a70c..ce323377a708 100644 --- a/fs/xfs/linux-2.6/xfs_sync.c +++ b/fs/xfs/linux-2.6/xfs_sync.c @@ -25,7 +25,6 @@ #include "xfs_sb.h" #include "xfs_ag.h" #include "xfs_dir2.h" -#include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" diff --git a/fs/xfs/linux-2.6/xfs_trace.c b/fs/xfs/linux-2.6/xfs_trace.c index d12be8470cba..03e2dca36d4b 100644 --- a/fs/xfs/linux-2.6/xfs_trace.c +++ b/fs/xfs/linux-2.6/xfs_trace.c @@ -34,7 +34,6 @@ #include "xfs_dinode.h" #include "xfs_inode.h" #include "xfs_btree.h" -#include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_ialloc.h" #include "xfs_itable.h" diff --git a/fs/xfs/quota/xfs_dquot.c b/fs/xfs/quota/xfs_dquot.c index 585e7633dfc7..3c111ea41033 100644 --- a/fs/xfs/quota/xfs_dquot.c +++ b/fs/xfs/quota/xfs_dquot.c @@ -25,7 +25,6 @@ #include "xfs_ag.h" #include "xfs_dir2.h" #include "xfs_alloc.h" -#include "xfs_dmapi.h" #include "xfs_quota.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" diff --git a/fs/xfs/quota/xfs_dquot_item.c b/fs/xfs/quota/xfs_dquot_item.c index 8d89a24ae324..15c211bd18d6 100644 --- a/fs/xfs/quota/xfs_dquot_item.c +++ b/fs/xfs/quota/xfs_dquot_item.c @@ -25,7 +25,6 @@ #include "xfs_ag.h" #include "xfs_dir2.h" #include "xfs_alloc.h" -#include "xfs_dmapi.h" #include "xfs_quota.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" diff --git a/fs/xfs/quota/xfs_qm.c b/fs/xfs/quota/xfs_qm.c index 67c018392d62..0e01edb00cd9 100644 --- a/fs/xfs/quota/xfs_qm.c +++ b/fs/xfs/quota/xfs_qm.c @@ -25,7 +25,6 @@ #include "xfs_ag.h" #include "xfs_dir2.h" #include "xfs_alloc.h" -#include "xfs_dmapi.h" #include "xfs_quota.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" diff --git a/fs/xfs/quota/xfs_qm_bhv.c b/fs/xfs/quota/xfs_qm_bhv.c index 97b410c12794..7283bccb37a6 100644 --- a/fs/xfs/quota/xfs_qm_bhv.c +++ b/fs/xfs/quota/xfs_qm_bhv.c @@ -25,7 +25,6 @@ #include "xfs_ag.h" #include "xfs_dir2.h" #include "xfs_alloc.h" -#include "xfs_dmapi.h" #include "xfs_quota.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" diff --git a/fs/xfs/quota/xfs_qm_stats.c b/fs/xfs/quota/xfs_qm_stats.c index 3d1fc79532e2..446ae61af9a2 100644 --- a/fs/xfs/quota/xfs_qm_stats.c +++ b/fs/xfs/quota/xfs_qm_stats.c @@ -25,7 +25,6 @@ #include "xfs_ag.h" #include "xfs_dir2.h" #include "xfs_alloc.h" -#include "xfs_dmapi.h" #include "xfs_quota.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" diff --git a/fs/xfs/quota/xfs_qm_syscalls.c b/fs/xfs/quota/xfs_qm_syscalls.c index b4487764e923..b286e0a3111b 100644 --- a/fs/xfs/quota/xfs_qm_syscalls.c +++ b/fs/xfs/quota/xfs_qm_syscalls.c @@ -28,7 +28,6 @@ #include "xfs_ag.h" #include "xfs_dir2.h" #include "xfs_alloc.h" -#include "xfs_dmapi.h" #include "xfs_quota.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" diff --git a/fs/xfs/quota/xfs_trans_dquot.c b/fs/xfs/quota/xfs_trans_dquot.c index 061d827da33c..04155c43b1b4 100644 --- a/fs/xfs/quota/xfs_trans_dquot.c +++ b/fs/xfs/quota/xfs_trans_dquot.c @@ -25,7 +25,6 @@ #include "xfs_ag.h" #include "xfs_dir2.h" #include "xfs_alloc.h" -#include "xfs_dmapi.h" #include "xfs_quota.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" diff --git a/fs/xfs/support/debug.c b/fs/xfs/support/debug.c index 3f3610a7ee05..975aa10e1a47 100644 --- a/fs/xfs/support/debug.c +++ b/fs/xfs/support/debug.c @@ -22,7 +22,6 @@ #include "xfs_sb.h" #include "xfs_inum.h" #include "xfs_ag.h" -#include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_error.h" diff --git a/fs/xfs/xfs_alloc.c b/fs/xfs/xfs_alloc.c index a7fbe8a99b12..42b4b52644eb 100644 --- a/fs/xfs/xfs_alloc.c +++ b/fs/xfs/xfs_alloc.c @@ -25,7 +25,6 @@ #include "xfs_sb.h" #include "xfs_ag.h" #include "xfs_dir2.h" -#include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" diff --git a/fs/xfs/xfs_alloc_btree.c b/fs/xfs/xfs_alloc_btree.c index 83f494218759..7d638a5d1f0d 100644 --- a/fs/xfs/xfs_alloc_btree.c +++ b/fs/xfs/xfs_alloc_btree.c @@ -25,7 +25,6 @@ #include "xfs_sb.h" #include "xfs_ag.h" #include "xfs_dir2.h" -#include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" diff --git a/fs/xfs/xfs_attr.c b/fs/xfs/xfs_attr.c index b9c196a53c42..426748955e3e 100644 --- a/fs/xfs/xfs_attr.c +++ b/fs/xfs/xfs_attr.c @@ -26,7 +26,6 @@ #include "xfs_sb.h" #include "xfs_ag.h" #include "xfs_dir2.h" -#include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_da_btree.h" #include "xfs_bmap_btree.h" diff --git a/fs/xfs/xfs_attr_leaf.c b/fs/xfs/xfs_attr_leaf.c index a90ce74fc256..ca4d11d3a7a7 100644 --- a/fs/xfs/xfs_attr_leaf.c +++ b/fs/xfs/xfs_attr_leaf.c @@ -25,7 +25,6 @@ #include "xfs_sb.h" #include "xfs_ag.h" #include "xfs_dir2.h" -#include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_da_btree.h" #include "xfs_bmap_btree.h" diff --git a/fs/xfs/xfs_bmap.c b/fs/xfs/xfs_bmap.c index 99587ded043f..9db1418a64bf 100644 --- a/fs/xfs/xfs_bmap.c +++ b/fs/xfs/xfs_bmap.c @@ -34,7 +34,6 @@ #include "xfs_dinode.h" #include "xfs_inode.h" #include "xfs_btree.h" -#include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_ialloc.h" #include "xfs_itable.h" @@ -5605,28 +5604,6 @@ xfs_getbmap( prealloced = 0; fixlen = 1LL << 32; } else { - /* - * If the BMV_IF_NO_DMAPI_READ interface bit specified, do - * not generate a DMAPI read event. Otherwise, if the - * DM_EVENT_READ bit is set for the file, generate a read - * event in order that the DMAPI application may do its thing - * before we return the extents. Usually this means restoring - * user file data to regions of the file that look like holes. - * - * The "old behavior" (from XFS_IOC_GETBMAP) is to not specify - * BMV_IF_NO_DMAPI_READ so that read events are generated. - * If this were not true, callers of ioctl(XFS_IOC_GETBMAP) - * could misinterpret holes in a DMAPI file as true holes, - * when in fact they may represent offline user data. - */ - if (DM_EVENT_ENABLED(ip, DM_EVENT_READ) && - !(iflags & BMV_IF_NO_DMAPI_READ)) { - error = XFS_SEND_DATA(mp, DM_EVENT_READ, ip, - 0, 0, 0, NULL); - if (error) - return XFS_ERROR(error); - } - if (ip->i_d.di_format != XFS_DINODE_FMT_EXTENTS && ip->i_d.di_format != XFS_DINODE_FMT_BTREE && ip->i_d.di_format != XFS_DINODE_FMT_LOCAL) diff --git a/fs/xfs/xfs_bmap_btree.c b/fs/xfs/xfs_bmap_btree.c index 416e47e54b83..998acf185c48 100644 --- a/fs/xfs/xfs_bmap_btree.c +++ b/fs/xfs/xfs_bmap_btree.c @@ -25,7 +25,6 @@ #include "xfs_sb.h" #include "xfs_ag.h" #include "xfs_dir2.h" -#include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" diff --git a/fs/xfs/xfs_btree.c b/fs/xfs/xfs_btree.c index 96be4b0f2496..beed23ec9259 100644 --- a/fs/xfs/xfs_btree.c +++ b/fs/xfs/xfs_btree.c @@ -25,7 +25,6 @@ #include "xfs_sb.h" #include "xfs_ag.h" #include "xfs_dir2.h" -#include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" diff --git a/fs/xfs/xfs_buf_item.c b/fs/xfs/xfs_buf_item.c index 02a80984aa05..91ad92e83bc6 100644 --- a/fs/xfs/xfs_buf_item.c +++ b/fs/xfs/xfs_buf_item.c @@ -24,7 +24,6 @@ #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_buf_item.h" #include "xfs_trans_priv.h" diff --git a/fs/xfs/xfs_da_btree.c b/fs/xfs/xfs_da_btree.c index 0ca556b4bf31..a4a8965a92f5 100644 --- a/fs/xfs/xfs_da_btree.c +++ b/fs/xfs/xfs_da_btree.c @@ -25,7 +25,6 @@ #include "xfs_sb.h" #include "xfs_ag.h" #include "xfs_dir2.h" -#include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_da_btree.h" #include "xfs_bmap_btree.h" diff --git a/fs/xfs/xfs_dfrag.c b/fs/xfs/xfs_dfrag.c index 7f159d2a429a..86330da07f29 100644 --- a/fs/xfs/xfs_dfrag.c +++ b/fs/xfs/xfs_dfrag.c @@ -25,7 +25,6 @@ #include "xfs_sb.h" #include "xfs_ag.h" #include "xfs_dir2.h" -#include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" diff --git a/fs/xfs/xfs_dir2.c b/fs/xfs/xfs_dir2.c index 42520f041265..429f234c1b16 100644 --- a/fs/xfs/xfs_dir2.c +++ b/fs/xfs/xfs_dir2.c @@ -25,7 +25,6 @@ #include "xfs_sb.h" #include "xfs_ag.h" #include "xfs_dir2.h" -#include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_da_btree.h" #include "xfs_bmap_btree.h" diff --git a/fs/xfs/xfs_dir2_block.c b/fs/xfs/xfs_dir2_block.c index 779a267b0a84..ce3bac65d056 100644 --- a/fs/xfs/xfs_dir2_block.c +++ b/fs/xfs/xfs_dir2_block.c @@ -24,7 +24,6 @@ #include "xfs_sb.h" #include "xfs_ag.h" #include "xfs_dir2.h" -#include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_da_btree.h" #include "xfs_bmap_btree.h" diff --git a/fs/xfs/xfs_dir2_data.c b/fs/xfs/xfs_dir2_data.c index 498f8d694330..ece400b833df 100644 --- a/fs/xfs/xfs_dir2_data.c +++ b/fs/xfs/xfs_dir2_data.c @@ -24,7 +24,6 @@ #include "xfs_sb.h" #include "xfs_ag.h" #include "xfs_dir2.h" -#include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_da_btree.h" #include "xfs_bmap_btree.h" diff --git a/fs/xfs/xfs_dir2_leaf.c b/fs/xfs/xfs_dir2_leaf.c index e2d89854ec9e..99c8fe0cf223 100644 --- a/fs/xfs/xfs_dir2_leaf.c +++ b/fs/xfs/xfs_dir2_leaf.c @@ -25,7 +25,6 @@ #include "xfs_sb.h" #include "xfs_ag.h" #include "xfs_dir2.h" -#include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_da_btree.h" #include "xfs_bmap_btree.h" diff --git a/fs/xfs/xfs_dir2_node.c b/fs/xfs/xfs_dir2_node.c index 78fc4d9ae756..d1b40ea0cd69 100644 --- a/fs/xfs/xfs_dir2_node.c +++ b/fs/xfs/xfs_dir2_node.c @@ -24,7 +24,6 @@ #include "xfs_sb.h" #include "xfs_ag.h" #include "xfs_dir2.h" -#include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_da_btree.h" #include "xfs_bmap_btree.h" diff --git a/fs/xfs/xfs_dir2_sf.c b/fs/xfs/xfs_dir2_sf.c index c1a5945d463a..d4e63e68fd70 100644 --- a/fs/xfs/xfs_dir2_sf.c +++ b/fs/xfs/xfs_dir2_sf.c @@ -24,7 +24,6 @@ #include "xfs_sb.h" #include "xfs_ag.h" #include "xfs_dir2.h" -#include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_da_btree.h" #include "xfs_bmap_btree.h" diff --git a/fs/xfs/xfs_dmapi.h b/fs/xfs/xfs_dmapi.h deleted file mode 100644 index 2813cdd72375..000000000000 --- a/fs/xfs/xfs_dmapi.h +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright (c) 2000-2005 Silicon Graphics, Inc. - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it would be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -#ifndef __XFS_DMAPI_H__ -#define __XFS_DMAPI_H__ - -/* Values used to define the on-disk version of dm_attrname_t. All - * on-disk attribute names start with the 8-byte string "SGI_DMI_". - * - * In the on-disk inode, DMAPI attribute names consist of the user-provided - * name with the DMATTR_PREFIXSTRING pre-pended. This string must NEVER be - * changed. - */ - -#define DMATTR_PREFIXLEN 8 -#define DMATTR_PREFIXSTRING "SGI_DMI_" - -typedef enum { - DM_EVENT_INVALID = -1, - DM_EVENT_CANCEL = 0, /* not supported */ - DM_EVENT_MOUNT = 1, - DM_EVENT_PREUNMOUNT = 2, - DM_EVENT_UNMOUNT = 3, - DM_EVENT_DEBUT = 4, /* not supported */ - DM_EVENT_CREATE = 5, - DM_EVENT_CLOSE = 6, /* not supported */ - DM_EVENT_POSTCREATE = 7, - DM_EVENT_REMOVE = 8, - DM_EVENT_POSTREMOVE = 9, - DM_EVENT_RENAME = 10, - DM_EVENT_POSTRENAME = 11, - DM_EVENT_LINK = 12, - DM_EVENT_POSTLINK = 13, - DM_EVENT_SYMLINK = 14, - DM_EVENT_POSTSYMLINK = 15, - DM_EVENT_READ = 16, - DM_EVENT_WRITE = 17, - DM_EVENT_TRUNCATE = 18, - DM_EVENT_ATTRIBUTE = 19, - DM_EVENT_DESTROY = 20, - DM_EVENT_NOSPACE = 21, - DM_EVENT_USER = 22, - DM_EVENT_MAX = 23 -} dm_eventtype_t; -#define HAVE_DM_EVENTTYPE_T - -typedef enum { - DM_RIGHT_NULL, - DM_RIGHT_SHARED, - DM_RIGHT_EXCL -} dm_right_t; -#define HAVE_DM_RIGHT_T - -/* Defines for determining if an event message should be sent. */ -#ifdef HAVE_DMAPI -#define DM_EVENT_ENABLED(ip, event) ( \ - unlikely ((ip)->i_mount->m_flags & XFS_MOUNT_DMAPI) && \ - ( ((ip)->i_d.di_dmevmask & (1 << event)) || \ - ((ip)->i_mount->m_dmevmask & (1 << event)) ) \ - ) -#else -#define DM_EVENT_ENABLED(ip, event) (0) -#endif - -#define DM_XFS_VALID_FS_EVENTS ( \ - (1 << DM_EVENT_PREUNMOUNT) | \ - (1 << DM_EVENT_UNMOUNT) | \ - (1 << DM_EVENT_NOSPACE) | \ - (1 << DM_EVENT_DEBUT) | \ - (1 << DM_EVENT_CREATE) | \ - (1 << DM_EVENT_POSTCREATE) | \ - (1 << DM_EVENT_REMOVE) | \ - (1 << DM_EVENT_POSTREMOVE) | \ - (1 << DM_EVENT_RENAME) | \ - (1 << DM_EVENT_POSTRENAME) | \ - (1 << DM_EVENT_LINK) | \ - (1 << DM_EVENT_POSTLINK) | \ - (1 << DM_EVENT_SYMLINK) | \ - (1 << DM_EVENT_POSTSYMLINK) | \ - (1 << DM_EVENT_ATTRIBUTE) | \ - (1 << DM_EVENT_DESTROY) ) - -/* Events valid in dm_set_eventlist() when called with a file handle for - a regular file or a symlink. These events are persistent. -*/ - -#define DM_XFS_VALID_FILE_EVENTS ( \ - (1 << DM_EVENT_ATTRIBUTE) | \ - (1 << DM_EVENT_DESTROY) ) - -/* Events valid in dm_set_eventlist() when called with a file handle for - a directory. These events are persistent. -*/ - -#define DM_XFS_VALID_DIRECTORY_EVENTS ( \ - (1 << DM_EVENT_CREATE) | \ - (1 << DM_EVENT_POSTCREATE) | \ - (1 << DM_EVENT_REMOVE) | \ - (1 << DM_EVENT_POSTREMOVE) | \ - (1 << DM_EVENT_RENAME) | \ - (1 << DM_EVENT_POSTRENAME) | \ - (1 << DM_EVENT_LINK) | \ - (1 << DM_EVENT_POSTLINK) | \ - (1 << DM_EVENT_SYMLINK) | \ - (1 << DM_EVENT_POSTSYMLINK) | \ - (1 << DM_EVENT_ATTRIBUTE) | \ - (1 << DM_EVENT_DESTROY) ) - -/* Events supported by the XFS filesystem. */ -#define DM_XFS_SUPPORTED_EVENTS ( \ - (1 << DM_EVENT_MOUNT) | \ - (1 << DM_EVENT_PREUNMOUNT) | \ - (1 << DM_EVENT_UNMOUNT) | \ - (1 << DM_EVENT_NOSPACE) | \ - (1 << DM_EVENT_CREATE) | \ - (1 << DM_EVENT_POSTCREATE) | \ - (1 << DM_EVENT_REMOVE) | \ - (1 << DM_EVENT_POSTREMOVE) | \ - (1 << DM_EVENT_RENAME) | \ - (1 << DM_EVENT_POSTRENAME) | \ - (1 << DM_EVENT_LINK) | \ - (1 << DM_EVENT_POSTLINK) | \ - (1 << DM_EVENT_SYMLINK) | \ - (1 << DM_EVENT_POSTSYMLINK) | \ - (1 << DM_EVENT_READ) | \ - (1 << DM_EVENT_WRITE) | \ - (1 << DM_EVENT_TRUNCATE) | \ - (1 << DM_EVENT_ATTRIBUTE) | \ - (1 << DM_EVENT_DESTROY) ) - - -/* - * Definitions used for the flags field on dm_send_*_event(). - */ - -#define DM_FLAGS_NDELAY 0x001 /* return EAGAIN after dm_pending() */ -#define DM_FLAGS_UNWANTED 0x002 /* event not in fsys dm_eventset_t */ -#define DM_FLAGS_IMUX 0x004 /* thread holds i_mutex */ -#define DM_FLAGS_IALLOCSEM_RD 0x010 /* thread holds i_alloc_sem rd */ -#define DM_FLAGS_IALLOCSEM_WR 0x020 /* thread holds i_alloc_sem wr */ - -/* - * Pull in platform specific event flags defines - */ -#include "xfs_dmapi_priv.h" - -/* - * Macros to turn caller specified delay/block flags into - * dm_send_xxxx_event flag DM_FLAGS_NDELAY. - */ - -#define FILP_DELAY_FLAG(filp) ((filp->f_flags&(O_NDELAY|O_NONBLOCK)) ? \ - DM_FLAGS_NDELAY : 0) -#define AT_DELAY_FLAG(f) ((f & XFS_ATTR_NONBLOCK) ? DM_FLAGS_NDELAY : 0) - -#endif /* __XFS_DMAPI_H__ */ diff --git a/fs/xfs/xfs_dmops.c b/fs/xfs/xfs_dmops.c deleted file mode 100644 index e71e2581c0c3..000000000000 --- a/fs/xfs/xfs_dmops.c +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2000-2003,2005 Silicon Graphics, Inc. - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it would be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -#include "xfs.h" -#include "xfs_fs.h" -#include "xfs_types.h" -#include "xfs_log.h" -#include "xfs_trans.h" -#include "xfs_sb.h" -#include "xfs_dmapi.h" -#include "xfs_inum.h" -#include "xfs_ag.h" -#include "xfs_mount.h" - - -static struct xfs_dmops xfs_dmcore_stub = { - .xfs_send_data = (xfs_send_data_t)fs_nosys, - .xfs_send_mmap = (xfs_send_mmap_t)fs_noerr, - .xfs_send_destroy = (xfs_send_destroy_t)fs_nosys, - .xfs_send_namesp = (xfs_send_namesp_t)fs_nosys, - .xfs_send_mount = (xfs_send_mount_t)fs_nosys, - .xfs_send_unmount = (xfs_send_unmount_t)fs_noerr, -}; - -int -xfs_dmops_get(struct xfs_mount *mp) -{ - if (mp->m_flags & XFS_MOUNT_DMAPI) { - cmn_err(CE_WARN, - "XFS: dmapi support not available in this kernel."); - return EINVAL; - } - - mp->m_dm_ops = &xfs_dmcore_stub; - return 0; -} - -void -xfs_dmops_put(struct xfs_mount *mp) -{ -} diff --git a/fs/xfs/xfs_error.c b/fs/xfs/xfs_error.c index 047b8a8e5c29..3d8456cb71ff 100644 --- a/fs/xfs/xfs_error.c +++ b/fs/xfs/xfs_error.c @@ -24,7 +24,6 @@ #include "xfs_sb.h" #include "xfs_ag.h" #include "xfs_dir2.h" -#include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_dir2_sf.h" diff --git a/fs/xfs/xfs_extfree_item.c b/fs/xfs/xfs_extfree_item.c index 409fe81585fd..1023b1fadfe8 100644 --- a/fs/xfs/xfs_extfree_item.c +++ b/fs/xfs/xfs_extfree_item.c @@ -24,7 +24,6 @@ #include "xfs_buf_item.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_trans_priv.h" #include "xfs_extfree_item.h" diff --git a/fs/xfs/xfs_filestream.c b/fs/xfs/xfs_filestream.c index 390850ee6603..ad118dac425d 100644 --- a/fs/xfs/xfs_filestream.c +++ b/fs/xfs/xfs_filestream.c @@ -24,7 +24,6 @@ #include "xfs_dinode.h" #include "xfs_inode.h" #include "xfs_ag.h" -#include "xfs_dmapi.h" #include "xfs_log.h" #include "xfs_trans.h" #include "xfs_sb.h" diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c index 37a6f62c57b6..84c002eab0d6 100644 --- a/fs/xfs/xfs_fsops.c +++ b/fs/xfs/xfs_fsops.c @@ -25,7 +25,6 @@ #include "xfs_sb.h" #include "xfs_ag.h" #include "xfs_dir2.h" -#include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" diff --git a/fs/xfs/xfs_ialloc.c b/fs/xfs/xfs_ialloc.c index c7142a064c48..ab76d73d54e9 100644 --- a/fs/xfs/xfs_ialloc.c +++ b/fs/xfs/xfs_ialloc.c @@ -25,7 +25,6 @@ #include "xfs_sb.h" #include "xfs_ag.h" #include "xfs_dir2.h" -#include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" diff --git a/fs/xfs/xfs_ialloc_btree.c b/fs/xfs/xfs_ialloc_btree.c index c282a9af5393..0e6563c05e05 100644 --- a/fs/xfs/xfs_ialloc_btree.c +++ b/fs/xfs/xfs_ialloc_btree.c @@ -25,7 +25,6 @@ #include "xfs_sb.h" #include "xfs_ag.h" #include "xfs_dir2.h" -#include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" diff --git a/fs/xfs/xfs_iget.c b/fs/xfs/xfs_iget.c index 8f8b91be2c99..0eb2f965503c 100644 --- a/fs/xfs/xfs_iget.c +++ b/fs/xfs/xfs_iget.c @@ -26,7 +26,6 @@ #include "xfs_sb.h" #include "xfs_ag.h" #include "xfs_dir2.h" -#include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index b76a829d7e20..767c43a8d164 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -28,7 +28,6 @@ #include "xfs_sb.h" #include "xfs_ag.h" #include "xfs_dir2.h" -#include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" diff --git a/fs/xfs/xfs_inode_item.c b/fs/xfs/xfs_inode_item.c index cf8249a60004..8c561b970c56 100644 --- a/fs/xfs/xfs_inode_item.c +++ b/fs/xfs/xfs_inode_item.c @@ -26,7 +26,6 @@ #include "xfs_sb.h" #include "xfs_ag.h" #include "xfs_dir2.h" -#include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_trans_priv.h" #include "xfs_bmap_btree.h" diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c index ef14943829da..a2653aa0f256 100644 --- a/fs/xfs/xfs_iomap.c +++ b/fs/xfs/xfs_iomap.c @@ -25,7 +25,6 @@ #include "xfs_ag.h" #include "xfs_dir2.h" #include "xfs_alloc.h" -#include "xfs_dmapi.h" #include "xfs_quota.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" diff --git a/fs/xfs/xfs_itable.c b/fs/xfs/xfs_itable.c index 2b86f8610512..69abb344f2c0 100644 --- a/fs/xfs/xfs_itable.c +++ b/fs/xfs/xfs_itable.c @@ -25,7 +25,6 @@ #include "xfs_sb.h" #include "xfs_ag.h" #include "xfs_dir2.h" -#include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" diff --git a/fs/xfs/xfs_log.c b/fs/xfs/xfs_log.c index 5215abc8023a..03a1ab60c480 100644 --- a/fs/xfs/xfs_log.c +++ b/fs/xfs/xfs_log.c @@ -25,7 +25,6 @@ #include "xfs_sb.h" #include "xfs_ag.h" #include "xfs_dir2.h" -#include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_error.h" #include "xfs_log_priv.h" diff --git a/fs/xfs/xfs_log_cil.c b/fs/xfs/xfs_log_cil.c index bb17cc044bf3..c7585ab160d5 100644 --- a/fs/xfs/xfs_log_cil.c +++ b/fs/xfs/xfs_log_cil.c @@ -27,7 +27,6 @@ #include "xfs_sb.h" #include "xfs_ag.h" #include "xfs_dir2.h" -#include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_error.h" #include "xfs_alloc.h" diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c index 9ac5cfab27b9..f91871397db7 100644 --- a/fs/xfs/xfs_log_recover.c +++ b/fs/xfs/xfs_log_recover.c @@ -25,7 +25,6 @@ #include "xfs_sb.h" #include "xfs_ag.h" #include "xfs_dir2.h" -#include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_error.h" #include "xfs_bmap_btree.h" diff --git a/fs/xfs/xfs_mount.c b/fs/xfs/xfs_mount.c index 69f62d8b2816..4bf79511f1c8 100644 --- a/fs/xfs/xfs_mount.c +++ b/fs/xfs/xfs_mount.c @@ -25,7 +25,6 @@ #include "xfs_sb.h" #include "xfs_ag.h" #include "xfs_dir2.h" -#include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" diff --git a/fs/xfs/xfs_mount.h b/fs/xfs/xfs_mount.h index 5761087ee8ea..e70dc39394ae 100644 --- a/fs/xfs/xfs_mount.h +++ b/fs/xfs/xfs_mount.h @@ -66,65 +66,6 @@ struct xfs_nameops; struct xfs_ail; struct xfs_quotainfo; - -/* - * Prototypes and functions for the Data Migration subsystem. - */ - -typedef int (*xfs_send_data_t)(int, struct xfs_inode *, - xfs_off_t, size_t, int, int *); -typedef int (*xfs_send_mmap_t)(struct vm_area_struct *, uint); -typedef int (*xfs_send_destroy_t)(struct xfs_inode *, dm_right_t); -typedef int (*xfs_send_namesp_t)(dm_eventtype_t, struct xfs_mount *, - struct xfs_inode *, dm_right_t, - struct xfs_inode *, dm_right_t, - const unsigned char *, const unsigned char *, - mode_t, int, int); -typedef int (*xfs_send_mount_t)(struct xfs_mount *, dm_right_t, - char *, char *); -typedef void (*xfs_send_unmount_t)(struct xfs_mount *, struct xfs_inode *, - dm_right_t, mode_t, int, int); - -typedef struct xfs_dmops { - xfs_send_data_t xfs_send_data; - xfs_send_mmap_t xfs_send_mmap; - xfs_send_destroy_t xfs_send_destroy; - xfs_send_namesp_t xfs_send_namesp; - xfs_send_mount_t xfs_send_mount; - xfs_send_unmount_t xfs_send_unmount; -} xfs_dmops_t; - -#define XFS_DMAPI_UNMOUNT_FLAGS(mp) \ - (((mp)->m_dmevmask & (1 << DM_EVENT_UNMOUNT)) ? 0 : DM_FLAGS_UNWANTED) - -#define XFS_SEND_DATA(mp, ev,ip,off,len,fl,lock) \ - (*(mp)->m_dm_ops->xfs_send_data)(ev,ip,off,len,fl,lock) -#define XFS_SEND_MMAP(mp, vma,fl) \ - (*(mp)->m_dm_ops->xfs_send_mmap)(vma,fl) -#define XFS_SEND_DESTROY(mp, ip,right) \ - (*(mp)->m_dm_ops->xfs_send_destroy)(ip,right) -#define XFS_SEND_NAMESP(mp, ev,b1,r1,b2,r2,n1,n2,mode,rval,fl) \ - (*(mp)->m_dm_ops->xfs_send_namesp)(ev,NULL,b1,r1,b2,r2,n1,n2,mode,rval,fl) -#define XFS_SEND_MOUNT(mp,right,path,name) \ - (*(mp)->m_dm_ops->xfs_send_mount)(mp,right,path,name) -#define XFS_SEND_PREUNMOUNT(mp) \ -do { \ - if (mp->m_flags & XFS_MOUNT_DMAPI) { \ - (*(mp)->m_dm_ops->xfs_send_namesp)(DM_EVENT_PREUNMOUNT, mp, \ - (mp)->m_rootip, DM_RIGHT_NULL, \ - (mp)->m_rootip, DM_RIGHT_NULL, \ - NULL, NULL, 0, 0, XFS_DMAPI_UNMOUNT_FLAGS(mp)); \ - } \ -} while (0) -#define XFS_SEND_UNMOUNT(mp) \ -do { \ - if (mp->m_flags & XFS_MOUNT_DMAPI) { \ - (*(mp)->m_dm_ops->xfs_send_unmount)(mp, (mp)->m_rootip, \ - DM_RIGHT_NULL, 0, 0, XFS_DMAPI_UNMOUNT_FLAGS(mp)); \ - } \ -} while (0) - - #ifdef HAVE_PERCPU_SB /* @@ -241,8 +182,6 @@ typedef struct xfs_mount { uint m_chsize; /* size of next field */ struct xfs_chash *m_chash; /* fs private inode per-cluster * hash table */ - struct xfs_dmops *m_dm_ops; /* vector of DMI ops */ - struct xfs_qmops *m_qm_ops; /* vector of XQM ops */ atomic_t m_active_trans; /* number trans frozen */ #ifdef HAVE_PERCPU_SB xfs_icsb_cnts_t __percpu *m_sb_cnts; /* per-cpu superblock counters */ @@ -269,7 +208,6 @@ typedef struct xfs_mount { must be synchronous except for space allocations */ #define XFS_MOUNT_DELAYLOG (1ULL << 1) /* delayed logging is enabled */ -#define XFS_MOUNT_DMAPI (1ULL << 2) /* dmapi is enabled */ #define XFS_MOUNT_WAS_CLEAN (1ULL << 3) #define XFS_MOUNT_FS_SHUTDOWN (1ULL << 4) /* atomic stop of all filesystem operations, typically for @@ -440,11 +378,6 @@ extern int xfs_sb_validate_fsb_count(struct xfs_sb *, __uint64_t); extern int xfs_dev_is_read_only(struct xfs_mount *, char *); -extern int xfs_dmops_get(struct xfs_mount *); -extern void xfs_dmops_put(struct xfs_mount *); - -extern struct xfs_dmops xfs_dmcore_xfs; - #endif /* __KERNEL__ */ extern void xfs_mod_sb(struct xfs_trans *, __int64_t); diff --git a/fs/xfs/xfs_rename.c b/fs/xfs/xfs_rename.c index fc1cda23b817..fa752f495a0c 100644 --- a/fs/xfs/xfs_rename.c +++ b/fs/xfs/xfs_rename.c @@ -24,7 +24,6 @@ #include "xfs_sb.h" #include "xfs_ag.h" #include "xfs_dir2.h" -#include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_da_btree.h" #include "xfs_bmap_btree.h" @@ -119,18 +118,6 @@ xfs_rename( xfs_itrace_entry(src_dp); xfs_itrace_entry(target_dp); - if (DM_EVENT_ENABLED(src_dp, DM_EVENT_RENAME) || - DM_EVENT_ENABLED(target_dp, DM_EVENT_RENAME)) { - error = XFS_SEND_NAMESP(mp, DM_EVENT_RENAME, - src_dp, DM_RIGHT_NULL, - target_dp, DM_RIGHT_NULL, - src_name->name, target_name->name, - 0, 0, 0); - if (error) - return error; - } - /* Return through std_return after this point. */ - new_parent = (src_dp != target_dp); src_is_directory = ((src_ip->i_d.di_mode & S_IFMT) == S_IFDIR); @@ -369,26 +356,13 @@ xfs_rename( * trans_commit will unlock src_ip, target_ip & decrement * the vnode references. */ - error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES); - - /* Fall through to std_return with error = 0 or errno from - * xfs_trans_commit */ -std_return: - if (DM_EVENT_ENABLED(src_dp, DM_EVENT_POSTRENAME) || - DM_EVENT_ENABLED(target_dp, DM_EVENT_POSTRENAME)) { - (void) XFS_SEND_NAMESP (mp, DM_EVENT_POSTRENAME, - src_dp, DM_RIGHT_NULL, - target_dp, DM_RIGHT_NULL, - src_name->name, target_name->name, - 0, error, 0); - } - return error; + return xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES); abort_return: cancel_flags |= XFS_TRANS_ABORT; - /* FALLTHROUGH */ error_return: xfs_bmap_cancel(&free_list); xfs_trans_cancel(tp, cancel_flags); - goto std_return; + std_return: + return error; } diff --git a/fs/xfs/xfs_rtalloc.c b/fs/xfs/xfs_rtalloc.c index a2d32ce335aa..c7b20568ee88 100644 --- a/fs/xfs/xfs_rtalloc.c +++ b/fs/xfs/xfs_rtalloc.c @@ -25,7 +25,6 @@ #include "xfs_sb.h" #include "xfs_ag.h" #include "xfs_dir2.h" -#include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" diff --git a/fs/xfs/xfs_rw.c b/fs/xfs/xfs_rw.c index e336742a58a4..1101bc63ced4 100644 --- a/fs/xfs/xfs_rw.c +++ b/fs/xfs/xfs_rw.c @@ -25,7 +25,6 @@ #include "xfs_sb.h" #include "xfs_ag.h" #include "xfs_dir2.h" -#include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" diff --git a/fs/xfs/xfs_trans.c b/fs/xfs/xfs_trans.c index 28547dfce037..2fd44d8fe3ae 100644 --- a/fs/xfs/xfs_trans.c +++ b/fs/xfs/xfs_trans.c @@ -25,7 +25,6 @@ #include "xfs_sb.h" #include "xfs_ag.h" #include "xfs_dir2.h" -#include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_error.h" #include "xfs_da_btree.h" diff --git a/fs/xfs/xfs_trans_ail.c b/fs/xfs/xfs_trans_ail.c index e799824f7245..dc9069568ff7 100644 --- a/fs/xfs/xfs_trans_ail.c +++ b/fs/xfs/xfs_trans_ail.c @@ -24,7 +24,6 @@ #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_trans_priv.h" #include "xfs_error.h" diff --git a/fs/xfs/xfs_trans_buf.c b/fs/xfs/xfs_trans_buf.c index 63d81a22f4fd..93ed4a5edde8 100644 --- a/fs/xfs/xfs_trans_buf.c +++ b/fs/xfs/xfs_trans_buf.c @@ -25,7 +25,6 @@ #include "xfs_sb.h" #include "xfs_ag.h" #include "xfs_dir2.h" -#include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" diff --git a/fs/xfs/xfs_trans_extfree.c b/fs/xfs/xfs_trans_extfree.c index 27cce2a9c7e9..dfb6a0fdcf9c 100644 --- a/fs/xfs/xfs_trans_extfree.c +++ b/fs/xfs/xfs_trans_extfree.c @@ -23,7 +23,6 @@ #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_trans_priv.h" #include "xfs_extfree_item.h" diff --git a/fs/xfs/xfs_trans_inode.c b/fs/xfs/xfs_trans_inode.c index 2559dfec946b..4f4df63144d7 100644 --- a/fs/xfs/xfs_trans_inode.c +++ b/fs/xfs/xfs_trans_inode.c @@ -25,7 +25,6 @@ #include "xfs_sb.h" #include "xfs_ag.h" #include "xfs_dir2.h" -#include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" diff --git a/fs/xfs/xfs_trans_item.c b/fs/xfs/xfs_trans_item.c index f11d37d06dcc..55035a7401cb 100644 --- a/fs/xfs/xfs_trans_item.c +++ b/fs/xfs/xfs_trans_item.c @@ -28,7 +28,6 @@ #include "xfs_sb.h" #include "xfs_ag.h" #include "xfs_dir2.h" -#include "xfs_dmapi.h" #include "xfs_mount.h" STATIC int xfs_trans_unlock_chunk(xfs_log_item_chunk_t *, diff --git a/fs/xfs/xfs_utils.c b/fs/xfs/xfs_utils.c index 4d88616bde91..4b1df677abd9 100644 --- a/fs/xfs/xfs_utils.c +++ b/fs/xfs/xfs_utils.c @@ -25,7 +25,6 @@ #include "xfs_sb.h" #include "xfs_ag.h" #include "xfs_dir2.h" -#include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_dir2_sf.h" diff --git a/fs/xfs/xfs_vnodeops.c b/fs/xfs/xfs_vnodeops.c index c1646838898f..f6fd7502fc0e 100644 --- a/fs/xfs/xfs_vnodeops.c +++ b/fs/xfs/xfs_vnodeops.c @@ -26,7 +26,6 @@ #include "xfs_sb.h" #include "xfs_ag.h" #include "xfs_dir2.h" -#include "xfs_dmapi.h" #include "xfs_mount.h" #include "xfs_da_btree.h" #include "xfs_bmap_btree.h" @@ -143,16 +142,6 @@ xfs_setattr( goto error_return; } } else { - if (DM_EVENT_ENABLED(ip, DM_EVENT_TRUNCATE) && - !(flags & XFS_ATTR_DMI)) { - int dmflags = AT_DELAY_FLAG(flags) | DM_SEM_FLAG_WR; - code = XFS_SEND_DATA(mp, DM_EVENT_TRUNCATE, ip, - iattr->ia_size, 0, dmflags, NULL); - if (code) { - lock_flags = 0; - goto error_return; - } - } if (need_iolock) lock_flags |= XFS_IOLOCK_EXCL; } @@ -470,17 +459,10 @@ xfs_setattr( return XFS_ERROR(code); } - if (DM_EVENT_ENABLED(ip, DM_EVENT_ATTRIBUTE) && - !(flags & XFS_ATTR_DMI)) { - (void) XFS_SEND_NAMESP(mp, DM_EVENT_ATTRIBUTE, ip, DM_RIGHT_NULL, - NULL, DM_RIGHT_NULL, NULL, NULL, - 0, 0, AT_DELAY_FLAG(flags)); - } return 0; abort_return: commit_flags |= XFS_TRANS_ABORT; - /* FALLTHROUGH */ error_return: xfs_qm_dqrele(udqp); xfs_qm_dqrele(gdqp); @@ -1060,9 +1042,6 @@ xfs_inactive( mp = ip->i_mount; - if (ip->i_d.di_nlink == 0 && DM_EVENT_ENABLED(ip, DM_EVENT_DESTROY)) - XFS_SEND_DESTROY(mp, ip, DM_RIGHT_NULL); - error = 0; /* If this is a read-only mount, don't do this (would generate I/O) */ @@ -1314,16 +1293,6 @@ xfs_create( if (XFS_FORCED_SHUTDOWN(mp)) return XFS_ERROR(EIO); - if (DM_EVENT_ENABLED(dp, DM_EVENT_CREATE)) { - error = XFS_SEND_NAMESP(mp, DM_EVENT_CREATE, - dp, DM_RIGHT_NULL, NULL, - DM_RIGHT_NULL, name->name, NULL, - mode, 0, 0); - - if (error) - return error; - } - if (dp->i_d.di_flags & XFS_DIFLAG_PROJINHERIT) prid = dp->i_d.di_projid; else @@ -1487,16 +1456,7 @@ xfs_create( xfs_qm_dqrele(gdqp); *ipp = ip; - - /* Fallthrough to std_return with error = 0 */ - std_return: - if (DM_EVENT_ENABLED(dp, DM_EVENT_POSTCREATE)) { - XFS_SEND_NAMESP(mp, DM_EVENT_POSTCREATE, dp, DM_RIGHT_NULL, - ip, DM_RIGHT_NULL, name->name, NULL, mode, - error, 0); - } - - return error; + return 0; out_bmap_cancel: xfs_bmap_cancel(&free_list); @@ -1510,8 +1470,8 @@ xfs_create( if (unlock_dp_on_error) xfs_iunlock(dp, XFS_ILOCK_EXCL); - - goto std_return; + std_return: + return error; out_abort_rele: /* @@ -1732,14 +1692,6 @@ xfs_remove( if (XFS_FORCED_SHUTDOWN(mp)) return XFS_ERROR(EIO); - if (DM_EVENT_ENABLED(dp, DM_EVENT_REMOVE)) { - error = XFS_SEND_NAMESP(mp, DM_EVENT_REMOVE, dp, DM_RIGHT_NULL, - NULL, DM_RIGHT_NULL, name->name, NULL, - ip->i_d.di_mode, 0, 0); - if (error) - return error; - } - error = xfs_qm_dqattach(dp, 0); if (error) goto std_return; @@ -1877,21 +1829,15 @@ xfs_remove( if (!is_dir && link_zero && xfs_inode_is_filestream(ip)) xfs_filestream_deassociate(ip); - std_return: - if (DM_EVENT_ENABLED(dp, DM_EVENT_POSTREMOVE)) { - XFS_SEND_NAMESP(mp, DM_EVENT_POSTREMOVE, dp, DM_RIGHT_NULL, - NULL, DM_RIGHT_NULL, name->name, NULL, - ip->i_d.di_mode, error, 0); - } - - return error; + return 0; out_bmap_cancel: xfs_bmap_cancel(&free_list); cancel_flags |= XFS_TRANS_ABORT; out_trans_cancel: xfs_trans_cancel(tp, cancel_flags); - goto std_return; + std_return: + return error; } int @@ -1917,17 +1863,6 @@ xfs_link( if (XFS_FORCED_SHUTDOWN(mp)) return XFS_ERROR(EIO); - if (DM_EVENT_ENABLED(tdp, DM_EVENT_LINK)) { - error = XFS_SEND_NAMESP(mp, DM_EVENT_LINK, - tdp, DM_RIGHT_NULL, - sip, DM_RIGHT_NULL, - target_name->name, NULL, 0, 0, 0); - if (error) - return error; - } - - /* Return through std_return after this point. */ - error = xfs_qm_dqattach(sip, 0); if (error) goto std_return; @@ -2014,27 +1949,14 @@ xfs_link( goto abort_return; } - error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES); - if (error) - goto std_return; - - /* Fall through to std_return with error = 0. */ -std_return: - if (DM_EVENT_ENABLED(sip, DM_EVENT_POSTLINK)) { - (void) XFS_SEND_NAMESP(mp, DM_EVENT_POSTLINK, - tdp, DM_RIGHT_NULL, - sip, DM_RIGHT_NULL, - target_name->name, NULL, 0, error, 0); - } - return error; + return xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES); abort_return: cancel_flags |= XFS_TRANS_ABORT; - /* FALLTHROUGH */ - error_return: xfs_trans_cancel(tp, cancel_flags); - goto std_return; + std_return: + return error; } int @@ -2086,17 +2008,6 @@ xfs_symlink( if (pathlen >= MAXPATHLEN) /* total string too long */ return XFS_ERROR(ENAMETOOLONG); - if (DM_EVENT_ENABLED(dp, DM_EVENT_SYMLINK)) { - error = XFS_SEND_NAMESP(mp, DM_EVENT_SYMLINK, dp, - DM_RIGHT_NULL, NULL, DM_RIGHT_NULL, - link_name->name, - (unsigned char *)target_path, 0, 0, 0); - if (error) - return error; - } - - /* Return through std_return after this point. */ - udqp = gdqp = NULL; if (dp->i_d.di_flags & XFS_DIFLAG_PROJINHERIT) prid = dp->i_d.di_projid; @@ -2278,21 +2189,8 @@ xfs_symlink( xfs_qm_dqrele(udqp); xfs_qm_dqrele(gdqp); - /* Fall through to std_return with error = 0 or errno from - * xfs_trans_commit */ -std_return: - if (DM_EVENT_ENABLED(dp, DM_EVENT_POSTSYMLINK)) { - (void) XFS_SEND_NAMESP(mp, DM_EVENT_POSTSYMLINK, - dp, DM_RIGHT_NULL, - error ? NULL : ip, - DM_RIGHT_NULL, link_name->name, - (unsigned char *)target_path, - 0, error, 0); - } - - if (!error) - *ipp = ip; - return error; + *ipp = ip; + return 0; error2: IRELE(ip); @@ -2306,8 +2204,8 @@ std_return: if (unlock_dp_on_error) xfs_iunlock(dp, XFS_ILOCK_EXCL); - - goto std_return; + std_return: + return error; } int @@ -2412,25 +2310,9 @@ xfs_alloc_file_space( startoffset_fsb = XFS_B_TO_FSBT(mp, offset); allocatesize_fsb = XFS_B_TO_FSB(mp, count); - /* Generate a DMAPI event if needed. */ - if (alloc_type != 0 && offset < ip->i_size && - (attr_flags & XFS_ATTR_DMI) == 0 && - DM_EVENT_ENABLED(ip, DM_EVENT_WRITE)) { - xfs_off_t end_dmi_offset; - - end_dmi_offset = offset+len; - if (end_dmi_offset > ip->i_size) - end_dmi_offset = ip->i_size; - error = XFS_SEND_DATA(mp, DM_EVENT_WRITE, ip, offset, - end_dmi_offset - offset, 0, NULL); - if (error) - return error; - } - /* * Allocate file space until done or until there is an error */ -retry: while (allocatesize_fsb && !error) { xfs_fileoff_t s, e; @@ -2527,17 +2409,6 @@ retry: startoffset_fsb += allocated_fsb; allocatesize_fsb -= allocated_fsb; } -dmapi_enospc_check: - if (error == ENOSPC && (attr_flags & XFS_ATTR_DMI) == 0 && - DM_EVENT_ENABLED(ip, DM_EVENT_NOSPACE)) { - error = XFS_SEND_NAMESP(mp, DM_EVENT_NOSPACE, - ip, DM_RIGHT_NULL, - ip, DM_RIGHT_NULL, - NULL, NULL, 0, 0, 0); /* Delay flag intentionally unused */ - if (error == 0) - goto retry; /* Maybe DMAPI app. has made space */ - /* else fall through with error from XFS_SEND_DATA */ - } return error; @@ -2548,7 +2419,7 @@ error0: /* Cancel bmap, unlock inode, unreserve quota blocks, cancel trans */ error1: /* Just cancel transaction */ xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES | XFS_TRANS_ABORT); xfs_iunlock(ip, XFS_ILOCK_EXCL); - goto dmapi_enospc_check; + return error; } /* @@ -2661,7 +2532,6 @@ xfs_free_file_space( { int committed; int done; - xfs_off_t end_dmi_offset; xfs_fileoff_t endoffset_fsb; int error; xfs_fsblock_t firstfsb; @@ -2691,19 +2561,7 @@ xfs_free_file_space( return error; rt = XFS_IS_REALTIME_INODE(ip); startoffset_fsb = XFS_B_TO_FSB(mp, offset); - end_dmi_offset = offset + len; - endoffset_fsb = XFS_B_TO_FSBT(mp, end_dmi_offset); - - if (offset < ip->i_size && (attr_flags & XFS_ATTR_DMI) == 0 && - DM_EVENT_ENABLED(ip, DM_EVENT_WRITE)) { - if (end_dmi_offset > ip->i_size) - end_dmi_offset = ip->i_size; - error = XFS_SEND_DATA(mp, DM_EVENT_WRITE, ip, - offset, end_dmi_offset - offset, - AT_DELAY_FLAG(attr_flags), NULL); - if (error) - return error; - } + endoffset_fsb = XFS_B_TO_FSBT(mp, offset + len); if (attr_flags & XFS_ATTR_NOLOCK) need_iolock = 0; -- cgit v1.2.3 From 3400777ff03a3cd4fdbc6cb15676fc7e7ceefc00 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 23 Jun 2010 18:11:15 +1000 Subject: xfs: remove unneeded #include statements Signed-off-by: Christoph Hellwig Reviewed-by: Dave Chinner --- fs/xfs/linux-2.6/xfs_aops.c | 6 ------ fs/xfs/linux-2.6/xfs_file.c | 7 ------- fs/xfs/linux-2.6/xfs_ioctl.c | 8 -------- fs/xfs/linux-2.6/xfs_ioctl32.c | 3 --- fs/xfs/linux-2.6/xfs_iops.c | 7 ------- fs/xfs/linux-2.6/xfs_super.c | 3 --- fs/xfs/linux-2.6/xfs_sync.c | 10 ---------- fs/xfs/linux-2.6/xfs_trace.c | 3 --- fs/xfs/quota/xfs_dquot.c | 9 --------- fs/xfs/quota/xfs_dquot_item.c | 9 --------- fs/xfs/quota/xfs_qm.c | 6 ------ fs/xfs/quota/xfs_qm_bhv.c | 9 --------- fs/xfs/quota/xfs_qm_stats.c | 9 --------- fs/xfs/quota/xfs_qm_syscalls.c | 9 --------- fs/xfs/quota/xfs_trans_dquot.c | 9 --------- fs/xfs/xfs_alloc.c | 4 ---- fs/xfs/xfs_alloc_btree.c | 4 ---- fs/xfs/xfs_attr.c | 5 ----- fs/xfs/xfs_attr_leaf.c | 2 -- fs/xfs/xfs_bmap.c | 2 -- fs/xfs/xfs_bmap_btree.c | 4 ---- fs/xfs/xfs_btree.c | 4 ---- fs/xfs/xfs_da_btree.c | 4 ---- fs/xfs/xfs_dfrag.c | 8 -------- fs/xfs/xfs_dir2.c | 1 - fs/xfs/xfs_dir2_block.c | 1 - fs/xfs/xfs_dir2_data.c | 1 - fs/xfs/xfs_dir2_leaf.c | 1 - fs/xfs/xfs_dir2_node.c | 1 - fs/xfs/xfs_dir2_sf.c | 1 - fs/xfs/xfs_error.c | 3 --- fs/xfs/xfs_filestream.c | 3 --- fs/xfs/xfs_fsops.c | 3 --- fs/xfs/xfs_ialloc.c | 3 --- fs/xfs/xfs_ialloc_btree.c | 3 --- fs/xfs/xfs_iget.c | 3 --- fs/xfs/xfs_inode.c | 3 --- fs/xfs/xfs_inode_item.c | 9 --------- fs/xfs/xfs_iomap.c | 4 ---- fs/xfs/xfs_itable.c | 3 --- fs/xfs/xfs_log.c | 3 --- fs/xfs/xfs_log_cil.c | 1 - fs/xfs/xfs_log_recover.c | 3 --- fs/xfs/xfs_mount.c | 2 -- fs/xfs/xfs_rename.c | 2 -- fs/xfs/xfs_rtalloc.c | 6 ------ fs/xfs/xfs_rw.c | 14 -------------- fs/xfs/xfs_trans.c | 3 --- fs/xfs/xfs_trans_buf.c | 3 --- fs/xfs/xfs_trans_inode.c | 4 ---- fs/xfs/xfs_utils.c | 3 --- fs/xfs/xfs_vnodeops.c | 4 ---- 52 files changed, 235 deletions(-) (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_aops.c b/fs/xfs/linux-2.6/xfs_aops.c index 4cd5e00f0c5c..e42c0ba6688a 100644 --- a/fs/xfs/linux-2.6/xfs_aops.c +++ b/fs/xfs/linux-2.6/xfs_aops.c @@ -21,18 +21,12 @@ #include "xfs_inum.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir2.h" #include "xfs_trans.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" -#include "xfs_alloc_btree.h" -#include "xfs_ialloc_btree.h" -#include "xfs_dir2_sf.h" -#include "xfs_attr_sf.h" #include "xfs_dinode.h" #include "xfs_inode.h" #include "xfs_alloc.h" -#include "xfs_btree.h" #include "xfs_error.h" #include "xfs_rw.h" #include "xfs_iomap.h" diff --git a/fs/xfs/linux-2.6/xfs_file.c b/fs/xfs/linux-2.6/xfs_file.c index dca06131551a..8d26d93648b4 100644 --- a/fs/xfs/linux-2.6/xfs_file.c +++ b/fs/xfs/linux-2.6/xfs_file.c @@ -22,22 +22,15 @@ #include "xfs_inum.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir2.h" #include "xfs_trans.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" -#include "xfs_alloc_btree.h" -#include "xfs_ialloc_btree.h" #include "xfs_alloc.h" -#include "xfs_btree.h" -#include "xfs_attr_sf.h" -#include "xfs_dir2_sf.h" #include "xfs_dinode.h" #include "xfs_inode.h" #include "xfs_inode_item.h" #include "xfs_bmap.h" #include "xfs_error.h" -#include "xfs_rw.h" #include "xfs_vnodeops.h" #include "xfs_da_btree.h" #include "xfs_ioctl.h" diff --git a/fs/xfs/linux-2.6/xfs_ioctl.c b/fs/xfs/linux-2.6/xfs_ioctl.c index addd37051aa9..8aa54f0eec15 100644 --- a/fs/xfs/linux-2.6/xfs_ioctl.c +++ b/fs/xfs/linux-2.6/xfs_ioctl.c @@ -23,23 +23,15 @@ #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir2.h" #include "xfs_alloc.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" -#include "xfs_alloc_btree.h" -#include "xfs_ialloc_btree.h" -#include "xfs_attr_sf.h" -#include "xfs_dir2_sf.h" #include "xfs_dinode.h" #include "xfs_inode.h" #include "xfs_ioctl.h" -#include "xfs_btree.h" -#include "xfs_ialloc.h" #include "xfs_rtalloc.h" #include "xfs_itable.h" #include "xfs_error.h" -#include "xfs_rw.h" #include "xfs_attr.h" #include "xfs_bmap.h" #include "xfs_buf_item.h" diff --git a/fs/xfs/linux-2.6/xfs_ioctl32.c b/fs/xfs/linux-2.6/xfs_ioctl32.c index e67be3ae3540..6cd1225608ac 100644 --- a/fs/xfs/linux-2.6/xfs_ioctl32.c +++ b/fs/xfs/linux-2.6/xfs_ioctl32.c @@ -28,11 +28,8 @@ #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir2.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" -#include "xfs_attr_sf.h" -#include "xfs_dir2_sf.h" #include "xfs_vnode.h" #include "xfs_dinode.h" #include "xfs_inode.h" diff --git a/fs/xfs/linux-2.6/xfs_iops.c b/fs/xfs/linux-2.6/xfs_iops.c index ce3118477d9e..4393de6b0c07 100644 --- a/fs/xfs/linux-2.6/xfs_iops.c +++ b/fs/xfs/linux-2.6/xfs_iops.c @@ -24,20 +24,13 @@ #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir2.h" #include "xfs_alloc.h" #include "xfs_quota.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" -#include "xfs_alloc_btree.h" -#include "xfs_ialloc_btree.h" -#include "xfs_dir2_sf.h" -#include "xfs_attr_sf.h" #include "xfs_dinode.h" #include "xfs_inode.h" #include "xfs_bmap.h" -#include "xfs_btree.h" -#include "xfs_ialloc.h" #include "xfs_rtalloc.h" #include "xfs_error.h" #include "xfs_itable.h" diff --git a/fs/xfs/linux-2.6/xfs_super.c b/fs/xfs/linux-2.6/xfs_super.c index 4c7b8f9f4deb..5593066d497d 100644 --- a/fs/xfs/linux-2.6/xfs_super.c +++ b/fs/xfs/linux-2.6/xfs_super.c @@ -30,8 +30,6 @@ #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" #include "xfs_ialloc_btree.h" -#include "xfs_dir2_sf.h" -#include "xfs_attr_sf.h" #include "xfs_dinode.h" #include "xfs_inode.h" #include "xfs_btree.h" @@ -42,7 +40,6 @@ #include "xfs_error.h" #include "xfs_itable.h" #include "xfs_fsops.h" -#include "xfs_rw.h" #include "xfs_attr.h" #include "xfs_buf_item.h" #include "xfs_utils.h" diff --git a/fs/xfs/linux-2.6/xfs_sync.c b/fs/xfs/linux-2.6/xfs_sync.c index ce323377a708..850b4198bf60 100644 --- a/fs/xfs/linux-2.6/xfs_sync.c +++ b/fs/xfs/linux-2.6/xfs_sync.c @@ -24,24 +24,14 @@ #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir2.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" -#include "xfs_alloc_btree.h" -#include "xfs_ialloc_btree.h" -#include "xfs_btree.h" -#include "xfs_dir2_sf.h" -#include "xfs_attr_sf.h" #include "xfs_inode.h" #include "xfs_dinode.h" #include "xfs_error.h" -#include "xfs_mru_cache.h" #include "xfs_filestream.h" #include "xfs_vnodeops.h" -#include "xfs_utils.h" -#include "xfs_buf_item.h" #include "xfs_inode_item.h" -#include "xfs_rw.h" #include "xfs_quota.h" #include "xfs_trace.h" diff --git a/fs/xfs/linux-2.6/xfs_trace.c b/fs/xfs/linux-2.6/xfs_trace.c index 03e2dca36d4b..88d25d4aa56e 100644 --- a/fs/xfs/linux-2.6/xfs_trace.c +++ b/fs/xfs/linux-2.6/xfs_trace.c @@ -24,13 +24,10 @@ #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir2.h" #include "xfs_da_btree.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" #include "xfs_ialloc_btree.h" -#include "xfs_dir2_sf.h" -#include "xfs_attr_sf.h" #include "xfs_dinode.h" #include "xfs_inode.h" #include "xfs_btree.h" diff --git a/fs/xfs/quota/xfs_dquot.c b/fs/xfs/quota/xfs_dquot.c index 3c111ea41033..f152af8cfab0 100644 --- a/fs/xfs/quota/xfs_dquot.c +++ b/fs/xfs/quota/xfs_dquot.c @@ -23,24 +23,15 @@ #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir2.h" #include "xfs_alloc.h" #include "xfs_quota.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" -#include "xfs_alloc_btree.h" -#include "xfs_ialloc_btree.h" -#include "xfs_dir2_sf.h" -#include "xfs_attr_sf.h" -#include "xfs_dinode.h" #include "xfs_inode.h" -#include "xfs_btree.h" -#include "xfs_ialloc.h" #include "xfs_bmap.h" #include "xfs_rtalloc.h" #include "xfs_error.h" #include "xfs_itable.h" -#include "xfs_rw.h" #include "xfs_attr.h" #include "xfs_buf_item.h" #include "xfs_trans_space.h" diff --git a/fs/xfs/quota/xfs_dquot_item.c b/fs/xfs/quota/xfs_dquot_item.c index 15c211bd18d6..fb7054c1539d 100644 --- a/fs/xfs/quota/xfs_dquot_item.c +++ b/fs/xfs/quota/xfs_dquot_item.c @@ -23,24 +23,15 @@ #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir2.h" #include "xfs_alloc.h" #include "xfs_quota.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" -#include "xfs_alloc_btree.h" -#include "xfs_ialloc_btree.h" -#include "xfs_dir2_sf.h" -#include "xfs_attr_sf.h" -#include "xfs_dinode.h" #include "xfs_inode.h" #include "xfs_bmap.h" -#include "xfs_btree.h" -#include "xfs_ialloc.h" #include "xfs_rtalloc.h" #include "xfs_error.h" #include "xfs_itable.h" -#include "xfs_rw.h" #include "xfs_attr.h" #include "xfs_buf_item.h" #include "xfs_trans_priv.h" diff --git a/fs/xfs/quota/xfs_qm.c b/fs/xfs/quota/xfs_qm.c index 0e01edb00cd9..b32c3fb9e779 100644 --- a/fs/xfs/quota/xfs_qm.c +++ b/fs/xfs/quota/xfs_qm.c @@ -23,24 +23,18 @@ #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir2.h" #include "xfs_alloc.h" #include "xfs_quota.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" -#include "xfs_alloc_btree.h" #include "xfs_ialloc_btree.h" -#include "xfs_dir2_sf.h" -#include "xfs_attr_sf.h" #include "xfs_dinode.h" #include "xfs_inode.h" -#include "xfs_btree.h" #include "xfs_ialloc.h" #include "xfs_itable.h" #include "xfs_rtalloc.h" #include "xfs_error.h" #include "xfs_bmap.h" -#include "xfs_rw.h" #include "xfs_attr.h" #include "xfs_buf_item.h" #include "xfs_trans_space.h" diff --git a/fs/xfs/quota/xfs_qm_bhv.c b/fs/xfs/quota/xfs_qm_bhv.c index 7283bccb37a6..bea02d786c5d 100644 --- a/fs/xfs/quota/xfs_qm_bhv.c +++ b/fs/xfs/quota/xfs_qm_bhv.c @@ -23,24 +23,15 @@ #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir2.h" #include "xfs_alloc.h" #include "xfs_quota.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" -#include "xfs_alloc_btree.h" -#include "xfs_ialloc_btree.h" -#include "xfs_dir2_sf.h" -#include "xfs_attr_sf.h" -#include "xfs_dinode.h" #include "xfs_inode.h" -#include "xfs_ialloc.h" #include "xfs_itable.h" -#include "xfs_btree.h" #include "xfs_bmap.h" #include "xfs_rtalloc.h" #include "xfs_error.h" -#include "xfs_rw.h" #include "xfs_attr.h" #include "xfs_buf_item.h" #include "xfs_qm.h" diff --git a/fs/xfs/quota/xfs_qm_stats.c b/fs/xfs/quota/xfs_qm_stats.c index 446ae61af9a2..8671a0b32644 100644 --- a/fs/xfs/quota/xfs_qm_stats.c +++ b/fs/xfs/quota/xfs_qm_stats.c @@ -23,24 +23,15 @@ #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir2.h" #include "xfs_alloc.h" #include "xfs_quota.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" -#include "xfs_alloc_btree.h" -#include "xfs_ialloc_btree.h" -#include "xfs_dir2_sf.h" -#include "xfs_attr_sf.h" -#include "xfs_dinode.h" #include "xfs_inode.h" -#include "xfs_ialloc.h" #include "xfs_itable.h" #include "xfs_bmap.h" -#include "xfs_btree.h" #include "xfs_rtalloc.h" #include "xfs_error.h" -#include "xfs_rw.h" #include "xfs_attr.h" #include "xfs_buf_item.h" #include "xfs_qm.h" diff --git a/fs/xfs/quota/xfs_qm_syscalls.c b/fs/xfs/quota/xfs_qm_syscalls.c index b286e0a3111b..98dc6feef9f1 100644 --- a/fs/xfs/quota/xfs_qm_syscalls.c +++ b/fs/xfs/quota/xfs_qm_syscalls.c @@ -26,24 +26,15 @@ #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir2.h" #include "xfs_alloc.h" #include "xfs_quota.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" -#include "xfs_alloc_btree.h" -#include "xfs_ialloc_btree.h" -#include "xfs_dir2_sf.h" -#include "xfs_attr_sf.h" -#include "xfs_dinode.h" #include "xfs_inode.h" -#include "xfs_ialloc.h" #include "xfs_itable.h" #include "xfs_bmap.h" -#include "xfs_btree.h" #include "xfs_rtalloc.h" #include "xfs_error.h" -#include "xfs_rw.h" #include "xfs_attr.h" #include "xfs_buf_item.h" #include "xfs_utils.h" diff --git a/fs/xfs/quota/xfs_trans_dquot.c b/fs/xfs/quota/xfs_trans_dquot.c index 04155c43b1b4..08f5604d092f 100644 --- a/fs/xfs/quota/xfs_trans_dquot.c +++ b/fs/xfs/quota/xfs_trans_dquot.c @@ -23,24 +23,15 @@ #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir2.h" #include "xfs_alloc.h" #include "xfs_quota.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" -#include "xfs_alloc_btree.h" -#include "xfs_ialloc_btree.h" -#include "xfs_attr_sf.h" -#include "xfs_dir2_sf.h" -#include "xfs_dinode.h" #include "xfs_inode.h" -#include "xfs_ialloc.h" #include "xfs_itable.h" -#include "xfs_btree.h" #include "xfs_bmap.h" #include "xfs_rtalloc.h" #include "xfs_error.h" -#include "xfs_rw.h" #include "xfs_attr.h" #include "xfs_buf_item.h" #include "xfs_trans_priv.h" diff --git a/fs/xfs/xfs_alloc.c b/fs/xfs/xfs_alloc.c index 42b4b52644eb..6fe5c02ba19a 100644 --- a/fs/xfs/xfs_alloc.c +++ b/fs/xfs/xfs_alloc.c @@ -24,17 +24,13 @@ #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir2.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" #include "xfs_ialloc_btree.h" -#include "xfs_dir2_sf.h" -#include "xfs_attr_sf.h" #include "xfs_dinode.h" #include "xfs_inode.h" #include "xfs_btree.h" -#include "xfs_ialloc.h" #include "xfs_alloc.h" #include "xfs_error.h" #include "xfs_trace.h" diff --git a/fs/xfs/xfs_alloc_btree.c b/fs/xfs/xfs_alloc_btree.c index 7d638a5d1f0d..97f7328967fd 100644 --- a/fs/xfs/xfs_alloc_btree.c +++ b/fs/xfs/xfs_alloc_btree.c @@ -24,18 +24,14 @@ #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir2.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" #include "xfs_ialloc_btree.h" -#include "xfs_dir2_sf.h" -#include "xfs_attr_sf.h" #include "xfs_dinode.h" #include "xfs_inode.h" #include "xfs_btree.h" #include "xfs_btree_trace.h" -#include "xfs_ialloc.h" #include "xfs_alloc.h" #include "xfs_error.h" #include "xfs_trace.h" diff --git a/fs/xfs/xfs_attr.c b/fs/xfs/xfs_attr.c index 426748955e3e..8bde79785a75 100644 --- a/fs/xfs/xfs_attr.c +++ b/fs/xfs/xfs_attr.c @@ -25,18 +25,13 @@ #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir2.h" #include "xfs_mount.h" #include "xfs_da_btree.h" #include "xfs_bmap_btree.h" -#include "xfs_alloc_btree.h" -#include "xfs_ialloc_btree.h" -#include "xfs_dir2_sf.h" #include "xfs_attr_sf.h" #include "xfs_dinode.h" #include "xfs_inode.h" #include "xfs_alloc.h" -#include "xfs_btree.h" #include "xfs_inode_item.h" #include "xfs_bmap.h" #include "xfs_attr.h" diff --git a/fs/xfs/xfs_attr_leaf.c b/fs/xfs/xfs_attr_leaf.c index ca4d11d3a7a7..e4d48b2cee19 100644 --- a/fs/xfs/xfs_attr_leaf.c +++ b/fs/xfs/xfs_attr_leaf.c @@ -24,7 +24,6 @@ #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir2.h" #include "xfs_mount.h" #include "xfs_da_btree.h" #include "xfs_bmap_btree.h" @@ -32,7 +31,6 @@ #include "xfs_ialloc_btree.h" #include "xfs_alloc.h" #include "xfs_btree.h" -#include "xfs_dir2_sf.h" #include "xfs_attr_sf.h" #include "xfs_dinode.h" #include "xfs_inode.h" diff --git a/fs/xfs/xfs_bmap.c b/fs/xfs/xfs_bmap.c index 9db1418a64bf..ed4e3ae2c1d0 100644 --- a/fs/xfs/xfs_bmap.c +++ b/fs/xfs/xfs_bmap.c @@ -30,12 +30,10 @@ #include "xfs_alloc_btree.h" #include "xfs_ialloc_btree.h" #include "xfs_dir2_sf.h" -#include "xfs_attr_sf.h" #include "xfs_dinode.h" #include "xfs_inode.h" #include "xfs_btree.h" #include "xfs_mount.h" -#include "xfs_ialloc.h" #include "xfs_itable.h" #include "xfs_dir2_data.h" #include "xfs_dir2_leaf.h" diff --git a/fs/xfs/xfs_bmap_btree.c b/fs/xfs/xfs_bmap_btree.c index 998acf185c48..87d3c10b6954 100644 --- a/fs/xfs/xfs_bmap_btree.c +++ b/fs/xfs/xfs_bmap_btree.c @@ -24,20 +24,16 @@ #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir2.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" #include "xfs_ialloc_btree.h" -#include "xfs_dir2_sf.h" -#include "xfs_attr_sf.h" #include "xfs_dinode.h" #include "xfs_inode.h" #include "xfs_inode_item.h" #include "xfs_alloc.h" #include "xfs_btree.h" #include "xfs_btree_trace.h" -#include "xfs_ialloc.h" #include "xfs_itable.h" #include "xfs_bmap.h" #include "xfs_error.h" diff --git a/fs/xfs/xfs_btree.c b/fs/xfs/xfs_btree.c index beed23ec9259..829af92f0fba 100644 --- a/fs/xfs/xfs_btree.c +++ b/fs/xfs/xfs_btree.c @@ -24,19 +24,15 @@ #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir2.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" #include "xfs_ialloc_btree.h" -#include "xfs_dir2_sf.h" -#include "xfs_attr_sf.h" #include "xfs_dinode.h" #include "xfs_inode.h" #include "xfs_inode_item.h" #include "xfs_btree.h" #include "xfs_btree_trace.h" -#include "xfs_ialloc.h" #include "xfs_error.h" #include "xfs_trace.h" diff --git a/fs/xfs/xfs_da_btree.c b/fs/xfs/xfs_da_btree.c index a4a8965a92f5..731f1f41eca1 100644 --- a/fs/xfs/xfs_da_btree.c +++ b/fs/xfs/xfs_da_btree.c @@ -28,15 +28,11 @@ #include "xfs_mount.h" #include "xfs_da_btree.h" #include "xfs_bmap_btree.h" -#include "xfs_alloc_btree.h" -#include "xfs_ialloc_btree.h" #include "xfs_dir2_sf.h" -#include "xfs_attr_sf.h" #include "xfs_dinode.h" #include "xfs_inode.h" #include "xfs_inode_item.h" #include "xfs_alloc.h" -#include "xfs_btree.h" #include "xfs_bmap.h" #include "xfs_attr.h" #include "xfs_attr_leaf.h" diff --git a/fs/xfs/xfs_dfrag.c b/fs/xfs/xfs_dfrag.c index 86330da07f29..7b11dc0494c2 100644 --- a/fs/xfs/xfs_dfrag.c +++ b/fs/xfs/xfs_dfrag.c @@ -24,23 +24,15 @@ #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir2.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" -#include "xfs_alloc_btree.h" -#include "xfs_ialloc_btree.h" -#include "xfs_dir2_sf.h" -#include "xfs_attr_sf.h" #include "xfs_dinode.h" #include "xfs_inode.h" #include "xfs_inode_item.h" #include "xfs_bmap.h" -#include "xfs_btree.h" -#include "xfs_ialloc.h" #include "xfs_itable.h" #include "xfs_dfrag.h" #include "xfs_error.h" -#include "xfs_rw.h" #include "xfs_vnodeops.h" #include "xfs_trace.h" diff --git a/fs/xfs/xfs_dir2.c b/fs/xfs/xfs_dir2.c index 429f234c1b16..9c279ede05c5 100644 --- a/fs/xfs/xfs_dir2.c +++ b/fs/xfs/xfs_dir2.c @@ -30,7 +30,6 @@ #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" #include "xfs_dir2_sf.h" -#include "xfs_attr_sf.h" #include "xfs_dinode.h" #include "xfs_inode.h" #include "xfs_inode_item.h" diff --git a/fs/xfs/xfs_dir2_block.c b/fs/xfs/xfs_dir2_block.c index ce3bac65d056..68f4926c7d16 100644 --- a/fs/xfs/xfs_dir2_block.c +++ b/fs/xfs/xfs_dir2_block.c @@ -28,7 +28,6 @@ #include "xfs_da_btree.h" #include "xfs_bmap_btree.h" #include "xfs_dir2_sf.h" -#include "xfs_attr_sf.h" #include "xfs_dinode.h" #include "xfs_inode.h" #include "xfs_inode_item.h" diff --git a/fs/xfs/xfs_dir2_data.c b/fs/xfs/xfs_dir2_data.c index ece400b833df..921595b84f5b 100644 --- a/fs/xfs/xfs_dir2_data.c +++ b/fs/xfs/xfs_dir2_data.c @@ -28,7 +28,6 @@ #include "xfs_da_btree.h" #include "xfs_bmap_btree.h" #include "xfs_dir2_sf.h" -#include "xfs_attr_sf.h" #include "xfs_dinode.h" #include "xfs_inode.h" #include "xfs_dir2_data.h" diff --git a/fs/xfs/xfs_dir2_leaf.c b/fs/xfs/xfs_dir2_leaf.c index 99c8fe0cf223..586b010e58b4 100644 --- a/fs/xfs/xfs_dir2_leaf.c +++ b/fs/xfs/xfs_dir2_leaf.c @@ -28,7 +28,6 @@ #include "xfs_mount.h" #include "xfs_da_btree.h" #include "xfs_bmap_btree.h" -#include "xfs_attr_sf.h" #include "xfs_dir2_sf.h" #include "xfs_dinode.h" #include "xfs_inode.h" diff --git a/fs/xfs/xfs_dir2_node.c b/fs/xfs/xfs_dir2_node.c index d1b40ea0cd69..f9a0864b696a 100644 --- a/fs/xfs/xfs_dir2_node.c +++ b/fs/xfs/xfs_dir2_node.c @@ -28,7 +28,6 @@ #include "xfs_da_btree.h" #include "xfs_bmap_btree.h" #include "xfs_dir2_sf.h" -#include "xfs_attr_sf.h" #include "xfs_dinode.h" #include "xfs_inode.h" #include "xfs_bmap.h" diff --git a/fs/xfs/xfs_dir2_sf.c b/fs/xfs/xfs_dir2_sf.c index d4e63e68fd70..b1bae6b1eed9 100644 --- a/fs/xfs/xfs_dir2_sf.c +++ b/fs/xfs/xfs_dir2_sf.c @@ -28,7 +28,6 @@ #include "xfs_da_btree.h" #include "xfs_bmap_btree.h" #include "xfs_dir2_sf.h" -#include "xfs_attr_sf.h" #include "xfs_dinode.h" #include "xfs_inode.h" #include "xfs_inode_item.h" diff --git a/fs/xfs/xfs_error.c b/fs/xfs/xfs_error.c index 3d8456cb71ff..ed9990267661 100644 --- a/fs/xfs/xfs_error.c +++ b/fs/xfs/xfs_error.c @@ -23,11 +23,8 @@ #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir2.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" -#include "xfs_dir2_sf.h" -#include "xfs_attr_sf.h" #include "xfs_dinode.h" #include "xfs_inode.h" #include "xfs_utils.h" diff --git a/fs/xfs/xfs_filestream.c b/fs/xfs/xfs_filestream.c index ad118dac425d..d34b9e8d2d37 100644 --- a/fs/xfs/xfs_filestream.c +++ b/fs/xfs/xfs_filestream.c @@ -18,9 +18,6 @@ #include "xfs.h" #include "xfs_bmap_btree.h" #include "xfs_inum.h" -#include "xfs_dir2.h" -#include "xfs_dir2_sf.h" -#include "xfs_attr_sf.h" #include "xfs_dinode.h" #include "xfs_inode.h" #include "xfs_ag.h" diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c index 84c002eab0d6..ade96922fc8c 100644 --- a/fs/xfs/xfs_fsops.c +++ b/fs/xfs/xfs_fsops.c @@ -24,13 +24,10 @@ #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir2.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" #include "xfs_ialloc_btree.h" -#include "xfs_dir2_sf.h" -#include "xfs_attr_sf.h" #include "xfs_dinode.h" #include "xfs_inode.h" #include "xfs_inode_item.h" diff --git a/fs/xfs/xfs_ialloc.c b/fs/xfs/xfs_ialloc.c index ab76d73d54e9..abf80ae1e95b 100644 --- a/fs/xfs/xfs_ialloc.c +++ b/fs/xfs/xfs_ialloc.c @@ -24,13 +24,10 @@ #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir2.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" #include "xfs_ialloc_btree.h" -#include "xfs_dir2_sf.h" -#include "xfs_attr_sf.h" #include "xfs_dinode.h" #include "xfs_inode.h" #include "xfs_btree.h" diff --git a/fs/xfs/xfs_ialloc_btree.c b/fs/xfs/xfs_ialloc_btree.c index 0e6563c05e05..d352862cefa0 100644 --- a/fs/xfs/xfs_ialloc_btree.c +++ b/fs/xfs/xfs_ialloc_btree.c @@ -24,13 +24,10 @@ #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir2.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" #include "xfs_ialloc_btree.h" -#include "xfs_dir2_sf.h" -#include "xfs_attr_sf.h" #include "xfs_dinode.h" #include "xfs_inode.h" #include "xfs_btree.h" diff --git a/fs/xfs/xfs_iget.c b/fs/xfs/xfs_iget.c index 0eb2f965503c..633cb331b9e9 100644 --- a/fs/xfs/xfs_iget.c +++ b/fs/xfs/xfs_iget.c @@ -25,13 +25,10 @@ #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir2.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" #include "xfs_ialloc_btree.h" -#include "xfs_dir2_sf.h" -#include "xfs_attr_sf.h" #include "xfs_dinode.h" #include "xfs_inode.h" #include "xfs_btree.h" diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index 767c43a8d164..dde2e5dc6c62 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -27,12 +27,10 @@ #include "xfs_trans_priv.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir2.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" #include "xfs_ialloc_btree.h" -#include "xfs_dir2_sf.h" #include "xfs_attr_sf.h" #include "xfs_dinode.h" #include "xfs_inode.h" @@ -43,7 +41,6 @@ #include "xfs_alloc.h" #include "xfs_ialloc.h" #include "xfs_bmap.h" -#include "xfs_rw.h" #include "xfs_error.h" #include "xfs_utils.h" #include "xfs_quota.h" diff --git a/fs/xfs/xfs_inode_item.c b/fs/xfs/xfs_inode_item.c index 8c561b970c56..4b97d7754b83 100644 --- a/fs/xfs/xfs_inode_item.c +++ b/fs/xfs/xfs_inode_item.c @@ -22,23 +22,14 @@ #include "xfs_log.h" #include "xfs_inum.h" #include "xfs_trans.h" -#include "xfs_buf_item.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir2.h" #include "xfs_mount.h" #include "xfs_trans_priv.h" #include "xfs_bmap_btree.h" -#include "xfs_alloc_btree.h" -#include "xfs_ialloc_btree.h" -#include "xfs_dir2_sf.h" -#include "xfs_attr_sf.h" #include "xfs_dinode.h" #include "xfs_inode.h" #include "xfs_inode_item.h" -#include "xfs_btree.h" -#include "xfs_ialloc.h" -#include "xfs_rw.h" #include "xfs_error.h" #include "xfs_trace.h" diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c index a2653aa0f256..772f3e791ebe 100644 --- a/fs/xfs/xfs_iomap.c +++ b/fs/xfs/xfs_iomap.c @@ -23,18 +23,14 @@ #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir2.h" #include "xfs_alloc.h" #include "xfs_quota.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" #include "xfs_ialloc_btree.h" -#include "xfs_dir2_sf.h" -#include "xfs_attr_sf.h" #include "xfs_dinode.h" #include "xfs_inode.h" -#include "xfs_ialloc.h" #include "xfs_btree.h" #include "xfs_bmap.h" #include "xfs_rtalloc.h" diff --git a/fs/xfs/xfs_itable.c b/fs/xfs/xfs_itable.c index 69abb344f2c0..200dc6fc8cc5 100644 --- a/fs/xfs/xfs_itable.c +++ b/fs/xfs/xfs_itable.c @@ -24,13 +24,10 @@ #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir2.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" #include "xfs_ialloc_btree.h" -#include "xfs_dir2_sf.h" -#include "xfs_attr_sf.h" #include "xfs_dinode.h" #include "xfs_inode.h" #include "xfs_ialloc.h" diff --git a/fs/xfs/xfs_log.c b/fs/xfs/xfs_log.c index 03a1ab60c480..1857c412d839 100644 --- a/fs/xfs/xfs_log.c +++ b/fs/xfs/xfs_log.c @@ -24,7 +24,6 @@ #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir2.h" #include "xfs_mount.h" #include "xfs_error.h" #include "xfs_log_priv.h" @@ -34,8 +33,6 @@ #include "xfs_ialloc_btree.h" #include "xfs_log_recover.h" #include "xfs_trans_priv.h" -#include "xfs_dir2_sf.h" -#include "xfs_attr_sf.h" #include "xfs_dinode.h" #include "xfs_inode.h" #include "xfs_rw.h" diff --git a/fs/xfs/xfs_log_cil.c b/fs/xfs/xfs_log_cil.c index c7585ab160d5..5eaeede942b5 100644 --- a/fs/xfs/xfs_log_cil.c +++ b/fs/xfs/xfs_log_cil.c @@ -26,7 +26,6 @@ #include "xfs_log_priv.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir2.h" #include "xfs_mount.h" #include "xfs_error.h" #include "xfs_alloc.h" diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c index f91871397db7..0fa18a88febc 100644 --- a/fs/xfs/xfs_log_recover.c +++ b/fs/xfs/xfs_log_recover.c @@ -24,14 +24,11 @@ #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir2.h" #include "xfs_mount.h" #include "xfs_error.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" #include "xfs_ialloc_btree.h" -#include "xfs_dir2_sf.h" -#include "xfs_attr_sf.h" #include "xfs_dinode.h" #include "xfs_inode.h" #include "xfs_inode_item.h" diff --git a/fs/xfs/xfs_mount.c b/fs/xfs/xfs_mount.c index 4bf79511f1c8..aeb9d72ebf6e 100644 --- a/fs/xfs/xfs_mount.c +++ b/fs/xfs/xfs_mount.c @@ -29,8 +29,6 @@ #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" #include "xfs_ialloc_btree.h" -#include "xfs_dir2_sf.h" -#include "xfs_attr_sf.h" #include "xfs_dinode.h" #include "xfs_inode.h" #include "xfs_btree.h" diff --git a/fs/xfs/xfs_rename.c b/fs/xfs/xfs_rename.c index fa752f495a0c..8edb1074847a 100644 --- a/fs/xfs/xfs_rename.c +++ b/fs/xfs/xfs_rename.c @@ -27,8 +27,6 @@ #include "xfs_mount.h" #include "xfs_da_btree.h" #include "xfs_bmap_btree.h" -#include "xfs_dir2_sf.h" -#include "xfs_attr_sf.h" #include "xfs_dinode.h" #include "xfs_inode.h" #include "xfs_inode_item.h" diff --git a/fs/xfs/xfs_rtalloc.c b/fs/xfs/xfs_rtalloc.c index c7b20568ee88..8da5d89dcbcd 100644 --- a/fs/xfs/xfs_rtalloc.c +++ b/fs/xfs/xfs_rtalloc.c @@ -27,14 +27,8 @@ #include "xfs_dir2.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" -#include "xfs_alloc_btree.h" -#include "xfs_ialloc_btree.h" -#include "xfs_dir2_sf.h" -#include "xfs_attr_sf.h" #include "xfs_dinode.h" #include "xfs_inode.h" -#include "xfs_btree.h" -#include "xfs_ialloc.h" #include "xfs_alloc.h" #include "xfs_bmap.h" #include "xfs_rtalloc.h" diff --git a/fs/xfs/xfs_rw.c b/fs/xfs/xfs_rw.c index 1101bc63ced4..56861d5daaef 100644 --- a/fs/xfs/xfs_rw.c +++ b/fs/xfs/xfs_rw.c @@ -24,26 +24,12 @@ #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir2.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" -#include "xfs_alloc_btree.h" -#include "xfs_ialloc_btree.h" -#include "xfs_dir2_sf.h" -#include "xfs_attr_sf.h" #include "xfs_dinode.h" #include "xfs_inode.h" -#include "xfs_inode_item.h" -#include "xfs_itable.h" -#include "xfs_btree.h" -#include "xfs_alloc.h" -#include "xfs_ialloc.h" -#include "xfs_attr.h" -#include "xfs_bmap.h" #include "xfs_error.h" -#include "xfs_buf_item.h" #include "xfs_rw.h" -#include "xfs_trace.h" /* * Force a shutdown of the filesystem instantly while keeping diff --git a/fs/xfs/xfs_trans.c b/fs/xfs/xfs_trans.c index 2fd44d8fe3ae..57c53f7ad2c9 100644 --- a/fs/xfs/xfs_trans.c +++ b/fs/xfs/xfs_trans.c @@ -24,15 +24,12 @@ #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir2.h" #include "xfs_mount.h" #include "xfs_error.h" #include "xfs_da_btree.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" #include "xfs_ialloc_btree.h" -#include "xfs_dir2_sf.h" -#include "xfs_attr_sf.h" #include "xfs_dinode.h" #include "xfs_inode.h" #include "xfs_btree.h" diff --git a/fs/xfs/xfs_trans_buf.c b/fs/xfs/xfs_trans_buf.c index 93ed4a5edde8..d1d08aa404b5 100644 --- a/fs/xfs/xfs_trans_buf.c +++ b/fs/xfs/xfs_trans_buf.c @@ -24,13 +24,10 @@ #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir2.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" #include "xfs_ialloc_btree.h" -#include "xfs_dir2_sf.h" -#include "xfs_attr_sf.h" #include "xfs_dinode.h" #include "xfs_inode.h" #include "xfs_buf_item.h" diff --git a/fs/xfs/xfs_trans_inode.c b/fs/xfs/xfs_trans_inode.c index 4f4df63144d7..04cc08a1b663 100644 --- a/fs/xfs/xfs_trans_inode.c +++ b/fs/xfs/xfs_trans_inode.c @@ -24,17 +24,13 @@ #include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_ag.h" -#include "xfs_dir2.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" #include "xfs_alloc_btree.h" #include "xfs_ialloc_btree.h" -#include "xfs_dir2_sf.h" -#include "xfs_attr_sf.h" #include "xfs_dinode.h" #include "xfs_inode.h" #include "xfs_btree.h" -#include "xfs_ialloc.h" #include "xfs_trans_priv.h" #include "xfs_inode_item.h" diff --git a/fs/xfs/xfs_utils.c b/fs/xfs/xfs_utils.c index 4b1df677abd9..8965887d26b1 100644 --- a/fs/xfs/xfs_utils.c +++ b/fs/xfs/xfs_utils.c @@ -27,15 +27,12 @@ #include "xfs_dir2.h" #include "xfs_mount.h" #include "xfs_bmap_btree.h" -#include "xfs_dir2_sf.h" -#include "xfs_attr_sf.h" #include "xfs_dinode.h" #include "xfs_inode.h" #include "xfs_inode_item.h" #include "xfs_bmap.h" #include "xfs_error.h" #include "xfs_quota.h" -#include "xfs_rw.h" #include "xfs_itable.h" #include "xfs_utils.h" diff --git a/fs/xfs/xfs_vnodeops.c b/fs/xfs/xfs_vnodeops.c index f6fd7502fc0e..161444e768b6 100644 --- a/fs/xfs/xfs_vnodeops.c +++ b/fs/xfs/xfs_vnodeops.c @@ -29,15 +29,11 @@ #include "xfs_mount.h" #include "xfs_da_btree.h" #include "xfs_bmap_btree.h" -#include "xfs_alloc_btree.h" #include "xfs_ialloc_btree.h" -#include "xfs_dir2_sf.h" -#include "xfs_attr_sf.h" #include "xfs_dinode.h" #include "xfs_inode.h" #include "xfs_inode_item.h" #include "xfs_itable.h" -#include "xfs_btree.h" #include "xfs_ialloc.h" #include "xfs_alloc.h" #include "xfs_bmap.h" -- cgit v1.2.3 From e98c414f9a3134fe7efc56ef8f1d394b54bfd40e Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 23 Jun 2010 18:11:15 +1000 Subject: xfs: simplify log item descriptor tracking Currently we track log item descriptor belonging to a transaction using a complex opencoded chunk allocator. This code has been there since day one and seems to work around the lack of an efficient slab allocator. This patch replaces it with dynamically allocated log item descriptors from a dedicated slab pool, linked to the transaction by a linked list. This allows to greatly simplify the log item descriptor tracking to the point where it's just a couple hundred lines in xfs_trans.c instead of a separate file. The external API has also been simplified while we're at it - the xfs_trans_add_item and xfs_trans_del_item functions to add/ delete items from a transaction have been simplified to the bare minium, and the xfs_trans_find_item function is replaced with a direct dereference of the li_desc field. All debug code walking the list of log items in a transaction is down to a simple list_for_each_entry. Note that we could easily use a singly linked list here instead of the double linked list from list.h as the fastpath only does deletion from sequential traversal. But given that we don't have one available as a library function yet I use the list.h functions for simplicity. Signed-off-by: Christoph Hellwig Reviewed-by: Dave Chinner --- fs/xfs/Makefile | 1 - fs/xfs/linux-2.6/xfs_super.c | 11 +- fs/xfs/quota/xfs_trans_dquot.c | 25 +-- fs/xfs/xfs_bmap.c | 45 +---- fs/xfs/xfs_buf_item.c | 5 +- fs/xfs/xfs_extfree_item.c | 8 +- fs/xfs/xfs_trans.c | 200 ++++++++++++------- fs/xfs/xfs_trans.h | 105 +--------- fs/xfs/xfs_trans_buf.c | 64 ++---- fs/xfs/xfs_trans_extfree.c | 22 +-- fs/xfs/xfs_trans_inode.c | 9 +- fs/xfs/xfs_trans_item.c | 440 ----------------------------------------- fs/xfs/xfs_trans_priv.h | 18 +- 13 files changed, 194 insertions(+), 759 deletions(-) delete mode 100644 fs/xfs/xfs_trans_item.c (limited to 'fs') diff --git a/fs/xfs/Makefile b/fs/xfs/Makefile index a5239b1713be..0dce969d6cad 100644 --- a/fs/xfs/Makefile +++ b/fs/xfs/Makefile @@ -87,7 +87,6 @@ xfs-y += xfs_alloc.o \ xfs_trans_buf.o \ xfs_trans_extfree.o \ xfs_trans_inode.o \ - xfs_trans_item.o \ xfs_utils.o \ xfs_vnodeops.o \ xfs_rw.o diff --git a/fs/xfs/linux-2.6/xfs_super.c b/fs/xfs/linux-2.6/xfs_super.c index 5593066d497d..4b90e4b531b7 100644 --- a/fs/xfs/linux-2.6/xfs_super.c +++ b/fs/xfs/linux-2.6/xfs_super.c @@ -1703,6 +1703,12 @@ xfs_init_zones(void) if (!xfs_trans_zone) goto out_destroy_ifork_zone; + xfs_log_item_desc_zone = + kmem_zone_init(sizeof(struct xfs_log_item_desc), + "xfs_log_item_desc"); + if (!xfs_log_item_desc_zone) + goto out_destroy_trans_zone; + /* * The size of the zone allocated buf log item is the maximum * size possible under XFS. This wastes a little bit of memory, @@ -1712,7 +1718,7 @@ xfs_init_zones(void) (((XFS_MAX_BLOCKSIZE / XFS_BLF_CHUNK) / NBWORD) * sizeof(int))), "xfs_buf_item"); if (!xfs_buf_item_zone) - goto out_destroy_trans_zone; + goto out_destroy_log_item_desc_zone; xfs_efd_zone = kmem_zone_init((sizeof(xfs_efd_log_item_t) + ((XFS_EFD_MAX_FAST_EXTENTS - 1) * @@ -1749,6 +1755,8 @@ xfs_init_zones(void) kmem_zone_destroy(xfs_efd_zone); out_destroy_buf_item_zone: kmem_zone_destroy(xfs_buf_item_zone); + out_destroy_log_item_desc_zone: + kmem_zone_destroy(xfs_log_item_desc_zone); out_destroy_trans_zone: kmem_zone_destroy(xfs_trans_zone); out_destroy_ifork_zone: @@ -1779,6 +1787,7 @@ xfs_destroy_zones(void) kmem_zone_destroy(xfs_efi_zone); kmem_zone_destroy(xfs_efd_zone); kmem_zone_destroy(xfs_buf_item_zone); + kmem_zone_destroy(xfs_log_item_desc_zone); kmem_zone_destroy(xfs_trans_zone); kmem_zone_destroy(xfs_ifork_zone); kmem_zone_destroy(xfs_dabuf_zone); diff --git a/fs/xfs/quota/xfs_trans_dquot.c b/fs/xfs/quota/xfs_trans_dquot.c index 08f5604d092f..7de91d1b75c0 100644 --- a/fs/xfs/quota/xfs_trans_dquot.c +++ b/fs/xfs/quota/xfs_trans_dquot.c @@ -49,16 +49,14 @@ xfs_trans_dqjoin( xfs_trans_t *tp, xfs_dquot_t *dqp) { - xfs_dq_logitem_t *lp = &dqp->q_logitem; - ASSERT(dqp->q_transp != tp); ASSERT(XFS_DQ_IS_LOCKED(dqp)); - ASSERT(lp->qli_dquot == dqp); + ASSERT(dqp->q_logitem.qli_dquot == dqp); /* * Get a log_item_desc to point at the new item. */ - (void) xfs_trans_add_item(tp, (xfs_log_item_t*)(lp)); + xfs_trans_add_item(tp, &dqp->q_logitem.qli_item); /* * Initialize i_transp so we can later determine if this dquot is @@ -83,16 +81,11 @@ xfs_trans_log_dquot( xfs_trans_t *tp, xfs_dquot_t *dqp) { - xfs_log_item_desc_t *lidp; - ASSERT(dqp->q_transp == tp); ASSERT(XFS_DQ_IS_LOCKED(dqp)); - lidp = xfs_trans_find_item(tp, (xfs_log_item_t*)(&dqp->q_logitem)); - ASSERT(lidp != NULL); - tp->t_flags |= XFS_TRANS_DIRTY; - lidp->lid_flags |= XFS_LID_DIRTY; + dqp->q_logitem.qli_item.li_desc->lid_flags |= XFS_LID_DIRTY; } /* @@ -864,9 +857,8 @@ xfs_trans_get_qoff_item( /* * Get a log_item_desc to point at the new item. */ - (void) xfs_trans_add_item(tp, (xfs_log_item_t*)q); - - return (q); + xfs_trans_add_item(tp, &q->qql_item); + return q; } @@ -880,13 +872,8 @@ xfs_trans_log_quotaoff_item( xfs_trans_t *tp, xfs_qoff_logitem_t *qlp) { - xfs_log_item_desc_t *lidp; - - lidp = xfs_trans_find_item(tp, (xfs_log_item_t *)qlp); - ASSERT(lidp != NULL); - tp->t_flags |= XFS_TRANS_DIRTY; - lidp->lid_flags |= XFS_LID_DIRTY; + qlp->qql_item.li_desc->lid_flags |= XFS_LID_DIRTY; } STATIC void diff --git a/fs/xfs/xfs_bmap.c b/fs/xfs/xfs_bmap.c index ed4e3ae2c1d0..ff8675b41973 100644 --- a/fs/xfs/xfs_bmap.c +++ b/fs/xfs/xfs_bmap.c @@ -5857,43 +5857,18 @@ xfs_bmap_get_bp( bp = NULL; if (!bp) { /* Chase down all the log items to see if the bp is there */ - xfs_log_item_chunk_t *licp; - xfs_trans_t *tp; - - tp = cur->bc_tp; - licp = &tp->t_items; - while (!bp && licp != NULL) { - if (xfs_lic_are_all_free(licp)) { - licp = licp->lic_next; - continue; - } - for (i = 0; i < licp->lic_unused; i++) { - xfs_log_item_desc_t *lidp; - xfs_log_item_t *lip; - xfs_buf_log_item_t *bip; - xfs_buf_t *lbp; - - if (xfs_lic_isfree(licp, i)) { - continue; - } - - lidp = xfs_lic_slot(licp, i); - lip = lidp->lid_item; - if (lip->li_type != XFS_LI_BUF) - continue; - - bip = (xfs_buf_log_item_t *)lip; - lbp = bip->bli_buf; - - if (XFS_BUF_ADDR(lbp) == bno) { - bp = lbp; - break; /* Found it */ - } - } - licp = licp->lic_next; + struct xfs_log_item_desc *lidp; + struct xfs_buf_log_item *bip; + + list_for_each_entry(lidp, &cur->bc_tp->t_items, lid_trans) { + bip = (struct xfs_buf_log_item *)lidp->lid_item; + if (bip->bli_item.li_type == XFS_LI_BUF && + XFS_BUF_ADDR(bip->bli_buf) == bno) + return bip->bli_buf; } } - return(bp); + + return bp; } STATIC void diff --git a/fs/xfs/xfs_buf_item.c b/fs/xfs/xfs_buf_item.c index 91ad92e83bc6..711f69abbbe4 100644 --- a/fs/xfs/xfs_buf_item.c +++ b/fs/xfs/xfs_buf_item.c @@ -460,13 +460,10 @@ xfs_buf_item_unpin_remove( * occurs later in the xfs_trans_uncommit() will try to * reference the buffer which we no longer have a hold on. */ - struct xfs_log_item_desc *lidp; - ASSERT(XFS_BUF_VALUSEMA(bip->bli_buf) <= 0); trace_xfs_buf_item_unpin_stale(bip); - lidp = xfs_trans_find_item(tp, (xfs_log_item_t *)bip); - xfs_trans_free_item(tp, lidp); + xfs_trans_del_item(&bip->bli_item); /* * Since the transaction no longer refers to the buffer, the diff --git a/fs/xfs/xfs_extfree_item.c b/fs/xfs/xfs_extfree_item.c index 1023b1fadfe8..8d0e543ca3c0 100644 --- a/fs/xfs/xfs_extfree_item.c +++ b/fs/xfs/xfs_extfree_item.c @@ -131,18 +131,18 @@ STATIC void xfs_efi_item_unpin_remove(xfs_efi_log_item_t *efip, xfs_trans_t *tp) { struct xfs_ail *ailp = efip->efi_item.li_ailp; - xfs_log_item_desc_t *lidp; spin_lock(&ailp->xa_lock); if (efip->efi_flags & XFS_EFI_CANCELED) { + struct xfs_log_item *lip = &efip->efi_item; + /* * free the xaction descriptor pointing to this item */ - lidp = xfs_trans_find_item(tp, (xfs_log_item_t *) efip); - xfs_trans_free_item(tp, lidp); + xfs_trans_del_item(lip); /* xfs_trans_ail_delete() drops the AIL lock. */ - xfs_trans_ail_delete(ailp, (xfs_log_item_t *)efip); + xfs_trans_ail_delete(ailp, lip); xfs_efi_item_free(efip); } else { efip->efi_flags |= XFS_EFI_COMMITTED; diff --git a/fs/xfs/xfs_trans.c b/fs/xfs/xfs_trans.c index 57c53f7ad2c9..9c41efccf728 100644 --- a/fs/xfs/xfs_trans.c +++ b/fs/xfs/xfs_trans.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2000-2003,2005 Silicon Graphics, Inc. + * Copyright (C) 2010 Red Hat, Inc. * All Rights Reserved. * * This program is free software; you can redistribute it and/or @@ -43,6 +44,7 @@ #include "xfs_trace.h" kmem_zone_t *xfs_trans_zone; +kmem_zone_t *xfs_log_item_desc_zone; /* @@ -593,8 +595,7 @@ _xfs_trans_alloc( tp->t_magic = XFS_TRANS_MAGIC; tp->t_type = type; tp->t_mountp = mp; - tp->t_items_free = XFS_LIC_NUM_SLOTS; - xfs_lic_init(&(tp->t_items)); + INIT_LIST_HEAD(&tp->t_items); INIT_LIST_HEAD(&tp->t_busy); return tp; } @@ -639,8 +640,7 @@ xfs_trans_dup( ntp->t_magic = XFS_TRANS_MAGIC; ntp->t_type = tp->t_type; ntp->t_mountp = tp->t_mountp; - ntp->t_items_free = XFS_LIC_NUM_SLOTS; - xfs_lic_init(&(ntp->t_items)); + INIT_LIST_HEAD(&ntp->t_items); INIT_LIST_HEAD(&ntp->t_busy); ASSERT(tp->t_flags & XFS_TRANS_PERM_LOG_RES); @@ -1119,6 +1119,108 @@ xfs_trans_unreserve_and_mod_sb( } } +/* + * Add the given log item to the transaction's list of log items. + * + * The log item will now point to its new descriptor with its li_desc field. + */ +void +xfs_trans_add_item( + struct xfs_trans *tp, + struct xfs_log_item *lip) +{ + struct xfs_log_item_desc *lidp; + + ASSERT(lip->li_mountp = tp->t_mountp); + ASSERT(lip->li_ailp = tp->t_mountp->m_ail); + + lidp = kmem_zone_zalloc(xfs_log_item_desc_zone, KM_SLEEP); + + lidp->lid_item = lip; + lidp->lid_flags = 0; + lidp->lid_size = 0; + list_add_tail(&lidp->lid_trans, &tp->t_items); + + lip->li_desc = lidp; +} + +STATIC void +xfs_trans_free_item_desc( + struct xfs_log_item_desc *lidp) +{ + list_del_init(&lidp->lid_trans); + kmem_zone_free(xfs_log_item_desc_zone, lidp); +} + +/* + * Unlink and free the given descriptor. + */ +void +xfs_trans_del_item( + struct xfs_log_item *lip) +{ + xfs_trans_free_item_desc(lip->li_desc); + lip->li_desc = NULL; +} + +/* + * Unlock all of the items of a transaction and free all the descriptors + * of that transaction. + */ +STATIC void +xfs_trans_free_items( + struct xfs_trans *tp, + xfs_lsn_t commit_lsn, + int flags) +{ + struct xfs_log_item_desc *lidp, *next; + + list_for_each_entry_safe(lidp, next, &tp->t_items, lid_trans) { + struct xfs_log_item *lip = lidp->lid_item; + + lip->li_desc = NULL; + + if (commit_lsn != NULLCOMMITLSN) + IOP_COMMITTING(lip, commit_lsn); + if (flags & XFS_TRANS_ABORT) + lip->li_flags |= XFS_LI_ABORTED; + IOP_UNLOCK(lip); + + xfs_trans_free_item_desc(lidp); + } +} + +/* + * Unlock the items associated with a transaction. + * + * Items which were not logged should be freed. Those which were logged must + * still be tracked so they can be unpinned when the transaction commits. + */ +STATIC void +xfs_trans_unlock_items( + struct xfs_trans *tp, + xfs_lsn_t commit_lsn) +{ + struct xfs_log_item_desc *lidp, *next; + + list_for_each_entry_safe(lidp, next, &tp->t_items, lid_trans) { + struct xfs_log_item *lip = lidp->lid_item; + + lip->li_desc = NULL; + + if (commit_lsn != NULLCOMMITLSN) + IOP_COMMITTING(lip, commit_lsn); + IOP_UNLOCK(lip); + + /* + * Free the descriptor if the item is not dirty + * within this transaction. + */ + if (!(lidp->lid_flags & XFS_LID_DIRTY)) + xfs_trans_free_item_desc(lidp); + } +} + /* * Total up the number of log iovecs needed to commit this * transaction. The transaction itself needs one for the @@ -1130,30 +1232,27 @@ xfs_trans_count_vecs( struct xfs_trans *tp) { int nvecs; - xfs_log_item_desc_t *lidp; + struct xfs_log_item_desc *lidp; nvecs = 1; - lidp = xfs_trans_first_item(tp); - ASSERT(lidp != NULL); /* In the non-debug case we need to start bailing out if we * didn't find a log_item here, return zero and let trans_commit * deal with it. */ - if (lidp == NULL) + if (list_empty(&tp->t_items)) { + ASSERT(0); return 0; + } - while (lidp != NULL) { + list_for_each_entry(lidp, &tp->t_items, lid_trans) { /* * Skip items which aren't dirty in this transaction. */ - if (!(lidp->lid_flags & XFS_LID_DIRTY)) { - lidp = xfs_trans_next_item(tp, lidp); + if (!(lidp->lid_flags & XFS_LID_DIRTY)) continue; - } lidp->lid_size = IOP_SIZE(lidp->lid_item); nvecs += lidp->lid_size; - lidp = xfs_trans_next_item(tp, lidp); } return nvecs; @@ -1173,7 +1272,7 @@ xfs_trans_fill_vecs( struct xfs_trans *tp, struct xfs_log_iovec *log_vector) { - xfs_log_item_desc_t *lidp; + struct xfs_log_item_desc *lidp; struct xfs_log_iovec *vecp; uint nitems; @@ -1184,14 +1283,11 @@ xfs_trans_fill_vecs( vecp = log_vector + 1; nitems = 0; - lidp = xfs_trans_first_item(tp); - ASSERT(lidp); - while (lidp) { + ASSERT(!list_empty(&tp->t_items)); + list_for_each_entry(lidp, &tp->t_items, lid_trans) { /* Skip items which aren't dirty in this transaction. */ - if (!(lidp->lid_flags & XFS_LID_DIRTY)) { - lidp = xfs_trans_next_item(tp, lidp); + if (!(lidp->lid_flags & XFS_LID_DIRTY)) continue; - } /* * The item may be marked dirty but not log anything. This can @@ -1202,7 +1298,6 @@ xfs_trans_fill_vecs( IOP_FORMAT(lidp->lid_item, vecp); vecp += lidp->lid_size; IOP_PIN(lidp->lid_item); - lidp = xfs_trans_next_item(tp, lidp); } /* @@ -1297,24 +1392,15 @@ xfs_trans_committed( struct xfs_trans *tp, int abortflag) { - xfs_log_item_desc_t *lidp; - xfs_log_item_chunk_t *licp; - xfs_log_item_chunk_t *next_licp; + struct xfs_log_item_desc *lidp, *next; /* Call the transaction's completion callback if there is one. */ if (tp->t_callback != NULL) tp->t_callback(tp, tp->t_callarg); - for (lidp = xfs_trans_first_item(tp); - lidp != NULL; - lidp = xfs_trans_next_item(tp, lidp)) { + list_for_each_entry_safe(lidp, next, &tp->t_items, lid_trans) { xfs_trans_item_committed(lidp->lid_item, tp->t_lsn, abortflag); - } - - /* free the item chunks, ignoring the embedded chunk */ - for (licp = tp->t_items.lic_next; licp != NULL; licp = next_licp) { - next_licp = licp->lic_next; - kmem_free(licp); + xfs_trans_free_item_desc(lidp); } xfs_trans_free(tp); @@ -1329,11 +1415,9 @@ xfs_trans_uncommit( struct xfs_trans *tp, uint flags) { - xfs_log_item_desc_t *lidp; + struct xfs_log_item_desc *lidp; - for (lidp = xfs_trans_first_item(tp); - lidp != NULL; - lidp = xfs_trans_next_item(tp, lidp)) { + list_for_each_entry(lidp, &tp->t_items, lid_trans) { /* * Unpin all but those that aren't dirty. */ @@ -1504,33 +1588,28 @@ STATIC struct xfs_log_vec * xfs_trans_alloc_log_vecs( xfs_trans_t *tp) { - xfs_log_item_desc_t *lidp; + struct xfs_log_item_desc *lidp; struct xfs_log_vec *lv = NULL; struct xfs_log_vec *ret_lv = NULL; - lidp = xfs_trans_first_item(tp); /* Bail out if we didn't find a log item. */ - if (!lidp) { + if (list_empty(&tp->t_items)) { ASSERT(0); return NULL; } - while (lidp != NULL) { + list_for_each_entry(lidp, &tp->t_items, lid_trans) { struct xfs_log_vec *new_lv; /* Skip items which aren't dirty in this transaction. */ - if (!(lidp->lid_flags & XFS_LID_DIRTY)) { - lidp = xfs_trans_next_item(tp, lidp); + if (!(lidp->lid_flags & XFS_LID_DIRTY)) continue; - } /* Skip items that do not have any vectors for writing */ lidp->lid_size = IOP_SIZE(lidp->lid_item); - if (!lidp->lid_size) { - lidp = xfs_trans_next_item(tp, lidp); + if (!lidp->lid_size) continue; - } new_lv = kmem_zalloc(sizeof(*new_lv) + lidp->lid_size * sizeof(struct xfs_log_iovec), @@ -1545,7 +1624,6 @@ xfs_trans_alloc_log_vecs( else lv->lv_next = new_lv; lv = new_lv; - lidp = xfs_trans_next_item(tp, lidp); } return ret_lv; @@ -1704,12 +1782,6 @@ xfs_trans_cancel( int flags) { int log_flags; -#ifdef DEBUG - xfs_log_item_chunk_t *licp; - xfs_log_item_desc_t *lidp; - xfs_log_item_t *lip; - int i; -#endif xfs_mount_t *mp = tp->t_mountp; /* @@ -1728,21 +1800,11 @@ xfs_trans_cancel( xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE); } #ifdef DEBUG - if (!(flags & XFS_TRANS_ABORT)) { - licp = &(tp->t_items); - while (licp != NULL) { - lidp = licp->lic_descs; - for (i = 0; i < licp->lic_unused; i++, lidp++) { - if (xfs_lic_isfree(licp, i)) { - continue; - } - - lip = lidp->lid_item; - if (!XFS_FORCED_SHUTDOWN(mp)) - ASSERT(!(lip->li_type == XFS_LI_EFD)); - } - licp = licp->lic_next; - } + if (!(flags & XFS_TRANS_ABORT) && !XFS_FORCED_SHUTDOWN(mp)) { + struct xfs_log_item_desc *lidp; + + list_for_each_entry(lidp, &tp->t_items, lid_trans) + ASSERT(!(lidp->lid_item->li_type == XFS_LI_EFD)); } #endif xfs_trans_unreserve_and_mod_sb(tp); diff --git a/fs/xfs/xfs_trans.h b/fs/xfs/xfs_trans.h index e639e8e9a2a9..0c903eb8bbe1 100644 --- a/fs/xfs/xfs_trans.h +++ b/fs/xfs/xfs_trans.h @@ -161,105 +161,14 @@ typedef struct xfs_trans_header { * the amount of space needed to log the item it describes * once we get to commit processing (see xfs_trans_commit()). */ -typedef struct xfs_log_item_desc { +struct xfs_log_item_desc { struct xfs_log_item *lid_item; - ushort lid_size; - unsigned char lid_flags; - unsigned char lid_index; -} xfs_log_item_desc_t; + ushort lid_size; + unsigned char lid_flags; + struct list_head lid_trans; +}; #define XFS_LID_DIRTY 0x1 -#define XFS_LID_PINNED 0x2 - -/* - * This structure is used to maintain a chunk list of log_item_desc - * structures. The free field is a bitmask indicating which descriptors - * in this chunk's array are free. The unused field is the first value - * not used since this chunk was allocated. - */ -#define XFS_LIC_NUM_SLOTS 15 -typedef struct xfs_log_item_chunk { - struct xfs_log_item_chunk *lic_next; - ushort lic_free; - ushort lic_unused; - xfs_log_item_desc_t lic_descs[XFS_LIC_NUM_SLOTS]; -} xfs_log_item_chunk_t; - -#define XFS_LIC_MAX_SLOT (XFS_LIC_NUM_SLOTS - 1) -#define XFS_LIC_FREEMASK ((1 << XFS_LIC_NUM_SLOTS) - 1) - - -/* - * Initialize the given chunk. Set the chunk's free descriptor mask - * to indicate that all descriptors are free. The caller gets to set - * lic_unused to the right value (0 matches all free). The - * lic_descs.lid_index values are set up as each desc is allocated. - */ -static inline void xfs_lic_init(xfs_log_item_chunk_t *cp) -{ - cp->lic_free = XFS_LIC_FREEMASK; -} - -static inline void xfs_lic_init_slot(xfs_log_item_chunk_t *cp, int slot) -{ - cp->lic_descs[slot].lid_index = (unsigned char)(slot); -} - -static inline int xfs_lic_vacancy(xfs_log_item_chunk_t *cp) -{ - return cp->lic_free & XFS_LIC_FREEMASK; -} - -static inline void xfs_lic_all_free(xfs_log_item_chunk_t *cp) -{ - cp->lic_free = XFS_LIC_FREEMASK; -} - -static inline int xfs_lic_are_all_free(xfs_log_item_chunk_t *cp) -{ - return ((cp->lic_free & XFS_LIC_FREEMASK) == XFS_LIC_FREEMASK); -} - -static inline int xfs_lic_isfree(xfs_log_item_chunk_t *cp, int slot) -{ - return (cp->lic_free & (1 << slot)); -} - -static inline void xfs_lic_claim(xfs_log_item_chunk_t *cp, int slot) -{ - cp->lic_free &= ~(1 << slot); -} - -static inline void xfs_lic_relse(xfs_log_item_chunk_t *cp, int slot) -{ - cp->lic_free |= 1 << slot; -} - -static inline xfs_log_item_desc_t * -xfs_lic_slot(xfs_log_item_chunk_t *cp, int slot) -{ - return &(cp->lic_descs[slot]); -} - -static inline int xfs_lic_desc_to_slot(xfs_log_item_desc_t *dp) -{ - return (uint)dp->lid_index; -} - -/* - * Calculate the address of a chunk given a descriptor pointer: - * dp - dp->lid_index give the address of the start of the lic_descs array. - * From this we subtract the offset of the lic_descs field in a chunk. - * All of this yields the address of the chunk, which is - * cast to a chunk pointer. - */ -static inline xfs_log_item_chunk_t * -xfs_lic_desc_to_chunk(xfs_log_item_desc_t *dp) -{ - return (xfs_log_item_chunk_t*) \ - (((xfs_caddr_t)((dp) - (dp)->lid_index)) - \ - (xfs_caddr_t)(((xfs_log_item_chunk_t*)0)->lic_descs)); -} #define XFS_TRANS_MAGIC 0x5452414E /* 'TRAN' */ /* @@ -516,8 +425,7 @@ typedef struct xfs_trans { int64_t t_rblocks_delta;/* superblock rblocks change */ int64_t t_rextents_delta;/* superblocks rextents chg */ int64_t t_rextslog_delta;/* superblocks rextslog chg */ - unsigned int t_items_free; /* log item descs free */ - xfs_log_item_chunk_t t_items; /* first log item desc chunk */ + struct list_head t_items; /* log item descriptors */ xfs_trans_header_t t_header; /* header for in-log trans */ struct list_head t_busy; /* list of busy extents */ unsigned long t_pflags; /* saved process flags state */ @@ -595,6 +503,7 @@ int xfs_trans_ail_init(struct xfs_mount *); void xfs_trans_ail_destroy(struct xfs_mount *); extern kmem_zone_t *xfs_trans_zone; +extern kmem_zone_t *xfs_log_item_desc_zone; #endif /* __KERNEL__ */ diff --git a/fs/xfs/xfs_trans_buf.c b/fs/xfs/xfs_trans_buf.c index d1d08aa404b5..74a1c33e4098 100644 --- a/fs/xfs/xfs_trans_buf.c +++ b/fs/xfs/xfs_trans_buf.c @@ -47,36 +47,17 @@ xfs_trans_buf_item_match( xfs_daddr_t blkno, int len) { - xfs_log_item_chunk_t *licp; - xfs_log_item_desc_t *lidp; - xfs_buf_log_item_t *blip; - int i; + struct xfs_log_item_desc *lidp; + struct xfs_buf_log_item *blip; len = BBTOB(len); - for (licp = &tp->t_items; licp != NULL; licp = licp->lic_next) { - if (xfs_lic_are_all_free(licp)) { - ASSERT(licp == &tp->t_items); - ASSERT(licp->lic_next == NULL); - return NULL; - } - - for (i = 0; i < licp->lic_unused; i++) { - /* - * Skip unoccupied slots. - */ - if (xfs_lic_isfree(licp, i)) - continue; - - lidp = xfs_lic_slot(licp, i); - blip = (xfs_buf_log_item_t *)lidp->lid_item; - if (blip->bli_item.li_type != XFS_LI_BUF) - continue; - - if (XFS_BUF_TARGET(blip->bli_buf) == target && - XFS_BUF_ADDR(blip->bli_buf) == blkno && - XFS_BUF_COUNT(blip->bli_buf) == len) - return blip->bli_buf; - } + list_for_each_entry(lidp, &tp->t_items, lid_trans) { + blip = (struct xfs_buf_log_item *)lidp->lid_item; + if (blip->bli_item.li_type == XFS_LI_BUF && + XFS_BUF_TARGET(blip->bli_buf) == target && + XFS_BUF_ADDR(blip->bli_buf) == blkno && + XFS_BUF_COUNT(blip->bli_buf) == len) + return blip->bli_buf; } return NULL; @@ -123,7 +104,7 @@ _xfs_trans_bjoin( /* * Get a log_item_desc to point at the new item. */ - (void) xfs_trans_add_item(tp, (xfs_log_item_t *)bip); + xfs_trans_add_item(tp, &bip->bli_item); /* * Initialize b_fsprivate2 so we can find it with incore_match() @@ -479,7 +460,6 @@ xfs_trans_brelse(xfs_trans_t *tp, { xfs_buf_log_item_t *bip; xfs_log_item_t *lip; - xfs_log_item_desc_t *lidp; /* * Default to a normal brelse() call if the tp is NULL. @@ -510,13 +490,6 @@ xfs_trans_brelse(xfs_trans_t *tp, ASSERT(!(bip->bli_format.blf_flags & XFS_BLF_CANCEL)); ASSERT(atomic_read(&bip->bli_refcount) > 0); - /* - * Find the item descriptor pointing to this buffer's - * log item. It must be there. - */ - lidp = xfs_trans_find_item(tp, (xfs_log_item_t*)bip); - ASSERT(lidp != NULL); - trace_xfs_trans_brelse(bip); /* @@ -532,7 +505,7 @@ xfs_trans_brelse(xfs_trans_t *tp, * If the buffer is dirty within this transaction, we can't * release it until we commit. */ - if (lidp->lid_flags & XFS_LID_DIRTY) + if (bip->bli_item.li_desc->lid_flags & XFS_LID_DIRTY) return; /* @@ -549,7 +522,7 @@ xfs_trans_brelse(xfs_trans_t *tp, /* * Free up the log item descriptor tracking the released item. */ - xfs_trans_free_item(tp, lidp); + xfs_trans_del_item(&bip->bli_item); /* * Clear the hold flag in the buf log item if it is set. @@ -661,7 +634,6 @@ xfs_trans_log_buf(xfs_trans_t *tp, uint last) { xfs_buf_log_item_t *bip; - xfs_log_item_desc_t *lidp; ASSERT(XFS_BUF_ISBUSY(bp)); ASSERT(XFS_BUF_FSPRIVATE2(bp, xfs_trans_t *) == tp); @@ -703,11 +675,8 @@ xfs_trans_log_buf(xfs_trans_t *tp, bip->bli_format.blf_flags &= ~XFS_BLF_CANCEL; } - lidp = xfs_trans_find_item(tp, (xfs_log_item_t*)bip); - ASSERT(lidp != NULL); - tp->t_flags |= XFS_TRANS_DIRTY; - lidp->lid_flags |= XFS_LID_DIRTY; + bip->bli_item.li_desc->lid_flags |= XFS_LID_DIRTY; bip->bli_flags |= XFS_BLI_LOGGED; xfs_buf_item_log(bip, first, last); } @@ -736,7 +705,6 @@ xfs_trans_binval( xfs_trans_t *tp, xfs_buf_t *bp) { - xfs_log_item_desc_t *lidp; xfs_buf_log_item_t *bip; ASSERT(XFS_BUF_ISBUSY(bp)); @@ -744,8 +712,6 @@ xfs_trans_binval( ASSERT(XFS_BUF_FSPRIVATE(bp, void *) != NULL); bip = XFS_BUF_FSPRIVATE(bp, xfs_buf_log_item_t *); - lidp = xfs_trans_find_item(tp, (xfs_log_item_t*)bip); - ASSERT(lidp != NULL); ASSERT(atomic_read(&bip->bli_refcount) > 0); trace_xfs_trans_binval(bip); @@ -760,7 +726,7 @@ xfs_trans_binval( ASSERT(!(bip->bli_flags & (XFS_BLI_LOGGED | XFS_BLI_DIRTY))); ASSERT(!(bip->bli_format.blf_flags & XFS_BLF_INODE_BUF)); ASSERT(bip->bli_format.blf_flags & XFS_BLF_CANCEL); - ASSERT(lidp->lid_flags & XFS_LID_DIRTY); + ASSERT(bip->bli_item.li_desc->lid_flags & XFS_LID_DIRTY); ASSERT(tp->t_flags & XFS_TRANS_DIRTY); return; } @@ -793,7 +759,7 @@ xfs_trans_binval( bip->bli_format.blf_flags |= XFS_BLF_CANCEL; memset((char *)(bip->bli_format.blf_data_map), 0, (bip->bli_format.blf_map_size * sizeof(uint))); - lidp->lid_flags |= XFS_LID_DIRTY; + bip->bli_item.li_desc->lid_flags |= XFS_LID_DIRTY; tp->t_flags |= XFS_TRANS_DIRTY; } diff --git a/fs/xfs/xfs_trans_extfree.c b/fs/xfs/xfs_trans_extfree.c index dfb6a0fdcf9c..f783d5e9fa70 100644 --- a/fs/xfs/xfs_trans_extfree.c +++ b/fs/xfs/xfs_trans_extfree.c @@ -48,9 +48,8 @@ xfs_trans_get_efi(xfs_trans_t *tp, /* * Get a log_item_desc to point at the new item. */ - (void) xfs_trans_add_item(tp, (xfs_log_item_t*)efip); - - return (efip); + xfs_trans_add_item(tp, &efip->efi_item); + return efip; } /* @@ -64,15 +63,11 @@ xfs_trans_log_efi_extent(xfs_trans_t *tp, xfs_fsblock_t start_block, xfs_extlen_t ext_len) { - xfs_log_item_desc_t *lidp; uint next_extent; xfs_extent_t *extp; - lidp = xfs_trans_find_item(tp, (xfs_log_item_t*)efip); - ASSERT(lidp != NULL); - tp->t_flags |= XFS_TRANS_DIRTY; - lidp->lid_flags |= XFS_LID_DIRTY; + efip->efi_item.li_desc->lid_flags |= XFS_LID_DIRTY; next_extent = efip->efi_next_extent; ASSERT(next_extent < efip->efi_format.efi_nextents); @@ -105,9 +100,8 @@ xfs_trans_get_efd(xfs_trans_t *tp, /* * Get a log_item_desc to point at the new item. */ - (void) xfs_trans_add_item(tp, (xfs_log_item_t*)efdp); - - return (efdp); + xfs_trans_add_item(tp, &efdp->efd_item); + return efdp; } /* @@ -121,15 +115,11 @@ xfs_trans_log_efd_extent(xfs_trans_t *tp, xfs_fsblock_t start_block, xfs_extlen_t ext_len) { - xfs_log_item_desc_t *lidp; uint next_extent; xfs_extent_t *extp; - lidp = xfs_trans_find_item(tp, (xfs_log_item_t*)efdp); - ASSERT(lidp != NULL); - tp->t_flags |= XFS_TRANS_DIRTY; - lidp->lid_flags |= XFS_LID_DIRTY; + efdp->efd_item.li_desc->lid_flags |= XFS_LID_DIRTY; next_extent = efdp->efd_next_extent; ASSERT(next_extent < efdp->efd_format.efd_nextents); diff --git a/fs/xfs/xfs_trans_inode.c b/fs/xfs/xfs_trans_inode.c index 04cc08a1b663..865eeb63ce16 100644 --- a/fs/xfs/xfs_trans_inode.c +++ b/fs/xfs/xfs_trans_inode.c @@ -88,7 +88,7 @@ xfs_trans_ijoin( /* * Get a log_item_desc to point at the new item. */ - (void) xfs_trans_add_item(tp, (xfs_log_item_t*)(iip)); + xfs_trans_add_item(tp, &iip->ili_item); xfs_trans_inode_broot_debug(ip); @@ -144,17 +144,12 @@ xfs_trans_log_inode( xfs_inode_t *ip, uint flags) { - xfs_log_item_desc_t *lidp; - ASSERT(ip->i_transp == tp); ASSERT(ip->i_itemp != NULL); ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL)); - lidp = xfs_trans_find_item(tp, (xfs_log_item_t*)(ip->i_itemp)); - ASSERT(lidp != NULL); - tp->t_flags |= XFS_TRANS_DIRTY; - lidp->lid_flags |= XFS_LID_DIRTY; + ip->i_itemp->ili_item.li_desc->lid_flags |= XFS_LID_DIRTY; /* * Always OR in the bits from the ili_last_fields field. diff --git a/fs/xfs/xfs_trans_item.c b/fs/xfs/xfs_trans_item.c deleted file mode 100644 index 55035a7401cb..000000000000 --- a/fs/xfs/xfs_trans_item.c +++ /dev/null @@ -1,440 +0,0 @@ -/* - * Copyright (c) 2000-2002,2005 Silicon Graphics, Inc. - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it would be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ -#include "xfs.h" -#include "xfs_fs.h" -#include "xfs_types.h" -#include "xfs_log.h" -#include "xfs_inum.h" -#include "xfs_trans.h" -#include "xfs_trans_priv.h" -/* XXX: from here down needed until struct xfs_trans has its own ailp */ -#include "xfs_bit.h" -#include "xfs_buf_item.h" -#include "xfs_sb.h" -#include "xfs_ag.h" -#include "xfs_dir2.h" -#include "xfs_mount.h" - -STATIC int xfs_trans_unlock_chunk(xfs_log_item_chunk_t *, - int, int, xfs_lsn_t); - -/* - * This is called to add the given log item to the transaction's - * list of log items. It must find a free log item descriptor - * or allocate a new one and add the item to that descriptor. - * The function returns a pointer to item descriptor used to point - * to the new item. The log item will now point to its new descriptor - * with its li_desc field. - */ -xfs_log_item_desc_t * -xfs_trans_add_item(xfs_trans_t *tp, xfs_log_item_t *lip) -{ - xfs_log_item_desc_t *lidp; - xfs_log_item_chunk_t *licp; - int i=0; - - /* - * If there are no free descriptors, allocate a new chunk - * of them and put it at the front of the chunk list. - */ - if (tp->t_items_free == 0) { - licp = (xfs_log_item_chunk_t*) - kmem_alloc(sizeof(xfs_log_item_chunk_t), KM_SLEEP); - ASSERT(licp != NULL); - /* - * Initialize the chunk, and then - * claim the first slot in the newly allocated chunk. - */ - xfs_lic_init(licp); - xfs_lic_claim(licp, 0); - licp->lic_unused = 1; - xfs_lic_init_slot(licp, 0); - lidp = xfs_lic_slot(licp, 0); - - /* - * Link in the new chunk and update the free count. - */ - licp->lic_next = tp->t_items.lic_next; - tp->t_items.lic_next = licp; - tp->t_items_free = XFS_LIC_NUM_SLOTS - 1; - - /* - * Initialize the descriptor and the generic portion - * of the log item. - * - * Point the new slot at this item and return it. - * Also point the log item at its currently active - * descriptor and set the item's mount pointer. - */ - lidp->lid_item = lip; - lidp->lid_flags = 0; - lidp->lid_size = 0; - lip->li_desc = lidp; - lip->li_mountp = tp->t_mountp; - lip->li_ailp = tp->t_mountp->m_ail; - return lidp; - } - - /* - * Find the free descriptor. It is somewhere in the chunklist - * of descriptors. - */ - licp = &tp->t_items; - while (licp != NULL) { - if (xfs_lic_vacancy(licp)) { - if (licp->lic_unused <= XFS_LIC_MAX_SLOT) { - i = licp->lic_unused; - ASSERT(xfs_lic_isfree(licp, i)); - break; - } - for (i = 0; i <= XFS_LIC_MAX_SLOT; i++) { - if (xfs_lic_isfree(licp, i)) - break; - } - ASSERT(i <= XFS_LIC_MAX_SLOT); - break; - } - licp = licp->lic_next; - } - ASSERT(licp != NULL); - /* - * If we find a free descriptor, claim it, - * initialize it, and return it. - */ - xfs_lic_claim(licp, i); - if (licp->lic_unused <= i) { - licp->lic_unused = i + 1; - xfs_lic_init_slot(licp, i); - } - lidp = xfs_lic_slot(licp, i); - tp->t_items_free--; - lidp->lid_item = lip; - lidp->lid_flags = 0; - lidp->lid_size = 0; - lip->li_desc = lidp; - lip->li_mountp = tp->t_mountp; - lip->li_ailp = tp->t_mountp->m_ail; - return lidp; -} - -/* - * Free the given descriptor. - * - * This requires setting the bit in the chunk's free mask corresponding - * to the given slot. - */ -void -xfs_trans_free_item(xfs_trans_t *tp, xfs_log_item_desc_t *lidp) -{ - uint slot; - xfs_log_item_chunk_t *licp; - xfs_log_item_chunk_t **licpp; - - slot = xfs_lic_desc_to_slot(lidp); - licp = xfs_lic_desc_to_chunk(lidp); - xfs_lic_relse(licp, slot); - lidp->lid_item->li_desc = NULL; - tp->t_items_free++; - - /* - * If there are no more used items in the chunk and this is not - * the chunk embedded in the transaction structure, then free - * the chunk. First pull it from the chunk list and then - * free it back to the heap. We didn't bother with a doubly - * linked list here because the lists should be very short - * and this is not a performance path. It's better to save - * the memory of the extra pointer. - * - * Also decrement the transaction structure's count of free items - * by the number in a chunk since we are freeing an empty chunk. - */ - if (xfs_lic_are_all_free(licp) && (licp != &(tp->t_items))) { - licpp = &(tp->t_items.lic_next); - while (*licpp != licp) { - ASSERT(*licpp != NULL); - licpp = &((*licpp)->lic_next); - } - *licpp = licp->lic_next; - kmem_free(licp); - tp->t_items_free -= XFS_LIC_NUM_SLOTS; - } -} - -/* - * This is called to find the descriptor corresponding to the given - * log item. It returns a pointer to the descriptor. - * The log item MUST have a corresponding descriptor in the given - * transaction. This routine does not return NULL, it panics. - * - * The descriptor pointer is kept in the log item's li_desc field. - * Just return it. - */ -/*ARGSUSED*/ -xfs_log_item_desc_t * -xfs_trans_find_item(xfs_trans_t *tp, xfs_log_item_t *lip) -{ - ASSERT(lip->li_desc != NULL); - - return lip->li_desc; -} - - -/* - * Return a pointer to the first descriptor in the chunk list. - * This does not return NULL if there are none, it panics. - * - * The first descriptor must be in either the first or second chunk. - * This is because the only chunk allowed to be empty is the first. - * All others are freed when they become empty. - * - * At some point this and xfs_trans_next_item() should be optimized - * to quickly look at the mask to determine if there is anything to - * look at. - */ -xfs_log_item_desc_t * -xfs_trans_first_item(xfs_trans_t *tp) -{ - xfs_log_item_chunk_t *licp; - int i; - - licp = &tp->t_items; - /* - * If it's not in the first chunk, skip to the second. - */ - if (xfs_lic_are_all_free(licp)) { - licp = licp->lic_next; - } - - /* - * Return the first non-free descriptor in the chunk. - */ - ASSERT(!xfs_lic_are_all_free(licp)); - for (i = 0; i < licp->lic_unused; i++) { - if (xfs_lic_isfree(licp, i)) { - continue; - } - - return xfs_lic_slot(licp, i); - } - cmn_err(CE_WARN, "xfs_trans_first_item() -- no first item"); - return NULL; -} - - -/* - * Given a descriptor, return the next descriptor in the chunk list. - * This returns NULL if there are no more used descriptors in the list. - * - * We do this by first locating the chunk in which the descriptor resides, - * and then scanning forward in the chunk and the list for the next - * used descriptor. - */ -/*ARGSUSED*/ -xfs_log_item_desc_t * -xfs_trans_next_item(xfs_trans_t *tp, xfs_log_item_desc_t *lidp) -{ - xfs_log_item_chunk_t *licp; - int i; - - licp = xfs_lic_desc_to_chunk(lidp); - - /* - * First search the rest of the chunk. The for loop keeps us - * from referencing things beyond the end of the chunk. - */ - for (i = (int)xfs_lic_desc_to_slot(lidp) + 1; i < licp->lic_unused; i++) { - if (xfs_lic_isfree(licp, i)) { - continue; - } - - return xfs_lic_slot(licp, i); - } - - /* - * Now search the next chunk. It must be there, because the - * next chunk would have been freed if it were empty. - * If there is no next chunk, return NULL. - */ - if (licp->lic_next == NULL) { - return NULL; - } - - licp = licp->lic_next; - ASSERT(!xfs_lic_are_all_free(licp)); - for (i = 0; i < licp->lic_unused; i++) { - if (xfs_lic_isfree(licp, i)) { - continue; - } - - return xfs_lic_slot(licp, i); - } - ASSERT(0); - /* NOTREACHED */ - return NULL; /* keep gcc quite */ -} - -/* - * This is called to unlock all of the items of a transaction and to free - * all the descriptors of that transaction. - * - * It walks the list of descriptors and unlocks each item. It frees - * each chunk except that embedded in the transaction as it goes along. - */ -void -xfs_trans_free_items( - xfs_trans_t *tp, - xfs_lsn_t commit_lsn, - int flags) -{ - xfs_log_item_chunk_t *licp; - xfs_log_item_chunk_t *next_licp; - int abort; - - abort = flags & XFS_TRANS_ABORT; - licp = &tp->t_items; - /* - * Special case the embedded chunk so we don't free it below. - */ - if (!xfs_lic_are_all_free(licp)) { - (void) xfs_trans_unlock_chunk(licp, 1, abort, commit_lsn); - xfs_lic_all_free(licp); - licp->lic_unused = 0; - } - licp = licp->lic_next; - - /* - * Unlock each item in each chunk and free the chunks. - */ - while (licp != NULL) { - ASSERT(!xfs_lic_are_all_free(licp)); - (void) xfs_trans_unlock_chunk(licp, 1, abort, commit_lsn); - next_licp = licp->lic_next; - kmem_free(licp); - licp = next_licp; - } - - /* - * Reset the transaction structure's free item count. - */ - tp->t_items_free = XFS_LIC_NUM_SLOTS; - tp->t_items.lic_next = NULL; -} - - - -/* - * This is called to unlock the items associated with a transaction. - * Items which were not logged should be freed. - * Those which were logged must still be tracked so they can be unpinned - * when the transaction commits. - */ -void -xfs_trans_unlock_items(xfs_trans_t *tp, xfs_lsn_t commit_lsn) -{ - xfs_log_item_chunk_t *licp; - xfs_log_item_chunk_t *next_licp; - xfs_log_item_chunk_t **licpp; - int freed; - - freed = 0; - licp = &tp->t_items; - - /* - * Special case the embedded chunk so we don't free. - */ - if (!xfs_lic_are_all_free(licp)) { - freed = xfs_trans_unlock_chunk(licp, 0, 0, commit_lsn); - } - licpp = &(tp->t_items.lic_next); - licp = licp->lic_next; - - /* - * Unlock each item in each chunk, free non-dirty descriptors, - * and free empty chunks. - */ - while (licp != NULL) { - ASSERT(!xfs_lic_are_all_free(licp)); - freed += xfs_trans_unlock_chunk(licp, 0, 0, commit_lsn); - next_licp = licp->lic_next; - if (xfs_lic_are_all_free(licp)) { - *licpp = next_licp; - kmem_free(licp); - freed -= XFS_LIC_NUM_SLOTS; - } else { - licpp = &(licp->lic_next); - } - ASSERT(*licpp == next_licp); - licp = next_licp; - } - - /* - * Fix the free descriptor count in the transaction. - */ - tp->t_items_free += freed; -} - -/* - * Unlock each item pointed to by a descriptor in the given chunk. - * Stamp the commit lsn into each item if necessary. - * Free descriptors pointing to items which are not dirty if freeing_chunk - * is zero. If freeing_chunk is non-zero, then we need to unlock all - * items in the chunk. - * - * Return the number of descriptors freed. - */ -STATIC int -xfs_trans_unlock_chunk( - xfs_log_item_chunk_t *licp, - int freeing_chunk, - int abort, - xfs_lsn_t commit_lsn) -{ - xfs_log_item_desc_t *lidp; - xfs_log_item_t *lip; - int i; - int freed; - - freed = 0; - lidp = licp->lic_descs; - for (i = 0; i < licp->lic_unused; i++, lidp++) { - if (xfs_lic_isfree(licp, i)) { - continue; - } - lip = lidp->lid_item; - lip->li_desc = NULL; - - if (commit_lsn != NULLCOMMITLSN) - IOP_COMMITTING(lip, commit_lsn); - if (abort) - lip->li_flags |= XFS_LI_ABORTED; - IOP_UNLOCK(lip); - - /* - * Free the descriptor if the item is not dirty - * within this transaction and the caller is not - * going to just free the entire thing regardless. - */ - if (!(freeing_chunk) && - (!(lidp->lid_flags & XFS_LID_DIRTY) || abort)) { - xfs_lic_relse(licp, i); - freed++; - } - } - - return freed; -} diff --git a/fs/xfs/xfs_trans_priv.h b/fs/xfs/xfs_trans_priv.h index c6e4f2c8de6e..e2d93d8ead7b 100644 --- a/fs/xfs/xfs_trans_priv.h +++ b/fs/xfs/xfs_trans_priv.h @@ -23,22 +23,8 @@ struct xfs_log_item_desc; struct xfs_mount; struct xfs_trans; -/* - * From xfs_trans_item.c - */ -struct xfs_log_item_desc *xfs_trans_add_item(struct xfs_trans *, - struct xfs_log_item *); -void xfs_trans_free_item(struct xfs_trans *, - struct xfs_log_item_desc *); -struct xfs_log_item_desc *xfs_trans_find_item(struct xfs_trans *, - struct xfs_log_item *); -struct xfs_log_item_desc *xfs_trans_first_item(struct xfs_trans *); -struct xfs_log_item_desc *xfs_trans_next_item(struct xfs_trans *, - struct xfs_log_item_desc *); - -void xfs_trans_unlock_items(struct xfs_trans *tp, xfs_lsn_t commit_lsn); -void xfs_trans_free_items(struct xfs_trans *tp, xfs_lsn_t commit_lsn, - int flags); +void xfs_trans_add_item(struct xfs_trans *, struct xfs_log_item *); +void xfs_trans_del_item(struct xfs_log_item *); void xfs_trans_item_committed(struct xfs_log_item *lip, xfs_lsn_t commit_lsn, int aborted); -- cgit v1.2.3 From 9412e3181c0ef82efc3d8e88d73e583ec10c34e9 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 23 Jun 2010 18:11:15 +1000 Subject: xfs: merge iop_unpin_remove into iop_unpin The unpin_remove item operation instances always share most of the implementation with the respective unpin implementation. So instead of keeping two different entry points add a remove flag to the unpin operation and share the code more easily. Signed-off-by: Christoph Hellwig Reviewed-by: Dave Chinner --- fs/xfs/quota/xfs_dquot_item.c | 33 ++++--------------- fs/xfs/xfs_buf_item.c | 74 ++++++++++++++++--------------------------- fs/xfs/xfs_extfree_item.c | 49 ++++------------------------ fs/xfs/xfs_inode_item.c | 17 ++-------- fs/xfs/xfs_trans.c | 4 +-- fs/xfs/xfs_trans.h | 6 ++-- 6 files changed, 47 insertions(+), 136 deletions(-) (limited to 'fs') diff --git a/fs/xfs/quota/xfs_dquot_item.c b/fs/xfs/quota/xfs_dquot_item.c index fb7054c1539d..fa2b6744937e 100644 --- a/fs/xfs/quota/xfs_dquot_item.c +++ b/fs/xfs/quota/xfs_dquot_item.c @@ -97,7 +97,8 @@ xfs_qm_dquot_logitem_pin( /* ARGSUSED */ STATIC void xfs_qm_dquot_logitem_unpin( - xfs_dq_logitem_t *logitem) + xfs_dq_logitem_t *logitem, + int remove) { xfs_dquot_t *dqp = logitem->qli_dquot; @@ -106,15 +107,6 @@ xfs_qm_dquot_logitem_unpin( wake_up(&dqp->q_pinwait); } -/* ARGSUSED */ -STATIC void -xfs_qm_dquot_logitem_unpin_remove( - xfs_dq_logitem_t *logitem, - xfs_trans_t *tp) -{ - xfs_qm_dquot_logitem_unpin(logitem); -} - /* * Given the logitem, this writes the corresponding dquot entry to disk * asynchronously. This is called with the dquot entry securely locked; @@ -318,9 +310,7 @@ static struct xfs_item_ops xfs_dquot_item_ops = { .iop_format = (void(*)(xfs_log_item_t*, xfs_log_iovec_t*)) xfs_qm_dquot_logitem_format, .iop_pin = (void(*)(xfs_log_item_t*))xfs_qm_dquot_logitem_pin, - .iop_unpin = (void(*)(xfs_log_item_t*))xfs_qm_dquot_logitem_unpin, - .iop_unpin_remove = (void(*)(xfs_log_item_t*, xfs_trans_t*)) - xfs_qm_dquot_logitem_unpin_remove, + .iop_unpin = (void(*)(xfs_log_item_t*, int))xfs_qm_dquot_logitem_unpin, .iop_trylock = (uint(*)(xfs_log_item_t*)) xfs_qm_dquot_logitem_trylock, .iop_unlock = (void(*)(xfs_log_item_t*))xfs_qm_dquot_logitem_unlock, @@ -413,14 +403,7 @@ xfs_qm_qoff_logitem_pin(xfs_qoff_logitem_t *qf) */ /*ARGSUSED*/ STATIC void -xfs_qm_qoff_logitem_unpin(xfs_qoff_logitem_t *qf) -{ - return; -} - -/*ARGSUSED*/ -STATIC void -xfs_qm_qoff_logitem_unpin_remove(xfs_qoff_logitem_t *qf, xfs_trans_t *tp) +xfs_qm_qoff_logitem_unpin(xfs_qoff_logitem_t *qf, int remove) { return; } @@ -524,9 +507,7 @@ static struct xfs_item_ops xfs_qm_qoffend_logitem_ops = { .iop_format = (void(*)(xfs_log_item_t*, xfs_log_iovec_t*)) xfs_qm_qoff_logitem_format, .iop_pin = (void(*)(xfs_log_item_t*))xfs_qm_qoff_logitem_pin, - .iop_unpin = (void(*)(xfs_log_item_t*))xfs_qm_qoff_logitem_unpin, - .iop_unpin_remove = (void(*)(xfs_log_item_t*,xfs_trans_t*)) - xfs_qm_qoff_logitem_unpin_remove, + .iop_unpin = (void(*)(xfs_log_item_t*, int))xfs_qm_qoff_logitem_unpin, .iop_trylock = (uint(*)(xfs_log_item_t*))xfs_qm_qoff_logitem_trylock, .iop_unlock = (void(*)(xfs_log_item_t*))xfs_qm_qoff_logitem_unlock, .iop_committed = (xfs_lsn_t(*)(xfs_log_item_t*, xfs_lsn_t)) @@ -545,9 +526,7 @@ static struct xfs_item_ops xfs_qm_qoff_logitem_ops = { .iop_format = (void(*)(xfs_log_item_t*, xfs_log_iovec_t*)) xfs_qm_qoff_logitem_format, .iop_pin = (void(*)(xfs_log_item_t*))xfs_qm_qoff_logitem_pin, - .iop_unpin = (void(*)(xfs_log_item_t*))xfs_qm_qoff_logitem_unpin, - .iop_unpin_remove = (void(*)(xfs_log_item_t*,xfs_trans_t*)) - xfs_qm_qoff_logitem_unpin_remove, + .iop_unpin = (void(*)(xfs_log_item_t*, int))xfs_qm_qoff_logitem_unpin, .iop_trylock = (uint(*)(xfs_log_item_t*))xfs_qm_qoff_logitem_trylock, .iop_unlock = (void(*)(xfs_log_item_t*))xfs_qm_qoff_logitem_unlock, .iop_committed = (xfs_lsn_t(*)(xfs_log_item_t*, xfs_lsn_t)) diff --git a/fs/xfs/xfs_buf_item.c b/fs/xfs/xfs_buf_item.c index 711f69abbbe4..93899953c603 100644 --- a/fs/xfs/xfs_buf_item.c +++ b/fs/xfs/xfs_buf_item.c @@ -388,20 +388,25 @@ xfs_buf_item_pin( * Also drop the reference to the buf item for the current transaction. * If the XFS_BLI_STALE flag is set and we are the last reference, * then free up the buf log item and unlock the buffer. + * + * If the remove flag is set we are called from uncommit in the + * forced-shutdown path. If that is true and the reference count on + * the log item is going to drop to zero we need to free the item's + * descriptor in the transaction. */ STATIC void xfs_buf_item_unpin( - xfs_buf_log_item_t *bip) + xfs_buf_log_item_t *bip, + int remove) { struct xfs_ail *ailp; - xfs_buf_t *bp; + xfs_buf_t *bp = bip->bli_buf; int freed; int stale = bip->bli_flags & XFS_BLI_STALE; - bp = bip->bli_buf; - ASSERT(bp != NULL); ASSERT(XFS_BUF_FSPRIVATE(bp, xfs_buf_log_item_t *) == bip); ASSERT(atomic_read(&bip->bli_refcount) > 0); + trace_xfs_buf_item_unpin(bip); freed = atomic_dec_and_test(&bip->bli_refcount); @@ -413,8 +418,26 @@ xfs_buf_item_unpin( ASSERT(!(XFS_BUF_ISDELAYWRITE(bp))); ASSERT(XFS_BUF_ISSTALE(bp)); ASSERT(bip->bli_format.blf_flags & XFS_BLF_CANCEL); + trace_xfs_buf_item_unpin_stale(bip); + if (remove) { + /* + * We have to remove the log item from the transaction + * as we are about to release our reference to the + * buffer. If we don't, the unlock that occurs later + * in xfs_trans_uncommit() will ry to reference the + * buffer which we no longer have a hold on. + */ + xfs_trans_del_item(&bip->bli_item); + + /* + * Since the transaction no longer refers to the buffer, + * the buffer should no longer refer to the transaction. + */ + XFS_BUF_SET_FSPRIVATE2(bp, NULL); + } + /* * If we get called here because of an IO error, we may * or may not have the item on the AIL. xfs_trans_ail_delete() @@ -435,45 +458,6 @@ xfs_buf_item_unpin( } } -/* - * this is called from uncommit in the forced-shutdown path. - * we need to check to see if the reference count on the log item - * is going to drop to zero. If so, unpin will free the log item - * so we need to free the item's descriptor (that points to the item) - * in the transaction. - */ -STATIC void -xfs_buf_item_unpin_remove( - xfs_buf_log_item_t *bip, - xfs_trans_t *tp) -{ - /* will xfs_buf_item_unpin() call xfs_buf_item_relse()? */ - if ((atomic_read(&bip->bli_refcount) == 1) && - (bip->bli_flags & XFS_BLI_STALE)) { - /* - * yes -- We can safely do some work here and then call - * buf_item_unpin to do the rest because we are - * are holding the buffer locked so no one else will be - * able to bump up the refcount. We have to remove the - * log item from the transaction as we are about to release - * our reference to the buffer. If we don't, the unlock that - * occurs later in the xfs_trans_uncommit() will try to - * reference the buffer which we no longer have a hold on. - */ - ASSERT(XFS_BUF_VALUSEMA(bip->bli_buf) <= 0); - trace_xfs_buf_item_unpin_stale(bip); - - xfs_trans_del_item(&bip->bli_item); - - /* - * Since the transaction no longer refers to the buffer, the - * buffer should no longer refer to the transaction. - */ - XFS_BUF_SET_FSPRIVATE2(bip->bli_buf, NULL); - } - xfs_buf_item_unpin(bip); -} - /* * This is called to attempt to lock the buffer associated with this * buf log item. Don't sleep on the buffer lock. If we can't get @@ -669,9 +653,7 @@ static struct xfs_item_ops xfs_buf_item_ops = { .iop_format = (void(*)(xfs_log_item_t*, xfs_log_iovec_t*)) xfs_buf_item_format, .iop_pin = (void(*)(xfs_log_item_t*))xfs_buf_item_pin, - .iop_unpin = (void(*)(xfs_log_item_t*))xfs_buf_item_unpin, - .iop_unpin_remove = (void(*)(xfs_log_item_t*, xfs_trans_t *)) - xfs_buf_item_unpin_remove, + .iop_unpin = (void(*)(xfs_log_item_t*, int))xfs_buf_item_unpin, .iop_trylock = (uint(*)(xfs_log_item_t*))xfs_buf_item_trylock, .iop_unlock = (void(*)(xfs_log_item_t*))xfs_buf_item_unlock, .iop_committed = (xfs_lsn_t(*)(xfs_log_item_t*, xfs_lsn_t)) diff --git a/fs/xfs/xfs_extfree_item.c b/fs/xfs/xfs_extfree_item.c index 8d0e543ca3c0..6ac7e596c54c 100644 --- a/fs/xfs/xfs_extfree_item.c +++ b/fs/xfs/xfs_extfree_item.c @@ -103,32 +103,8 @@ xfs_efi_item_pin(xfs_efi_log_item_t *efip) * Here we coordinate with xfs_efi_cancel() to determine who gets to * free the EFI. */ -/*ARGSUSED*/ -STATIC void -xfs_efi_item_unpin(xfs_efi_log_item_t *efip) -{ - struct xfs_ail *ailp = efip->efi_item.li_ailp; - - spin_lock(&ailp->xa_lock); - if (efip->efi_flags & XFS_EFI_CANCELED) { - /* xfs_trans_ail_delete() drops the AIL lock. */ - xfs_trans_ail_delete(ailp, (xfs_log_item_t *)efip); - xfs_efi_item_free(efip); - } else { - efip->efi_flags |= XFS_EFI_COMMITTED; - spin_unlock(&ailp->xa_lock); - } -} - -/* - * like unpin only we have to also clear the xaction descriptor - * pointing the log item if we free the item. This routine duplicates - * unpin because efi_flags is protected by the AIL lock. Freeing - * the descriptor and then calling unpin would force us to drop the AIL - * lock which would open up a race condition. - */ STATIC void -xfs_efi_item_unpin_remove(xfs_efi_log_item_t *efip, xfs_trans_t *tp) +xfs_efi_item_unpin(xfs_efi_log_item_t *efip, int remove) { struct xfs_ail *ailp = efip->efi_item.li_ailp; @@ -136,10 +112,8 @@ xfs_efi_item_unpin_remove(xfs_efi_log_item_t *efip, xfs_trans_t *tp) if (efip->efi_flags & XFS_EFI_CANCELED) { struct xfs_log_item *lip = &efip->efi_item; - /* - * free the xaction descriptor pointing to this item - */ - xfs_trans_del_item(lip); + if (remove) + xfs_trans_del_item(lip); /* xfs_trans_ail_delete() drops the AIL lock. */ xfs_trans_ail_delete(ailp, lip); @@ -223,9 +197,7 @@ static struct xfs_item_ops xfs_efi_item_ops = { .iop_format = (void(*)(xfs_log_item_t*, xfs_log_iovec_t*)) xfs_efi_item_format, .iop_pin = (void(*)(xfs_log_item_t*))xfs_efi_item_pin, - .iop_unpin = (void(*)(xfs_log_item_t*))xfs_efi_item_unpin, - .iop_unpin_remove = (void(*)(xfs_log_item_t*, xfs_trans_t *)) - xfs_efi_item_unpin_remove, + .iop_unpin = (void(*)(xfs_log_item_t*, int))xfs_efi_item_unpin, .iop_trylock = (uint(*)(xfs_log_item_t*))xfs_efi_item_trylock, .iop_unlock = (void(*)(xfs_log_item_t*))xfs_efi_item_unlock, .iop_committed = (xfs_lsn_t(*)(xfs_log_item_t*, xfs_lsn_t)) @@ -424,14 +396,7 @@ xfs_efd_item_pin(xfs_efd_log_item_t *efdp) */ /*ARGSUSED*/ STATIC void -xfs_efd_item_unpin(xfs_efd_log_item_t *efdp) -{ - return; -} - -/*ARGSUSED*/ -STATIC void -xfs_efd_item_unpin_remove(xfs_efd_log_item_t *efdp, xfs_trans_t *tp) +xfs_efd_item_unpin(xfs_efd_log_item_t *efdp, int remove) { return; } @@ -514,9 +479,7 @@ static struct xfs_item_ops xfs_efd_item_ops = { .iop_format = (void(*)(xfs_log_item_t*, xfs_log_iovec_t*)) xfs_efd_item_format, .iop_pin = (void(*)(xfs_log_item_t*))xfs_efd_item_pin, - .iop_unpin = (void(*)(xfs_log_item_t*))xfs_efd_item_unpin, - .iop_unpin_remove = (void(*)(xfs_log_item_t*, xfs_trans_t*)) - xfs_efd_item_unpin_remove, + .iop_unpin = (void(*)(xfs_log_item_t*, int))xfs_efd_item_unpin, .iop_trylock = (uint(*)(xfs_log_item_t*))xfs_efd_item_trylock, .iop_unlock = (void(*)(xfs_log_item_t*))xfs_efd_item_unlock, .iop_committed = (xfs_lsn_t(*)(xfs_log_item_t*, xfs_lsn_t)) diff --git a/fs/xfs/xfs_inode_item.c b/fs/xfs/xfs_inode_item.c index 4b97d7754b83..a01990dbb945 100644 --- a/fs/xfs/xfs_inode_item.c +++ b/fs/xfs/xfs_inode_item.c @@ -544,10 +544,10 @@ xfs_inode_item_pin( * * Also wake up anyone in xfs_iunpin_wait() if the count goes to 0. */ -/* ARGSUSED */ STATIC void xfs_inode_item_unpin( - xfs_inode_log_item_t *iip) + xfs_inode_log_item_t *iip, + int remove) { struct xfs_inode *ip = iip->ili_inode; @@ -557,15 +557,6 @@ xfs_inode_item_unpin( wake_up(&ip->i_ipin_wait); } -/* ARGSUSED */ -STATIC void -xfs_inode_item_unpin_remove( - xfs_inode_log_item_t *iip, - xfs_trans_t *tp) -{ - xfs_inode_item_unpin(iip); -} - /* * This is called to attempt to lock the inode associated with this * inode log item, in preparation for the push routine which does the actual @@ -829,9 +820,7 @@ static struct xfs_item_ops xfs_inode_item_ops = { .iop_format = (void(*)(xfs_log_item_t*, xfs_log_iovec_t*)) xfs_inode_item_format, .iop_pin = (void(*)(xfs_log_item_t*))xfs_inode_item_pin, - .iop_unpin = (void(*)(xfs_log_item_t*))xfs_inode_item_unpin, - .iop_unpin_remove = (void(*)(xfs_log_item_t*, xfs_trans_t*)) - xfs_inode_item_unpin_remove, + .iop_unpin = (void(*)(xfs_log_item_t*, int))xfs_inode_item_unpin, .iop_trylock = (uint(*)(xfs_log_item_t*))xfs_inode_item_trylock, .iop_unlock = (void(*)(xfs_log_item_t*))xfs_inode_item_unlock, .iop_committed = (xfs_lsn_t(*)(xfs_log_item_t*, xfs_lsn_t)) diff --git a/fs/xfs/xfs_trans.c b/fs/xfs/xfs_trans.c index 9c41efccf728..213792e1ad02 100644 --- a/fs/xfs/xfs_trans.c +++ b/fs/xfs/xfs_trans.c @@ -1375,7 +1375,7 @@ xfs_trans_item_committed( * log item flags, if anyone else stales the buffer we do not want to * pay any attention to it. */ - IOP_UNPIN(lip); + IOP_UNPIN(lip, 0); } /* @@ -1422,7 +1422,7 @@ xfs_trans_uncommit( * Unpin all but those that aren't dirty. */ if (lidp->lid_flags & XFS_LID_DIRTY) - IOP_UNPIN_REMOVE(lidp->lid_item, tp); + IOP_UNPIN(lidp->lid_item, 1); } xfs_trans_unreserve_and_mod_sb(tp); diff --git a/fs/xfs/xfs_trans.h b/fs/xfs/xfs_trans.h index 0c903eb8bbe1..37c0ce1ccd48 100644 --- a/fs/xfs/xfs_trans.h +++ b/fs/xfs/xfs_trans.h @@ -347,8 +347,7 @@ typedef struct xfs_item_ops { uint (*iop_size)(xfs_log_item_t *); void (*iop_format)(xfs_log_item_t *, struct xfs_log_iovec *); void (*iop_pin)(xfs_log_item_t *); - void (*iop_unpin)(xfs_log_item_t *); - void (*iop_unpin_remove)(xfs_log_item_t *, struct xfs_trans *); + void (*iop_unpin)(xfs_log_item_t *, int remove); uint (*iop_trylock)(xfs_log_item_t *); void (*iop_unlock)(xfs_log_item_t *); xfs_lsn_t (*iop_committed)(xfs_log_item_t *, xfs_lsn_t); @@ -360,8 +359,7 @@ typedef struct xfs_item_ops { #define IOP_SIZE(ip) (*(ip)->li_ops->iop_size)(ip) #define IOP_FORMAT(ip,vp) (*(ip)->li_ops->iop_format)(ip, vp) #define IOP_PIN(ip) (*(ip)->li_ops->iop_pin)(ip) -#define IOP_UNPIN(ip) (*(ip)->li_ops->iop_unpin)(ip) -#define IOP_UNPIN_REMOVE(ip,tp) (*(ip)->li_ops->iop_unpin_remove)(ip, tp) +#define IOP_UNPIN(ip, remove) (*(ip)->li_ops->iop_unpin)(ip, remove) #define IOP_TRYLOCK(ip) (*(ip)->li_ops->iop_trylock)(ip) #define IOP_UNLOCK(ip) (*(ip)->li_ops->iop_unlock)(ip) #define IOP_COMMITTED(ip, lsn) (*(ip)->li_ops->iop_committed)(ip, lsn) -- cgit v1.2.3 From 7bfa31d8e0f90b65ff23be94fca65ce261b43fc8 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 23 Jun 2010 18:11:15 +1000 Subject: xfs: give xfs_item_ops methods the correct prototypes Stop the function pointer casting madness and give all the xfs_item_ops the correct prototypes. Signed-off-by: Christoph Hellwig Reviewed-by: Dave Chinner --- fs/xfs/quota/xfs_dquot_item.c | 268 +++++++++++++++++++----------------------- fs/xfs/xfs_buf_item.c | 129 ++++++++++---------- fs/xfs/xfs_extfree_item.c | 226 +++++++++++++++++------------------ fs/xfs/xfs_inode_item.c | 141 ++++++++++------------ 4 files changed, 360 insertions(+), 404 deletions(-) (limited to 'fs') diff --git a/fs/xfs/quota/xfs_dquot_item.c b/fs/xfs/quota/xfs_dquot_item.c index fa2b6744937e..cd86bc317d59 100644 --- a/fs/xfs/quota/xfs_dquot_item.c +++ b/fs/xfs/quota/xfs_dquot_item.c @@ -37,18 +37,22 @@ #include "xfs_trans_priv.h" #include "xfs_qm.h" +static inline struct xfs_dq_logitem *DQUOT_ITEM(struct xfs_log_item *lip) +{ + return container_of(lip, struct xfs_dq_logitem, qli_item); +} + /* * returns the number of iovecs needed to log the given dquot item. */ -/* ARGSUSED */ STATIC uint xfs_qm_dquot_logitem_size( - xfs_dq_logitem_t *logitem) + struct xfs_log_item *lip) { /* * we need only two iovecs, one for the format, one for the real thing */ - return (2); + return 2; } /* @@ -56,22 +60,21 @@ xfs_qm_dquot_logitem_size( */ STATIC void xfs_qm_dquot_logitem_format( - xfs_dq_logitem_t *logitem, - xfs_log_iovec_t *logvec) + struct xfs_log_item *lip, + struct xfs_log_iovec *logvec) { - ASSERT(logitem); - ASSERT(logitem->qli_dquot); + struct xfs_dq_logitem *qlip = DQUOT_ITEM(lip); - logvec->i_addr = (xfs_caddr_t)&logitem->qli_format; + logvec->i_addr = (xfs_caddr_t)&qlip->qli_format; logvec->i_len = sizeof(xfs_dq_logformat_t); logvec->i_type = XLOG_REG_TYPE_QFORMAT; logvec++; - logvec->i_addr = (xfs_caddr_t)&logitem->qli_dquot->q_core; + logvec->i_addr = (xfs_caddr_t)&qlip->qli_dquot->q_core; logvec->i_len = sizeof(xfs_disk_dquot_t); logvec->i_type = XLOG_REG_TYPE_DQUOT; - ASSERT(2 == logitem->qli_item.li_desc->lid_size); - logitem->qli_format.qlf_size = 2; + ASSERT(2 == lip->li_desc->lid_size); + qlip->qli_format.qlf_size = 2; } @@ -80,9 +83,9 @@ xfs_qm_dquot_logitem_format( */ STATIC void xfs_qm_dquot_logitem_pin( - xfs_dq_logitem_t *logitem) + struct xfs_log_item *lip) { - xfs_dquot_t *dqp = logitem->qli_dquot; + struct xfs_dquot *dqp = DQUOT_ITEM(lip)->qli_dquot; ASSERT(XFS_DQ_IS_LOCKED(dqp)); atomic_inc(&dqp->q_pincount); @@ -94,13 +97,12 @@ xfs_qm_dquot_logitem_pin( * dquot must have been previously pinned with a call to * xfs_qm_dquot_logitem_pin(). */ -/* ARGSUSED */ STATIC void xfs_qm_dquot_logitem_unpin( - xfs_dq_logitem_t *logitem, + struct xfs_log_item *lip, int remove) { - xfs_dquot_t *dqp = logitem->qli_dquot; + struct xfs_dquot *dqp = DQUOT_ITEM(lip)->qli_dquot; ASSERT(atomic_read(&dqp->q_pincount) > 0); if (atomic_dec_and_test(&dqp->q_pincount)) @@ -115,12 +117,10 @@ xfs_qm_dquot_logitem_unpin( */ STATIC void xfs_qm_dquot_logitem_push( - xfs_dq_logitem_t *logitem) + struct xfs_log_item *lip) { - xfs_dquot_t *dqp; - int error; - - dqp = logitem->qli_dquot; + struct xfs_dquot *dqp = DQUOT_ITEM(lip)->qli_dquot; + int error; ASSERT(XFS_DQ_IS_LOCKED(dqp)); ASSERT(!completion_done(&dqp->q_flush)); @@ -142,27 +142,25 @@ xfs_qm_dquot_logitem_push( xfs_dqunlock(dqp); } -/*ARGSUSED*/ STATIC xfs_lsn_t xfs_qm_dquot_logitem_committed( - xfs_dq_logitem_t *l, + struct xfs_log_item *lip, xfs_lsn_t lsn) { /* * We always re-log the entire dquot when it becomes dirty, * so, the latest copy _is_ the only one that matters. */ - return (lsn); + return lsn; } - /* * This is called to wait for the given dquot to be unpinned. * Most of these pin/unpin routines are plagiarized from inode code. */ void xfs_qm_dqunpin_wait( - xfs_dquot_t *dqp) + struct xfs_dquot *dqp) { ASSERT(XFS_DQ_IS_LOCKED(dqp)); if (atomic_read(&dqp->q_pincount) == 0) @@ -188,13 +186,12 @@ xfs_qm_dqunpin_wait( */ STATIC void xfs_qm_dquot_logitem_pushbuf( - xfs_dq_logitem_t *qip) + struct xfs_log_item *lip) { - xfs_dquot_t *dqp; - xfs_mount_t *mp; - xfs_buf_t *bp; + struct xfs_dq_logitem *qlip = DQUOT_ITEM(lip); + struct xfs_dquot *dqp = qlip->qli_dquot; + struct xfs_buf *bp; - dqp = qip->qli_dquot; ASSERT(XFS_DQ_IS_LOCKED(dqp)); /* @@ -202,22 +199,20 @@ xfs_qm_dquot_logitem_pushbuf( * inode flush completed and the inode was taken off the AIL. * So, just get out. */ - if (completion_done(&dqp->q_flush) || - ((qip->qli_item.li_flags & XFS_LI_IN_AIL) == 0)) { + if (completion_done(&dqp->q_flush) || + !(lip->li_flags & XFS_LI_IN_AIL)) { xfs_dqunlock(dqp); return; } - mp = dqp->q_mount; - bp = xfs_incore(mp->m_ddev_targp, qip->qli_format.qlf_blkno, - mp->m_quotainfo->qi_dqchunklen, XBF_TRYLOCK); + + bp = xfs_incore(dqp->q_mount->m_ddev_targp, qlip->qli_format.qlf_blkno, + dqp->q_mount->m_quotainfo->qi_dqchunklen, XBF_TRYLOCK); xfs_dqunlock(dqp); if (!bp) return; if (XFS_BUF_ISDELAYWRITE(bp)) xfs_buf_delwri_promote(bp); xfs_buf_relse(bp); - return; - } /* @@ -232,15 +227,14 @@ xfs_qm_dquot_logitem_pushbuf( */ STATIC uint xfs_qm_dquot_logitem_trylock( - xfs_dq_logitem_t *qip) + struct xfs_log_item *lip) { - xfs_dquot_t *dqp; + struct xfs_dquot *dqp = DQUOT_ITEM(lip)->qli_dquot; - dqp = qip->qli_dquot; if (atomic_read(&dqp->q_pincount) > 0) return XFS_ITEM_PINNED; - if (! xfs_qm_dqlock_nowait(dqp)) + if (!xfs_qm_dqlock_nowait(dqp)) return XFS_ITEM_LOCKED; if (!xfs_dqflock_nowait(dqp)) { @@ -251,11 +245,10 @@ xfs_qm_dquot_logitem_trylock( return XFS_ITEM_PUSHBUF; } - ASSERT(qip->qli_item.li_flags & XFS_LI_IN_AIL); + ASSERT(lip->li_flags & XFS_LI_IN_AIL); return XFS_ITEM_SUCCESS; } - /* * Unlock the dquot associated with the log item. * Clear the fields of the dquot and dquot log item that @@ -264,12 +257,10 @@ xfs_qm_dquot_logitem_trylock( */ STATIC void xfs_qm_dquot_logitem_unlock( - xfs_dq_logitem_t *ql) + struct xfs_log_item *lip) { - xfs_dquot_t *dqp; + struct xfs_dquot *dqp = DQUOT_ITEM(lip)->qli_dquot; - ASSERT(ql != NULL); - dqp = ql->qli_dquot; ASSERT(XFS_DQ_IS_LOCKED(dqp)); /* @@ -286,41 +277,32 @@ xfs_qm_dquot_logitem_unlock( xfs_dqunlock(dqp); } - /* * this needs to stamp an lsn into the dquot, I think. * rpc's that look at user dquot's would then have to * push on the dependency recorded in the dquot */ -/* ARGSUSED */ STATIC void xfs_qm_dquot_logitem_committing( - xfs_dq_logitem_t *l, + struct xfs_log_item *lip, xfs_lsn_t lsn) { - return; } - /* * This is the ops vector for dquots */ static struct xfs_item_ops xfs_dquot_item_ops = { - .iop_size = (uint(*)(xfs_log_item_t*))xfs_qm_dquot_logitem_size, - .iop_format = (void(*)(xfs_log_item_t*, xfs_log_iovec_t*)) - xfs_qm_dquot_logitem_format, - .iop_pin = (void(*)(xfs_log_item_t*))xfs_qm_dquot_logitem_pin, - .iop_unpin = (void(*)(xfs_log_item_t*, int))xfs_qm_dquot_logitem_unpin, - .iop_trylock = (uint(*)(xfs_log_item_t*)) - xfs_qm_dquot_logitem_trylock, - .iop_unlock = (void(*)(xfs_log_item_t*))xfs_qm_dquot_logitem_unlock, - .iop_committed = (xfs_lsn_t(*)(xfs_log_item_t*, xfs_lsn_t)) - xfs_qm_dquot_logitem_committed, - .iop_push = (void(*)(xfs_log_item_t*))xfs_qm_dquot_logitem_push, - .iop_pushbuf = (void(*)(xfs_log_item_t*)) - xfs_qm_dquot_logitem_pushbuf, - .iop_committing = (void(*)(xfs_log_item_t*, xfs_lsn_t)) - xfs_qm_dquot_logitem_committing + .iop_size = xfs_qm_dquot_logitem_size, + .iop_format = xfs_qm_dquot_logitem_format, + .iop_pin = xfs_qm_dquot_logitem_pin, + .iop_unpin = xfs_qm_dquot_logitem_unpin, + .iop_trylock = xfs_qm_dquot_logitem_trylock, + .iop_unlock = xfs_qm_dquot_logitem_unlock, + .iop_committed = xfs_qm_dquot_logitem_committed, + .iop_push = xfs_qm_dquot_logitem_push, + .iop_pushbuf = xfs_qm_dquot_logitem_pushbuf, + .iop_committing = xfs_qm_dquot_logitem_committing }; /* @@ -330,10 +312,9 @@ static struct xfs_item_ops xfs_dquot_item_ops = { */ void xfs_qm_dquot_logitem_init( - struct xfs_dquot *dqp) + struct xfs_dquot *dqp) { - xfs_dq_logitem_t *lp; - lp = &dqp->q_logitem; + struct xfs_dq_logitem *lp = &dqp->q_logitem; xfs_log_item_init(dqp->q_mount, &lp->qli_item, XFS_LI_DQUOT, &xfs_dquot_item_ops); @@ -354,16 +335,22 @@ xfs_qm_dquot_logitem_init( /*------------------ QUOTAOFF LOG ITEMS -------------------*/ +static inline struct xfs_qoff_logitem *QOFF_ITEM(struct xfs_log_item *lip) +{ + return container_of(lip, struct xfs_qoff_logitem, qql_item); +} + + /* * This returns the number of iovecs needed to log the given quotaoff item. * We only need 1 iovec for an quotaoff item. It just logs the * quotaoff_log_format structure. */ -/*ARGSUSED*/ STATIC uint -xfs_qm_qoff_logitem_size(xfs_qoff_logitem_t *qf) +xfs_qm_qoff_logitem_size( + struct xfs_log_item *lip) { - return (1); + return 1; } /* @@ -374,46 +361,46 @@ xfs_qm_qoff_logitem_size(xfs_qoff_logitem_t *qf) * slots in the quotaoff item have been filled. */ STATIC void -xfs_qm_qoff_logitem_format(xfs_qoff_logitem_t *qf, - xfs_log_iovec_t *log_vector) +xfs_qm_qoff_logitem_format( + struct xfs_log_item *lip, + struct xfs_log_iovec *log_vector) { - ASSERT(qf->qql_format.qf_type == XFS_LI_QUOTAOFF); + struct xfs_qoff_logitem *qflip = QOFF_ITEM(lip); - log_vector->i_addr = (xfs_caddr_t)&(qf->qql_format); + ASSERT(qflip->qql_format.qf_type == XFS_LI_QUOTAOFF); + + log_vector->i_addr = (xfs_caddr_t)&(qflip->qql_format); log_vector->i_len = sizeof(xfs_qoff_logitem_t); log_vector->i_type = XLOG_REG_TYPE_QUOTAOFF; - qf->qql_format.qf_size = 1; + qflip->qql_format.qf_size = 1; } - /* * Pinning has no meaning for an quotaoff item, so just return. */ -/*ARGSUSED*/ STATIC void -xfs_qm_qoff_logitem_pin(xfs_qoff_logitem_t *qf) +xfs_qm_qoff_logitem_pin( + struct xfs_log_item *lip) { - return; } - /* * Since pinning has no meaning for an quotaoff item, unpinning does * not either. */ -/*ARGSUSED*/ STATIC void -xfs_qm_qoff_logitem_unpin(xfs_qoff_logitem_t *qf, int remove) +xfs_qm_qoff_logitem_unpin( + struct xfs_log_item *lip, + int remove) { - return; } /* * Quotaoff items have no locking, so just return success. */ -/*ARGSUSED*/ STATIC uint -xfs_qm_qoff_logitem_trylock(xfs_qoff_logitem_t *qf) +xfs_qm_qoff_logitem_trylock( + struct xfs_log_item *lip) { return XFS_ITEM_LOCKED; } @@ -422,53 +409,51 @@ xfs_qm_qoff_logitem_trylock(xfs_qoff_logitem_t *qf) * Quotaoff items have no locking or pushing, so return failure * so that the caller doesn't bother with us. */ -/*ARGSUSED*/ STATIC void -xfs_qm_qoff_logitem_unlock(xfs_qoff_logitem_t *qf) +xfs_qm_qoff_logitem_unlock( + struct xfs_log_item *lip) { - return; } /* * The quotaoff-start-item is logged only once and cannot be moved in the log, * so simply return the lsn at which it's been logged. */ -/*ARGSUSED*/ STATIC xfs_lsn_t -xfs_qm_qoff_logitem_committed(xfs_qoff_logitem_t *qf, xfs_lsn_t lsn) +xfs_qm_qoff_logitem_committed( + struct xfs_log_item *lip, + xfs_lsn_t lsn) { - return (lsn); + return lsn; } /* * There isn't much you can do to push on an quotaoff item. It is simply * stuck waiting for the log to be flushed to disk. */ -/*ARGSUSED*/ STATIC void -xfs_qm_qoff_logitem_push(xfs_qoff_logitem_t *qf) +xfs_qm_qoff_logitem_push( + struct xfs_log_item *lip) { - return; } -/*ARGSUSED*/ STATIC xfs_lsn_t xfs_qm_qoffend_logitem_committed( - xfs_qoff_logitem_t *qfe, - xfs_lsn_t lsn) + struct xfs_log_item *lip, + xfs_lsn_t lsn) { - xfs_qoff_logitem_t *qfs; - struct xfs_ail *ailp; + struct xfs_qoff_logitem *qfe = QOFF_ITEM(lip); + struct xfs_qoff_logitem *qfs = qfe->qql_start_lip; + struct xfs_ail *ailp = qfs->qql_item.li_ailp; - qfs = qfe->qql_start_lip; - ailp = qfs->qql_item.li_ailp; - spin_lock(&ailp->xa_lock); /* * Delete the qoff-start logitem from the AIL. * xfs_trans_ail_delete() drops the AIL lock. */ + spin_lock(&ailp->xa_lock); xfs_trans_ail_delete(ailp, (xfs_log_item_t *)qfs); + kmem_free(qfs); kmem_free(qfe); return (xfs_lsn_t)-1; @@ -488,67 +473,52 @@ xfs_qm_qoffend_logitem_committed( * (truly makes the quotaoff irrevocable). If we do something else, * then maybe we don't need two. */ -/* ARGSUSED */ -STATIC void -xfs_qm_qoff_logitem_committing(xfs_qoff_logitem_t *qip, xfs_lsn_t commit_lsn) -{ - return; -} - -/* ARGSUSED */ STATIC void -xfs_qm_qoffend_logitem_committing(xfs_qoff_logitem_t *qip, xfs_lsn_t commit_lsn) +xfs_qm_qoff_logitem_committing( + struct xfs_log_item *lip, + xfs_lsn_t commit_lsn) { - return; } static struct xfs_item_ops xfs_qm_qoffend_logitem_ops = { - .iop_size = (uint(*)(xfs_log_item_t*))xfs_qm_qoff_logitem_size, - .iop_format = (void(*)(xfs_log_item_t*, xfs_log_iovec_t*)) - xfs_qm_qoff_logitem_format, - .iop_pin = (void(*)(xfs_log_item_t*))xfs_qm_qoff_logitem_pin, - .iop_unpin = (void(*)(xfs_log_item_t*, int))xfs_qm_qoff_logitem_unpin, - .iop_trylock = (uint(*)(xfs_log_item_t*))xfs_qm_qoff_logitem_trylock, - .iop_unlock = (void(*)(xfs_log_item_t*))xfs_qm_qoff_logitem_unlock, - .iop_committed = (xfs_lsn_t(*)(xfs_log_item_t*, xfs_lsn_t)) - xfs_qm_qoffend_logitem_committed, - .iop_push = (void(*)(xfs_log_item_t*))xfs_qm_qoff_logitem_push, - .iop_pushbuf = NULL, - .iop_committing = (void(*)(xfs_log_item_t*, xfs_lsn_t)) - xfs_qm_qoffend_logitem_committing + .iop_size = xfs_qm_qoff_logitem_size, + .iop_format = xfs_qm_qoff_logitem_format, + .iop_pin = xfs_qm_qoff_logitem_pin, + .iop_unpin = xfs_qm_qoff_logitem_unpin, + .iop_trylock = xfs_qm_qoff_logitem_trylock, + .iop_unlock = xfs_qm_qoff_logitem_unlock, + .iop_committed = xfs_qm_qoffend_logitem_committed, + .iop_push = xfs_qm_qoff_logitem_push, + .iop_committing = xfs_qm_qoff_logitem_committing }; /* * This is the ops vector shared by all quotaoff-start log items. */ static struct xfs_item_ops xfs_qm_qoff_logitem_ops = { - .iop_size = (uint(*)(xfs_log_item_t*))xfs_qm_qoff_logitem_size, - .iop_format = (void(*)(xfs_log_item_t*, xfs_log_iovec_t*)) - xfs_qm_qoff_logitem_format, - .iop_pin = (void(*)(xfs_log_item_t*))xfs_qm_qoff_logitem_pin, - .iop_unpin = (void(*)(xfs_log_item_t*, int))xfs_qm_qoff_logitem_unpin, - .iop_trylock = (uint(*)(xfs_log_item_t*))xfs_qm_qoff_logitem_trylock, - .iop_unlock = (void(*)(xfs_log_item_t*))xfs_qm_qoff_logitem_unlock, - .iop_committed = (xfs_lsn_t(*)(xfs_log_item_t*, xfs_lsn_t)) - xfs_qm_qoff_logitem_committed, - .iop_push = (void(*)(xfs_log_item_t*))xfs_qm_qoff_logitem_push, - .iop_pushbuf = NULL, - .iop_committing = (void(*)(xfs_log_item_t*, xfs_lsn_t)) - xfs_qm_qoff_logitem_committing + .iop_size = xfs_qm_qoff_logitem_size, + .iop_format = xfs_qm_qoff_logitem_format, + .iop_pin = xfs_qm_qoff_logitem_pin, + .iop_unpin = xfs_qm_qoff_logitem_unpin, + .iop_trylock = xfs_qm_qoff_logitem_trylock, + .iop_unlock = xfs_qm_qoff_logitem_unlock, + .iop_committed = xfs_qm_qoff_logitem_committed, + .iop_push = xfs_qm_qoff_logitem_push, + .iop_committing = xfs_qm_qoff_logitem_committing }; /* * Allocate and initialize an quotaoff item of the correct quota type(s). */ -xfs_qoff_logitem_t * +struct xfs_qoff_logitem * xfs_qm_qoff_logitem_init( - struct xfs_mount *mp, - xfs_qoff_logitem_t *start, - uint flags) + struct xfs_mount *mp, + struct xfs_qoff_logitem *start, + uint flags) { - xfs_qoff_logitem_t *qf; + struct xfs_qoff_logitem *qf; - qf = (xfs_qoff_logitem_t*) kmem_zalloc(sizeof(xfs_qoff_logitem_t), KM_SLEEP); + qf = kmem_zalloc(sizeof(struct xfs_qoff_logitem), KM_SLEEP); xfs_log_item_init(mp, &qf->qql_item, XFS_LI_QUOTAOFF, start ? &xfs_qm_qoffend_logitem_ops : &xfs_qm_qoff_logitem_ops); @@ -556,5 +526,5 @@ xfs_qm_qoff_logitem_init( qf->qql_format.qf_type = XFS_LI_QUOTAOFF; qf->qql_format.qf_flags = flags; qf->qql_start_lip = start; - return (qf); + return qf; } diff --git a/fs/xfs/xfs_buf_item.c b/fs/xfs/xfs_buf_item.c index 93899953c603..992d6be101cb 100644 --- a/fs/xfs/xfs_buf_item.c +++ b/fs/xfs/xfs_buf_item.c @@ -33,6 +33,12 @@ kmem_zone_t *xfs_buf_item_zone; +static inline struct xfs_buf_log_item *BUF_ITEM(struct xfs_log_item *lip) +{ + return container_of(lip, struct xfs_buf_log_item, bli_item); +} + + #ifdef XFS_TRANS_DEBUG /* * This function uses an alternate strategy for tracking the bytes @@ -150,12 +156,13 @@ STATIC void xfs_buf_do_callbacks(xfs_buf_t *bp, xfs_log_item_t *lip); */ STATIC uint xfs_buf_item_size( - xfs_buf_log_item_t *bip) + struct xfs_log_item *lip) { - uint nvecs; - int next_bit; - int last_bit; - xfs_buf_t *bp; + struct xfs_buf_log_item *bip = BUF_ITEM(lip); + struct xfs_buf *bp = bip->bli_buf; + uint nvecs; + int next_bit; + int last_bit; ASSERT(atomic_read(&bip->bli_refcount) > 0); if (bip->bli_flags & XFS_BLI_STALE) { @@ -169,7 +176,6 @@ xfs_buf_item_size( return 1; } - bp = bip->bli_buf; ASSERT(bip->bli_flags & XFS_BLI_LOGGED); nvecs = 1; last_bit = xfs_next_bit(bip->bli_format.blf_data_map, @@ -218,13 +224,13 @@ xfs_buf_item_size( */ STATIC void xfs_buf_item_format( - xfs_buf_log_item_t *bip, - xfs_log_iovec_t *log_vector) + struct xfs_log_item *lip, + struct xfs_log_iovec *vecp) { + struct xfs_buf_log_item *bip = BUF_ITEM(lip); + struct xfs_buf *bp = bip->bli_buf; uint base_size; uint nvecs; - xfs_log_iovec_t *vecp; - xfs_buf_t *bp; int first_bit; int last_bit; int next_bit; @@ -234,8 +240,6 @@ xfs_buf_item_format( ASSERT(atomic_read(&bip->bli_refcount) > 0); ASSERT((bip->bli_flags & XFS_BLI_LOGGED) || (bip->bli_flags & XFS_BLI_STALE)); - bp = bip->bli_buf; - vecp = log_vector; /* * The size of the base structure is the size of the @@ -262,7 +266,7 @@ xfs_buf_item_format( */ if (bip->bli_flags & XFS_BLI_INODE_BUF) { if (!((bip->bli_flags & XFS_BLI_INODE_ALLOC_BUF) && - xfs_log_item_in_current_chkpt(&bip->bli_item))) + xfs_log_item_in_current_chkpt(lip))) bip->bli_format.blf_flags |= XFS_BLF_INODE_BUF; bip->bli_flags &= ~XFS_BLI_INODE_BUF; } @@ -365,21 +369,20 @@ xfs_buf_item_format( STATIC void xfs_buf_item_pin( - xfs_buf_log_item_t *bip) + struct xfs_log_item *lip) { - xfs_buf_t *bp; + struct xfs_buf_log_item *bip = BUF_ITEM(lip); - bp = bip->bli_buf; - ASSERT(XFS_BUF_ISBUSY(bp)); + ASSERT(XFS_BUF_ISBUSY(bip->bli_buf)); ASSERT(atomic_read(&bip->bli_refcount) > 0); ASSERT((bip->bli_flags & XFS_BLI_LOGGED) || (bip->bli_flags & XFS_BLI_STALE)); + atomic_inc(&bip->bli_refcount); trace_xfs_buf_item_pin(bip); - xfs_bpin(bp); + xfs_bpin(bip->bli_buf); } - /* * This is called to unpin the buffer associated with the buf log * item which was previously pinned with a call to xfs_buf_item_pin(). @@ -396,13 +399,14 @@ xfs_buf_item_pin( */ STATIC void xfs_buf_item_unpin( - xfs_buf_log_item_t *bip, + struct xfs_log_item *lip, int remove) { - struct xfs_ail *ailp; + struct xfs_buf_log_item *bip = BUF_ITEM(lip); xfs_buf_t *bp = bip->bli_buf; - int freed; + struct xfs_ail *ailp = lip->li_ailp; int stale = bip->bli_flags & XFS_BLI_STALE; + int freed; ASSERT(XFS_BUF_FSPRIVATE(bp, xfs_buf_log_item_t *) == bip); ASSERT(atomic_read(&bip->bli_refcount) > 0); @@ -410,8 +414,8 @@ xfs_buf_item_unpin( trace_xfs_buf_item_unpin(bip); freed = atomic_dec_and_test(&bip->bli_refcount); - ailp = bip->bli_item.li_ailp; xfs_bunpin(bp); + if (freed && stale) { ASSERT(bip->bli_flags & XFS_BLI_STALE); ASSERT(XFS_BUF_VALUSEMA(bp) <= 0); @@ -429,7 +433,7 @@ xfs_buf_item_unpin( * in xfs_trans_uncommit() will ry to reference the * buffer which we no longer have a hold on. */ - xfs_trans_del_item(&bip->bli_item); + xfs_trans_del_item(lip); /* * Since the transaction no longer refers to the buffer, @@ -468,11 +472,11 @@ xfs_buf_item_unpin( */ STATIC uint xfs_buf_item_trylock( - xfs_buf_log_item_t *bip) + struct xfs_log_item *lip) { - xfs_buf_t *bp; + struct xfs_buf_log_item *bip = BUF_ITEM(lip); + struct xfs_buf *bp = bip->bli_buf; - bp = bip->bli_buf; if (XFS_BUF_ISPINNED(bp)) return XFS_ITEM_PINNED; if (!XFS_BUF_CPSEMA(bp)) @@ -509,13 +513,12 @@ xfs_buf_item_trylock( */ STATIC void xfs_buf_item_unlock( - xfs_buf_log_item_t *bip) + struct xfs_log_item *lip) { - int aborted; - xfs_buf_t *bp; - uint hold; - - bp = bip->bli_buf; + struct xfs_buf_log_item *bip = BUF_ITEM(lip); + struct xfs_buf *bp = bip->bli_buf; + int aborted; + uint hold; /* Clear the buffer's association with this transaction. */ XFS_BUF_SET_FSPRIVATE2(bp, NULL); @@ -526,7 +529,7 @@ xfs_buf_item_unlock( * (cancelled) buffers at unpin time, but we'll never go through the * pin/unpin cycle if we abort inside commit. */ - aborted = (bip->bli_item.li_flags & XFS_LI_ABORTED) != 0; + aborted = (lip->li_flags & XFS_LI_ABORTED) != 0; /* * Before possibly freeing the buf item, determine if we should @@ -587,16 +590,16 @@ xfs_buf_item_unlock( */ STATIC xfs_lsn_t xfs_buf_item_committed( - xfs_buf_log_item_t *bip, + struct xfs_log_item *lip, xfs_lsn_t lsn) { + struct xfs_buf_log_item *bip = BUF_ITEM(lip); + trace_xfs_buf_item_committed(bip); - if ((bip->bli_flags & XFS_BLI_INODE_ALLOC_BUF) && - (bip->bli_item.li_lsn != 0)) { - return bip->bli_item.li_lsn; - } - return (lsn); + if ((bip->bli_flags & XFS_BLI_INODE_ALLOC_BUF) && lip->li_lsn != 0) + return lip->li_lsn; + return lsn; } /* @@ -606,15 +609,16 @@ xfs_buf_item_committed( */ STATIC void xfs_buf_item_push( - xfs_buf_log_item_t *bip) + struct xfs_log_item *lip) { - xfs_buf_t *bp; + struct xfs_buf_log_item *bip = BUF_ITEM(lip); + struct xfs_buf *bp = bip->bli_buf; ASSERT(!(bip->bli_flags & XFS_BLI_STALE)); + ASSERT(!XFS_BUF_ISDELAYWRITE(bp)); + trace_xfs_buf_item_push(bip); - bp = bip->bli_buf; - ASSERT(!XFS_BUF_ISDELAYWRITE(bp)); xfs_buf_relse(bp); } @@ -626,22 +630,24 @@ xfs_buf_item_push( */ STATIC void xfs_buf_item_pushbuf( - xfs_buf_log_item_t *bip) + struct xfs_log_item *lip) { - xfs_buf_t *bp; + struct xfs_buf_log_item *bip = BUF_ITEM(lip); + struct xfs_buf *bp = bip->bli_buf; ASSERT(!(bip->bli_flags & XFS_BLI_STALE)); + ASSERT(XFS_BUF_ISDELAYWRITE(bp)); + trace_xfs_buf_item_pushbuf(bip); - bp = bip->bli_buf; - ASSERT(XFS_BUF_ISDELAYWRITE(bp)); xfs_buf_delwri_promote(bp); xfs_buf_relse(bp); } -/* ARGSUSED */ STATIC void -xfs_buf_item_committing(xfs_buf_log_item_t *bip, xfs_lsn_t commit_lsn) +xfs_buf_item_committing( + struct xfs_log_item *lip, + xfs_lsn_t commit_lsn) { } @@ -649,19 +655,16 @@ xfs_buf_item_committing(xfs_buf_log_item_t *bip, xfs_lsn_t commit_lsn) * This is the ops vector shared by all buf log items. */ static struct xfs_item_ops xfs_buf_item_ops = { - .iop_size = (uint(*)(xfs_log_item_t*))xfs_buf_item_size, - .iop_format = (void(*)(xfs_log_item_t*, xfs_log_iovec_t*)) - xfs_buf_item_format, - .iop_pin = (void(*)(xfs_log_item_t*))xfs_buf_item_pin, - .iop_unpin = (void(*)(xfs_log_item_t*, int))xfs_buf_item_unpin, - .iop_trylock = (uint(*)(xfs_log_item_t*))xfs_buf_item_trylock, - .iop_unlock = (void(*)(xfs_log_item_t*))xfs_buf_item_unlock, - .iop_committed = (xfs_lsn_t(*)(xfs_log_item_t*, xfs_lsn_t)) - xfs_buf_item_committed, - .iop_push = (void(*)(xfs_log_item_t*))xfs_buf_item_push, - .iop_pushbuf = (void(*)(xfs_log_item_t*))xfs_buf_item_pushbuf, - .iop_committing = (void(*)(xfs_log_item_t*, xfs_lsn_t)) - xfs_buf_item_committing + .iop_size = xfs_buf_item_size, + .iop_format = xfs_buf_item_format, + .iop_pin = xfs_buf_item_pin, + .iop_unpin = xfs_buf_item_unpin, + .iop_trylock = xfs_buf_item_trylock, + .iop_unlock = xfs_buf_item_unlock, + .iop_committed = xfs_buf_item_committed, + .iop_push = xfs_buf_item_push, + .iop_pushbuf = xfs_buf_item_pushbuf, + .iop_committing = xfs_buf_item_committing }; diff --git a/fs/xfs/xfs_extfree_item.c b/fs/xfs/xfs_extfree_item.c index 6ac7e596c54c..61d83c64ed32 100644 --- a/fs/xfs/xfs_extfree_item.c +++ b/fs/xfs/xfs_extfree_item.c @@ -32,18 +32,19 @@ kmem_zone_t *xfs_efi_zone; kmem_zone_t *xfs_efd_zone; -STATIC void xfs_efi_item_unlock(xfs_efi_log_item_t *); +static inline struct xfs_efi_log_item *EFI_ITEM(struct xfs_log_item *lip) +{ + return container_of(lip, struct xfs_efi_log_item, efi_item); +} void -xfs_efi_item_free(xfs_efi_log_item_t *efip) +xfs_efi_item_free( + struct xfs_efi_log_item *efip) { - int nexts = efip->efi_format.efi_nextents; - - if (nexts > XFS_EFI_MAX_FAST_EXTENTS) { + if (efip->efi_format.efi_nextents > XFS_EFI_MAX_FAST_EXTENTS) kmem_free(efip); - } else { + else kmem_zone_free(xfs_efi_zone, efip); - } } /* @@ -51,9 +52,9 @@ xfs_efi_item_free(xfs_efi_log_item_t *efip) * We only need 1 iovec for an efi item. It just logs the efi_log_format * structure. */ -/*ARGSUSED*/ STATIC uint -xfs_efi_item_size(xfs_efi_log_item_t *efip) +xfs_efi_item_size( + struct xfs_log_item *lip) { return 1; } @@ -66,10 +67,12 @@ xfs_efi_item_size(xfs_efi_log_item_t *efip) * slots in the efi item have been filled. */ STATIC void -xfs_efi_item_format(xfs_efi_log_item_t *efip, - xfs_log_iovec_t *log_vector) +xfs_efi_item_format( + struct xfs_log_item *lip, + struct xfs_log_iovec *log_vector) { - uint size; + struct xfs_efi_log_item *efip = EFI_ITEM(lip); + uint size; ASSERT(efip->efi_next_extent == efip->efi_format.efi_nextents); @@ -79,7 +82,7 @@ xfs_efi_item_format(xfs_efi_log_item_t *efip, size += (efip->efi_format.efi_nextents - 1) * sizeof(xfs_extent_t); efip->efi_format.efi_size = 1; - log_vector->i_addr = (xfs_caddr_t)&(efip->efi_format); + log_vector->i_addr = (xfs_caddr_t)&efip->efi_format; log_vector->i_len = size; log_vector->i_type = XLOG_REG_TYPE_EFI_FORMAT; ASSERT(size >= sizeof(xfs_efi_log_format_t)); @@ -89,14 +92,12 @@ xfs_efi_item_format(xfs_efi_log_item_t *efip, /* * Pinning has no meaning for an efi item, so just return. */ -/*ARGSUSED*/ STATIC void -xfs_efi_item_pin(xfs_efi_log_item_t *efip) +xfs_efi_item_pin( + struct xfs_log_item *lip) { - return; } - /* * While EFIs cannot really be pinned, the unpin operation is the * last place at which the EFI is manipulated during a transaction. @@ -104,14 +105,15 @@ xfs_efi_item_pin(xfs_efi_log_item_t *efip) * free the EFI. */ STATIC void -xfs_efi_item_unpin(xfs_efi_log_item_t *efip, int remove) +xfs_efi_item_unpin( + struct xfs_log_item *lip, + int remove) { - struct xfs_ail *ailp = efip->efi_item.li_ailp; + struct xfs_efi_log_item *efip = EFI_ITEM(lip); + struct xfs_ail *ailp = lip->li_ailp; spin_lock(&ailp->xa_lock); if (efip->efi_flags & XFS_EFI_CANCELED) { - struct xfs_log_item *lip = &efip->efi_item; - if (remove) xfs_trans_del_item(lip); @@ -131,9 +133,9 @@ xfs_efi_item_unpin(xfs_efi_log_item_t *efip, int remove) * XFS_ITEM_PINNED so that the caller will eventually flush the log. * This should help in getting the EFI out of the AIL. */ -/*ARGSUSED*/ STATIC uint -xfs_efi_item_trylock(xfs_efi_log_item_t *efip) +xfs_efi_item_trylock( + struct xfs_log_item *lip) { return XFS_ITEM_PINNED; } @@ -141,13 +143,12 @@ xfs_efi_item_trylock(xfs_efi_log_item_t *efip) /* * Efi items have no locking, so just return. */ -/*ARGSUSED*/ STATIC void -xfs_efi_item_unlock(xfs_efi_log_item_t *efip) +xfs_efi_item_unlock( + struct xfs_log_item *lip) { - if (efip->efi_item.li_flags & XFS_LI_ABORTED) - xfs_efi_item_free(efip); - return; + if (lip->li_flags & XFS_LI_ABORTED) + xfs_efi_item_free(EFI_ITEM(lip)); } /* @@ -156,9 +157,10 @@ xfs_efi_item_unlock(xfs_efi_log_item_t *efip) * flag is not paid any attention here. Checking for that is delayed * until the EFI is unpinned. */ -/*ARGSUSED*/ STATIC xfs_lsn_t -xfs_efi_item_committed(xfs_efi_log_item_t *efip, xfs_lsn_t lsn) +xfs_efi_item_committed( + struct xfs_log_item *lip, + xfs_lsn_t lsn) { return lsn; } @@ -168,11 +170,10 @@ xfs_efi_item_committed(xfs_efi_log_item_t *efip, xfs_lsn_t lsn) * stuck waiting for all of its corresponding efd items to be * committed to disk. */ -/*ARGSUSED*/ STATIC void -xfs_efi_item_push(xfs_efi_log_item_t *efip) +xfs_efi_item_push( + struct xfs_log_item *lip) { - return; } /* @@ -182,59 +183,55 @@ xfs_efi_item_push(xfs_efi_log_item_t *efip) * example, for inodes, the inode is locked throughout the extent freeing * so the dependency should be recorded there. */ -/*ARGSUSED*/ STATIC void -xfs_efi_item_committing(xfs_efi_log_item_t *efip, xfs_lsn_t lsn) +xfs_efi_item_committing( + struct xfs_log_item *lip, + xfs_lsn_t lsn) { - return; } /* * This is the ops vector shared by all efi log items. */ static struct xfs_item_ops xfs_efi_item_ops = { - .iop_size = (uint(*)(xfs_log_item_t*))xfs_efi_item_size, - .iop_format = (void(*)(xfs_log_item_t*, xfs_log_iovec_t*)) - xfs_efi_item_format, - .iop_pin = (void(*)(xfs_log_item_t*))xfs_efi_item_pin, - .iop_unpin = (void(*)(xfs_log_item_t*, int))xfs_efi_item_unpin, - .iop_trylock = (uint(*)(xfs_log_item_t*))xfs_efi_item_trylock, - .iop_unlock = (void(*)(xfs_log_item_t*))xfs_efi_item_unlock, - .iop_committed = (xfs_lsn_t(*)(xfs_log_item_t*, xfs_lsn_t)) - xfs_efi_item_committed, - .iop_push = (void(*)(xfs_log_item_t*))xfs_efi_item_push, - .iop_pushbuf = NULL, - .iop_committing = (void(*)(xfs_log_item_t*, xfs_lsn_t)) - xfs_efi_item_committing + .iop_size = xfs_efi_item_size, + .iop_format = xfs_efi_item_format, + .iop_pin = xfs_efi_item_pin, + .iop_unpin = xfs_efi_item_unpin, + .iop_trylock = xfs_efi_item_trylock, + .iop_unlock = xfs_efi_item_unlock, + .iop_committed = xfs_efi_item_committed, + .iop_push = xfs_efi_item_push, + .iop_committing = xfs_efi_item_committing }; /* * Allocate and initialize an efi item with the given number of extents. */ -xfs_efi_log_item_t * -xfs_efi_init(xfs_mount_t *mp, - uint nextents) +struct xfs_efi_log_item * +xfs_efi_init( + struct xfs_mount *mp, + uint nextents) { - xfs_efi_log_item_t *efip; + struct xfs_efi_log_item *efip; uint size; ASSERT(nextents > 0); if (nextents > XFS_EFI_MAX_FAST_EXTENTS) { size = (uint)(sizeof(xfs_efi_log_item_t) + ((nextents - 1) * sizeof(xfs_extent_t))); - efip = (xfs_efi_log_item_t*)kmem_zalloc(size, KM_SLEEP); + efip = kmem_zalloc(size, KM_SLEEP); } else { - efip = (xfs_efi_log_item_t*)kmem_zone_zalloc(xfs_efi_zone, - KM_SLEEP); + efip = kmem_zone_zalloc(xfs_efi_zone, KM_SLEEP); } xfs_log_item_init(mp, &efip->efi_item, XFS_LI_EFI, &xfs_efi_item_ops); efip->efi_format.efi_nextents = nextents; efip->efi_format.efi_id = (__psint_t)(void*)efip; - return (efip); + return efip; } /* @@ -327,16 +324,18 @@ xfs_efi_release(xfs_efi_log_item_t *efip, } } -STATIC void -xfs_efd_item_free(xfs_efd_log_item_t *efdp) +static inline struct xfs_efd_log_item *EFD_ITEM(struct xfs_log_item *lip) { - int nexts = efdp->efd_format.efd_nextents; + return container_of(lip, struct xfs_efd_log_item, efd_item); +} - if (nexts > XFS_EFD_MAX_FAST_EXTENTS) { +STATIC void +xfs_efd_item_free(struct xfs_efd_log_item *efdp) +{ + if (efdp->efd_format.efd_nextents > XFS_EFD_MAX_FAST_EXTENTS) kmem_free(efdp); - } else { + else kmem_zone_free(xfs_efd_zone, efdp); - } } /* @@ -344,9 +343,9 @@ xfs_efd_item_free(xfs_efd_log_item_t *efdp) * We only need 1 iovec for an efd item. It just logs the efd_log_format * structure. */ -/*ARGSUSED*/ STATIC uint -xfs_efd_item_size(xfs_efd_log_item_t *efdp) +xfs_efd_item_size( + struct xfs_log_item *lip) { return 1; } @@ -359,10 +358,12 @@ xfs_efd_item_size(xfs_efd_log_item_t *efdp) * slots in the efd item have been filled. */ STATIC void -xfs_efd_item_format(xfs_efd_log_item_t *efdp, - xfs_log_iovec_t *log_vector) +xfs_efd_item_format( + struct xfs_log_item *lip, + struct xfs_log_iovec *log_vector) { - uint size; + struct xfs_efd_log_item *efdp = EFD_ITEM(lip); + uint size; ASSERT(efdp->efd_next_extent == efdp->efd_format.efd_nextents); @@ -372,41 +373,38 @@ xfs_efd_item_format(xfs_efd_log_item_t *efdp, size += (efdp->efd_format.efd_nextents - 1) * sizeof(xfs_extent_t); efdp->efd_format.efd_size = 1; - log_vector->i_addr = (xfs_caddr_t)&(efdp->efd_format); + log_vector->i_addr = (xfs_caddr_t)&efdp->efd_format; log_vector->i_len = size; log_vector->i_type = XLOG_REG_TYPE_EFD_FORMAT; ASSERT(size >= sizeof(xfs_efd_log_format_t)); } - /* * Pinning has no meaning for an efd item, so just return. */ -/*ARGSUSED*/ STATIC void -xfs_efd_item_pin(xfs_efd_log_item_t *efdp) +xfs_efd_item_pin( + struct xfs_log_item *lip) { - return; } - /* * Since pinning has no meaning for an efd item, unpinning does * not either. */ -/*ARGSUSED*/ STATIC void -xfs_efd_item_unpin(xfs_efd_log_item_t *efdp, int remove) +xfs_efd_item_unpin( + struct xfs_log_item *lip, + int remove) { - return; } /* * Efd items have no locking, so just return success. */ -/*ARGSUSED*/ STATIC uint -xfs_efd_item_trylock(xfs_efd_log_item_t *efdp) +xfs_efd_item_trylock( + struct xfs_log_item *lip) { return XFS_ITEM_LOCKED; } @@ -415,13 +413,12 @@ xfs_efd_item_trylock(xfs_efd_log_item_t *efdp) * Efd items have no locking or pushing, so return failure * so that the caller doesn't bother with us. */ -/*ARGSUSED*/ STATIC void -xfs_efd_item_unlock(xfs_efd_log_item_t *efdp) +xfs_efd_item_unlock( + struct xfs_log_item *lip) { - if (efdp->efd_item.li_flags & XFS_LI_ABORTED) - xfs_efd_item_free(efdp); - return; + if (lip->li_flags & XFS_LI_ABORTED) + xfs_efd_item_free(EFD_ITEM(lip)); } /* @@ -431,15 +428,18 @@ xfs_efd_item_unlock(xfs_efd_log_item_t *efdp) * return -1 to keep the transaction code from further referencing * this item. */ -/*ARGSUSED*/ STATIC xfs_lsn_t -xfs_efd_item_committed(xfs_efd_log_item_t *efdp, xfs_lsn_t lsn) +xfs_efd_item_committed( + struct xfs_log_item *lip, + xfs_lsn_t lsn) { + struct xfs_efd_log_item *efdp = EFD_ITEM(lip); + /* * If we got a log I/O error, it's always the case that the LR with the * EFI got unpinned and freed before the EFD got aborted. */ - if ((efdp->efd_item.li_flags & XFS_LI_ABORTED) == 0) + if (!(lip->li_flags & XFS_LI_ABORTED)) xfs_efi_release(efdp->efd_efip, efdp->efd_format.efd_nextents); xfs_efd_item_free(efdp); @@ -450,11 +450,10 @@ xfs_efd_item_committed(xfs_efd_log_item_t *efdp, xfs_lsn_t lsn) * There isn't much you can do to push on an efd item. It is simply * stuck waiting for the log to be flushed to disk. */ -/*ARGSUSED*/ STATIC void -xfs_efd_item_push(xfs_efd_log_item_t *efdp) +xfs_efd_item_push( + struct xfs_log_item *lip) { - return; } /* @@ -464,53 +463,48 @@ xfs_efd_item_push(xfs_efd_log_item_t *efdp) * example, for inodes, the inode is locked throughout the extent freeing * so the dependency should be recorded there. */ -/*ARGSUSED*/ STATIC void -xfs_efd_item_committing(xfs_efd_log_item_t *efip, xfs_lsn_t lsn) +xfs_efd_item_committing( + struct xfs_log_item *lip, + xfs_lsn_t lsn) { - return; } /* * This is the ops vector shared by all efd log items. */ static struct xfs_item_ops xfs_efd_item_ops = { - .iop_size = (uint(*)(xfs_log_item_t*))xfs_efd_item_size, - .iop_format = (void(*)(xfs_log_item_t*, xfs_log_iovec_t*)) - xfs_efd_item_format, - .iop_pin = (void(*)(xfs_log_item_t*))xfs_efd_item_pin, - .iop_unpin = (void(*)(xfs_log_item_t*, int))xfs_efd_item_unpin, - .iop_trylock = (uint(*)(xfs_log_item_t*))xfs_efd_item_trylock, - .iop_unlock = (void(*)(xfs_log_item_t*))xfs_efd_item_unlock, - .iop_committed = (xfs_lsn_t(*)(xfs_log_item_t*, xfs_lsn_t)) - xfs_efd_item_committed, - .iop_push = (void(*)(xfs_log_item_t*))xfs_efd_item_push, - .iop_pushbuf = NULL, - .iop_committing = (void(*)(xfs_log_item_t*, xfs_lsn_t)) - xfs_efd_item_committing + .iop_size = xfs_efd_item_size, + .iop_format = xfs_efd_item_format, + .iop_pin = xfs_efd_item_pin, + .iop_unpin = xfs_efd_item_unpin, + .iop_trylock = xfs_efd_item_trylock, + .iop_unlock = xfs_efd_item_unlock, + .iop_committed = xfs_efd_item_committed, + .iop_push = xfs_efd_item_push, + .iop_committing = xfs_efd_item_committing }; - /* * Allocate and initialize an efd item with the given number of extents. */ -xfs_efd_log_item_t * -xfs_efd_init(xfs_mount_t *mp, - xfs_efi_log_item_t *efip, - uint nextents) +struct xfs_efd_log_item * +xfs_efd_init( + struct xfs_mount *mp, + struct xfs_efi_log_item *efip, + uint nextents) { - xfs_efd_log_item_t *efdp; + struct xfs_efd_log_item *efdp; uint size; ASSERT(nextents > 0); if (nextents > XFS_EFD_MAX_FAST_EXTENTS) { size = (uint)(sizeof(xfs_efd_log_item_t) + ((nextents - 1) * sizeof(xfs_extent_t))); - efdp = (xfs_efd_log_item_t*)kmem_zalloc(size, KM_SLEEP); + efdp = kmem_zalloc(size, KM_SLEEP); } else { - efdp = (xfs_efd_log_item_t*)kmem_zone_zalloc(xfs_efd_zone, - KM_SLEEP); + efdp = kmem_zone_zalloc(xfs_efd_zone, KM_SLEEP); } xfs_log_item_init(mp, &efdp->efd_item, XFS_LI_EFD, &xfs_efd_item_ops); @@ -518,5 +512,5 @@ xfs_efd_init(xfs_mount_t *mp, efdp->efd_format.efd_nextents = nextents; efdp->efd_format.efd_efi_id = efip->efi_format.efi_id; - return (efdp); + return efdp; } diff --git a/fs/xfs/xfs_inode_item.c b/fs/xfs/xfs_inode_item.c index a01990dbb945..2626aaca42f2 100644 --- a/fs/xfs/xfs_inode_item.c +++ b/fs/xfs/xfs_inode_item.c @@ -36,6 +36,12 @@ kmem_zone_t *xfs_ili_zone; /* inode log item zone */ +static inline struct xfs_inode_log_item *INODE_ITEM(struct xfs_log_item *lip) +{ + return container_of(lip, struct xfs_inode_log_item, ili_item); +} + + /* * This returns the number of iovecs needed to log the given inode item. * @@ -45,13 +51,11 @@ kmem_zone_t *xfs_ili_zone; /* inode log item zone */ */ STATIC uint xfs_inode_item_size( - xfs_inode_log_item_t *iip) + struct xfs_log_item *lip) { - uint nvecs; - xfs_inode_t *ip; - - ip = iip->ili_inode; - nvecs = 2; + struct xfs_inode_log_item *iip = INODE_ITEM(lip); + struct xfs_inode *ip = iip->ili_inode; + uint nvecs = 2; /* * Only log the data/extents/b-tree root if there is something @@ -202,20 +206,17 @@ xfs_inode_item_size( */ STATIC void xfs_inode_item_format( - xfs_inode_log_item_t *iip, - xfs_log_iovec_t *log_vector) + struct xfs_log_item *lip, + struct xfs_log_iovec *vecp) { + struct xfs_inode_log_item *iip = INODE_ITEM(lip); + struct xfs_inode *ip = iip->ili_inode; uint nvecs; - xfs_log_iovec_t *vecp; - xfs_inode_t *ip; size_t data_bytes; xfs_bmbt_rec_t *ext_buffer; int nrecs; xfs_mount_t *mp; - ip = iip->ili_inode; - vecp = log_vector; - vecp->i_addr = (xfs_caddr_t)&iip->ili_format; vecp->i_len = sizeof(xfs_inode_log_format_t); vecp->i_type = XLOG_REG_TYPE_IFORMAT; @@ -427,7 +428,7 @@ xfs_inode_item_format( * Assert that no attribute-related log flags are set. */ if (!XFS_IFORK_Q(ip)) { - ASSERT(nvecs == iip->ili_item.li_desc->lid_size); + ASSERT(nvecs == lip->li_desc->lid_size); iip->ili_format.ilf_size = nvecs; ASSERT(!(iip->ili_format.ilf_fields & (XFS_ILOG_ADATA | XFS_ILOG_ABROOT | XFS_ILOG_AEXT))); @@ -518,7 +519,7 @@ xfs_inode_item_format( break; } - ASSERT(nvecs == iip->ili_item.li_desc->lid_size); + ASSERT(nvecs == lip->li_desc->lid_size); iip->ili_format.ilf_size = nvecs; } @@ -529,12 +530,14 @@ xfs_inode_item_format( */ STATIC void xfs_inode_item_pin( - xfs_inode_log_item_t *iip) + struct xfs_log_item *lip) { - ASSERT(xfs_isilocked(iip->ili_inode, XFS_ILOCK_EXCL)); + struct xfs_inode *ip = INODE_ITEM(lip)->ili_inode; - trace_xfs_inode_pin(iip->ili_inode, _RET_IP_); - atomic_inc(&iip->ili_inode->i_pincount); + ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL)); + + trace_xfs_inode_pin(ip, _RET_IP_); + atomic_inc(&ip->i_pincount); } @@ -546,10 +549,10 @@ xfs_inode_item_pin( */ STATIC void xfs_inode_item_unpin( - xfs_inode_log_item_t *iip, + struct xfs_log_item *lip, int remove) { - struct xfs_inode *ip = iip->ili_inode; + struct xfs_inode *ip = INODE_ITEM(lip)->ili_inode; trace_xfs_inode_unpin(ip, _RET_IP_); ASSERT(atomic_read(&ip->i_pincount) > 0); @@ -572,19 +575,16 @@ xfs_inode_item_unpin( */ STATIC uint xfs_inode_item_trylock( - xfs_inode_log_item_t *iip) + struct xfs_log_item *lip) { - register xfs_inode_t *ip; - - ip = iip->ili_inode; + struct xfs_inode_log_item *iip = INODE_ITEM(lip); + struct xfs_inode *ip = iip->ili_inode; - if (xfs_ipincount(ip) > 0) { + if (xfs_ipincount(ip) > 0) return XFS_ITEM_PINNED; - } - if (!xfs_ilock_nowait(ip, XFS_ILOCK_SHARED)) { + if (!xfs_ilock_nowait(ip, XFS_ILOCK_SHARED)) return XFS_ITEM_LOCKED; - } if (!xfs_iflock_nowait(ip)) { /* @@ -610,7 +610,7 @@ xfs_inode_item_trylock( if (!XFS_FORCED_SHUTDOWN(ip->i_mount)) { ASSERT(iip->ili_format.ilf_fields != 0); ASSERT(iip->ili_logged == 0); - ASSERT(iip->ili_item.li_flags & XFS_LI_IN_AIL); + ASSERT(lip->li_flags & XFS_LI_IN_AIL); } #endif return XFS_ITEM_SUCCESS; @@ -624,12 +624,13 @@ xfs_inode_item_trylock( */ STATIC void xfs_inode_item_unlock( - xfs_inode_log_item_t *iip) + struct xfs_log_item *lip) { - uint hold; - uint iolocked; - uint lock_flags; - xfs_inode_t *ip; + struct xfs_inode_log_item *iip = INODE_ITEM(lip); + struct xfs_inode *ip = iip->ili_inode; + uint hold; + uint iolocked; + uint lock_flags; ASSERT(iip != NULL); ASSERT(iip->ili_inode->i_itemp != NULL); @@ -640,10 +641,10 @@ xfs_inode_item_unlock( ASSERT((!(iip->ili_inode->i_itemp->ili_flags & XFS_ILI_IOLOCKED_SHARED)) || xfs_isilocked(iip->ili_inode, XFS_IOLOCK_SHARED)); + /* * Clear the transaction pointer in the inode. */ - ip = iip->ili_inode; ip->i_transp = NULL; /* @@ -706,13 +707,12 @@ xfs_inode_item_unlock( * is the only one that matters. Therefore, simply return the * given lsn. */ -/*ARGSUSED*/ STATIC xfs_lsn_t xfs_inode_item_committed( - xfs_inode_log_item_t *iip, + struct xfs_log_item *lip, xfs_lsn_t lsn) { - return (lsn); + return lsn; } /* @@ -724,13 +724,12 @@ xfs_inode_item_committed( */ STATIC void xfs_inode_item_pushbuf( - xfs_inode_log_item_t *iip) + struct xfs_log_item *lip) { - xfs_inode_t *ip; - xfs_mount_t *mp; - xfs_buf_t *bp; + struct xfs_inode_log_item *iip = INODE_ITEM(lip); + struct xfs_inode *ip = iip->ili_inode; + struct xfs_buf *bp; - ip = iip->ili_inode; ASSERT(xfs_isilocked(ip, XFS_ILOCK_SHARED)); /* @@ -738,14 +737,13 @@ xfs_inode_item_pushbuf( * inode was taken off the AIL. So, just get out. */ if (completion_done(&ip->i_flush) || - ((iip->ili_item.li_flags & XFS_LI_IN_AIL) == 0)) { + !(lip->li_flags & XFS_LI_IN_AIL)) { xfs_iunlock(ip, XFS_ILOCK_SHARED); return; } - mp = ip->i_mount; - bp = xfs_incore(mp->m_ddev_targp, iip->ili_format.ilf_blkno, - iip->ili_format.ilf_len, XBF_TRYLOCK); + bp = xfs_incore(ip->i_mount->m_ddev_targp, iip->ili_format.ilf_blkno, + iip->ili_format.ilf_len, XBF_TRYLOCK); xfs_iunlock(ip, XFS_ILOCK_SHARED); if (!bp) @@ -753,10 +751,8 @@ xfs_inode_item_pushbuf( if (XFS_BUF_ISDELAYWRITE(bp)) xfs_buf_delwri_promote(bp); xfs_buf_relse(bp); - return; } - /* * This is called to asynchronously write the inode associated with this * inode log item out to disk. The inode will already have been locked by @@ -764,14 +760,14 @@ xfs_inode_item_pushbuf( */ STATIC void xfs_inode_item_push( - xfs_inode_log_item_t *iip) + struct xfs_log_item *lip) { - xfs_inode_t *ip; - - ip = iip->ili_inode; + struct xfs_inode_log_item *iip = INODE_ITEM(lip); + struct xfs_inode *ip = iip->ili_inode; ASSERT(xfs_isilocked(ip, XFS_ILOCK_SHARED)); ASSERT(!completion_done(&ip->i_flush)); + /* * Since we were able to lock the inode's flush lock and * we found it on the AIL, the inode must be dirty. This @@ -794,41 +790,34 @@ xfs_inode_item_push( */ (void) xfs_iflush(ip, 0); xfs_iunlock(ip, XFS_ILOCK_SHARED); - - return; } /* * XXX rcc - this one really has to do something. Probably needs * to stamp in a new field in the incore inode. */ -/* ARGSUSED */ STATIC void xfs_inode_item_committing( - xfs_inode_log_item_t *iip, + struct xfs_log_item *lip, xfs_lsn_t lsn) { - iip->ili_last_lsn = lsn; - return; + INODE_ITEM(lip)->ili_last_lsn = lsn; } /* * This is the ops vector shared by all buf log items. */ static struct xfs_item_ops xfs_inode_item_ops = { - .iop_size = (uint(*)(xfs_log_item_t*))xfs_inode_item_size, - .iop_format = (void(*)(xfs_log_item_t*, xfs_log_iovec_t*)) - xfs_inode_item_format, - .iop_pin = (void(*)(xfs_log_item_t*))xfs_inode_item_pin, - .iop_unpin = (void(*)(xfs_log_item_t*, int))xfs_inode_item_unpin, - .iop_trylock = (uint(*)(xfs_log_item_t*))xfs_inode_item_trylock, - .iop_unlock = (void(*)(xfs_log_item_t*))xfs_inode_item_unlock, - .iop_committed = (xfs_lsn_t(*)(xfs_log_item_t*, xfs_lsn_t)) - xfs_inode_item_committed, - .iop_push = (void(*)(xfs_log_item_t*))xfs_inode_item_push, - .iop_pushbuf = (void(*)(xfs_log_item_t*))xfs_inode_item_pushbuf, - .iop_committing = (void(*)(xfs_log_item_t*, xfs_lsn_t)) - xfs_inode_item_committing + .iop_size = xfs_inode_item_size, + .iop_format = xfs_inode_item_format, + .iop_pin = xfs_inode_item_pin, + .iop_unpin = xfs_inode_item_unpin, + .iop_trylock = xfs_inode_item_trylock, + .iop_unlock = xfs_inode_item_unlock, + .iop_committed = xfs_inode_item_committed, + .iop_push = xfs_inode_item_push, + .iop_pushbuf = xfs_inode_item_pushbuf, + .iop_committing = xfs_inode_item_committing }; @@ -837,10 +826,10 @@ static struct xfs_item_ops xfs_inode_item_ops = { */ void xfs_inode_item_init( - xfs_inode_t *ip, - xfs_mount_t *mp) + struct xfs_inode *ip, + struct xfs_mount *mp) { - xfs_inode_log_item_t *iip; + struct xfs_inode_log_item *iip; ASSERT(ip->i_itemp == NULL); iip = ip->i_itemp = kmem_zone_zalloc(xfs_ili_zone, KM_SLEEP); -- cgit v1.2.3 From ca30b2a7b7ac899ac4da6030ccbebf2f137b8e6d Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 23 Jun 2010 18:11:15 +1000 Subject: xfs: give li_cb callbacks the correct prototype Stop the function pointer casting madness and give all the li_cb instances correct prototype. Signed-off-by: Christoph Hellwig Reviewed-by: Dave Chinner --- fs/xfs/quota/xfs_dquot.c | 91 +++++++++++++++++++++++------------------------- fs/xfs/xfs_buf_item.c | 13 ++++--- fs/xfs/xfs_buf_item.h | 2 +- fs/xfs/xfs_inode.c | 10 +++--- fs/xfs/xfs_inode_item.c | 23 ++++++------ fs/xfs/xfs_inode_item.h | 4 +-- fs/xfs/xfs_trans_buf.c | 7 ++-- 7 files changed, 68 insertions(+), 82 deletions(-) (limited to 'fs') diff --git a/fs/xfs/quota/xfs_dquot.c b/fs/xfs/quota/xfs_dquot.c index f152af8cfab0..6526e87cade0 100644 --- a/fs/xfs/quota/xfs_dquot.c +++ b/fs/xfs/quota/xfs_dquot.c @@ -54,8 +54,6 @@ flush lock - ditto. */ -STATIC void xfs_qm_dqflush_done(xfs_buf_t *, xfs_dq_logitem_t *); - #ifdef DEBUG xfs_buftarg_t *xfs_dqerror_target; int xfs_do_dqerror; @@ -1131,6 +1129,46 @@ xfs_qm_dqrele( xfs_qm_dqput(dqp); } +/* + * This is the dquot flushing I/O completion routine. It is called + * from interrupt level when the buffer containing the dquot is + * flushed to disk. It is responsible for removing the dquot logitem + * from the AIL if it has not been re-logged, and unlocking the dquot's + * flush lock. This behavior is very similar to that of inodes.. + */ +STATIC void +xfs_qm_dqflush_done( + struct xfs_buf *bp, + struct xfs_log_item *lip) +{ + xfs_dq_logitem_t *qip = (struct xfs_dq_logitem *)lip; + xfs_dquot_t *dqp = qip->qli_dquot; + struct xfs_ail *ailp = lip->li_ailp; + + /* + * We only want to pull the item from the AIL if its + * location in the log has not changed since we started the flush. + * Thus, we only bother if the dquot's lsn has + * not changed. First we check the lsn outside the lock + * since it's cheaper, and then we recheck while + * holding the lock before removing the dquot from the AIL. + */ + if ((lip->li_flags & XFS_LI_IN_AIL) && + lip->li_lsn == qip->qli_flush_lsn) { + + /* xfs_trans_ail_delete() drops the AIL lock. */ + spin_lock(&ailp->xa_lock); + if (lip->li_lsn == qip->qli_flush_lsn) + xfs_trans_ail_delete(ailp, lip); + else + spin_unlock(&ailp->xa_lock); + } + + /* + * Release the dq's flush lock since we're done with it. + */ + xfs_dqfunlock(dqp); +} /* * Write a modified dquot to disk. @@ -1212,8 +1250,9 @@ xfs_qm_dqflush( * Attach an iodone routine so that we can remove this dquot from the * AIL and release the flush lock once the dquot is synced to disk. */ - xfs_buf_attach_iodone(bp, (void(*)(xfs_buf_t *, xfs_log_item_t *)) - xfs_qm_dqflush_done, &(dqp->q_logitem.qli_item)); + xfs_buf_attach_iodone(bp, xfs_qm_dqflush_done, + &dqp->q_logitem.qli_item); + /* * If the buffer is pinned then push on the log so we won't * get stuck waiting in the write for too long. @@ -1237,50 +1276,6 @@ xfs_qm_dqflush( } -/* - * This is the dquot flushing I/O completion routine. It is called - * from interrupt level when the buffer containing the dquot is - * flushed to disk. It is responsible for removing the dquot logitem - * from the AIL if it has not been re-logged, and unlocking the dquot's - * flush lock. This behavior is very similar to that of inodes.. - */ -/*ARGSUSED*/ -STATIC void -xfs_qm_dqflush_done( - xfs_buf_t *bp, - xfs_dq_logitem_t *qip) -{ - xfs_dquot_t *dqp; - struct xfs_ail *ailp; - - dqp = qip->qli_dquot; - ailp = qip->qli_item.li_ailp; - - /* - * We only want to pull the item from the AIL if its - * location in the log has not changed since we started the flush. - * Thus, we only bother if the dquot's lsn has - * not changed. First we check the lsn outside the lock - * since it's cheaper, and then we recheck while - * holding the lock before removing the dquot from the AIL. - */ - if ((qip->qli_item.li_flags & XFS_LI_IN_AIL) && - qip->qli_item.li_lsn == qip->qli_flush_lsn) { - - /* xfs_trans_ail_delete() drops the AIL lock. */ - spin_lock(&ailp->xa_lock); - if (qip->qli_item.li_lsn == qip->qli_flush_lsn) - xfs_trans_ail_delete(ailp, (xfs_log_item_t*)qip); - else - spin_unlock(&ailp->xa_lock); - } - - /* - * Release the dq's flush lock since we're done with it. - */ - xfs_dqfunlock(dqp); -} - int xfs_qm_dqlock_nowait( xfs_dquot_t *dqp) diff --git a/fs/xfs/xfs_buf_item.c b/fs/xfs/xfs_buf_item.c index 992d6be101cb..60e063d96f8d 100644 --- a/fs/xfs/xfs_buf_item.c +++ b/fs/xfs/xfs_buf_item.c @@ -1079,15 +1079,14 @@ xfs_buf_error_relse( * It is called by xfs_buf_iodone_callbacks() above which will take * care of cleaning up the buffer itself. */ -/* ARGSUSED */ void xfs_buf_iodone( - xfs_buf_t *bp, - xfs_buf_log_item_t *bip) + struct xfs_buf *bp, + struct xfs_log_item *lip) { - struct xfs_ail *ailp = bip->bli_item.li_ailp; + struct xfs_ail *ailp = lip->li_ailp; - ASSERT(bip->bli_buf == bp); + ASSERT(BUF_ITEM(lip)->bli_buf == bp); xfs_buf_rele(bp); @@ -1101,6 +1100,6 @@ xfs_buf_iodone( * Either way, AIL is useless if we're forcing a shutdown. */ spin_lock(&ailp->xa_lock); - xfs_trans_ail_delete(ailp, (xfs_log_item_t *)bip); - xfs_buf_item_free(bip); + xfs_trans_ail_delete(ailp, lip); + xfs_buf_item_free(BUF_ITEM(lip)); } diff --git a/fs/xfs/xfs_buf_item.h b/fs/xfs/xfs_buf_item.h index f20bb472d582..0e2ed43f16c7 100644 --- a/fs/xfs/xfs_buf_item.h +++ b/fs/xfs/xfs_buf_item.h @@ -124,7 +124,7 @@ void xfs_buf_attach_iodone(struct xfs_buf *, void(*)(struct xfs_buf *, xfs_log_item_t *), xfs_log_item_t *); void xfs_buf_iodone_callbacks(struct xfs_buf *); -void xfs_buf_iodone(struct xfs_buf *, xfs_buf_log_item_t *); +void xfs_buf_iodone(struct xfs_buf *, struct xfs_log_item *); #ifdef XFS_TRANS_DEBUG void diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index dde2e5dc6c62..c7c48da97ad4 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -1981,7 +1981,7 @@ xfs_ifree_cluster( if (lip->li_type == XFS_LI_INODE) { iip = (xfs_inode_log_item_t *)lip; ASSERT(iip->ili_logged == 1); - lip->li_cb = (void(*)(xfs_buf_t*,xfs_log_item_t*)) xfs_istale_done; + lip->li_cb = xfs_istale_done; xfs_trans_ail_copy_lsn(mp->m_ail, &iip->ili_flush_lsn, &iip->ili_item.li_lsn); @@ -2051,9 +2051,8 @@ xfs_ifree_cluster( xfs_trans_ail_copy_lsn(mp->m_ail, &iip->ili_flush_lsn, &iip->ili_item.li_lsn); - xfs_buf_attach_iodone(bp, - (void(*)(xfs_buf_t*,xfs_log_item_t*)) - xfs_istale_done, (xfs_log_item_t *)iip); + xfs_buf_attach_iodone(bp, xfs_istale_done, + &iip->ili_item); if (ip != free_ip) xfs_iunlock(ip, XFS_ILOCK_EXCL); @@ -3065,8 +3064,7 @@ xfs_iflush_int( * and unlock the inode's flush lock when the inode is * completely written to disk. */ - xfs_buf_attach_iodone(bp, (void(*)(xfs_buf_t*,xfs_log_item_t*)) - xfs_iflush_done, (xfs_log_item_t *)iip); + xfs_buf_attach_iodone(bp, xfs_iflush_done, &iip->ili_item); ASSERT(XFS_BUF_FSPRIVATE(bp, void *) != NULL); ASSERT(XFS_BUF_IODONE_FUNC(bp) != NULL); diff --git a/fs/xfs/xfs_inode_item.c b/fs/xfs/xfs_inode_item.c index 2626aaca42f2..c7e70d708345 100644 --- a/fs/xfs/xfs_inode_item.c +++ b/fs/xfs/xfs_inode_item.c @@ -867,14 +867,14 @@ xfs_inode_item_destroy( * from the AIL if it has not been re-logged, and unlocking the inode's * flush lock. */ -/*ARGSUSED*/ void xfs_iflush_done( - xfs_buf_t *bp, - xfs_inode_log_item_t *iip) + struct xfs_buf *bp, + struct xfs_log_item *lip) { + struct xfs_inode_log_item *iip = INODE_ITEM(lip); xfs_inode_t *ip = iip->ili_inode; - struct xfs_ail *ailp = iip->ili_item.li_ailp; + struct xfs_ail *ailp = lip->li_ailp; /* * We only want to pull the item from the AIL if it is @@ -885,12 +885,11 @@ xfs_iflush_done( * the lock since it's cheaper, and then we recheck while * holding the lock before removing the inode from the AIL. */ - if (iip->ili_logged && - (iip->ili_item.li_lsn == iip->ili_flush_lsn)) { + if (iip->ili_logged && lip->li_lsn == iip->ili_flush_lsn) { spin_lock(&ailp->xa_lock); - if (iip->ili_item.li_lsn == iip->ili_flush_lsn) { + if (lip->li_lsn == iip->ili_flush_lsn) { /* xfs_trans_ail_delete() drops the AIL lock. */ - xfs_trans_ail_delete(ailp, (xfs_log_item_t*)iip); + xfs_trans_ail_delete(ailp, lip); } else { spin_unlock(&ailp->xa_lock); } @@ -908,8 +907,6 @@ xfs_iflush_done( * Release the inode's flush lock since we're done with it. */ xfs_ifunlock(ip); - - return; } /* @@ -959,10 +956,10 @@ xfs_iflush_abort( void xfs_istale_done( - xfs_buf_t *bp, - xfs_inode_log_item_t *iip) + struct xfs_buf *bp, + struct xfs_log_item *lip) { - xfs_iflush_abort(iip->ili_inode); + xfs_iflush_abort(INODE_ITEM(lip)->ili_inode); } /* diff --git a/fs/xfs/xfs_inode_item.h b/fs/xfs/xfs_inode_item.h index 9a467958ecdd..b6a97ff1c3ab 100644 --- a/fs/xfs/xfs_inode_item.h +++ b/fs/xfs/xfs_inode_item.h @@ -161,8 +161,8 @@ static inline int xfs_inode_clean(xfs_inode_t *ip) extern void xfs_inode_item_init(struct xfs_inode *, struct xfs_mount *); extern void xfs_inode_item_destroy(struct xfs_inode *); -extern void xfs_iflush_done(struct xfs_buf *, xfs_inode_log_item_t *); -extern void xfs_istale_done(struct xfs_buf *, xfs_inode_log_item_t *); +extern void xfs_iflush_done(struct xfs_buf *, struct xfs_log_item *); +extern void xfs_istale_done(struct xfs_buf *, struct xfs_log_item *); extern void xfs_iflush_abort(struct xfs_inode *); extern int xfs_inode_item_format_convert(xfs_log_iovec_t *, xfs_inode_log_format_t *); diff --git a/fs/xfs/xfs_trans_buf.c b/fs/xfs/xfs_trans_buf.c index 74a1c33e4098..90af025e6839 100644 --- a/fs/xfs/xfs_trans_buf.c +++ b/fs/xfs/xfs_trans_buf.c @@ -658,7 +658,7 @@ xfs_trans_log_buf(xfs_trans_t *tp, bip = XFS_BUF_FSPRIVATE(bp, xfs_buf_log_item_t *); ASSERT(atomic_read(&bip->bli_refcount) > 0); XFS_BUF_SET_IODONE_FUNC(bp, xfs_buf_iodone_callbacks); - bip->bli_item.li_cb = (void(*)(xfs_buf_t*,xfs_log_item_t*))xfs_buf_iodone; + bip->bli_item.li_cb = xfs_buf_iodone; trace_xfs_trans_log_buf(bip); @@ -815,12 +815,9 @@ xfs_trans_stale_inode_buf( ASSERT(atomic_read(&bip->bli_refcount) > 0); bip->bli_flags |= XFS_BLI_STALE_INODE; - bip->bli_item.li_cb = (void(*)(xfs_buf_t*,xfs_log_item_t*)) - xfs_buf_iodone; + bip->bli_item.li_cb = xfs_buf_iodone; } - - /* * Mark the buffer as being one which contains newly allocated * inodes. We need to make sure that even if this buffer is -- cgit v1.2.3 From 4d16e9246fc3b3cf7bc95609eff66929a39daa06 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 23 Jun 2010 18:11:15 +1000 Subject: xfs: simplify buffer pinning Get rid of the xfs_buf_pin/xfs_buf_unpin/xfs_buf_ispin helpers and opencode them in their only callers, just like we did for the inode pinning a while ago. Also remove duplicate trace points - the bufitem tracepoints cover all the information that is present in a buffer tracepoint. Signed-off-by: Christoph Hellwig Reviewed-by: Dave Chinner --- fs/xfs/linux-2.6/xfs_buf.c | 32 +------------------------------- fs/xfs/linux-2.6/xfs_buf.h | 9 +-------- fs/xfs/linux-2.6/xfs_trace.h | 2 -- fs/xfs/xfs_buf_item.c | 13 +++++++------ 4 files changed, 9 insertions(+), 47 deletions(-) (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_buf.c b/fs/xfs/linux-2.6/xfs_buf.c index 4b2177f5b223..efce8abb375c 100644 --- a/fs/xfs/linux-2.6/xfs_buf.c +++ b/fs/xfs/linux-2.6/xfs_buf.c @@ -896,36 +896,6 @@ xfs_buf_unlock( trace_xfs_buf_unlock(bp, _RET_IP_); } - -/* - * Pinning Buffer Storage in Memory - * Ensure that no attempt to force a buffer to disk will succeed. - */ -void -xfs_buf_pin( - xfs_buf_t *bp) -{ - trace_xfs_buf_pin(bp, _RET_IP_); - atomic_inc(&bp->b_pin_count); -} - -void -xfs_buf_unpin( - xfs_buf_t *bp) -{ - trace_xfs_buf_unpin(bp, _RET_IP_); - - if (atomic_dec_and_test(&bp->b_pin_count)) - wake_up_all(&bp->b_waiters); -} - -int -xfs_buf_ispin( - xfs_buf_t *bp) -{ - return atomic_read(&bp->b_pin_count); -} - STATIC void xfs_buf_wait_unpin( xfs_buf_t *bp) @@ -1803,7 +1773,7 @@ xfs_buf_delwri_split( trace_xfs_buf_delwri_split(bp, _RET_IP_); ASSERT(bp->b_flags & XBF_DELWRI); - if (!xfs_buf_ispin(bp) && !xfs_buf_cond_lock(bp)) { + if (!XFS_BUF_ISPINNED(bp) && !xfs_buf_cond_lock(bp)) { if (!force && time_before(jiffies, bp->b_queuetime + age)) { xfs_buf_unlock(bp); diff --git a/fs/xfs/linux-2.6/xfs_buf.h b/fs/xfs/linux-2.6/xfs_buf.h index 5fbecefa5dfd..2a9749a3a762 100644 --- a/fs/xfs/linux-2.6/xfs_buf.h +++ b/fs/xfs/linux-2.6/xfs_buf.h @@ -258,11 +258,6 @@ static inline int xfs_buf_geterror(xfs_buf_t *bp) /* Buffer Utility Routines */ extern xfs_caddr_t xfs_buf_offset(xfs_buf_t *, size_t); -/* Pinning Buffer Storage in Memory */ -extern void xfs_buf_pin(xfs_buf_t *); -extern void xfs_buf_unpin(xfs_buf_t *); -extern int xfs_buf_ispin(xfs_buf_t *); - /* Delayed Write Buffer Routines */ extern void xfs_buf_delwri_dequeue(xfs_buf_t *); extern void xfs_buf_delwri_promote(xfs_buf_t *); @@ -351,7 +346,7 @@ extern void xfs_buf_terminate(void); #define XFS_BUF_SET_VTYPE(bp, type) do { } while (0) #define XFS_BUF_SET_REF(bp, ref) do { } while (0) -#define XFS_BUF_ISPINNED(bp) xfs_buf_ispin(bp) +#define XFS_BUF_ISPINNED(bp) atomic_read(&((bp)->b_pin_count)) #define XFS_BUF_VALUSEMA(bp) xfs_buf_lock_value(bp) #define XFS_BUF_CPSEMA(bp) (xfs_buf_cond_lock(bp) == 0) @@ -370,8 +365,6 @@ static inline void xfs_buf_relse(xfs_buf_t *bp) xfs_buf_rele(bp); } -#define xfs_bpin(bp) xfs_buf_pin(bp) -#define xfs_bunpin(bp) xfs_buf_unpin(bp) #define xfs_biodone(bp) xfs_buf_ioend(bp, 0) #define xfs_biomove(bp, off, len, data, rw) \ diff --git a/fs/xfs/linux-2.6/xfs_trace.h b/fs/xfs/linux-2.6/xfs_trace.h index 302820690904..0aea6d5f705a 100644 --- a/fs/xfs/linux-2.6/xfs_trace.h +++ b/fs/xfs/linux-2.6/xfs_trace.h @@ -317,8 +317,6 @@ DEFINE_BUF_EVENT(xfs_buf_init); DEFINE_BUF_EVENT(xfs_buf_free); DEFINE_BUF_EVENT(xfs_buf_hold); DEFINE_BUF_EVENT(xfs_buf_rele); -DEFINE_BUF_EVENT(xfs_buf_pin); -DEFINE_BUF_EVENT(xfs_buf_unpin); DEFINE_BUF_EVENT(xfs_buf_iodone); DEFINE_BUF_EVENT(xfs_buf_iorequest); DEFINE_BUF_EVENT(xfs_buf_bawrite); diff --git a/fs/xfs/xfs_buf_item.c b/fs/xfs/xfs_buf_item.c index 60e063d96f8d..f53327a19e0d 100644 --- a/fs/xfs/xfs_buf_item.c +++ b/fs/xfs/xfs_buf_item.c @@ -359,14 +359,13 @@ xfs_buf_item_format( /* * This is called to pin the buffer associated with the buf log item in memory - * so it cannot be written out. Simply call bpin() on the buffer to do this. + * so it cannot be written out. * * We also always take a reference to the buffer log item here so that the bli * is held while the item is pinned in memory. This means that we can * unconditionally drop the reference count a transaction holds when the * transaction is completed. */ - STATIC void xfs_buf_item_pin( struct xfs_log_item *lip) @@ -378,15 +377,15 @@ xfs_buf_item_pin( ASSERT((bip->bli_flags & XFS_BLI_LOGGED) || (bip->bli_flags & XFS_BLI_STALE)); - atomic_inc(&bip->bli_refcount); trace_xfs_buf_item_pin(bip); - xfs_bpin(bip->bli_buf); + + atomic_inc(&bip->bli_refcount); + atomic_inc(&bip->bli_buf->b_pin_count); } /* * This is called to unpin the buffer associated with the buf log * item which was previously pinned with a call to xfs_buf_item_pin(). - * Just call bunpin() on the buffer to do this. * * Also drop the reference to the buf item for the current transaction. * If the XFS_BLI_STALE flag is set and we are the last reference, @@ -414,7 +413,9 @@ xfs_buf_item_unpin( trace_xfs_buf_item_unpin(bip); freed = atomic_dec_and_test(&bip->bli_refcount); - xfs_bunpin(bp); + + if (atomic_dec_and_test(&bp->b_pin_count)) + wake_up_all(&bp->b_waiters); if (freed && stale) { ASSERT(bip->bli_flags & XFS_BLI_STALE); -- cgit v1.2.3 From 898621d5a72c6799a9a13fce20443b4b6699899c Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 24 Jun 2010 11:36:58 +1000 Subject: xfs: simplify inode to transaction joining Currently we need to either call IHOLD or xfs_trans_ihold on an inode when joining it to a transaction via xfs_trans_ijoin. This patches instead makes xfs_trans_ijoin usable on it's own by doing an implicity xfs_trans_ihold, which also allows us to drop the third argument. For the case where we want to hold a reference on the inode a xfs_trans_ijoin_ref wrapper is added which does the IHOLD and marks the inode for needing an xfs_iput. In addition to the cleaner interface to the caller this also simplifies the implementation. Signed-off-by: Christoph Hellwig Reviewed-by: Dave Chinner --- fs/xfs/linux-2.6/xfs_file.c | 3 +- fs/xfs/linux-2.6/xfs_ioctl.c | 3 +- fs/xfs/linux-2.6/xfs_super.c | 3 +- fs/xfs/linux-2.6/xfs_sync.c | 3 +- fs/xfs/quota/xfs_dquot.c | 9 +----- fs/xfs/xfs_attr.c | 75 +++++++++++++++----------------------------- fs/xfs/xfs_bmap.c | 5 +-- fs/xfs/xfs_dfrag.c | 7 ++--- fs/xfs/xfs_fsops.c | 3 +- fs/xfs/xfs_inode.c | 14 +++------ fs/xfs/xfs_inode_item.c | 42 +++---------------------- fs/xfs/xfs_inode_item.h | 8 +---- fs/xfs/xfs_iomap.c | 9 ++---- fs/xfs/xfs_rename.c | 26 +++++---------- fs/xfs/xfs_trans.c | 3 +- fs/xfs/xfs_trans.h | 4 +-- fs/xfs/xfs_trans_inode.c | 60 ++++++++++++++--------------------- fs/xfs/xfs_utils.c | 4 +-- fs/xfs/xfs_vnodeops.c | 69 +++++++++++----------------------------- 19 files changed, 103 insertions(+), 247 deletions(-) (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_file.c b/fs/xfs/linux-2.6/xfs_file.c index 8d26d93648b4..9a9b446a58a7 100644 --- a/fs/xfs/linux-2.6/xfs_file.c +++ b/fs/xfs/linux-2.6/xfs_file.c @@ -158,8 +158,7 @@ xfs_file_fsync( * transaction. So we play it safe and fire off the * transaction anyway. */ - xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL); - xfs_trans_ihold(tp, ip); + xfs_trans_ijoin(tp, ip); xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); xfs_trans_set_sync(tp); error = _xfs_trans_commit(tp, 0, &log_flushed); diff --git a/fs/xfs/linux-2.6/xfs_ioctl.c b/fs/xfs/linux-2.6/xfs_ioctl.c index 8aa54f0eec15..a12dddad126e 100644 --- a/fs/xfs/linux-2.6/xfs_ioctl.c +++ b/fs/xfs/linux-2.6/xfs_ioctl.c @@ -1034,8 +1034,7 @@ xfs_ioctl_setattr( } } - xfs_trans_ijoin(tp, ip, lock_flags); - xfs_trans_ihold(tp, ip); + xfs_trans_ijoin(tp, ip); /* * Change file ownership. Must be the owner or privileged. diff --git a/fs/xfs/linux-2.6/xfs_super.c b/fs/xfs/linux-2.6/xfs_super.c index 4b90e4b531b7..b8ad17e730b6 100644 --- a/fs/xfs/linux-2.6/xfs_super.c +++ b/fs/xfs/linux-2.6/xfs_super.c @@ -1023,8 +1023,7 @@ xfs_log_inode( * an inode in another recent transaction. So we play it safe and * fire off the transaction anyway. */ - xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL); - xfs_trans_ihold(tp, ip); + xfs_trans_ijoin(tp, ip); xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); xfs_trans_set_sync(tp); error = xfs_trans_commit(tp, 0); diff --git a/fs/xfs/linux-2.6/xfs_sync.c b/fs/xfs/linux-2.6/xfs_sync.c index 850b4198bf60..0283b88bc16c 100644 --- a/fs/xfs/linux-2.6/xfs_sync.c +++ b/fs/xfs/linux-2.6/xfs_sync.c @@ -362,8 +362,7 @@ xfs_commit_dummy_trans( xfs_ilock(ip, XFS_ILOCK_EXCL); - xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL); - xfs_trans_ihold(tp, ip); + xfs_trans_ijoin(tp, ip); xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); error = xfs_trans_commit(tp, 0); xfs_iunlock(ip, XFS_ILOCK_EXCL); diff --git a/fs/xfs/quota/xfs_dquot.c b/fs/xfs/quota/xfs_dquot.c index 6526e87cade0..56f366e327f3 100644 --- a/fs/xfs/quota/xfs_dquot.c +++ b/fs/xfs/quota/xfs_dquot.c @@ -378,14 +378,7 @@ xfs_qm_dqalloc( return (ESRCH); } - /* - * xfs_trans_commit normally decrements the vnode ref count - * when it unlocks the inode. Since we want to keep the quota - * inode around, we bump the vnode ref count now. - */ - IHOLD(quotip); - - xfs_trans_ijoin(tp, quotip, XFS_ILOCK_EXCL); + xfs_trans_ijoin_ref(tp, quotip, XFS_ILOCK_EXCL); nmaps = 1; if ((error = xfs_bmapi(tp, quotip, offset_fsb, XFS_DQUOT_CLUSTER_SIZE_FSB, diff --git a/fs/xfs/xfs_attr.c b/fs/xfs/xfs_attr.c index 8bde79785a75..f3ca7186155a 100644 --- a/fs/xfs/xfs_attr.c +++ b/fs/xfs/xfs_attr.c @@ -319,8 +319,7 @@ xfs_attr_set_int( return (error); } - xfs_trans_ijoin(args.trans, dp, XFS_ILOCK_EXCL); - xfs_trans_ihold(args.trans, dp); + xfs_trans_ijoin(args.trans, dp); /* * If the attribute list is non-existent or a shortform list, @@ -390,10 +389,8 @@ xfs_attr_set_int( * bmap_finish() may have committed the last trans and started * a new one. We need the inode to be in all transactions. */ - if (committed) { - xfs_trans_ijoin(args.trans, dp, XFS_ILOCK_EXCL); - xfs_trans_ihold(args.trans, dp); - } + if (committed) + xfs_trans_ijoin(args.trans, dp); /* * Commit the leaf transformation. We'll need another (linked) @@ -538,8 +535,7 @@ xfs_attr_remove_int(xfs_inode_t *dp, struct xfs_name *name, int flags) * No need to make quota reservations here. We expect to release some * blocks not allocate in the common case. */ - xfs_trans_ijoin(args.trans, dp, XFS_ILOCK_EXCL); - xfs_trans_ihold(args.trans, dp); + xfs_trans_ijoin(args.trans, dp); /* * Decide on what work routines to call based on the inode size. @@ -815,8 +811,7 @@ xfs_attr_inactive(xfs_inode_t *dp) * No need to make quota reservations here. We expect to release some * blocks, not allocate, in the common case. */ - xfs_trans_ijoin(trans, dp, XFS_ILOCK_EXCL); - xfs_trans_ihold(trans, dp); + xfs_trans_ijoin(trans, dp); /* * Decide on what work routines to call based on the inode size. @@ -975,10 +970,8 @@ xfs_attr_leaf_addname(xfs_da_args_t *args) * bmap_finish() may have committed the last trans and started * a new one. We need the inode to be in all transactions. */ - if (committed) { - xfs_trans_ijoin(args->trans, dp, XFS_ILOCK_EXCL); - xfs_trans_ihold(args->trans, dp); - } + if (committed) + xfs_trans_ijoin(args->trans, dp); /* * Commit the current trans (including the inode) and start @@ -1079,10 +1072,8 @@ xfs_attr_leaf_addname(xfs_da_args_t *args) * and started a new one. We need the inode to be * in all transactions. */ - if (committed) { - xfs_trans_ijoin(args->trans, dp, XFS_ILOCK_EXCL); - xfs_trans_ihold(args->trans, dp); - } + if (committed) + xfs_trans_ijoin(args->trans, dp); } else xfs_da_buf_done(bp); @@ -1155,10 +1146,8 @@ xfs_attr_leaf_removename(xfs_da_args_t *args) * bmap_finish() may have committed the last trans and started * a new one. We need the inode to be in all transactions. */ - if (committed) { - xfs_trans_ijoin(args->trans, dp, XFS_ILOCK_EXCL); - xfs_trans_ihold(args->trans, dp); - } + if (committed) + xfs_trans_ijoin(args->trans, dp); } else xfs_da_buf_done(bp); return(0); @@ -1311,10 +1300,8 @@ restart: * and started a new one. We need the inode to be * in all transactions. */ - if (committed) { - xfs_trans_ijoin(args->trans, dp, XFS_ILOCK_EXCL); - xfs_trans_ihold(args->trans, dp); - } + if (committed) + xfs_trans_ijoin(args->trans, dp); /* * Commit the node conversion and start the next @@ -1350,10 +1337,8 @@ restart: * bmap_finish() may have committed the last trans and started * a new one. We need the inode to be in all transactions. */ - if (committed) { - xfs_trans_ijoin(args->trans, dp, XFS_ILOCK_EXCL); - xfs_trans_ihold(args->trans, dp); - } + if (committed) + xfs_trans_ijoin(args->trans, dp); } else { /* * Addition succeeded, update Btree hashvals. @@ -1464,10 +1449,8 @@ restart: * and started a new one. We need the inode to be * in all transactions. */ - if (committed) { - xfs_trans_ijoin(args->trans, dp, XFS_ILOCK_EXCL); - xfs_trans_ihold(args->trans, dp); - } + if (committed) + xfs_trans_ijoin(args->trans, dp); } /* @@ -1598,10 +1581,8 @@ xfs_attr_node_removename(xfs_da_args_t *args) * bmap_finish() may have committed the last trans and started * a new one. We need the inode to be in all transactions. */ - if (committed) { - xfs_trans_ijoin(args->trans, dp, XFS_ILOCK_EXCL); - xfs_trans_ihold(args->trans, dp); - } + if (committed) + xfs_trans_ijoin(args->trans, dp); /* * Commit the Btree join operation and start a new trans. @@ -1652,10 +1633,8 @@ xfs_attr_node_removename(xfs_da_args_t *args) * and started a new one. We need the inode to be * in all transactions. */ - if (committed) { - xfs_trans_ijoin(args->trans, dp, XFS_ILOCK_EXCL); - xfs_trans_ihold(args->trans, dp); - } + if (committed) + xfs_trans_ijoin(args->trans, dp); } else xfs_da_brelse(args->trans, bp); } @@ -2093,10 +2072,8 @@ xfs_attr_rmtval_set(xfs_da_args_t *args) * bmap_finish() may have committed the last trans and started * a new one. We need the inode to be in all transactions. */ - if (committed) { - xfs_trans_ijoin(args->trans, dp, XFS_ILOCK_EXCL); - xfs_trans_ihold(args->trans, dp); - } + if (committed) + xfs_trans_ijoin(args->trans, dp); ASSERT(nmap == 1); ASSERT((map.br_startblock != DELAYSTARTBLOCK) && @@ -2249,10 +2226,8 @@ xfs_attr_rmtval_remove(xfs_da_args_t *args) * bmap_finish() may have committed the last trans and started * a new one. We need the inode to be in all transactions. */ - if (committed) { - xfs_trans_ijoin(args->trans, args->dp, XFS_ILOCK_EXCL); - xfs_trans_ihold(args->trans, args->dp); - } + if (committed) + xfs_trans_ijoin(args->trans, args->dp); /* * Close out trans and start the next one in the chain. diff --git a/fs/xfs/xfs_bmap.c b/fs/xfs/xfs_bmap.c index ff8675b41973..e0389656ad2c 100644 --- a/fs/xfs/xfs_bmap.c +++ b/fs/xfs/xfs_bmap.c @@ -3751,9 +3751,10 @@ xfs_bmap_add_attrfork( ip->i_d.di_aformat = XFS_DINODE_FMT_EXTENTS; } ASSERT(ip->i_d.di_anextents == 0); - IHOLD(ip); - xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL); + + xfs_trans_ijoin_ref(tp, ip, XFS_ILOCK_EXCL); xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); + switch (ip->i_d.di_format) { case XFS_DINODE_FMT_DEV: ip->i_d.di_forkoff = roundup(sizeof(xfs_dev_t), 8) >> 3; diff --git a/fs/xfs/xfs_dfrag.c b/fs/xfs/xfs_dfrag.c index 7b11dc0494c2..3b9582c60a22 100644 --- a/fs/xfs/xfs_dfrag.c +++ b/fs/xfs/xfs_dfrag.c @@ -416,11 +416,8 @@ xfs_swap_extents( } - IHOLD(ip); - xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL); - - IHOLD(tip); - xfs_trans_ijoin(tp, tip, XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL); + xfs_trans_ijoin_ref(tp, ip, XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL); + xfs_trans_ijoin_ref(tp, tip, XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL); xfs_trans_log_inode(tp, ip, ilf_fields); xfs_trans_log_inode(tp, tip, tilf_fields); diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c index ade96922fc8c..dbca5f5c37ba 100644 --- a/fs/xfs/xfs_fsops.c +++ b/fs/xfs/xfs_fsops.c @@ -622,8 +622,7 @@ xfs_fs_log_dummy( ip = mp->m_rootip; xfs_ilock(ip, XFS_ILOCK_EXCL); - xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL); - xfs_trans_ihold(tp, ip); + xfs_trans_ijoin(tp, ip); xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); xfs_trans_set_sync(tp); error = xfs_trans_commit(tp, 0); diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index c7c48da97ad4..d22b580162cc 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -1456,7 +1456,7 @@ xfs_itruncate_finish( ASSERT((*tp)->t_flags & XFS_TRANS_PERM_LOG_RES); ASSERT(ip->i_transp == *tp); ASSERT(ip->i_itemp != NULL); - ASSERT(ip->i_itemp->ili_flags & XFS_ILI_HOLD); + ASSERT(ip->i_itemp->ili_lock_flags == 0); ntp = *tp; @@ -1608,12 +1608,8 @@ xfs_itruncate_finish( */ error = xfs_bmap_finish(tp, &free_list, &committed); ntp = *tp; - if (committed) { - /* link the inode into the next xact in the chain */ - xfs_trans_ijoin(ntp, ip, - XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL); - xfs_trans_ihold(ntp, ip); - } + if (committed) + xfs_trans_ijoin(ntp, ip); if (error) { /* @@ -1642,9 +1638,7 @@ xfs_itruncate_finish( error = xfs_trans_commit(*tp, 0); *tp = ntp; - /* link the inode into the next transaction in the chain */ - xfs_trans_ijoin(ntp, ip, XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL); - xfs_trans_ihold(ntp, ip); + xfs_trans_ijoin(ntp, ip); if (error) return error; diff --git a/fs/xfs/xfs_inode_item.c b/fs/xfs/xfs_inode_item.c index c7e70d708345..ad050c618e62 100644 --- a/fs/xfs/xfs_inode_item.c +++ b/fs/xfs/xfs_inode_item.c @@ -628,19 +628,10 @@ xfs_inode_item_unlock( { struct xfs_inode_log_item *iip = INODE_ITEM(lip); struct xfs_inode *ip = iip->ili_inode; - uint hold; - uint iolocked; - uint lock_flags; + unsigned short lock_flags; - ASSERT(iip != NULL); ASSERT(iip->ili_inode->i_itemp != NULL); ASSERT(xfs_isilocked(iip->ili_inode, XFS_ILOCK_EXCL)); - ASSERT((!(iip->ili_inode->i_itemp->ili_flags & - XFS_ILI_IOLOCKED_EXCL)) || - xfs_isilocked(iip->ili_inode, XFS_IOLOCK_EXCL)); - ASSERT((!(iip->ili_inode->i_itemp->ili_flags & - XFS_ILI_IOLOCKED_SHARED)) || - xfs_isilocked(iip->ili_inode, XFS_IOLOCK_SHARED)); /* * Clear the transaction pointer in the inode. @@ -668,35 +659,10 @@ xfs_inode_item_unlock( iip->ili_aextents_buf = NULL; } - /* - * Figure out if we should unlock the inode or not. - */ - hold = iip->ili_flags & XFS_ILI_HOLD; - - /* - * Before clearing out the flags, remember whether we - * are holding the inode's IO lock. - */ - iolocked = iip->ili_flags & XFS_ILI_IOLOCKED_ANY; - - /* - * Clear out the fields of the inode log item particular - * to the current transaction. - */ - iip->ili_flags = 0; - - /* - * Unlock the inode if XFS_ILI_HOLD was not set. - */ - if (!hold) { - lock_flags = XFS_ILOCK_EXCL; - if (iolocked & XFS_ILI_IOLOCKED_EXCL) { - lock_flags |= XFS_IOLOCK_EXCL; - } else if (iolocked & XFS_ILI_IOLOCKED_SHARED) { - lock_flags |= XFS_IOLOCK_SHARED; - } + lock_flags = iip->ili_lock_flags; + iip->ili_lock_flags = 0; + if (lock_flags) xfs_iput(iip->ili_inode, lock_flags); - } } /* diff --git a/fs/xfs/xfs_inode_item.h b/fs/xfs/xfs_inode_item.h index b6a97ff1c3ab..d3dee61e6d91 100644 --- a/fs/xfs/xfs_inode_item.h +++ b/fs/xfs/xfs_inode_item.h @@ -103,12 +103,6 @@ typedef struct xfs_inode_log_format_64 { XFS_ILOG_ADATA | XFS_ILOG_AEXT | \ XFS_ILOG_ABROOT) -#define XFS_ILI_HOLD 0x1 -#define XFS_ILI_IOLOCKED_EXCL 0x2 -#define XFS_ILI_IOLOCKED_SHARED 0x4 - -#define XFS_ILI_IOLOCKED_ANY (XFS_ILI_IOLOCKED_EXCL | XFS_ILI_IOLOCKED_SHARED) - static inline int xfs_ilog_fbroot(int w) { return (w == XFS_DATA_FORK ? XFS_ILOG_DBROOT : XFS_ILOG_ABROOT); @@ -137,7 +131,7 @@ typedef struct xfs_inode_log_item { struct xfs_inode *ili_inode; /* inode ptr */ xfs_lsn_t ili_flush_lsn; /* lsn at last flush */ xfs_lsn_t ili_last_lsn; /* lsn at last transaction */ - unsigned short ili_flags; /* misc flags */ + unsigned short ili_lock_flags; /* lock flags */ unsigned short ili_logged; /* flushed logged data */ unsigned int ili_last_fields; /* fields when flushed */ struct xfs_bmbt_rec *ili_extents_buf; /* array of logged diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c index 772f3e791ebe..aeac00294a18 100644 --- a/fs/xfs/xfs_iomap.c +++ b/fs/xfs/xfs_iomap.c @@ -329,8 +329,7 @@ xfs_iomap_write_direct( if (error) goto error1; - xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL); - xfs_trans_ihold(tp, ip); + xfs_trans_ijoin(tp, ip); bmapi_flag = XFS_BMAPI_WRITE; if ((flags & BMAPI_DIRECT) && (offset < ip->i_size || extsz)) @@ -597,8 +596,7 @@ xfs_iomap_write_allocate( return XFS_ERROR(error); } xfs_ilock(ip, XFS_ILOCK_EXCL); - xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL); - xfs_trans_ihold(tp, ip); + xfs_trans_ijoin(tp, ip); xfs_bmap_init(&free_list, &first_block); @@ -761,8 +759,7 @@ xfs_iomap_write_unwritten( } xfs_ilock(ip, XFS_ILOCK_EXCL); - xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL); - xfs_trans_ihold(tp, ip); + xfs_trans_ijoin(tp, ip); /* * Modify the unwritten extent state of the buffer. diff --git a/fs/xfs/xfs_rename.c b/fs/xfs/xfs_rename.c index 8edb1074847a..778c87a8ebfc 100644 --- a/fs/xfs/xfs_rename.c +++ b/fs/xfs/xfs_rename.c @@ -169,26 +169,14 @@ xfs_rename( /* * Join all the inodes to the transaction. From this point on, * we can rely on either trans_commit or trans_cancel to unlock - * them. Note that we need to add a vnode reference to the - * directories since trans_commit & trans_cancel will decrement - * them when they unlock the inodes. Also, we need to be careful - * not to add an inode to the transaction more than once. + * them. */ - IHOLD(src_dp); - xfs_trans_ijoin(tp, src_dp, XFS_ILOCK_EXCL); - - if (new_parent) { - IHOLD(target_dp); - xfs_trans_ijoin(tp, target_dp, XFS_ILOCK_EXCL); - } - - IHOLD(src_ip); - xfs_trans_ijoin(tp, src_ip, XFS_ILOCK_EXCL); - - if (target_ip) { - IHOLD(target_ip); - xfs_trans_ijoin(tp, target_ip, XFS_ILOCK_EXCL); - } + xfs_trans_ijoin_ref(tp, src_dp, XFS_ILOCK_EXCL); + if (new_parent) + xfs_trans_ijoin_ref(tp, target_dp, XFS_ILOCK_EXCL); + xfs_trans_ijoin_ref(tp, src_ip, XFS_ILOCK_EXCL); + if (target_ip) + xfs_trans_ijoin_ref(tp, target_ip, XFS_ILOCK_EXCL); /* * If we are using project inheritance, we only allow renames diff --git a/fs/xfs/xfs_trans.c b/fs/xfs/xfs_trans.c index 213792e1ad02..f2065ccb6c2d 100644 --- a/fs/xfs/xfs_trans.c +++ b/fs/xfs/xfs_trans.c @@ -1892,7 +1892,6 @@ xfs_trans_roll( if (error) return error; - xfs_trans_ijoin(trans, dp, XFS_ILOCK_EXCL); - xfs_trans_ihold(trans, dp); + xfs_trans_ijoin(trans, dp); return 0; } diff --git a/fs/xfs/xfs_trans.h b/fs/xfs/xfs_trans.h index 37c0ce1ccd48..aa6f422f0361 100644 --- a/fs/xfs/xfs_trans.h +++ b/fs/xfs/xfs_trans.h @@ -475,8 +475,8 @@ void xfs_trans_dquot_buf(xfs_trans_t *, struct xfs_buf *, uint); void xfs_trans_inode_alloc_buf(xfs_trans_t *, struct xfs_buf *); int xfs_trans_iget(struct xfs_mount *, xfs_trans_t *, xfs_ino_t , uint, uint, struct xfs_inode **); -void xfs_trans_ijoin(xfs_trans_t *, struct xfs_inode *, uint); -void xfs_trans_ihold(xfs_trans_t *, struct xfs_inode *); +void xfs_trans_ijoin_ref(struct xfs_trans *, struct xfs_inode *, uint); +void xfs_trans_ijoin(struct xfs_trans *, struct xfs_inode *); void xfs_trans_log_buf(xfs_trans_t *, struct xfs_buf *, uint, uint); void xfs_trans_log_inode(xfs_trans_t *, struct xfs_inode *, uint); struct xfs_efi_log_item *xfs_trans_get_efi(xfs_trans_t *, uint); diff --git a/fs/xfs/xfs_trans_inode.c b/fs/xfs/xfs_trans_inode.c index 865eeb63ce16..cdc53a1050c5 100644 --- a/fs/xfs/xfs_trans_inode.c +++ b/fs/xfs/xfs_trans_inode.c @@ -33,6 +33,7 @@ #include "xfs_btree.h" #include "xfs_trans_priv.h" #include "xfs_inode_item.h" +#include "xfs_trace.h" #ifdef XFS_TRANS_DEBUG STATIC void @@ -42,7 +43,6 @@ xfs_trans_inode_broot_debug( #define xfs_trans_inode_broot_debug(ip) #endif - /* * Get an inode and join it to the transaction. */ @@ -58,32 +58,31 @@ xfs_trans_iget( int error; error = xfs_iget(mp, tp, ino, flags, lock_flags, ipp); - if (!error && tp) - xfs_trans_ijoin(tp, *ipp, lock_flags); + if (!error && tp) { + xfs_trans_ijoin(tp, *ipp); + (*ipp)->i_itemp->ili_lock_flags = lock_flags; + } return error; } /* - * Add the locked inode to the transaction. - * The inode must be locked, and it cannot be associated with any - * transaction. The caller must specify the locks already held - * on the inode. + * Add a locked inode to the transaction. + * + * The inode must be locked, and it cannot be associated with any transaction. */ void xfs_trans_ijoin( - xfs_trans_t *tp, - xfs_inode_t *ip, - uint lock_flags) + struct xfs_trans *tp, + struct xfs_inode *ip) { xfs_inode_log_item_t *iip; ASSERT(ip->i_transp == NULL); ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL)); - ASSERT(lock_flags & XFS_ILOCK_EXCL); if (ip->i_itemp == NULL) xfs_inode_item_init(ip, ip->i_mount); iip = ip->i_itemp; - ASSERT(iip->ili_flags == 0); + ASSERT(iip->ili_lock_flags == 0); /* * Get a log_item_desc to point at the new item. @@ -92,15 +91,6 @@ xfs_trans_ijoin( xfs_trans_inode_broot_debug(ip); - /* - * If the IO lock is already held, mark that in the inode log item. - */ - if (lock_flags & XFS_IOLOCK_EXCL) { - iip->ili_flags |= XFS_ILI_IOLOCKED_EXCL; - } else if (lock_flags & XFS_IOLOCK_SHARED) { - iip->ili_flags |= XFS_ILI_IOLOCKED_SHARED; - } - /* * Initialize i_transp so we can find it with xfs_inode_incore() * in xfs_trans_iget() above. @@ -108,27 +98,25 @@ xfs_trans_ijoin( ip->i_transp = tp; } - - /* - * Mark the inode as not needing to be unlocked when the inode item's - * IOP_UNLOCK() routine is called. The inode must already be locked - * and associated with the given transaction. + * Add a locked inode to the transaction. + * + * + * Grabs a reference to the inode which will be dropped when the transaction + * is commited. The inode will also be unlocked at that point. The inode + * must be locked, and it cannot be associated with any transaction. */ -/*ARGSUSED*/ void -xfs_trans_ihold( - xfs_trans_t *tp, - xfs_inode_t *ip) +xfs_trans_ijoin_ref( + struct xfs_trans *tp, + struct xfs_inode *ip, + uint lock_flags) { - ASSERT(ip->i_transp == tp); - ASSERT(ip->i_itemp != NULL); - ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL)); - - ip->i_itemp->ili_flags |= XFS_ILI_HOLD; + xfs_trans_ijoin(tp, ip); + IHOLD(ip); + ip->i_itemp->ili_lock_flags = lock_flags; } - /* * This is called to mark the fields indicated in fieldmask as needing * to be logged when the transaction is committed. The inode must diff --git a/fs/xfs/xfs_utils.c b/fs/xfs/xfs_utils.c index 8965887d26b1..102ce4898ab7 100644 --- a/fs/xfs/xfs_utils.c +++ b/fs/xfs/xfs_utils.c @@ -374,8 +374,8 @@ xfs_truncate_file( * of references will stay constant. */ xfs_ilock(ip, XFS_ILOCK_EXCL); - xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL); - xfs_trans_ihold(tp, ip); + xfs_trans_ijoin(tp, ip); + /* * Signal a sync xaction. The only case where that isn't * the case is if we're truncating an already unlinked file diff --git a/fs/xfs/xfs_vnodeops.c b/fs/xfs/xfs_vnodeops.c index 161444e768b6..130343a5d22d 100644 --- a/fs/xfs/xfs_vnodeops.c +++ b/fs/xfs/xfs_vnodeops.c @@ -268,8 +268,7 @@ xfs_setattr( commit_flags = XFS_TRANS_RELEASE_LOG_RES; xfs_ilock(ip, XFS_ILOCK_EXCL); - xfs_trans_ijoin(tp, ip, lock_flags); - xfs_trans_ihold(tp, ip); + xfs_trans_ijoin(tp, ip); /* * Only change the c/mtime if we are changing the size @@ -319,8 +318,7 @@ xfs_setattr( xfs_iflags_set(ip, XFS_ITRUNCATED); } } else if (tp) { - xfs_trans_ijoin(tp, ip, lock_flags); - xfs_trans_ihold(tp, ip); + xfs_trans_ijoin(tp, ip); } /* @@ -653,10 +651,7 @@ xfs_free_eofblocks( } xfs_ilock(ip, XFS_ILOCK_EXCL); - xfs_trans_ijoin(tp, ip, - XFS_IOLOCK_EXCL | - XFS_ILOCK_EXCL); - xfs_trans_ihold(tp, ip); + xfs_trans_ijoin(tp, ip); error = xfs_itruncate_finish(&tp, ip, ip->i_size, @@ -728,8 +723,7 @@ xfs_inactive_symlink_rmt( xfs_ilock(ip, XFS_IOLOCK_EXCL | XFS_ILOCK_EXCL); size = (int)ip->i_d.di_size; ip->i_d.di_size = 0; - xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL); - xfs_trans_ihold(tp, ip); + xfs_trans_ijoin(tp, ip); xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); /* * Find the block(s) so we can inval and unmap them. @@ -773,8 +767,7 @@ xfs_inactive_symlink_rmt( * Mark it dirty so it will be logged and moved forward in the log as * part of every commit. */ - xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL); - xfs_trans_ihold(tp, ip); + xfs_trans_ijoin(tp, ip); xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); /* * Get a new, empty transaction to return to our caller. @@ -907,8 +900,7 @@ xfs_inactive_attrs( goto error_cancel; xfs_ilock(ip, XFS_ILOCK_EXCL); - xfs_trans_ijoin(tp, ip, XFS_IOLOCK_EXCL | XFS_ILOCK_EXCL); - xfs_trans_ihold(tp, ip); + xfs_trans_ijoin(tp, ip); xfs_idestroy_fork(ip, XFS_ATTR_FORK); ASSERT(ip->i_d.di_anextents == 0); @@ -1095,8 +1087,7 @@ xfs_inactive( } xfs_ilock(ip, XFS_ILOCK_EXCL); - xfs_trans_ijoin(tp, ip, XFS_IOLOCK_EXCL | XFS_ILOCK_EXCL); - xfs_trans_ihold(tp, ip); + xfs_trans_ijoin(tp, ip); /* * normally, we have to run xfs_itruncate_finish sync. @@ -1129,8 +1120,7 @@ xfs_inactive( return VN_INACTIVE_CACHE; } - xfs_trans_ijoin(tp, ip, XFS_IOLOCK_EXCL | XFS_ILOCK_EXCL); - xfs_trans_ihold(tp, ip); + xfs_trans_ijoin(tp, ip); } else { error = xfs_trans_reserve(tp, 0, XFS_IFREE_LOG_RES(mp), @@ -1143,8 +1133,7 @@ xfs_inactive( } xfs_ilock(ip, XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL); - xfs_trans_ijoin(tp, ip, XFS_IOLOCK_EXCL | XFS_ILOCK_EXCL); - xfs_trans_ihold(tp, ip); + xfs_trans_ijoin(tp, ip); } /* @@ -1392,8 +1381,7 @@ xfs_create( * the transaction cancel unlocking dp so don't do it explicitly in the * error path. */ - IHOLD(dp); - xfs_trans_ijoin(tp, dp, XFS_ILOCK_EXCL); + xfs_trans_ijoin_ref(tp, dp, XFS_ILOCK_EXCL); unlock_dp_on_error = B_FALSE; error = xfs_dir_createname(tp, dp, name, ip->i_ino, @@ -1730,15 +1718,8 @@ xfs_remove( xfs_lock_two_inodes(dp, ip, XFS_ILOCK_EXCL); - /* - * At this point, we've gotten both the directory and the entry - * inodes locked. - */ - IHOLD(ip); - xfs_trans_ijoin(tp, dp, XFS_ILOCK_EXCL); - - IHOLD(dp); - xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL); + xfs_trans_ijoin_ref(tp, dp, XFS_ILOCK_EXCL); + xfs_trans_ijoin_ref(tp, ip, XFS_ILOCK_EXCL); /* * If we're removing a directory perform some additional validation. @@ -1884,15 +1865,8 @@ xfs_link( xfs_lock_two_inodes(sip, tdp, XFS_ILOCK_EXCL); - /* - * Increment vnode ref counts since xfs_trans_commit & - * xfs_trans_cancel will both unlock the inodes and - * decrement the associated ref counts. - */ - IHOLD(sip); - IHOLD(tdp); - xfs_trans_ijoin(tp, sip, XFS_ILOCK_EXCL); - xfs_trans_ijoin(tp, tdp, XFS_ILOCK_EXCL); + xfs_trans_ijoin_ref(tp, sip, XFS_ILOCK_EXCL); + xfs_trans_ijoin_ref(tp, tdp, XFS_ILOCK_EXCL); /* * If the source has too many links, we can't make any more to it. @@ -2087,8 +2061,7 @@ xfs_symlink( * transaction cancel unlocking dp so don't do it explicitly in the * error path. */ - IHOLD(dp); - xfs_trans_ijoin(tp, dp, XFS_ILOCK_EXCL); + xfs_trans_ijoin_ref(tp, dp, XFS_ILOCK_EXCL); unlock_dp_on_error = B_FALSE; /* @@ -2227,13 +2200,12 @@ xfs_set_dmattrs( return error; } xfs_ilock(ip, XFS_ILOCK_EXCL); - xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL); + xfs_trans_ijoin_ref(tp, ip, XFS_ILOCK_EXCL); ip->i_d.di_dmevmask = evmask; ip->i_d.di_dmstate = state; xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); - IHOLD(ip); error = xfs_trans_commit(tp, 0); return error; @@ -2366,8 +2338,7 @@ xfs_alloc_file_space( if (error) goto error1; - xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL); - xfs_trans_ihold(tp, ip); + xfs_trans_ijoin(tp, ip); /* * Issue the xfs_bmapi() call to allocate the blocks @@ -2668,8 +2639,7 @@ xfs_free_file_space( if (error) goto error1; - xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL); - xfs_trans_ihold(tp, ip); + xfs_trans_ijoin(tp, ip); /* * issue the bunmapi() call to free the blocks @@ -2839,8 +2809,7 @@ xfs_change_file_space( xfs_ilock(ip, XFS_ILOCK_EXCL); - xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL); - xfs_trans_ihold(tp, ip); + xfs_trans_ijoin(tp, ip); if ((attr_flags & XFS_ATTR_DMI) == 0) { ip->i_d.di_mode &= ~S_ISUID; -- cgit v1.2.3 From 4e0d5f926b80b06234a4ed664d6ae8c54fb08c4b Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 23 Jun 2010 18:11:15 +1000 Subject: xfs: fix the xfs_log_iovec i_addr type By making this member a void pointer we can get rid of a lot of pointless casts. Signed-off-by: Christoph Hellwig Reviewed-by: Dave Chinner --- fs/xfs/quota/xfs_dquot_item.c | 6 +++--- fs/xfs/xfs_buf_item.c | 2 +- fs/xfs/xfs_extfree_item.c | 12 +++++------- fs/xfs/xfs_inode_item.c | 27 ++++++++++++--------------- fs/xfs/xfs_log.c | 2 +- fs/xfs/xfs_log.h | 2 +- fs/xfs/xfs_log_cil.c | 2 +- fs/xfs/xfs_log_recover.c | 38 +++++++++++++++----------------------- 8 files changed, 39 insertions(+), 52 deletions(-) (limited to 'fs') diff --git a/fs/xfs/quota/xfs_dquot_item.c b/fs/xfs/quota/xfs_dquot_item.c index cd86bc317d59..2a1f3dc10a02 100644 --- a/fs/xfs/quota/xfs_dquot_item.c +++ b/fs/xfs/quota/xfs_dquot_item.c @@ -65,11 +65,11 @@ xfs_qm_dquot_logitem_format( { struct xfs_dq_logitem *qlip = DQUOT_ITEM(lip); - logvec->i_addr = (xfs_caddr_t)&qlip->qli_format; + logvec->i_addr = &qlip->qli_format; logvec->i_len = sizeof(xfs_dq_logformat_t); logvec->i_type = XLOG_REG_TYPE_QFORMAT; logvec++; - logvec->i_addr = (xfs_caddr_t)&qlip->qli_dquot->q_core; + logvec->i_addr = &qlip->qli_dquot->q_core; logvec->i_len = sizeof(xfs_disk_dquot_t); logvec->i_type = XLOG_REG_TYPE_DQUOT; @@ -369,7 +369,7 @@ xfs_qm_qoff_logitem_format( ASSERT(qflip->qql_format.qf_type == XFS_LI_QUOTAOFF); - log_vector->i_addr = (xfs_caddr_t)&(qflip->qql_format); + log_vector->i_addr = &qflip->qql_format; log_vector->i_len = sizeof(xfs_qoff_logitem_t); log_vector->i_type = XLOG_REG_TYPE_QUOTAOFF; qflip->qql_format.qf_size = 1; diff --git a/fs/xfs/xfs_buf_item.c b/fs/xfs/xfs_buf_item.c index f53327a19e0d..2a9e4ef12110 100644 --- a/fs/xfs/xfs_buf_item.c +++ b/fs/xfs/xfs_buf_item.c @@ -251,7 +251,7 @@ xfs_buf_item_format( base_size = (uint)(sizeof(xfs_buf_log_format_t) + ((bip->bli_format.blf_map_size - 1) * sizeof(uint))); - vecp->i_addr = (xfs_caddr_t)&bip->bli_format; + vecp->i_addr = &bip->bli_format; vecp->i_len = base_size; vecp->i_type = XLOG_REG_TYPE_BFORMAT; vecp++; diff --git a/fs/xfs/xfs_extfree_item.c b/fs/xfs/xfs_extfree_item.c index 61d83c64ed32..a55e687bf562 100644 --- a/fs/xfs/xfs_extfree_item.c +++ b/fs/xfs/xfs_extfree_item.c @@ -82,7 +82,7 @@ xfs_efi_item_format( size += (efip->efi_format.efi_nextents - 1) * sizeof(xfs_extent_t); efip->efi_format.efi_size = 1; - log_vector->i_addr = (xfs_caddr_t)&efip->efi_format; + log_vector->i_addr = &efip->efi_format; log_vector->i_len = size; log_vector->i_type = XLOG_REG_TYPE_EFI_FORMAT; ASSERT(size >= sizeof(xfs_efi_log_format_t)); @@ -244,7 +244,7 @@ xfs_efi_init( int xfs_efi_copy_format(xfs_log_iovec_t *buf, xfs_efi_log_format_t *dst_efi_fmt) { - xfs_efi_log_format_t *src_efi_fmt = (xfs_efi_log_format_t *)buf->i_addr; + xfs_efi_log_format_t *src_efi_fmt = buf->i_addr; uint i; uint len = sizeof(xfs_efi_log_format_t) + (src_efi_fmt->efi_nextents - 1) * sizeof(xfs_extent_t); @@ -257,8 +257,7 @@ xfs_efi_copy_format(xfs_log_iovec_t *buf, xfs_efi_log_format_t *dst_efi_fmt) memcpy((char *)dst_efi_fmt, (char*)src_efi_fmt, len); return 0; } else if (buf->i_len == len32) { - xfs_efi_log_format_32_t *src_efi_fmt_32 = - (xfs_efi_log_format_32_t *)buf->i_addr; + xfs_efi_log_format_32_t *src_efi_fmt_32 = buf->i_addr; dst_efi_fmt->efi_type = src_efi_fmt_32->efi_type; dst_efi_fmt->efi_size = src_efi_fmt_32->efi_size; @@ -272,8 +271,7 @@ xfs_efi_copy_format(xfs_log_iovec_t *buf, xfs_efi_log_format_t *dst_efi_fmt) } return 0; } else if (buf->i_len == len64) { - xfs_efi_log_format_64_t *src_efi_fmt_64 = - (xfs_efi_log_format_64_t *)buf->i_addr; + xfs_efi_log_format_64_t *src_efi_fmt_64 = buf->i_addr; dst_efi_fmt->efi_type = src_efi_fmt_64->efi_type; dst_efi_fmt->efi_size = src_efi_fmt_64->efi_size; @@ -373,7 +371,7 @@ xfs_efd_item_format( size += (efdp->efd_format.efd_nextents - 1) * sizeof(xfs_extent_t); efdp->efd_format.efd_size = 1; - log_vector->i_addr = (xfs_caddr_t)&efdp->efd_format; + log_vector->i_addr = &efdp->efd_format; log_vector->i_len = size; log_vector->i_type = XLOG_REG_TYPE_EFD_FORMAT; ASSERT(size >= sizeof(xfs_efd_log_format_t)); diff --git a/fs/xfs/xfs_inode_item.c b/fs/xfs/xfs_inode_item.c index ad050c618e62..2998b2cb7466 100644 --- a/fs/xfs/xfs_inode_item.c +++ b/fs/xfs/xfs_inode_item.c @@ -217,7 +217,7 @@ xfs_inode_item_format( int nrecs; xfs_mount_t *mp; - vecp->i_addr = (xfs_caddr_t)&iip->ili_format; + vecp->i_addr = &iip->ili_format; vecp->i_len = sizeof(xfs_inode_log_format_t); vecp->i_type = XLOG_REG_TYPE_IFORMAT; vecp++; @@ -268,7 +268,7 @@ xfs_inode_item_format( */ xfs_synchronize_times(ip); - vecp->i_addr = (xfs_caddr_t)&ip->i_d; + vecp->i_addr = &ip->i_d; vecp->i_len = sizeof(struct xfs_icdinode); vecp->i_type = XLOG_REG_TYPE_ICORE; vecp++; @@ -324,8 +324,7 @@ xfs_inode_item_format( * extents, so just point to the * real extents array. */ - vecp->i_addr = - (char *)(ip->i_df.if_u1.if_extents); + vecp->i_addr = ip->i_df.if_u1.if_extents; vecp->i_len = ip->i_df.if_bytes; vecp->i_type = XLOG_REG_TYPE_IEXT; } else @@ -343,7 +342,7 @@ xfs_inode_item_format( ext_buffer = kmem_alloc(ip->i_df.if_bytes, KM_SLEEP); iip->ili_extents_buf = ext_buffer; - vecp->i_addr = (xfs_caddr_t)ext_buffer; + vecp->i_addr = ext_buffer; vecp->i_len = xfs_iextents_copy(ip, ext_buffer, XFS_DATA_FORK); vecp->i_type = XLOG_REG_TYPE_IEXT; @@ -362,7 +361,7 @@ xfs_inode_item_format( if (iip->ili_format.ilf_fields & XFS_ILOG_DBROOT) { ASSERT(ip->i_df.if_broot_bytes > 0); ASSERT(ip->i_df.if_broot != NULL); - vecp->i_addr = (xfs_caddr_t)ip->i_df.if_broot; + vecp->i_addr = ip->i_df.if_broot; vecp->i_len = ip->i_df.if_broot_bytes; vecp->i_type = XLOG_REG_TYPE_IBROOT; vecp++; @@ -380,7 +379,7 @@ xfs_inode_item_format( ASSERT(ip->i_df.if_u1.if_data != NULL); ASSERT(ip->i_d.di_size > 0); - vecp->i_addr = (xfs_caddr_t)ip->i_df.if_u1.if_data; + vecp->i_addr = ip->i_df.if_u1.if_data; /* * Round i_bytes up to a word boundary. * The underlying memory is guaranteed to @@ -454,7 +453,7 @@ xfs_inode_item_format( * There are not delayed allocation extents * for attributes, so just point at the array. */ - vecp->i_addr = (char *)(ip->i_afp->if_u1.if_extents); + vecp->i_addr = ip->i_afp->if_u1.if_extents; vecp->i_len = ip->i_afp->if_bytes; #else ASSERT(iip->ili_aextents_buf == NULL); @@ -464,7 +463,7 @@ xfs_inode_item_format( ext_buffer = kmem_alloc(ip->i_afp->if_bytes, KM_SLEEP); iip->ili_aextents_buf = ext_buffer; - vecp->i_addr = (xfs_caddr_t)ext_buffer; + vecp->i_addr = ext_buffer; vecp->i_len = xfs_iextents_copy(ip, ext_buffer, XFS_ATTR_FORK); #endif @@ -481,7 +480,7 @@ xfs_inode_item_format( if (iip->ili_format.ilf_fields & XFS_ILOG_ABROOT) { ASSERT(ip->i_afp->if_broot_bytes > 0); ASSERT(ip->i_afp->if_broot != NULL); - vecp->i_addr = (xfs_caddr_t)ip->i_afp->if_broot; + vecp->i_addr = ip->i_afp->if_broot; vecp->i_len = ip->i_afp->if_broot_bytes; vecp->i_type = XLOG_REG_TYPE_IATTR_BROOT; vecp++; @@ -497,7 +496,7 @@ xfs_inode_item_format( ASSERT(ip->i_afp->if_bytes > 0); ASSERT(ip->i_afp->if_u1.if_data != NULL); - vecp->i_addr = (xfs_caddr_t)ip->i_afp->if_u1.if_data; + vecp->i_addr = ip->i_afp->if_u1.if_data; /* * Round i_bytes up to a word boundary. * The underlying memory is guaranteed to @@ -938,9 +937,8 @@ xfs_inode_item_format_convert( xfs_inode_log_format_t *in_f) { if (buf->i_len == sizeof(xfs_inode_log_format_32_t)) { - xfs_inode_log_format_32_t *in_f32; + xfs_inode_log_format_32_t *in_f32 = buf->i_addr; - in_f32 = (xfs_inode_log_format_32_t *)buf->i_addr; in_f->ilf_type = in_f32->ilf_type; in_f->ilf_size = in_f32->ilf_size; in_f->ilf_fields = in_f32->ilf_fields; @@ -956,9 +954,8 @@ xfs_inode_item_format_convert( in_f->ilf_boffset = in_f32->ilf_boffset; return 0; } else if (buf->i_len == sizeof(xfs_inode_log_format_64_t)){ - xfs_inode_log_format_64_t *in_f64; + xfs_inode_log_format_64_t *in_f64 = buf->i_addr; - in_f64 = (xfs_inode_log_format_64_t *)buf->i_addr; in_f->ilf_type = in_f64->ilf_type; in_f->ilf_size = in_f64->ilf_size; in_f->ilf_fields = in_f64->ilf_fields; diff --git a/fs/xfs/xfs_log.c b/fs/xfs/xfs_log.c index 1857c412d839..f461760c5b76 100644 --- a/fs/xfs/xfs_log.c +++ b/fs/xfs/xfs_log.c @@ -548,7 +548,7 @@ xfs_log_unmount_write(xfs_mount_t *mp) .magic = XLOG_UNMOUNT_TYPE, }; struct xfs_log_iovec reg = { - .i_addr = (void *)&magic, + .i_addr = &magic, .i_len = sizeof(magic), .i_type = XLOG_REG_TYPE_UNMOUNT, }; diff --git a/fs/xfs/xfs_log.h b/fs/xfs/xfs_log.h index 04c78e642cc8..5b72e9a40ab2 100644 --- a/fs/xfs/xfs_log.h +++ b/fs/xfs/xfs_log.h @@ -104,7 +104,7 @@ static inline xfs_lsn_t _lsn_cmp(xfs_lsn_t lsn1, xfs_lsn_t lsn2) #define XLOG_REG_TYPE_MAX 19 typedef struct xfs_log_iovec { - xfs_caddr_t i_addr; /* beginning address of region */ + void *i_addr; /* beginning address of region */ int i_len; /* length in bytes of region */ uint i_type; /* type of region */ } xfs_log_iovec_t; diff --git a/fs/xfs/xfs_log_cil.c b/fs/xfs/xfs_log_cil.c index 5eaeede942b5..31e4ea2d19ac 100644 --- a/fs/xfs/xfs_log_cil.c +++ b/fs/xfs/xfs_log_cil.c @@ -552,7 +552,7 @@ xlog_cil_push( thdr.th_type = XFS_TRANS_CHECKPOINT; thdr.th_tid = tic->t_tid; thdr.th_num_items = num_iovecs; - lhdr.i_addr = (xfs_caddr_t)&thdr; + lhdr.i_addr = &thdr; lhdr.i_len = sizeof(xfs_trans_header_t); lhdr.i_type = XLOG_REG_TYPE_TRANSHDR; tic->t_curr_res -= lhdr.i_len + sizeof(xlog_op_header_t); diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c index 0fa18a88febc..6f3f5fa37acf 100644 --- a/fs/xfs/xfs_log_recover.c +++ b/fs/xfs/xfs_log_recover.c @@ -1561,9 +1561,7 @@ xlog_recover_reorder_trans( list_splice_init(&trans->r_itemq, &sort_list); list_for_each_entry_safe(item, n, &sort_list, ri_list) { - xfs_buf_log_format_t *buf_f; - - buf_f = (xfs_buf_log_format_t *)item->ri_buf[0].i_addr; + xfs_buf_log_format_t *buf_f = item->ri_buf[0].i_addr; switch (ITEM_TYPE(item)) { case XFS_LI_BUF: @@ -1888,9 +1886,8 @@ xlog_recover_do_inode_buffer( * current di_next_unlinked field. Extract its value * and copy it to the buffer copy. */ - logged_nextp = (xfs_agino_t *) - ((char *)(item->ri_buf[item_index].i_addr) + - (next_unlinked_offset - reg_buf_offset)); + logged_nextp = item->ri_buf[item_index].i_addr + + next_unlinked_offset - reg_buf_offset; if (unlikely(*logged_nextp == 0)) { xfs_fs_cmn_err(CE_ALERT, mp, "bad inode buffer log record (ptr = 0x%p, bp = 0x%p). XFS trying to replay bad (0) inode di_next_unlinked field", @@ -1969,8 +1966,7 @@ xlog_recover_do_reg_buffer( item->ri_buf[i].i_len, __func__); goto next; } - error = xfs_qm_dqcheck((xfs_disk_dquot_t *) - item->ri_buf[i].i_addr, + error = xfs_qm_dqcheck(item->ri_buf[i].i_addr, -1, 0, XFS_QMOPT_DOWARN, "dquot_buf_recover"); if (error) @@ -2183,7 +2179,7 @@ xlog_recover_do_buffer_trans( xlog_recover_item_t *item, int pass) { - xfs_buf_log_format_t *buf_f; + xfs_buf_log_format_t *buf_f = item->ri_buf[0].i_addr; xfs_mount_t *mp; xfs_buf_t *bp; int error; @@ -2193,8 +2189,6 @@ xlog_recover_do_buffer_trans( ushort flags; uint buf_flags; - buf_f = (xfs_buf_log_format_t *)item->ri_buf[0].i_addr; - if (pass == XLOG_RECOVER_PASS1) { /* * In this pass we're only looking for buf items @@ -2315,10 +2309,9 @@ xlog_recover_do_inode_trans( } if (item->ri_buf[0].i_len == sizeof(xfs_inode_log_format_t)) { - in_f = (xfs_inode_log_format_t *)item->ri_buf[0].i_addr; + in_f = item->ri_buf[0].i_addr; } else { - in_f = (xfs_inode_log_format_t *)kmem_alloc( - sizeof(xfs_inode_log_format_t), KM_SLEEP); + in_f = kmem_alloc(sizeof(xfs_inode_log_format_t), KM_SLEEP); need_free = 1; error = xfs_inode_item_format_convert(&item->ri_buf[0], in_f); if (error) @@ -2366,7 +2359,7 @@ xlog_recover_do_inode_trans( error = EFSCORRUPTED; goto error; } - dicp = (xfs_icdinode_t *)(item->ri_buf[1].i_addr); + dicp = item->ri_buf[1].i_addr; if (unlikely(dicp->di_magic != XFS_DINODE_MAGIC)) { xfs_buf_relse(bp); xfs_fs_cmn_err(CE_ALERT, mp, @@ -2457,7 +2450,7 @@ xlog_recover_do_inode_trans( } /* The core is in in-core format */ - xfs_dinode_to_disk(dip, (xfs_icdinode_t *)item->ri_buf[1].i_addr); + xfs_dinode_to_disk(dip, item->ri_buf[1].i_addr); /* the rest is in on-disk format */ if (item->ri_buf[1].i_len > sizeof(struct xfs_icdinode)) { @@ -2574,7 +2567,7 @@ xlog_recover_do_quotaoff_trans( return (0); } - qoff_f = (xfs_qoff_logformat_t *)item->ri_buf[0].i_addr; + qoff_f = item->ri_buf[0].i_addr; ASSERT(qoff_f); /* @@ -2618,9 +2611,8 @@ xlog_recover_do_dquot_trans( if (mp->m_qflags == 0) return (0); - recddq = (xfs_disk_dquot_t *)item->ri_buf[1].i_addr; - - if (item->ri_buf[1].i_addr == NULL) { + recddq = item->ri_buf[1].i_addr; + if (recddq == NULL) { cmn_err(CE_ALERT, "XFS: NULL dquot in %s.", __func__); return XFS_ERROR(EIO); @@ -2650,7 +2642,7 @@ xlog_recover_do_dquot_trans( * The other possibility, of course, is that the quota subsystem was * removed since the last mount - ENOSYS. */ - dq_f = (xfs_dq_logformat_t *)item->ri_buf[0].i_addr; + dq_f = item->ri_buf[0].i_addr; ASSERT(dq_f); if ((error = xfs_qm_dqcheck(recddq, dq_f->qlf_id, @@ -2717,7 +2709,7 @@ xlog_recover_do_efi_trans( return 0; } - efi_formatp = (xfs_efi_log_format_t *)item->ri_buf[0].i_addr; + efi_formatp = item->ri_buf[0].i_addr; mp = log->l_mp; efip = xfs_efi_init(mp, efi_formatp->efi_nextents); @@ -2763,7 +2755,7 @@ xlog_recover_do_efd_trans( return; } - efd_formatp = (xfs_efd_log_format_t *)item->ri_buf[0].i_addr; + efd_formatp = item->ri_buf[0].i_addr; ASSERT((item->ri_buf[0].i_len == (sizeof(xfs_efd_log_format_32_t) + ((efd_formatp->efd_nextents - 1) * sizeof(xfs_extent_32_t)))) || (item->ri_buf[0].i_len == (sizeof(xfs_efd_log_format_64_t) + -- cgit v1.2.3 From dbb2f6529feeee8f4de77849edeee2e60c40c805 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 23 Jun 2010 18:11:15 +1000 Subject: xfs: kill the unused xlog_debug variable Signed-off-by: Christoph Hellwig Reviewed-by: Dave Chinner --- fs/xfs/xfs_log.h | 5 ----- 1 file changed, 5 deletions(-) (limited to 'fs') diff --git a/fs/xfs/xfs_log.h b/fs/xfs/xfs_log.h index 5b72e9a40ab2..7fc2c8d4c802 100644 --- a/fs/xfs/xfs_log.h +++ b/fs/xfs/xfs_log.h @@ -201,9 +201,4 @@ int xfs_log_commit_cil(struct xfs_mount *mp, struct xfs_trans *tp, bool xfs_log_item_in_current_chkpt(struct xfs_log_item *lip); #endif - - -extern int xlog_debug; /* set to 1 to enable real log */ - - #endif /* __XFS_LOG_H__ */ -- cgit v1.2.3 From 9134c2332ecb9154860669d778ef2808f06503ec Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 23 Jun 2010 18:11:15 +1000 Subject: xfs: remove the unused XFS_LOG_SLEEP and XFS_LOG_NOSLEEP flags Signed-off-by: Christoph Hellwig Reviewed-by: Dave Chinner --- fs/xfs/xfs_log.c | 1 - fs/xfs/xfs_log.h | 4 ---- 2 files changed, 5 deletions(-) (limited to 'fs') diff --git a/fs/xfs/xfs_log.c b/fs/xfs/xfs_log.c index f461760c5b76..5a5a54ad3685 100644 --- a/fs/xfs/xfs_log.c +++ b/fs/xfs/xfs_log.c @@ -333,7 +333,6 @@ xfs_log_reserve( int retval = 0; ASSERT(client == XFS_TRANSACTION || client == XFS_LOG); - ASSERT((flags & XFS_LOG_NOSLEEP) == 0); if (XLOG_FORCED_SHUTDOWN(log)) return XFS_ERROR(EIO); diff --git a/fs/xfs/xfs_log.h b/fs/xfs/xfs_log.h index 7fc2c8d4c802..916eb7db14d9 100644 --- a/fs/xfs/xfs_log.h +++ b/fs/xfs/xfs_log.h @@ -55,14 +55,10 @@ static inline xfs_lsn_t _lsn_cmp(xfs_lsn_t lsn1, xfs_lsn_t lsn2) /* * Flags to xfs_log_reserve() * - * XFS_LOG_SLEEP: If space is not available, sleep (default) - * XFS_LOG_NOSLEEP: If space is not available, return error * XFS_LOG_PERM_RESERV: Permanent reservation. When writes are * performed against this type of reservation, the reservation * is not decreased. Long running transactions should use this. */ -#define XFS_LOG_SLEEP 0x0 -#define XFS_LOG_NOSLEEP 0x1 #define XFS_LOG_PERM_RESERV 0x2 /* -- cgit v1.2.3 From a59f55703c3725b2fa582924f51db62b58be05bb Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 23 Jun 2010 18:11:15 +1000 Subject: xfs: remove the unused XFS_TRANS_NOSLEEP/XFS_TRANS_WAIT flags Signed-off-by: Christoph Hellwig Reviewed-by: Dave Chinner --- fs/xfs/xfs_trans.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'fs') diff --git a/fs/xfs/xfs_trans.h b/fs/xfs/xfs_trans.h index aa6f422f0361..c13c0f97b494 100644 --- a/fs/xfs/xfs_trans.h +++ b/fs/xfs/xfs_trans.h @@ -184,8 +184,6 @@ struct xfs_log_item_desc { /* * Values for call flags parameter. */ -#define XFS_TRANS_NOSLEEP 0x1 -#define XFS_TRANS_WAIT 0x2 #define XFS_TRANS_RELEASE_LOG_RES 0x4 #define XFS_TRANS_ABORT 0x8 -- cgit v1.2.3 From cd8b0bb3c49d0691e9e7b4cf19e21ca63b92c053 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 23 Jun 2010 18:11:15 +1000 Subject: xfs: remove unused XFS_BMAPI_ flags Signed-off-by: Christoph Hellwig Reviewed-by: Dave Chinner --- fs/xfs/xfs_bmap.c | 8 +------- fs/xfs/xfs_bmap.h | 17 ++++++----------- fs/xfs/xfs_inode.c | 3 +-- 3 files changed, 8 insertions(+), 20 deletions(-) (limited to 'fs') diff --git a/fs/xfs/xfs_bmap.c b/fs/xfs/xfs_bmap.c index e0389656ad2c..598a30ba3141 100644 --- a/fs/xfs/xfs_bmap.c +++ b/fs/xfs/xfs_bmap.c @@ -4618,19 +4618,13 @@ xfs_bmapi( * allocate the stuff asked for in this bmap call * but that wouldn't be as good. */ - if (wasdelay && !(flags & XFS_BMAPI_EXACT)) { + if (wasdelay) { alen = (xfs_extlen_t)got.br_blockcount; aoff = got.br_startoff; if (lastx != NULLEXTNUM && lastx) { ep = xfs_iext_get_ext(ifp, lastx - 1); xfs_bmbt_get_all(ep, &prev); } - } else if (wasdelay) { - alen = (xfs_extlen_t) - XFS_FILBLKS_MIN(len, - (got.br_startoff + - got.br_blockcount) - bno); - aoff = bno; } else { alen = (xfs_extlen_t) XFS_FILBLKS_MIN(len, MAXEXTLEN); diff --git a/fs/xfs/xfs_bmap.h b/fs/xfs/xfs_bmap.h index 419dafb9d87d..d9c8a39b2855 100644 --- a/fs/xfs/xfs_bmap.h +++ b/fs/xfs/xfs_bmap.h @@ -82,16 +82,13 @@ typedef struct xfs_bmap_free #define XFS_BMAPI_DELAY 0x002 /* delayed write operation */ #define XFS_BMAPI_ENTIRE 0x004 /* return entire extent, not trimmed */ #define XFS_BMAPI_METADATA 0x008 /* mapping metadata not user data */ -#define XFS_BMAPI_EXACT 0x010 /* allocate only to spec'd bounds */ -#define XFS_BMAPI_ATTRFORK 0x020 /* use attribute fork not data */ -#define XFS_BMAPI_ASYNC 0x040 /* bunmapi xactions can be async */ -#define XFS_BMAPI_RSVBLOCKS 0x080 /* OK to alloc. reserved data blocks */ -#define XFS_BMAPI_PREALLOC 0x100 /* preallocation op: unwritten space */ -#define XFS_BMAPI_IGSTATE 0x200 /* Ignore state - */ +#define XFS_BMAPI_ATTRFORK 0x010 /* use attribute fork not data */ +#define XFS_BMAPI_RSVBLOCKS 0x020 /* OK to alloc. reserved data blocks */ +#define XFS_BMAPI_PREALLOC 0x040 /* preallocation op: unwritten space */ +#define XFS_BMAPI_IGSTATE 0x080 /* Ignore state - */ /* combine contig. space */ -#define XFS_BMAPI_CONTIG 0x400 /* must allocate only one extent */ -/* XFS_BMAPI_DIRECT_IO 0x800 */ -#define XFS_BMAPI_CONVERT 0x1000 /* unwritten extent conversion - */ +#define XFS_BMAPI_CONTIG 0x100 /* must allocate only one extent */ +#define XFS_BMAPI_CONVERT 0x200 /* unwritten extent conversion - */ /* need write cache flushing and no */ /* additional allocation alignments */ @@ -100,9 +97,7 @@ typedef struct xfs_bmap_free { XFS_BMAPI_DELAY, "DELAY" }, \ { XFS_BMAPI_ENTIRE, "ENTIRE" }, \ { XFS_BMAPI_METADATA, "METADATA" }, \ - { XFS_BMAPI_EXACT, "EXACT" }, \ { XFS_BMAPI_ATTRFORK, "ATTRFORK" }, \ - { XFS_BMAPI_ASYNC, "ASYNC" }, \ { XFS_BMAPI_RSVBLOCKS, "RSVBLOCKS" }, \ { XFS_BMAPI_PREALLOC, "PREALLOC" }, \ { XFS_BMAPI_IGSTATE, "IGSTATE" }, \ diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index d22b580162cc..1e6ae68f91af 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -1585,8 +1585,7 @@ xfs_itruncate_finish( xfs_bmap_init(&free_list, &first_block); error = xfs_bunmapi(ntp, ip, first_unmap_block, unmap_len, - xfs_bmapi_aflag(fork) | - (sync ? 0 : XFS_BMAPI_ASYNC), + xfs_bmapi_aflag(fork), XFS_ITRUNC_MAX_EXTENTS, &first_block, &free_list, NULL, &done); -- cgit v1.2.3 From b4e9181e772b0c8b9038c5822ead368b96c2b533 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 23 Jun 2010 18:11:15 +1000 Subject: xfs: remove unused delta tracking code in xfs_bmapi This code was introduced four years ago in commit 3e57ecf640428c01ba1ed8c8fc538447ada1715b without any review and has been unused since. Remove it just as the rest of the code introduced in that commit to reduce that stack usage and complexity in this central piece of code. Signed-off-by: Christoph Hellwig Reviewed-by: Dave Chinner --- fs/xfs/linux-2.6/xfs_aops.c | 4 +- fs/xfs/linux-2.6/xfs_file.c | 4 +- fs/xfs/quota/xfs_dquot.c | 4 +- fs/xfs/quota/xfs_qm.c | 2 +- fs/xfs/xfs_attr.c | 10 +- fs/xfs/xfs_attr_leaf.c | 2 +- fs/xfs/xfs_bmap.c | 217 ++++---------------------------------------- fs/xfs/xfs_bmap.h | 20 +--- fs/xfs/xfs_da_btree.c | 9 +- fs/xfs/xfs_dir2.c | 7 +- fs/xfs/xfs_dir2_leaf.c | 2 +- fs/xfs/xfs_inode.c | 4 +- fs/xfs/xfs_iomap.c | 12 +-- fs/xfs/xfs_rtalloc.c | 2 +- fs/xfs/xfs_vnodeops.c | 20 ++-- 15 files changed, 59 insertions(+), 260 deletions(-) (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_aops.c b/fs/xfs/linux-2.6/xfs_aops.c index e42c0ba6688a..b25d11a3d84e 100644 --- a/fs/xfs/linux-2.6/xfs_aops.c +++ b/fs/xfs/linux-2.6/xfs_aops.c @@ -974,7 +974,7 @@ xfs_aops_discard_page( */ error = xfs_bmapi(NULL, ip, offset_fsb, 1, XFS_BMAPI_ENTIRE, NULL, 0, &imap, - &nimaps, NULL, NULL); + &nimaps, NULL); if (error) { /* something screwed, just bail */ @@ -1002,7 +1002,7 @@ xfs_aops_discard_page( */ xfs_bmap_init(&flist, &firstblock); error = xfs_bunmapi(NULL, ip, offset_fsb, 1, 0, 1, &firstblock, - &flist, NULL, &done); + &flist, &done); ASSERT(!flist.xbf_count && !flist.xbf_first); if (error) { diff --git a/fs/xfs/linux-2.6/xfs_file.c b/fs/xfs/linux-2.6/xfs_file.c index 9a9b446a58a7..22edad7a0bec 100644 --- a/fs/xfs/linux-2.6/xfs_file.c +++ b/fs/xfs/linux-2.6/xfs_file.c @@ -414,7 +414,7 @@ xfs_zero_last_block( last_fsb = XFS_B_TO_FSBT(mp, isize); nimaps = 1; error = xfs_bmapi(NULL, ip, last_fsb, 1, 0, NULL, 0, &imap, - &nimaps, NULL, NULL); + &nimaps, NULL); if (error) { return error; } @@ -509,7 +509,7 @@ xfs_zero_eof( nimaps = 1; zero_count_fsb = end_zero_fsb - start_zero_fsb + 1; error = xfs_bmapi(NULL, ip, start_zero_fsb, zero_count_fsb, - 0, NULL, 0, &imap, &nimaps, NULL, NULL); + 0, NULL, 0, &imap, &nimaps, NULL); if (error) { ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL|XFS_IOLOCK_EXCL)); return error; diff --git a/fs/xfs/quota/xfs_dquot.c b/fs/xfs/quota/xfs_dquot.c index 56f366e327f3..e1a2f6800e01 100644 --- a/fs/xfs/quota/xfs_dquot.c +++ b/fs/xfs/quota/xfs_dquot.c @@ -385,7 +385,7 @@ xfs_qm_dqalloc( XFS_BMAPI_METADATA | XFS_BMAPI_WRITE, &firstblock, XFS_QM_DQALLOC_SPACE_RES(mp), - &map, &nmaps, &flist, NULL))) { + &map, &nmaps, &flist))) { goto error0; } ASSERT(map.br_blockcount == XFS_DQUOT_CLUSTER_SIZE_FSB); @@ -501,7 +501,7 @@ xfs_qm_dqtobp( error = xfs_bmapi(NULL, quotip, dqp->q_fileoffset, XFS_DQUOT_CLUSTER_SIZE_FSB, XFS_BMAPI_METADATA, - NULL, 0, &map, &nmaps, NULL, NULL); + NULL, 0, &map, &nmaps, NULL); xfs_iunlock(quotip, XFS_ILOCK_SHARED); if (error) diff --git a/fs/xfs/quota/xfs_qm.c b/fs/xfs/quota/xfs_qm.c index b32c3fb9e779..7a33d65e2d28 100644 --- a/fs/xfs/quota/xfs_qm.c +++ b/fs/xfs/quota/xfs_qm.c @@ -1490,7 +1490,7 @@ xfs_qm_dqiterate( maxlblkcnt - lblkno, XFS_BMAPI_METADATA, NULL, - 0, map, &nmaps, NULL, NULL); + 0, map, &nmaps, NULL); xfs_iunlock(qip, XFS_ILOCK_SHARED); if (error) break; diff --git a/fs/xfs/xfs_attr.c b/fs/xfs/xfs_attr.c index f3ca7186155a..c2568242a901 100644 --- a/fs/xfs/xfs_attr.c +++ b/fs/xfs/xfs_attr.c @@ -1977,7 +1977,7 @@ xfs_attr_rmtval_get(xfs_da_args_t *args) error = xfs_bmapi(args->trans, args->dp, (xfs_fileoff_t)lblkno, args->rmtblkcnt, XFS_BMAPI_ATTRFORK | XFS_BMAPI_METADATA, - NULL, 0, map, &nmap, NULL, NULL); + NULL, 0, map, &nmap, NULL); if (error) return(error); ASSERT(nmap >= 1); @@ -2056,7 +2056,7 @@ xfs_attr_rmtval_set(xfs_da_args_t *args) XFS_BMAPI_ATTRFORK | XFS_BMAPI_METADATA | XFS_BMAPI_WRITE, args->firstblock, args->total, &map, &nmap, - args->flist, NULL); + args->flist); if (!error) { error = xfs_bmap_finish(&args->trans, args->flist, &committed); @@ -2107,7 +2107,7 @@ xfs_attr_rmtval_set(xfs_da_args_t *args) args->rmtblkcnt, XFS_BMAPI_ATTRFORK | XFS_BMAPI_METADATA, args->firstblock, 0, &map, &nmap, - NULL, NULL); + NULL); if (error) { return(error); } @@ -2172,7 +2172,7 @@ xfs_attr_rmtval_remove(xfs_da_args_t *args) args->rmtblkcnt, XFS_BMAPI_ATTRFORK | XFS_BMAPI_METADATA, args->firstblock, 0, &map, &nmap, - args->flist, NULL); + args->flist); if (error) { return(error); } @@ -2210,7 +2210,7 @@ xfs_attr_rmtval_remove(xfs_da_args_t *args) error = xfs_bunmapi(args->trans, args->dp, lblkno, blkcnt, XFS_BMAPI_ATTRFORK | XFS_BMAPI_METADATA, 1, args->firstblock, args->flist, - NULL, &done); + &done); if (!error) { error = xfs_bmap_finish(&args->trans, args->flist, &committed); diff --git a/fs/xfs/xfs_attr_leaf.c b/fs/xfs/xfs_attr_leaf.c index e4d48b2cee19..a6cff8edcdb6 100644 --- a/fs/xfs/xfs_attr_leaf.c +++ b/fs/xfs/xfs_attr_leaf.c @@ -2928,7 +2928,7 @@ xfs_attr_leaf_freextent(xfs_trans_t **trans, xfs_inode_t *dp, nmap = 1; error = xfs_bmapi(*trans, dp, (xfs_fileoff_t)tblkno, tblkcnt, XFS_BMAPI_ATTRFORK | XFS_BMAPI_METADATA, - NULL, 0, &map, &nmap, NULL, NULL); + NULL, 0, &map, &nmap, NULL); if (error) { return(error); } diff --git a/fs/xfs/xfs_bmap.c b/fs/xfs/xfs_bmap.c index 598a30ba3141..d74fbec80622 100644 --- a/fs/xfs/xfs_bmap.c +++ b/fs/xfs/xfs_bmap.c @@ -101,7 +101,6 @@ xfs_bmap_add_extent( xfs_fsblock_t *first, /* pointer to firstblock variable */ xfs_bmap_free_t *flist, /* list of extents to be freed */ int *logflagsp, /* inode logging flags */ - xfs_extdelta_t *delta, /* Change made to incore extents */ int whichfork, /* data or attr fork */ int rsvd); /* OK to allocate reserved blocks */ @@ -119,7 +118,6 @@ xfs_bmap_add_extent_delay_real( xfs_fsblock_t *first, /* pointer to firstblock variable */ xfs_bmap_free_t *flist, /* list of extents to be freed */ int *logflagsp, /* inode logging flags */ - xfs_extdelta_t *delta, /* Change made to incore extents */ int rsvd); /* OK to allocate reserved blocks */ /* @@ -132,7 +130,6 @@ xfs_bmap_add_extent_hole_delay( xfs_extnum_t idx, /* extent number to update/insert */ xfs_bmbt_irec_t *new, /* new data to add to file extents */ int *logflagsp,/* inode logging flags */ - xfs_extdelta_t *delta, /* Change made to incore extents */ int rsvd); /* OK to allocate reserved blocks */ /* @@ -146,7 +143,6 @@ xfs_bmap_add_extent_hole_real( xfs_btree_cur_t *cur, /* if null, not a btree */ xfs_bmbt_irec_t *new, /* new data to add to file extents */ int *logflagsp, /* inode logging flags */ - xfs_extdelta_t *delta, /* Change made to incore extents */ int whichfork); /* data or attr fork */ /* @@ -159,8 +155,7 @@ xfs_bmap_add_extent_unwritten_real( xfs_extnum_t idx, /* extent number to update/insert */ xfs_btree_cur_t **curp, /* if *curp is null, not a btree */ xfs_bmbt_irec_t *new, /* new data to add to file extents */ - int *logflagsp, /* inode logging flags */ - xfs_extdelta_t *delta); /* Change made to incore extents */ + int *logflagsp); /* inode logging flags */ /* * xfs_bmap_alloc is called by xfs_bmapi to allocate an extent for a file. @@ -197,7 +192,6 @@ xfs_bmap_del_extent( xfs_btree_cur_t *cur, /* if null, not a btree */ xfs_bmbt_irec_t *new, /* new data to add to file extents */ int *logflagsp,/* inode logging flags */ - xfs_extdelta_t *delta, /* Change made to incore extents */ int whichfork, /* data or attr fork */ int rsvd); /* OK to allocate reserved blocks */ @@ -486,7 +480,6 @@ xfs_bmap_add_extent( xfs_fsblock_t *first, /* pointer to firstblock variable */ xfs_bmap_free_t *flist, /* list of extents to be freed */ int *logflagsp, /* inode logging flags */ - xfs_extdelta_t *delta, /* Change made to incore extents */ int whichfork, /* data or attr fork */ int rsvd) /* OK to use reserved data blocks */ { @@ -521,15 +514,6 @@ xfs_bmap_add_extent( logflags = XFS_ILOG_CORE | xfs_ilog_fext(whichfork); } else logflags = 0; - /* DELTA: single new extent */ - if (delta) { - if (delta->xed_startoff > new->br_startoff) - delta->xed_startoff = new->br_startoff; - if (delta->xed_blockcount < - new->br_startoff + new->br_blockcount) - delta->xed_blockcount = new->br_startoff + - new->br_blockcount; - } } /* * Any kind of new delayed allocation goes here. @@ -539,7 +523,7 @@ xfs_bmap_add_extent( ASSERT((cur->bc_private.b.flags & XFS_BTCUR_BPRV_WASDEL) == 0); if ((error = xfs_bmap_add_extent_hole_delay(ip, idx, new, - &logflags, delta, rsvd))) + &logflags, rsvd))) goto done; } /* @@ -550,7 +534,7 @@ xfs_bmap_add_extent( ASSERT((cur->bc_private.b.flags & XFS_BTCUR_BPRV_WASDEL) == 0); if ((error = xfs_bmap_add_extent_hole_real(ip, idx, cur, new, - &logflags, delta, whichfork))) + &logflags, whichfork))) goto done; } else { xfs_bmbt_irec_t prev; /* old extent at offset idx */ @@ -575,17 +559,17 @@ xfs_bmap_add_extent( XFS_BTCUR_BPRV_WASDEL); if ((error = xfs_bmap_add_extent_delay_real(ip, idx, &cur, new, &da_new, first, flist, - &logflags, delta, rsvd))) + &logflags, rsvd))) goto done; } else if (new->br_state == XFS_EXT_NORM) { ASSERT(new->br_state == XFS_EXT_NORM); if ((error = xfs_bmap_add_extent_unwritten_real( - ip, idx, &cur, new, &logflags, delta))) + ip, idx, &cur, new, &logflags))) goto done; } else { ASSERT(new->br_state == XFS_EXT_UNWRITTEN); if ((error = xfs_bmap_add_extent_unwritten_real( - ip, idx, &cur, new, &logflags, delta))) + ip, idx, &cur, new, &logflags))) goto done; } ASSERT(*curp == cur || *curp == NULL); @@ -598,7 +582,7 @@ xfs_bmap_add_extent( ASSERT((cur->bc_private.b.flags & XFS_BTCUR_BPRV_WASDEL) == 0); if ((error = xfs_bmap_add_extent_hole_real(ip, idx, cur, - new, &logflags, delta, whichfork))) + new, &logflags, whichfork))) goto done; } } @@ -663,7 +647,6 @@ xfs_bmap_add_extent_delay_real( xfs_fsblock_t *first, /* pointer to firstblock variable */ xfs_bmap_free_t *flist, /* list of extents to be freed */ int *logflagsp, /* inode logging flags */ - xfs_extdelta_t *delta, /* Change made to incore extents */ int rsvd) /* OK to use reserved data block allocation */ { xfs_btree_cur_t *cur; /* btree cursor */ @@ -794,11 +777,6 @@ xfs_bmap_add_extent_delay_real( goto done; } *dnew = 0; - /* DELTA: Three in-core extents are replaced by one. */ - temp = LEFT.br_startoff; - temp2 = LEFT.br_blockcount + - PREV.br_blockcount + - RIGHT.br_blockcount; break; case BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING | BMAP_LEFT_CONTIG: @@ -829,10 +807,6 @@ xfs_bmap_add_extent_delay_real( goto done; } *dnew = 0; - /* DELTA: Two in-core extents are replaced by one. */ - temp = LEFT.br_startoff; - temp2 = LEFT.br_blockcount + - PREV.br_blockcount; break; case BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING | BMAP_RIGHT_CONTIG: @@ -864,10 +838,6 @@ xfs_bmap_add_extent_delay_real( goto done; } *dnew = 0; - /* DELTA: Two in-core extents are replaced by one. */ - temp = PREV.br_startoff; - temp2 = PREV.br_blockcount + - RIGHT.br_blockcount; break; case BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING: @@ -897,9 +867,6 @@ xfs_bmap_add_extent_delay_real( XFS_WANT_CORRUPTED_GOTO(i == 1, done); } *dnew = 0; - /* DELTA: The in-core extent described by new changed type. */ - temp = new->br_startoff; - temp2 = new->br_blockcount; break; case BMAP_LEFT_FILLING | BMAP_LEFT_CONTIG: @@ -939,10 +906,6 @@ xfs_bmap_add_extent_delay_real( xfs_bmbt_set_startblock(ep, nullstartblock((int)temp)); trace_xfs_bmap_post_update(ip, idx, state, _THIS_IP_); *dnew = temp; - /* DELTA: The boundary between two in-core extents moved. */ - temp = LEFT.br_startoff; - temp2 = LEFT.br_blockcount + - PREV.br_blockcount; break; case BMAP_LEFT_FILLING: @@ -987,9 +950,6 @@ xfs_bmap_add_extent_delay_real( xfs_bmbt_set_startblock(ep, nullstartblock((int)temp)); trace_xfs_bmap_post_update(ip, idx + 1, state, _THIS_IP_); *dnew = temp; - /* DELTA: One in-core extent is split in two. */ - temp = PREV.br_startoff; - temp2 = PREV.br_blockcount; break; case BMAP_RIGHT_FILLING | BMAP_RIGHT_CONTIG: @@ -1028,10 +988,6 @@ xfs_bmap_add_extent_delay_real( xfs_bmbt_set_startblock(ep, nullstartblock((int)temp)); trace_xfs_bmap_post_update(ip, idx, state, _THIS_IP_); *dnew = temp; - /* DELTA: The boundary between two in-core extents moved. */ - temp = PREV.br_startoff; - temp2 = PREV.br_blockcount + - RIGHT.br_blockcount; break; case BMAP_RIGHT_FILLING: @@ -1075,9 +1031,6 @@ xfs_bmap_add_extent_delay_real( xfs_bmbt_set_startblock(ep, nullstartblock((int)temp)); trace_xfs_bmap_post_update(ip, idx, state, _THIS_IP_); *dnew = temp; - /* DELTA: One in-core extent is split in two. */ - temp = PREV.br_startoff; - temp2 = PREV.br_blockcount; break; case 0: @@ -1158,9 +1111,6 @@ xfs_bmap_add_extent_delay_real( nullstartblock((int)temp2)); trace_xfs_bmap_post_update(ip, idx + 2, state, _THIS_IP_); *dnew = temp + temp2; - /* DELTA: One in-core extent is split in three. */ - temp = PREV.br_startoff; - temp2 = PREV.br_blockcount; break; case BMAP_LEFT_FILLING | BMAP_LEFT_CONTIG | BMAP_RIGHT_CONTIG: @@ -1176,13 +1126,6 @@ xfs_bmap_add_extent_delay_real( ASSERT(0); } *curp = cur; - if (delta) { - temp2 += temp; - if (delta->xed_startoff > temp) - delta->xed_startoff = temp; - if (delta->xed_blockcount < temp2) - delta->xed_blockcount = temp2; - } done: *logflagsp = rval; return error; @@ -1201,8 +1144,7 @@ xfs_bmap_add_extent_unwritten_real( xfs_extnum_t idx, /* extent number to update/insert */ xfs_btree_cur_t **curp, /* if *curp is null, not a btree */ xfs_bmbt_irec_t *new, /* new data to add to file extents */ - int *logflagsp, /* inode logging flags */ - xfs_extdelta_t *delta) /* Change made to incore extents */ + int *logflagsp) /* inode logging flags */ { xfs_btree_cur_t *cur; /* btree cursor */ xfs_bmbt_rec_host_t *ep; /* extent entry for idx */ @@ -1216,8 +1158,6 @@ xfs_bmap_add_extent_unwritten_real( /* left is 0, right is 1, prev is 2 */ int rval=0; /* return value (logging flags) */ int state = 0;/* state bits, accessed thru macros */ - xfs_filblks_t temp=0; - xfs_filblks_t temp2=0; #define LEFT r[0] #define RIGHT r[1] @@ -1338,11 +1278,6 @@ xfs_bmap_add_extent_unwritten_real( RIGHT.br_blockcount, LEFT.br_state))) goto done; } - /* DELTA: Three in-core extents are replaced by one. */ - temp = LEFT.br_startoff; - temp2 = LEFT.br_blockcount + - PREV.br_blockcount + - RIGHT.br_blockcount; break; case BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING | BMAP_LEFT_CONTIG: @@ -1379,10 +1314,6 @@ xfs_bmap_add_extent_unwritten_real( LEFT.br_state))) goto done; } - /* DELTA: Two in-core extents are replaced by one. */ - temp = LEFT.br_startoff; - temp2 = LEFT.br_blockcount + - PREV.br_blockcount; break; case BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING | BMAP_RIGHT_CONTIG: @@ -1419,10 +1350,6 @@ xfs_bmap_add_extent_unwritten_real( newext))) goto done; } - /* DELTA: Two in-core extents are replaced by one. */ - temp = PREV.br_startoff; - temp2 = PREV.br_blockcount + - RIGHT.br_blockcount; break; case BMAP_LEFT_FILLING | BMAP_RIGHT_FILLING: @@ -1450,9 +1377,6 @@ xfs_bmap_add_extent_unwritten_real( newext))) goto done; } - /* DELTA: The in-core extent described by new changed type. */ - temp = new->br_startoff; - temp2 = new->br_blockcount; break; case BMAP_LEFT_FILLING | BMAP_LEFT_CONTIG: @@ -1498,10 +1422,6 @@ xfs_bmap_add_extent_unwritten_real( LEFT.br_state)) goto done; } - /* DELTA: The boundary between two in-core extents moved. */ - temp = LEFT.br_startoff; - temp2 = LEFT.br_blockcount + - PREV.br_blockcount; break; case BMAP_LEFT_FILLING: @@ -1541,9 +1461,6 @@ xfs_bmap_add_extent_unwritten_real( goto done; XFS_WANT_CORRUPTED_GOTO(i == 1, done); } - /* DELTA: One in-core extent is split in two. */ - temp = PREV.br_startoff; - temp2 = PREV.br_blockcount; break; case BMAP_RIGHT_FILLING | BMAP_RIGHT_CONTIG: @@ -1584,10 +1501,6 @@ xfs_bmap_add_extent_unwritten_real( newext))) goto done; } - /* DELTA: The boundary between two in-core extents moved. */ - temp = PREV.br_startoff; - temp2 = PREV.br_blockcount + - RIGHT.br_blockcount; break; case BMAP_RIGHT_FILLING: @@ -1627,9 +1540,6 @@ xfs_bmap_add_extent_unwritten_real( goto done; XFS_WANT_CORRUPTED_GOTO(i == 1, done); } - /* DELTA: One in-core extent is split in two. */ - temp = PREV.br_startoff; - temp2 = PREV.br_blockcount; break; case 0: @@ -1689,9 +1599,6 @@ xfs_bmap_add_extent_unwritten_real( goto done; XFS_WANT_CORRUPTED_GOTO(i == 1, done); } - /* DELTA: One in-core extent is split in three. */ - temp = PREV.br_startoff; - temp2 = PREV.br_blockcount; break; case BMAP_LEFT_FILLING | BMAP_LEFT_CONTIG | BMAP_RIGHT_CONTIG: @@ -1707,13 +1614,6 @@ xfs_bmap_add_extent_unwritten_real( ASSERT(0); } *curp = cur; - if (delta) { - temp2 += temp; - if (delta->xed_startoff > temp) - delta->xed_startoff = temp; - if (delta->xed_blockcount < temp2) - delta->xed_blockcount = temp2; - } done: *logflagsp = rval; return error; @@ -1733,7 +1633,6 @@ xfs_bmap_add_extent_hole_delay( xfs_extnum_t idx, /* extent number to update/insert */ xfs_bmbt_irec_t *new, /* new data to add to file extents */ int *logflagsp, /* inode logging flags */ - xfs_extdelta_t *delta, /* Change made to incore extents */ int rsvd) /* OK to allocate reserved blocks */ { xfs_bmbt_rec_host_t *ep; /* extent record for idx */ @@ -1744,7 +1643,6 @@ xfs_bmap_add_extent_hole_delay( xfs_bmbt_irec_t right; /* right neighbor extent entry */ int state; /* state bits, accessed thru macros */ xfs_filblks_t temp=0; /* temp for indirect calculations */ - xfs_filblks_t temp2=0; ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK); ep = xfs_iext_get_ext(ifp, idx); @@ -1816,9 +1714,6 @@ xfs_bmap_add_extent_hole_delay( xfs_iext_remove(ip, idx, 1, state); ip->i_df.if_lastex = idx - 1; - /* DELTA: Two in-core extents were replaced by one. */ - temp2 = temp; - temp = left.br_startoff; break; case BMAP_LEFT_CONTIG: @@ -1838,9 +1733,6 @@ xfs_bmap_add_extent_hole_delay( trace_xfs_bmap_post_update(ip, idx - 1, state, _THIS_IP_); ip->i_df.if_lastex = idx - 1; - /* DELTA: One in-core extent grew into a hole. */ - temp2 = temp; - temp = left.br_startoff; break; case BMAP_RIGHT_CONTIG: @@ -1859,9 +1751,6 @@ xfs_bmap_add_extent_hole_delay( trace_xfs_bmap_post_update(ip, idx, state, _THIS_IP_); ip->i_df.if_lastex = idx; - /* DELTA: One in-core extent grew into a hole. */ - temp2 = temp; - temp = new->br_startoff; break; case 0: @@ -1873,9 +1762,6 @@ xfs_bmap_add_extent_hole_delay( oldlen = newlen = 0; xfs_iext_insert(ip, idx, 1, new, state); ip->i_df.if_lastex = idx; - /* DELTA: A new in-core extent was added in a hole. */ - temp2 = new->br_blockcount; - temp = new->br_startoff; break; } if (oldlen != newlen) { @@ -1886,13 +1772,6 @@ xfs_bmap_add_extent_hole_delay( * Nothing to do for disk quota accounting here. */ } - if (delta) { - temp2 += temp; - if (delta->xed_startoff > temp) - delta->xed_startoff = temp; - if (delta->xed_blockcount < temp2) - delta->xed_blockcount = temp2; - } *logflagsp = 0; return 0; } @@ -1908,7 +1787,6 @@ xfs_bmap_add_extent_hole_real( xfs_btree_cur_t *cur, /* if null, not a btree */ xfs_bmbt_irec_t *new, /* new data to add to file extents */ int *logflagsp, /* inode logging flags */ - xfs_extdelta_t *delta, /* Change made to incore extents */ int whichfork) /* data or attr fork */ { xfs_bmbt_rec_host_t *ep; /* pointer to extent entry ins. point */ @@ -1919,8 +1797,6 @@ xfs_bmap_add_extent_hole_real( xfs_bmbt_irec_t right; /* right neighbor extent entry */ int rval=0; /* return value (logging flags) */ int state; /* state bits, accessed thru macros */ - xfs_filblks_t temp=0; - xfs_filblks_t temp2=0; ifp = XFS_IFORK_PTR(ip, whichfork); ASSERT(idx <= ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t)); @@ -2017,11 +1893,6 @@ xfs_bmap_add_extent_hole_real( left.br_state))) goto done; } - /* DELTA: Two in-core extents were replaced by one. */ - temp = left.br_startoff; - temp2 = left.br_blockcount + - new->br_blockcount + - right.br_blockcount; break; case BMAP_LEFT_CONTIG: @@ -2053,10 +1924,6 @@ xfs_bmap_add_extent_hole_real( left.br_state))) goto done; } - /* DELTA: One in-core extent grew. */ - temp = left.br_startoff; - temp2 = left.br_blockcount + - new->br_blockcount; break; case BMAP_RIGHT_CONTIG: @@ -2089,10 +1956,6 @@ xfs_bmap_add_extent_hole_real( right.br_state))) goto done; } - /* DELTA: One in-core extent grew. */ - temp = new->br_startoff; - temp2 = new->br_blockcount + - right.br_blockcount; break; case 0: @@ -2120,18 +1983,8 @@ xfs_bmap_add_extent_hole_real( goto done; XFS_WANT_CORRUPTED_GOTO(i == 1, done); } - /* DELTA: A new extent was added in a hole. */ - temp = new->br_startoff; - temp2 = new->br_blockcount; break; } - if (delta) { - temp2 += temp; - if (delta->xed_startoff > temp) - delta->xed_startoff = temp; - if (delta->xed_blockcount < temp2) - delta->xed_blockcount = temp2; - } done: *logflagsp = rval; return error; @@ -2956,7 +2809,6 @@ xfs_bmap_del_extent( xfs_btree_cur_t *cur, /* if null, not a btree */ xfs_bmbt_irec_t *del, /* data to remove from extents */ int *logflagsp, /* inode logging flags */ - xfs_extdelta_t *delta, /* Change made to incore extents */ int whichfork, /* data or attr fork */ int rsvd) /* OK to allocate reserved blocks */ { @@ -3262,14 +3114,6 @@ xfs_bmap_del_extent( if (da_old > da_new) xfs_mod_incore_sb(mp, XFS_SBS_FDBLOCKS, (int64_t)(da_old - da_new), rsvd); - if (delta) { - /* DELTA: report the original extent. */ - if (delta->xed_startoff > got.br_startoff) - delta->xed_startoff = got.br_startoff; - if (delta->xed_blockcount < got.br_startoff+got.br_blockcount) - delta->xed_blockcount = got.br_startoff + - got.br_blockcount; - } done: *logflagsp = flags; return error; @@ -4481,8 +4325,7 @@ xfs_bmapi( xfs_extlen_t total, /* total blocks needed */ xfs_bmbt_irec_t *mval, /* output: map values */ int *nmap, /* i/o: mval size/count */ - xfs_bmap_free_t *flist, /* i/o: list extents to free */ - xfs_extdelta_t *delta) /* o: change made to incore extents */ + xfs_bmap_free_t *flist) /* i/o: list extents to free */ { xfs_fsblock_t abno; /* allocated block number */ xfs_extlen_t alen; /* allocated extent length */ @@ -4594,10 +4437,7 @@ xfs_bmapi( end = bno + len; obno = bno; bma.ip = NULL; - if (delta) { - delta->xed_startoff = NULLFILEOFF; - delta->xed_blockcount = 0; - } + while (bno < end && n < *nmap) { /* * Reading past eof, act as though there's a hole @@ -4823,7 +4663,7 @@ xfs_bmapi( got.br_state = XFS_EXT_UNWRITTEN; } error = xfs_bmap_add_extent(ip, lastx, &cur, &got, - firstblock, flist, &tmp_logflags, delta, + firstblock, flist, &tmp_logflags, whichfork, (flags & XFS_BMAPI_RSVBLOCKS)); logflags |= tmp_logflags; if (error) @@ -4919,7 +4759,7 @@ xfs_bmapi( } mval->br_state = XFS_EXT_NORM; error = xfs_bmap_add_extent(ip, lastx, &cur, mval, - firstblock, flist, &tmp_logflags, delta, + firstblock, flist, &tmp_logflags, whichfork, (flags & XFS_BMAPI_RSVBLOCKS)); logflags |= tmp_logflags; if (error) @@ -5009,14 +4849,6 @@ xfs_bmapi( ASSERT(XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_BTREE || XFS_IFORK_NEXTENTS(ip, whichfork) > ifp->if_ext_max); error = 0; - if (delta && delta->xed_startoff != NULLFILEOFF) { - /* A change was actually made. - * Note that delta->xed_blockount is an offset at this - * point and needs to be converted to a block count. - */ - ASSERT(delta->xed_blockcount > delta->xed_startoff); - delta->xed_blockcount -= delta->xed_startoff; - } error0: /* * Log everything. Do this after conversion, there's no point in @@ -5128,8 +4960,6 @@ xfs_bunmapi( xfs_fsblock_t *firstblock, /* first allocated block controls a.g. for allocs */ xfs_bmap_free_t *flist, /* i/o: list extents to free */ - xfs_extdelta_t *delta, /* o: change made to incore - extents */ int *done) /* set if not done yet */ { xfs_btree_cur_t *cur; /* bmap btree cursor */ @@ -5188,10 +5018,7 @@ xfs_bunmapi( bno = start + len - 1; ep = xfs_bmap_search_extents(ip, bno, whichfork, &eof, &lastx, &got, &prev); - if (delta) { - delta->xed_startoff = NULLFILEOFF; - delta->xed_blockcount = 0; - } + /* * Check to see if the given block number is past the end of the * file, back up to the last block if so... @@ -5289,7 +5116,7 @@ xfs_bunmapi( } del.br_state = XFS_EXT_UNWRITTEN; error = xfs_bmap_add_extent(ip, lastx, &cur, &del, - firstblock, flist, &logflags, delta, + firstblock, flist, &logflags, XFS_DATA_FORK, 0); if (error) goto error0; @@ -5344,7 +5171,7 @@ xfs_bunmapi( prev.br_state = XFS_EXT_UNWRITTEN; error = xfs_bmap_add_extent(ip, lastx - 1, &cur, &prev, firstblock, flist, &logflags, - delta, XFS_DATA_FORK, 0); + XFS_DATA_FORK, 0); if (error) goto error0; goto nodelete; @@ -5353,7 +5180,7 @@ xfs_bunmapi( del.br_state = XFS_EXT_UNWRITTEN; error = xfs_bmap_add_extent(ip, lastx, &cur, &del, firstblock, flist, &logflags, - delta, XFS_DATA_FORK, 0); + XFS_DATA_FORK, 0); if (error) goto error0; goto nodelete; @@ -5406,7 +5233,7 @@ xfs_bunmapi( goto error0; } error = xfs_bmap_del_extent(ip, tp, lastx, flist, cur, &del, - &tmp_logflags, delta, whichfork, rsvd); + &tmp_logflags, whichfork, rsvd); logflags |= tmp_logflags; if (error) goto error0; @@ -5463,14 +5290,6 @@ nodelete: ASSERT(ifp->if_ext_max == XFS_IFORK_SIZE(ip, whichfork) / (uint)sizeof(xfs_bmbt_rec_t)); error = 0; - if (delta && delta->xed_startoff != NULLFILEOFF) { - /* A change was actually made. - * Note that delta->xed_blockount is an offset at this - * point and needs to be converted to a block count. - */ - ASSERT(delta->xed_blockcount > delta->xed_startoff); - delta->xed_blockcount -= delta->xed_startoff; - } error0: /* * Log everything. Do this after conversion, there's no point in @@ -5683,7 +5502,7 @@ xfs_getbmap( error = xfs_bmapi(NULL, ip, XFS_BB_TO_FSBT(mp, bmv->bmv_offset), XFS_BB_TO_FSB(mp, bmv->bmv_length), bmapi_flags, NULL, 0, map, &nmap, - NULL, NULL); + NULL); if (error) goto out_free_map; ASSERT(nmap <= subnex); diff --git a/fs/xfs/xfs_bmap.h b/fs/xfs/xfs_bmap.h index d9c8a39b2855..b13569a6179b 100644 --- a/fs/xfs/xfs_bmap.h +++ b/fs/xfs/xfs_bmap.h @@ -27,20 +27,6 @@ struct xfs_trans; extern kmem_zone_t *xfs_bmap_free_item_zone; -/* - * DELTA: describe a change to the in-core extent list. - * - * Internally the use of xed_blockount is somewhat funky. - * xed_blockcount contains an offset much of the time because this - * makes merging changes easier. (xfs_fileoff_t and xfs_filblks_t are - * the same underlying type). - */ -typedef struct xfs_extdelta -{ - xfs_fileoff_t xed_startoff; /* offset of range */ - xfs_filblks_t xed_blockcount; /* blocks in range */ -} xfs_extdelta_t; - /* * List of extents to be free "later". * The list is kept sorted on xbf_startblock. @@ -305,9 +291,7 @@ xfs_bmapi( xfs_extlen_t total, /* total blocks needed */ struct xfs_bmbt_irec *mval, /* output: map values */ int *nmap, /* i/o: mval size/count */ - xfs_bmap_free_t *flist, /* i/o: list extents to free */ - xfs_extdelta_t *delta); /* o: change made to incore - extents */ + xfs_bmap_free_t *flist); /* i/o: list extents to free */ /* * Map file blocks to filesystem blocks, simple version. @@ -341,8 +325,6 @@ xfs_bunmapi( xfs_fsblock_t *firstblock, /* first allocated block controls a.g. for allocs */ xfs_bmap_free_t *flist, /* i/o: list extents to free */ - xfs_extdelta_t *delta, /* o: change made to incore - extents */ int *done); /* set if not done yet */ /* diff --git a/fs/xfs/xfs_da_btree.c b/fs/xfs/xfs_da_btree.c index 731f1f41eca1..2adfb761ab13 100644 --- a/fs/xfs/xfs_da_btree.c +++ b/fs/xfs/xfs_da_btree.c @@ -1596,7 +1596,7 @@ xfs_da_grow_inode(xfs_da_args_t *args, xfs_dablk_t *new_blkno) xfs_bmapi_aflag(w)|XFS_BMAPI_WRITE|XFS_BMAPI_METADATA| XFS_BMAPI_CONTIG, args->firstblock, args->total, &map, &nmap, - args->flist, NULL))) { + args->flist))) { return error; } ASSERT(nmap <= 1); @@ -1617,8 +1617,7 @@ xfs_da_grow_inode(xfs_da_args_t *args, xfs_dablk_t *new_blkno) xfs_bmapi_aflag(w)|XFS_BMAPI_WRITE| XFS_BMAPI_METADATA, args->firstblock, args->total, - &mapp[mapi], &nmap, args->flist, - NULL))) { + &mapp[mapi], &nmap, args->flist))) { kmem_free(mapp); return error; } @@ -1879,7 +1878,7 @@ xfs_da_shrink_inode(xfs_da_args_t *args, xfs_dablk_t dead_blkno, */ if ((error = xfs_bunmapi(tp, dp, dead_blkno, count, xfs_bmapi_aflag(w)|XFS_BMAPI_METADATA, - 0, args->firstblock, args->flist, NULL, + 0, args->firstblock, args->flist, &done)) == ENOSPC) { if (w != XFS_DATA_FORK) break; @@ -1984,7 +1983,7 @@ xfs_da_do_buf( nfsb, XFS_BMAPI_METADATA | xfs_bmapi_aflag(whichfork), - NULL, 0, mapp, &nmap, NULL, NULL))) + NULL, 0, mapp, &nmap, NULL))) goto exit0; } } else { diff --git a/fs/xfs/xfs_dir2.c b/fs/xfs/xfs_dir2.c index 9c279ede05c5..b53960a5f41e 100644 --- a/fs/xfs/xfs_dir2.c +++ b/fs/xfs/xfs_dir2.c @@ -547,7 +547,7 @@ xfs_dir2_grow_inode( if ((error = xfs_bmapi(tp, dp, bno, count, XFS_BMAPI_WRITE|XFS_BMAPI_METADATA|XFS_BMAPI_CONTIG, args->firstblock, args->total, &map, &nmap, - args->flist, NULL))) + args->flist))) return error; ASSERT(nmap <= 1); if (nmap == 1) { @@ -579,8 +579,7 @@ xfs_dir2_grow_inode( if ((error = xfs_bmapi(tp, dp, b, c, XFS_BMAPI_WRITE|XFS_BMAPI_METADATA, args->firstblock, args->total, - &mapp[mapi], &nmap, args->flist, - NULL))) { + &mapp[mapi], &nmap, args->flist))) { kmem_free(mapp); return error; } @@ -713,7 +712,7 @@ xfs_dir2_shrink_inode( */ if ((error = xfs_bunmapi(tp, dp, da, mp->m_dirblkfsbs, XFS_BMAPI_METADATA, 0, args->firstblock, args->flist, - NULL, &done))) { + &done))) { /* * ENOSPC actually can happen if we're in a removename with * no space reservation, and the resulting block removal diff --git a/fs/xfs/xfs_dir2_leaf.c b/fs/xfs/xfs_dir2_leaf.c index 586b010e58b4..504be8640e91 100644 --- a/fs/xfs/xfs_dir2_leaf.c +++ b/fs/xfs/xfs_dir2_leaf.c @@ -873,7 +873,7 @@ xfs_dir2_leaf_getdents( xfs_dir2_byte_to_da(mp, XFS_DIR2_LEAF_OFFSET) - map_off, XFS_BMAPI_METADATA, NULL, 0, - &map[map_valid], &nmap, NULL, NULL); + &map[map_valid], &nmap, NULL); /* * Don't know if we should ignore this or * try to return an error. diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index 1e6ae68f91af..5715a9d8bcd6 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -1222,7 +1222,7 @@ xfs_isize_check( (xfs_ufsize_t)XFS_MAXIOFFSET(mp)) - map_first), XFS_BMAPI_ENTIRE, NULL, 0, imaps, &nimaps, - NULL, NULL)) + NULL)) return; ASSERT(nimaps == 1); ASSERT(imaps[0].br_startblock == HOLESTARTBLOCK); @@ -1588,7 +1588,7 @@ xfs_itruncate_finish( xfs_bmapi_aflag(fork), XFS_ITRUNC_MAX_EXTENTS, &first_block, &free_list, - NULL, &done); + &done); if (error) { /* * If the bunmapi call encounters an error, diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c index aeac00294a18..39ad46b3ed46 100644 --- a/fs/xfs/xfs_iomap.c +++ b/fs/xfs/xfs_iomap.c @@ -118,7 +118,7 @@ xfs_iomap( error = xfs_bmapi(NULL, ip, offset_fsb, (xfs_filblks_t)(end_fsb - offset_fsb), bmapi_flags, NULL, 0, imap, - nimaps, NULL, NULL); + nimaps, NULL); if (error) goto out; @@ -341,7 +341,7 @@ xfs_iomap_write_direct( xfs_bmap_init(&free_list, &firstfsb); nimaps = 1; error = xfs_bmapi(tp, ip, offset_fsb, count_fsb, bmapi_flag, - &firstfsb, 0, &imap, &nimaps, &free_list, NULL); + &firstfsb, 0, &imap, &nimaps, &free_list); if (error) goto error0; @@ -419,7 +419,7 @@ xfs_iomap_eof_want_preallocate( imaps = nimaps; firstblock = NULLFSBLOCK; error = xfs_bmapi(NULL, ip, start_fsb, count_fsb, 0, - &firstblock, 0, imap, &imaps, NULL, NULL); + &firstblock, 0, imap, &imaps, NULL); if (error) return error; for (n = 0; n < imaps; n++) { @@ -494,7 +494,7 @@ retry: (xfs_filblks_t)(last_fsb - offset_fsb), XFS_BMAPI_DELAY | XFS_BMAPI_WRITE | XFS_BMAPI_ENTIRE, &firstblock, 1, imap, - &nimaps, NULL, NULL); + &nimaps, NULL); if (error && (error != ENOSPC)) return XFS_ERROR(error); @@ -650,7 +650,7 @@ xfs_iomap_write_allocate( /* Go get the actual blocks */ error = xfs_bmapi(tp, ip, map_start_fsb, count_fsb, XFS_BMAPI_WRITE, &first_block, 1, - &imap, &nimaps, &free_list, NULL); + &imap, &nimaps, &free_list); if (error) goto trans_cancel; @@ -768,7 +768,7 @@ xfs_iomap_write_unwritten( nimaps = 1; error = xfs_bmapi(tp, ip, offset_fsb, count_fsb, XFS_BMAPI_WRITE|XFS_BMAPI_CONVERT, &firstfsb, - 1, &imap, &nimaps, &free_list, NULL); + 1, &imap, &nimaps, &free_list); if (error) goto error_on_bmapi_transaction; diff --git a/fs/xfs/xfs_rtalloc.c b/fs/xfs/xfs_rtalloc.c index 8da5d89dcbcd..891260fea11e 100644 --- a/fs/xfs/xfs_rtalloc.c +++ b/fs/xfs/xfs_rtalloc.c @@ -122,7 +122,7 @@ xfs_growfs_rt_alloc( cancelflags |= XFS_TRANS_ABORT; error = xfs_bmapi(tp, ip, oblocks, nblocks - oblocks, XFS_BMAPI_WRITE | XFS_BMAPI_METADATA, &firstblock, - resblks, &map, &nmap, &flist, NULL); + resblks, &map, &nmap, &flist); if (!error && nmap < 1) error = XFS_ERROR(ENOSPC); if (error) diff --git a/fs/xfs/xfs_vnodeops.c b/fs/xfs/xfs_vnodeops.c index 130343a5d22d..ad599ccc416b 100644 --- a/fs/xfs/xfs_vnodeops.c +++ b/fs/xfs/xfs_vnodeops.c @@ -492,7 +492,7 @@ xfs_readlink_bmap( int error = 0; error = xfs_bmapi(NULL, ip, 0, XFS_B_TO_FSB(mp, pathlen), 0, NULL, 0, - mval, &nmaps, NULL, NULL); + mval, &nmaps, NULL); if (error) goto out; @@ -596,7 +596,7 @@ xfs_free_eofblocks( nimaps = 1; xfs_ilock(ip, XFS_ILOCK_SHARED); error = xfs_bmapi(NULL, ip, end_fsb, map_len, 0, - NULL, 0, &imap, &nimaps, NULL, NULL); + NULL, 0, &imap, &nimaps, NULL); xfs_iunlock(ip, XFS_ILOCK_SHARED); if (!error && (nimaps != 0) && @@ -733,7 +733,7 @@ xfs_inactive_symlink_rmt( nmaps = ARRAY_SIZE(mval); if ((error = xfs_bmapi(tp, ip, 0, XFS_B_TO_FSB(mp, size), XFS_BMAPI_METADATA, &first_block, 0, mval, &nmaps, - &free_list, NULL))) + &free_list))) goto error0; /* * Invalidate the block(s). @@ -748,7 +748,7 @@ xfs_inactive_symlink_rmt( * Unmap the dead block(s) to the free_list. */ if ((error = xfs_bunmapi(tp, ip, 0, size, XFS_BMAPI_METADATA, nmaps, - &first_block, &free_list, NULL, &done))) + &first_block, &free_list, &done))) goto error1; ASSERT(done); /* @@ -2095,7 +2095,7 @@ xfs_symlink( error = xfs_bmapi(tp, ip, first_fsb, fs_blocks, XFS_BMAPI_WRITE | XFS_BMAPI_METADATA, &first_block, resblks, mval, &nmaps, - &free_list, NULL); + &free_list); if (error) { goto error1; } @@ -2347,7 +2347,7 @@ xfs_alloc_file_space( error = xfs_bmapi(tp, ip, startoffset_fsb, allocatesize_fsb, bmapi_flag, &firstfsb, 0, imapp, &nimaps, - &free_list, NULL); + &free_list); if (error) { goto error0; } @@ -2436,7 +2436,7 @@ xfs_zero_remaining_bytes( offset_fsb = XFS_B_TO_FSBT(mp, offset); nimap = 1; error = xfs_bmapi(NULL, ip, offset_fsb, 1, 0, - NULL, 0, &imap, &nimap, NULL, NULL); + NULL, 0, &imap, &nimap, NULL); if (error || nimap < 1) break; ASSERT(imap.br_blockcount >= 1); @@ -2556,7 +2556,7 @@ xfs_free_file_space( if (rt && !xfs_sb_version_hasextflgbit(&mp->m_sb)) { nimap = 1; error = xfs_bmapi(NULL, ip, startoffset_fsb, - 1, 0, NULL, 0, &imap, &nimap, NULL, NULL); + 1, 0, NULL, 0, &imap, &nimap, NULL); if (error) goto out_unlock_iolock; ASSERT(nimap == 0 || nimap == 1); @@ -2571,7 +2571,7 @@ xfs_free_file_space( } nimap = 1; error = xfs_bmapi(NULL, ip, endoffset_fsb - 1, - 1, 0, NULL, 0, &imap, &nimap, NULL, NULL); + 1, 0, NULL, 0, &imap, &nimap, NULL); if (error) goto out_unlock_iolock; ASSERT(nimap == 0 || nimap == 1); @@ -2647,7 +2647,7 @@ xfs_free_file_space( xfs_bmap_init(&free_list, &firstfsb); error = xfs_bunmapi(tp, ip, startoffset_fsb, endoffset_fsb - startoffset_fsb, - 0, 2, &firstfsb, &free_list, NULL, &done); + 0, 2, &firstfsb, &free_list, &done); if (error) { goto error0; } -- cgit v1.2.3 From 3d9b02e3c76531687ab5314e0edf266256f13c2d Mon Sep 17 00:00:00 2001 From: Eric Sandeen Date: Thu, 24 Jun 2010 09:45:30 +1000 Subject: xfs: fix corruption case for block size < page size xfstests 194 first truncats a file back and then extends it again by truncating it to a larger size. This causes discard_buffer to drop the mapped, but not the uptodate bit and thus creates something that xfs_page_state_convert takes for unmapped space created by mmap because it doesn't check for the dirty bit, which also gets cleared by discard_buffer and checked by other ->writepage implementations like block_write_full_page. Handle this kind of buffers early, and unlike Eric's first version of the patch simply ASSERT that the buffers is dirty, given that the mmap write case can't happen anymore since the introduction of ->page_mkwrite. The now dead code dealing with that will be deleted in a follow on patch. Signed-off-by: Eric Sandeen Signed-off-by: Christoph Hellwig Reviewed-by: Dave Chinner --- fs/xfs/linux-2.6/xfs_aops.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_aops.c b/fs/xfs/linux-2.6/xfs_aops.c index b25d11a3d84e..bd5e1cf5428d 100644 --- a/fs/xfs/linux-2.6/xfs_aops.c +++ b/fs/xfs/linux-2.6/xfs_aops.c @@ -1125,6 +1125,16 @@ xfs_page_state_convert( continue; } + /* + * A hole may still be marked uptodate because discard_buffer + * leaves the flag set. + */ + if (!buffer_mapped(bh) && buffer_uptodate(bh)) { + ASSERT(!buffer_dirty(bh)); + imap_valid = 0; + continue; + } + if (imap_valid) imap_valid = xfs_imap_valid(inode, &imap, offset); -- cgit v1.2.3 From 89f3b363967a958e756a549c8747c1fb9c930c1a Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 24 Jun 2010 09:45:48 +1000 Subject: xfs: simplify xfs_vm_releasepage Currently the xfs releasepage implementation has code to deal with converting delayed allocated and unwritten space. But we never get called for those as we always convert delayed and unwritten space when cleaning a page, or drop the state from the buffers in block_invalidatepage. We still keep a WARN_ON on those cases for now, but remove all the case dealing with it, which allows to fold xfs_page_state_convert into xfs_vm_writepage and remove the !startio case from the whole writeback path. Signed-off-by: Christoph Hellwig Reviewed-by: Dave Chinner --- fs/xfs/linux-2.6/xfs_aops.c | 354 +++++++++++++------------------------------- 1 file changed, 107 insertions(+), 247 deletions(-) (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_aops.c b/fs/xfs/linux-2.6/xfs_aops.c index bd5e1cf5428d..7744a3b630e0 100644 --- a/fs/xfs/linux-2.6/xfs_aops.c +++ b/fs/xfs/linux-2.6/xfs_aops.c @@ -754,7 +754,6 @@ xfs_convert_page( struct xfs_bmbt_irec *imap, xfs_ioend_t **ioendp, struct writeback_control *wbc, - int startio, int all_bh) { struct buffer_head *bh, *head; @@ -825,19 +824,14 @@ xfs_convert_page( ASSERT(imap->br_startblock != DELAYSTARTBLOCK); xfs_map_at_offset(inode, bh, imap, offset); - if (startio) { - xfs_add_to_ioend(inode, bh, offset, - type, ioendp, done); - } else { - set_buffer_dirty(bh); - unlock_buffer(bh); - mark_buffer_dirty(bh); - } + xfs_add_to_ioend(inode, bh, offset, type, + ioendp, done); + page_dirty--; count++; } else { type = IO_NEW; - if (buffer_mapped(bh) && all_bh && startio) { + if (buffer_mapped(bh) && all_bh) { lock_buffer(bh); xfs_add_to_ioend(inode, bh, offset, type, ioendp, done); @@ -852,14 +846,12 @@ xfs_convert_page( if (uptodate && bh == head) SetPageUptodate(page); - if (startio) { - if (count) { - wbc->nr_to_write--; - if (wbc->nr_to_write <= 0) - done = 1; - } - xfs_start_page_writeback(page, !page_dirty, count); + if (count) { + wbc->nr_to_write--; + if (wbc->nr_to_write <= 0) + done = 1; } + xfs_start_page_writeback(page, !page_dirty, count); return done; fail_unlock_page: @@ -879,7 +871,6 @@ xfs_cluster_write( struct xfs_bmbt_irec *imap, xfs_ioend_t **ioendp, struct writeback_control *wbc, - int startio, int all_bh, pgoff_t tlast) { @@ -895,7 +886,7 @@ xfs_cluster_write( for (i = 0; i < pagevec_count(&pvec); i++) { done = xfs_convert_page(inode, pvec.pages[i], tindex++, - imap, ioendp, wbc, startio, all_bh); + imap, ioendp, wbc, all_bh); if (done) break; } @@ -1025,51 +1016,94 @@ out_invalidate: } /* - * Calling this without startio set means we are being asked to make a dirty - * page ready for freeing it's buffers. When called with startio set then - * we are coming from writepage. + * Write out a dirty page. + * + * For delalloc space on the page we need to allocate space and flush it. + * For unwritten space on the page we need to start the conversion to + * regular allocated space. + * For unmapped buffer heads on the page we should allocate space if the + * page is uptodate. + * For any other dirty buffer heads on the page we should flush them. * - * When called with startio set it is important that we write the WHOLE - * page if possible. - * The bh->b_state's cannot know if any of the blocks or which block for - * that matter are dirty due to mmap writes, and therefore bh uptodate is - * only valid if the page itself isn't completely uptodate. Some layers - * may clear the page dirty flag prior to calling write page, under the - * assumption the entire page will be written out; by not writing out the - * whole page the page can be reused before all valid dirty data is - * written out. Note: in the case of a page that has been dirty'd by - * mapwrite and but partially setup by block_prepare_write the - * bh->b_states's will not agree and only ones setup by BPW/BCW will have - * valid state, thus the whole page must be written out thing. + * If we detect that a transaction would be required to flush the page, we + * have to check the process flags first, if we are already in a transaction + * or disk I/O during allocations is off, we need to fail the writepage and + * redirty the page. + * + * The bh->b_state's cannot know if any of the blocks or which block for that + * matter are dirty due to mmap writes, and therefore bh uptodate is only + * valid if the page itself isn't completely uptodate. */ - STATIC int -xfs_page_state_convert( - struct inode *inode, - struct page *page, - struct writeback_control *wbc, - int startio, - int unmapped) /* also implies page uptodate */ +xfs_vm_writepage( + struct page *page, + struct writeback_control *wbc) { + struct inode *inode = page->mapping->host; + int need_trans; + int delalloc, unmapped, unwritten; struct buffer_head *bh, *head; struct xfs_bmbt_irec imap; xfs_ioend_t *ioend = NULL, *iohead = NULL; loff_t offset; - unsigned long p_offset = 0; unsigned int type; __uint64_t end_offset; pgoff_t end_index, last_index; ssize_t size, len; int flags, err, imap_valid = 0, uptodate = 1; - int page_dirty, count = 0; - int trylock = 0; - int all_bh = unmapped; + int count = 0; + int all_bh; + + trace_xfs_writepage(inode, page, 0); + + /* + * Refuse to write the page out if we are called from reclaim context. + * + * This is primarily to avoid stack overflows when called from deep + * used stacks in random callers for direct reclaim, but disabling + * reclaim for kswap is a nice side-effect as kswapd causes rather + * suboptimal I/O patters, too. + * + * This should really be done by the core VM, but until that happens + * filesystems like XFS, btrfs and ext4 have to take care of this + * by themselves. + */ + if (current->flags & PF_MEMALLOC) + goto out_fail; - if (startio) { - if (wbc->sync_mode == WB_SYNC_NONE && wbc->nonblocking) - trylock |= BMAPI_TRYLOCK; + /* + * We need a transaction if: + * 1. There are delalloc buffers on the page + * 2. The page is uptodate and we have unmapped buffers + * 3. The page is uptodate and we have no buffers + * 4. There are unwritten buffers on the page + */ + if (!page_has_buffers(page)) { + unmapped = 1; + need_trans = 1; + } else { + xfs_count_page_state(page, &delalloc, &unmapped, &unwritten); + if (!PageUptodate(page)) + unmapped = 0; + need_trans = delalloc + unmapped + unwritten; } + /* + * If we need a transaction and the process flags say + * we are already in a transaction, or no IO is allowed + * then mark the page dirty again and leave the page + * as is. + */ + if (current_test_flags(PF_FSTRANS) && need_trans) + goto out_fail; + + /* + * Delay hooking up buffer heads until we have + * made our go/no-go decision. + */ + if (!page_has_buffers(page)) + create_empty_buffers(page, 1 << inode->i_blkbits, 0); + /* Is this page beyond the end of the file? */ offset = i_size_read(inode); end_index = offset >> PAGE_CACHE_SHIFT; @@ -1077,53 +1111,27 @@ xfs_page_state_convert( if (page->index >= end_index) { if ((page->index >= end_index + 1) || !(i_size_read(inode) & (PAGE_CACHE_SIZE - 1))) { - if (startio) - unlock_page(page); + unlock_page(page); return 0; } } - /* - * page_dirty is initially a count of buffers on the page before - * EOF and is decremented as we move each into a cleanable state. - * - * Derivation: - * - * End offset is the highest offset that this page should represent. - * If we are on the last page, (end_offset & (PAGE_CACHE_SIZE - 1)) - * will evaluate non-zero and be less than PAGE_CACHE_SIZE and - * hence give us the correct page_dirty count. On any other page, - * it will be zero and in that case we need page_dirty to be the - * count of buffers on the page. - */ end_offset = min_t(unsigned long long, (xfs_off_t)(page->index + 1) << PAGE_CACHE_SHIFT, offset); len = 1 << inode->i_blkbits; - p_offset = min_t(unsigned long, end_offset & (PAGE_CACHE_SIZE - 1), - PAGE_CACHE_SIZE); - p_offset = p_offset ? roundup(p_offset, len) : PAGE_CACHE_SIZE; - page_dirty = p_offset / len; bh = head = page_buffers(page); offset = page_offset(page); flags = BMAPI_READ; type = IO_NEW; - /* TODO: cleanup count and page_dirty */ + all_bh = unmapped; do { if (offset >= end_offset) break; if (!buffer_uptodate(bh)) uptodate = 0; - if (!(PageUptodate(page) || buffer_uptodate(bh)) && !startio) { - /* - * the iomap is actually still valid, but the ioend - * isn't. shouldn't happen too often. - */ - imap_valid = 0; - continue; - } /* * A hole may still be marked uptodate because discard_buffer @@ -1150,7 +1158,7 @@ xfs_page_state_convert( */ if (buffer_unwritten(bh) || buffer_delay(bh) || ((buffer_uptodate(bh) || PageUptodate(page)) && - !buffer_mapped(bh) && (unmapped || startio))) { + !buffer_mapped(bh))) { int new_ioend = 0; /* @@ -1164,7 +1172,11 @@ xfs_page_state_convert( flags = BMAPI_WRITE | BMAPI_IGNSTATE; } else if (buffer_delay(bh)) { type = IO_DELAY; - flags = BMAPI_ALLOCATE | trylock; + flags = BMAPI_ALLOCATE; + + if (wbc->sync_mode == WB_SYNC_NONE && + wbc->nonblocking) + flags |= BMAPI_TRYLOCK; } else { type = IO_NEW; flags = BMAPI_WRITE | BMAPI_MMAP; @@ -1196,19 +1208,11 @@ xfs_page_state_convert( } if (imap_valid) { xfs_map_at_offset(inode, bh, &imap, offset); - if (startio) { - xfs_add_to_ioend(inode, bh, offset, - type, &ioend, - new_ioend); - } else { - set_buffer_dirty(bh); - unlock_buffer(bh); - mark_buffer_dirty(bh); - } - page_dirty--; + xfs_add_to_ioend(inode, bh, offset, type, + &ioend, new_ioend); count++; } - } else if (buffer_uptodate(bh) && startio) { + } else if (buffer_uptodate(bh)) { /* * we got here because the buffer is already mapped. * That means it must already have extents allocated @@ -1241,13 +1245,11 @@ xfs_page_state_convert( all_bh = 1; xfs_add_to_ioend(inode, bh, offset, type, &ioend, !imap_valid); - page_dirty--; count++; } else { imap_valid = 0; } - } else if ((buffer_uptodate(bh) || PageUptodate(page)) && - (unmapped || startio)) { + } else if (PageUptodate(page)) { imap_valid = 0; } @@ -1259,8 +1261,7 @@ xfs_page_state_convert( if (uptodate && bh == head) SetPageUptodate(page); - if (startio) - xfs_start_page_writeback(page, 1, count); + xfs_start_page_writeback(page, 1, count); if (ioend && imap_valid) { xfs_off_t end_index; @@ -1278,131 +1279,28 @@ xfs_page_state_convert( end_index = last_index; xfs_cluster_write(inode, page->index + 1, &imap, &ioend, - wbc, startio, all_bh, end_index); + wbc, all_bh, end_index); } if (iohead) xfs_submit_ioend(wbc, iohead); - return page_dirty; + return 0; error: if (iohead) xfs_cancel_ioend(iohead); - /* - * If it's delalloc and we have nowhere to put it, - * throw it away, unless the lower layers told - * us to try again. - */ - if (err != -EAGAIN) { - if (!unmapped) - xfs_aops_discard_page(page); - ClearPageUptodate(page); - } + if (!unmapped) + xfs_aops_discard_page(page); + ClearPageUptodate(page); + unlock_page(page); return err; -} - -/* - * writepage: Called from one of two places: - * - * 1. we are flushing a delalloc buffer head. - * - * 2. we are writing out a dirty page. Typically the page dirty - * state is cleared before we get here. In this case is it - * conceivable we have no buffer heads. - * - * For delalloc space on the page we need to allocate space and - * flush it. For unmapped buffer heads on the page we should - * allocate space if the page is uptodate. For any other dirty - * buffer heads on the page we should flush them. - * - * If we detect that a transaction would be required to flush - * the page, we have to check the process flags first, if we - * are already in a transaction or disk I/O during allocations - * is off, we need to fail the writepage and redirty the page. - */ - -STATIC int -xfs_vm_writepage( - struct page *page, - struct writeback_control *wbc) -{ - int error; - int need_trans; - int delalloc, unmapped, unwritten; - struct inode *inode = page->mapping->host; - - trace_xfs_writepage(inode, page, 0); - - /* - * Refuse to write the page out if we are called from reclaim context. - * - * This is primarily to avoid stack overflows when called from deep - * used stacks in random callers for direct reclaim, but disabling - * reclaim for kswap is a nice side-effect as kswapd causes rather - * suboptimal I/O patters, too. - * - * This should really be done by the core VM, but until that happens - * filesystems like XFS, btrfs and ext4 have to take care of this - * by themselves. - */ - if (current->flags & PF_MEMALLOC) - goto out_fail; - - /* - * We need a transaction if: - * 1. There are delalloc buffers on the page - * 2. The page is uptodate and we have unmapped buffers - * 3. The page is uptodate and we have no buffers - * 4. There are unwritten buffers on the page - */ - - if (!page_has_buffers(page)) { - unmapped = 1; - need_trans = 1; - } else { - xfs_count_page_state(page, &delalloc, &unmapped, &unwritten); - if (!PageUptodate(page)) - unmapped = 0; - need_trans = delalloc + unmapped + unwritten; - } - - /* - * If we need a transaction and the process flags say - * we are already in a transaction, or no IO is allowed - * then mark the page dirty again and leave the page - * as is. - */ - if (current_test_flags(PF_FSTRANS) && need_trans) - goto out_fail; - - /* - * Delay hooking up buffer heads until we have - * made our go/no-go decision. - */ - if (!page_has_buffers(page)) - create_empty_buffers(page, 1 << inode->i_blkbits, 0); - - /* - * Convert delayed allocate, unwritten or unmapped space - * to real space and flush out to disk. - */ - error = xfs_page_state_convert(inode, page, wbc, 1, unmapped); - if (error == -EAGAIN) - goto out_fail; - if (unlikely(error < 0)) - goto out_unlock; - - return 0; out_fail: redirty_page_for_writepage(wbc, page); unlock_page(page); return 0; -out_unlock: - unlock_page(page); - return error; } STATIC int @@ -1416,65 +1314,27 @@ xfs_vm_writepages( /* * Called to move a page into cleanable state - and from there - * to be released. Possibly the page is already clean. We always + * to be released. The page should already be clean. We always * have buffer heads in this call. * - * Returns 0 if the page is ok to release, 1 otherwise. - * - * Possible scenarios are: - * - * 1. We are being called to release a page which has been written - * to via regular I/O. buffer heads will be dirty and possibly - * delalloc. If no delalloc buffer heads in this case then we - * can just return zero. - * - * 2. We are called to release a page which has been written via - * mmap, all we need to do is ensure there is no delalloc - * state in the buffer heads, if not we can let the caller - * free them and we should come back later via writepage. + * Returns 1 if the page is ok to release, 0 otherwise. */ STATIC int xfs_vm_releasepage( struct page *page, gfp_t gfp_mask) { - struct inode *inode = page->mapping->host; - int dirty, delalloc, unmapped, unwritten; - struct writeback_control wbc = { - .sync_mode = WB_SYNC_ALL, - .nr_to_write = 1, - }; - - trace_xfs_releasepage(inode, page, 0); + int delalloc, unmapped, unwritten; - if (!page_has_buffers(page)) - return 0; + trace_xfs_releasepage(page->mapping->host, page, 0); xfs_count_page_state(page, &delalloc, &unmapped, &unwritten); - if (!delalloc && !unwritten) - goto free_buffers; - if (!(gfp_mask & __GFP_FS)) + if (WARN_ON(delalloc)) return 0; - - /* If we are already inside a transaction or the thread cannot - * do I/O, we cannot release this page. - */ - if (current_test_flags(PF_FSTRANS)) + if (WARN_ON(unwritten)) return 0; - /* - * Convert delalloc space to real space, do not flush the - * data out to disk, that will be done by the caller. - * Never need to allocate space here - we will always - * come back to writepage in that case. - */ - dirty = xfs_page_state_convert(inode, page, &wbc, 0, 0); - if (dirty == 0 && !unwritten) - goto free_buffers; - return 0; - -free_buffers: return try_to_free_buffers(page); } -- cgit v1.2.3 From 20cb52ebd1b5ca6fa8a5d9b6b1392292f5ca8a45 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 24 Jun 2010 09:46:01 +1000 Subject: xfs: simplify xfs_vm_writepage The writepage implementation in XFS still tries to deal with dirty but unmapped buffers which used to caused by writes through shared mmaps. Since the introduction of ->page_mkwrite these can't happen anymore, so remove the code dealing with them. Note that the all_bh variable which causes us to start I/O on all buffers on the pages was controlled by the count of unmapped buffers, which also included those not actually dirty. It's now unconditionally initialized to 0 but set to 1 for the case of small file size extensions. It probably can be removed entirely, but that's left for another patch. Signed-off-by: Christoph Hellwig Reviewed-by: Dave Chinner --- fs/xfs/linux-2.6/xfs_aops.c | 139 ++++++++++++++----------------------------- fs/xfs/linux-2.6/xfs_aops.h | 2 +- fs/xfs/linux-2.6/xfs_trace.h | 10 +--- 3 files changed, 49 insertions(+), 102 deletions(-) (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_aops.c b/fs/xfs/linux-2.6/xfs_aops.c index 7744a3b630e0..1776cdd944b5 100644 --- a/fs/xfs/linux-2.6/xfs_aops.c +++ b/fs/xfs/linux-2.6/xfs_aops.c @@ -85,18 +85,15 @@ void xfs_count_page_state( struct page *page, int *delalloc, - int *unmapped, int *unwritten) { struct buffer_head *bh, *head; - *delalloc = *unmapped = *unwritten = 0; + *delalloc = *unwritten = 0; bh = head = page_buffers(page); do { - if (buffer_uptodate(bh) && !buffer_mapped(bh)) - (*unmapped) = 1; - else if (buffer_unwritten(bh)) + if (buffer_unwritten(bh)) (*unwritten) = 1; else if (buffer_delay(bh)) (*delalloc) = 1; @@ -607,31 +604,30 @@ xfs_map_at_offset( STATIC unsigned int xfs_probe_page( struct page *page, - unsigned int pg_offset, - int mapped) + unsigned int pg_offset) { + struct buffer_head *bh, *head; int ret = 0; if (PageWriteback(page)) return 0; + if (!PageDirty(page)) + return 0; + if (!page->mapping) + return 0; + if (!page_has_buffers(page)) + return 0; - if (page->mapping && PageDirty(page)) { - if (page_has_buffers(page)) { - struct buffer_head *bh, *head; - - bh = head = page_buffers(page); - do { - if (!buffer_uptodate(bh)) - break; - if (mapped != buffer_mapped(bh)) - break; - ret += bh->b_size; - if (ret >= pg_offset) - break; - } while ((bh = bh->b_this_page) != head); - } else - ret = mapped ? 0 : PAGE_CACHE_SIZE; - } + bh = head = page_buffers(page); + do { + if (!buffer_uptodate(bh)) + break; + if (!buffer_mapped(bh)) + break; + ret += bh->b_size; + if (ret >= pg_offset) + break; + } while ((bh = bh->b_this_page) != head); return ret; } @@ -641,8 +637,7 @@ xfs_probe_cluster( struct inode *inode, struct page *startpage, struct buffer_head *bh, - struct buffer_head *head, - int mapped) + struct buffer_head *head) { struct pagevec pvec; pgoff_t tindex, tlast, tloff; @@ -651,7 +646,7 @@ xfs_probe_cluster( /* First sum forwards in this page */ do { - if (!buffer_uptodate(bh) || (mapped != buffer_mapped(bh))) + if (!buffer_uptodate(bh) || !buffer_mapped(bh)) return total; total += bh->b_size; } while ((bh = bh->b_this_page) != head); @@ -685,7 +680,7 @@ xfs_probe_cluster( pg_offset = PAGE_CACHE_SIZE; if (page->index == tindex && trylock_page(page)) { - pg_len = xfs_probe_page(page, pg_offset, mapped); + pg_len = xfs_probe_page(page, pg_offset); unlock_page(page); } @@ -1021,18 +1016,12 @@ out_invalidate: * For delalloc space on the page we need to allocate space and flush it. * For unwritten space on the page we need to start the conversion to * regular allocated space. - * For unmapped buffer heads on the page we should allocate space if the - * page is uptodate. * For any other dirty buffer heads on the page we should flush them. * * If we detect that a transaction would be required to flush the page, we * have to check the process flags first, if we are already in a transaction * or disk I/O during allocations is off, we need to fail the writepage and * redirty the page. - * - * The bh->b_state's cannot know if any of the blocks or which block for that - * matter are dirty due to mmap writes, and therefore bh uptodate is only - * valid if the page itself isn't completely uptodate. */ STATIC int xfs_vm_writepage( @@ -1040,8 +1029,7 @@ xfs_vm_writepage( struct writeback_control *wbc) { struct inode *inode = page->mapping->host; - int need_trans; - int delalloc, unmapped, unwritten; + int delalloc, unwritten; struct buffer_head *bh, *head; struct xfs_bmbt_irec imap; xfs_ioend_t *ioend = NULL, *iohead = NULL; @@ -1052,10 +1040,12 @@ xfs_vm_writepage( ssize_t size, len; int flags, err, imap_valid = 0, uptodate = 1; int count = 0; - int all_bh; + int all_bh = 0; trace_xfs_writepage(inode, page, 0); + ASSERT(page_has_buffers(page)); + /* * Refuse to write the page out if we are called from reclaim context. * @@ -1072,29 +1062,15 @@ xfs_vm_writepage( goto out_fail; /* - * We need a transaction if: - * 1. There are delalloc buffers on the page - * 2. The page is uptodate and we have unmapped buffers - * 3. The page is uptodate and we have no buffers - * 4. There are unwritten buffers on the page - */ - if (!page_has_buffers(page)) { - unmapped = 1; - need_trans = 1; - } else { - xfs_count_page_state(page, &delalloc, &unmapped, &unwritten); - if (!PageUptodate(page)) - unmapped = 0; - need_trans = delalloc + unmapped + unwritten; - } - - /* - * If we need a transaction and the process flags say - * we are already in a transaction, or no IO is allowed - * then mark the page dirty again and leave the page - * as is. + * We need a transaction if there are delalloc or unwritten buffers + * on the page. + * + * If we need a transaction and the process flags say we are already + * in a transaction, or no IO is allowed then mark the page dirty + * again and leave the page as is. */ - if (current_test_flags(PF_FSTRANS) && need_trans) + xfs_count_page_state(page, &delalloc, &unwritten); + if ((current->flags & PF_FSTRANS) && (delalloc || unwritten)) goto out_fail; /* @@ -1117,7 +1093,8 @@ xfs_vm_writepage( } end_offset = min_t(unsigned long long, - (xfs_off_t)(page->index + 1) << PAGE_CACHE_SHIFT, offset); + (xfs_off_t)(page->index + 1) << PAGE_CACHE_SHIFT, + offset); len = 1 << inode->i_blkbits; bh = head = page_buffers(page); @@ -1125,8 +1102,6 @@ xfs_vm_writepage( flags = BMAPI_READ; type = IO_NEW; - all_bh = unmapped; - do { if (offset >= end_offset) break; @@ -1146,19 +1121,7 @@ xfs_vm_writepage( if (imap_valid) imap_valid = xfs_imap_valid(inode, &imap, offset); - /* - * First case, map an unwritten extent and prepare for - * extent state conversion transaction on completion. - * - * Second case, allocate space for a delalloc buffer. - * We can return EAGAIN here in the release page case. - * - * Third case, an unmapped buffer was found, and we are - * in a path where we need to write the whole page out. - */ - if (buffer_unwritten(bh) || buffer_delay(bh) || - ((buffer_uptodate(bh) || PageUptodate(page)) && - !buffer_mapped(bh))) { + if (buffer_unwritten(bh) || buffer_delay(bh)) { int new_ioend = 0; /* @@ -1177,14 +1140,11 @@ xfs_vm_writepage( if (wbc->sync_mode == WB_SYNC_NONE && wbc->nonblocking) flags |= BMAPI_TRYLOCK; - } else { - type = IO_NEW; - flags = BMAPI_WRITE | BMAPI_MMAP; } if (!imap_valid) { /* - * if we didn't have a valid mapping then we + * If we didn't have a valid mapping then we * need to ensure that we put the new mapping * in a new ioend structure. This needs to be * done to ensure that the ioends correctly @@ -1192,14 +1152,7 @@ xfs_vm_writepage( * for unwritten extent conversion. */ new_ioend = 1; - if (type == IO_NEW) { - size = xfs_probe_cluster(inode, - page, bh, head, 0); - } else { - size = len; - } - - err = xfs_map_blocks(inode, offset, size, + err = xfs_map_blocks(inode, offset, len, &imap, flags); if (err) goto error; @@ -1220,8 +1173,7 @@ xfs_vm_writepage( */ if (!imap_valid || flags != BMAPI_READ) { flags = BMAPI_READ; - size = xfs_probe_cluster(inode, page, bh, - head, 1); + size = xfs_probe_cluster(inode, page, bh, head); err = xfs_map_blocks(inode, offset, size, &imap, flags); if (err) @@ -1240,7 +1192,6 @@ xfs_vm_writepage( */ type = IO_NEW; if (trylock_buffer(bh)) { - ASSERT(buffer_mapped(bh)); if (imap_valid) all_bh = 1; xfs_add_to_ioend(inode, bh, offset, type, @@ -1250,6 +1201,7 @@ xfs_vm_writepage( imap_valid = 0; } } else if (PageUptodate(page)) { + ASSERT(buffer_mapped(bh)); imap_valid = 0; } @@ -1291,8 +1243,7 @@ error: if (iohead) xfs_cancel_ioend(iohead); - if (!unmapped) - xfs_aops_discard_page(page); + xfs_aops_discard_page(page); ClearPageUptodate(page); unlock_page(page); return err; @@ -1324,11 +1275,11 @@ xfs_vm_releasepage( struct page *page, gfp_t gfp_mask) { - int delalloc, unmapped, unwritten; + int delalloc, unwritten; trace_xfs_releasepage(page->mapping->host, page, 0); - xfs_count_page_state(page, &delalloc, &unmapped, &unwritten); + xfs_count_page_state(page, &delalloc, &unwritten); if (WARN_ON(delalloc)) return 0; diff --git a/fs/xfs/linux-2.6/xfs_aops.h b/fs/xfs/linux-2.6/xfs_aops.h index 4cfc6ea87df8..319da173cc1a 100644 --- a/fs/xfs/linux-2.6/xfs_aops.h +++ b/fs/xfs/linux-2.6/xfs_aops.h @@ -45,6 +45,6 @@ extern int xfs_get_blocks(struct inode *, sector_t, struct buffer_head *, int); extern void xfs_ioend_init(void); extern void xfs_ioend_wait(struct xfs_inode *); -extern void xfs_count_page_state(struct page *, int *, int *, int *); +extern void xfs_count_page_state(struct page *, int *, int *); #endif /* __XFS_AOPS_H__ */ diff --git a/fs/xfs/linux-2.6/xfs_trace.h b/fs/xfs/linux-2.6/xfs_trace.h index 0aea6d5f705a..3f2eec28c70d 100644 --- a/fs/xfs/linux-2.6/xfs_trace.h +++ b/fs/xfs/linux-2.6/xfs_trace.h @@ -832,33 +832,29 @@ DECLARE_EVENT_CLASS(xfs_page_class, __field(loff_t, size) __field(unsigned long, offset) __field(int, delalloc) - __field(int, unmapped) __field(int, unwritten) ), TP_fast_assign( - int delalloc = -1, unmapped = -1, unwritten = -1; + int delalloc = -1, unwritten = -1; if (page_has_buffers(page)) - xfs_count_page_state(page, &delalloc, - &unmapped, &unwritten); + xfs_count_page_state(page, &delalloc, &unwritten); __entry->dev = inode->i_sb->s_dev; __entry->ino = XFS_I(inode)->i_ino; __entry->pgoff = page_offset(page); __entry->size = i_size_read(inode); __entry->offset = off; __entry->delalloc = delalloc; - __entry->unmapped = unmapped; __entry->unwritten = unwritten; ), TP_printk("dev %d:%d ino 0x%llx pgoff 0x%lx size 0x%llx offset %lx " - "delalloc %d unmapped %d unwritten %d", + "delalloc %d unwritten %d", MAJOR(__entry->dev), MINOR(__entry->dev), __entry->ino, __entry->pgoff, __entry->size, __entry->offset, __entry->delalloc, - __entry->unmapped, __entry->unwritten) ) -- cgit v1.2.3 From 7a36c8a98a7dd05756bb147be2ac350325ff5830 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 24 Jun 2010 11:39:25 +1000 Subject: xfs: avoid synchronous transaction in xfs_fs_write_inode We already rely on the fact that the sync code will cause a synchronous log force later on (currently via xfs_fs_sync_fs -> xfs_quiesce_data -> xfs_sync_data), so no need to do this here. This allows us to avoid a lot of synchronous log forces during sync, which pays of especially with delayed logging enabled. Some compilebench numbers that show this: xfs (delayed logging, 256k logbufs) =================================== intial create 25.94 MB/s 25.75 MB/s 25.64 MB/s create 8.54 MB/s 9.12 MB/s 9.15 MB/s patch 2.47 MB/s 2.47 MB/s 3.17 MB/s compile 29.65 MB/s 30.51 MB/s 27.33 MB/s clean 90.92 MB/s 98.83 MB/s 128.87 MB/s read tree 11.90 MB/s 11.84 MB/s 8.56 MB/s read compiled 28.75 MB/s 29.96 MB/s 24.25 MB/s delete tree 8.39 seconds 8.12 seconds 8.46 seconds delete compiled 8.35 seconds 8.44 seconds 5.11 seconds stat tree 6.03 seconds 5.59 seconds 5.19 seconds stat compiled tree 9.00 seconds 9.52 seconds 8.49 seconds xfs + write_inode log_force removal =================================== intial create 25.87 MB/s 25.76 MB/s 25.87 MB/s create 15.18 MB/s 14.80 MB/s 14.94 MB/s patch 3.13 MB/s 3.14 MB/s 3.11 MB/s compile 36.74 MB/s 37.17 MB/s 36.84 MB/s clean 226.02 MB/s 222.58 MB/s 217.94 MB/s read tree 15.14 MB/s 15.02 MB/s 15.14 MB/s read compiled tree 29.30 MB/s 29.31 MB/s 29.32 MB/s delete tree 6.22 seconds 6.14 seconds 6.15 seconds delete compiled tree 5.75 seconds 5.92 seconds 5.81 seconds stat tree 4.60 seconds 4.51 seconds 4.56 seconds stat compiled tree 4.07 seconds 3.87 seconds 3.96 seconds In addition to that also remove the delwri inode flush that is unessecary now that bulkstat is always coherent. Signed-off-by: Christoph Hellwig Reviewed-by: Dave Chinner --- fs/xfs/linux-2.6/xfs_super.c | 52 +++++++++++++++++++------------------------- 1 file changed, 22 insertions(+), 30 deletions(-) (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_super.c b/fs/xfs/linux-2.6/xfs_super.c index b8ad17e730b6..9a72c05b6177 100644 --- a/fs/xfs/linux-2.6/xfs_super.c +++ b/fs/xfs/linux-2.6/xfs_super.c @@ -1025,7 +1025,6 @@ xfs_log_inode( */ xfs_trans_ijoin(tp, ip); xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); - xfs_trans_set_sync(tp); error = xfs_trans_commit(tp, 0); xfs_ilock_demote(ip, XFS_ILOCK_EXCL); @@ -1048,20 +1047,11 @@ xfs_fs_write_inode( if (wbc->sync_mode == WB_SYNC_ALL) { /* - * Make sure the inode has hit stable storage. By using the - * log and the fsync transactions we reduce the IOs we have - * to do here from two (log and inode) to just the log. - * - * Note: We still need to do a delwri write of the inode after - * this to flush it to the backing buffer so that bulkstat - * works properly if this is the first time the inode has been - * written. Because we hold the ilock atomically over the - * transaction commit and the inode flush we are guaranteed - * that the inode is not pinned when it returns. If the flush - * lock is already held, then the inode has already been - * flushed once and we don't need to flush it again. Hence - * the code will only flush the inode if it isn't already - * being flushed. + * Make sure the inode has made it it into the log. Instead + * of forcing it all the way to stable storage using a + * synchronous transaction we let the log force inside the + * ->sync_fs call do that for thus, which reduces the number + * of synchronous log foces dramatically. */ xfs_ioend_wait(ip); xfs_ilock(ip, XFS_ILOCK_SHARED); @@ -1075,27 +1065,29 @@ xfs_fs_write_inode( * We make this non-blocking if the inode is contended, return * EAGAIN to indicate to the caller that they did not succeed. * This prevents the flush path from blocking on inodes inside - * another operation right now, they get caught later by xfs_sync. + * another operation right now, they get caught later by + * xfs_sync. */ if (!xfs_ilock_nowait(ip, XFS_ILOCK_SHARED)) goto out; - } - if (xfs_ipincount(ip) || !xfs_iflock_nowait(ip)) - goto out_unlock; + if (xfs_ipincount(ip) || !xfs_iflock_nowait(ip)) + goto out_unlock; - /* - * Now we have the flush lock and the inode is not pinned, we can check - * if the inode is really clean as we know that there are no pending - * transaction completions, it is not waiting on the delayed write - * queue and there is no IO in progress. - */ - if (xfs_inode_clean(ip)) { - xfs_ifunlock(ip); - error = 0; - goto out_unlock; + /* + * Now we have the flush lock and the inode is not pinned, we + * can check if the inode is really clean as we know that + * there are no pending transaction completions, it is not + * waiting on the delayed write queue and there is no IO in + * progress. + */ + if (xfs_inode_clean(ip)) { + xfs_ifunlock(ip); + error = 0; + goto out_unlock; + } + error = xfs_iflush(ip, 0); } - error = xfs_iflush(ip, 0); out_unlock: xfs_iunlock(ip, XFS_ILOCK_SHARED); -- cgit v1.2.3 From 3070451eea1ed8e3bde0573183c7d8ac25fd5e97 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 24 Jun 2010 11:42:19 +1000 Subject: xfs: reduce stack usage in xfs_iomap xfs_iomap passes a xfs_bmbt_irec pointer to xfs_iomap_write_direct and xfs_iomap_write_allocate to give them the results of our read-only xfs_bmapi query. Instead of allocating a new xfs_bmbt_irec on stack for the next call to xfs_bmapi re use the one we got passed as it's not used after this point. Signed-off-by: Christoph Hellwig Reviewed-by: Dave Chinner --- fs/xfs/xfs_iomap.c | 52 ++++++++++++++++++++++++++++------------------------ 1 file changed, 28 insertions(+), 24 deletions(-) (limited to 'fs') diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c index 39ad46b3ed46..a0dbcaff911a 100644 --- a/fs/xfs/xfs_iomap.c +++ b/fs/xfs/xfs_iomap.c @@ -242,7 +242,7 @@ xfs_iomap_write_direct( xfs_off_t offset, size_t count, int flags, - xfs_bmbt_irec_t *ret_imap, + xfs_bmbt_irec_t *imap, int *nmaps) { xfs_mount_t *mp = ip->i_mount; @@ -256,7 +256,6 @@ xfs_iomap_write_direct( int quota_flag; int rt; xfs_trans_t *tp; - xfs_bmbt_irec_t imap; xfs_bmap_free_t free_list; uint qblocks, resblks, resrtextents; int committed; @@ -280,10 +279,10 @@ xfs_iomap_write_direct( if (error) goto error_out; } else { - if (*nmaps && (ret_imap->br_startblock == HOLESTARTBLOCK)) + if (*nmaps && (imap->br_startblock == HOLESTARTBLOCK)) last_fsb = MIN(last_fsb, (xfs_fileoff_t) - ret_imap->br_blockcount + - ret_imap->br_startoff); + imap->br_blockcount + + imap->br_startoff); } count_fsb = last_fsb - offset_fsb; ASSERT(count_fsb > 0); @@ -336,12 +335,15 @@ xfs_iomap_write_direct( bmapi_flag |= XFS_BMAPI_PREALLOC; /* - * Issue the xfs_bmapi() call to allocate the blocks + * Issue the xfs_bmapi() call to allocate the blocks. + * + * From this point onwards we overwrite the imap pointer that the + * caller gave to us. */ xfs_bmap_init(&free_list, &firstfsb); nimaps = 1; error = xfs_bmapi(tp, ip, offset_fsb, count_fsb, bmapi_flag, - &firstfsb, 0, &imap, &nimaps, &free_list); + &firstfsb, 0, imap, &nimaps, &free_list); if (error) goto error0; @@ -363,12 +365,11 @@ xfs_iomap_write_direct( goto error_out; } - if (!(imap.br_startblock || XFS_IS_REALTIME_INODE(ip))) { - error = xfs_cmn_err_fsblock_zero(ip, &imap); + if (!(imap->br_startblock || XFS_IS_REALTIME_INODE(ip))) { + error = xfs_cmn_err_fsblock_zero(ip, imap); goto error_out; } - *ret_imap = imap; *nmaps = 1; return 0; @@ -542,7 +543,7 @@ xfs_iomap_write_allocate( xfs_inode_t *ip, xfs_off_t offset, size_t count, - xfs_bmbt_irec_t *map, + xfs_bmbt_irec_t *imap, int *retmap) { xfs_mount_t *mp = ip->i_mount; @@ -551,7 +552,6 @@ xfs_iomap_write_allocate( xfs_fsblock_t first_block; xfs_bmap_free_t free_list; xfs_filblks_t count_fsb; - xfs_bmbt_irec_t imap; xfs_trans_t *tp; int nimaps, committed; int error = 0; @@ -567,8 +567,8 @@ xfs_iomap_write_allocate( return XFS_ERROR(error); offset_fsb = XFS_B_TO_FSBT(mp, offset); - count_fsb = map->br_blockcount; - map_start_fsb = map->br_startoff; + count_fsb = imap->br_blockcount; + map_start_fsb = imap->br_startoff; XFS_STATS_ADD(xs_xstrat_bytes, XFS_FSB_TO_B(mp, count_fsb)); @@ -647,10 +647,15 @@ xfs_iomap_write_allocate( } } - /* Go get the actual blocks */ + /* + * Go get the actual blocks. + * + * From this point onwards we overwrite the imap + * pointer that the caller gave to us. + */ error = xfs_bmapi(tp, ip, map_start_fsb, count_fsb, XFS_BMAPI_WRITE, &first_block, 1, - &imap, &nimaps, &free_list); + imap, &nimaps, &free_list); if (error) goto trans_cancel; @@ -669,13 +674,12 @@ xfs_iomap_write_allocate( * See if we were able to allocate an extent that * covers at least part of the callers request */ - if (!(imap.br_startblock || XFS_IS_REALTIME_INODE(ip))) - return xfs_cmn_err_fsblock_zero(ip, &imap); + if (!(imap->br_startblock || XFS_IS_REALTIME_INODE(ip))) + return xfs_cmn_err_fsblock_zero(ip, imap); - if ((offset_fsb >= imap.br_startoff) && - (offset_fsb < (imap.br_startoff + - imap.br_blockcount))) { - *map = imap; + if ((offset_fsb >= imap->br_startoff) && + (offset_fsb < (imap->br_startoff + + imap->br_blockcount))) { *retmap = 1; XFS_STATS_INC(xs_xstrat_quick); return 0; @@ -685,8 +689,8 @@ xfs_iomap_write_allocate( * So far we have not mapped the requested part of the * file, just surrounding data, try again. */ - count_fsb -= imap.br_blockcount; - map_start_fsb = imap.br_startoff + imap.br_blockcount; + count_fsb -= imap->br_blockcount; + map_start_fsb = imap->br_startoff + imap->br_blockcount; } trans_cancel: -- cgit v1.2.3 From f2bde9b89b4d67c9bc3b963cb996f449ddcd27a4 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 24 Jun 2010 11:44:35 +1000 Subject: xfs: small cleanups for xfs_iomap / __xfs_get_blocks Remove the flags argument to __xfs_get_blocks as we can easily derive it from the direct argument, and remove the unused BMAPI_MMAP flag. Signed-off-by: Christoph Hellwig Reviewed-by: Dave Chinner --- fs/xfs/linux-2.6/xfs_aops.c | 17 +++++++++-------- fs/xfs/xfs_iomap.c | 2 +- fs/xfs/xfs_iomap.h | 2 -- 3 files changed, 10 insertions(+), 11 deletions(-) (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_aops.c b/fs/xfs/linux-2.6/xfs_aops.c index 1776cdd944b5..88ce1c6efff0 100644 --- a/fs/xfs/linux-2.6/xfs_aops.c +++ b/fs/xfs/linux-2.6/xfs_aops.c @@ -1295,9 +1295,9 @@ __xfs_get_blocks( sector_t iblock, struct buffer_head *bh_result, int create, - int direct, - bmapi_flags_t flags) + int direct) { + int flags = create ? BMAPI_WRITE : BMAPI_READ; struct xfs_bmbt_irec imap; xfs_off_t offset; ssize_t size; @@ -1312,8 +1312,11 @@ __xfs_get_blocks( if (!create && direct && offset >= i_size_read(inode)) return 0; - error = xfs_iomap(XFS_I(inode), offset, size, - create ? flags : BMAPI_READ, &imap, &nimap, &new); + if (direct && create) + flags |= BMAPI_DIRECT; + + error = xfs_iomap(XFS_I(inode), offset, size, flags, &imap, &nimap, + &new); if (error) return -error; if (nimap == 0) @@ -1393,8 +1396,7 @@ xfs_get_blocks( struct buffer_head *bh_result, int create) { - return __xfs_get_blocks(inode, iblock, - bh_result, create, 0, BMAPI_WRITE); + return __xfs_get_blocks(inode, iblock, bh_result, create, 0); } STATIC int @@ -1404,8 +1406,7 @@ xfs_get_blocks_direct( struct buffer_head *bh_result, int create) { - return __xfs_get_blocks(inode, iblock, - bh_result, create, 1, BMAPI_WRITE|BMAPI_DIRECT); + return __xfs_get_blocks(inode, iblock, bh_result, create, 1); } STATIC void diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c index a0dbcaff911a..20576146369f 100644 --- a/fs/xfs/xfs_iomap.c +++ b/fs/xfs/xfs_iomap.c @@ -133,7 +133,7 @@ xfs_iomap( break; } - if (flags & (BMAPI_DIRECT|BMAPI_MMAP)) { + if (flags & BMAPI_DIRECT) { error = xfs_iomap_write_direct(ip, offset, count, flags, imap, nimaps); } else { diff --git a/fs/xfs/xfs_iomap.h b/fs/xfs/xfs_iomap.h index 81ac4afd45b3..2cea2daf01ca 100644 --- a/fs/xfs/xfs_iomap.h +++ b/fs/xfs/xfs_iomap.h @@ -26,7 +26,6 @@ typedef enum { /* modifiers */ BMAPI_IGNSTATE = (1 << 4), /* ignore unwritten state on read */ BMAPI_DIRECT = (1 << 5), /* direct instead of buffered write */ - BMAPI_MMAP = (1 << 6), /* allocate for mmap write */ BMAPI_TRYLOCK = (1 << 7), /* non-blocking request */ } bmapi_flags_t; @@ -36,7 +35,6 @@ typedef enum { { BMAPI_ALLOCATE, "ALLOCATE" }, \ { BMAPI_IGNSTATE, "IGNSTATE" }, \ { BMAPI_DIRECT, "DIRECT" }, \ - { BMAPI_MMAP, "MMAP" }, \ { BMAPI_TRYLOCK, "TRYLOCK" } struct xfs_inode; -- cgit v1.2.3 From 64c86149410bc62d9ac27a0594b3402a2aca03d8 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 24 Jun 2010 11:45:34 +1000 Subject: xfs: remove explicit xfs_sync_data/xfs_sync_attr calls on umount On the final put of a superblock the VFS already calls sync_filesystem for us to write out all data and wait for it. No need to start another asynchronous writeback inside ->put_super. Signed-off-by: Christoph Hellwig Reviewed-by: Dave Chinner --- fs/xfs/linux-2.6/xfs_super.c | 12 ------------ fs/xfs/linux-2.6/xfs_sync.c | 4 ++-- fs/xfs/linux-2.6/xfs_sync.h | 3 --- 3 files changed, 2 insertions(+), 17 deletions(-) (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_super.c b/fs/xfs/linux-2.6/xfs_super.c index 9a72c05b6177..0ac1df74341f 100644 --- a/fs/xfs/linux-2.6/xfs_super.c +++ b/fs/xfs/linux-2.6/xfs_super.c @@ -1146,18 +1146,6 @@ xfs_fs_put_super( xfs_syncd_stop(mp); - if (!(sb->s_flags & MS_RDONLY)) { - /* - * XXX(hch): this should be SYNC_WAIT. - * - * Or more likely not needed at all because the VFS is already - * calling ->sync_fs after shutting down all filestem - * operations and just before calling ->put_super. - */ - xfs_sync_data(mp, 0); - xfs_sync_attr(mp, 0); - } - /* * Blow away any referenced inode in the filestreams cache. * This can and will cause log traffic as inodes go inactive diff --git a/fs/xfs/linux-2.6/xfs_sync.c b/fs/xfs/linux-2.6/xfs_sync.c index 0283b88bc16c..66cefb274385 100644 --- a/fs/xfs/linux-2.6/xfs_sync.c +++ b/fs/xfs/linux-2.6/xfs_sync.c @@ -308,7 +308,7 @@ xfs_sync_inode_attr( /* * Write out pagecache data for the whole filesystem. */ -int +STATIC int xfs_sync_data( struct xfs_mount *mp, int flags) @@ -329,7 +329,7 @@ xfs_sync_data( /* * Write out inode metadata (attributes) for the whole filesystem. */ -int +STATIC int xfs_sync_attr( struct xfs_mount *mp, int flags) diff --git a/fs/xfs/linux-2.6/xfs_sync.h b/fs/xfs/linux-2.6/xfs_sync.h index e28139aaa4aa..fe78726196f8 100644 --- a/fs/xfs/linux-2.6/xfs_sync.h +++ b/fs/xfs/linux-2.6/xfs_sync.h @@ -35,9 +35,6 @@ typedef struct xfs_sync_work { int xfs_syncd_init(struct xfs_mount *mp); void xfs_syncd_stop(struct xfs_mount *mp); -int xfs_sync_attr(struct xfs_mount *mp, int flags); -int xfs_sync_data(struct xfs_mount *mp, int flags); - int xfs_quiesce_data(struct xfs_mount *mp); void xfs_quiesce_attr(struct xfs_mount *mp); -- cgit v1.2.3 From 807cbbdb438d172b87b380eebc1f1c1a5a3549b2 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 24 Jun 2010 11:49:12 +1000 Subject: xfs: do not use emums for flags used in tracing The tracing code can't print flags defined as enums. Most flags that we want to print are defines as macros already, but move the few remaining ones over to make the trace output more useful. Signed-off-by: Christoph Hellwig Reviewed-by: Dave Chinner --- fs/xfs/linux-2.6/xfs_buf.h | 102 ++++++++++++++++++++++----------------------- fs/xfs/xfs_alloc.h | 20 ++++----- fs/xfs/xfs_iomap.h | 20 ++++----- 3 files changed, 71 insertions(+), 71 deletions(-) (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_buf.h b/fs/xfs/linux-2.6/xfs_buf.h index 2a9749a3a762..814f9e83b516 100644 --- a/fs/xfs/linux-2.6/xfs_buf.h +++ b/fs/xfs/linux-2.6/xfs_buf.h @@ -44,57 +44,57 @@ typedef enum { XBRW_ZERO = 3, /* Zero target memory */ } xfs_buf_rw_t; -typedef enum { - XBF_READ = (1 << 0), /* buffer intended for reading from device */ - XBF_WRITE = (1 << 1), /* buffer intended for writing to device */ - XBF_MAPPED = (1 << 2), /* buffer mapped (b_addr valid) */ - XBF_ASYNC = (1 << 4), /* initiator will not wait for completion */ - XBF_DONE = (1 << 5), /* all pages in the buffer uptodate */ - XBF_DELWRI = (1 << 6), /* buffer has dirty pages */ - XBF_STALE = (1 << 7), /* buffer has been staled, do not find it */ - XBF_FS_MANAGED = (1 << 8), /* filesystem controls freeing memory */ - XBF_ORDERED = (1 << 11), /* use ordered writes */ - XBF_READ_AHEAD = (1 << 12), /* asynchronous read-ahead */ - XBF_LOG_BUFFER = (1 << 13), /* this is a buffer used for the log */ - - /* flags used only as arguments to access routines */ - XBF_LOCK = (1 << 14), /* lock requested */ - XBF_TRYLOCK = (1 << 15), /* lock requested, but do not wait */ - XBF_DONT_BLOCK = (1 << 16), /* do not block in current thread */ - - /* flags used only internally */ - _XBF_PAGE_CACHE = (1 << 17),/* backed by pagecache */ - _XBF_PAGES = (1 << 18), /* backed by refcounted pages */ - _XBF_RUN_QUEUES = (1 << 19),/* run block device task queue */ - _XBF_DELWRI_Q = (1 << 21), /* buffer on delwri queue */ - - /* - * Special flag for supporting metadata blocks smaller than a FSB. - * - * In this case we can have multiple xfs_buf_t on a single page and - * need to lock out concurrent xfs_buf_t readers as they only - * serialise access to the buffer. - * - * If the FSB size >= PAGE_CACHE_SIZE case, we have no serialisation - * between reads of the page. Hence we can have one thread read the - * page and modify it, but then race with another thread that thinks - * the page is not up-to-date and hence reads it again. - * - * The result is that the first modifcation to the page is lost. - * This sort of AGF/AGI reading race can happen when unlinking inodes - * that require truncation and results in the AGI unlinked list - * modifications being lost. - */ - _XBF_PAGE_LOCKED = (1 << 22), - - /* - * If we try a barrier write, but it fails we have to communicate - * this to the upper layers. Unfortunately b_error gets overwritten - * when the buffer is re-issued so we have to add another flag to - * keep this information. - */ - _XFS_BARRIER_FAILED = (1 << 23), -} xfs_buf_flags_t; +#define XBF_READ (1 << 0) /* buffer intended for reading from device */ +#define XBF_WRITE (1 << 1) /* buffer intended for writing to device */ +#define XBF_MAPPED (1 << 2) /* buffer mapped (b_addr valid) */ +#define XBF_ASYNC (1 << 4) /* initiator will not wait for completion */ +#define XBF_DONE (1 << 5) /* all pages in the buffer uptodate */ +#define XBF_DELWRI (1 << 6) /* buffer has dirty pages */ +#define XBF_STALE (1 << 7) /* buffer has been staled, do not find it */ +#define XBF_FS_MANAGED (1 << 8) /* filesystem controls freeing memory */ +#define XBF_ORDERED (1 << 11)/* use ordered writes */ +#define XBF_READ_AHEAD (1 << 12)/* asynchronous read-ahead */ +#define XBF_LOG_BUFFER (1 << 13)/* this is a buffer used for the log */ + +/* flags used only as arguments to access routines */ +#define XBF_LOCK (1 << 14)/* lock requested */ +#define XBF_TRYLOCK (1 << 15)/* lock requested, but do not wait */ +#define XBF_DONT_BLOCK (1 << 16)/* do not block in current thread */ + +/* flags used only internally */ +#define _XBF_PAGE_CACHE (1 << 17)/* backed by pagecache */ +#define _XBF_PAGES (1 << 18)/* backed by refcounted pages */ +#define _XBF_RUN_QUEUES (1 << 19)/* run block device task queue */ +#define _XBF_DELWRI_Q (1 << 21)/* buffer on delwri queue */ + +/* + * Special flag for supporting metadata blocks smaller than a FSB. + * + * In this case we can have multiple xfs_buf_t on a single page and + * need to lock out concurrent xfs_buf_t readers as they only + * serialise access to the buffer. + * + * If the FSB size >= PAGE_CACHE_SIZE case, we have no serialisation + * between reads of the page. Hence we can have one thread read the + * page and modify it, but then race with another thread that thinks + * the page is not up-to-date and hence reads it again. + * + * The result is that the first modifcation to the page is lost. + * This sort of AGF/AGI reading race can happen when unlinking inodes + * that require truncation and results in the AGI unlinked list + * modifications being lost. + */ +#define _XBF_PAGE_LOCKED (1 << 22) + +/* + * If we try a barrier write, but it fails we have to communicate + * this to the upper layers. Unfortunately b_error gets overwritten + * when the buffer is re-issued so we have to add another flag to + * keep this information. + */ +#define _XFS_BARRIER_FAILED (1 << 23) + +typedef unsigned int xfs_buf_flags_t; #define XFS_BUF_FLAGS \ { XBF_READ, "READ" }, \ diff --git a/fs/xfs/xfs_alloc.h b/fs/xfs/xfs_alloc.h index 6d05199b667c..895009a97271 100644 --- a/fs/xfs/xfs_alloc.h +++ b/fs/xfs/xfs_alloc.h @@ -27,16 +27,16 @@ struct xfs_busy_extent; /* * Freespace allocation types. Argument to xfs_alloc_[v]extent. */ -typedef enum xfs_alloctype -{ - XFS_ALLOCTYPE_ANY_AG, /* allocate anywhere, use rotor */ - XFS_ALLOCTYPE_FIRST_AG, /* ... start at ag 0 */ - XFS_ALLOCTYPE_START_AG, /* anywhere, start in this a.g. */ - XFS_ALLOCTYPE_THIS_AG, /* anywhere in this a.g. */ - XFS_ALLOCTYPE_START_BNO, /* near this block else anywhere */ - XFS_ALLOCTYPE_NEAR_BNO, /* in this a.g. and near this block */ - XFS_ALLOCTYPE_THIS_BNO /* at exactly this block */ -} xfs_alloctype_t; +#define XFS_ALLOCTYPE_ANY_AG 0x01 /* allocate anywhere, use rotor */ +#define XFS_ALLOCTYPE_FIRST_AG 0x02 /* ... start at ag 0 */ +#define XFS_ALLOCTYPE_START_AG 0x04 /* anywhere, start in this a.g. */ +#define XFS_ALLOCTYPE_THIS_AG 0x08 /* anywhere in this a.g. */ +#define XFS_ALLOCTYPE_START_BNO 0x10 /* near this block else anywhere */ +#define XFS_ALLOCTYPE_NEAR_BNO 0x20 /* in this a.g. and near this block */ +#define XFS_ALLOCTYPE_THIS_BNO 0x40 /* at exactly this block */ + +/* this should become an enum again when the tracing code is fixed */ +typedef unsigned int xfs_alloctype_t; #define XFS_ALLOC_TYPES \ { XFS_ALLOCTYPE_ANY_AG, "ANY_AG" }, \ diff --git a/fs/xfs/xfs_iomap.h b/fs/xfs/xfs_iomap.h index 2cea2daf01ca..7748a430f50d 100644 --- a/fs/xfs/xfs_iomap.h +++ b/fs/xfs/xfs_iomap.h @@ -18,16 +18,16 @@ #ifndef __XFS_IOMAP_H__ #define __XFS_IOMAP_H__ -typedef enum { - /* base extent manipulation calls */ - BMAPI_READ = (1 << 0), /* read extents */ - BMAPI_WRITE = (1 << 1), /* create extents */ - BMAPI_ALLOCATE = (1 << 2), /* delayed allocate to real extents */ - /* modifiers */ - BMAPI_IGNSTATE = (1 << 4), /* ignore unwritten state on read */ - BMAPI_DIRECT = (1 << 5), /* direct instead of buffered write */ - BMAPI_TRYLOCK = (1 << 7), /* non-blocking request */ -} bmapi_flags_t; +/* base extent manipulation calls */ +#define BMAPI_READ (1 << 0) /* read extents */ +#define BMAPI_WRITE (1 << 1) /* create extents */ +#define BMAPI_ALLOCATE (1 << 2) /* delayed allocate to real extents */ + +/* modifiers */ +#define BMAPI_IGNSTATE (1 << 4) /* ignore unwritten state on read */ +#define BMAPI_DIRECT (1 << 5) /* direct instead of buffered write */ +#define BMAPI_MMA (1 << 6) /* allocate for mmap write */ +#define BMAPI_TRYLOCK (1 << 7) /* non-blocking request */ #define BMAPI_FLAGS \ { BMAPI_READ, "READ" }, \ -- cgit v1.2.3 From d2e078c33c24f97411b0fdd7cd2173e68125e7e3 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 24 Jun 2010 11:50:22 +1000 Subject: xfs: some iget tracing cleanups / fixes The xfs_iget_alloc/found tracepoints are a bit misnamed and misplaced. Rename them to xfs_iget_hit/xfs_iget_miss and move them to the beggining of the xfs_iget_cache_hit/miss functions. Add a new xfs_iget_reclaim_fail tracepoint for the case where we fail to re-initialize a VFS inode, and add a second instance of the xfs_iget_skip tracepoint for the case of a failed igrab() call. Signed-off-by: Christoph Hellwig Reviewed-by: Dave Chinner --- fs/xfs/linux-2.6/xfs_trace.h | 5 +++-- fs/xfs/xfs_iget.c | 8 ++++---- 2 files changed, 7 insertions(+), 6 deletions(-) (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_trace.h b/fs/xfs/linux-2.6/xfs_trace.h index 3f2eec28c70d..9efe368d38c7 100644 --- a/fs/xfs/linux-2.6/xfs_trace.h +++ b/fs/xfs/linux-2.6/xfs_trace.h @@ -561,8 +561,9 @@ DEFINE_EVENT(xfs_iget_class, name, \ TP_ARGS(ip)) DEFINE_IGET_EVENT(xfs_iget_skip); DEFINE_IGET_EVENT(xfs_iget_reclaim); -DEFINE_IGET_EVENT(xfs_iget_found); -DEFINE_IGET_EVENT(xfs_iget_alloc); +DEFINE_IGET_EVENT(xfs_iget_reclaim_fail); +DEFINE_IGET_EVENT(xfs_iget_hit); +DEFINE_IGET_EVENT(xfs_iget_miss); DECLARE_EVENT_CLASS(xfs_inode_class, TP_PROTO(struct xfs_inode *ip, unsigned long caller_ip), diff --git a/fs/xfs/xfs_iget.c b/fs/xfs/xfs_iget.c index 633cb331b9e9..75664d1b9f59 100644 --- a/fs/xfs/xfs_iget.c +++ b/fs/xfs/xfs_iget.c @@ -208,7 +208,7 @@ xfs_iget_cache_hit( ip->i_flags &= ~XFS_INEW; ip->i_flags |= XFS_IRECLAIMABLE; __xfs_inode_set_reclaim_tag(pag, ip); - trace_xfs_iget_reclaim(ip); + trace_xfs_iget_reclaim_fail(ip); goto out_error; } @@ -223,6 +223,7 @@ xfs_iget_cache_hit( } else { /* If the VFS inode is being torn down, pause and try again. */ if (!igrab(inode)) { + trace_xfs_iget_skip(ip); error = EAGAIN; goto out_error; } @@ -230,6 +231,7 @@ xfs_iget_cache_hit( /* We've got a live one. */ spin_unlock(&ip->i_flags_lock); read_unlock(&pag->pag_ici_lock); + trace_xfs_iget_hit(ip); } if (lock_flags != 0) @@ -238,7 +240,6 @@ xfs_iget_cache_hit( xfs_iflags_clear(ip, XFS_ISTALE); XFS_STATS_INC(xs_ig_found); - trace_xfs_iget_found(ip); return 0; out_error: @@ -271,7 +272,7 @@ xfs_iget_cache_miss( if (error) goto out_destroy; - xfs_itrace_entry(ip); + trace_xfs_iget_miss(ip); if ((ip->i_d.di_mode == 0) && !(flags & XFS_IGET_CREATE)) { error = ENOENT; @@ -317,7 +318,6 @@ xfs_iget_cache_miss( write_unlock(&pag->pag_ici_lock); radix_tree_preload_end(); - trace_xfs_iget_alloc(ip); *ipp = ip; return 0; -- cgit v1.2.3 From ef35e9255d4ed12522e836fbcec861e7306d794a Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 24 Jun 2010 11:51:19 +1000 Subject: xfs: remove xfs_iput_new We never get an i_mode of 0 or a locked VFS inode until we pass in the XFS_IGET_CREATE flag to xfs_iget, which makes xfs_iput_new equivalent to xfs_iput for the only caller. In addition to that xfs_nfs_get_inode does not even need to lock the inode given that the generation never changes for a life inode, so just pass a 0 lock_flags to xfs_iget and release the inode using IRELE in the error path. Signed-off-by: Christoph Hellwig Reviewed-by: Dave Chinner --- fs/xfs/linux-2.6/xfs_export.c | 7 +++---- fs/xfs/xfs_iget.c | 23 ----------------------- fs/xfs/xfs_inode.h | 1 - 3 files changed, 3 insertions(+), 28 deletions(-) (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_export.c b/fs/xfs/linux-2.6/xfs_export.c index 09c91325f727..3764d74790ec 100644 --- a/fs/xfs/linux-2.6/xfs_export.c +++ b/fs/xfs/linux-2.6/xfs_export.c @@ -29,6 +29,7 @@ #include "xfs_bmap_btree.h" #include "xfs_inode.h" #include "xfs_inode_item.h" +#include "xfs_trace.h" /* * Note that we only accept fileids which are long enough rather than allow @@ -131,8 +132,7 @@ xfs_nfs_get_inode( * fine and not an indication of a corrupted filesystem as clients can * send invalid file handles and we have to handle it gracefully.. */ - error = xfs_iget(mp, NULL, ino, XFS_IGET_UNTRUSTED, - XFS_ILOCK_SHARED, &ip); + error = xfs_iget(mp, NULL, ino, XFS_IGET_UNTRUSTED, 0, &ip); if (error) { /* * EINVAL means the inode cluster doesn't exist anymore. @@ -147,11 +147,10 @@ xfs_nfs_get_inode( } if (ip->i_d.di_gen != generation) { - xfs_iput_new(ip, XFS_ILOCK_SHARED); + IRELE(ip); return ERR_PTR(-ENOENT); } - xfs_iunlock(ip, XFS_ILOCK_SHARED); return VFS_I(ip); } diff --git a/fs/xfs/xfs_iget.c b/fs/xfs/xfs_iget.c index 75664d1b9f59..b460e62bcf86 100644 --- a/fs/xfs/xfs_iget.c +++ b/fs/xfs/xfs_iget.c @@ -434,29 +434,6 @@ xfs_iput(xfs_inode_t *ip, IRELE(ip); } -/* - * Special iput for brand-new inodes that are still locked - */ -void -xfs_iput_new( - xfs_inode_t *ip, - uint lock_flags) -{ - struct inode *inode = VFS_I(ip); - - xfs_itrace_entry(ip); - - if ((ip->i_d.di_mode == 0)) { - ASSERT(!xfs_iflags_test(ip, XFS_IRECLAIMABLE)); - make_bad_inode(inode); - } - if (inode->i_state & I_NEW) - unlock_new_inode(inode); - if (lock_flags) - xfs_iunlock(ip, lock_flags); - IRELE(ip); -} - /* * This is called free all the memory associated with an inode. * It must free the inode itself and any buffers allocated for diff --git a/fs/xfs/xfs_inode.h b/fs/xfs/xfs_inode.h index 78550df13cd6..7a19d5237656 100644 --- a/fs/xfs/xfs_inode.h +++ b/fs/xfs/xfs_inode.h @@ -444,7 +444,6 @@ static inline void xfs_ifunlock(xfs_inode_t *ip) int xfs_iget(struct xfs_mount *, struct xfs_trans *, xfs_ino_t, uint, uint, xfs_inode_t **); void xfs_iput(xfs_inode_t *, uint); -void xfs_iput_new(xfs_inode_t *, uint); void xfs_ilock(xfs_inode_t *, uint); int xfs_ilock_nowait(xfs_inode_t *, uint); void xfs_iunlock(xfs_inode_t *, uint); -- cgit v1.2.3 From f2d6761433d69d94e0b39ac44ef0f0f0b0508065 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 24 Jun 2010 11:52:50 +1000 Subject: xfs: remove xfs_iput xfs_iput is just a small wrapper for xfs_iunlock + IRELE. Having this out of line wrapper means the trace events in those two can't track their caller properly. So just remove the wrapper and opencode the unlock + rele in the few callers. Signed-off-by: Christoph Hellwig Reviewed-by: Dave Chinner --- fs/xfs/quota/xfs_qm.c | 6 ++++-- fs/xfs/quota/xfs_qm_syscalls.c | 9 ++++++--- fs/xfs/xfs_iget.c | 17 ----------------- fs/xfs/xfs_inode.h | 1 - fs/xfs/xfs_inode_item.c | 6 ++++-- fs/xfs/xfs_itable.c | 4 +++- 6 files changed, 17 insertions(+), 26 deletions(-) (limited to 'fs') diff --git a/fs/xfs/quota/xfs_qm.c b/fs/xfs/quota/xfs_qm.c index 7a33d65e2d28..9a92407109a1 100644 --- a/fs/xfs/quota/xfs_qm.c +++ b/fs/xfs/quota/xfs_qm.c @@ -1662,7 +1662,8 @@ xfs_qm_dqusage_adjust( * making us disable quotas for the file system. */ if ((error = xfs_qm_dqget_noattach(ip, &udqp, &gdqp))) { - xfs_iput(ip, XFS_ILOCK_EXCL); + xfs_iunlock(ip, XFS_ILOCK_EXCL); + IRELE(ip); *res = BULKSTAT_RV_GIVEUP; return error; } @@ -1675,7 +1676,8 @@ xfs_qm_dqusage_adjust( * Walk thru the extent list and count the realtime blocks. */ if ((error = xfs_qm_get_rtblks(ip, &rtblks))) { - xfs_iput(ip, XFS_ILOCK_EXCL); + xfs_iunlock(ip, XFS_ILOCK_EXCL); + IRELE(ip); if (udqp) xfs_qm_dqput(udqp); if (gdqp) diff --git a/fs/xfs/quota/xfs_qm_syscalls.c b/fs/xfs/quota/xfs_qm_syscalls.c index 98dc6feef9f1..73f2b203975e 100644 --- a/fs/xfs/quota/xfs_qm_syscalls.c +++ b/fs/xfs/quota/xfs_qm_syscalls.c @@ -865,8 +865,9 @@ xfs_dqrele_inode( xfs_qm_dqrele(ip->i_gdquot); ip->i_gdquot = NULL; } - xfs_iput(ip, XFS_ILOCK_EXCL); + xfs_iunlock(ip, XFS_ILOCK_EXCL); + IRELE(ip); return 0; } @@ -1133,7 +1134,8 @@ xfs_qm_internalqcheck_adjust( * of those now. */ if (! ipreleased) { - xfs_iput(ip, lock_flags); + xfs_iunlock(ip, lock_flags); + IRELE(ip); ipreleased = B_TRUE; goto again; } @@ -1150,7 +1152,8 @@ xfs_qm_internalqcheck_adjust( ASSERT(gd); xfs_qm_internalqcheck_dqadjust(ip, gd); } - xfs_iput(ip, lock_flags); + xfs_iunlock(ip, lock_flags); + IRELE(ip); *res = BULKSTAT_RV_DIDONE; return (0); } diff --git a/fs/xfs/xfs_iget.c b/fs/xfs/xfs_iget.c index b460e62bcf86..9e86f2116aa8 100644 --- a/fs/xfs/xfs_iget.c +++ b/fs/xfs/xfs_iget.c @@ -417,23 +417,6 @@ out_error_or_again: return error; } -/* - * Decrement reference count of an inode structure and unlock it. - * - * ip -- the inode being released - * lock_flags -- this parameter indicates the inode's locks to be - * to be released. See the comment on xfs_iunlock() for a list - * of valid values. - */ -void -xfs_iput(xfs_inode_t *ip, - uint lock_flags) -{ - xfs_itrace_entry(ip); - xfs_iunlock(ip, lock_flags); - IRELE(ip); -} - /* * This is called free all the memory associated with an inode. * It must free the inode itself and any buffers allocated for diff --git a/fs/xfs/xfs_inode.h b/fs/xfs/xfs_inode.h index 7a19d5237656..eb41559ea8cd 100644 --- a/fs/xfs/xfs_inode.h +++ b/fs/xfs/xfs_inode.h @@ -443,7 +443,6 @@ static inline void xfs_ifunlock(xfs_inode_t *ip) */ int xfs_iget(struct xfs_mount *, struct xfs_trans *, xfs_ino_t, uint, uint, xfs_inode_t **); -void xfs_iput(xfs_inode_t *, uint); void xfs_ilock(xfs_inode_t *, uint); int xfs_ilock_nowait(xfs_inode_t *, uint); void xfs_iunlock(xfs_inode_t *, uint); diff --git a/fs/xfs/xfs_inode_item.c b/fs/xfs/xfs_inode_item.c index 2998b2cb7466..065c1ad9b708 100644 --- a/fs/xfs/xfs_inode_item.c +++ b/fs/xfs/xfs_inode_item.c @@ -660,8 +660,10 @@ xfs_inode_item_unlock( lock_flags = iip->ili_lock_flags; iip->ili_lock_flags = 0; - if (lock_flags) - xfs_iput(iip->ili_inode, lock_flags); + if (lock_flags) { + xfs_iunlock(iip->ili_inode, lock_flags); + IRELE(iip->ili_inode); + } } /* diff --git a/fs/xfs/xfs_itable.c b/fs/xfs/xfs_itable.c index 200dc6fc8cc5..7e3626e5925c 100644 --- a/fs/xfs/xfs_itable.c +++ b/fs/xfs/xfs_itable.c @@ -34,6 +34,7 @@ #include "xfs_itable.h" #include "xfs_error.h" #include "xfs_btree.h" +#include "xfs_trace.h" STATIC int xfs_internal_inum( @@ -139,7 +140,8 @@ xfs_bulkstat_one_int( buf->bs_blocks = dic->di_nblocks + ip->i_delayed_blks; break; } - xfs_iput(ip, XFS_ILOCK_SHARED); + xfs_iunlock(ip, XFS_ILOCK_SHARED); + IRELE(ip); error = formatter(buffer, ubsize, ubused, buf); -- cgit v1.2.3 From cca28fb83d9e60779bb348edc33a62068e5f04a4 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 24 Jun 2010 11:57:09 +1000 Subject: xfs: split xfs_itrace_entry Replace the xfs_itrace_entry catchall with specific trace points. For most simple callers we now use the simple inode class, which used to be the iget class, but add more details tracing for namespace events, which now includes the name of the directory entries manipulated. Remove the xfs_inactive trace point, which is a duplicate of the clear_inode one, and the xfs_change_file_space trace point, which is immediately followed by the more specific alloc/free space trace points. Signed-off-by: Christoph Hellwig Reviewed-by: Dave Chinner --- fs/xfs/linux-2.6/xfs_acl.c | 2 +- fs/xfs/linux-2.6/xfs_aops.c | 2 +- fs/xfs/linux-2.6/xfs_file.c | 2 +- fs/xfs/linux-2.6/xfs_ioctl.c | 4 +- fs/xfs/linux-2.6/xfs_ioctl32.c | 2 +- fs/xfs/linux-2.6/xfs_iops.c | 2 +- fs/xfs/linux-2.6/xfs_super.c | 7 +-- fs/xfs/linux-2.6/xfs_trace.h | 115 ++++++++++++++++++++++++++++++++--------- fs/xfs/xfs_dir2.c | 2 +- fs/xfs/xfs_rename.c | 3 +- fs/xfs/xfs_vnodeops.c | 24 ++++----- 11 files changed, 113 insertions(+), 52 deletions(-) (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_acl.c b/fs/xfs/linux-2.6/xfs_acl.c index 9f769b5b38fc..b2771862fd3d 100644 --- a/fs/xfs/linux-2.6/xfs_acl.c +++ b/fs/xfs/linux-2.6/xfs_acl.c @@ -225,7 +225,7 @@ xfs_check_acl(struct inode *inode, int mask) struct posix_acl *acl; int error = -EAGAIN; - xfs_itrace_entry(ip); + trace_xfs_check_acl(ip); /* * If there is no attribute fork no ACL exists on this inode and diff --git a/fs/xfs/linux-2.6/xfs_aops.c b/fs/xfs/linux-2.6/xfs_aops.c index 88ce1c6efff0..ed9c3db376c3 100644 --- a/fs/xfs/linux-2.6/xfs_aops.c +++ b/fs/xfs/linux-2.6/xfs_aops.c @@ -1513,7 +1513,7 @@ xfs_vm_bmap( struct inode *inode = (struct inode *)mapping->host; struct xfs_inode *ip = XFS_I(inode); - xfs_itrace_entry(XFS_I(inode)); + trace_xfs_vm_bmap(XFS_I(inode)); xfs_ilock(ip, XFS_IOLOCK_SHARED); xfs_flush_pages(ip, (xfs_off_t)0, -1, 0, FI_REMAPF); xfs_iunlock(ip, XFS_IOLOCK_SHARED); diff --git a/fs/xfs/linux-2.6/xfs_file.c b/fs/xfs/linux-2.6/xfs_file.c index 22edad7a0bec..3447555e9f76 100644 --- a/fs/xfs/linux-2.6/xfs_file.c +++ b/fs/xfs/linux-2.6/xfs_file.c @@ -100,7 +100,7 @@ xfs_file_fsync( int error = 0; int log_flushed = 0; - xfs_itrace_entry(ip); + trace_xfs_file_fsync(ip); if (XFS_FORCED_SHUTDOWN(ip->i_mount)) return -XFS_ERROR(EIO); diff --git a/fs/xfs/linux-2.6/xfs_ioctl.c b/fs/xfs/linux-2.6/xfs_ioctl.c index a12dddad126e..237f5ffb2ee8 100644 --- a/fs/xfs/linux-2.6/xfs_ioctl.c +++ b/fs/xfs/linux-2.6/xfs_ioctl.c @@ -899,7 +899,7 @@ xfs_ioctl_setattr( struct xfs_dquot *olddquot = NULL; int code; - xfs_itrace_entry(ip); + trace_xfs_ioctl_setattr(ip); if (mp->m_flags & XFS_MOUNT_RDONLY) return XFS_ERROR(EROFS); @@ -1282,7 +1282,7 @@ xfs_file_ioctl( if (filp->f_mode & FMODE_NOCMTIME) ioflags |= IO_INVIS; - xfs_itrace_entry(ip); + trace_xfs_file_ioctl(ip); switch (cmd) { case XFS_IOC_ALLOCSP: diff --git a/fs/xfs/linux-2.6/xfs_ioctl32.c b/fs/xfs/linux-2.6/xfs_ioctl32.c index 6cd1225608ac..6c83f7f62dc9 100644 --- a/fs/xfs/linux-2.6/xfs_ioctl32.c +++ b/fs/xfs/linux-2.6/xfs_ioctl32.c @@ -540,7 +540,7 @@ xfs_file_compat_ioctl( if (filp->f_mode & FMODE_NOCMTIME) ioflags |= IO_INVIS; - xfs_itrace_entry(ip); + trace_xfs_file_compat_ioctl(ip); switch (cmd) { /* No size or alignment issues on any arch */ diff --git a/fs/xfs/linux-2.6/xfs_iops.c b/fs/xfs/linux-2.6/xfs_iops.c index 4393de6b0c07..536b81e63a3d 100644 --- a/fs/xfs/linux-2.6/xfs_iops.c +++ b/fs/xfs/linux-2.6/xfs_iops.c @@ -488,7 +488,7 @@ xfs_vn_getattr( struct xfs_inode *ip = XFS_I(inode); struct xfs_mount *mp = ip->i_mount; - xfs_itrace_entry(ip); + trace_xfs_getattr(ip); if (XFS_FORCED_SHUTDOWN(mp)) return XFS_ERROR(EIO); diff --git a/fs/xfs/linux-2.6/xfs_super.c b/fs/xfs/linux-2.6/xfs_super.c index 0ac1df74341f..22faaea5f3e1 100644 --- a/fs/xfs/linux-2.6/xfs_super.c +++ b/fs/xfs/linux-2.6/xfs_super.c @@ -907,7 +907,7 @@ xfs_fs_destroy_inode( { struct xfs_inode *ip = XFS_I(inode); - xfs_itrace_entry(ip); + trace_xfs_destroy_inode(ip); XFS_STATS_INC(vn_reclaim); @@ -1040,7 +1040,7 @@ xfs_fs_write_inode( struct xfs_mount *mp = ip->i_mount; int error = EAGAIN; - xfs_itrace_entry(ip); + trace_xfs_write_inode(ip); if (XFS_FORCED_SHUTDOWN(mp)) return XFS_ERROR(EIO); @@ -1107,7 +1107,8 @@ xfs_fs_clear_inode( { xfs_inode_t *ip = XFS_I(inode); - xfs_itrace_entry(ip); + trace_xfs_clear_inode(ip); + XFS_STATS_INC(vn_rele); XFS_STATS_INC(vn_remove); XFS_STATS_DEC(vn_active); diff --git a/fs/xfs/linux-2.6/xfs_trace.h b/fs/xfs/linux-2.6/xfs_trace.h index 9efe368d38c7..24e5580bf3e7 100644 --- a/fs/xfs/linux-2.6/xfs_trace.h +++ b/fs/xfs/linux-2.6/xfs_trace.h @@ -539,7 +539,7 @@ DEFINE_LOCK_EVENT(xfs_ilock_nowait); DEFINE_LOCK_EVENT(xfs_ilock_demote); DEFINE_LOCK_EVENT(xfs_iunlock); -DECLARE_EVENT_CLASS(xfs_iget_class, +DECLARE_EVENT_CLASS(xfs_inode_class, TP_PROTO(struct xfs_inode *ip), TP_ARGS(ip), TP_STRUCT__entry( @@ -555,17 +555,36 @@ DECLARE_EVENT_CLASS(xfs_iget_class, __entry->ino) ) -#define DEFINE_IGET_EVENT(name) \ -DEFINE_EVENT(xfs_iget_class, name, \ +#define DEFINE_INODE_EVENT(name) \ +DEFINE_EVENT(xfs_inode_class, name, \ TP_PROTO(struct xfs_inode *ip), \ TP_ARGS(ip)) -DEFINE_IGET_EVENT(xfs_iget_skip); -DEFINE_IGET_EVENT(xfs_iget_reclaim); -DEFINE_IGET_EVENT(xfs_iget_reclaim_fail); -DEFINE_IGET_EVENT(xfs_iget_hit); -DEFINE_IGET_EVENT(xfs_iget_miss); - -DECLARE_EVENT_CLASS(xfs_inode_class, +DEFINE_INODE_EVENT(xfs_iget_skip); +DEFINE_INODE_EVENT(xfs_iget_reclaim); +DEFINE_INODE_EVENT(xfs_iget_reclaim_fail); +DEFINE_INODE_EVENT(xfs_iget_hit); +DEFINE_INODE_EVENT(xfs_iget_miss); + +DEFINE_INODE_EVENT(xfs_getattr); +DEFINE_INODE_EVENT(xfs_setattr); +DEFINE_INODE_EVENT(xfs_readlink); +DEFINE_INODE_EVENT(xfs_alloc_file_space); +DEFINE_INODE_EVENT(xfs_free_file_space); +DEFINE_INODE_EVENT(xfs_readdir); +DEFINE_INODE_EVENT(xfs_check_acl); +DEFINE_INODE_EVENT(xfs_vm_bmap); +DEFINE_INODE_EVENT(xfs_file_ioctl); +DEFINE_INODE_EVENT(xfs_file_compat_ioctl); +DEFINE_INODE_EVENT(xfs_ioctl_setattr); +DEFINE_INODE_EVENT(xfs_file_fsync); +DEFINE_INODE_EVENT(xfs_destroy_inode); +DEFINE_INODE_EVENT(xfs_write_inode); +DEFINE_INODE_EVENT(xfs_clear_inode); + +DEFINE_INODE_EVENT(xfs_dquot_dqalloc); +DEFINE_INODE_EVENT(xfs_dquot_dqdetach); + +DECLARE_EVENT_CLASS(xfs_iref_class, TP_PROTO(struct xfs_inode *ip, unsigned long caller_ip), TP_ARGS(ip, caller_ip), TP_STRUCT__entry( @@ -590,20 +609,71 @@ DECLARE_EVENT_CLASS(xfs_inode_class, (char *)__entry->caller_ip) ) -#define DEFINE_INODE_EVENT(name) \ -DEFINE_EVENT(xfs_inode_class, name, \ +#define DEFINE_IREF_EVENT(name) \ +DEFINE_EVENT(xfs_iref_class, name, \ TP_PROTO(struct xfs_inode *ip, unsigned long caller_ip), \ TP_ARGS(ip, caller_ip)) -DEFINE_INODE_EVENT(xfs_ihold); -DEFINE_INODE_EVENT(xfs_irele); -DEFINE_INODE_EVENT(xfs_inode_pin); -DEFINE_INODE_EVENT(xfs_inode_unpin); -DEFINE_INODE_EVENT(xfs_inode_unpin_nowait); +DEFINE_IREF_EVENT(xfs_ihold); +DEFINE_IREF_EVENT(xfs_irele); +DEFINE_IREF_EVENT(xfs_inode_pin); +DEFINE_IREF_EVENT(xfs_inode_unpin); +DEFINE_IREF_EVENT(xfs_inode_unpin_nowait); + +DECLARE_EVENT_CLASS(xfs_namespace_class, + TP_PROTO(struct xfs_inode *dp, struct xfs_name *name), + TP_ARGS(dp, name), + TP_STRUCT__entry( + __field(dev_t, dev) + __field(xfs_ino_t, dp_ino) + __dynamic_array(char, name, name->len) + ), + TP_fast_assign( + __entry->dev = VFS_I(dp)->i_sb->s_dev; + __entry->dp_ino = dp->i_ino; + memcpy(__get_str(name), name->name, name->len); + ), + TP_printk("dev %d:%d dp ino 0x%llx name %s", + MAJOR(__entry->dev), MINOR(__entry->dev), + __entry->dp_ino, + __get_str(name)) +) -/* the old xfs_itrace_entry tracer - to be replaced by s.th. in the VFS */ -DEFINE_INODE_EVENT(xfs_inode); -#define xfs_itrace_entry(ip) \ - trace_xfs_inode(ip, _THIS_IP_) +#define DEFINE_NAMESPACE_EVENT(name) \ +DEFINE_EVENT(xfs_namespace_class, name, \ + TP_PROTO(struct xfs_inode *dp, struct xfs_name *name), \ + TP_ARGS(dp, name)) +DEFINE_NAMESPACE_EVENT(xfs_remove); +DEFINE_NAMESPACE_EVENT(xfs_link); +DEFINE_NAMESPACE_EVENT(xfs_lookup); +DEFINE_NAMESPACE_EVENT(xfs_create); +DEFINE_NAMESPACE_EVENT(xfs_symlink); + +TRACE_EVENT(xfs_rename, + TP_PROTO(struct xfs_inode *src_dp, struct xfs_inode *target_dp, + struct xfs_name *src_name, struct xfs_name *target_name), + TP_ARGS(src_dp, target_dp, src_name, target_name), + TP_STRUCT__entry( + __field(dev_t, dev) + __field(xfs_ino_t, src_dp_ino) + __field(xfs_ino_t, target_dp_ino) + __dynamic_array(char, src_name, src_name->len) + __dynamic_array(char, target_name, target_name->len) + ), + TP_fast_assign( + __entry->dev = VFS_I(src_dp)->i_sb->s_dev; + __entry->src_dp_ino = src_dp->i_ino; + __entry->target_dp_ino = target_dp->i_ino; + memcpy(__get_str(src_name), src_name->name, src_name->len); + memcpy(__get_str(target_name), target_name->name, target_name->len); + ), + TP_printk("dev %d:%d src dp ino 0x%llx target dp ino 0x%llx" + " src name %s target name %s", + MAJOR(__entry->dev), MINOR(__entry->dev), + __entry->src_dp_ino, + __entry->target_dp_ino, + __get_str(src_name), + __get_str(target_name)) +) DECLARE_EVENT_CLASS(xfs_dquot_class, TP_PROTO(struct xfs_dquot *dqp), @@ -683,9 +753,6 @@ DEFINE_DQUOT_EVENT(xfs_dqrele); DEFINE_DQUOT_EVENT(xfs_dqflush); DEFINE_DQUOT_EVENT(xfs_dqflush_force); DEFINE_DQUOT_EVENT(xfs_dqflush_done); -/* not really iget events, but we re-use the format */ -DEFINE_IGET_EVENT(xfs_dquot_dqalloc); -DEFINE_IGET_EVENT(xfs_dquot_dqdetach); DECLARE_EVENT_CLASS(xfs_loggrant_class, TP_PROTO(struct log *log, struct xlog_ticket *tic), diff --git a/fs/xfs/xfs_dir2.c b/fs/xfs/xfs_dir2.c index b53960a5f41e..a1321bc7f192 100644 --- a/fs/xfs/xfs_dir2.c +++ b/fs/xfs/xfs_dir2.c @@ -380,7 +380,7 @@ xfs_readdir( int rval; /* return value */ int v; /* type-checking value */ - xfs_itrace_entry(dp); + trace_xfs_readdir(dp); if (XFS_FORCED_SHUTDOWN(dp->i_mount)) return XFS_ERROR(EIO); diff --git a/fs/xfs/xfs_rename.c b/fs/xfs/xfs_rename.c index 778c87a8ebfc..8fca957200df 100644 --- a/fs/xfs/xfs_rename.c +++ b/fs/xfs/xfs_rename.c @@ -113,8 +113,7 @@ xfs_rename( int spaceres; int num_inodes; - xfs_itrace_entry(src_dp); - xfs_itrace_entry(target_dp); + trace_xfs_rename(src_dp, target_dp, src_name, target_name); new_parent = (src_dp != target_dp); src_is_directory = ((src_ip->i_d.di_mode & S_IFMT) == S_IFDIR); diff --git a/fs/xfs/xfs_vnodeops.c b/fs/xfs/xfs_vnodeops.c index ad599ccc416b..9865e1136017 100644 --- a/fs/xfs/xfs_vnodeops.c +++ b/fs/xfs/xfs_vnodeops.c @@ -68,7 +68,7 @@ xfs_setattr( struct xfs_dquot *udqp, *gdqp, *olddquot1, *olddquot2; int need_iolock = 1; - xfs_itrace_entry(ip); + trace_xfs_setattr(ip); if (mp->m_flags & XFS_MOUNT_RDONLY) return XFS_ERROR(EROFS); @@ -533,7 +533,7 @@ xfs_readlink( int pathlen; int error = 0; - xfs_itrace_entry(ip); + trace_xfs_readlink(ip); if (XFS_FORCED_SHUTDOWN(mp)) return XFS_ERROR(EIO); @@ -1005,8 +1005,6 @@ xfs_inactive( int error; int truncate; - xfs_itrace_entry(ip); - /* * If the inode is already free, then there can be nothing * to clean up here. @@ -1221,7 +1219,7 @@ xfs_lookup( int error; uint lock_mode; - xfs_itrace_entry(dp); + trace_xfs_lookup(dp, name); if (XFS_FORCED_SHUTDOWN(dp->i_mount)) return XFS_ERROR(EIO); @@ -1273,7 +1271,7 @@ xfs_create( uint log_res; uint log_count; - xfs_itrace_entry(dp); + trace_xfs_create(dp, name); if (XFS_FORCED_SHUTDOWN(mp)) return XFS_ERROR(EIO); @@ -1670,8 +1668,7 @@ xfs_remove( uint resblks; uint log_count; - xfs_itrace_entry(dp); - xfs_itrace_entry(ip); + trace_xfs_remove(dp, name); if (XFS_FORCED_SHUTDOWN(mp)) return XFS_ERROR(EIO); @@ -1832,8 +1829,7 @@ xfs_link( int committed; int resblks; - xfs_itrace_entry(tdp); - xfs_itrace_entry(sip); + trace_xfs_link(tdp, target_name); ASSERT(!S_ISDIR(sip->i_d.di_mode)); @@ -1966,7 +1962,7 @@ xfs_symlink( ip = NULL; tp = NULL; - xfs_itrace_entry(dp); + trace_xfs_symlink(dp, link_name); if (XFS_FORCED_SHUTDOWN(mp)) return XFS_ERROR(EIO); @@ -2256,7 +2252,7 @@ xfs_alloc_file_space( int committed; int error; - xfs_itrace_entry(ip); + trace_xfs_alloc_file_space(ip); if (XFS_FORCED_SHUTDOWN(mp)) return XFS_ERROR(EIO); @@ -2517,7 +2513,7 @@ xfs_free_file_space( mp = ip->i_mount; - xfs_itrace_entry(ip); + trace_xfs_free_file_space(ip); error = xfs_qm_dqattach(ip, 0); if (error) @@ -2707,8 +2703,6 @@ xfs_change_file_space( xfs_trans_t *tp; struct iattr iattr; - xfs_itrace_entry(ip); - if (!S_ISREG(ip->i_d.di_mode)) return XFS_ERROR(EINVAL); -- cgit v1.2.3 From 2727ccc950ae17375b15005403e1c35ba8fec1df Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Fri, 25 Jun 2010 11:08:40 +1000 Subject: xfs: unregister inode shrinker before freeing filesystem structures Currently we don't remove the XFS mount from the shrinker list until late in the unmount path. By this time, we have already torn down the internals of the filesystem (e.g. the per-ag structures), and hence if the shrinker is executed between the teardown and the unregistering, the shrinker will get NULL per-ag structure pointers and panic trying to dereference them. Fix this by removing the xfs mount from the shrinker list before tearing down it's internal structures. Signed-off-by: Dave Chinner Reviewed-by: Eric Sandeen Signed-off-by: Alex Elder --- fs/xfs/linux-2.6/xfs_super.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_super.c b/fs/xfs/linux-2.6/xfs_super.c index 22faaea5f3e1..c734bc6cf32e 100644 --- a/fs/xfs/linux-2.6/xfs_super.c +++ b/fs/xfs/linux-2.6/xfs_super.c @@ -1156,9 +1156,13 @@ xfs_fs_put_super( XFS_bflush(mp->m_ddev_targp); + /* + * Unregister the memory shrinker before we tear down the mount + * structure so we don't have memory reclaim racing with us here. + */ + xfs_inode_shrinker_unregister(mp); xfs_unmountfs(mp); xfs_freesb(mp); - xfs_inode_shrinker_unregister(mp); xfs_icsb_destroy_counters(mp); xfs_close_devices(mp); xfs_free_fsname(mp); -- cgit v1.2.3 From 651701d71da4dc0ac607f17a638e77906f0d280e Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 28 Jun 2010 10:34:34 -0400 Subject: xfs: remove incorrect log write optimization We do need a barrier for the first buffer of a split log write. Otherwise we might incorrectly stamp the tail LSN into transactions in the first part of the split write, or not flush data I/O before updating the inode size. Signed-off-by: Christoph Hellwig Signed-off-by: Alex Elder --- fs/xfs/xfs_log.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'fs') diff --git a/fs/xfs/xfs_log.c b/fs/xfs/xfs_log.c index 5a5a54ad3685..f309e1404fd6 100644 --- a/fs/xfs/xfs_log.c +++ b/fs/xfs/xfs_log.c @@ -1423,11 +1423,8 @@ xlog_sync(xlog_t *log, XFS_BUF_BUSY(bp); XFS_BUF_ASYNC(bp); bp->b_flags |= XBF_LOG_BUFFER; - /* - * Do an ordered write for the log block. - * Its unnecessary to flush the first split block in the log wrap case. - */ - if (!split && (log->l_mp->m_flags & XFS_MOUNT_BARRIER)) + + if (log->l_mp->m_flags & XFS_MOUNT_BARRIER) XFS_BUF_ORDERED(bp); ASSERT(XFS_BUF_ADDR(bp) <= log->l_logBBsize-1); -- cgit v1.2.3 From d4f7a5cbd5449a3d2097f601f588886ea7b70dc3 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 28 Jun 2010 10:34:44 -0400 Subject: xfs: allow writeback from kswapd We only need disable I/O from direct or memcg reclaim. Signed-off-by: Christoph Hellwig Signed-off-by: Alex Elder --- fs/xfs/linux-2.6/xfs_aops.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_aops.c b/fs/xfs/linux-2.6/xfs_aops.c index ed9c3db376c3..44ac7a0e2926 100644 --- a/fs/xfs/linux-2.6/xfs_aops.c +++ b/fs/xfs/linux-2.6/xfs_aops.c @@ -1049,16 +1049,15 @@ xfs_vm_writepage( /* * Refuse to write the page out if we are called from reclaim context. * - * This is primarily to avoid stack overflows when called from deep - * used stacks in random callers for direct reclaim, but disabling - * reclaim for kswap is a nice side-effect as kswapd causes rather - * suboptimal I/O patters, too. + * This avoids stack overflows when called from deeply used stacks in + * random callers for direct reclaim or memcg reclaim. We explicitly + * allow reclaim from kswapd as the stack usage there is relatively low. * * This should really be done by the core VM, but until that happens * filesystems like XFS, btrfs and ext4 have to take care of this * by themselves. */ - if (current->flags & PF_MEMALLOC) + if ((current->flags & (PF_MEMALLOC|PF_KSWAPD)) == PF_MEMALLOC) goto out_fail; /* -- cgit v1.2.3 From 78558fe8d8326b2395da33456cd9eec57ffc081a Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 28 Jun 2010 10:34:57 -0400 Subject: xfs: writepage always has buffers These days we always have buffers thanks to ->page_mkwrite. And we already have an assert a few lines above tripping in case that was not true due to a bug. Signed-off-by: Christoph Hellwig Signed-off-by: Alex Elder --- fs/xfs/linux-2.6/xfs_aops.c | 7 ------- 1 file changed, 7 deletions(-) (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_aops.c b/fs/xfs/linux-2.6/xfs_aops.c index 44ac7a0e2926..225ec0fa65b6 100644 --- a/fs/xfs/linux-2.6/xfs_aops.c +++ b/fs/xfs/linux-2.6/xfs_aops.c @@ -1072,13 +1072,6 @@ xfs_vm_writepage( if ((current->flags & PF_FSTRANS) && (delalloc || unwritten)) goto out_fail; - /* - * Delay hooking up buffer heads until we have - * made our go/no-go decision. - */ - if (!page_has_buffers(page)) - create_empty_buffers(page, 1 << inode->i_blkbits, 0); - /* Is this page beyond the end of the file? */ offset = i_size_read(inode); end_index = offset >> PAGE_CACHE_SHIFT; -- cgit v1.2.3 From fa17b25e9f95375081b43a741cf1c188682ec588 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sat, 3 Jul 2010 09:21:17 +0000 Subject: xfs: remove a dmapi leftover The open_exec file operation is only added by the external dmapi patch. Signed-off-by: Christoph Hellwig Reviewed-by: Alex Elder Signed-off-by: Alex Elder --- fs/xfs/linux-2.6/xfs_file.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_file.c b/fs/xfs/linux-2.6/xfs_file.c index 3447555e9f76..ba8ad422a165 100644 --- a/fs/xfs/linux-2.6/xfs_file.c +++ b/fs/xfs/linux-2.6/xfs_file.c @@ -921,9 +921,6 @@ const struct file_operations xfs_file_operations = { .open = xfs_file_open, .release = xfs_file_release, .fsync = xfs_file_fsync, -#ifdef HAVE_FOP_OPEN_EXEC - .open_exec = xfs_file_open_exec, -#endif }; const struct file_operations xfs_dir_file_operations = { -- cgit v1.2.3 From a4190f90b4e22bde8b01b0086e00dd95439e2edd Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Mon, 12 Jul 2010 06:40:58 +0000 Subject: xfs: move inode shrinker unregister even earlier I missed Dave Chinner's second revision of this change, and pushed his first version out to the repository instead. commit a476c59ebb279d738718edc0e3fb76aab3687114 Author: Dave Chinner This commit compensates for that by moving a block of code up a bit further, with a result that matches the the effect of Dave's second version. Dave's first version was: Reviewed-by: Eric Sandeen Dave's second version was: Reviewed-by: Christoph Hellwig Signed-off-by: Alex Elder Reviewed-by: Eric Sandeen --- fs/xfs/linux-2.6/xfs_super.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_super.c b/fs/xfs/linux-2.6/xfs_super.c index c734bc6cf32e..8c4f4476e5c2 100644 --- a/fs/xfs/linux-2.6/xfs_super.c +++ b/fs/xfs/linux-2.6/xfs_super.c @@ -1145,6 +1145,11 @@ xfs_fs_put_super( { struct xfs_mount *mp = XFS_M(sb); + /* + * Unregister the memory shrinker before we tear down the mount + * structure so we don't have memory reclaim racing with us here. + */ + xfs_inode_shrinker_unregister(mp); xfs_syncd_stop(mp); /* @@ -1156,11 +1161,6 @@ xfs_fs_put_super( XFS_bflush(mp->m_ddev_targp); - /* - * Unregister the memory shrinker before we tear down the mount - * structure so we don't have memory reclaim racing with us here. - */ - xfs_inode_shrinker_unregister(mp); xfs_unmountfs(mp); xfs_freesb(mp); xfs_icsb_destroy_counters(mp); -- cgit v1.2.3 From ec53d1dbb3ca960e7b552397613358ba1dbd12bd Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Tue, 20 Jul 2010 17:52:59 +1000 Subject: xfs: don't block on buffer read errors xfs_buf_read() fails to detect dispatch errors before attempting to wait on sychronous IO. If there was an error, it will get stuck forever, waiting for an I/O that was never started. Make sure the error is detected correctly. Further, such a failure can leave locked pages in the page cache which will cause a later operation to hang on the page. Ensure that we correctly process pages in the buffers when we get a dispatch error. Signed-off-by: Dave Chinner Reviewed-by: Christoph Hellwig Signed-off-by: Dave Chinner --- fs/xfs/linux-2.6/xfs_buf.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_buf.c b/fs/xfs/linux-2.6/xfs_buf.c index efce8abb375c..f4d4e708a8d6 100644 --- a/fs/xfs/linux-2.6/xfs_buf.c +++ b/fs/xfs/linux-2.6/xfs_buf.c @@ -578,9 +578,9 @@ _xfs_buf_read( XBF_READ_AHEAD | _XBF_RUN_QUEUES); status = xfs_buf_iorequest(bp); - if (!status && !(flags & XBF_ASYNC)) - status = xfs_buf_iowait(bp); - return status; + if (status || XFS_BUF_ISERROR(bp) || (flags & XBF_ASYNC)) + return status; + return xfs_buf_iowait(bp); } xfs_buf_t * @@ -1280,8 +1280,19 @@ submit_io: if (size) goto next_chunk; } else { - bio_put(bio); + /* + * if we get here, no pages were added to the bio. However, + * we can't just error out here - if the pages are locked then + * we have to unlock them otherwise we can hang on a later + * access to the page. + */ xfs_buf_ioerror(bp, EIO); + if (bp->b_flags & _XBF_PAGE_LOCKED) { + int i; + for (i = 0; i < bp->b_page_count; i++) + unlock_page(bp->b_pages[i]); + } + bio_put(bio); } } -- cgit v1.2.3 From 2f11feabb19748c0ffa2eb82d438e8a91b9f6ea0 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Tue, 20 Jul 2010 17:53:25 +1000 Subject: xfs: simplify and remove xfs_ireclaim xfs_ireclaim has to get and put te pag structure because it is only called with the inode to reclaim. The one caller of this function already has a reference on the pag and a pointer to is, so move the radix tree delete to the caller and remove xfs_ireclaim completely. This avoids a xfs_perag_get/put on every inode being reclaimed. The overhead was noticed in a bug report at: https://bugzilla.kernel.org/show_bug.cgi?id=16348 Signed-off-by: Dave Chinner Reviewed-by: Christoph Hellwig Reviewed-by: Alex Elder Signed-off-by: Dave Chinner --- fs/xfs/linux-2.6/xfs_sync.c | 31 +++++++++++++++++++++++++- fs/xfs/xfs_iget.c | 53 +-------------------------------------------- fs/xfs/xfs_inode.h | 2 +- 3 files changed, 32 insertions(+), 54 deletions(-) (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_sync.c b/fs/xfs/linux-2.6/xfs_sync.c index 66cefb274385..dfcbd98d1599 100644 --- a/fs/xfs/linux-2.6/xfs_sync.c +++ b/fs/xfs/linux-2.6/xfs_sync.c @@ -855,7 +855,36 @@ out: reclaim: xfs_ifunlock(ip); xfs_iunlock(ip, XFS_ILOCK_EXCL); - xfs_ireclaim(ip); + + XFS_STATS_INC(xs_ig_reclaims); + /* + * Remove the inode from the per-AG radix tree. + * + * Because radix_tree_delete won't complain even if the item was never + * added to the tree assert that it's been there before to catch + * problems with the inode life time early on. + */ + write_lock(&pag->pag_ici_lock); + if (!radix_tree_delete(&pag->pag_ici_root, + XFS_INO_TO_AGINO(ip->i_mount, ip->i_ino))) + ASSERT(0); + write_unlock(&pag->pag_ici_lock); + + /* + * Here we do an (almost) spurious inode lock in order to coordinate + * with inode cache radix tree lookups. This is because the lookup + * can reference the inodes in the cache without taking references. + * + * We make that OK here by ensuring that we wait until the inode is + * unlocked after the lookup before we go ahead and free it. We get + * both the ilock and the iolock because the code may need to drop the + * ilock one but will still hold the iolock. + */ + xfs_ilock(ip, XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL); + xfs_qm_dqdetach(ip); + xfs_iunlock(ip, XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL); + + xfs_inode_free(ip); return error; } diff --git a/fs/xfs/xfs_iget.c b/fs/xfs/xfs_iget.c index 9e86f2116aa8..eba5ae61d362 100644 --- a/fs/xfs/xfs_iget.c +++ b/fs/xfs/xfs_iget.c @@ -91,7 +91,7 @@ xfs_inode_alloc( return ip; } -STATIC void +void xfs_inode_free( struct xfs_inode *ip) { @@ -417,57 +417,6 @@ out_error_or_again: return error; } -/* - * This is called free all the memory associated with an inode. - * It must free the inode itself and any buffers allocated for - * if_extents/if_data and if_broot. It must also free the lock - * associated with the inode. - * - * Note: because we don't initialise everything on reallocation out - * of the zone, we must ensure we nullify everything correctly before - * freeing the structure. - */ -void -xfs_ireclaim( - struct xfs_inode *ip) -{ - struct xfs_mount *mp = ip->i_mount; - struct xfs_perag *pag; - xfs_agino_t agino = XFS_INO_TO_AGINO(mp, ip->i_ino); - - XFS_STATS_INC(xs_ig_reclaims); - - /* - * Remove the inode from the per-AG radix tree. - * - * Because radix_tree_delete won't complain even if the item was never - * added to the tree assert that it's been there before to catch - * problems with the inode life time early on. - */ - pag = xfs_perag_get(mp, XFS_INO_TO_AGNO(mp, ip->i_ino)); - write_lock(&pag->pag_ici_lock); - if (!radix_tree_delete(&pag->pag_ici_root, agino)) - ASSERT(0); - write_unlock(&pag->pag_ici_lock); - xfs_perag_put(pag); - - /* - * Here we do an (almost) spurious inode lock in order to coordinate - * with inode cache radix tree lookups. This is because the lookup - * can reference the inodes in the cache without taking references. - * - * We make that OK here by ensuring that we wait until the inode is - * unlocked after the lookup before we go ahead and free it. We get - * both the ilock and the iolock because the code may need to drop the - * ilock one but will still hold the iolock. - */ - xfs_ilock(ip, XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL); - xfs_qm_dqdetach(ip); - xfs_iunlock(ip, XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL); - - xfs_inode_free(ip); -} - /* * This is a wrapper routine around the xfs_ilock() routine * used to centralize some grungy code. It is used in places diff --git a/fs/xfs/xfs_inode.h b/fs/xfs/xfs_inode.h index eb41559ea8cd..0898c5417d12 100644 --- a/fs/xfs/xfs_inode.h +++ b/fs/xfs/xfs_inode.h @@ -450,7 +450,7 @@ void xfs_ilock_demote(xfs_inode_t *, uint); int xfs_isilocked(xfs_inode_t *, uint); uint xfs_ilock_map_shared(xfs_inode_t *); void xfs_iunlock_map_shared(xfs_inode_t *, uint); -void xfs_ireclaim(xfs_inode_t *); +void xfs_inode_free(struct xfs_inode *ip); /* * xfs_inode.c prototypes. -- cgit v1.2.3 From 438697064aaa2f64e0fcc6586582a3e7ec36005b Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Tue, 20 Jul 2010 17:53:44 +1000 Subject: xfs: fix xfs_trans_add_item() lockdep warnings xfs_trans_add_item() is called with ip->i_ilock held, which means it is unsafe for memory reclaim to recurse back into the filesystem (ilock is required in writeback). Hence the allocation needs to be KM_NOFS to avoid recursion. Lockdep report indicating memory allocation being called with the ip->i_ilock held is as follows: [ 1749.866796] ================================= [ 1749.867788] [ INFO: inconsistent lock state ] [ 1749.868327] 2.6.35-rc3-dgc+ #25 [ 1749.868741] --------------------------------- [ 1749.868741] inconsistent {IN-RECLAIM_FS-W} -> {RECLAIM_FS-ON-W} usage. [ 1749.868741] dd/2835 [HC0[0]:SC0[0]:HE1:SE1] takes: [ 1749.868741] (&(&ip->i_lock)->mr_lock){++++?.}, at: [] xfs_ilock+0x10b/0x190 [ 1749.868741] {IN-RECLAIM_FS-W} state was registered at: [ 1749.868741] [] __lock_acquire+0x437/0x1450 [ 1749.868741] [] lock_acquire+0xa6/0x160 [ 1749.868741] [] down_write_nested+0x65/0xb0 [ 1749.868741] [] xfs_ilock+0x10b/0x190 [ 1749.868741] [] xfs_reclaim_inode+0x99/0x310 [ 1749.868741] [] xfs_inode_ag_walk+0x8b/0x150 [ 1749.868741] [] xfs_inode_ag_iterator+0x8b/0xf0 [ 1749.868741] [] xfs_reclaim_inode_shrink+0x88/0x90 [ 1749.868741] [] shrink_slab+0x137/0x1a0 [ 1749.868741] [] balance_pgdat+0x421/0x6a0 [ 1749.868741] [] kswapd+0x11d/0x320 [ 1749.868741] [] kthread+0x96/0xa0 [ 1749.868741] [] kernel_thread_helper+0x4/0x10 [ 1749.868741] irq event stamp: 4234335 [ 1749.868741] hardirqs last enabled at (4234335): [] kmem_cache_free+0x115/0x220 [ 1749.868741] hardirqs last disabled at (4234334): [] kmem_cache_free+0x3d/0x220 [ 1749.868741] softirqs last enabled at (4233112): [] __do_softirq+0x142/0x260 [ 1749.868741] softirqs last disabled at (4233095): [] call_softirq+0x1c/0x50 [ 1749.868741] [ 1749.868741] other info that might help us debug this: [ 1749.868741] 2 locks held by dd/2835: [ 1749.868741] #0: (&(&ip->i_iolock)->mr_lock#2){+.+.+.}, at: [] xfs_ilock_nowait+0xed/0x200 [ 1749.868741] #1: (&(&ip->i_lock)->mr_lock){++++?.}, at: [] xfs_ilock+0x10b/0x190 [ 1749.868741] [ 1749.868741] stack backtrace: [ 1749.868741] Pid: 2835, comm: dd Not tainted 2.6.35-rc3-dgc+ #25 [ 1749.868741] Call Trace: [ 1749.868741] [] print_usage_bug+0x18a/0x190 [ 1749.868741] [] ? save_stack_trace+0x2f/0x50 [ 1749.868741] [] ? check_usage_backwards+0x0/0xf0 [ 1749.868741] [] mark_lock+0x331/0x400 [ 1749.868741] [] mark_held_locks+0x67/0x90 [ 1749.868741] [] lockdep_trace_alloc+0xa1/0xe0 [ 1749.868741] [] kmem_cache_alloc+0x39/0x1e0 [ 1749.868741] [] kmem_zone_alloc+0x94/0xe0 [ 1749.868741] [] kmem_zone_zalloc+0x1e/0x50 [ 1749.868741] [] xfs_trans_add_item+0x72/0xb0 [ 1749.868741] [] xfs_trans_ijoin+0xa1/0xd0 [ 1749.868741] [] xfs_itruncate_finish+0x312/0x5d0 [ 1749.868741] [] xfs_free_eofblocks+0x227/0x280 [ 1749.868741] [] xfs_release+0x138/0x190 [ 1749.868741] [] xfs_file_release+0x15/0x20 [ 1749.868741] [] fput+0x13f/0x260 [ 1749.868741] [] filp_close+0x52/0x80 [ 1749.868741] [] sys_close+0xb9/0x120 [ 1749.868741] [] system_call_fastpath+0x16/0x1b Signed-off-by: Dave Chinner Reviewed-by: Christoph Hellwig Reviewed-by: Alex Elder Signed-off-by: Dave Chinner --- fs/xfs/xfs_trans.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/xfs/xfs_trans.c b/fs/xfs/xfs_trans.c index f2065ccb6c2d..fdca7416c754 100644 --- a/fs/xfs/xfs_trans.c +++ b/fs/xfs/xfs_trans.c @@ -1134,7 +1134,7 @@ xfs_trans_add_item( ASSERT(lip->li_mountp = tp->t_mountp); ASSERT(lip->li_ailp = tp->t_mountp->m_ail); - lidp = kmem_zone_zalloc(xfs_log_item_desc_zone, KM_SLEEP); + lidp = kmem_zone_zalloc(xfs_log_item_desc_zone, KM_SLEEP | KM_NOFS); lidp->lid_item = lip; lidp->lid_flags = 0; -- cgit v1.2.3 From 4a7edddcb5b14ddb5962e6906b6fd6b500d7a361 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Tue, 20 Jul 2010 17:53:59 +1000 Subject: xfs: fix memory reclaim recursion deadlock on locked inode buffer Calling into memory reclaim with a locked inode buffer can deadlock if memory reclaim tries to lock the inode buffer during inode teardown. Convert the relevant memory allocations to use KM_NOFS to avoid this deadlock condition. Reported-by: Peter Watkins Signed-off-by: Dave Chinner Reviewed-by: Christoph Hellwig Reviewed-by: Alex Elder Signed-off-by: Dave Chinner --- fs/xfs/xfs_inode.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) (limited to 'fs') diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index 5715a9d8bcd6..4a08c91dc976 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -422,7 +422,7 @@ xfs_iformat( if (!XFS_DFORK_Q(dip)) return 0; ASSERT(ip->i_afp == NULL); - ip->i_afp = kmem_zone_zalloc(xfs_ifork_zone, KM_SLEEP); + ip->i_afp = kmem_zone_zalloc(xfs_ifork_zone, KM_SLEEP | KM_NOFS); ip->i_afp->if_ext_max = XFS_IFORK_ASIZE(ip) / (uint)sizeof(xfs_bmbt_rec_t); switch (dip->di_aformat) { @@ -505,7 +505,7 @@ xfs_iformat_local( ifp->if_u1.if_data = ifp->if_u2.if_inline_data; else { real_size = roundup(size, 4); - ifp->if_u1.if_data = kmem_alloc(real_size, KM_SLEEP); + ifp->if_u1.if_data = kmem_alloc(real_size, KM_SLEEP | KM_NOFS); } ifp->if_bytes = size; ifp->if_real_bytes = real_size; @@ -632,7 +632,7 @@ xfs_iformat_btree( } ifp->if_broot_bytes = size; - ifp->if_broot = kmem_alloc(size, KM_SLEEP); + ifp->if_broot = kmem_alloc(size, KM_SLEEP | KM_NOFS); ASSERT(ifp->if_broot != NULL); /* * Copy and convert from the on-disk structure @@ -2191,7 +2191,7 @@ xfs_iroot_realloc( */ if (ifp->if_broot_bytes == 0) { new_size = (size_t)XFS_BMAP_BROOT_SPACE_CALC(rec_diff); - ifp->if_broot = kmem_alloc(new_size, KM_SLEEP); + ifp->if_broot = kmem_alloc(new_size, KM_SLEEP | KM_NOFS); ifp->if_broot_bytes = (int)new_size; return; } @@ -2207,7 +2207,7 @@ xfs_iroot_realloc( new_size = (size_t)XFS_BMAP_BROOT_SPACE_CALC(new_max); ifp->if_broot = kmem_realloc(ifp->if_broot, new_size, (size_t)XFS_BMAP_BROOT_SPACE_CALC(cur_max), /* old size */ - KM_SLEEP); + KM_SLEEP | KM_NOFS); op = (char *)XFS_BMAP_BROOT_PTR_ADDR(mp, ifp->if_broot, 1, ifp->if_broot_bytes); np = (char *)XFS_BMAP_BROOT_PTR_ADDR(mp, ifp->if_broot, 1, @@ -2233,7 +2233,7 @@ xfs_iroot_realloc( else new_size = 0; if (new_size > 0) { - new_broot = kmem_alloc(new_size, KM_SLEEP); + new_broot = kmem_alloc(new_size, KM_SLEEP | KM_NOFS); /* * First copy over the btree block header. */ @@ -2337,7 +2337,8 @@ xfs_idata_realloc( real_size = roundup(new_size, 4); if (ifp->if_u1.if_data == NULL) { ASSERT(ifp->if_real_bytes == 0); - ifp->if_u1.if_data = kmem_alloc(real_size, KM_SLEEP); + ifp->if_u1.if_data = kmem_alloc(real_size, + KM_SLEEP | KM_NOFS); } else if (ifp->if_u1.if_data != ifp->if_u2.if_inline_data) { /* * Only do the realloc if the underlying size @@ -2348,11 +2349,12 @@ xfs_idata_realloc( kmem_realloc(ifp->if_u1.if_data, real_size, ifp->if_real_bytes, - KM_SLEEP); + KM_SLEEP | KM_NOFS); } } else { ASSERT(ifp->if_real_bytes == 0); - ifp->if_u1.if_data = kmem_alloc(real_size, KM_SLEEP); + ifp->if_u1.if_data = kmem_alloc(real_size, + KM_SLEEP | KM_NOFS); memcpy(ifp->if_u1.if_data, ifp->if_u2.if_inline_data, ifp->if_bytes); } -- cgit v1.2.3 From aea1b9532143218f8599ecedbbd6bfbf812385e1 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Tue, 20 Jul 2010 17:54:12 +1000 Subject: xfs: use GFP_NOFS for page cache allocation Avoid a lockdep warning by preventing page cache allocation from recursing back into the filesystem during memory reclaim. Signed-off-by: Dave Chinner Reviewed-by: Christoph Hellwig Reviewed-by: Alex Elder Signed-off-by: Dave Chinner --- fs/xfs/linux-2.6/xfs_aops.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_aops.c b/fs/xfs/linux-2.6/xfs_aops.c index 225ec0fa65b6..8abbf0532ea1 100644 --- a/fs/xfs/linux-2.6/xfs_aops.c +++ b/fs/xfs/linux-2.6/xfs_aops.c @@ -1493,8 +1493,8 @@ xfs_vm_write_begin( void **fsdata) { *pagep = NULL; - return block_write_begin(file, mapping, pos, len, flags, pagep, fsdata, - xfs_get_blocks); + return block_write_begin(file, mapping, pos, len, flags | AOP_FLAG_NOFS, + pagep, fsdata, xfs_get_blocks); } STATIC sector_t -- cgit v1.2.3 From 3f34885cd7c6a3f4deea48e3bbc704d91d5704f4 Mon Sep 17 00:00:00 2001 From: Kulikov Vasiliy Date: Tue, 20 Jul 2010 17:54:28 +1000 Subject: xfs: fix unsigned underflow in xfs_free_eofblocks map_len is unsigned. Checking map_len <= 0 is buggy when it should be below zero. So, check exact expression instead of map_len. Signed-off-by: Kulikov Vasiliy Reviewed-by: Christoph Hellwig Signed-off-by: Dave Chinner --- fs/xfs/xfs_vnodeops.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/xfs/xfs_vnodeops.c b/fs/xfs/xfs_vnodeops.c index 9865e1136017..3ac137dd531b 100644 --- a/fs/xfs/xfs_vnodeops.c +++ b/fs/xfs/xfs_vnodeops.c @@ -589,9 +589,9 @@ xfs_free_eofblocks( */ end_fsb = XFS_B_TO_FSB(mp, ((xfs_ufsize_t)ip->i_size)); last_fsb = XFS_B_TO_FSB(mp, (xfs_ufsize_t)XFS_MAXIOFFSET(mp)); - map_len = last_fsb - end_fsb; - if (map_len <= 0) + if (last_fsb <= end_fsb) return 0; + map_len = last_fsb - end_fsb; nimaps = 1; xfs_ilock(ip, XFS_ILOCK_SHARED); -- cgit v1.2.3 From 0f1a932f5d4d6ee71afb141914e2d5f11f27eee1 Mon Sep 17 00:00:00 2001 From: Tony Luck Date: Tue, 20 Jul 2010 17:54:41 +1000 Subject: xfs: Fix build when CONFIG_XFS_POSIX_ACL=n When CONFIG_XFS_POSIX_ACL is not set "xfs_check_acl" is #defined to NULL - which breaks the code attempting to add a tracepoint on this function. Only define the tracepoint when the function exists. Signed-off-by: Tony Luck Reviewed-by: Christoph Hellwig Signed-off-by: Dave Chinner --- fs/xfs/linux-2.6/xfs_trace.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_trace.h b/fs/xfs/linux-2.6/xfs_trace.h index 24e5580bf3e7..c657cdca2cd2 100644 --- a/fs/xfs/linux-2.6/xfs_trace.h +++ b/fs/xfs/linux-2.6/xfs_trace.h @@ -571,7 +571,9 @@ DEFINE_INODE_EVENT(xfs_readlink); DEFINE_INODE_EVENT(xfs_alloc_file_space); DEFINE_INODE_EVENT(xfs_free_file_space); DEFINE_INODE_EVENT(xfs_readdir); +#ifdef CONFIG_XFS_POSIX_ACL DEFINE_INODE_EVENT(xfs_check_acl); +#endif DEFINE_INODE_EVENT(xfs_vm_bmap); DEFINE_INODE_EVENT(xfs_file_ioctl); DEFINE_INODE_EVENT(xfs_file_compat_ioctl); -- cgit v1.2.3 From 73523a2ecf03f0bfe7c36c244aff8a2ef2208a4a Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 20 Jul 2010 17:54:45 +1000 Subject: xfs: fix gcc 4.6 set but not read and unused statement warnings [hch: dropped a few hunks that need structural changes instead] Signed-off-by: Andi Kleen Reviewed-by: Dave Chinner Signed-off-by: Dave Chinner --- fs/xfs/xfs_alloc.c | 10 +++------- fs/xfs/xfs_da_btree.c | 6 ++---- fs/xfs/xfs_dir2_block.c | 6 +++--- fs/xfs/xfs_iget.c | 3 --- fs/xfs/xfs_inode.c | 4 ---- fs/xfs/xfs_inode_item.c | 18 +++++++----------- fs/xfs/xfs_log.c | 2 -- 7 files changed, 15 insertions(+), 34 deletions(-) (limited to 'fs') diff --git a/fs/xfs/xfs_alloc.c b/fs/xfs/xfs_alloc.c index 6fe5c02ba19a..af168faccc7a 100644 --- a/fs/xfs/xfs_alloc.c +++ b/fs/xfs/xfs_alloc.c @@ -683,8 +683,6 @@ xfs_alloc_ag_vextent_near( xfs_agblock_t ltbno; /* start bno of left side entry */ xfs_agblock_t ltbnoa; /* aligned ... */ xfs_extlen_t ltdiff; /* difference to left side entry */ - /*REFERENCED*/ - xfs_agblock_t ltend; /* end bno of left side entry */ xfs_extlen_t ltlen; /* length of left side entry */ xfs_extlen_t ltlena; /* aligned ... */ xfs_agblock_t ltnew; /* useful start bno of left side */ @@ -809,8 +807,7 @@ xfs_alloc_ag_vextent_near( if ((error = xfs_alloc_get_rec(cnt_cur, <bno, <len, &i))) goto error0; XFS_WANT_CORRUPTED_GOTO(i == 1, error0); - ltend = ltbno + ltlen; - ASSERT(ltend <= be32_to_cpu(XFS_BUF_TO_AGF(args->agbp)->agf_length)); + ASSERT(ltbno + ltlen <= be32_to_cpu(XFS_BUF_TO_AGF(args->agbp)->agf_length)); args->len = blen; if (!xfs_alloc_fix_minleft(args)) { xfs_btree_del_cursor(cnt_cur, XFS_BTREE_NOERROR); @@ -823,7 +820,7 @@ xfs_alloc_ag_vextent_near( */ args->agbno = bnew; ASSERT(bnew >= ltbno); - ASSERT(bnew + blen <= ltend); + ASSERT(bnew + blen <= ltbno + ltlen); /* * Set up a cursor for the by-bno tree. */ @@ -1152,7 +1149,6 @@ xfs_alloc_ag_vextent_near( /* * Fix up the length and compute the useful address. */ - ltend = ltbno + ltlen; args->len = XFS_EXTLEN_MIN(ltlena, args->maxlen); xfs_alloc_fix_len(args); if (!xfs_alloc_fix_minleft(args)) { @@ -1165,7 +1161,7 @@ xfs_alloc_ag_vextent_near( (void)xfs_alloc_compute_diff(args->agbno, rlen, args->alignment, ltbno, ltlen, <new); ASSERT(ltnew >= ltbno); - ASSERT(ltnew + rlen <= ltend); + ASSERT(ltnew + rlen <= ltbno + ltlen); ASSERT(ltnew + rlen <= be32_to_cpu(XFS_BUF_TO_AGF(args->agbp)->agf_length)); args->agbno = ltnew; if ((error = xfs_alloc_fixup_trees(cnt_cur, bno_cur_lt, ltbno, ltlen, diff --git a/fs/xfs/xfs_da_btree.c b/fs/xfs/xfs_da_btree.c index 2adfb761ab13..30fa0e206fba 100644 --- a/fs/xfs/xfs_da_btree.c +++ b/fs/xfs/xfs_da_btree.c @@ -576,16 +576,14 @@ xfs_da_node_add(xfs_da_state_t *state, xfs_da_state_blk_t *oldblk, xfs_da_intnode_t *node; xfs_da_node_entry_t *btree; int tmp; - xfs_mount_t *mp; node = oldblk->bp->data; - mp = state->mp; ASSERT(be16_to_cpu(node->hdr.info.magic) == XFS_DA_NODE_MAGIC); ASSERT((oldblk->index >= 0) && (oldblk->index <= be16_to_cpu(node->hdr.count))); ASSERT(newblk->blkno != 0); if (state->args->whichfork == XFS_DATA_FORK) - ASSERT(newblk->blkno >= mp->m_dirleafblk && - newblk->blkno < mp->m_dirfreeblk); + ASSERT(newblk->blkno >= state->mp->m_dirleafblk && + newblk->blkno < state->mp->m_dirfreeblk); /* * We may need to make some room before we insert the new node. diff --git a/fs/xfs/xfs_dir2_block.c b/fs/xfs/xfs_dir2_block.c index 68f4926c7d16..580d99cef9e7 100644 --- a/fs/xfs/xfs_dir2_block.c +++ b/fs/xfs/xfs_dir2_block.c @@ -1071,10 +1071,10 @@ xfs_dir2_sf_to_block( */ buf_len = dp->i_df.if_bytes; - buf = kmem_alloc(dp->i_df.if_bytes, KM_SLEEP); + buf = kmem_alloc(buf_len, KM_SLEEP); - memcpy(buf, sfp, dp->i_df.if_bytes); - xfs_idata_realloc(dp, -dp->i_df.if_bytes, XFS_DATA_FORK); + memcpy(buf, sfp, buf_len); + xfs_idata_realloc(dp, -buf_len, XFS_DATA_FORK); dp->i_d.di_size = 0; xfs_trans_log_inode(tp, dp, XFS_ILOG_CORE); /* diff --git a/fs/xfs/xfs_iget.c b/fs/xfs/xfs_iget.c index eba5ae61d362..b1ecc6f97ade 100644 --- a/fs/xfs/xfs_iget.c +++ b/fs/xfs/xfs_iget.c @@ -261,7 +261,6 @@ xfs_iget_cache_miss( { struct xfs_inode *ip; int error; - unsigned long first_index, mask; xfs_agino_t agino = XFS_INO_TO_AGINO(mp, ino); ip = xfs_inode_alloc(mp, ino); @@ -298,8 +297,6 @@ xfs_iget_cache_miss( BUG(); } - mask = ~(((XFS_INODE_CLUSTER_SIZE(mp) >> mp->m_sb.sb_inodelog)) - 1); - first_index = agino & mask; write_lock(&pag->pag_ici_lock); /* insert the new inode */ diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index 4a08c91dc976..eef211dfca03 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -918,7 +918,6 @@ xfs_iread_extents( int error; xfs_ifork_t *ifp; xfs_extnum_t nextents; - size_t size; if (unlikely(XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_BTREE)) { XFS_ERROR_REPORT("xfs_iread_extents", XFS_ERRLEVEL_LOW, @@ -926,7 +925,6 @@ xfs_iread_extents( return XFS_ERROR(EFSCORRUPTED); } nextents = XFS_IFORK_NEXTENTS(ip, whichfork); - size = nextents * sizeof(xfs_bmbt_rec_t); ifp = XFS_IFORK_PTR(ip, whichfork); /* @@ -3503,13 +3501,11 @@ xfs_iext_remove_indirect( xfs_extnum_t ext_diff; /* extents to remove in current list */ xfs_extnum_t nex1; /* number of extents before idx */ xfs_extnum_t nex2; /* extents after idx + count */ - int nlists; /* entries in indirection array */ int page_idx = idx; /* index in target extent list */ ASSERT(ifp->if_flags & XFS_IFEXTIREC); erp = xfs_iext_idx_to_irec(ifp, &page_idx, &erp_idx, 0); ASSERT(erp != NULL); - nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ; nex1 = page_idx; ext_cnt = count; while (ext_cnt) { diff --git a/fs/xfs/xfs_inode_item.c b/fs/xfs/xfs_inode_item.c index 065c1ad9b708..2d6fcfdc7834 100644 --- a/fs/xfs/xfs_inode_item.c +++ b/fs/xfs/xfs_inode_item.c @@ -214,7 +214,6 @@ xfs_inode_item_format( uint nvecs; size_t data_bytes; xfs_bmbt_rec_t *ext_buffer; - int nrecs; xfs_mount_t *mp; vecp->i_addr = &iip->ili_format; @@ -314,9 +313,8 @@ xfs_inode_item_format( ASSERT(ip->i_df.if_u1.if_extents != NULL); ASSERT(ip->i_d.di_nextents > 0); ASSERT(iip->ili_extents_buf == NULL); - nrecs = ip->i_df.if_bytes / - (uint)sizeof(xfs_bmbt_rec_t); - ASSERT(nrecs > 0); + ASSERT((ip->i_df.if_bytes / + (uint)sizeof(xfs_bmbt_rec_t)) > 0); #ifdef XFS_NATIVE_HOST if (nrecs == ip->i_d.di_nextents) { /* @@ -439,15 +437,15 @@ xfs_inode_item_format( ASSERT(!(iip->ili_format.ilf_fields & (XFS_ILOG_ADATA | XFS_ILOG_ABROOT))); if (iip->ili_format.ilf_fields & XFS_ILOG_AEXT) { - ASSERT(ip->i_afp->if_bytes > 0); - ASSERT(ip->i_afp->if_u1.if_extents != NULL); - ASSERT(ip->i_d.di_anextents > 0); #ifdef DEBUG - nrecs = ip->i_afp->if_bytes / + int nrecs = ip->i_afp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t); -#endif ASSERT(nrecs > 0); ASSERT(nrecs == ip->i_d.di_anextents); + ASSERT(ip->i_afp->if_bytes > 0); + ASSERT(ip->i_afp->if_u1.if_extents != NULL); + ASSERT(ip->i_d.di_anextents > 0); +#endif #ifdef XFS_NATIVE_HOST /* * There are not delayed allocation extents @@ -889,10 +887,8 @@ xfs_iflush_abort( xfs_inode_t *ip) { xfs_inode_log_item_t *iip = ip->i_itemp; - xfs_mount_t *mp; iip = ip->i_itemp; - mp = ip->i_mount; if (iip) { struct xfs_ail *ailp = iip->ili_item.li_ailp; if (iip->ili_item.li_flags & XFS_LI_IN_AIL) { diff --git a/fs/xfs/xfs_log.c b/fs/xfs/xfs_log.c index f309e1404fd6..925d572bf0f4 100644 --- a/fs/xfs/xfs_log.c +++ b/fs/xfs/xfs_log.c @@ -1042,7 +1042,6 @@ xlog_alloc_log(xfs_mount_t *mp, xlog_in_core_t *iclog, *prev_iclog=NULL; xfs_buf_t *bp; int i; - int iclogsize; int error = ENOMEM; uint log2_size = 0; @@ -1122,7 +1121,6 @@ xlog_alloc_log(xfs_mount_t *mp, * with different amounts of memory. See the definition of * xlog_in_core_t in xfs_log_priv.h for details. */ - iclogsize = log->l_iclog_size; ASSERT(log->l_iclog_size >= 4096); for (i=0; i < log->l_iclog_bufs; i++) { *iclogp = kmem_zalloc(sizeof(xlog_in_core_t), KM_MAYFAIL); -- cgit v1.2.3 From 0664ce8d0fde731d76fa7e86b3afb54f3a6830ff Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 20 Jul 2010 17:31:01 +1000 Subject: xfs: clean up filestreams helpers Move xfs_filestream_peek_ag, xxfs_filestream_get_ag and xfs_filestream_put_ag from xfs_filestream.h to xfs_filestream.c where it's only callers are, and remove the inline marker while we're at it to let the compiler decide on the inlining. Also don't return a value from xfs_filestream_put_ag because we don't need it. Signed-off-by: Christoph Hellwig Reviewed-by: Dave Chinner Signed-off-by: Dave Chinner --- fs/xfs/xfs_filestream.c | 80 +++++++++++++++++++++++++++++++++++++++++++++-- fs/xfs/xfs_filestream.h | 82 ------------------------------------------------- 2 files changed, 77 insertions(+), 85 deletions(-) (limited to 'fs') diff --git a/fs/xfs/xfs_filestream.c b/fs/xfs/xfs_filestream.c index d34b9e8d2d37..9b715dce5699 100644 --- a/fs/xfs/xfs_filestream.c +++ b/fs/xfs/xfs_filestream.c @@ -123,6 +123,82 @@ typedef struct fstrm_item xfs_inode_t *pip; /* Parent directory inode pointer. */ } fstrm_item_t; +/* + * Allocation group filestream associations are tracked with per-ag atomic + * counters. These counters allow _xfs_filestream_pick_ag() to tell whether a + * particular AG already has active filestreams associated with it. The mount + * point's m_peraglock is used to protect these counters from per-ag array + * re-allocation during a growfs operation. When xfs_growfs_data_private() is + * about to reallocate the array, it calls xfs_filestream_flush() with the + * m_peraglock held in write mode. + * + * Since xfs_mru_cache_flush() guarantees that all the free functions for all + * the cache elements have finished executing before it returns, it's safe for + * the free functions to use the atomic counters without m_peraglock protection. + * This allows the implementation of xfs_fstrm_free_func() to be agnostic about + * whether it was called with the m_peraglock held in read mode, write mode or + * not held at all. The race condition this addresses is the following: + * + * - The work queue scheduler fires and pulls a filestream directory cache + * element off the LRU end of the cache for deletion, then gets pre-empted. + * - A growfs operation grabs the m_peraglock in write mode, flushes all the + * remaining items from the cache and reallocates the mount point's per-ag + * array, resetting all the counters to zero. + * - The work queue thread resumes and calls the free function for the element + * it started cleaning up earlier. In the process it decrements the + * filestreams counter for an AG that now has no references. + * + * With a shrinkfs feature, the above scenario could panic the system. + * + * All other uses of the following macros should be protected by either the + * m_peraglock held in read mode, or the cache's internal locking exposed by the + * interval between a call to xfs_mru_cache_lookup() and a call to + * xfs_mru_cache_done(). In addition, the m_peraglock must be held in read mode + * when new elements are added to the cache. + * + * Combined, these locking rules ensure that no associations will ever exist in + * the cache that reference per-ag array elements that have since been + * reallocated. + */ +static int +xfs_filestream_peek_ag( + xfs_mount_t *mp, + xfs_agnumber_t agno) +{ + struct xfs_perag *pag; + int ret; + + pag = xfs_perag_get(mp, agno); + ret = atomic_read(&pag->pagf_fstrms); + xfs_perag_put(pag); + return ret; +} + +static int +xfs_filestream_get_ag( + xfs_mount_t *mp, + xfs_agnumber_t agno) +{ + struct xfs_perag *pag; + int ret; + + pag = xfs_perag_get(mp, agno); + ret = atomic_inc_return(&pag->pagf_fstrms); + xfs_perag_put(pag); + return ret; +} + +static void +xfs_filestream_put_ag( + xfs_mount_t *mp, + xfs_agnumber_t agno) +{ + struct xfs_perag *pag; + + pag = xfs_perag_get(mp, agno); + atomic_dec(&pag->pagf_fstrms); + xfs_perag_put(pag); +} /* * Scan the AGs starting at startag looking for an AG that isn't in use and has @@ -351,16 +427,14 @@ xfs_fstrm_free_func( { fstrm_item_t *item = (fstrm_item_t *)data; xfs_inode_t *ip = item->ip; - int ref; ASSERT(ip->i_ino == ino); xfs_iflags_clear(ip, XFS_IFILESTREAM); /* Drop the reference taken on the AG when the item was added. */ - ref = xfs_filestream_put_ag(ip->i_mount, item->ag); + xfs_filestream_put_ag(ip->i_mount, item->ag); - ASSERT(ref >= 0); TRACE_FREE(ip->i_mount, ip, item->pip, item->ag, xfs_filestream_peek_ag(ip->i_mount, item->ag)); diff --git a/fs/xfs/xfs_filestream.h b/fs/xfs/xfs_filestream.h index 260f757bbc5d..09dd9af45434 100644 --- a/fs/xfs/xfs_filestream.h +++ b/fs/xfs/xfs_filestream.h @@ -42,88 +42,6 @@ extern ktrace_t *xfs_filestreams_trace_buf; #endif -/* - * Allocation group filestream associations are tracked with per-ag atomic - * counters. These counters allow _xfs_filestream_pick_ag() to tell whether a - * particular AG already has active filestreams associated with it. The mount - * point's m_peraglock is used to protect these counters from per-ag array - * re-allocation during a growfs operation. When xfs_growfs_data_private() is - * about to reallocate the array, it calls xfs_filestream_flush() with the - * m_peraglock held in write mode. - * - * Since xfs_mru_cache_flush() guarantees that all the free functions for all - * the cache elements have finished executing before it returns, it's safe for - * the free functions to use the atomic counters without m_peraglock protection. - * This allows the implementation of xfs_fstrm_free_func() to be agnostic about - * whether it was called with the m_peraglock held in read mode, write mode or - * not held at all. The race condition this addresses is the following: - * - * - The work queue scheduler fires and pulls a filestream directory cache - * element off the LRU end of the cache for deletion, then gets pre-empted. - * - A growfs operation grabs the m_peraglock in write mode, flushes all the - * remaining items from the cache and reallocates the mount point's per-ag - * array, resetting all the counters to zero. - * - The work queue thread resumes and calls the free function for the element - * it started cleaning up earlier. In the process it decrements the - * filestreams counter for an AG that now has no references. - * - * With a shrinkfs feature, the above scenario could panic the system. - * - * All other uses of the following macros should be protected by either the - * m_peraglock held in read mode, or the cache's internal locking exposed by the - * interval between a call to xfs_mru_cache_lookup() and a call to - * xfs_mru_cache_done(). In addition, the m_peraglock must be held in read mode - * when new elements are added to the cache. - * - * Combined, these locking rules ensure that no associations will ever exist in - * the cache that reference per-ag array elements that have since been - * reallocated. - */ -/* - * xfs_filestream_peek_ag is only used in tracing code - */ -static inline int -xfs_filestream_peek_ag( - xfs_mount_t *mp, - xfs_agnumber_t agno) -{ - struct xfs_perag *pag; - int ret; - - pag = xfs_perag_get(mp, agno); - ret = atomic_read(&pag->pagf_fstrms); - xfs_perag_put(pag); - return ret; -} - -static inline int -xfs_filestream_get_ag( - xfs_mount_t *mp, - xfs_agnumber_t agno) -{ - struct xfs_perag *pag; - int ret; - - pag = xfs_perag_get(mp, agno); - ret = atomic_inc_return(&pag->pagf_fstrms); - xfs_perag_put(pag); - return ret; -} - -static inline int -xfs_filestream_put_ag( - xfs_mount_t *mp, - xfs_agnumber_t agno) -{ - struct xfs_perag *pag; - int ret; - - pag = xfs_perag_get(mp, agno); - ret = atomic_dec_return(&pag->pagf_fstrms); - xfs_perag_put(pag); - return ret; -} - /* allocation selection flags */ typedef enum xfs_fstrm_alloc { XFS_PICK_USERDATA = 1, -- cgit v1.2.3 From a64afb057b607c04383ab5fb53c51421ba18c434 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 20 Jul 2010 17:50:52 +1000 Subject: xfs: remove obsolete osyncisosync mount option Since Linux 2.6.33 the kernel has support for real O_SYNC, which made the osyncisosync option a no-op. Warn the users about this and remove the mount flag for it. Signed-off-by: Christoph Hellwig Reviewed-by: Dave Chinner Signed-off-by: Dave Chinner --- Documentation/filesystems/xfs.txt | 11 ----------- fs/xfs/linux-2.6/xfs_super.c | 10 ++++------ fs/xfs/xfs_mount.h | 2 -- 3 files changed, 4 insertions(+), 19 deletions(-) (limited to 'fs') diff --git a/Documentation/filesystems/xfs.txt b/Documentation/filesystems/xfs.txt index 9878f50d6ed6..7bff3e4f35df 100644 --- a/Documentation/filesystems/xfs.txt +++ b/Documentation/filesystems/xfs.txt @@ -131,17 +131,6 @@ When mounting an XFS filesystem, the following options are accepted. Don't check for double mounted file systems using the file system uuid. This is useful to mount LVM snapshot volumes. - osyncisosync - Make O_SYNC writes implement true O_SYNC. WITHOUT this option, - Linux XFS behaves as if an "osyncisdsync" option is used, - which will make writes to files opened with the O_SYNC flag set - behave as if the O_DSYNC flag had been used instead. - This can result in better performance without compromising - data safety. - However if this option is not in effect, timestamp updates from - O_SYNC writes can be lost if the system crashes. - If timestamp updates are critical, use the osyncisosync option. - uquota/usrquota/uqnoenforce/quota User disk quota accounting enabled, and limits (optionally) enforced. Refer to xfs_quota(8) for further details. diff --git a/fs/xfs/linux-2.6/xfs_super.c b/fs/xfs/linux-2.6/xfs_super.c index 8c4f4476e5c2..758df94690ed 100644 --- a/fs/xfs/linux-2.6/xfs_super.c +++ b/fs/xfs/linux-2.6/xfs_super.c @@ -90,7 +90,6 @@ mempool_t *xfs_ioend_pool; #define MNTOPT_BARRIER "barrier" /* use writer barriers for log write and * unwritten extent conversion */ #define MNTOPT_NOBARRIER "nobarrier" /* .. disable */ -#define MNTOPT_OSYNCISOSYNC "osyncisosync" /* o_sync is REALLY o_sync */ #define MNTOPT_64BITINODE "inode64" /* inodes can be allocated anywhere */ #define MNTOPT_IKEEP "ikeep" /* do not free empty inode clusters */ #define MNTOPT_NOIKEEP "noikeep" /* free empty inode clusters */ @@ -274,8 +273,6 @@ xfs_parseargs( mp->m_flags &= ~XFS_MOUNT_GRPID; } else if (!strcmp(this_char, MNTOPT_WSYNC)) { mp->m_flags |= XFS_MOUNT_WSYNC; - } else if (!strcmp(this_char, MNTOPT_OSYNCISOSYNC)) { - mp->m_flags |= XFS_MOUNT_OSYNCISOSYNC; } else if (!strcmp(this_char, MNTOPT_NORECOVERY)) { mp->m_flags |= XFS_MOUNT_NORECOVERY; } else if (!strcmp(this_char, MNTOPT_NOALIGN)) { @@ -366,9 +363,11 @@ xfs_parseargs( cmn_err(CE_WARN, "XFS: ihashsize no longer used, option is deprecated."); } else if (!strcmp(this_char, "osyncisdsync")) { - /* no-op, this is now the default */ cmn_err(CE_WARN, - "XFS: osyncisdsync is now the default, option is deprecated."); + "XFS: osyncisdsync has no effect, option is deprecated."); + } else if (!strcmp(this_char, "osyncisosync")) { + cmn_err(CE_WARN, + "XFS: osyncisosync has no effect, option is deprecated."); } else if (!strcmp(this_char, "irixsgid")) { cmn_err(CE_WARN, "XFS: irixsgid is now a sysctl(2) variable, option is deprecated."); @@ -500,7 +499,6 @@ xfs_showargs( { XFS_MOUNT_SWALLOC, "," MNTOPT_SWALLOC }, { XFS_MOUNT_NOUUID, "," MNTOPT_NOUUID }, { XFS_MOUNT_NORECOVERY, "," MNTOPT_NORECOVERY }, - { XFS_MOUNT_OSYNCISOSYNC, "," MNTOPT_OSYNCISOSYNC }, { XFS_MOUNT_ATTR2, "," MNTOPT_ATTR2 }, { XFS_MOUNT_FILESTREAMS, "," MNTOPT_FILESTREAM }, { XFS_MOUNT_GRPID, "," MNTOPT_GRPID }, diff --git a/fs/xfs/xfs_mount.h b/fs/xfs/xfs_mount.h index e70dc39394ae..622da2179a57 100644 --- a/fs/xfs/xfs_mount.h +++ b/fs/xfs/xfs_mount.h @@ -220,8 +220,6 @@ typedef struct xfs_mount { #define XFS_MOUNT_GRPID (1ULL << 9) /* group-ID assigned from directory */ #define XFS_MOUNT_NORECOVERY (1ULL << 10) /* no recovery - dirty fs */ #define XFS_MOUNT_DFLT_IOSIZE (1ULL << 12) /* set default i/o size */ -#define XFS_MOUNT_OSYNCISOSYNC (1ULL << 13) /* o_sync is REALLY o_sync */ - /* osyncisdsync is now default*/ #define XFS_MOUNT_32BITINODES (1ULL << 14) /* do not create inodes above * 32 bits in size */ #define XFS_MOUNT_SMALL_INUMS (1ULL << 15) /* users wants 32bit inodes */ -- cgit v1.2.3 From 939d723b721eef71060201738653a73443ff4510 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 20 Jul 2010 17:51:16 +1000 Subject: xfs: kill the b_strat callback in xfs_buf The b_strat callback is used by xfs_buf_iostrategy to perform additional checks before submitting a buffer. It is used in xfs_bwrite and when writing out delayed buffers. In xfs_bwrite it we can de-virtualize the call easily as b_strat is set a few lines above the call to xfs_buf_iostrategy. For the delayed buffers the rationale is a bit more complicated: - there are three callers of xfs_buf_delwri_queue, which places buffers on the delwri list: (1) xfs_bdwrite - this sets up b_strat, so it's fine (2) xfs_buf_iorequest. None of the callers can have XBF_DELWRI set: - xlog_bdstrat is only used for log buffers, which are never delwri - _xfs_buf_read explicitly clears the delwri flag - xfs_buf_iodone_work retries log buffers only - xfsbdstrat - only used for reads, superblock writes without the delwri flag, log I/O and file zeroing with explicitly allocated buffers. - xfs_buf_iostrategy - only calls xfs_buf_iorequest if b_strat is not set (3) xfs_buf_unlock - only puts the buffer on the delwri list if the DELWRI flag is already set. The DELWRI flag is only ever set in xfs_bwrite, xfs_buf_iodone_callbacks, or xfs_trans_log_buf. For xfs_buf_iodone_callbacks and xfs_trans_log_buf we require an initialized buf item, which means b_strat was set to xfs_bdstrat_cb in xfs_buf_item_init. Conclusion: we can just get rid of the callback and replace it with explicit calls to xfs_bdstrat_cb. Signed-off-by: Christoph Hellwig Reviewed-by: Dave Chinner Signed-off-by: Dave Chinner --- fs/xfs/linux-2.6/xfs_buf.c | 10 +++------- fs/xfs/linux-2.6/xfs_buf.h | 8 -------- fs/xfs/xfs_buf_item.c | 1 - fs/xfs/xfs_inode.c | 1 - 4 files changed, 3 insertions(+), 17 deletions(-) (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_buf.c b/fs/xfs/linux-2.6/xfs_buf.c index f4d4e708a8d6..ea79072f5210 100644 --- a/fs/xfs/linux-2.6/xfs_buf.c +++ b/fs/xfs/linux-2.6/xfs_buf.c @@ -987,13 +987,12 @@ xfs_bwrite( { int error; - bp->b_strat = xfs_bdstrat_cb; bp->b_mount = mp; bp->b_flags |= XBF_WRITE; bp->b_flags &= ~(XBF_ASYNC | XBF_READ); xfs_buf_delwri_dequeue(bp); - xfs_buf_iostrategy(bp); + xfs_bdstrat_cb(bp); error = xfs_buf_iowait(bp); if (error) @@ -1009,7 +1008,6 @@ xfs_bdwrite( { trace_xfs_buf_bdwrite(bp, _RET_IP_); - bp->b_strat = xfs_bdstrat_cb; bp->b_mount = mp; bp->b_flags &= ~XBF_READ; @@ -1044,7 +1042,6 @@ xfs_bioerror( XFS_BUF_UNDONE(bp); XFS_BUF_STALE(bp); - XFS_BUF_CLR_BDSTRAT_FUNC(bp); xfs_biodone(bp); return EIO; @@ -1074,7 +1071,6 @@ xfs_bioerror_relse( XFS_BUF_DONE(bp); XFS_BUF_STALE(bp); XFS_BUF_CLR_IODONE_FUNC(bp); - XFS_BUF_CLR_BDSTRAT_FUNC(bp); if (!(fl & XBF_ASYNC)) { /* * Mark b_error and B_ERROR _both_. @@ -1869,7 +1865,7 @@ xfsbufd( struct xfs_buf *bp; bp = list_first_entry(&tmp, struct xfs_buf, b_list); list_del_init(&bp->b_list); - xfs_buf_iostrategy(bp); + xfs_bdstrat_cb(bp); count++; } if (count) @@ -1916,7 +1912,7 @@ xfs_flush_buftarg( bp->b_flags &= ~XBF_ASYNC; list_add(&bp->b_list, &wait_list); } - xfs_buf_iostrategy(bp); + xfs_bdstrat_cb(bp); } if (wait) { diff --git a/fs/xfs/linux-2.6/xfs_buf.h b/fs/xfs/linux-2.6/xfs_buf.h index 814f9e83b516..d072e5ff923b 100644 --- a/fs/xfs/linux-2.6/xfs_buf.h +++ b/fs/xfs/linux-2.6/xfs_buf.h @@ -187,7 +187,6 @@ typedef struct xfs_buf { atomic_t b_io_remaining; /* #outstanding I/O requests */ xfs_buf_iodone_t b_iodone; /* I/O completion function */ xfs_buf_relse_t b_relse; /* releasing function */ - xfs_buf_bdstrat_t b_strat; /* pre-write function */ struct completion b_iowait; /* queue for I/O waiters */ void *b_fspriv; void *b_fspriv2; @@ -245,11 +244,6 @@ extern int xfs_buf_iowait(xfs_buf_t *); extern void xfs_buf_iomove(xfs_buf_t *, size_t, size_t, void *, xfs_buf_rw_t); -static inline int xfs_buf_iostrategy(xfs_buf_t *bp) -{ - return bp->b_strat ? bp->b_strat(bp) : xfs_buf_iorequest(bp); -} - static inline int xfs_buf_geterror(xfs_buf_t *bp) { return bp ? bp->b_error : ENOMEM; @@ -321,8 +315,6 @@ extern void xfs_buf_terminate(void); #define XFS_BUF_IODONE_FUNC(bp) ((bp)->b_iodone) #define XFS_BUF_SET_IODONE_FUNC(bp, func) ((bp)->b_iodone = (func)) #define XFS_BUF_CLR_IODONE_FUNC(bp) ((bp)->b_iodone = NULL) -#define XFS_BUF_SET_BDSTRAT_FUNC(bp, func) ((bp)->b_strat = (func)) -#define XFS_BUF_CLR_BDSTRAT_FUNC(bp) ((bp)->b_strat = NULL) #define XFS_BUF_FSPRIVATE(bp, type) ((type)(bp)->b_fspriv) #define XFS_BUF_SET_FSPRIVATE(bp, val) ((bp)->b_fspriv = (void*)(val)) diff --git a/fs/xfs/xfs_buf_item.c b/fs/xfs/xfs_buf_item.c index 2a9e4ef12110..1b09d7a280df 100644 --- a/fs/xfs/xfs_buf_item.c +++ b/fs/xfs/xfs_buf_item.c @@ -694,7 +694,6 @@ xfs_buf_item_init( */ if (bp->b_mount != mp) bp->b_mount = mp; - XFS_BUF_SET_BDSTRAT_FUNC(bp, xfs_bdstrat_cb); if (XFS_BUF_FSPRIVATE(bp, void *) != NULL) { lip = XFS_BUF_FSPRIVATE(bp, xfs_log_item_t *); if (lip->li_type == XFS_LI_BUF) { diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index eef211dfca03..68415cb4f23c 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -2719,7 +2719,6 @@ cluster_corrupt_out: * mark it as stale and brelse. */ if (XFS_BUF_IODONE_FUNC(bp)) { - XFS_BUF_CLR_BDSTRAT_FUNC(bp); XFS_BUF_UNDONE(bp); XFS_BUF_STALE(bp); XFS_BUF_ERROR(bp,EIO); -- cgit v1.2.3 From 5d18898b20dfed5f373f8a9a7cbe01446036f8e9 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 20 Jul 2010 17:51:31 +1000 Subject: xfs: simplify xfs_truncate_file xfs_truncate_file is only used for truncating quota files. Move it to xfs_qm_syscalls.c so it can be marked static and take advatange of the fact by removing the unused page cache validation and taking the iget into the helper. Signed-off-by: Christoph Hellwig Reviewed-by: Dave Chinner Signed-off-by: Dave Chinner --- fs/xfs/quota/xfs_qm_syscalls.c | 70 ++++++++++++++++++++++++++--------- fs/xfs/xfs_utils.c | 83 ------------------------------------------ fs/xfs/xfs_utils.h | 1 - 3 files changed, 52 insertions(+), 102 deletions(-) (limited to 'fs') diff --git a/fs/xfs/quota/xfs_qm_syscalls.c b/fs/xfs/quota/xfs_qm_syscalls.c index 73f2b203975e..d257eb8557c4 100644 --- a/fs/xfs/quota/xfs_qm_syscalls.c +++ b/fs/xfs/quota/xfs_qm_syscalls.c @@ -238,40 +238,74 @@ out_unlock: return error; } +STATIC int +xfs_qm_scall_trunc_qfile( + struct xfs_mount *mp, + xfs_ino_t ino) +{ + struct xfs_inode *ip; + struct xfs_trans *tp; + int error; + + if (ino == NULLFSINO) + return 0; + + error = xfs_iget(mp, NULL, ino, 0, 0, &ip); + if (error) + return error; + + xfs_ilock(ip, XFS_IOLOCK_EXCL); + + tp = xfs_trans_alloc(mp, XFS_TRANS_TRUNCATE_FILE); + error = xfs_trans_reserve(tp, 0, XFS_ITRUNCATE_LOG_RES(mp), 0, + XFS_TRANS_PERM_LOG_RES, + XFS_ITRUNCATE_LOG_COUNT); + if (error) { + xfs_trans_cancel(tp, 0); + xfs_iunlock(ip, XFS_IOLOCK_EXCL); + goto out_put; + } + + xfs_ilock(ip, XFS_ILOCK_EXCL); + xfs_trans_ijoin(tp, ip); + + error = xfs_itruncate_finish(&tp, ip, 0, XFS_DATA_FORK, 1); + if (error) { + xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES | + XFS_TRANS_ABORT); + goto out_unlock; + } + + xfs_ichgtime(ip, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG); + error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES); + +out_unlock: + xfs_iunlock(ip, XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL); +out_put: + IRELE(ip); + return error; +} + int xfs_qm_scall_trunc_qfiles( xfs_mount_t *mp, uint flags) { int error = 0, error2 = 0; - xfs_inode_t *qip; if (!xfs_sb_version_hasquota(&mp->m_sb) || flags == 0) { qdprintk("qtrunc flags=%x m_qflags=%x\n", flags, mp->m_qflags); return XFS_ERROR(EINVAL); } - if ((flags & XFS_DQ_USER) && mp->m_sb.sb_uquotino != NULLFSINO) { - error = xfs_iget(mp, NULL, mp->m_sb.sb_uquotino, 0, 0, &qip); - if (!error) { - error = xfs_truncate_file(mp, qip); - IRELE(qip); - } - } - - if ((flags & (XFS_DQ_GROUP|XFS_DQ_PROJ)) && - mp->m_sb.sb_gquotino != NULLFSINO) { - error2 = xfs_iget(mp, NULL, mp->m_sb.sb_gquotino, 0, 0, &qip); - if (!error2) { - error2 = xfs_truncate_file(mp, qip); - IRELE(qip); - } - } + if (flags & XFS_DQ_USER) + error = xfs_qm_scall_trunc_qfile(mp, mp->m_sb.sb_uquotino); + if (flags & (XFS_DQ_GROUP|XFS_DQ_PROJ)) + error2 = xfs_qm_scall_trunc_qfile(mp, mp->m_sb.sb_gquotino); return error ? error : error2; } - /* * Switch on (a given) quota enforcement for a filesystem. This takes * effect immediately. diff --git a/fs/xfs/xfs_utils.c b/fs/xfs/xfs_utils.c index 102ce4898ab7..b7d5769d2df0 100644 --- a/fs/xfs/xfs_utils.c +++ b/fs/xfs/xfs_utils.c @@ -320,86 +320,3 @@ xfs_bumplink( xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); return 0; } - -/* - * Try to truncate the given file to 0 length. Currently called - * only out of xfs_remove when it has to truncate a file to free - * up space for the remove to proceed. - */ -int -xfs_truncate_file( - xfs_mount_t *mp, - xfs_inode_t *ip) -{ - xfs_trans_t *tp; - int error; - -#ifdef QUOTADEBUG - /* - * This is called to truncate the quotainodes too. - */ - if (XFS_IS_UQUOTA_ON(mp)) { - if (ip->i_ino != mp->m_sb.sb_uquotino) - ASSERT(ip->i_udquot); - } - if (XFS_IS_OQUOTA_ON(mp)) { - if (ip->i_ino != mp->m_sb.sb_gquotino) - ASSERT(ip->i_gdquot); - } -#endif - /* - * Make the call to xfs_itruncate_start before starting the - * transaction, because we cannot make the call while we're - * in a transaction. - */ - xfs_ilock(ip, XFS_IOLOCK_EXCL); - error = xfs_itruncate_start(ip, XFS_ITRUNC_DEFINITE, (xfs_fsize_t)0); - if (error) { - xfs_iunlock(ip, XFS_IOLOCK_EXCL); - return error; - } - - tp = xfs_trans_alloc(mp, XFS_TRANS_TRUNCATE_FILE); - if ((error = xfs_trans_reserve(tp, 0, XFS_ITRUNCATE_LOG_RES(mp), 0, - XFS_TRANS_PERM_LOG_RES, - XFS_ITRUNCATE_LOG_COUNT))) { - xfs_trans_cancel(tp, 0); - xfs_iunlock(ip, XFS_IOLOCK_EXCL); - return error; - } - - /* - * Follow the normal truncate locking protocol. Since we - * hold the inode in the transaction, we know that its number - * of references will stay constant. - */ - xfs_ilock(ip, XFS_ILOCK_EXCL); - xfs_trans_ijoin(tp, ip); - - /* - * Signal a sync xaction. The only case where that isn't - * the case is if we're truncating an already unlinked file - * on a wsync fs. In that case, we know the blocks can't - * reappear in the file because the links to file are - * permanently toast. Currently, we're always going to - * want a sync transaction because this code is being - * called from places where nlink is guaranteed to be 1 - * but I'm leaving the tests in to protect against future - * changes -- rcc. - */ - error = xfs_itruncate_finish(&tp, ip, (xfs_fsize_t)0, - XFS_DATA_FORK, - ((ip->i_d.di_nlink != 0 || - !(mp->m_flags & XFS_MOUNT_WSYNC)) - ? 1 : 0)); - if (error) { - xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES | - XFS_TRANS_ABORT); - } else { - xfs_ichgtime(ip, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG); - error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES); - } - xfs_iunlock(ip, XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL); - - return error; -} diff --git a/fs/xfs/xfs_utils.h b/fs/xfs/xfs_utils.h index ef321225d269..f55b9678264f 100644 --- a/fs/xfs/xfs_utils.h +++ b/fs/xfs/xfs_utils.h @@ -18,7 +18,6 @@ #ifndef __XFS_UTILS_H__ #define __XFS_UTILS_H__ -extern int xfs_truncate_file(xfs_mount_t *, xfs_inode_t *); extern int xfs_dir_ialloc(xfs_trans_t **, xfs_inode_t *, mode_t, xfs_nlink_t, xfs_dev_t, cred_t *, prid_t, int, xfs_inode_t **, int *); -- cgit v1.2.3 From ecd7f082d68d7fb1c96bcf72071aa85db9c00ddf Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 22 Jul 2010 12:52:08 +1000 Subject: xfs: clean up xfs_bmap_get_bp Signed-off-by: Christoph Hellwig Reviewed-by: Dave Chinner Signed-off-by: Dave Chinner --- fs/xfs/xfs_bmap.c | 43 ++++++++++++++++++------------------------- 1 file changed, 18 insertions(+), 25 deletions(-) (limited to 'fs') diff --git a/fs/xfs/xfs_bmap.c b/fs/xfs/xfs_bmap.c index d74fbec80622..23f14e595c18 100644 --- a/fs/xfs/xfs_bmap.c +++ b/fs/xfs/xfs_bmap.c @@ -5648,41 +5648,34 @@ xfs_bmap_eof( } #ifdef DEBUG -STATIC -xfs_buf_t * +STATIC struct xfs_buf * xfs_bmap_get_bp( - xfs_btree_cur_t *cur, + struct xfs_btree_cur *cur, xfs_fsblock_t bno) { - int i; - xfs_buf_t *bp; + struct xfs_log_item_desc *lidp; + int i; if (!cur) - return(NULL); - - bp = NULL; - for(i = 0; i < XFS_BTREE_MAXLEVELS; i++) { - bp = cur->bc_bufs[i]; - if (!bp) break; - if (XFS_BUF_ADDR(bp) == bno) - break; /* Found it */ + return NULL; + + for (i = 0; i < XFS_BTREE_MAXLEVELS; i++) { + if (!cur->bc_bufs[i]) + break; + if (XFS_BUF_ADDR(cur->bc_bufs[i]) == bno) + return cur->bc_bufs[i]; } - if (i == XFS_BTREE_MAXLEVELS) - bp = NULL; - if (!bp) { /* Chase down all the log items to see if the bp is there */ - struct xfs_log_item_desc *lidp; + /* Chase down all the log items to see if the bp is there */ + list_for_each_entry(lidp, &cur->bc_tp->t_items, lid_trans) { struct xfs_buf_log_item *bip; - - list_for_each_entry(lidp, &cur->bc_tp->t_items, lid_trans) { - bip = (struct xfs_buf_log_item *)lidp->lid_item; - if (bip->bli_item.li_type == XFS_LI_BUF && - XFS_BUF_ADDR(bip->bli_buf) == bno) - return bip->bli_buf; - } + bip = (struct xfs_buf_log_item *)lidp->lid_item; + if (bip->bli_item.li_type == XFS_LI_BUF && + XFS_BUF_ADDR(bip->bli_buf) == bno) + return bip->bli_buf; } - return bp; + return NULL; } STATIC void -- cgit v1.2.3 From 96d6523adffbab64f099561a021892125e0c672c Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 8 Jul 2010 09:31:24 -0700 Subject: sysfs: Don't allow the creation of symlinks we can't remove Recently my tagged sysfs support revealed a flaw in the device core that a few rare drivers are running into such that we don't always put network devices in a class subdirectory named net/. Since we are not creating the class directory the network devices wind up in a non-tagged directory, but the symlinks to the network devices from /sys/class/net are in a tagged directory. All of which works until we go to remove or rename the symlink. When we remove or rename a symlink we look in the namespace of the target of the symlink. Since the target of the symlink is in a non-tagged sysfs directory we don't have a namespace to look in, and we fail to remove the symlink. Detect this problem up front and simply don't create symlinks we won't be able to remove later. This prevents symlink leakage and fails in a much clearer and more understandable way. Signed-off-by: Eric W. Biederman Cc: Andrew Morton Cc: Rafael J. Wysocki Cc: Maciej W. Rozycki Cc: Kay Sievers Cc: Johannes Berg Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/symlink.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) (limited to 'fs') diff --git a/fs/sysfs/symlink.c b/fs/sysfs/symlink.c index f71246bebfe4..44bca5f49cd5 100644 --- a/fs/sysfs/symlink.c +++ b/fs/sysfs/symlink.c @@ -28,6 +28,7 @@ static int sysfs_do_create_link(struct kobject *kobj, struct kobject *target, struct sysfs_dirent *target_sd = NULL; struct sysfs_dirent *sd = NULL; struct sysfs_addrm_cxt acxt; + enum kobj_ns_type ns_type; int error; BUG_ON(!name); @@ -58,16 +59,28 @@ static int sysfs_do_create_link(struct kobject *kobj, struct kobject *target, if (!sd) goto out_put; - if (sysfs_ns_type(parent_sd)) + ns_type = sysfs_ns_type(parent_sd); + if (ns_type) sd->s_ns = target->ktype->namespace(target); sd->s_symlink.target_sd = target_sd; target_sd = NULL; /* reference is now owned by the symlink */ sysfs_addrm_start(&acxt, parent_sd); - if (warn) - error = sysfs_add_one(&acxt, sd); - else - error = __sysfs_add_one(&acxt, sd); + /* Symlinks must be between directories with the same ns_type */ + if (ns_type == sysfs_ns_type(sd->s_symlink.target_sd->s_parent)) { + if (warn) + error = sysfs_add_one(&acxt, sd); + else + error = __sysfs_add_one(&acxt, sd); + } else { + error = -EINVAL; + WARN(1, KERN_WARNING + "sysfs: symlink across ns_types %s/%s -> %s/%s\n", + parent_sd->s_name, + sd->s_name, + sd->s_symlink.target_sd->s_parent->s_name, + sd->s_symlink.target_sd->s_name); + } sysfs_addrm_finish(&acxt); if (error) -- cgit v1.2.3 From 521d0453547d6195d200176328aaec6c98a7a290 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Tue, 20 Jul 2010 22:10:58 -0700 Subject: sysfs: sysfs_delete_link handle symlinks from untagged to tagged directories. This happens for network devices when SYSFS_DEPRECATED is enabled. Signed-off-by: Eric W. Biederman Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/symlink.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/sysfs/symlink.c b/fs/sysfs/symlink.c index 44bca5f49cd5..660383321347 100644 --- a/fs/sysfs/symlink.c +++ b/fs/sysfs/symlink.c @@ -135,7 +135,7 @@ void sysfs_delete_link(struct kobject *kobj, struct kobject *targ, { const void *ns = NULL; spin_lock(&sysfs_assoc_lock); - if (targ->sd) + if (targ->sd && sysfs_ns_type(kobj->sd)) ns = targ->sd->s_ns; spin_unlock(&sysfs_assoc_lock); sysfs_hash_and_remove(kobj->sd, ns, name); -- cgit v1.2.3 From d33002129eee4717a92e320b0b764a784bbcad3a Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Tue, 20 Jul 2010 22:12:01 -0700 Subject: sysfs: allow creating symlinks from untagged to tagged directories Supporting symlinks from untagged to tagged directories is reasonable, and needed to support CONFIG_SYSFS_DEPRECATED. So don't fail a prior allowing that case to work. Signed-off-by: Eric W. Biederman Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/symlink.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/sysfs/symlink.c b/fs/sysfs/symlink.c index 660383321347..a7ac78f8e67a 100644 --- a/fs/sysfs/symlink.c +++ b/fs/sysfs/symlink.c @@ -67,7 +67,8 @@ static int sysfs_do_create_link(struct kobject *kobj, struct kobject *target, sysfs_addrm_start(&acxt, parent_sd); /* Symlinks must be between directories with the same ns_type */ - if (ns_type == sysfs_ns_type(sd->s_symlink.target_sd->s_parent)) { + if (!ns_type || + (ns_type == sysfs_ns_type(sd->s_symlink.target_sd->s_parent))) { if (warn) error = sysfs_add_one(&acxt, sd); else -- cgit v1.2.3 From 696123fca877905696591829c97a2cef11c8d048 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Mon, 26 Jul 2010 13:51:46 -0500 Subject: xfs: fix big endian build Commit 0fd7275cc42ab734eaa1a2c747e65479bd1e42af ("xfs: fix gcc 4.6 set but not read and unused statement warnings") failed to convert some code inside XFS_NATIVE_HOST (big endian host code only) and hence fails to build on such machines. Fix it. Signed-off-by: Dave Chinner Signed-off-by: Alex Elder --- fs/xfs/xfs_inode_item.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/xfs/xfs_inode_item.c b/fs/xfs/xfs_inode_item.c index 2d6fcfdc7834..fe00777e2796 100644 --- a/fs/xfs/xfs_inode_item.c +++ b/fs/xfs/xfs_inode_item.c @@ -316,7 +316,8 @@ xfs_inode_item_format( ASSERT((ip->i_df.if_bytes / (uint)sizeof(xfs_bmbt_rec_t)) > 0); #ifdef XFS_NATIVE_HOST - if (nrecs == ip->i_d.di_nextents) { + if (ip->i_d.di_nextents == ip->i_df.if_bytes / + (uint)sizeof(xfs_bmbt_rec_t)) { /* * There are no delayed allocation * extents, so just point to the -- cgit v1.2.3 From 40e2e97316af6e62affab7a392e792494b8d9dde Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sun, 18 Jul 2010 21:17:09 +0000 Subject: direct-io: move aio_complete into ->end_io Filesystems with unwritten extent support must not complete an AIO request until the transaction to convert the extent has been commited. That means the aio_complete calls needs to be moved into the ->end_io callback so that the filesystem can control when to call it exactly. This makes a bit of a mess out of dio_complete and the ->end_io callback prototype even more complicated. Signed-off-by: Christoph Hellwig Reviewed-by: Jan Kara Signed-off-by: Alex Elder --- fs/direct-io.c | 26 ++++++++++++++------------ fs/ext4/inode.c | 10 +++++++--- fs/ocfs2/aops.c | 7 ++++++- fs/xfs/linux-2.6/xfs_aops.c | 7 ++++++- fs/xfs/linux-2.6/xfs_aops.h | 2 ++ include/linux/fs.h | 3 ++- 6 files changed, 37 insertions(+), 18 deletions(-) (limited to 'fs') diff --git a/fs/direct-io.c b/fs/direct-io.c index 7600aacf531d..a10cb91cadea 100644 --- a/fs/direct-io.c +++ b/fs/direct-io.c @@ -218,7 +218,7 @@ static struct page *dio_get_page(struct dio *dio) * filesystems can use it to hold additional state between get_block calls and * dio_complete. */ -static int dio_complete(struct dio *dio, loff_t offset, int ret) +static int dio_complete(struct dio *dio, loff_t offset, int ret, bool is_async) { ssize_t transferred = 0; @@ -239,14 +239,6 @@ static int dio_complete(struct dio *dio, loff_t offset, int ret) transferred = dio->i_size - offset; } - if (dio->end_io && dio->result) - dio->end_io(dio->iocb, offset, transferred, - dio->map_bh.b_private); - - if (dio->flags & DIO_LOCKING) - /* lockdep: non-owner release */ - up_read_non_owner(&dio->inode->i_alloc_sem); - if (ret == 0) ret = dio->page_errors; if (ret == 0) @@ -254,6 +246,17 @@ static int dio_complete(struct dio *dio, loff_t offset, int ret) if (ret == 0) ret = transferred; + if (dio->end_io && dio->result) { + dio->end_io(dio->iocb, offset, transferred, + dio->map_bh.b_private, ret, is_async); + } else if (is_async) { + aio_complete(dio->iocb, ret, 0); + } + + if (dio->flags & DIO_LOCKING) + /* lockdep: non-owner release */ + up_read_non_owner(&dio->inode->i_alloc_sem); + return ret; } @@ -277,8 +280,7 @@ static void dio_bio_end_aio(struct bio *bio, int error) spin_unlock_irqrestore(&dio->bio_lock, flags); if (remaining == 0) { - int ret = dio_complete(dio, dio->iocb->ki_pos, 0); - aio_complete(dio->iocb, ret, 0); + dio_complete(dio, dio->iocb->ki_pos, 0, true); kfree(dio); } } @@ -1126,7 +1128,7 @@ direct_io_worker(int rw, struct kiocb *iocb, struct inode *inode, spin_unlock_irqrestore(&dio->bio_lock, flags); if (ret2 == 0) { - ret = dio_complete(dio, offset, ret); + ret = dio_complete(dio, offset, ret, false); kfree(dio); } else BUG_ON(ret != -EIOCBQUEUED); diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 42272d67955a..0afc8c1d8cf3 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -3775,7 +3775,8 @@ static ext4_io_end_t *ext4_init_io_end (struct inode *inode, gfp_t flags) } static void ext4_end_io_dio(struct kiocb *iocb, loff_t offset, - ssize_t size, void *private) + ssize_t size, void *private, int ret, + bool is_async) { ext4_io_end_t *io_end = iocb->private; struct workqueue_struct *wq; @@ -3784,7 +3785,7 @@ static void ext4_end_io_dio(struct kiocb *iocb, loff_t offset, /* if not async direct IO or dio with 0 bytes write, just return */ if (!io_end || !size) - return; + goto out; ext_debug("ext4_end_io_dio(): io_end 0x%p" "for inode %lu, iocb 0x%p, offset %llu, size %llu\n", @@ -3795,7 +3796,7 @@ static void ext4_end_io_dio(struct kiocb *iocb, loff_t offset, if (io_end->flag != EXT4_IO_UNWRITTEN){ ext4_free_io_end(io_end); iocb->private = NULL; - return; + goto out; } io_end->offset = offset; @@ -3812,6 +3813,9 @@ static void ext4_end_io_dio(struct kiocb *iocb, loff_t offset, list_add_tail(&io_end->list, &ei->i_completed_io_list); spin_unlock_irqrestore(&ei->i_completed_io_lock, flags); iocb->private = NULL; +out: + if (is_async) + aio_complete(iocb, ret, 0); } static void ext4_end_io_buffer_write(struct buffer_head *bh, int uptodate) diff --git a/fs/ocfs2/aops.c b/fs/ocfs2/aops.c index 356e976772bf..96337a4fbbdf 100644 --- a/fs/ocfs2/aops.c +++ b/fs/ocfs2/aops.c @@ -578,7 +578,9 @@ bail: static void ocfs2_dio_end_io(struct kiocb *iocb, loff_t offset, ssize_t bytes, - void *private) + void *private, + int ret, + bool is_async) { struct inode *inode = iocb->ki_filp->f_path.dentry->d_inode; int level; @@ -592,6 +594,9 @@ static void ocfs2_dio_end_io(struct kiocb *iocb, if (!level) up_read(&inode->i_alloc_sem); ocfs2_rw_unlock(inode, level); + + if (is_async) + aio_complete(iocb, ret, 0); } /* diff --git a/fs/xfs/linux-2.6/xfs_aops.c b/fs/xfs/linux-2.6/xfs_aops.c index 8abbf0532ea1..95d1e2695c3a 100644 --- a/fs/xfs/linux-2.6/xfs_aops.c +++ b/fs/xfs/linux-2.6/xfs_aops.c @@ -1406,7 +1406,9 @@ xfs_end_io_direct( struct kiocb *iocb, loff_t offset, ssize_t size, - void *private) + void *private, + int ret, + bool is_async) { xfs_ioend_t *ioend = iocb->private; @@ -1452,6 +1454,9 @@ xfs_end_io_direct( * against double-freeing. */ iocb->private = NULL; + + if (is_async) + aio_complete(iocb, ret, 0); } STATIC ssize_t diff --git a/fs/xfs/linux-2.6/xfs_aops.h b/fs/xfs/linux-2.6/xfs_aops.h index 319da173cc1a..c5057fb6237a 100644 --- a/fs/xfs/linux-2.6/xfs_aops.h +++ b/fs/xfs/linux-2.6/xfs_aops.h @@ -37,6 +37,8 @@ typedef struct xfs_ioend { size_t io_size; /* size of the extent */ xfs_off_t io_offset; /* offset in the file */ struct work_struct io_work; /* xfsdatad work queue */ + struct kiocb *io_iocb; + int io_result; } xfs_ioend_t; extern const struct address_space_operations xfs_address_space_operations; diff --git a/include/linux/fs.h b/include/linux/fs.h index 68ca1b0491af..f91affb7d530 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -415,7 +415,8 @@ struct buffer_head; typedef int (get_block_t)(struct inode *inode, sector_t iblock, struct buffer_head *bh_result, int create); typedef void (dio_iodone_t)(struct kiocb *iocb, loff_t offset, - ssize_t bytes, void *private); + ssize_t bytes, void *private, int ret, + bool is_async); /* * Attribute flags. These should be or-ed together to figure out what -- cgit v1.2.3 From fb511f2150174b18b28ad54708c1adda0df39b17 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sun, 18 Jul 2010 21:17:10 +0000 Subject: xfs: move aio completion after unwritten extent conversion If we write into an unwritten extent using AIO we need to complete the AIO request after the extent conversion has finished. Without that a read could race to see see the extent still unwritten and return zeros. For synchronous I/O we already take care of that by flushing the xfsconvertd workqueue (which might be a bit of overkill). To do that add iocb and result fields to struct xfs_ioend, so that we can call aio_complete from xfs_end_io after the extent conversion has happened. Note that we need a new result field as io_error is used for positive errno values, while the AIO code can return negative error values and positive transfer sizes. Signed-off-by: Christoph Hellwig Reviewed-by: Dave Chinner Signed-off-by: Alex Elder --- fs/xfs/linux-2.6/xfs_aops.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_aops.c b/fs/xfs/linux-2.6/xfs_aops.c index 95d1e2695c3a..13622d5ba068 100644 --- a/fs/xfs/linux-2.6/xfs_aops.c +++ b/fs/xfs/linux-2.6/xfs_aops.c @@ -265,8 +265,11 @@ xfs_end_io( xfs_finish_ioend(ioend, 0); /* ensure we don't spin on blocked ioends */ delay(1); - } else + } else { + if (ioend->io_iocb) + aio_complete(ioend->io_iocb, ioend->io_result, 0); xfs_destroy_ioend(ioend); + } } /* @@ -299,6 +302,8 @@ xfs_alloc_ioend( atomic_inc(&XFS_I(ioend->io_inode)->i_iocount); ioend->io_offset = 0; ioend->io_size = 0; + ioend->io_iocb = NULL; + ioend->io_result = 0; INIT_WORK(&ioend->io_work, xfs_end_io); return ioend; @@ -1411,6 +1416,7 @@ xfs_end_io_direct( bool is_async) { xfs_ioend_t *ioend = iocb->private; + bool complete_aio = is_async; /* * Non-NULL private data means we need to issue a transaction to @@ -1436,7 +1442,14 @@ xfs_end_io_direct( if (ioend->io_type == IO_READ) { xfs_finish_ioend(ioend, 0); } else if (private && size > 0) { - xfs_finish_ioend(ioend, is_sync_kiocb(iocb)); + if (is_async) { + ioend->io_iocb = iocb; + ioend->io_result = ret; + complete_aio = false; + xfs_finish_ioend(ioend, 0); + } else { + xfs_finish_ioend(ioend, 1); + } } else { /* * A direct I/O write ioend starts it's life in unwritten @@ -1455,7 +1468,7 @@ xfs_end_io_direct( */ iocb->private = NULL; - if (is_async) + if (complete_aio) aio_complete(iocb, ret, 0); } -- cgit v1.2.3 From 209fb87a259ead17e966627b7f053d16a96898da Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sun, 18 Jul 2010 21:17:11 +0000 Subject: xfs simplify and speed up direct I/O completions Our current handling of direct I/O completions is rather suboptimal, because we defer it to a workqueue more often than needed, and we perform a much to aggressive flush of the workqueue in case unwritten extent conversions happen. This patch changes the direct I/O reads to not even use a completion handler, as we don't bother to use it at all, and to perform the unwritten extent conversions in caller context for synchronous direct I/O. For a small I/O size direct I/O workload on a consumer grade SSD, such as the untar of a kernel tree inside qemu this patch gives speedups of about 5%. Getting us much closer to the speed of a native block device, or a fully allocated XFS file. Signed-off-by: Christoph Hellwig Reviewed-by: Dave Chinner Signed-off-by: Alex Elder --- fs/xfs/linux-2.6/xfs_aops.c | 158 +++++++++++++++++++++----------------------- 1 file changed, 76 insertions(+), 82 deletions(-) (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_aops.c b/fs/xfs/linux-2.6/xfs_aops.c index 13622d5ba068..d24e78f32f3e 100644 --- a/fs/xfs/linux-2.6/xfs_aops.c +++ b/fs/xfs/linux-2.6/xfs_aops.c @@ -202,23 +202,17 @@ xfs_setfilesize( } /* - * Schedule IO completion handling on a xfsdatad if this was - * the final hold on this ioend. If we are asked to wait, - * flush the workqueue. + * Schedule IO completion handling on the final put of an ioend. */ STATIC void xfs_finish_ioend( - xfs_ioend_t *ioend, - int wait) + struct xfs_ioend *ioend) { if (atomic_dec_and_test(&ioend->io_remaining)) { - struct workqueue_struct *wq; - - wq = (ioend->io_type == IO_UNWRITTEN) ? - xfsconvertd_workqueue : xfsdatad_workqueue; - queue_work(wq, &ioend->io_work); - if (wait) - flush_workqueue(wq); + if (ioend->io_type == IO_UNWRITTEN) + queue_work(xfsconvertd_workqueue, &ioend->io_work); + else + queue_work(xfsdatad_workqueue, &ioend->io_work); } } @@ -262,7 +256,7 @@ xfs_end_io( */ if (error == EAGAIN) { atomic_inc(&ioend->io_remaining); - xfs_finish_ioend(ioend, 0); + xfs_finish_ioend(ioend); /* ensure we don't spin on blocked ioends */ delay(1); } else { @@ -272,6 +266,17 @@ xfs_end_io( } } +/* + * Call IO completion handling in caller context on the final put of an ioend. + */ +STATIC void +xfs_finish_ioend_sync( + struct xfs_ioend *ioend) +{ + if (atomic_dec_and_test(&ioend->io_remaining)) + xfs_end_io(&ioend->io_work); +} + /* * Allocate and initialise an IO completion structure. * We need to track unwritten extent write completion here initially. @@ -353,7 +358,7 @@ xfs_end_bio( bio->bi_end_io = NULL; bio_put(bio); - xfs_finish_ioend(ioend, 0); + xfs_finish_ioend(ioend); } STATIC void @@ -495,7 +500,7 @@ xfs_submit_ioend( } if (bio) xfs_submit_ioend_bio(wbc, ioend, bio); - xfs_finish_ioend(ioend, 0); + xfs_finish_ioend(ioend); } while ((ioend = next) != NULL); } @@ -1406,70 +1411,56 @@ xfs_get_blocks_direct( return __xfs_get_blocks(inode, iblock, bh_result, create, 1); } +/* + * Complete a direct I/O write request. + * + * If the private argument is non-NULL __xfs_get_blocks signals us that we + * need to issue a transaction to convert the range from unwritten to written + * extents. In case this is regular synchronous I/O we just call xfs_end_io + * to do this and we are done. But in case this was a successfull AIO + * request this handler is called from interrupt context, from which we + * can't start transactions. In that case offload the I/O completion to + * the workqueues we also use for buffered I/O completion. + */ STATIC void -xfs_end_io_direct( - struct kiocb *iocb, - loff_t offset, - ssize_t size, - void *private, - int ret, - bool is_async) +xfs_end_io_direct_write( + struct kiocb *iocb, + loff_t offset, + ssize_t size, + void *private, + int ret, + bool is_async) { - xfs_ioend_t *ioend = iocb->private; - bool complete_aio = is_async; + struct xfs_ioend *ioend = iocb->private; /* - * Non-NULL private data means we need to issue a transaction to - * convert a range from unwritten to written extents. This needs - * to happen from process context but aio+dio I/O completion - * happens from irq context so we need to defer it to a workqueue. - * This is not necessary for synchronous direct I/O, but we do - * it anyway to keep the code uniform and simpler. - * - * Well, if only it were that simple. Because synchronous direct I/O - * requires extent conversion to occur *before* we return to userspace, - * we have to wait for extent conversion to complete. Look at the - * iocb that has been passed to us to determine if this is AIO or - * not. If it is synchronous, tell xfs_finish_ioend() to kick the - * workqueue and wait for it to complete. - * - * The core direct I/O code might be changed to always call the - * completion handler in the future, in which case all this can - * go away. + * blockdev_direct_IO can return an error even after the I/O + * completion handler was called. Thus we need to protect + * against double-freeing. */ + iocb->private = NULL; + ioend->io_offset = offset; ioend->io_size = size; - if (ioend->io_type == IO_READ) { - xfs_finish_ioend(ioend, 0); - } else if (private && size > 0) { - if (is_async) { + if (private && size > 0) + ioend->io_type = IO_UNWRITTEN; + + if (is_async) { + /* + * If we are converting an unwritten extent we need to delay + * the AIO completion until after the unwrittent extent + * conversion has completed, otherwise do it ASAP. + */ + if (ioend->io_type == IO_UNWRITTEN) { ioend->io_iocb = iocb; ioend->io_result = ret; - complete_aio = false; - xfs_finish_ioend(ioend, 0); } else { - xfs_finish_ioend(ioend, 1); + aio_complete(iocb, ret, 0); } + xfs_finish_ioend(ioend); } else { - /* - * A direct I/O write ioend starts it's life in unwritten - * state in case they map an unwritten extent. This write - * didn't map an unwritten extent so switch it's completion - * handler. - */ - ioend->io_type = IO_NEW; - xfs_finish_ioend(ioend, 0); + xfs_finish_ioend_sync(ioend); } - - /* - * blockdev_direct_IO can return an error even after the I/O - * completion handler was called. Thus we need to protect - * against double-freeing. - */ - iocb->private = NULL; - - if (complete_aio) - aio_complete(iocb, ret, 0); } STATIC ssize_t @@ -1480,23 +1471,26 @@ xfs_vm_direct_IO( loff_t offset, unsigned long nr_segs) { - struct file *file = iocb->ki_filp; - struct inode *inode = file->f_mapping->host; - struct block_device *bdev; - ssize_t ret; - - bdev = xfs_find_bdev_for_inode(inode); - - iocb->private = xfs_alloc_ioend(inode, rw == WRITE ? - IO_UNWRITTEN : IO_READ); - - ret = blockdev_direct_IO_no_locking(rw, iocb, inode, bdev, iov, - offset, nr_segs, - xfs_get_blocks_direct, - xfs_end_io_direct); + struct inode *inode = iocb->ki_filp->f_mapping->host; + struct block_device *bdev = xfs_find_bdev_for_inode(inode); + ssize_t ret; + + if (rw & WRITE) { + iocb->private = xfs_alloc_ioend(inode, IO_NEW); + + ret = blockdev_direct_IO_no_locking(rw, iocb, inode, bdev, iov, + offset, nr_segs, + xfs_get_blocks_direct, + xfs_end_io_direct_write); + if (ret != -EIOCBQUEUED && iocb->private) + xfs_destroy_ioend(iocb->private); + } else { + ret = blockdev_direct_IO_no_locking(rw, iocb, inode, bdev, iov, + offset, nr_segs, + xfs_get_blocks_direct, + NULL); + } - if (unlikely(ret != -EIOCBQUEUED && iocb->private)) - xfs_destroy_ioend(iocb->private); return ret; } -- cgit v1.2.3 From 60fd4da34d55a9cc0d857fc76dc12cf8cab4ed02 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Tue, 27 Jul 2010 11:54:40 -0400 Subject: ext4: Cleanup ext4_check_dir_entry so __func__ is now implicit Also start passing the line number to ext4_check_dir since we're going to need it in upcoming patch. Signed-off-by: "Theodore Ts'o" --- fs/ext4/dir.c | 11 ++++++----- fs/ext4/ext4.h | 8 +++++--- fs/ext4/namei.c | 14 ++++++-------- 3 files changed, 17 insertions(+), 16 deletions(-) (limited to 'fs') diff --git a/fs/ext4/dir.c b/fs/ext4/dir.c index 2965c39d4183..af581f08fe3a 100644 --- a/fs/ext4/dir.c +++ b/fs/ext4/dir.c @@ -61,10 +61,11 @@ static unsigned char get_dtype(struct super_block *sb, int filetype) } -int ext4_check_dir_entry(const char *function, struct inode *dir, - struct ext4_dir_entry_2 *de, - struct buffer_head *bh, - unsigned int offset) +int __ext4_check_dir_entry(const char *function, unsigned int line, + struct inode *dir, + struct ext4_dir_entry_2 *de, + struct buffer_head *bh, + unsigned int offset) { const char *error_msg = NULL; const int rlen = ext4_rec_len_from_disk(de->rec_len, @@ -194,7 +195,7 @@ revalidate: while (!error && filp->f_pos < inode->i_size && offset < sb->s_blocksize) { de = (struct ext4_dir_entry_2 *) (bh->b_data + offset); - if (!ext4_check_dir_entry("ext4_readdir", inode, de, + if (!ext4_check_dir_entry(inode, de, bh, offset)) { /* * On error, skip the f_pos to the next block diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 5a41881cafca..73465b26976d 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -1515,9 +1515,11 @@ extern unsigned ext4_init_block_bitmap(struct super_block *sb, ext4_init_block_bitmap(sb, NULL, group, desc) /* dir.c */ -extern int ext4_check_dir_entry(const char *, struct inode *, - struct ext4_dir_entry_2 *, - struct buffer_head *, unsigned int); +extern int __ext4_check_dir_entry(const char *, unsigned int, struct inode *, + struct ext4_dir_entry_2 *, + struct buffer_head *, unsigned int); +#define ext4_check_dir_entry(dir, de, bh, offset) \ + __ext4_check_dir_entry(__func__, __LINE__, (dir), (de), (bh), (offset)) extern int ext4_htree_store_dirent(struct file *dir_file, __u32 hash, __u32 minor_hash, struct ext4_dir_entry_2 *dirent); diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 5a61f77e7d7c..ea8b59d96213 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -605,7 +605,7 @@ static int htree_dirblock_to_tree(struct file *dir_file, dir->i_sb->s_blocksize - EXT4_DIR_REC_LEN(0)); for (; de < top; de = ext4_next_entry(de, dir->i_sb->s_blocksize)) { - if (!ext4_check_dir_entry("htree_dirblock_to_tree", dir, de, bh, + if (!ext4_check_dir_entry(dir, de, bh, (block<i_sb)) +((char *)de - bh->b_data))) { /* On error, skip the f_pos to the next block. */ @@ -844,8 +844,7 @@ static inline int search_dirblock(struct buffer_head *bh, if ((char *) de + namelen <= dlimit && ext4_match (namelen, name, de)) { /* found a match - just to be sure, do a full check */ - if (!ext4_check_dir_entry("ext4_find_entry", - dir, de, bh, offset)) + if (!ext4_check_dir_entry(dir, de, bh, offset)) return -1; *res_dir = de; return 1; @@ -1019,7 +1018,7 @@ static struct buffer_head * ext4_dx_find_entry(struct inode *dir, const struct q int off = (block << EXT4_BLOCK_SIZE_BITS(sb)) + ((char *) de - bh->b_data); - if (!ext4_check_dir_entry(__func__, dir, de, bh, off)) { + if (!ext4_check_dir_entry(dir, de, bh, off)) { brelse(bh); *err = ERR_BAD_DX_DIR; goto errout; @@ -1303,8 +1302,7 @@ static int add_dirent_to_buf(handle_t *handle, struct dentry *dentry, de = (struct ext4_dir_entry_2 *)bh->b_data; top = bh->b_data + blocksize - reclen; while ((char *) de <= top) { - if (!ext4_check_dir_entry("ext4_add_entry", dir, de, - bh, offset)) + if (!ext4_check_dir_entry(dir, de, bh, offset)) return -EIO; if (ext4_match(namelen, name, de)) return -EEXIST; @@ -1671,7 +1669,7 @@ static int ext4_delete_entry(handle_t *handle, pde = NULL; de = (struct ext4_dir_entry_2 *) bh->b_data; while (i < bh->b_size) { - if (!ext4_check_dir_entry("ext4_delete_entry", dir, de, bh, i)) + if (!ext4_check_dir_entry(dir, de, bh, i)) return -EIO; if (de == de_del) { BUFFER_TRACE(bh, "get_write_access"); @@ -1954,7 +1952,7 @@ static int empty_dir(struct inode *inode) } de = (struct ext4_dir_entry_2 *) bh->b_data; } - if (!ext4_check_dir_entry("empty_dir", inode, de, bh, offset)) { + if (!ext4_check_dir_entry(inode, de, bh, offset)) { de = (struct ext4_dir_entry_2 *)(bh->b_data + sb->s_blocksize); offset = (offset | (sb->s_blocksize - 1)) + 1; -- cgit v1.2.3 From c398eda0e43a791be0fca6f197a1e2bbb9f16070 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Tue, 27 Jul 2010 11:56:40 -0400 Subject: ext4: Pass line numbers to ext4_error() and friends Signed-off-by: "Theodore Ts'o" --- fs/ext4/dir.c | 7 +++---- fs/ext4/ext4.h | 44 +++++++++++++++++++++++++++----------------- fs/ext4/ext4_jbd2.c | 12 +++++------- fs/ext4/ext4_jbd2.h | 4 ++-- fs/ext4/extents.c | 10 +++++----- fs/ext4/inode.c | 44 +++++++++++++++++++++++--------------------- fs/ext4/move_extent.c | 10 +++++----- fs/ext4/super.c | 47 ++++++++++++++++++++++++++++------------------- 8 files changed, 98 insertions(+), 80 deletions(-) (limited to 'fs') diff --git a/fs/ext4/dir.c b/fs/ext4/dir.c index af581f08fe3a..62e8af04ed1e 100644 --- a/fs/ext4/dir.c +++ b/fs/ext4/dir.c @@ -84,11 +84,10 @@ int __ext4_check_dir_entry(const char *function, unsigned int line, error_msg = "inode out of bounds"; if (error_msg != NULL) - ext4_error_inode(function, dir, - "bad entry in directory: %s - block=%llu" + ext4_error_inode(dir, function, line, bh->b_blocknr, + "bad entry in directory: %s - " "offset=%u(%u), inode=%u, rec_len=%d, name_len=%d", - error_msg, (unsigned long long) bh->b_blocknr, - (unsigned) (offset%bh->b_size), offset, + error_msg, (unsigned) (offset%bh->b_size), offset, le32_to_cpu(de->inode), rlen, de->name_len); return error_msg == NULL ? 1 : 0; diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 73465b26976d..088938148f5c 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -57,10 +57,13 @@ #endif #define EXT4_ERROR_INODE(inode, fmt, a...) \ - ext4_error_inode(__func__, (inode), (fmt), ## a) + ext4_error_inode((inode), __func__, __LINE__, 0, (fmt), ## a) + +#define EXT4_ERROR_INODE_BLOCK(inode, block, fmt, a...) \ + ext4_error_inode((inode), __func__, __LINE__, (block), (fmt), ## a) #define EXT4_ERROR_FILE(file, fmt, a...) \ - ext4_error_file(__func__, (file), (fmt), ## a) + ext4_error_file(__func__, __LINE__, (file), (fmt), ## a) /* data type for block offset of block group */ typedef int ext4_grpblk_t; @@ -1623,22 +1626,29 @@ extern int ext4_group_extend(struct super_block *sb, ext4_fsblk_t n_blocks_count); /* super.c */ -extern void __ext4_error(struct super_block *, const char *, const char *, ...) - __attribute__ ((format (printf, 3, 4))); -#define ext4_error(sb, message...) __ext4_error(sb, __func__, ## message) -extern void ext4_error_inode(const char *, struct inode *, const char *, ...) - __attribute__ ((format (printf, 3, 4))); -extern void ext4_error_file(const char *, struct file *, const char *, ...) - __attribute__ ((format (printf, 3, 4))); -extern void __ext4_std_error(struct super_block *, const char *, int); -extern void __ext4_abort(struct super_block *, const char *, const char *, ...) - __attribute__ ((format (printf, 3, 4))); +extern void __ext4_error(struct super_block *, const char *, unsigned int, + const char *, ...) + __attribute__ ((format (printf, 4, 5))); +#define ext4_error(sb, message...) __ext4_error(sb, __func__, \ + __LINE__, ## message) +extern void ext4_error_inode(struct inode *, const char *, unsigned int, + ext4_fsblk_t, const char *, ...) + __attribute__ ((format (printf, 5, 6))); +extern void ext4_error_file(struct file *, const char *, unsigned int, + const char *, ...) + __attribute__ ((format (printf, 4, 5))); +extern void __ext4_std_error(struct super_block *, const char *, + unsigned int, int); +extern void __ext4_abort(struct super_block *, const char *, unsigned int, + const char *, ...) + __attribute__ ((format (printf, 4, 5))); #define ext4_abort(sb, message...) __ext4_abort(sb, __func__, \ - ## message) -extern void __ext4_warning(struct super_block *, const char *, + __LINE__, ## message) +extern void __ext4_warning(struct super_block *, const char *, unsigned int, const char *, ...) - __attribute__ ((format (printf, 3, 4))); -#define ext4_warning(sb, message...) __ext4_warning(sb, __func__, ## message) + __attribute__ ((format (printf, 4, 5))); +#define ext4_warning(sb, message...) __ext4_warning(sb, __func__, \ + __LINE__, ## message) extern void ext4_msg(struct super_block *, const char *, const char *, ...) __attribute__ ((format (printf, 3, 4))); extern void __ext4_grp_locked_error(const char *, unsigned int, \ @@ -1781,7 +1791,7 @@ static inline unsigned int ext4_flex_bg_size(struct ext4_sb_info *sbi) #define ext4_std_error(sb, errno) \ do { \ if ((errno)) \ - __ext4_std_error((sb), __func__, (errno)); \ + __ext4_std_error((sb), __func__, __LINE__, (errno)); \ } while (0) #ifdef CONFIG_SMP diff --git a/fs/ext4/ext4_jbd2.c b/fs/ext4/ext4_jbd2.c index 9de37b9e177a..23425cd68daa 100644 --- a/fs/ext4/ext4_jbd2.c +++ b/fs/ext4/ext4_jbd2.c @@ -94,8 +94,8 @@ int __ext4_forget(const char *where, unsigned int line, handle_t *handle, if (err) { ext4_journal_abort_handle(where, line, __func__, bh, handle, err); - __ext4_abort(inode->i_sb, where, - "error %d when attempting revoke", err); + __ext4_abort(inode->i_sb, where, line, + "error %d when attempting revoke", err); } BUFFER_TRACE(bh, "exit"); return err; @@ -134,11 +134,9 @@ int __ext4_handle_dirty_metadata(const char *where, unsigned int line, if (inode && inode_needs_sync(inode)) { sync_dirty_buffer(bh); if (buffer_req(bh) && !buffer_uptodate(bh)) { - ext4_error(inode->i_sb, - "IO error syncing inode, " - "inode=%lu, block=%llu", - inode->i_ino, - (unsigned long long) bh->b_blocknr); + ext4_error_inode(inode, where, line, + bh->b_blocknr, + "IO error syncing itable block"); err = -EIO; } } diff --git a/fs/ext4/ext4_jbd2.h b/fs/ext4/ext4_jbd2.h index 6883c6be5b8d..b0bd792c58c5 100644 --- a/fs/ext4/ext4_jbd2.h +++ b/fs/ext4/ext4_jbd2.h @@ -162,7 +162,7 @@ int __ext4_handle_dirty_super(const char *where, unsigned int line, __ext4_handle_dirty_super(__func__, __LINE__, (handle), (sb)) handle_t *ext4_journal_start_sb(struct super_block *sb, int nblocks); -int __ext4_journal_stop(const char *where, handle_t *handle); +int __ext4_journal_stop(const char *where, unsigned int line, handle_t *handle); #define EXT4_NOJOURNAL_MAX_REF_COUNT ((unsigned long) 4096) @@ -215,7 +215,7 @@ static inline handle_t *ext4_journal_start(struct inode *inode, int nblocks) } #define ext4_journal_stop(handle) \ - __ext4_journal_stop(__func__, (handle)) + __ext4_journal_stop(__func__, __LINE__, (handle)) static inline handle_t *ext4_journal_current_handle(void) { diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index 346de3daab79..2c01d7391f1e 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c @@ -401,9 +401,9 @@ static int ext4_valid_extent_entries(struct inode *inode, return 1; } -static int __ext4_ext_check(const char *function, struct inode *inode, - struct ext4_extent_header *eh, - int depth) +static int __ext4_ext_check(const char *function, unsigned int line, + struct inode *inode, struct ext4_extent_header *eh, + int depth) { const char *error_msg; int max = 0; @@ -436,7 +436,7 @@ static int __ext4_ext_check(const char *function, struct inode *inode, return 0; corrupted: - ext4_error_inode(function, inode, + ext4_error_inode(inode, function, line, 0, "bad header/extent: %s - magic %x, " "entries %u, max %u(%u), depth %u(%u)", error_msg, le16_to_cpu(eh->eh_magic), @@ -447,7 +447,7 @@ corrupted: } #define ext4_ext_check(inode, eh, depth) \ - __ext4_ext_check(__func__, inode, eh, depth) + __ext4_ext_check(__func__, __LINE__, inode, eh, depth) int ext4_ext_check_inode(struct inode *inode) { diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 64baadb4956d..69ea663ef03e 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -337,7 +337,8 @@ static int ext4_block_to_path(struct inode *inode, return n; } -static int __ext4_check_blockref(const char *function, struct inode *inode, +static int __ext4_check_blockref(const char *function, unsigned int line, + struct inode *inode, __le32 *p, unsigned int max) { __le32 *bref = p; @@ -348,8 +349,8 @@ static int __ext4_check_blockref(const char *function, struct inode *inode, if (blk && unlikely(!ext4_data_block_valid(EXT4_SB(inode->i_sb), blk, 1))) { - ext4_error_inode(function, inode, - "invalid block reference %u", blk); + ext4_error_inode(inode, function, line, blk, + "invalid block"); return -EIO; } } @@ -358,11 +359,13 @@ static int __ext4_check_blockref(const char *function, struct inode *inode, #define ext4_check_indirect_blockref(inode, bh) \ - __ext4_check_blockref(__func__, inode, (__le32 *)(bh)->b_data, \ + __ext4_check_blockref(__func__, __LINE__, inode, \ + (__le32 *)(bh)->b_data, \ EXT4_ADDR_PER_BLOCK((inode)->i_sb)) #define ext4_check_inode_blockref(inode) \ - __ext4_check_blockref(__func__, inode, EXT4_I(inode)->i_data, \ + __ext4_check_blockref(__func__, __LINE__, inode, \ + EXT4_I(inode)->i_data, \ EXT4_NDIR_BLOCKS) /** @@ -1129,21 +1132,22 @@ void ext4_da_update_reserve_space(struct inode *inode, } static int __check_block_validity(struct inode *inode, const char *func, - struct ext4_map_blocks *map) + unsigned int line, + struct ext4_map_blocks *map) { if (!ext4_data_block_valid(EXT4_SB(inode->i_sb), map->m_pblk, map->m_len)) { - ext4_error_inode(func, inode, - "lblock %lu mapped to illegal pblock %llu " - "(length %d)", (unsigned long) map->m_lblk, - map->m_pblk, map->m_len); + ext4_error_inode(inode, func, line, map->m_pblk, + "lblock %lu mapped to illegal pblock " + "(length %d)", (unsigned long) map->m_lblk, + map->m_len); return -EIO; } return 0; } #define check_block_validity(inode, map) \ - __check_block_validity((inode), __func__, (map)) + __check_block_validity((inode), __func__, __LINE__, (map)) /* * Return the number of contiguous dirty pages in a given inode @@ -4471,9 +4475,8 @@ static void ext4_free_branches(handle_t *handle, struct inode *inode, * (should be rare). */ if (!bh) { - EXT4_ERROR_INODE(inode, - "Read failure block=%llu", - (unsigned long long) nr); + EXT4_ERROR_INODE_BLOCK(inode, nr, + "Read failure"); continue; } @@ -4788,8 +4791,8 @@ static int __ext4_get_inode_loc(struct inode *inode, bh = sb_getblk(sb, block); if (!bh) { - EXT4_ERROR_INODE(inode, "unable to read inode block - " - "block %llu", block); + EXT4_ERROR_INODE_BLOCK(inode, block, + "unable to read itable block"); return -EIO; } if (!buffer_uptodate(bh)) { @@ -4887,8 +4890,8 @@ make_io: submit_bh(READ_META, bh); wait_on_buffer(bh); if (!buffer_uptodate(bh)) { - EXT4_ERROR_INODE(inode, "unable to read inode " - "block %llu", block); + EXT4_ERROR_INODE_BLOCK(inode, block, + "unable to read itable block"); brelse(bh); return -EIO; } @@ -5389,9 +5392,8 @@ int ext4_write_inode(struct inode *inode, struct writeback_control *wbc) if (wbc->sync_mode == WB_SYNC_ALL) sync_dirty_buffer(iloc.bh); if (buffer_req(iloc.bh) && !buffer_uptodate(iloc.bh)) { - EXT4_ERROR_INODE(inode, - "IO error syncing inode (block=%llu)", - (unsigned long long) iloc.bh->b_blocknr); + EXT4_ERROR_INODE_BLOCK(inode, iloc.bh->b_blocknr, + "IO error syncing inode"); err = -EIO; } brelse(iloc.bh); diff --git a/fs/ext4/move_extent.c b/fs/ext4/move_extent.c index 52abfa12762a..5f1ed9fc913c 100644 --- a/fs/ext4/move_extent.c +++ b/fs/ext4/move_extent.c @@ -148,17 +148,17 @@ mext_next_extent(struct inode *inode, struct ext4_ext_path *path, */ static int mext_check_null_inode(struct inode *inode1, struct inode *inode2, - const char *function) + const char *function, unsigned int line) { int ret = 0; if (inode1 == NULL) { - __ext4_error(inode2->i_sb, function, + __ext4_error(inode2->i_sb, function, line, "Both inodes should not be NULL: " "inode1 NULL inode2 %lu", inode2->i_ino); ret = -EIO; } else if (inode2 == NULL) { - __ext4_error(inode1->i_sb, function, + __ext4_error(inode1->i_sb, function, line, "Both inodes should not be NULL: " "inode1 %lu inode2 NULL", inode1->i_ino); ret = -EIO; @@ -1084,7 +1084,7 @@ mext_inode_double_lock(struct inode *inode1, struct inode *inode2) BUG_ON(inode1 == NULL && inode2 == NULL); - ret = mext_check_null_inode(inode1, inode2, __func__); + ret = mext_check_null_inode(inode1, inode2, __func__, __LINE__); if (ret < 0) goto out; @@ -1121,7 +1121,7 @@ mext_inode_double_unlock(struct inode *inode1, struct inode *inode2) BUG_ON(inode1 == NULL && inode2 == NULL); - ret = mext_check_null_inode(inode1, inode2, __func__); + ret = mext_check_null_inode(inode1, inode2, __func__, __LINE__); if (ret < 0) goto out; diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 87db5ecfccb4..bcf74b31d014 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -262,7 +262,7 @@ handle_t *ext4_journal_start_sb(struct super_block *sb, int nblocks) * that sync() will call the filesystem's write_super callback if * appropriate. */ -int __ext4_journal_stop(const char *where, handle_t *handle) +int __ext4_journal_stop(const char *where, unsigned int line, handle_t *handle) { struct super_block *sb; int err; @@ -279,7 +279,7 @@ int __ext4_journal_stop(const char *where, handle_t *handle) if (!err) err = rc; if (err) - __ext4_std_error(sb, where, err); + __ext4_std_error(sb, where, line, err); return err; } @@ -350,12 +350,13 @@ static void ext4_handle_error(struct super_block *sb) } void __ext4_error(struct super_block *sb, const char *function, - const char *fmt, ...) + unsigned int line, const char *fmt, ...) { va_list args; va_start(args, fmt); - printk(KERN_CRIT "EXT4-fs error (device %s): %s: ", sb->s_id, function); + printk(KERN_CRIT "EXT4-fs error (device %s): %s:%d: comm %s: ", + sb->s_id, function, line, current->comm); vprintk(fmt, args); printk("\n"); va_end(args); @@ -363,14 +364,18 @@ void __ext4_error(struct super_block *sb, const char *function, ext4_handle_error(sb); } -void ext4_error_inode(const char *function, struct inode *inode, +void ext4_error_inode(struct inode *inode, const char *function, + unsigned int line, ext4_fsblk_t block, const char *fmt, ...) { va_list args; va_start(args, fmt); - printk(KERN_CRIT "EXT4-fs error (device %s): %s: inode #%lu: (comm %s) ", - inode->i_sb->s_id, function, inode->i_ino, current->comm); + printk(KERN_CRIT "EXT4-fs error (device %s): %s:%d: inode #%lu: ", + inode->i_sb->s_id, function, line, inode->i_ino); + if (block) + printk("block %llu: ", block); + printk("comm %s: ", current->comm); vprintk(fmt, args); printk("\n"); va_end(args); @@ -378,8 +383,8 @@ void ext4_error_inode(const char *function, struct inode *inode, ext4_handle_error(inode->i_sb); } -void ext4_error_file(const char *function, struct file *file, - const char *fmt, ...) +void ext4_error_file(struct file *file, const char *function, + unsigned int line, const char *fmt, ...) { va_list args; struct inode *inode = file->f_dentry->d_inode; @@ -390,8 +395,10 @@ void ext4_error_file(const char *function, struct file *file, if (!path) path = "(unknown)"; printk(KERN_CRIT - "EXT4-fs error (device %s): %s: inode #%lu (comm %s path %s): ", - inode->i_sb->s_id, function, inode->i_ino, current->comm, path); + "EXT4-fs error (device %s): %s:%d: inode #%lu " + "(comm %s path %s): ", + inode->i_sb->s_id, function, line, inode->i_ino, + current->comm, path); vprintk(fmt, args); printk("\n"); va_end(args); @@ -436,7 +443,8 @@ static const char *ext4_decode_error(struct super_block *sb, int errno, /* __ext4_std_error decodes expected errors from journaling functions * automatically and invokes the appropriate error response. */ -void __ext4_std_error(struct super_block *sb, const char *function, int errno) +void __ext4_std_error(struct super_block *sb, const char *function, + unsigned int line, int errno) { char nbuf[16]; const char *errstr; @@ -449,8 +457,8 @@ void __ext4_std_error(struct super_block *sb, const char *function, int errno) return; errstr = ext4_decode_error(sb, errno, nbuf); - printk(KERN_CRIT "EXT4-fs error (device %s) in %s: %s\n", - sb->s_id, function, errstr); + printk(KERN_CRIT "EXT4-fs error (device %s) in %s:%d: %s\n", + sb->s_id, function, line, errstr); ext4_handle_error(sb); } @@ -466,12 +474,13 @@ void __ext4_std_error(struct super_block *sb, const char *function, int errno) */ void __ext4_abort(struct super_block *sb, const char *function, - const char *fmt, ...) + unsigned int line, const char *fmt, ...) { va_list args; va_start(args, fmt); - printk(KERN_CRIT "EXT4-fs error (device %s): %s: ", sb->s_id, function); + printk(KERN_CRIT "EXT4-fs error (device %s): %s:%d: ", sb->s_id, + function, line); vprintk(fmt, args); printk("\n"); va_end(args); @@ -503,13 +512,13 @@ void ext4_msg (struct super_block * sb, const char *prefix, } void __ext4_warning(struct super_block *sb, const char *function, - const char *fmt, ...) + unsigned int line, const char *fmt, ...) { va_list args; va_start(args, fmt); - printk(KERN_WARNING "EXT4-fs warning (device %s): %s: ", - sb->s_id, function); + printk(KERN_WARNING "EXT4-fs warning (device %s): %s:%d: ", + sb->s_id, function, line); vprintk(fmt, args); printk("\n"); va_end(args); -- cgit v1.2.3 From 1c13d5c0872870cca3e612aa045d492ead9ab004 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Tue, 27 Jul 2010 11:56:03 -0400 Subject: ext4: Save error information to the superblock for analysis Save number of file system errors, and the time function name, line number, block number, and inode number of the first and most recent errors reported on the file system in the superblock. Signed-off-by: "Theodore Ts'o" --- fs/ext4/block_validity.c | 8 +++-- fs/ext4/ext4.h | 17 +++++++++- fs/ext4/ext4_jbd2.c | 5 +++ fs/ext4/inode.c | 2 ++ fs/ext4/super.c | 80 ++++++++++++++++++++++++++++++++++++------------ 5 files changed, 90 insertions(+), 22 deletions(-) (limited to 'fs') diff --git a/fs/ext4/block_validity.c b/fs/ext4/block_validity.c index 5b6973fbf1bd..3db5084db9bd 100644 --- a/fs/ext4/block_validity.c +++ b/fs/ext4/block_validity.c @@ -229,16 +229,20 @@ int ext4_data_block_valid(struct ext4_sb_info *sbi, ext4_fsblk_t start_blk, if ((start_blk <= le32_to_cpu(sbi->s_es->s_first_data_block)) || (start_blk + count < start_blk) || - (start_blk + count > ext4_blocks_count(sbi->s_es))) + (start_blk + count > ext4_blocks_count(sbi->s_es))) { + sbi->s_es->s_last_error_block = cpu_to_le64(start_blk); return 0; + } while (n) { entry = rb_entry(n, struct ext4_system_zone, node); if (start_blk + count - 1 < entry->start_blk) n = n->rb_left; else if (start_blk >= (entry->start_blk + entry->count)) n = n->rb_right; - else + else { + sbi->s_es->s_last_error_block = cpu_to_le64(start_blk); return 0; + } } return 1; } diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 088938148f5c..6b96125e7255 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -1011,9 +1011,24 @@ struct ext4_super_block { snapshot's future use */ __le32 s_snapshot_list; /* inode number of the head of the on-disk snapshot list */ - __u32 s_reserved[155]; /* Padding to the end of the block */ +#define EXT4_S_ERR_START offsetof(struct ext4_super_block, s_error_count) + __le32 s_error_count; /* number of fs errors */ + __le32 s_first_error_time; /* first time an error happened */ + __le32 s_first_error_ino; /* inode involved in first error */ + __le64 s_first_error_block; /* block involved of first error */ + __u8 s_first_error_func[32]; /* function where the error happened */ + __le32 s_first_error_line; /* line number where error happened */ + __le32 s_last_error_time; /* most recent time of an error */ + __le32 s_last_error_ino; /* inode involved in last error */ + __le32 s_last_error_line; /* line number where error happened */ + __le64 s_last_error_block; /* block involved of last error */ + __u8 s_last_error_func[32]; /* function where the error happened */ +#define EXT4_S_ERR_END offsetof(struct ext4_super_block, s_reserved) + __le32 s_reserved[128]; /* Padding to the end of the block */ }; +#define EXT4_S_ERR_LEN (EXT4_S_ERR_END - EXT4_S_ERR_START) + #ifdef __KERNEL__ /* diff --git a/fs/ext4/ext4_jbd2.c b/fs/ext4/ext4_jbd2.c index 23425cd68daa..6e272ef6ba96 100644 --- a/fs/ext4/ext4_jbd2.c +++ b/fs/ext4/ext4_jbd2.c @@ -134,6 +134,11 @@ int __ext4_handle_dirty_metadata(const char *where, unsigned int line, if (inode && inode_needs_sync(inode)) { sync_dirty_buffer(bh); if (buffer_req(bh) && !buffer_uptodate(bh)) { + struct ext4_super_block *es; + + es = EXT4_SB(inode->i_sb)->s_es; + es->s_last_error_block = + cpu_to_le64(bh->b_blocknr); ext4_error_inode(inode, where, line, bh->b_blocknr, "IO error syncing itable block"); diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 69ea663ef03e..755ba8682233 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -341,6 +341,7 @@ static int __ext4_check_blockref(const char *function, unsigned int line, struct inode *inode, __le32 *p, unsigned int max) { + struct ext4_super_block *es = EXT4_SB(inode->i_sb)->s_es; __le32 *bref = p; unsigned int blk; @@ -349,6 +350,7 @@ static int __ext4_check_blockref(const char *function, unsigned int line, if (blk && unlikely(!ext4_data_block_valid(EXT4_SB(inode->i_sb), blk, 1))) { + es->s_last_error_block = cpu_to_le64(blk); ext4_error_inode(inode, function, line, blk, "invalid block"); return -EIO; diff --git a/fs/ext4/super.c b/fs/ext4/super.c index bcf74b31d014..a94d3f56898f 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -307,6 +307,35 @@ void ext4_journal_abort_handle(const char *caller, unsigned int line, jbd2_journal_abort_handle(handle); } +static void __save_error_info(struct super_block *sb, const char *func, + unsigned int line) +{ + struct ext4_super_block *es = EXT4_SB(sb)->s_es; + + EXT4_SB(sb)->s_mount_state |= EXT4_ERROR_FS; + es->s_state |= cpu_to_le16(EXT4_ERROR_FS); + es->s_last_error_time = cpu_to_le32(get_seconds()); + strncpy(es->s_last_error_func, func, sizeof(es->s_last_error_func)); + es->s_last_error_line = cpu_to_le32(line); + if (!es->s_first_error_time) { + es->s_first_error_time = es->s_last_error_time; + strncpy(es->s_first_error_func, func, + sizeof(es->s_first_error_func)); + es->s_first_error_line = cpu_to_le32(line); + es->s_first_error_ino = es->s_last_error_ino; + es->s_first_error_block = es->s_last_error_block; + } + es->s_error_count = cpu_to_le32(le32_to_cpu(es->s_error_count) + 1); +} + +static void save_error_info(struct super_block *sb, const char *func, + unsigned int line) +{ + __save_error_info(sb, func, line); + ext4_commit_super(sb, 1); +} + + /* Deal with the reporting of failure conditions on a filesystem such as * inconsistencies detected or read IO failures. * @@ -324,11 +353,6 @@ void ext4_journal_abort_handle(const char *caller, unsigned int line, static void ext4_handle_error(struct super_block *sb) { - struct ext4_super_block *es = EXT4_SB(sb)->s_es; - - EXT4_SB(sb)->s_mount_state |= EXT4_ERROR_FS; - es->s_state |= cpu_to_le16(EXT4_ERROR_FS); - if (sb->s_flags & MS_RDONLY) return; @@ -343,7 +367,6 @@ static void ext4_handle_error(struct super_block *sb) ext4_msg(sb, KERN_CRIT, "Remounting filesystem read-only"); sb->s_flags |= MS_RDONLY; } - ext4_commit_super(sb, 1); if (test_opt(sb, ERRORS_PANIC)) panic("EXT4-fs (device %s): panic forced after error\n", sb->s_id); @@ -369,7 +392,11 @@ void ext4_error_inode(struct inode *inode, const char *function, const char *fmt, ...) { va_list args; + struct ext4_super_block *es = EXT4_SB(inode->i_sb)->s_es; + es->s_last_error_ino = cpu_to_le32(inode->i_ino); + es->s_last_error_block = cpu_to_le64(block); + save_error_info(inode->i_sb, function, line); va_start(args, fmt); printk(KERN_CRIT "EXT4-fs error (device %s): %s:%d: inode #%lu: ", inode->i_sb->s_id, function, line, inode->i_ino); @@ -387,9 +414,13 @@ void ext4_error_file(struct file *file, const char *function, unsigned int line, const char *fmt, ...) { va_list args; + struct ext4_super_block *es; struct inode *inode = file->f_dentry->d_inode; char pathname[80], *path; + es = EXT4_SB(inode->i_sb)->s_es; + es->s_last_error_ino = cpu_to_le32(inode->i_ino); + save_error_info(inode->i_sb, function, line); va_start(args, fmt); path = d_path(&(file->f_path), pathname, sizeof(pathname)); if (!path) @@ -459,6 +490,7 @@ void __ext4_std_error(struct super_block *sb, const char *function, errstr = ext4_decode_error(sb, errno, nbuf); printk(KERN_CRIT "EXT4-fs error (device %s) in %s:%d: %s\n", sb->s_id, function, line, errstr); + save_error_info(sb, function, line); ext4_handle_error(sb); } @@ -478,6 +510,7 @@ void __ext4_abort(struct super_block *sb, const char *function, { va_list args; + save_error_info(sb, function, line); va_start(args, fmt); printk(KERN_CRIT "EXT4-fs error (device %s): %s:%d: ", sb->s_id, function, line); @@ -485,18 +518,16 @@ void __ext4_abort(struct super_block *sb, const char *function, printk("\n"); va_end(args); + if ((sb->s_flags & MS_RDONLY) == 0) { + ext4_msg(sb, KERN_CRIT, "Remounting filesystem read-only"); + sb->s_flags |= MS_RDONLY; + EXT4_SB(sb)->s_mount_flags |= EXT4_MF_FS_ABORTED; + if (EXT4_SB(sb)->s_journal) + jbd2_journal_abort(EXT4_SB(sb)->s_journal, -EIO); + save_error_info(sb, function, line); + } if (test_opt(sb, ERRORS_PANIC)) panic("EXT4-fs panic from previous error\n"); - - if (sb->s_flags & MS_RDONLY) - return; - - ext4_msg(sb, KERN_CRIT, "Remounting filesystem read-only"); - EXT4_SB(sb)->s_mount_state |= EXT4_ERROR_FS; - sb->s_flags |= MS_RDONLY; - EXT4_SB(sb)->s_mount_flags |= EXT4_MF_FS_ABORTED; - if (EXT4_SB(sb)->s_journal) - jbd2_journal_abort(EXT4_SB(sb)->s_journal, -EIO); } void ext4_msg (struct super_block * sb, const char *prefix, @@ -534,6 +565,9 @@ __acquires(bitlock) va_list args; struct ext4_super_block *es = EXT4_SB(sb)->s_es; + es->s_last_error_ino = cpu_to_le32(ino); + es->s_last_error_block = cpu_to_le64(block); + __save_error_info(sb, function, line); va_start(args, fmt); printk(KERN_CRIT "EXT4-fs error (device %s): %s:%d: group %u", sb->s_id, function, line, grp); @@ -546,11 +580,10 @@ __acquires(bitlock) va_end(args); if (test_opt(sb, ERRORS_CONT)) { - EXT4_SB(sb)->s_mount_state |= EXT4_ERROR_FS; - es->s_state |= cpu_to_le16(EXT4_ERROR_FS); ext4_commit_super(sb, 0); return; } + ext4_unlock_group(sb, grp); ext4_handle_error(sb); /* @@ -3332,8 +3365,17 @@ static int ext4_load_journal(struct super_block *sb, if (!EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_RECOVER)) err = jbd2_journal_wipe(journal, !really_read_only); - if (!err) + if (!err) { + char *save = kmalloc(EXT4_S_ERR_LEN, GFP_KERNEL); + if (save) + memcpy(save, ((char *) es) + + EXT4_S_ERR_START, EXT4_S_ERR_LEN); err = jbd2_journal_load(journal); + if (save) + memcpy(((char *) es) + EXT4_S_ERR_START, + save, EXT4_S_ERR_LEN); + kfree(save); + } if (err) { ext4_msg(sb, KERN_ERR, "error loading journal"); -- cgit v1.2.3 From 66e61a9e9504f61b9a928c9055368c81da613a50 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Tue, 27 Jul 2010 11:56:04 -0400 Subject: ext4: Once a day, printk file system error information to dmesg This allows us to grab any file system error messages by scraping /var/log/messages. This will make it easy for us to do error analysis across the very large number of machines as we deploy ext4 across the fleet. Signed-off-by: "Theodore Ts'o" --- fs/ext4/ext4.h | 3 +++ fs/ext4/super.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) (limited to 'fs') diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 6b96125e7255..5d3d768d9503 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -1166,6 +1166,9 @@ struct ext4_sb_info { /* workqueue for dio unwritten */ struct workqueue_struct *dio_unwritten_wq; + + /* timer for periodic error stats printing */ + struct timer_list s_err_report; }; static inline struct ext4_sb_info *EXT4_SB(struct super_block *sb) diff --git a/fs/ext4/super.c b/fs/ext4/super.c index a94d3f56898f..ed00c14d7081 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -325,6 +325,12 @@ static void __save_error_info(struct super_block *sb, const char *func, es->s_first_error_ino = es->s_last_error_ino; es->s_first_error_block = es->s_last_error_block; } + /* + * Start the daily error reporting function if it hasn't been + * started already + */ + if (!es->s_error_count) + mod_timer(&EXT4_SB(sb)->s_err_report, jiffies + 24*60*60*HZ); es->s_error_count = cpu_to_le32(le32_to_cpu(es->s_error_count) + 1); } @@ -2480,6 +2486,53 @@ static int ext4_feature_set_ok(struct super_block *sb, int readonly) return 1; } +/* + * This function is called once a day if we have errors logged + * on the file system + */ +static void print_daily_error_info(unsigned long arg) +{ + struct super_block *sb = (struct super_block *) arg; + struct ext4_sb_info *sbi; + struct ext4_super_block *es; + + sbi = EXT4_SB(sb); + es = sbi->s_es; + + if (es->s_error_count) + ext4_msg(sb, KERN_NOTICE, "error count: %u", + le32_to_cpu(es->s_error_count)); + if (es->s_first_error_time) { + printk(KERN_NOTICE "EXT4-fs (%s): initial error at %u: %.*s:%d", + sb->s_id, le32_to_cpu(es->s_first_error_time), + (int) sizeof(es->s_first_error_func), + es->s_first_error_func, + le32_to_cpu(es->s_first_error_line)); + if (es->s_first_error_ino) + printk(": inode %u", + le32_to_cpu(es->s_first_error_ino)); + if (es->s_first_error_block) + printk(": block %llu", (unsigned long long) + le64_to_cpu(es->s_first_error_block)); + printk("\n"); + } + if (es->s_last_error_time) { + printk(KERN_NOTICE "EXT4-fs (%s): last error at %u: %.*s:%d", + sb->s_id, le32_to_cpu(es->s_last_error_time), + (int) sizeof(es->s_last_error_func), + es->s_last_error_func, + le32_to_cpu(es->s_last_error_line)); + if (es->s_last_error_ino) + printk(": inode %u", + le32_to_cpu(es->s_last_error_ino)); + if (es->s_last_error_block) + printk(": block %llu", (unsigned long long) + le64_to_cpu(es->s_last_error_block)); + printk("\n"); + } + mod_timer(&sbi->s_err_report, jiffies + 24*60*60*HZ); /* Once a day */ +} + static int ext4_fill_super(struct super_block *sb, void *data, int silent) __releases(kernel_lock) __acquires(kernel_lock) @@ -3083,6 +3136,12 @@ no_journal: ext4_msg(sb, KERN_INFO, "mounted filesystem with%s. " "Opts: %s", descr, orig_data); + init_timer(&sbi->s_err_report); + sbi->s_err_report.function = print_daily_error_info; + sbi->s_err_report.data = (unsigned long) sb; + if (es->s_error_count) + mod_timer(&sbi->s_err_report, jiffies + 300*HZ); /* 5 minutes */ + lock_kernel(); kfree(orig_data); return 0; -- cgit v1.2.3 From 89eeddf03327e19cfcbb18efa98e5470e2f5c563 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Tue, 27 Jul 2010 11:56:04 -0400 Subject: ext4: Define s_jnl_backup_type in superblock This has been in use by e2fsprogs for a while; define it to keep the super block fields in sync. Signed-off-by: "Theodore Ts'o" --- fs/ext4/ext4.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 5d3d768d9503..4c7d4727d6ba 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -984,7 +984,7 @@ struct ext4_super_block { __le32 s_last_orphan; /* start of list of inodes to delete */ __le32 s_hash_seed[4]; /* HTREE hash seed */ __u8 s_def_hash_version; /* Default hash version to use */ - __u8 s_reserved_char_pad; + __u8 s_jnl_backup_type; __le16 s_desc_size; /* size of group descriptor */ /*100*/ __le32 s_default_mount_opts; __le32 s_first_meta_bg; /* First metablock block group */ @@ -1002,7 +1002,7 @@ struct ext4_super_block { __le64 s_mmp_block; /* Block for multi-mount protection */ __le32 s_raid_stripe_width; /* blocks on all data disks (N*stride)*/ __u8 s_log_groups_per_flex; /* FLEX_BG group size */ - __u8 s_reserved_char_pad2; + __u8 s_reserved_char_pad; __le16 s_reserved_pad; __le64 s_kbytes_written; /* nr of lifetime kilobytes written */ __le32 s_snapshot_inum; /* Inode number of active snapshot */ -- cgit v1.2.3 From e5880d76aea443b04e07da19830da0f6f7494eef Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Tue, 27 Jul 2010 11:56:04 -0400 Subject: ext4: fix potential NULL dereference while tracing The allocation_context pointer can be NULL. Signed-off-by: "Theodore Ts'o" --- fs/ext4/mballoc.c | 4 ++-- include/trace/events/ext4.h | 20 ++++++++++++-------- 2 files changed, 14 insertions(+), 10 deletions(-) (limited to 'fs') diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c index 3dfad95f0f98..8b3b9344a595 100644 --- a/fs/ext4/mballoc.c +++ b/fs/ext4/mballoc.c @@ -3575,7 +3575,7 @@ ext4_mb_release_inode_pa(struct ext4_buddy *e4b, struct buffer_head *bitmap_bh, trace_ext4_mballoc_discard(ac); } - trace_ext4_mb_release_inode_pa(ac, pa, grp_blk_start + bit, + trace_ext4_mb_release_inode_pa(sb, ac, pa, grp_blk_start + bit, next - bit); mb_free_blocks(pa->pa_inode, e4b, bit, next - bit); bit = next + 1; @@ -3606,7 +3606,7 @@ ext4_mb_release_group_pa(struct ext4_buddy *e4b, ext4_group_t group; ext4_grpblk_t bit; - trace_ext4_mb_release_group_pa(ac, pa); + trace_ext4_mb_release_group_pa(sb, ac, pa); BUG_ON(pa->pa_deleted == 0); ext4_get_group_no_and_offset(sb, pa->pa_pstart, &group, &bit); BUG_ON(group != e4b->bd_group && pa->pa_len != 0); diff --git a/include/trace/events/ext4.h b/include/trace/events/ext4.h index f3865c7b4166..01e9e0076a92 100644 --- a/include/trace/events/ext4.h +++ b/include/trace/events/ext4.h @@ -395,11 +395,12 @@ DEFINE_EVENT(ext4__mb_new_pa, ext4_mb_new_group_pa, ); TRACE_EVENT(ext4_mb_release_inode_pa, - TP_PROTO(struct ext4_allocation_context *ac, + TP_PROTO(struct super_block *sb, + struct ext4_allocation_context *ac, struct ext4_prealloc_space *pa, unsigned long long block, unsigned int count), - TP_ARGS(ac, pa, block, count), + TP_ARGS(sb, ac, pa, block, count), TP_STRUCT__entry( __field( dev_t, dev ) @@ -410,8 +411,9 @@ TRACE_EVENT(ext4_mb_release_inode_pa, ), TP_fast_assign( - __entry->dev = ac->ac_sb->s_dev; - __entry->ino = ac->ac_inode->i_ino; + __entry->dev = sb->s_dev; + __entry->ino = (ac && ac->ac_inode) ? + ac->ac_inode->i_ino : 0; __entry->block = block; __entry->count = count; ), @@ -422,10 +424,11 @@ TRACE_EVENT(ext4_mb_release_inode_pa, ); TRACE_EVENT(ext4_mb_release_group_pa, - TP_PROTO(struct ext4_allocation_context *ac, + TP_PROTO(struct super_block *sb, + struct ext4_allocation_context *ac, struct ext4_prealloc_space *pa), - TP_ARGS(ac, pa), + TP_ARGS(sb, ac, pa), TP_STRUCT__entry( __field( dev_t, dev ) @@ -436,8 +439,9 @@ TRACE_EVENT(ext4_mb_release_group_pa, ), TP_fast_assign( - __entry->dev = ac->ac_sb->s_dev; - __entry->ino = ac->ac_inode->i_ino; + __entry->dev = sb->s_dev; + __entry->ino = (ac && ac->ac_inode) ? + ac->ac_inode->i_ino : 0; __entry->pa_pstart = pa->pa_pstart; __entry->pa_len = pa->pa_len; ), -- cgit v1.2.3 From a271fe8527fe9637bdd82c97123b1356940dd84b Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Tue, 27 Jul 2010 11:56:04 -0400 Subject: ext4: Remove unnecessary casts of private_data Signed-off-by: Joe Perches Signed-off-by: "Theodore Ts'o" --- fs/ext4/dir.c | 2 +- fs/ext4/mballoc.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/ext4/dir.c b/fs/ext4/dir.c index 62e8af04ed1e..374510f72baa 100644 --- a/fs/ext4/dir.c +++ b/fs/ext4/dir.c @@ -344,7 +344,7 @@ int ext4_htree_store_dirent(struct file *dir_file, __u32 hash, struct dir_private_info *info; int len; - info = (struct dir_private_info *) dir_file->private_data; + info = dir_file->private_data; p = &info->root.rb_node; /* Create and allocate the fname structure */ diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c index 8b3b9344a595..84185d215004 100644 --- a/fs/ext4/mballoc.c +++ b/fs/ext4/mballoc.c @@ -2219,7 +2219,7 @@ static int ext4_mb_seq_groups_open(struct inode *inode, struct file *file) rc = seq_open(file, &ext4_mb_seq_groups_ops); if (rc == 0) { - struct seq_file *m = (struct seq_file *)file->private_data; + struct seq_file *m = file->private_data; m->private = sb; } return rc; -- cgit v1.2.3 From 40389687382bf0ae71458e7c0f828137a438a956 Mon Sep 17 00:00:00 2001 From: Amir G Date: Tue, 27 Jul 2010 11:56:05 -0400 Subject: ext4: Fix block bitmap inconsistencies after a crash when deleting files We have experienced bitmap inconsistencies after crash during file delete under heavy load. The crash is not file system related and I the following patch in ext4_free_branches() fixes the recovery problem. If the transaction is restarted and there is a crash before the new transaction is committed, then after recovery, the blocks that this indirect block points to have been freed, but the indirect block itself has not been freed and may still point to some of the free blocks (because of the ext4_forget()). So ext4_forget() should be called inside ext4_free_blocks() to avoid this problem. Signed-off-by: Amir Goldstein Signed-off-by: "Theodore Ts'o" --- fs/ext4/inode.c | 35 +++++++++++++---------------------- 1 file changed, 13 insertions(+), 22 deletions(-) (limited to 'fs') diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 755ba8682233..699d1d01c5df 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -4489,27 +4489,6 @@ static void ext4_free_branches(handle_t *handle, struct inode *inode, (__le32 *) bh->b_data + addr_per_block, depth); - /* - * We've probably journalled the indirect block several - * times during the truncate. But it's no longer - * needed and we now drop it from the transaction via - * jbd2_journal_revoke(). - * - * That's easy if it's exclusively part of this - * transaction. But if it's part of the committing - * transaction then jbd2_journal_forget() will simply - * brelse() it. That means that if the underlying - * block is reallocated in ext4_get_block(), - * unmap_underlying_metadata() will find this block - * and will try to get rid of it. damn, damn. - * - * If this block has already been committed to the - * journal, a revoke record will be written. And - * revoke records must be emitted *before* clearing - * this block's bit in the bitmaps. - */ - ext4_forget(handle, 1, inode, bh, bh->b_blocknr); - /* * Everything below this this pointer has been * released. Now let this top-of-subtree go. @@ -4534,8 +4513,20 @@ static void ext4_free_branches(handle_t *handle, struct inode *inode, blocks_for_truncate(inode)); } + /* + * The forget flag here is critical because if + * we are journaling (and not doing data + * journaling), we have to make sure a revoke + * record is written to prevent the journal + * replay from overwriting the (former) + * indirect block if it gets reallocated as a + * data block. This must happen in the same + * transaction where the data blocks are + * actually freed. + */ ext4_free_blocks(handle, inode, 0, nr, 1, - EXT4_FREE_BLOCKS_METADATA); + EXT4_FREE_BLOCKS_METADATA| + EXT4_FREE_BLOCKS_FORGET); if (parent_bh) { /* -- cgit v1.2.3 From 47def82672b3ba4e7c5e9a4fe48a556f8684d0d6 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Tue, 27 Jul 2010 11:56:05 -0400 Subject: jbd2: Remove __GFP_NOFAIL from jbd2 layer __GFP_NOFAIL is going away, so add our own retry loop. Also add jbd2__journal_start() and jbd2__journal_restart() which take a gfp mask, so that file systems can optionally (re)start transaction handles using GFP_KERNEL. If they do this, then they need to be prepared to handle receiving an PTR_ERR(-ENOMEM) error, and be ready to reflect that error up to userspace. Signed-off-by: "Theodore Ts'o" --- fs/jbd2/journal.c | 15 ++++++++++--- fs/jbd2/transaction.c | 61 +++++++++++++++++++++++++++++++++++---------------- include/linux/jbd2.h | 4 +++- 3 files changed, 57 insertions(+), 23 deletions(-) (limited to 'fs') diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c index f7bf15787d68..a79d3345b55a 100644 --- a/fs/jbd2/journal.c +++ b/fs/jbd2/journal.c @@ -41,6 +41,7 @@ #include #include #include +#include #define CREATE_TRACE_POINTS #include @@ -48,8 +49,6 @@ #include #include -EXPORT_SYMBOL(jbd2_journal_start); -EXPORT_SYMBOL(jbd2_journal_restart); EXPORT_SYMBOL(jbd2_journal_extend); EXPORT_SYMBOL(jbd2_journal_stop); EXPORT_SYMBOL(jbd2_journal_lock_updates); @@ -311,7 +310,17 @@ int jbd2_journal_write_metadata_buffer(transaction_t *transaction, */ J_ASSERT_BH(bh_in, buffer_jbddirty(bh_in)); - new_bh = alloc_buffer_head(GFP_NOFS|__GFP_NOFAIL); +retry_alloc: + new_bh = alloc_buffer_head(GFP_NOFS); + if (!new_bh) { + /* + * Failure is not an option, but __GFP_NOFAIL is going + * away; so we retry ourselves here. + */ + congestion_wait(BLK_RW_ASYNC, HZ/50); + goto retry_alloc; + } + /* keep subsequent assertions sane */ new_bh->b_state = 0; init_buffer(new_bh, NULL, NULL); diff --git a/fs/jbd2/transaction.c b/fs/jbd2/transaction.c index e214d68620ac..001e95fb0fe1 100644 --- a/fs/jbd2/transaction.c +++ b/fs/jbd2/transaction.c @@ -26,6 +26,8 @@ #include #include #include +#include +#include static void __jbd2_journal_temp_unlink_buffer(struct journal_head *jh); @@ -83,30 +85,38 @@ jbd2_get_transaction(journal_t *journal, transaction_t *transaction) * transaction's buffer credits. */ -static int start_this_handle(journal_t *journal, handle_t *handle) +static int start_this_handle(journal_t *journal, handle_t *handle, + int gfp_mask) { transaction_t *transaction; int needed; int nblocks = handle->h_buffer_credits; transaction_t *new_transaction = NULL; - int ret = 0; unsigned long ts = jiffies; if (nblocks > journal->j_max_transaction_buffers) { printk(KERN_ERR "JBD: %s wants too many credits (%d > %d)\n", current->comm, nblocks, journal->j_max_transaction_buffers); - ret = -ENOSPC; - goto out; + return -ENOSPC; } alloc_transaction: if (!journal->j_running_transaction) { - new_transaction = kzalloc(sizeof(*new_transaction), - GFP_NOFS|__GFP_NOFAIL); + new_transaction = kzalloc(sizeof(*new_transaction), gfp_mask); if (!new_transaction) { - ret = -ENOMEM; - goto out; + /* + * If __GFP_FS is not present, then we may be + * being called from inside the fs writeback + * layer, so we MUST NOT fail. Since + * __GFP_NOFAIL is going away, we will arrange + * to retry the allocation ourselves. + */ + if ((gfp_mask & __GFP_FS) == 0) { + congestion_wait(BLK_RW_ASYNC, HZ/50); + goto alloc_transaction; + } + return -ENOMEM; } } @@ -123,8 +133,8 @@ repeat_locked: if (is_journal_aborted(journal) || (journal->j_errno != 0 && !(journal->j_flags & JBD2_ACK_ERR))) { spin_unlock(&journal->j_state_lock); - ret = -EROFS; - goto out; + kfree(new_transaction); + return -EROFS; } /* Wait on the journal's transaction barrier if necessary */ @@ -240,10 +250,8 @@ repeat_locked: spin_unlock(&journal->j_state_lock); lock_map_acquire(&handle->h_lockdep_map); -out: - if (unlikely(new_transaction)) /* It's usually NULL */ - kfree(new_transaction); - return ret; + kfree(new_transaction); + return 0; } static struct lock_class_key jbd2_handle_key; @@ -278,7 +286,7 @@ static handle_t *new_handle(int nblocks) * * Return a pointer to a newly allocated handle, or NULL on failure */ -handle_t *jbd2_journal_start(journal_t *journal, int nblocks) +handle_t *jbd2__journal_start(journal_t *journal, int nblocks, int gfp_mask) { handle_t *handle = journal_current_handle(); int err; @@ -298,7 +306,7 @@ handle_t *jbd2_journal_start(journal_t *journal, int nblocks) current->journal_info = handle; - err = start_this_handle(journal, handle); + err = start_this_handle(journal, handle, gfp_mask); if (err < 0) { jbd2_free_handle(handle); current->journal_info = NULL; @@ -308,6 +316,15 @@ handle_t *jbd2_journal_start(journal_t *journal, int nblocks) out: return handle; } +EXPORT_SYMBOL(jbd2__journal_start); + + +handle_t *jbd2_journal_start(journal_t *journal, int nblocks) +{ + return jbd2__journal_start(journal, nblocks, GFP_NOFS); +} +EXPORT_SYMBOL(jbd2_journal_start); + /** * int jbd2_journal_extend() - extend buffer credits. @@ -394,8 +411,7 @@ out: * transaction capabable of guaranteeing the requested number of * credits. */ - -int jbd2_journal_restart(handle_t *handle, int nblocks) +int jbd2__journal_restart(handle_t *handle, int nblocks, int gfp_mask) { transaction_t *transaction = handle->h_transaction; journal_t *journal = transaction->t_journal; @@ -428,10 +444,17 @@ int jbd2_journal_restart(handle_t *handle, int nblocks) lock_map_release(&handle->h_lockdep_map); handle->h_buffer_credits = nblocks; - ret = start_this_handle(journal, handle); + ret = start_this_handle(journal, handle, gfp_mask); return ret; } +EXPORT_SYMBOL(jbd2__journal_restart); + +int jbd2_journal_restart(handle_t *handle, int nblocks) +{ + return jbd2__journal_restart(handle, nblocks, GFP_NOFS); +} +EXPORT_SYMBOL(jbd2_journal_restart); /** * void jbd2_journal_lock_updates () - establish a transaction barrier. diff --git a/include/linux/jbd2.h b/include/linux/jbd2.h index a4d2e9f7088a..5a72bc75b273 100644 --- a/include/linux/jbd2.h +++ b/include/linux/jbd2.h @@ -1081,7 +1081,9 @@ static inline handle_t *journal_current_handle(void) */ extern handle_t *jbd2_journal_start(journal_t *, int nblocks); -extern int jbd2_journal_restart (handle_t *, int nblocks); +extern handle_t *jbd2__journal_start(journal_t *, int nblocks, int gfp_mask); +extern int jbd2_journal_restart(handle_t *, int nblocks); +extern int jbd2__journal_restart(handle_t *, int nblocks, int gfp_mask); extern int jbd2_journal_extend (handle_t *, int nblocks); extern int jbd2_journal_get_write_access(handle_t *, struct buffer_head *); extern int jbd2_journal_get_create_access (handle_t *, struct buffer_head *); -- cgit v1.2.3 From 5c521830cf3dfcf7638d409d8e02ed21020c064f Mon Sep 17 00:00:00 2001 From: Jiaying Zhang Date: Tue, 27 Jul 2010 11:56:05 -0400 Subject: ext4: Support discard requests when running in no-journal mode Issue discard request in ext4_free_blocks() when ext4 has no journal and is mounted with discard option. Signed-off-by: Jiaying Zhang Signed-off-by: "Theodore Ts'o" --- fs/ext4/mballoc.c | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) (limited to 'fs') diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c index 84185d215004..5338b1ca64bb 100644 --- a/fs/ext4/mballoc.c +++ b/fs/ext4/mballoc.c @@ -2558,6 +2558,22 @@ int ext4_mb_release(struct super_block *sb) return 0; } +static inline void ext4_issue_discard(struct super_block *sb, + ext4_group_t block_group, ext4_grpblk_t block, int count) +{ + int ret; + ext4_fsblk_t discard_block; + + discard_block = block + ext4_group_first_block_no(sb, block_group); + trace_ext4_discard_blocks(sb, + (unsigned long long) discard_block, count); + ret = sb_issue_discard(sb, discard_block, count); + if (ret == EOPNOTSUPP) { + ext4_warning(sb, "discard not supported, disabling"); + clear_opt(EXT4_SB(sb)->s_mount_opt, DISCARD); + } +} + /* * This function is called by the jbd2 layer once the commit has finished, * so we know we can free the blocks that were released with that commit. @@ -2577,22 +2593,9 @@ static void release_blocks_on_commit(journal_t *journal, transaction_t *txn) mb_debug(1, "gonna free %u blocks in group %u (0x%p):", entry->count, entry->group, entry); - if (test_opt(sb, DISCARD)) { - int ret; - ext4_fsblk_t discard_block; - - discard_block = entry->start_blk + - ext4_group_first_block_no(sb, entry->group); - trace_ext4_discard_blocks(sb, - (unsigned long long)discard_block, - entry->count); - ret = sb_issue_discard(sb, discard_block, entry->count); - if (ret == EOPNOTSUPP) { - ext4_warning(sb, - "discard not supported, disabling"); - clear_opt(EXT4_SB(sb)->s_mount_opt, DISCARD); - } - } + if (test_opt(sb, DISCARD)) + ext4_issue_discard(sb, entry->group, + entry->start_blk, entry->count); err = ext4_mb_load_buddy(sb, entry->group, &e4b); /* we expect to find existing buddy because it's pinned */ @@ -4639,6 +4642,8 @@ do_more: mb_clear_bits(bitmap_bh->b_data, bit, count); mb_free_blocks(inode, &e4b, bit, count); ext4_mb_return_to_preallocation(inode, &e4b, block, count); + if (test_opt(sb, DISCARD)) + ext4_issue_discard(sb, block_group, bit, count); } ret = ext4_free_blks_count(sb, gdp) + count; -- cgit v1.2.3 From 552ef8024f909d9b3a7442d0ab0d48a22de24e9e Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 27 Jul 2010 11:56:06 -0400 Subject: direct-io: move aio_complete into ->end_io Filesystems with unwritten extent support must not complete an AIO request until the transaction to convert the extent has been commited. That means the aio_complete calls needs to be moved into the ->end_io callback so that the filesystem can control when to call it exactly. This makes a bit of a mess out of dio_complete and the ->end_io callback prototype even more complicated. Signed-off-by: Christoph Hellwig Reviewed-by: Jan Kara Signed-off-by: "Theodore Ts'o" --- fs/direct-io.c | 26 ++++++++++++++------------ fs/ext4/inode.c | 10 +++++++--- fs/ocfs2/aops.c | 7 ++++++- fs/xfs/linux-2.6/xfs_aops.c | 7 ++++++- fs/xfs/linux-2.6/xfs_aops.h | 2 ++ include/linux/fs.h | 3 ++- 6 files changed, 37 insertions(+), 18 deletions(-) (limited to 'fs') diff --git a/fs/direct-io.c b/fs/direct-io.c index 7600aacf531d..a10cb91cadea 100644 --- a/fs/direct-io.c +++ b/fs/direct-io.c @@ -218,7 +218,7 @@ static struct page *dio_get_page(struct dio *dio) * filesystems can use it to hold additional state between get_block calls and * dio_complete. */ -static int dio_complete(struct dio *dio, loff_t offset, int ret) +static int dio_complete(struct dio *dio, loff_t offset, int ret, bool is_async) { ssize_t transferred = 0; @@ -239,14 +239,6 @@ static int dio_complete(struct dio *dio, loff_t offset, int ret) transferred = dio->i_size - offset; } - if (dio->end_io && dio->result) - dio->end_io(dio->iocb, offset, transferred, - dio->map_bh.b_private); - - if (dio->flags & DIO_LOCKING) - /* lockdep: non-owner release */ - up_read_non_owner(&dio->inode->i_alloc_sem); - if (ret == 0) ret = dio->page_errors; if (ret == 0) @@ -254,6 +246,17 @@ static int dio_complete(struct dio *dio, loff_t offset, int ret) if (ret == 0) ret = transferred; + if (dio->end_io && dio->result) { + dio->end_io(dio->iocb, offset, transferred, + dio->map_bh.b_private, ret, is_async); + } else if (is_async) { + aio_complete(dio->iocb, ret, 0); + } + + if (dio->flags & DIO_LOCKING) + /* lockdep: non-owner release */ + up_read_non_owner(&dio->inode->i_alloc_sem); + return ret; } @@ -277,8 +280,7 @@ static void dio_bio_end_aio(struct bio *bio, int error) spin_unlock_irqrestore(&dio->bio_lock, flags); if (remaining == 0) { - int ret = dio_complete(dio, dio->iocb->ki_pos, 0); - aio_complete(dio->iocb, ret, 0); + dio_complete(dio, dio->iocb->ki_pos, 0, true); kfree(dio); } } @@ -1126,7 +1128,7 @@ direct_io_worker(int rw, struct kiocb *iocb, struct inode *inode, spin_unlock_irqrestore(&dio->bio_lock, flags); if (ret2 == 0) { - ret = dio_complete(dio, offset, ret); + ret = dio_complete(dio, offset, ret, false); kfree(dio); } else BUG_ON(ret != -EIOCBQUEUED); diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 699d1d01c5df..609159e990de 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -3775,7 +3775,8 @@ static ext4_io_end_t *ext4_init_io_end (struct inode *inode, gfp_t flags) } static void ext4_end_io_dio(struct kiocb *iocb, loff_t offset, - ssize_t size, void *private) + ssize_t size, void *private, int ret, + bool is_async) { ext4_io_end_t *io_end = iocb->private; struct workqueue_struct *wq; @@ -3784,7 +3785,7 @@ static void ext4_end_io_dio(struct kiocb *iocb, loff_t offset, /* if not async direct IO or dio with 0 bytes write, just return */ if (!io_end || !size) - return; + goto out; ext_debug("ext4_end_io_dio(): io_end 0x%p" "for inode %lu, iocb 0x%p, offset %llu, size %llu\n", @@ -3795,7 +3796,7 @@ static void ext4_end_io_dio(struct kiocb *iocb, loff_t offset, if (io_end->flag != EXT4_IO_UNWRITTEN){ ext4_free_io_end(io_end); iocb->private = NULL; - return; + goto out; } io_end->offset = offset; @@ -3812,6 +3813,9 @@ static void ext4_end_io_dio(struct kiocb *iocb, loff_t offset, list_add_tail(&io_end->list, &ei->i_completed_io_list); spin_unlock_irqrestore(&ei->i_completed_io_lock, flags); iocb->private = NULL; +out: + if (is_async) + aio_complete(iocb, ret, 0); } static void ext4_end_io_buffer_write(struct buffer_head *bh, int uptodate) diff --git a/fs/ocfs2/aops.c b/fs/ocfs2/aops.c index 3623ca20cc18..1d2b1f156bcf 100644 --- a/fs/ocfs2/aops.c +++ b/fs/ocfs2/aops.c @@ -609,7 +609,9 @@ bail: static void ocfs2_dio_end_io(struct kiocb *iocb, loff_t offset, ssize_t bytes, - void *private) + void *private, + int ret, + bool is_async) { struct inode *inode = iocb->ki_filp->f_path.dentry->d_inode; int level; @@ -623,6 +625,9 @@ static void ocfs2_dio_end_io(struct kiocb *iocb, if (!level) up_read(&inode->i_alloc_sem); ocfs2_rw_unlock(inode, level); + + if (is_async) + aio_complete(iocb, ret, 0); } /* diff --git a/fs/xfs/linux-2.6/xfs_aops.c b/fs/xfs/linux-2.6/xfs_aops.c index 34640d6dbdcb..5895aaf62ace 100644 --- a/fs/xfs/linux-2.6/xfs_aops.c +++ b/fs/xfs/linux-2.6/xfs_aops.c @@ -1599,7 +1599,9 @@ xfs_end_io_direct( struct kiocb *iocb, loff_t offset, ssize_t size, - void *private) + void *private, + int ret, + bool is_async) { xfs_ioend_t *ioend = iocb->private; @@ -1645,6 +1647,9 @@ xfs_end_io_direct( * against double-freeing. */ iocb->private = NULL; + + if (is_async) + aio_complete(iocb, ret, 0); } STATIC ssize_t diff --git a/fs/xfs/linux-2.6/xfs_aops.h b/fs/xfs/linux-2.6/xfs_aops.h index 4cfc6ea87df8..9f566d92ae3a 100644 --- a/fs/xfs/linux-2.6/xfs_aops.h +++ b/fs/xfs/linux-2.6/xfs_aops.h @@ -37,6 +37,8 @@ typedef struct xfs_ioend { size_t io_size; /* size of the extent */ xfs_off_t io_offset; /* offset in the file */ struct work_struct io_work; /* xfsdatad work queue */ + struct kiocb *io_iocb; + int io_result; } xfs_ioend_t; extern const struct address_space_operations xfs_address_space_operations; diff --git a/include/linux/fs.h b/include/linux/fs.h index 471e1ff5079a..a0912f6075e5 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -415,7 +415,8 @@ struct buffer_head; typedef int (get_block_t)(struct inode *inode, sector_t iblock, struct buffer_head *bh_result, int create); typedef void (dio_iodone_t)(struct kiocb *iocb, loff_t offset, - ssize_t bytes, void *private); + ssize_t bytes, void *private, int ret, + bool is_async); /* * Attribute flags. These should be or-ed together to figure out what -- cgit v1.2.3 From 5b3ff237bef43b9e7fb7d1eb858e29b73fd664f9 Mon Sep 17 00:00:00 2001 From: "jiayingz@google.com (Jiaying Zhang)" <> Date: Tue, 27 Jul 2010 11:56:06 -0400 Subject: ext4: move aio completion after unwritten extent conversion This patch is to be applied upon Christoph's "direct-io: move aio_complete into ->end_io" patch. It adds iocb and result fields to struct ext4_io_end_t, so that we can call aio_complete from ext4_end_io_nolock() after the extent conversion has finished. I have verified with Christoph's aio-dio test that used to fail after a few runs on an original kernel but now succeeds on the patched kernel. See http://thread.gmane.org/gmane.comp.file-systems.ext4/19659 for details. Signed-off-by: Jiaying Zhang Signed-off-by: "Theodore Ts'o" --- fs/ext4/ext4.h | 4 +++- fs/ext4/inode.c | 17 ++++++++++++----- 2 files changed, 15 insertions(+), 6 deletions(-) (limited to 'fs') diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 4c7d4727d6ba..fbb39478df28 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -170,13 +170,15 @@ struct mpage_da_data { }; #define EXT4_IO_UNWRITTEN 0x1 typedef struct ext4_io_end { - struct list_head list; /* per-file finished AIO list */ + struct list_head list; /* per-file finished IO list */ struct inode *inode; /* file being written to */ unsigned int flag; /* unwritten or not */ struct page *page; /* page struct for buffer write */ loff_t offset; /* offset in the file */ ssize_t size; /* size of the extent */ struct work_struct work; /* data work queue */ + struct kiocb *iocb; /* iocb struct for AIO */ + int result; /* error value for AIO */ } ext4_io_end_t; /* diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 609159e990de..46d2079373c9 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -3668,6 +3668,8 @@ static int ext4_end_io_nolock(ext4_io_end_t *io) return ret; } + if (io->iocb) + aio_complete(io->iocb, io->result, 0); /* clear the DIO AIO unwritten flag */ io->flag = 0; return ret; @@ -3767,6 +3769,8 @@ static ext4_io_end_t *ext4_init_io_end (struct inode *inode, gfp_t flags) io->offset = 0; io->size = 0; io->page = NULL; + io->iocb = NULL; + io->result = 0; INIT_WORK(&io->work, ext4_end_io_work); INIT_LIST_HEAD(&io->list); } @@ -3796,12 +3800,18 @@ static void ext4_end_io_dio(struct kiocb *iocb, loff_t offset, if (io_end->flag != EXT4_IO_UNWRITTEN){ ext4_free_io_end(io_end); iocb->private = NULL; - goto out; +out: + if (is_async) + aio_complete(iocb, ret, 0); + return; } io_end->offset = offset; io_end->size = size; - io_end->flag = EXT4_IO_UNWRITTEN; + if (is_async) { + io_end->iocb = iocb; + io_end->result = ret; + } wq = EXT4_SB(io_end->inode->i_sb)->dio_unwritten_wq; /* queue the work to convert unwritten extents to written */ @@ -3813,9 +3823,6 @@ static void ext4_end_io_dio(struct kiocb *iocb, loff_t offset, list_add_tail(&io_end->list, &ei->i_completed_io_list); spin_unlock_irqrestore(&ei->i_completed_io_lock, flags); iocb->private = NULL; -out: - if (is_async) - aio_complete(iocb, ret, 0); } static void ext4_end_io_buffer_write(struct buffer_head *bh, int uptodate) -- cgit v1.2.3 From 506bf2d82165c09b179a5077e01037f6270a4db3 Mon Sep 17 00:00:00 2001 From: Eric Sandeen Date: Tue, 27 Jul 2010 11:56:06 -0400 Subject: ext4: allocate stripe-multiple IOs on stripe boundaries For some reason, today mballoc only allocates IOs which are exactly stripe-sized on a stripe boundary. If you have a multiple (say, a 128k IO on a 64k stripe) you may end up unaligned. It seems to me that a simple change to align stripe-multiple IOs on stripe boundaries would be a very good idea, unless this breaks some other mballoc heuristic for some reason... Reported-by: Mike Snitzer Signed-off-by: Eric Sandeen Signed-off-by: "Theodore Ts'o" --- fs/ext4/mballoc.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c index 5338b1ca64bb..a75de7d44dc9 100644 --- a/fs/ext4/mballoc.c +++ b/fs/ext4/mballoc.c @@ -1822,8 +1822,7 @@ void ext4_mb_complex_scan_group(struct ext4_allocation_context *ac, /* * This is a special case for storages like raid5 - * we try to find stripe-aligned chunks for stripe-size requests - * XXX should do so at least for multiples of stripe size as well + * we try to find stripe-aligned chunks for stripe-size-multiple requests */ static noinline_for_stack void ext4_mb_scan_aligned(struct ext4_allocation_context *ac, @@ -2092,8 +2091,8 @@ repeat: ac->ac_groups_scanned++; if (cr == 0) ext4_mb_simple_scan_group(ac, &e4b); - else if (cr == 1 && - ac->ac_g_ex.fe_len == sbi->s_stripe) + else if (cr == 1 && sbi->s_stripe && + !(ac->ac_g_ex.fe_len % sbi->s_stripe)) ext4_mb_scan_aligned(ac, &e4b); else ext4_mb_complex_scan_group(ac, &e4b); -- cgit v1.2.3 From 0c095c7f113e9fd05913d6e1b2cccbe356be039e Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Tue, 27 Jul 2010 11:56:06 -0400 Subject: ext4: Don't error out the fs if the user tries to make a file too big If the user attempts to make a non-extent-mapped file to be too large, return EFBIG, but don't call ext4_std_err() which will end up marking the file system as containing an error. Thanks to Toshiyuki Okajima-san at Fujitsu for pointing this out. Signed-off-by: "Theodore Ts'o" --- fs/ext4/inode.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 46d2079373c9..38ec77fc3279 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -5472,10 +5472,8 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr) if (!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))) { struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); - if (attr->ia_size > sbi->s_bitmap_maxbytes) { - error = -EFBIG; - goto err_out; - } + if (attr->ia_size > sbi->s_bitmap_maxbytes) + return -EFBIG; } } -- cgit v1.2.3 From dcc7dae3cb21184a317f10a12250bd8d6f458077 Mon Sep 17 00:00:00 2001 From: Cyrill Gorcunov Date: Tue, 27 Jul 2010 11:56:07 -0400 Subject: ext4: Fix potential memory leak in ext4_fill_super Under heavy memory pressure we may hit out of memory situation and as result kstrdup'ed options will not be freed. Fix it. Signed-off-by: Cyrill Gorcunov Signed-off-by: "Theodore Ts'o" --- fs/ext4/super.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/ext4/super.c b/fs/ext4/super.c index ed00c14d7081..d573f6c1a4de 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -2550,7 +2550,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) struct inode *root; char *cp; const char *descr; - int ret = -EINVAL; + int ret = -ENOMEM; int blocksize; unsigned int db_count; unsigned int i; @@ -2561,13 +2561,13 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) sbi = kzalloc(sizeof(*sbi), GFP_KERNEL); if (!sbi) - return -ENOMEM; + goto out_free_orig; sbi->s_blockgroup_lock = kzalloc(sizeof(struct blockgroup_lock), GFP_KERNEL); if (!sbi->s_blockgroup_lock) { kfree(sbi); - return -ENOMEM; + goto out_free_orig; } sb->s_fs_info = sbi; sbi->s_mount_opt = 0; @@ -2584,6 +2584,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) for (cp = sb->s_id; (cp = strchr(cp, '/'));) *cp = '!'; + ret = -EINVAL; blocksize = sb_min_blocksize(sb, EXT4_MIN_BLOCK_SIZE); if (!blocksize) { ext4_msg(sb, KERN_ERR, "unable to set blocksize"); @@ -3190,6 +3191,7 @@ out_fail: kfree(sbi->s_blockgroup_lock); kfree(sbi); lock_kernel(); +out_free_orig: kfree(orig_data); return ret; } -- cgit v1.2.3 From 62d2b5f2dcd3707b070efb16bbfdf6947c38c194 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Tue, 27 Jul 2010 11:56:07 -0400 Subject: ext4: Always journal quota file modifications When journaled quota options are not specified, we do writes to quota files just in data=ordered mode. This actually causes warnings from JBD2 about dirty journaled buffer because ext4_getblk unconditionally treats a block allocated by it as metadata. Since quota actually is filesystem metadata, the easiest way to get rid of the warning is to always treat quota writes as metadata... Signed-off-by: Jan Kara Signed-off-by: "Theodore Ts'o" --- fs/ext4/super.c | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) (limited to 'fs') diff --git a/fs/ext4/super.c b/fs/ext4/super.c index d573f6c1a4de..c1036bc8a539 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -4138,7 +4138,6 @@ static ssize_t ext4_quota_write(struct super_block *sb, int type, ext4_lblk_t blk = off >> EXT4_BLOCK_SIZE_BITS(sb); int err = 0; int offset = off & (sb->s_blocksize - 1); - int journal_quota = EXT4_SB(sb)->s_qf_names[type] != NULL; struct buffer_head *bh; handle_t *handle = journal_current_handle(); @@ -4163,24 +4162,16 @@ static ssize_t ext4_quota_write(struct super_block *sb, int type, bh = ext4_bread(handle, inode, blk, 1, &err); if (!bh) goto out; - if (journal_quota) { - err = ext4_journal_get_write_access(handle, bh); - if (err) { - brelse(bh); - goto out; - } + err = ext4_journal_get_write_access(handle, bh); + if (err) { + brelse(bh); + goto out; } lock_buffer(bh); memcpy(bh->b_data+offset, data, len); flush_dcache_page(bh->b_page); unlock_buffer(bh); - if (journal_quota) - err = ext4_handle_dirty_metadata(handle, NULL, bh); - else { - /* Always do at least ordered writes for quotas */ - err = ext4_jbd2_file_inode(handle, inode); - mark_buffer_dirty(bh); - } + err = ext4_handle_dirty_metadata(handle, NULL, bh); brelse(bh); out: if (err) { -- cgit v1.2.3 From 79e8303677fc15f508b9877e0fea1925c4add6f3 Mon Sep 17 00:00:00 2001 From: Eric Sandeen Date: Tue, 27 Jul 2010 11:56:07 -0400 Subject: ext4: fix ext4_get_blocks references ext4_get_blocks got renamed to ext4_map_blocks, but left stale comments and a prototype littered around. Signed-off-by: Eric Sandeen Signed-off-by: "Theodore Ts'o" --- fs/ext4/ext4.h | 5 +---- fs/ext4/inode.c | 10 +++++----- 2 files changed, 6 insertions(+), 9 deletions(-) (limited to 'fs') diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index fbb39478df28..9ca3637eca5f 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -465,7 +465,7 @@ struct ext4_new_group_data { }; /* - * Flags used by ext4_get_blocks() + * Flags used by ext4_map_blocks() */ /* Allocate any needed blocks and/or convert an unitialized extent to be an initialized ext4 */ @@ -1954,9 +1954,6 @@ extern int ext4_convert_unwritten_extents(struct inode *inode, loff_t offset, ssize_t len); extern int ext4_map_blocks(handle_t *handle, struct inode *inode, struct ext4_map_blocks *map, int flags); -extern int ext4_get_blocks(handle_t *handle, struct inode *inode, - sector_t block, unsigned int max_blocks, - struct buffer_head *bh, int flags); extern int ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, __u64 start, __u64 len); /* move_extent.c */ diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 38ec77fc3279..8279ad73b929 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -2201,7 +2201,7 @@ static int mpage_da_map_blocks(struct mpage_da_data *mpd) BUG_ON(!handle); /* - * Call ext4_get_blocks() to allocate any delayed allocation + * Call ext4_map_blocks() to allocate any delayed allocation * blocks, or to convert an uninitialized extent to be * initialized (in the case where we have written into * one or more preallocated blocks). @@ -2210,7 +2210,7 @@ static int mpage_da_map_blocks(struct mpage_da_data *mpd) * indicate that we are on the delayed allocation path. This * affects functions in many different parts of the allocation * call path. This flag exists primarily because we don't - * want to change *many* call functions, so ext4_get_blocks() + * want to change *many* call functions, so ext4_map_blocks() * will set the magic i_delalloc_reserved_flag once the * inode's allocation semaphore is taken. * @@ -2327,7 +2327,7 @@ static void mpage_add_bh_to_extent(struct mpage_da_data *mpd, * XXX Don't go larger than mballoc is willing to allocate * This is a stopgap solution. We eventually need to fold * mpage_da_submit_io() into this function and then call - * ext4_get_blocks() multiple times in a loop + * ext4_map_blocks() multiple times in a loop */ if (nrblocks >= 8*1024*1024/mpd->inode->i_sb->s_blocksize) goto flush_it; @@ -3948,7 +3948,7 @@ static ssize_t ext4_ext_direct_IO(int rw, struct kiocb *iocb, return -ENOMEM; /* * we save the io structure for current async - * direct IO, so that later ext4_get_blocks() + * direct IO, so that later ext4_map_blocks() * could flag the io structure whether there * is a unwritten extents needs to be converted * when IO is completed. @@ -5675,7 +5675,7 @@ int ext4_writepage_trans_blocks(struct inode *inode) * Calculate the journal credits for a chunk of data modification. * * This is called from DIO, fallocate or whoever calling - * ext4_get_blocks() to map/allocate a chunk of contiguous disk blocks. + * ext4_map_blocks() to map/allocate a chunk of contiguous disk blocks. * * journal buffers for data blocks are not included here, as DIO * and fallocate do no need to journal data buffers. -- cgit v1.2.3 From d889dc8382c4d71b6d538b7b13777bc1ec51df10 Mon Sep 17 00:00:00 2001 From: Toshiyuki Okajima Date: Tue, 27 Jul 2010 11:56:07 -0400 Subject: ext4: fix EFBIG edge case when writing to large non-extent file By running the following reproducer, we can confirm that the write system call returns with 0 when it should return the error EFBIG. #!/bin/sh /bin/dd if=/dev/zero of=./img bs=1k count=1 seek=1024k > /dev/null 2>&1 /sbin/mkfs.ext3 -Fq ./img /bin/mount -o loop -t ext4 ./img /mnt /bin/touch /mnt/file strace /bin/dd if=/dev/zero of=/mnt/file conv=notrunc bs=1k count=1 seek=$((2194719883264/1024)) 2>&1 | /bin/egrep "write.* 1024\) = " /bin/umount /mnt exit Signed-off-by: Toshiyuki Okajima Signed-off-by: "Theodore Ts'o" Cc: Eric Sandeen --- fs/ext4/file.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/ext4/file.c b/fs/ext4/file.c index bd411c12d63d..ee92b66d4558 100644 --- a/fs/ext4/file.c +++ b/fs/ext4/file.c @@ -70,7 +70,8 @@ ext4_file_write(struct kiocb *iocb, const struct iovec *iov, struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); size_t length = iov_length(iov, nr_segs); - if (pos > sbi->s_bitmap_maxbytes) + if ((pos > sbi->s_bitmap_maxbytes || + (pos == sbi->s_bitmap_maxbytes && length > 0))) return -EFBIG; if (pos + length > sbi->s_bitmap_maxbytes) { -- cgit v1.2.3 From e3570639c8b5f2c6a5018a2649c2b7c276af76d7 Mon Sep 17 00:00:00 2001 From: Eric Sandeen Date: Tue, 27 Jul 2010 11:56:08 -0400 Subject: ext4: don't print scary messages for allocation failures post-abort I often get emails containing the "This should not happen!!" message, conveniently trimmed to remove things like: sd 0:0:0:0: [sda] Unhandled error code sd 0:0:0:0: [sda] Result: hostbyte=DID_OK driverbyte=DRIVER_TIMEOUT sd 0:0:0:0: [sda] CDB: Write(10): 2a 00 03 13 c9 70 00 00 28 00 end_request: I/O error, dev sda, sector 51628400 Aborting journal on device dm-0-8. EXT4-fs error (device dm-0): ext4_journal_start_sb: Detected aborted journal EXT4-fs (dm-0): Remounting filesystem read-only I don't think there is any value to the verbosity if the reason is due to a filesystem abort; it just obfuscates the root cause. Signed-off-by: Eric Sandeen Signed-off-by: "Theodore Ts'o" --- fs/ext4/inode.c | 25 ++++++++++++++----------- fs/ext4/mballoc.c | 3 +++ 2 files changed, 17 insertions(+), 11 deletions(-) (limited to 'fs') diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 8279ad73b929..a52d5af99187 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -2228,6 +2228,8 @@ static int mpage_da_map_blocks(struct mpage_da_data *mpd) blks = ext4_map_blocks(handle, mpd->inode, &map, get_blocks_flags); if (blks < 0) { + struct super_block *sb = mpd->inode->i_sb; + err = blks; /* * If get block returns with error we simply @@ -2238,7 +2240,7 @@ static int mpage_da_map_blocks(struct mpage_da_data *mpd) return 0; if (err == -ENOSPC && - ext4_count_free_blocks(mpd->inode->i_sb)) { + ext4_count_free_blocks(sb)) { mpd->retval = err; return 0; } @@ -2250,16 +2252,17 @@ static int mpage_da_map_blocks(struct mpage_da_data *mpd) * writepage and writepages will again try to write * the same. */ - ext4_msg(mpd->inode->i_sb, KERN_CRIT, - "delayed block allocation failed for inode %lu at " - "logical offset %llu with max blocks %zd with " - "error %d", mpd->inode->i_ino, - (unsigned long long) next, - mpd->b_size >> mpd->inode->i_blkbits, err); - printk(KERN_CRIT "This should not happen!! " - "Data will be lost\n"); - if (err == -ENOSPC) { - ext4_print_free_blocks(mpd->inode); + if (!(EXT4_SB(sb)->s_mount_flags & EXT4_MF_FS_ABORTED)) { + ext4_msg(sb, KERN_CRIT, + "delayed block allocation failed for inode %lu " + "at logical offset %llu with max blocks %zd " + "with error %d", mpd->inode->i_ino, + (unsigned long long) next, + mpd->b_size >> mpd->inode->i_blkbits, err); + ext4_msg(sb, KERN_CRIT, + "This should not happen!! Data will be lost\n"); + if (err == -ENOSPC) + ext4_print_free_blocks(mpd->inode); } /* invalidate all the pages */ ext4_da_block_invalidatepages(mpd, next, diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c index a75de7d44dc9..3da28281bc54 100644 --- a/fs/ext4/mballoc.c +++ b/fs/ext4/mballoc.c @@ -3884,6 +3884,9 @@ static void ext4_mb_show_ac(struct ext4_allocation_context *ac) struct super_block *sb = ac->ac_sb; ext4_group_t ngroups, i; + if (EXT4_SB(sb)->s_mount_flags & EXT4_MF_FS_ABORTED) + return; + printk(KERN_ERR "EXT4-fs: Can't allocate:" " Allocation context details:\n"); printk(KERN_ERR "EXT4-fs: status %d flags %d\n", -- cgit v1.2.3 From cc937db74bd5fe75f4cfebbfc4a04c1da10f0695 Mon Sep 17 00:00:00 2001 From: Eric Sandeen Date: Tue, 27 Jul 2010 11:56:08 -0400 Subject: jbd2: Make barrier messages less scary Saying things like "sync failed" when a device does not support barriers makes users slightly more worried than they need to be; rather than talking about sync failures, let's just state the barrier-based facts. Signed-off-by: Eric Sandeen Signed-off-by: "Theodore Ts'o" --- fs/jbd2/commit.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/jbd2/commit.c b/fs/jbd2/commit.c index 75716d3d2be0..af056810acb6 100644 --- a/fs/jbd2/commit.c +++ b/fs/jbd2/commit.c @@ -150,8 +150,8 @@ static int journal_submit_commit_record(journal_t *journal, */ if (ret == -EOPNOTSUPP && barrier_done) { printk(KERN_WARNING - "JBD: barrier-based sync failed on %s - " - "disabling barriers\n", journal->j_devname); + "JBD2: Disabling barriers on %s, " + "not supported by device\n", journal->j_devname); spin_lock(&journal->j_state_lock); journal->j_flags &= ~JBD2_BARRIER; spin_unlock(&journal->j_state_lock); @@ -180,8 +180,8 @@ retry: wait_on_buffer(bh); if (buffer_eopnotsupp(bh) && (journal->j_flags & JBD2_BARRIER)) { printk(KERN_WARNING - "JBD2: wait_on_commit_record: sync failed on %s - " - "disabling barriers\n", journal->j_devname); + "JBD2: %s: disabling barries on %s - not supported " + "by device\n", __func__, journal->j_devname); spin_lock(&journal->j_state_lock); journal->j_flags &= ~JBD2_BARRIER; spin_unlock(&journal->j_state_lock); -- cgit v1.2.3 From f613dfcb3345dacb8cf99b7bb359acc1c18a1157 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Tue, 27 Jul 2010 11:56:08 -0400 Subject: ext4: check to make make sure bd_dev is set before dereferencing it There are some drivers which may not set bdev->bd_dev. So make sure it is non-NULL before dereferencing it. Google-Bug-Id: 1773557 Signed-off-by: "Theodore Ts'o" --- fs/ext4/super.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/ext4/super.c b/fs/ext4/super.c index c1036bc8a539..e046eba24782 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -2304,6 +2304,8 @@ static ssize_t session_write_kbytes_show(struct ext4_attr *a, { struct super_block *sb = sbi->s_buddy_cache->i_sb; + if (!sb->s_bdev->bd_part) + return snprintf(buf, PAGE_SIZE, "0\n"); return snprintf(buf, PAGE_SIZE, "%lu\n", (part_stat_read(sb->s_bdev->bd_part, sectors[1]) - sbi->s_sectors_written_start) >> 1); @@ -2314,6 +2316,8 @@ static ssize_t lifetime_write_kbytes_show(struct ext4_attr *a, { struct super_block *sb = sbi->s_buddy_cache->i_sb; + if (!sb->s_bdev->bd_part) + return snprintf(buf, PAGE_SIZE, "0\n"); return snprintf(buf, PAGE_SIZE, "%llu\n", (unsigned long long)(sbi->s_kbytes_written + ((part_stat_read(sb->s_bdev->bd_part, sectors[1]) - @@ -2575,8 +2579,9 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) sbi->s_resgid = EXT4_DEF_RESGID; sbi->s_inode_readahead_blks = EXT4_DEF_INODE_READAHEAD_BLKS; sbi->s_sb_block = sb_block; - sbi->s_sectors_written_start = part_stat_read(sb->s_bdev->bd_part, - sectors[1]); + if (sb->s_bdev->bd_part) + sbi->s_sectors_written_start = + part_stat_read(sb->s_bdev->bd_part, sectors[1]); unlock_kernel(); @@ -3492,10 +3497,14 @@ static int ext4_commit_super(struct super_block *sb, int sync) */ if (!(sb->s_flags & MS_RDONLY)) es->s_wtime = cpu_to_le32(get_seconds()); - es->s_kbytes_written = - cpu_to_le64(EXT4_SB(sb)->s_kbytes_written + + if (sb->s_bdev->bd_part) + es->s_kbytes_written = + cpu_to_le64(EXT4_SB(sb)->s_kbytes_written + ((part_stat_read(sb->s_bdev->bd_part, sectors[1]) - EXT4_SB(sb)->s_sectors_written_start) >> 1)); + 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)); es->s_free_inodes_count = cpu_to_le32(percpu_counter_sum_positive( -- cgit v1.2.3 From da7ddd3296505b4cb46685e1bbf7d0075b3cd4f1 Mon Sep 17 00:00:00 2001 From: Latchesar Ionkov Date: Mon, 19 Jul 2010 15:40:03 -0500 Subject: 9p: Pass the correct end of buffer to p9stat_read Pass the correct end of the buffer to p9stat_read. Signed-off-by: Latchesar Ionkov Signed-off-by: Eric Van Hensbergen --- fs/9p/vfs_dir.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/9p/vfs_dir.c b/fs/9p/vfs_dir.c index d61e3b28ce37..36d961f342af 100644 --- a/fs/9p/vfs_dir.c +++ b/fs/9p/vfs_dir.c @@ -146,7 +146,7 @@ static int v9fs_dir_readdir(struct file *filp, void *dirent, filldir_t filldir) while (rdir->head < rdir->tail) { p9stat_init(&st); err = p9stat_read(rdir->buf + rdir->head, - buflen - rdir->head, &st, + rdir->tail - rdir->head, &st, fid->clnt->proto_version); if (err) { P9_DPRINTK(P9_DEBUG_VFS, "returned %d\n", err); -- cgit v1.2.3 From 03066f23452ff088ad8e2c8acdf4443043f35b51 Mon Sep 17 00:00:00 2001 From: Yehuda Sadeh Date: Tue, 27 Jul 2010 13:11:08 -0700 Subject: ceph: use complete_all and wake_up_all This fixes an issue triggered by running concurrent syncs. One of the syncs would go through while the other would just hang indefinitely. In any case, we never actually want to wake a single waiter, so the *_all functions should be used. Signed-off-by: Yehuda Sadeh Signed-off-by: Sage Weil --- fs/ceph/caps.c | 14 +++++++------- fs/ceph/file.c | 2 +- fs/ceph/inode.c | 2 +- fs/ceph/mds_client.c | 10 +++++----- fs/ceph/mon_client.c | 6 +++--- fs/ceph/osd_client.c | 6 +++--- 6 files changed, 20 insertions(+), 20 deletions(-) (limited to 'fs') diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index 6afc1affb50a..b81be9a56487 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -627,7 +627,7 @@ retry: if (fmode >= 0) __ceph_get_fmode(ci, fmode); spin_unlock(&inode->i_lock); - wake_up(&ci->i_cap_wq); + wake_up_all(&ci->i_cap_wq); return 0; } @@ -1181,7 +1181,7 @@ static int __send_cap(struct ceph_mds_client *mdsc, struct ceph_cap *cap, } if (wake) - wake_up(&ci->i_cap_wq); + wake_up_all(&ci->i_cap_wq); return delayed; } @@ -2153,7 +2153,7 @@ void ceph_put_cap_refs(struct ceph_inode_info *ci, int had) else if (flushsnaps) ceph_flush_snaps(ci); if (wake) - wake_up(&ci->i_cap_wq); + wake_up_all(&ci->i_cap_wq); if (put) iput(inode); } @@ -2229,7 +2229,7 @@ void ceph_put_wrbuffer_cap_refs(struct ceph_inode_info *ci, int nr, iput(inode); } else if (complete_capsnap) { ceph_flush_snaps(ci); - wake_up(&ci->i_cap_wq); + wake_up_all(&ci->i_cap_wq); } if (drop_capsnap) iput(inode); @@ -2405,7 +2405,7 @@ static void handle_cap_grant(struct inode *inode, struct ceph_mds_caps *grant, if (queue_invalidate) ceph_queue_invalidate(inode); if (wake) - wake_up(&ci->i_cap_wq); + wake_up_all(&ci->i_cap_wq); if (check_caps == 1) ceph_check_caps(ci, CHECK_CAPS_NODELAY|CHECK_CAPS_AUTHONLY, @@ -2460,7 +2460,7 @@ static void handle_cap_flush_ack(struct inode *inode, u64 flush_tid, struct ceph_inode_info, i_flushing_item)->vfs_inode); mdsc->num_cap_flushing--; - wake_up(&mdsc->cap_flushing_wq); + wake_up_all(&mdsc->cap_flushing_wq); dout(" inode %p now !flushing\n", inode); if (ci->i_dirty_caps == 0) { @@ -2472,7 +2472,7 @@ static void handle_cap_flush_ack(struct inode *inode, u64 flush_tid, } } spin_unlock(&mdsc->cap_dirty_lock); - wake_up(&ci->i_cap_wq); + wake_up_all(&ci->i_cap_wq); out: spin_unlock(&inode->i_lock); diff --git a/fs/ceph/file.c b/fs/ceph/file.c index 6251a1574b94..7c08698fad3e 100644 --- a/fs/ceph/file.c +++ b/fs/ceph/file.c @@ -265,7 +265,7 @@ int ceph_release(struct inode *inode, struct file *file) kmem_cache_free(ceph_file_cachep, cf); /* wake up anyone waiting for caps on this inode */ - wake_up(&ci->i_cap_wq); + wake_up_all(&ci->i_cap_wq); return 0; } diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c index 3582e79f46e0..389f9dbd9949 100644 --- a/fs/ceph/inode.c +++ b/fs/ceph/inode.c @@ -1501,7 +1501,7 @@ retry: if (wrbuffer_refs == 0) ceph_check_caps(ci, CHECK_CAPS_AUTHONLY, NULL); if (wake) - wake_up(&ci->i_cap_wq); + wake_up_all(&ci->i_cap_wq); } diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 416c08d315db..dd440bd438a9 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -868,7 +868,7 @@ static int wake_up_session_cb(struct inode *inode, struct ceph_cap *cap, { struct ceph_inode_info *ci = ceph_inode(inode); - wake_up(&ci->i_cap_wq); + wake_up_all(&ci->i_cap_wq); if (arg) { spin_lock(&inode->i_lock); ci->i_wanted_max_size = 0; @@ -1564,7 +1564,7 @@ static void complete_request(struct ceph_mds_client *mdsc, if (req->r_callback) req->r_callback(mdsc, req); else - complete(&req->r_completion); + complete_all(&req->r_completion); } /* @@ -1932,7 +1932,7 @@ static void handle_reply(struct ceph_mds_session *session, struct ceph_msg *msg) if (head->safe) { req->r_got_safe = true; __unregister_request(mdsc, req); - complete(&req->r_safe_completion); + complete_all(&req->r_safe_completion); if (req->r_got_unsafe) { /* @@ -1947,7 +1947,7 @@ static void handle_reply(struct ceph_mds_session *session, struct ceph_msg *msg) /* last unsafe request during umount? */ if (mdsc->stopping && !__get_oldest_req(mdsc)) - complete(&mdsc->safe_umount_waiters); + complete_all(&mdsc->safe_umount_waiters); mutex_unlock(&mdsc->mutex); goto out; } @@ -2126,7 +2126,7 @@ static void handle_session(struct ceph_mds_session *session, pr_info("mds%d reconnect denied\n", session->s_mds); remove_session_caps(session); wake = 1; /* for good measure */ - complete(&mdsc->session_close_waiters); + complete_all(&mdsc->session_close_waiters); kick_requests(mdsc, mds); break; diff --git a/fs/ceph/mon_client.c b/fs/ceph/mon_client.c index cc115eafae11..54fe01c50706 100644 --- a/fs/ceph/mon_client.c +++ b/fs/ceph/mon_client.c @@ -345,7 +345,7 @@ static void ceph_monc_handle_map(struct ceph_mon_client *monc, out: mutex_unlock(&monc->mutex); - wake_up(&client->auth_wq); + wake_up_all(&client->auth_wq); } /* @@ -462,7 +462,7 @@ static void handle_statfs_reply(struct ceph_mon_client *monc, } mutex_unlock(&monc->mutex); if (req) { - complete(&req->completion); + complete_all(&req->completion); put_generic_request(req); } return; @@ -718,7 +718,7 @@ static void handle_auth_reply(struct ceph_mon_client *monc, monc->m_auth->front_max); if (ret < 0) { monc->client->auth_err = ret; - wake_up(&monc->client->auth_wq); + wake_up_all(&monc->client->auth_wq); } else if (ret > 0) { __send_prepared_auth_request(monc, ret); } else if (!was_auth && monc->auth->ops->is_authenticated(monc->auth)) { diff --git a/fs/ceph/osd_client.c b/fs/ceph/osd_client.c index 92b7251a53f1..e38522347898 100644 --- a/fs/ceph/osd_client.c +++ b/fs/ceph/osd_client.c @@ -862,12 +862,12 @@ static void handle_reply(struct ceph_osd_client *osdc, struct ceph_msg *msg, if (req->r_callback) req->r_callback(req, msg); else - complete(&req->r_completion); + complete_all(&req->r_completion); if (flags & CEPH_OSD_FLAG_ONDISK) { if (req->r_safe_callback) req->r_safe_callback(req, msg); - complete(&req->r_safe_completion); /* fsync waiter */ + complete_all(&req->r_safe_completion); /* fsync waiter */ } done: @@ -1083,7 +1083,7 @@ done: if (newmap) kick_requests(osdc, NULL); up_read(&osdc->map_sem); - wake_up(&osdc->client->auth_wq); + wake_up_all(&osdc->client->auth_wq); return; bad: -- cgit v1.2.3 From b7ba83715317007962ee318587de92f14e9c3aaa Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 20:12:04 -0500 Subject: inotify: simplify the inotify idr handling This patch moves all of the idr editing operations into their own idr functions. It makes it easier to prove locking correctness and to to understand the code flow. Signed-off-by: Eric Paris --- fs/notify/inotify/inotify_user.c | 194 ++++++++++++++++++++++++++++----------- 1 file changed, 139 insertions(+), 55 deletions(-) (limited to 'fs') diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index e46ca685b9be..653c507b1bb3 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -357,6 +357,77 @@ static int inotify_find_inode(const char __user *dirname, struct path *path, uns return error; } +static int inotify_add_to_idr(struct idr *idr, spinlock_t *idr_lock, + int last_wd, + struct inotify_inode_mark_entry *ientry) +{ + int ret; + + do { + if (unlikely(!idr_pre_get(idr, GFP_KERNEL))) + return -ENOMEM; + + spin_lock(idr_lock); + ret = idr_get_new_above(idr, ientry, last_wd + 1, + &ientry->wd); + /* we added the mark to the idr, take a reference */ + if (!ret) + fsnotify_get_mark(&ientry->fsn_entry); + spin_unlock(idr_lock); + } while (ret == -EAGAIN); + + return ret; +} + +static struct inotify_inode_mark_entry *inotify_idr_find_locked(struct fsnotify_group *group, + int wd) +{ + struct idr *idr = &group->inotify_data.idr; + spinlock_t *idr_lock = &group->inotify_data.idr_lock; + struct inotify_inode_mark_entry *ientry; + + assert_spin_locked(idr_lock); + + ientry = idr_find(idr, wd); + if (ientry) { + struct fsnotify_mark_entry *fsn_entry = &ientry->fsn_entry; + + fsnotify_get_mark(fsn_entry); + /* One ref for being in the idr, one ref we just took */ + BUG_ON(atomic_read(&fsn_entry->refcnt) < 2); + } + + return ientry; +} + +static struct inotify_inode_mark_entry *inotify_idr_find(struct fsnotify_group *group, + int wd) +{ + struct inotify_inode_mark_entry *ientry; + spinlock_t *idr_lock = &group->inotify_data.idr_lock; + + spin_lock(idr_lock); + ientry = inotify_idr_find_locked(group, wd); + spin_unlock(idr_lock); + + return ientry; +} + +static void do_inotify_remove_from_idr(struct fsnotify_group *group, + struct inotify_inode_mark_entry *ientry) +{ + struct idr *idr = &group->inotify_data.idr; + spinlock_t *idr_lock = &group->inotify_data.idr_lock; + int wd = ientry->wd; + + assert_spin_locked(idr_lock); + + idr_remove(idr, wd); + + /* removed from the idr, drop that ref */ + fsnotify_put_mark(&ientry->fsn_entry); +} + /* * Remove the mark from the idr (if present) and drop the reference * on the mark because it was in the idr. @@ -364,42 +435,72 @@ static int inotify_find_inode(const char __user *dirname, struct path *path, uns static void inotify_remove_from_idr(struct fsnotify_group *group, struct inotify_inode_mark_entry *ientry) { - struct idr *idr; - struct fsnotify_mark_entry *entry; - struct inotify_inode_mark_entry *found_ientry; + spinlock_t *idr_lock = &group->inotify_data.idr_lock; + struct inotify_inode_mark_entry *found_ientry = NULL; int wd; - spin_lock(&group->inotify_data.idr_lock); - idr = &group->inotify_data.idr; + spin_lock(idr_lock); wd = ientry->wd; - if (wd == -1) + /* + * does this ientry think it is in the idr? we shouldn't get called + * if it wasn't.... + */ + if (wd == -1) { + printk(KERN_WARNING "%s: ientry=%p ientry->wd=%d ientry->group=%p" + " ientry->inode=%p\n", __func__, ientry, ientry->wd, + ientry->fsn_entry.group, ientry->fsn_entry.inode); + WARN_ON(1); goto out; + } - entry = idr_find(&group->inotify_data.idr, wd); - if (unlikely(!entry)) + /* Lets look in the idr to see if we find it */ + found_ientry = inotify_idr_find_locked(group, wd); + if (unlikely(!found_ientry)) { + printk(KERN_WARNING "%s: ientry=%p ientry->wd=%d ientry->group=%p" + " ientry->inode=%p\n", __func__, ientry, ientry->wd, + ientry->fsn_entry.group, ientry->fsn_entry.inode); + WARN_ON(1); goto out; + } - found_ientry = container_of(entry, struct inotify_inode_mark_entry, fsn_entry); + /* + * We found an entry in the idr at the right wd, but it's + * not the entry we were told to remove. eparis seriously + * fucked up somewhere. + */ if (unlikely(found_ientry != ientry)) { - /* We found an entry in the idr with the right wd, but it's - * not the entry we were told to remove. eparis seriously - * fucked up somewhere. */ WARN_ON(1); - ientry->wd = -1; + printk(KERN_WARNING "%s: ientry=%p ientry->wd=%d ientry->group=%p " + "entry->inode=%p found_ientry=%p found_ientry->wd=%d " + "found_ientry->group=%p found_ientry->inode=%p\n", + __func__, ientry, ientry->wd, ientry->fsn_entry.group, + ientry->fsn_entry.inode, found_ientry, found_ientry->wd, + found_ientry->fsn_entry.group, + found_ientry->fsn_entry.inode); goto out; } - /* One ref for being in the idr, one ref held by the caller */ - BUG_ON(atomic_read(&entry->refcnt) < 2); - - idr_remove(idr, wd); - ientry->wd = -1; + /* + * One ref for being in the idr + * one ref held by the caller trying to kill us + * one ref grabbed by inotify_idr_find + */ + if (unlikely(atomic_read(&ientry->fsn_entry.refcnt) < 3)) { + printk(KERN_WARNING "%s: ientry=%p ientry->wd=%d ientry->group=%p" + " ientry->inode=%p\n", __func__, ientry, ientry->wd, + ientry->fsn_entry.group, ientry->fsn_entry.inode); + /* we can't really recover with bad ref cnting.. */ + BUG(); + } - /* removed from the idr, drop that ref */ - fsnotify_put_mark(entry); + do_inotify_remove_from_idr(group, ientry); out: - spin_unlock(&group->inotify_data.idr_lock); + /* match the ref taken by inotify_idr_find_locked() */ + if (found_ientry) + fsnotify_put_mark(&found_ientry->fsn_entry); + ientry->wd = -1; + spin_unlock(idr_lock); } /* @@ -524,6 +625,8 @@ static int inotify_new_watch(struct fsnotify_group *group, struct inotify_inode_mark_entry *tmp_ientry; __u32 mask; int ret; + struct idr *idr = &group->inotify_data.idr; + spinlock_t *idr_lock = &group->inotify_data.idr_lock; /* don't allow invalid bits: we don't want flags set */ mask = inotify_arg_to_mask(arg); @@ -541,28 +644,11 @@ static int inotify_new_watch(struct fsnotify_group *group, ret = -ENOSPC; if (atomic_read(&group->inotify_data.user->inotify_watches) >= inotify_max_user_watches) goto out_err; -retry: - ret = -ENOMEM; - if (unlikely(!idr_pre_get(&group->inotify_data.idr, GFP_KERNEL))) - goto out_err; - - /* we are putting the mark on the idr, take a reference */ - fsnotify_get_mark(&tmp_ientry->fsn_entry); - - spin_lock(&group->inotify_data.idr_lock); - ret = idr_get_new_above(&group->inotify_data.idr, &tmp_ientry->fsn_entry, - group->inotify_data.last_wd+1, - &tmp_ientry->wd); - spin_unlock(&group->inotify_data.idr_lock); - if (ret) { - /* we didn't get on the idr, drop the idr reference */ - fsnotify_put_mark(&tmp_ientry->fsn_entry); - /* idr was out of memory allocate and try again */ - if (ret == -EAGAIN) - goto retry; + ret = inotify_add_to_idr(idr, idr_lock, group->inotify_data.last_wd, + tmp_ientry); + if (ret) goto out_err; - } /* we are on the idr, now get on the inode */ ret = fsnotify_add_mark(&tmp_ientry->fsn_entry, group, inode); @@ -726,7 +812,7 @@ fput_and_out: SYSCALL_DEFINE2(inotify_rm_watch, int, fd, __s32, wd) { struct fsnotify_group *group; - struct fsnotify_mark_entry *entry; + struct inotify_inode_mark_entry *ientry; struct file *filp; int ret = 0, fput_needed; @@ -735,25 +821,23 @@ SYSCALL_DEFINE2(inotify_rm_watch, int, fd, __s32, wd) return -EBADF; /* verify that this is indeed an inotify instance */ - if (unlikely(filp->f_op != &inotify_fops)) { - ret = -EINVAL; + ret = -EINVAL; + if (unlikely(filp->f_op != &inotify_fops)) goto out; - } group = filp->private_data; - spin_lock(&group->inotify_data.idr_lock); - entry = idr_find(&group->inotify_data.idr, wd); - if (unlikely(!entry)) { - spin_unlock(&group->inotify_data.idr_lock); - ret = -EINVAL; + ret = -EINVAL; + ientry = inotify_idr_find(group, wd); + if (unlikely(!ientry)) goto out; - } - fsnotify_get_mark(entry); - spin_unlock(&group->inotify_data.idr_lock); - fsnotify_destroy_mark_by_entry(entry); - fsnotify_put_mark(entry); + ret = 0; + + fsnotify_destroy_mark_by_entry(&ientry->fsn_entry); + + /* match ref taken by inotify_idr_find */ + fsnotify_put_mark(&ientry->fsn_entry); out: fput_light(filp, fput_needed); -- cgit v1.2.3 From 9e1c74321d87a8b079f04d89e750b39a43365e1f Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 20:12:05 -0500 Subject: fsnotify: duplicate fsnotify_mark_entry data between 2 marks Simple copy fsnotify information from one mark to another in preparation for the second mark to replace the first. Signed-off-by: Eric Paris --- fs/notify/inode_mark.c | 10 +++++++++- include/linux/fsnotify_backend.h | 2 ++ 2 files changed, 11 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c index 0399bcbe09c8..a13cf9e9233a 100644 --- a/fs/notify/inode_mark.c +++ b/fs/notify/inode_mark.c @@ -282,12 +282,20 @@ struct fsnotify_mark_entry *fsnotify_find_mark_entry(struct fsnotify_group *grou return NULL; } +void fsnotify_duplicate_mark(struct fsnotify_mark_entry *new, struct fsnotify_mark_entry *old) +{ + assert_spin_locked(&old->lock); + new->inode = old->inode; + new->group = old->group; + new->mask = old->mask; + new->free_mark = old->free_mark; +} + /* * Nothing fancy, just initialize lists and locks and counters. */ void fsnotify_init_mark(struct fsnotify_mark_entry *entry, void (*free_mark)(struct fsnotify_mark_entry *entry)) - { spin_lock_init(&entry->lock); atomic_set(&entry->refcnt, 1); diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index 8f8341e9f021..390516732956 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -342,6 +342,8 @@ extern void fsnotify_recalc_inode_mask(struct inode *inode); extern void fsnotify_init_mark(struct fsnotify_mark_entry *entry, void (*free_mark)(struct fsnotify_mark_entry *entry)); /* find (and take a reference) to a mark associated with group and inode */ extern struct fsnotify_mark_entry *fsnotify_find_mark_entry(struct fsnotify_group *group, struct inode *inode); +/* copy the values from old into new */ +extern void fsnotify_duplicate_mark(struct fsnotify_mark_entry *new, struct fsnotify_mark_entry *old); /* attach the mark to both the group and the inode */ extern int fsnotify_add_mark(struct fsnotify_mark_entry *entry, struct fsnotify_group *group, struct inode *inode); /* given a mark, flag it to be freed when all references are dropped */ -- cgit v1.2.3 From 40554c3dae83bd892b7fbfaa2ea9de739cbcf065 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 20:12:05 -0500 Subject: fsnotify: allow addition of duplicate fsnotify marks This patch allows a task to add a second fsnotify mark to an inode for the same group. This mark will be added to the end of the inode's list and this will never be found by the stand fsnotify_find_mark() function. This is useful if a user wants to add a new mark before removing the old one. Signed-off-by: Eric Paris --- fs/notify/dnotify/dnotify.c | 2 +- fs/notify/inode_mark.c | 8 +++++--- fs/notify/inotify/inotify_user.c | 2 +- include/linux/fsnotify_backend.h | 2 +- kernel/audit_watch.c | 2 +- 5 files changed, 9 insertions(+), 7 deletions(-) (limited to 'fs') diff --git a/fs/notify/dnotify/dnotify.c b/fs/notify/dnotify/dnotify.c index 7e54e52964dd..85b97fca14de 100644 --- a/fs/notify/dnotify/dnotify.c +++ b/fs/notify/dnotify/dnotify.c @@ -362,7 +362,7 @@ int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg) dnentry = container_of(entry, struct dnotify_mark_entry, fsn_entry); spin_lock(&entry->lock); } else { - fsnotify_add_mark(new_entry, dnotify_group, inode); + fsnotify_add_mark(new_entry, dnotify_group, inode, 0); spin_lock(&new_entry->lock); entry = new_entry; dnentry = new_dnentry; diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c index a13cf9e9233a..7d2962e5328e 100644 --- a/fs/notify/inode_mark.c +++ b/fs/notify/inode_mark.c @@ -312,9 +312,10 @@ void fsnotify_init_mark(struct fsnotify_mark_entry *entry, * event types should be delivered to which group and for which inodes. */ int fsnotify_add_mark(struct fsnotify_mark_entry *entry, - struct fsnotify_group *group, struct inode *inode) + struct fsnotify_group *group, struct inode *inode, + int allow_dups) { - struct fsnotify_mark_entry *lentry; + struct fsnotify_mark_entry *lentry = NULL; int ret = 0; inode = igrab(inode); @@ -331,7 +332,8 @@ int fsnotify_add_mark(struct fsnotify_mark_entry *entry, spin_lock(&group->mark_lock); spin_lock(&inode->i_lock); - lentry = fsnotify_find_mark_entry(group, inode); + if (!allow_dups) + lentry = fsnotify_find_mark_entry(group, inode); if (!lentry) { entry->group = group; entry->inode = inode; diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index 653c507b1bb3..f22a04005db2 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -651,7 +651,7 @@ static int inotify_new_watch(struct fsnotify_group *group, goto out_err; /* we are on the idr, now get on the inode */ - ret = fsnotify_add_mark(&tmp_ientry->fsn_entry, group, inode); + ret = fsnotify_add_mark(&tmp_ientry->fsn_entry, group, inode, 0); if (ret) { /* we failed to get on the inode, get off the idr */ inotify_remove_from_idr(group, tmp_ientry); diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index 390516732956..1679f250d59e 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -345,7 +345,7 @@ extern struct fsnotify_mark_entry *fsnotify_find_mark_entry(struct fsnotify_grou /* copy the values from old into new */ extern void fsnotify_duplicate_mark(struct fsnotify_mark_entry *new, struct fsnotify_mark_entry *old); /* attach the mark to both the group and the inode */ -extern int fsnotify_add_mark(struct fsnotify_mark_entry *entry, struct fsnotify_group *group, struct inode *inode); +extern int fsnotify_add_mark(struct fsnotify_mark_entry *entry, struct fsnotify_group *group, struct inode *inode, int allow_dups); /* given a mark, flag it to be freed when all references are dropped */ extern void fsnotify_destroy_mark_by_entry(struct fsnotify_mark_entry *entry); /* run all the marks in a group, and flag them to be freed */ diff --git a/kernel/audit_watch.c b/kernel/audit_watch.c index 75ab53987ece..c44de0c4fc47 100644 --- a/kernel/audit_watch.c +++ b/kernel/audit_watch.c @@ -161,7 +161,7 @@ static struct audit_parent *audit_init_parent(struct nameidata *ndp) fsnotify_init_mark(&parent->mark, audit_watch_free_mark); parent->mark.mask = AUDIT_FS_WATCH; - ret = fsnotify_add_mark(&parent->mark, audit_watch_group, inode); + ret = fsnotify_add_mark(&parent->mark, audit_watch_group, inode, 0); if (ret < 0) { audit_free_parent(parent); return ERR_PTR(ret); -- cgit v1.2.3 From b4277d3dd5a7400c1ea7fd4e7d64bda8899f84f5 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 20:12:06 -0500 Subject: fsnotify: use fsnotify_create_event to allocate the q_overflow event Currently fsnotify defines a static fsnotify event which is sent when a group overflows its allotted queue length. This patch just allocates that event from the event cache rather than defining it statically. There is no known reason that the current implementation is wrong, but this makes sure the event is initialized and created like any other. Signed-off-by: Eric Paris --- fs/notify/notification.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/notify/notification.c b/fs/notify/notification.c index b8bf53b4c108..8481253d64b5 100644 --- a/fs/notify/notification.c +++ b/fs/notify/notification.c @@ -56,7 +56,7 @@ static struct kmem_cache *fsnotify_event_holder_cachep; * it is needed. It's refcnt is set 1 at kernel init time and will never * get set to 0 so it will never get 'freed' */ -static struct fsnotify_event q_overflow_event; +static struct fsnotify_event *q_overflow_event; static atomic_t fsnotify_sync_cookie = ATOMIC_INIT(0); /** @@ -195,7 +195,7 @@ alloc_holder: mutex_lock(&group->notification_mutex); if (group->q_len >= group->max_events) { - event = &q_overflow_event; + event = q_overflow_event; ret = -EOVERFLOW; /* sorry, no private data on the overflow event */ priv = NULL; @@ -412,8 +412,11 @@ __init int fsnotify_notification_init(void) fsnotify_event_cachep = KMEM_CACHE(fsnotify_event, SLAB_PANIC); fsnotify_event_holder_cachep = KMEM_CACHE(fsnotify_event_holder, SLAB_PANIC); - initialize_event(&q_overflow_event); - q_overflow_event.mask = FS_Q_OVERFLOW; + q_overflow_event = fsnotify_create_event(NULL, FS_Q_OVERFLOW, NULL, + FSNOTIFY_EVENT_NONE, NULL, 0, + GFP_KERNEL); + if (!q_overflow_event) + panic("unable to allocate fsnotify q_overflow_event\n"); return 0; } -- cgit v1.2.3 From 31ddd3268dcb6c1d70e9930a83be43bf86e4bf17 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 20:12:06 -0500 Subject: inotify: use container_of instead of casting inotify_free_mark casts directly from an fsnotify_mark_entry to an inotify_inode_mark_entry. This works, but should use container_of instead for future proofing. Signed-off-by: Eric Paris --- fs/notify/inotify/inotify_user.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index f22a04005db2..a0e40f7c9781 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -550,7 +550,9 @@ skip_send_ignore: /* ding dong the mark is dead */ static void inotify_free_mark(struct fsnotify_mark_entry *entry) { - struct inotify_inode_mark_entry *ientry = (struct inotify_inode_mark_entry *)entry; + struct inotify_inode_mark_entry *ientry; + + ientry = container_of(entry, struct inotify_inode_mark_entry, fsn_entry); kmem_cache_free(inotify_inode_mark_cachep, ientry); } -- cgit v1.2.3 From f0553af054d31f48a75fddd3ef8beb5c39715019 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 20:12:06 -0500 Subject: fsnotify: kzalloc fsnotify groups Use kzalloc for fsnotify_groups so that none of the fields can leak any information accidentally. Signed-off-by: Eric Paris --- fs/notify/group.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/notify/group.c b/fs/notify/group.c index 0e1677144bc5..777ca8212255 100644 --- a/fs/notify/group.c +++ b/fs/notify/group.c @@ -207,7 +207,7 @@ struct fsnotify_group *fsnotify_obtain_group(unsigned int group_num, __u32 mask, struct fsnotify_group *group, *tgroup; /* very low use, simpler locking if we just always alloc */ - group = kmalloc(sizeof(struct fsnotify_group), GFP_KERNEL); + group = kzalloc(sizeof(struct fsnotify_group), GFP_KERNEL); if (!group) return ERR_PTR(-ENOMEM); -- cgit v1.2.3 From 6f3a539e3bd8ed2eafa532443723d56896153a00 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 20:12:07 -0500 Subject: fsnotify: use kmem_cache_zalloc to simplify event initialization fsnotify event initialization is done entry by entry with almost everything set to either 0 or NULL. Use kmem_cache_zalloc and only initialize things that need non-zero initialization. Also means we don't have to change initialization entries based on the config options. Signed-off-by: Eric Paris --- fs/notify/notification.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) (limited to 'fs') diff --git a/fs/notify/notification.c b/fs/notify/notification.c index 8481253d64b5..b34ce7ad0409 100644 --- a/fs/notify/notification.c +++ b/fs/notify/notification.c @@ -314,25 +314,14 @@ void fsnotify_flush_notify(struct fsnotify_group *group) static void initialize_event(struct fsnotify_event *event) { - event->holder.event = NULL; INIT_LIST_HEAD(&event->holder.event_list); atomic_set(&event->refcnt, 1); spin_lock_init(&event->lock); - event->path.dentry = NULL; - event->path.mnt = NULL; - event->inode = NULL; event->data_type = FSNOTIFY_EVENT_NONE; INIT_LIST_HEAD(&event->private_data_list); - - event->to_tell = NULL; - - event->file_name = NULL; - event->name_len = 0; - - event->sync_cookie = 0; } /* @@ -353,7 +342,7 @@ struct fsnotify_event *fsnotify_create_event(struct inode *to_tell, __u32 mask, { struct fsnotify_event *event; - event = kmem_cache_alloc(fsnotify_event_cachep, gfp); + event = kmem_cache_zalloc(fsnotify_event_cachep, gfp); if (!event) return NULL; -- cgit v1.2.3 From 7050c48826d5adb2210bddfb6a67aa13bbe984ed Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 20:27:10 -0500 Subject: inotify: do not reuse watch descriptors Prior to 2.6.31 inotify would not reuse watch descriptors until all of them had been used at least once. After the rewrite inotify would reuse watch descriptors. The selinux utility 'restorecond' was found to have problems when watch descriptors were reused. This patch reverts to the pre inotify rewrite behavior to not reuse watch descriptors. Signed-off-by: Eric Paris --- fs/notify/inotify/inotify_user.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'fs') diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index a0e40f7c9781..ce21ebaee89e 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -358,7 +358,7 @@ static int inotify_find_inode(const char __user *dirname, struct path *path, uns } static int inotify_add_to_idr(struct idr *idr, spinlock_t *idr_lock, - int last_wd, + int *last_wd, struct inotify_inode_mark_entry *ientry) { int ret; @@ -368,11 +368,13 @@ static int inotify_add_to_idr(struct idr *idr, spinlock_t *idr_lock, return -ENOMEM; spin_lock(idr_lock); - ret = idr_get_new_above(idr, ientry, last_wd + 1, + ret = idr_get_new_above(idr, ientry, *last_wd + 1, &ientry->wd); /* we added the mark to the idr, take a reference */ - if (!ret) + if (!ret) { fsnotify_get_mark(&ientry->fsn_entry); + *last_wd = ientry->wd; + } spin_unlock(idr_lock); } while (ret == -EAGAIN); @@ -647,7 +649,7 @@ static int inotify_new_watch(struct fsnotify_group *group, if (atomic_read(&group->inotify_data.user->inotify_watches) >= inotify_max_user_watches) goto out_err; - ret = inotify_add_to_idr(idr, idr_lock, group->inotify_data.last_wd, + ret = inotify_add_to_idr(idr, idr_lock, &group->inotify_data.last_wd, tmp_ientry); if (ret) goto out_err; @@ -660,9 +662,6 @@ static int inotify_new_watch(struct fsnotify_group *group, goto out_err; } - /* update the idr hint, who cares about races, it's just a hint */ - group->inotify_data.last_wd = tmp_ientry->wd; - /* increment the number of watches the user has */ atomic_inc(&group->inotify_data.user->inotify_watches); -- cgit v1.2.3 From 2dfc1cae4c42b93b831b2417540df2b895ab7108 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 20:30:52 -0500 Subject: inotify: remove inotify in kernel interface nothing uses inotify in the kernel, drop it! Signed-off-by: Eric Paris --- Documentation/feature-removal-schedule.txt | 8 - fs/inode.c | 6 - fs/notify/inotify/Kconfig | 15 - fs/notify/inotify/Makefile | 1 - fs/notify/inotify/inotify.c | 873 ----------------------------- fs/open.c | 1 + include/linux/fs.h | 5 - include/linux/fsnotify.h | 50 +- include/linux/inotify.h | 174 ------ kernel/auditsc.c | 1 - 10 files changed, 4 insertions(+), 1130 deletions(-) delete mode 100644 fs/notify/inotify/inotify.c (limited to 'fs') diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 1571c0c83dba..a8188bd3dab8 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -367,14 +367,6 @@ When: 2.6.33 Why: Should be implemented in userspace, policy daemon. Who: Johannes Berg ---------------------------- - -What: CONFIG_INOTIFY -When: 2.6.33 -Why: last user (audit) will be converted to the newer more generic - and more easily maintained fsnotify subsystem -Who: Eric Paris - ---------------------------- What: lock_policy_rwsem_* and unlock_policy_rwsem_* will not be diff --git a/fs/inode.c b/fs/inode.c index 722860b323a9..8e1bee998796 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include @@ -264,10 +263,6 @@ void inode_init_once(struct inode *inode) INIT_RAW_PRIO_TREE_ROOT(&inode->i_data.i_mmap); INIT_LIST_HEAD(&inode->i_data.i_mmap_nonlinear); i_size_ordered_init(inode); -#ifdef CONFIG_INOTIFY - INIT_LIST_HEAD(&inode->inotify_watches); - mutex_init(&inode->inotify_mutex); -#endif #ifdef CONFIG_FSNOTIFY INIT_HLIST_HEAD(&inode->i_fsnotify_mark_entries); #endif @@ -413,7 +408,6 @@ int invalidate_inodes(struct super_block *sb) down_write(&iprune_sem); spin_lock(&inode_lock); - inotify_unmount_inodes(&sb->s_inodes); fsnotify_unmount_inodes(&sb->s_inodes); busy = invalidate_list(&sb->s_inodes, &throw_away); spin_unlock(&inode_lock); diff --git a/fs/notify/inotify/Kconfig b/fs/notify/inotify/Kconfig index b3a159b21cfd..b981fc0c8379 100644 --- a/fs/notify/inotify/Kconfig +++ b/fs/notify/inotify/Kconfig @@ -1,18 +1,3 @@ -config INOTIFY - bool "Inotify file change notification support" - default n - ---help--- - Say Y here to enable legacy in kernel inotify support. Inotify is a - file change notification system. It is a replacement for dnotify. - This option only provides the legacy inotify in kernel API. There - are no in tree kernel users of this interface since it is deprecated. - You only need this if you are loading an out of tree kernel module - that uses inotify. - - For more information, see - - If unsure, say N. - config INOTIFY_USER bool "Inotify support for userspace" select ANON_INODES diff --git a/fs/notify/inotify/Makefile b/fs/notify/inotify/Makefile index 943828171362..a380dabe09de 100644 --- a/fs/notify/inotify/Makefile +++ b/fs/notify/inotify/Makefile @@ -1,2 +1 @@ -obj-$(CONFIG_INOTIFY) += inotify.o obj-$(CONFIG_INOTIFY_USER) += inotify_fsnotify.o inotify_user.o diff --git a/fs/notify/inotify/inotify.c b/fs/notify/inotify/inotify.c deleted file mode 100644 index 27b75ebc7460..000000000000 --- a/fs/notify/inotify/inotify.c +++ /dev/null @@ -1,873 +0,0 @@ -/* - * fs/inotify.c - inode-based file event notifications - * - * Authors: - * John McCutchan - * Robert Love - * - * Kernel API added by: Amy Griffis - * - * Copyright (C) 2005 John McCutchan - * Copyright 2006 Hewlett-Packard Development Company, L.P. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2, or (at your option) any - * later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static atomic_t inotify_cookie; - -/* - * Lock ordering: - * - * dentry->d_lock (used to keep d_move() away from dentry->d_parent) - * iprune_mutex (synchronize shrink_icache_memory()) - * inode_lock (protects the super_block->s_inodes list) - * inode->inotify_mutex (protects inode->inotify_watches and watches->i_list) - * inotify_handle->mutex (protects inotify_handle and watches->h_list) - * - * The inode->inotify_mutex and inotify_handle->mutex and held during execution - * of a caller's event handler. Thus, the caller must not hold any locks - * taken in their event handler while calling any of the published inotify - * interfaces. - */ - -/* - * Lifetimes of the three main data structures--inotify_handle, inode, and - * inotify_watch--are managed by reference count. - * - * inotify_handle: Lifetime is from inotify_init() to inotify_destroy(). - * Additional references can bump the count via get_inotify_handle() and drop - * the count via put_inotify_handle(). - * - * inotify_watch: for inotify's purposes, lifetime is from inotify_add_watch() - * to remove_watch_no_event(). Additional references can bump the count via - * get_inotify_watch() and drop the count via put_inotify_watch(). The caller - * is reponsible for the final put after receiving IN_IGNORED, or when using - * IN_ONESHOT after receiving the first event. Inotify does the final put if - * inotify_destroy() is called. - * - * inode: Pinned so long as the inode is associated with a watch, from - * inotify_add_watch() to the final put_inotify_watch(). - */ - -/* - * struct inotify_handle - represents an inotify instance - * - * This structure is protected by the mutex 'mutex'. - */ -struct inotify_handle { - struct idr idr; /* idr mapping wd -> watch */ - struct mutex mutex; /* protects this bad boy */ - struct list_head watches; /* list of watches */ - atomic_t count; /* reference count */ - u32 last_wd; /* the last wd allocated */ - const struct inotify_operations *in_ops; /* inotify caller operations */ -}; - -static inline void get_inotify_handle(struct inotify_handle *ih) -{ - atomic_inc(&ih->count); -} - -static inline void put_inotify_handle(struct inotify_handle *ih) -{ - if (atomic_dec_and_test(&ih->count)) { - idr_destroy(&ih->idr); - kfree(ih); - } -} - -/** - * get_inotify_watch - grab a reference to an inotify_watch - * @watch: watch to grab - */ -void get_inotify_watch(struct inotify_watch *watch) -{ - atomic_inc(&watch->count); -} -EXPORT_SYMBOL_GPL(get_inotify_watch); - -int pin_inotify_watch(struct inotify_watch *watch) -{ - struct super_block *sb = watch->inode->i_sb; - if (atomic_inc_not_zero(&sb->s_active)) { - atomic_inc(&watch->count); - return 1; - } - return 0; -} - -/** - * put_inotify_watch - decrements the ref count on a given watch. cleans up - * watch references if the count reaches zero. inotify_watch is freed by - * inotify callers via the destroy_watch() op. - * @watch: watch to release - */ -void put_inotify_watch(struct inotify_watch *watch) -{ - if (atomic_dec_and_test(&watch->count)) { - struct inotify_handle *ih = watch->ih; - - iput(watch->inode); - ih->in_ops->destroy_watch(watch); - put_inotify_handle(ih); - } -} -EXPORT_SYMBOL_GPL(put_inotify_watch); - -void unpin_inotify_watch(struct inotify_watch *watch) -{ - struct super_block *sb = watch->inode->i_sb; - put_inotify_watch(watch); - deactivate_super(sb); -} - -/* - * inotify_handle_get_wd - returns the next WD for use by the given handle - * - * Callers must hold ih->mutex. This function can sleep. - */ -static int inotify_handle_get_wd(struct inotify_handle *ih, - struct inotify_watch *watch) -{ - int ret; - - do { - if (unlikely(!idr_pre_get(&ih->idr, GFP_NOFS))) - return -ENOSPC; - ret = idr_get_new_above(&ih->idr, watch, ih->last_wd+1, &watch->wd); - } while (ret == -EAGAIN); - - if (likely(!ret)) - ih->last_wd = watch->wd; - - return ret; -} - -/* - * inotify_inode_watched - returns nonzero if there are watches on this inode - * and zero otherwise. We call this lockless, we do not care if we race. - */ -static inline int inotify_inode_watched(struct inode *inode) -{ - return !list_empty(&inode->inotify_watches); -} - -/* - * Get child dentry flag into synch with parent inode. - * Flag should always be clear for negative dentrys. - */ -static void set_dentry_child_flags(struct inode *inode, int watched) -{ - struct dentry *alias; - - spin_lock(&dcache_lock); - list_for_each_entry(alias, &inode->i_dentry, d_alias) { - struct dentry *child; - - list_for_each_entry(child, &alias->d_subdirs, d_u.d_child) { - if (!child->d_inode) - continue; - - spin_lock(&child->d_lock); - if (watched) - child->d_flags |= DCACHE_INOTIFY_PARENT_WATCHED; - else - child->d_flags &=~DCACHE_INOTIFY_PARENT_WATCHED; - spin_unlock(&child->d_lock); - } - } - spin_unlock(&dcache_lock); -} - -/* - * inotify_find_handle - find the watch associated with the given inode and - * handle - * - * Callers must hold inode->inotify_mutex. - */ -static struct inotify_watch *inode_find_handle(struct inode *inode, - struct inotify_handle *ih) -{ - struct inotify_watch *watch; - - list_for_each_entry(watch, &inode->inotify_watches, i_list) { - if (watch->ih == ih) - return watch; - } - - return NULL; -} - -/* - * remove_watch_no_event - remove watch without the IN_IGNORED event. - * - * Callers must hold both inode->inotify_mutex and ih->mutex. - */ -static void remove_watch_no_event(struct inotify_watch *watch, - struct inotify_handle *ih) -{ - list_del(&watch->i_list); - list_del(&watch->h_list); - - if (!inotify_inode_watched(watch->inode)) - set_dentry_child_flags(watch->inode, 0); - - idr_remove(&ih->idr, watch->wd); -} - -/** - * inotify_remove_watch_locked - Remove a watch from both the handle and the - * inode. Sends the IN_IGNORED event signifying that the inode is no longer - * watched. May be invoked from a caller's event handler. - * @ih: inotify handle associated with watch - * @watch: watch to remove - * - * Callers must hold both inode->inotify_mutex and ih->mutex. - */ -void inotify_remove_watch_locked(struct inotify_handle *ih, - struct inotify_watch *watch) -{ - remove_watch_no_event(watch, ih); - ih->in_ops->handle_event(watch, watch->wd, IN_IGNORED, 0, NULL, NULL); -} -EXPORT_SYMBOL_GPL(inotify_remove_watch_locked); - -/* Kernel API for producing events */ - -/* - * inotify_d_instantiate - instantiate dcache entry for inode - */ -void inotify_d_instantiate(struct dentry *entry, struct inode *inode) -{ - struct dentry *parent; - - if (!inode) - return; - - spin_lock(&entry->d_lock); - parent = entry->d_parent; - if (parent->d_inode && inotify_inode_watched(parent->d_inode)) - entry->d_flags |= DCACHE_INOTIFY_PARENT_WATCHED; - spin_unlock(&entry->d_lock); -} - -/* - * inotify_d_move - dcache entry has been moved - */ -void inotify_d_move(struct dentry *entry) -{ - struct dentry *parent; - - parent = entry->d_parent; - if (inotify_inode_watched(parent->d_inode)) - entry->d_flags |= DCACHE_INOTIFY_PARENT_WATCHED; - else - entry->d_flags &= ~DCACHE_INOTIFY_PARENT_WATCHED; -} - -/** - * inotify_inode_queue_event - queue an event to all watches on this inode - * @inode: inode event is originating from - * @mask: event mask describing this event - * @cookie: cookie for synchronization, or zero - * @name: filename, if any - * @n_inode: inode associated with name - */ -void inotify_inode_queue_event(struct inode *inode, u32 mask, u32 cookie, - const char *name, struct inode *n_inode) -{ - struct inotify_watch *watch, *next; - - if (!inotify_inode_watched(inode)) - return; - - mutex_lock(&inode->inotify_mutex); - list_for_each_entry_safe(watch, next, &inode->inotify_watches, i_list) { - u32 watch_mask = watch->mask; - if (watch_mask & mask) { - struct inotify_handle *ih= watch->ih; - mutex_lock(&ih->mutex); - if (watch_mask & IN_ONESHOT) - remove_watch_no_event(watch, ih); - ih->in_ops->handle_event(watch, watch->wd, mask, cookie, - name, n_inode); - mutex_unlock(&ih->mutex); - } - } - mutex_unlock(&inode->inotify_mutex); -} -EXPORT_SYMBOL_GPL(inotify_inode_queue_event); - -/** - * inotify_dentry_parent_queue_event - queue an event to a dentry's parent - * @dentry: the dentry in question, we queue against this dentry's parent - * @mask: event mask describing this event - * @cookie: cookie for synchronization, or zero - * @name: filename, if any - */ -void inotify_dentry_parent_queue_event(struct dentry *dentry, u32 mask, - u32 cookie, const char *name) -{ - struct dentry *parent; - struct inode *inode; - - if (!(dentry->d_flags & DCACHE_INOTIFY_PARENT_WATCHED)) - return; - - spin_lock(&dentry->d_lock); - parent = dentry->d_parent; - inode = parent->d_inode; - - if (inotify_inode_watched(inode)) { - dget(parent); - spin_unlock(&dentry->d_lock); - inotify_inode_queue_event(inode, mask, cookie, name, - dentry->d_inode); - dput(parent); - } else - spin_unlock(&dentry->d_lock); -} -EXPORT_SYMBOL_GPL(inotify_dentry_parent_queue_event); - -/** - * inotify_get_cookie - return a unique cookie for use in synchronizing events. - */ -u32 inotify_get_cookie(void) -{ - return atomic_inc_return(&inotify_cookie); -} -EXPORT_SYMBOL_GPL(inotify_get_cookie); - -/** - * inotify_unmount_inodes - an sb is unmounting. handle any watched inodes. - * @list: list of inodes being unmounted (sb->s_inodes) - * - * Called with inode_lock held, protecting the unmounting super block's list - * of inodes, and with iprune_mutex held, keeping shrink_icache_memory() at bay. - * We temporarily drop inode_lock, however, and CAN block. - */ -void inotify_unmount_inodes(struct list_head *list) -{ - struct inode *inode, *next_i, *need_iput = NULL; - - list_for_each_entry_safe(inode, next_i, list, i_sb_list) { - struct inotify_watch *watch, *next_w; - struct inode *need_iput_tmp; - struct list_head *watches; - - /* - * We cannot __iget() an inode in state I_CLEAR, I_FREEING, - * I_WILL_FREE, or I_NEW which is fine because by that point - * the inode cannot have any associated watches. - */ - if (inode->i_state & (I_CLEAR|I_FREEING|I_WILL_FREE|I_NEW)) - continue; - - /* - * If i_count is zero, the inode cannot have any watches and - * doing an __iget/iput with MS_ACTIVE clear would actually - * evict all inodes with zero i_count from icache which is - * unnecessarily violent and may in fact be illegal to do. - */ - if (!atomic_read(&inode->i_count)) - continue; - - need_iput_tmp = need_iput; - need_iput = NULL; - /* In case inotify_remove_watch_locked() drops a reference. */ - if (inode != need_iput_tmp) - __iget(inode); - else - need_iput_tmp = NULL; - /* In case the dropping of a reference would nuke next_i. */ - if ((&next_i->i_sb_list != list) && - atomic_read(&next_i->i_count) && - !(next_i->i_state & (I_CLEAR | I_FREEING | - I_WILL_FREE))) { - __iget(next_i); - need_iput = next_i; - } - - /* - * We can safely drop inode_lock here because we hold - * references on both inode and next_i. Also no new inodes - * will be added since the umount has begun. Finally, - * iprune_mutex keeps shrink_icache_memory() away. - */ - spin_unlock(&inode_lock); - - if (need_iput_tmp) - iput(need_iput_tmp); - - /* for each watch, send IN_UNMOUNT and then remove it */ - mutex_lock(&inode->inotify_mutex); - watches = &inode->inotify_watches; - list_for_each_entry_safe(watch, next_w, watches, i_list) { - struct inotify_handle *ih= watch->ih; - get_inotify_watch(watch); - mutex_lock(&ih->mutex); - ih->in_ops->handle_event(watch, watch->wd, IN_UNMOUNT, 0, - NULL, NULL); - inotify_remove_watch_locked(ih, watch); - mutex_unlock(&ih->mutex); - put_inotify_watch(watch); - } - mutex_unlock(&inode->inotify_mutex); - iput(inode); - - spin_lock(&inode_lock); - } -} -EXPORT_SYMBOL_GPL(inotify_unmount_inodes); - -/** - * inotify_inode_is_dead - an inode has been deleted, cleanup any watches - * @inode: inode that is about to be removed - */ -void inotify_inode_is_dead(struct inode *inode) -{ - struct inotify_watch *watch, *next; - - mutex_lock(&inode->inotify_mutex); - list_for_each_entry_safe(watch, next, &inode->inotify_watches, i_list) { - struct inotify_handle *ih = watch->ih; - mutex_lock(&ih->mutex); - inotify_remove_watch_locked(ih, watch); - mutex_unlock(&ih->mutex); - } - mutex_unlock(&inode->inotify_mutex); -} -EXPORT_SYMBOL_GPL(inotify_inode_is_dead); - -/* Kernel Consumer API */ - -/** - * inotify_init - allocate and initialize an inotify instance - * @ops: caller's inotify operations - */ -struct inotify_handle *inotify_init(const struct inotify_operations *ops) -{ - struct inotify_handle *ih; - - ih = kmalloc(sizeof(struct inotify_handle), GFP_KERNEL); - if (unlikely(!ih)) - return ERR_PTR(-ENOMEM); - - idr_init(&ih->idr); - INIT_LIST_HEAD(&ih->watches); - mutex_init(&ih->mutex); - ih->last_wd = 0; - ih->in_ops = ops; - atomic_set(&ih->count, 0); - get_inotify_handle(ih); - - return ih; -} -EXPORT_SYMBOL_GPL(inotify_init); - -/** - * inotify_init_watch - initialize an inotify watch - * @watch: watch to initialize - */ -void inotify_init_watch(struct inotify_watch *watch) -{ - INIT_LIST_HEAD(&watch->h_list); - INIT_LIST_HEAD(&watch->i_list); - atomic_set(&watch->count, 0); - get_inotify_watch(watch); /* initial get */ -} -EXPORT_SYMBOL_GPL(inotify_init_watch); - -/* - * Watch removals suck violently. To kick the watch out we need (in this - * order) inode->inotify_mutex and ih->mutex. That's fine if we have - * a hold on inode; however, for all other cases we need to make damn sure - * we don't race with umount. We can *NOT* just grab a reference to a - * watch - inotify_unmount_inodes() will happily sail past it and we'll end - * with reference to inode potentially outliving its superblock. Ideally - * we just want to grab an active reference to superblock if we can; that - * will make sure we won't go into inotify_umount_inodes() until we are - * done. Cleanup is just deactivate_super(). However, that leaves a messy - * case - what if we *are* racing with umount() and active references to - * superblock can't be acquired anymore? We can bump ->s_count, grab - * ->s_umount, which will wait until the superblock is shut down and the - * watch in question is pining for fjords. - * - * And yes, this is far beyond mere "not very pretty"; so's the entire - * concept of inotify to start with. - */ - -/** - * pin_to_kill - pin the watch down for removal - * @ih: inotify handle - * @watch: watch to kill - * - * Called with ih->mutex held, drops it. Possible return values: - * 0 - nothing to do, it has died - * 1 - remove it, drop the reference and deactivate_super() - */ -static int pin_to_kill(struct inotify_handle *ih, struct inotify_watch *watch) -{ - struct super_block *sb = watch->inode->i_sb; - - if (atomic_inc_not_zero(&sb->s_active)) { - get_inotify_watch(watch); - mutex_unlock(&ih->mutex); - return 1; /* the best outcome */ - } - spin_lock(&sb_lock); - sb->s_count++; - spin_unlock(&sb_lock); - mutex_unlock(&ih->mutex); /* can't grab ->s_umount under it */ - down_read(&sb->s_umount); - /* fs is already shut down; the watch is dead */ - drop_super(sb); - return 0; -} - -static void unpin_and_kill(struct inotify_watch *watch) -{ - struct super_block *sb = watch->inode->i_sb; - put_inotify_watch(watch); - deactivate_super(sb); -} - -/** - * inotify_destroy - clean up and destroy an inotify instance - * @ih: inotify handle - */ -void inotify_destroy(struct inotify_handle *ih) -{ - /* - * Destroy all of the watches for this handle. Unfortunately, not very - * pretty. We cannot do a simple iteration over the list, because we - * do not know the inode until we iterate to the watch. But we need to - * hold inode->inotify_mutex before ih->mutex. The following works. - * - * AV: it had to become even uglier to start working ;-/ - */ - while (1) { - struct inotify_watch *watch; - struct list_head *watches; - struct super_block *sb; - struct inode *inode; - - mutex_lock(&ih->mutex); - watches = &ih->watches; - if (list_empty(watches)) { - mutex_unlock(&ih->mutex); - break; - } - watch = list_first_entry(watches, struct inotify_watch, h_list); - sb = watch->inode->i_sb; - if (!pin_to_kill(ih, watch)) - continue; - - inode = watch->inode; - mutex_lock(&inode->inotify_mutex); - mutex_lock(&ih->mutex); - - /* make sure we didn't race with another list removal */ - if (likely(idr_find(&ih->idr, watch->wd))) { - remove_watch_no_event(watch, ih); - put_inotify_watch(watch); - } - - mutex_unlock(&ih->mutex); - mutex_unlock(&inode->inotify_mutex); - unpin_and_kill(watch); - } - - /* free this handle: the put matching the get in inotify_init() */ - put_inotify_handle(ih); -} -EXPORT_SYMBOL_GPL(inotify_destroy); - -/** - * inotify_find_watch - find an existing watch for an (ih,inode) pair - * @ih: inotify handle - * @inode: inode to watch - * @watchp: pointer to existing inotify_watch - * - * Caller must pin given inode (via nameidata). - */ -s32 inotify_find_watch(struct inotify_handle *ih, struct inode *inode, - struct inotify_watch **watchp) -{ - struct inotify_watch *old; - int ret = -ENOENT; - - mutex_lock(&inode->inotify_mutex); - mutex_lock(&ih->mutex); - - old = inode_find_handle(inode, ih); - if (unlikely(old)) { - get_inotify_watch(old); /* caller must put watch */ - *watchp = old; - ret = old->wd; - } - - mutex_unlock(&ih->mutex); - mutex_unlock(&inode->inotify_mutex); - - return ret; -} -EXPORT_SYMBOL_GPL(inotify_find_watch); - -/** - * inotify_find_update_watch - find and update the mask of an existing watch - * @ih: inotify handle - * @inode: inode's watch to update - * @mask: mask of events to watch - * - * Caller must pin given inode (via nameidata). - */ -s32 inotify_find_update_watch(struct inotify_handle *ih, struct inode *inode, - u32 mask) -{ - struct inotify_watch *old; - int mask_add = 0; - int ret; - - if (mask & IN_MASK_ADD) - mask_add = 1; - - /* don't allow invalid bits: we don't want flags set */ - mask &= IN_ALL_EVENTS | IN_ONESHOT; - if (unlikely(!mask)) - return -EINVAL; - - mutex_lock(&inode->inotify_mutex); - mutex_lock(&ih->mutex); - - /* - * Handle the case of re-adding a watch on an (inode,ih) pair that we - * are already watching. We just update the mask and return its wd. - */ - old = inode_find_handle(inode, ih); - if (unlikely(!old)) { - ret = -ENOENT; - goto out; - } - - if (mask_add) - old->mask |= mask; - else - old->mask = mask; - ret = old->wd; -out: - mutex_unlock(&ih->mutex); - mutex_unlock(&inode->inotify_mutex); - return ret; -} -EXPORT_SYMBOL_GPL(inotify_find_update_watch); - -/** - * inotify_add_watch - add a watch to an inotify instance - * @ih: inotify handle - * @watch: caller allocated watch structure - * @inode: inode to watch - * @mask: mask of events to watch - * - * Caller must pin given inode (via nameidata). - * Caller must ensure it only calls inotify_add_watch() once per watch. - * Calls inotify_handle_get_wd() so may sleep. - */ -s32 inotify_add_watch(struct inotify_handle *ih, struct inotify_watch *watch, - struct inode *inode, u32 mask) -{ - int ret = 0; - int newly_watched; - - /* don't allow invalid bits: we don't want flags set */ - mask &= IN_ALL_EVENTS | IN_ONESHOT; - if (unlikely(!mask)) - return -EINVAL; - watch->mask = mask; - - mutex_lock(&inode->inotify_mutex); - mutex_lock(&ih->mutex); - - /* Initialize a new watch */ - ret = inotify_handle_get_wd(ih, watch); - if (unlikely(ret)) - goto out; - ret = watch->wd; - - /* save a reference to handle and bump the count to make it official */ - get_inotify_handle(ih); - watch->ih = ih; - - /* - * Save a reference to the inode and bump the ref count to make it - * official. We hold a reference to nameidata, which makes this safe. - */ - watch->inode = igrab(inode); - - /* Add the watch to the handle's and the inode's list */ - newly_watched = !inotify_inode_watched(inode); - list_add(&watch->h_list, &ih->watches); - list_add(&watch->i_list, &inode->inotify_watches); - /* - * Set child flags _after_ adding the watch, so there is no race - * windows where newly instantiated children could miss their parent's - * watched flag. - */ - if (newly_watched) - set_dentry_child_flags(inode, 1); - -out: - mutex_unlock(&ih->mutex); - mutex_unlock(&inode->inotify_mutex); - return ret; -} -EXPORT_SYMBOL_GPL(inotify_add_watch); - -/** - * inotify_clone_watch - put the watch next to existing one - * @old: already installed watch - * @new: new watch - * - * Caller must hold the inotify_mutex of inode we are dealing with; - * it is expected to remove the old watch before unlocking the inode. - */ -s32 inotify_clone_watch(struct inotify_watch *old, struct inotify_watch *new) -{ - struct inotify_handle *ih = old->ih; - int ret = 0; - - new->mask = old->mask; - new->ih = ih; - - mutex_lock(&ih->mutex); - - /* Initialize a new watch */ - ret = inotify_handle_get_wd(ih, new); - if (unlikely(ret)) - goto out; - ret = new->wd; - - get_inotify_handle(ih); - - new->inode = igrab(old->inode); - - list_add(&new->h_list, &ih->watches); - list_add(&new->i_list, &old->inode->inotify_watches); -out: - mutex_unlock(&ih->mutex); - return ret; -} - -void inotify_evict_watch(struct inotify_watch *watch) -{ - get_inotify_watch(watch); - mutex_lock(&watch->ih->mutex); - inotify_remove_watch_locked(watch->ih, watch); - mutex_unlock(&watch->ih->mutex); -} - -/** - * inotify_rm_wd - remove a watch from an inotify instance - * @ih: inotify handle - * @wd: watch descriptor to remove - * - * Can sleep. - */ -int inotify_rm_wd(struct inotify_handle *ih, u32 wd) -{ - struct inotify_watch *watch; - struct super_block *sb; - struct inode *inode; - - mutex_lock(&ih->mutex); - watch = idr_find(&ih->idr, wd); - if (unlikely(!watch)) { - mutex_unlock(&ih->mutex); - return -EINVAL; - } - sb = watch->inode->i_sb; - if (!pin_to_kill(ih, watch)) - return 0; - - inode = watch->inode; - - mutex_lock(&inode->inotify_mutex); - mutex_lock(&ih->mutex); - - /* make sure that we did not race */ - if (likely(idr_find(&ih->idr, wd) == watch)) - inotify_remove_watch_locked(ih, watch); - - mutex_unlock(&ih->mutex); - mutex_unlock(&inode->inotify_mutex); - unpin_and_kill(watch); - - return 0; -} -EXPORT_SYMBOL_GPL(inotify_rm_wd); - -/** - * inotify_rm_watch - remove a watch from an inotify instance - * @ih: inotify handle - * @watch: watch to remove - * - * Can sleep. - */ -int inotify_rm_watch(struct inotify_handle *ih, - struct inotify_watch *watch) -{ - return inotify_rm_wd(ih, watch->wd); -} -EXPORT_SYMBOL_GPL(inotify_rm_watch); - -/* - * inotify_setup - core initialization function - */ -static int __init inotify_setup(void) -{ - BUILD_BUG_ON(IN_ACCESS != FS_ACCESS); - BUILD_BUG_ON(IN_MODIFY != FS_MODIFY); - BUILD_BUG_ON(IN_ATTRIB != FS_ATTRIB); - BUILD_BUG_ON(IN_CLOSE_WRITE != FS_CLOSE_WRITE); - BUILD_BUG_ON(IN_CLOSE_NOWRITE != FS_CLOSE_NOWRITE); - BUILD_BUG_ON(IN_OPEN != FS_OPEN); - BUILD_BUG_ON(IN_MOVED_FROM != FS_MOVED_FROM); - BUILD_BUG_ON(IN_MOVED_TO != FS_MOVED_TO); - BUILD_BUG_ON(IN_CREATE != FS_CREATE); - BUILD_BUG_ON(IN_DELETE != FS_DELETE); - BUILD_BUG_ON(IN_DELETE_SELF != FS_DELETE_SELF); - BUILD_BUG_ON(IN_MOVE_SELF != FS_MOVE_SELF); - BUILD_BUG_ON(IN_Q_OVERFLOW != FS_Q_OVERFLOW); - - BUILD_BUG_ON(IN_UNMOUNT != FS_UNMOUNT); - BUILD_BUG_ON(IN_ISDIR != FS_IN_ISDIR); - BUILD_BUG_ON(IN_IGNORED != FS_IN_IGNORED); - BUILD_BUG_ON(IN_ONESHOT != FS_IN_ONESHOT); - - atomic_set(&inotify_cookie, 0); - - return 0; -} - -module_init(inotify_setup); diff --git a/fs/open.c b/fs/open.c index 5463266db9e6..94d54d3efa8b 100644 --- a/fs/open.c +++ b/fs/open.c @@ -29,6 +29,7 @@ #include #include #include +#include #include "internal.h" diff --git a/include/linux/fs.h b/include/linux/fs.h index 68ca1b0491af..e5598d2f99b9 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -771,11 +771,6 @@ struct inode { struct hlist_head i_fsnotify_mark_entries; /* fsnotify mark entries */ #endif -#ifdef CONFIG_INOTIFY - struct list_head inotify_watches; /* watches on this inode */ - struct mutex inotify_mutex; /* protects the watches list */ -#endif - unsigned long i_state; unsigned long dirtied_when; /* jiffies of first dirtying */ diff --git a/include/linux/fsnotify.h b/include/linux/fsnotify.h index 01755909ce81..f958e93feb97 100644 --- a/include/linux/fsnotify.h +++ b/include/linux/fsnotify.h @@ -11,8 +11,6 @@ * (C) Copyright 2005 Robert Love */ -#include -#include #include #include #include @@ -25,16 +23,12 @@ static inline void fsnotify_d_instantiate(struct dentry *entry, struct inode *inode) { __fsnotify_d_instantiate(entry, inode); - - inotify_d_instantiate(entry, inode); } /* Notify this dentry's parent about a child's events. */ static inline void fsnotify_parent(struct dentry *dentry, __u32 mask) { __fsnotify_parent(dentry, mask); - - inotify_dentry_parent_queue_event(dentry, mask, 0, dentry->d_name.name); } /* @@ -48,8 +42,6 @@ static inline void fsnotify_d_move(struct dentry *entry) * cares about events from this entry. */ __fsnotify_update_dcache_flags(entry); - - inotify_d_move(entry); } /* @@ -57,8 +49,6 @@ static inline void fsnotify_d_move(struct dentry *entry) */ static inline void fsnotify_link_count(struct inode *inode) { - inotify_inode_queue_event(inode, IN_ATTRIB, 0, NULL, NULL); - fsnotify(inode, FS_ATTRIB, inode, FSNOTIFY_EVENT_INODE, NULL, 0); } @@ -70,7 +60,6 @@ static inline void fsnotify_move(struct inode *old_dir, struct inode *new_dir, int isdir, struct inode *target, struct dentry *moved) { struct inode *source = moved->d_inode; - u32 in_cookie = inotify_get_cookie(); u32 fs_cookie = fsnotify_get_cookie(); __u32 old_dir_mask = (FS_EVENT_ON_CHILD | FS_MOVED_FROM); __u32 new_dir_mask = (FS_EVENT_ON_CHILD | FS_MOVED_TO); @@ -80,31 +69,18 @@ static inline void fsnotify_move(struct inode *old_dir, struct inode *new_dir, old_dir_mask |= FS_DN_RENAME; if (isdir) { - isdir = IN_ISDIR; old_dir_mask |= FS_IN_ISDIR; new_dir_mask |= FS_IN_ISDIR; } - inotify_inode_queue_event(old_dir, IN_MOVED_FROM|isdir, in_cookie, old_name, - source); - inotify_inode_queue_event(new_dir, IN_MOVED_TO|isdir, in_cookie, new_name, - source); - fsnotify(old_dir, old_dir_mask, old_dir, FSNOTIFY_EVENT_INODE, old_name, fs_cookie); fsnotify(new_dir, new_dir_mask, new_dir, FSNOTIFY_EVENT_INODE, new_name, fs_cookie); - if (target) { - inotify_inode_queue_event(target, IN_DELETE_SELF, 0, NULL, NULL); - inotify_inode_is_dead(target); - - /* this is really a link_count change not a removal */ + if (target) fsnotify_link_count(target); - } - if (source) { - inotify_inode_queue_event(source, IN_MOVE_SELF, 0, NULL, NULL); + if (source) fsnotify(source, FS_MOVE_SELF, moved->d_inode, FSNOTIFY_EVENT_INODE, NULL, 0); - } audit_inode_child(moved, new_dir); } @@ -134,9 +110,6 @@ static inline void fsnotify_nameremove(struct dentry *dentry, int isdir) */ static inline void fsnotify_inoderemove(struct inode *inode) { - inotify_inode_queue_event(inode, IN_DELETE_SELF, 0, NULL, NULL); - inotify_inode_is_dead(inode); - fsnotify(inode, FS_DELETE_SELF, inode, FSNOTIFY_EVENT_INODE, NULL, 0); __fsnotify_inode_delete(inode); } @@ -146,8 +119,6 @@ static inline void fsnotify_inoderemove(struct inode *inode) */ static inline void fsnotify_create(struct inode *inode, struct dentry *dentry) { - inotify_inode_queue_event(inode, IN_CREATE, 0, dentry->d_name.name, - dentry->d_inode); audit_inode_child(dentry, inode); fsnotify(inode, FS_CREATE, dentry->d_inode, FSNOTIFY_EVENT_INODE, dentry->d_name.name, 0); @@ -160,8 +131,6 @@ static inline void fsnotify_create(struct inode *inode, struct dentry *dentry) */ static inline void fsnotify_link(struct inode *dir, struct inode *inode, struct dentry *new_dentry) { - inotify_inode_queue_event(dir, IN_CREATE, 0, new_dentry->d_name.name, - inode); fsnotify_link_count(inode); audit_inode_child(new_dentry, dir); @@ -176,7 +145,6 @@ static inline void fsnotify_mkdir(struct inode *inode, struct dentry *dentry) __u32 mask = (FS_CREATE | FS_IN_ISDIR); struct inode *d_inode = dentry->d_inode; - inotify_inode_queue_event(inode, mask, 0, dentry->d_name.name, d_inode); audit_inode_child(dentry, inode); fsnotify(inode, mask, d_inode, FSNOTIFY_EVENT_INODE, dentry->d_name.name, 0); @@ -193,8 +161,6 @@ static inline void fsnotify_access(struct dentry *dentry) if (S_ISDIR(inode->i_mode)) mask |= FS_IN_ISDIR; - inotify_inode_queue_event(inode, mask, 0, NULL, NULL); - fsnotify_parent(dentry, mask); fsnotify(inode, mask, inode, FSNOTIFY_EVENT_INODE, NULL, 0); } @@ -210,8 +176,6 @@ static inline void fsnotify_modify(struct dentry *dentry) if (S_ISDIR(inode->i_mode)) mask |= FS_IN_ISDIR; - inotify_inode_queue_event(inode, mask, 0, NULL, NULL); - fsnotify_parent(dentry, mask); fsnotify(inode, mask, inode, FSNOTIFY_EVENT_INODE, NULL, 0); } @@ -227,8 +191,6 @@ static inline void fsnotify_open(struct dentry *dentry) if (S_ISDIR(inode->i_mode)) mask |= FS_IN_ISDIR; - inotify_inode_queue_event(inode, mask, 0, NULL, NULL); - fsnotify_parent(dentry, mask); fsnotify(inode, mask, inode, FSNOTIFY_EVENT_INODE, NULL, 0); } @@ -246,8 +208,6 @@ static inline void fsnotify_close(struct file *file) if (S_ISDIR(inode->i_mode)) mask |= FS_IN_ISDIR; - inotify_inode_queue_event(inode, mask, 0, NULL, NULL); - fsnotify_parent(dentry, mask); fsnotify(inode, mask, file, FSNOTIFY_EVENT_FILE, NULL, 0); } @@ -263,8 +223,6 @@ static inline void fsnotify_xattr(struct dentry *dentry) if (S_ISDIR(inode->i_mode)) mask |= FS_IN_ISDIR; - inotify_inode_queue_event(inode, mask, 0, NULL, NULL); - fsnotify_parent(dentry, mask); fsnotify(inode, mask, inode, FSNOTIFY_EVENT_INODE, NULL, 0); } @@ -299,14 +257,12 @@ static inline void fsnotify_change(struct dentry *dentry, unsigned int ia_valid) if (mask) { if (S_ISDIR(inode->i_mode)) mask |= FS_IN_ISDIR; - inotify_inode_queue_event(inode, mask, 0, NULL, NULL); - fsnotify_parent(dentry, mask); fsnotify(inode, mask, inode, FSNOTIFY_EVENT_INODE, NULL, 0); } } -#if defined(CONFIG_INOTIFY) || defined(CONFIG_FSNOTIFY) /* notify helpers */ +#if defined(CONFIG_FSNOTIFY) /* notify helpers */ /* * fsnotify_oldname_init - save off the old filename before we change it diff --git a/include/linux/inotify.h b/include/linux/inotify.h index 37ea2894b3c0..959a38b8f75d 100644 --- a/include/linux/inotify.h +++ b/include/linux/inotify.h @@ -69,178 +69,4 @@ struct inotify_event { #define IN_CLOEXEC O_CLOEXEC #define IN_NONBLOCK O_NONBLOCK -#ifdef __KERNEL__ - -#include -#include - -/* - * struct inotify_watch - represents a watch request on a specific inode - * - * h_list is protected by ih->mutex of the associated inotify_handle. - * i_list, mask are protected by inode->inotify_mutex of the associated inode. - * ih, inode, and wd are never written to once the watch is created. - * - * Callers must use the established inotify interfaces to access inotify_watch - * contents. The content of this structure is private to the inotify - * implementation. - */ -struct inotify_watch { - struct list_head h_list; /* entry in inotify_handle's list */ - struct list_head i_list; /* entry in inode's list */ - atomic_t count; /* reference count */ - struct inotify_handle *ih; /* associated inotify handle */ - struct inode *inode; /* associated inode */ - __s32 wd; /* watch descriptor */ - __u32 mask; /* event mask for this watch */ -}; - -struct inotify_operations { - void (*handle_event)(struct inotify_watch *, u32, u32, u32, - const char *, struct inode *); - void (*destroy_watch)(struct inotify_watch *); -}; - -#ifdef CONFIG_INOTIFY - -/* Kernel API for producing events */ - -extern void inotify_d_instantiate(struct dentry *, struct inode *); -extern void inotify_d_move(struct dentry *); -extern void inotify_inode_queue_event(struct inode *, __u32, __u32, - const char *, struct inode *); -extern void inotify_dentry_parent_queue_event(struct dentry *, __u32, __u32, - const char *); -extern void inotify_unmount_inodes(struct list_head *); -extern void inotify_inode_is_dead(struct inode *); -extern u32 inotify_get_cookie(void); - -/* Kernel Consumer API */ - -extern struct inotify_handle *inotify_init(const struct inotify_operations *); -extern void inotify_init_watch(struct inotify_watch *); -extern void inotify_destroy(struct inotify_handle *); -extern __s32 inotify_find_watch(struct inotify_handle *, struct inode *, - struct inotify_watch **); -extern __s32 inotify_find_update_watch(struct inotify_handle *, struct inode *, - u32); -extern __s32 inotify_add_watch(struct inotify_handle *, struct inotify_watch *, - struct inode *, __u32); -extern __s32 inotify_clone_watch(struct inotify_watch *, struct inotify_watch *); -extern void inotify_evict_watch(struct inotify_watch *); -extern int inotify_rm_watch(struct inotify_handle *, struct inotify_watch *); -extern int inotify_rm_wd(struct inotify_handle *, __u32); -extern void inotify_remove_watch_locked(struct inotify_handle *, - struct inotify_watch *); -extern void get_inotify_watch(struct inotify_watch *); -extern void put_inotify_watch(struct inotify_watch *); -extern int pin_inotify_watch(struct inotify_watch *); -extern void unpin_inotify_watch(struct inotify_watch *); - -#else - -static inline void inotify_d_instantiate(struct dentry *dentry, - struct inode *inode) -{ -} - -static inline void inotify_d_move(struct dentry *dentry) -{ -} - -static inline void inotify_inode_queue_event(struct inode *inode, - __u32 mask, __u32 cookie, - const char *filename, - struct inode *n_inode) -{ -} - -static inline void inotify_dentry_parent_queue_event(struct dentry *dentry, - __u32 mask, __u32 cookie, - const char *filename) -{ -} - -static inline void inotify_unmount_inodes(struct list_head *list) -{ -} - -static inline void inotify_inode_is_dead(struct inode *inode) -{ -} - -static inline u32 inotify_get_cookie(void) -{ - return 0; -} - -static inline struct inotify_handle *inotify_init(const struct inotify_operations *ops) -{ - return ERR_PTR(-EOPNOTSUPP); -} - -static inline void inotify_init_watch(struct inotify_watch *watch) -{ -} - -static inline void inotify_destroy(struct inotify_handle *ih) -{ -} - -static inline __s32 inotify_find_watch(struct inotify_handle *ih, struct inode *inode, - struct inotify_watch **watchp) -{ - return -EOPNOTSUPP; -} - -static inline __s32 inotify_find_update_watch(struct inotify_handle *ih, - struct inode *inode, u32 mask) -{ - return -EOPNOTSUPP; -} - -static inline __s32 inotify_add_watch(struct inotify_handle *ih, - struct inotify_watch *watch, - struct inode *inode, __u32 mask) -{ - return -EOPNOTSUPP; -} - -static inline int inotify_rm_watch(struct inotify_handle *ih, - struct inotify_watch *watch) -{ - return -EOPNOTSUPP; -} - -static inline int inotify_rm_wd(struct inotify_handle *ih, __u32 wd) -{ - return -EOPNOTSUPP; -} - -static inline void inotify_remove_watch_locked(struct inotify_handle *ih, - struct inotify_watch *watch) -{ -} - -static inline void get_inotify_watch(struct inotify_watch *watch) -{ -} - -static inline void put_inotify_watch(struct inotify_watch *watch) -{ -} - -extern inline int pin_inotify_watch(struct inotify_watch *watch) -{ - return 0; -} - -extern inline void unpin_inotify_watch(struct inotify_watch *watch) -{ -} - -#endif /* CONFIG_INOTIFY */ - -#endif /* __KERNEL __ */ - #endif /* _LINUX_INOTIFY_H */ diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 786901cd8217..853185f7ba7e 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -65,7 +65,6 @@ #include #include #include -#include #include #include -- cgit v1.2.3 From d7f0ce4e436b6109527c51b0efe0deff53d215f7 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Tue, 22 Dec 2009 23:16:33 -0500 Subject: inotify: do not spam console without limit inotify was supposed to have a dmesg printk ratelimitor which would cause inotify to only emit one message per boot. The static bool was never set so it kept firing messages. This patch correctly limits warnings in multiple places. Signed-off-by: Eric Paris --- fs/notify/inotify/inotify_user.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'fs') diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index ce21ebaee89e..f2b542479e91 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -449,20 +449,18 @@ static void inotify_remove_from_idr(struct fsnotify_group *group, * if it wasn't.... */ if (wd == -1) { - printk(KERN_WARNING "%s: ientry=%p ientry->wd=%d ientry->group=%p" + WARN_ONCE(1, "%s: ientry=%p ientry->wd=%d ientry->group=%p" " ientry->inode=%p\n", __func__, ientry, ientry->wd, ientry->fsn_entry.group, ientry->fsn_entry.inode); - WARN_ON(1); goto out; } /* Lets look in the idr to see if we find it */ found_ientry = inotify_idr_find_locked(group, wd); if (unlikely(!found_ientry)) { - printk(KERN_WARNING "%s: ientry=%p ientry->wd=%d ientry->group=%p" + WARN_ONCE(1, "%s: ientry=%p ientry->wd=%d ientry->group=%p" " ientry->inode=%p\n", __func__, ientry, ientry->wd, ientry->fsn_entry.group, ientry->fsn_entry.inode); - WARN_ON(1); goto out; } @@ -472,8 +470,7 @@ static void inotify_remove_from_idr(struct fsnotify_group *group, * fucked up somewhere. */ if (unlikely(found_ientry != ientry)) { - WARN_ON(1); - printk(KERN_WARNING "%s: ientry=%p ientry->wd=%d ientry->group=%p " + WARN_ONCE(1, "%s: ientry=%p ientry->wd=%d ientry->group=%p " "entry->inode=%p found_ientry=%p found_ientry->wd=%d " "found_ientry->group=%p found_ientry->inode=%p\n", __func__, ientry, ientry->wd, ientry->fsn_entry.group, @@ -489,7 +486,7 @@ static void inotify_remove_from_idr(struct fsnotify_group *group, * one ref grabbed by inotify_idr_find */ if (unlikely(atomic_read(&ientry->fsn_entry.refcnt) < 3)) { - printk(KERN_WARNING "%s: ientry=%p ientry->wd=%d ientry->group=%p" + printk(KERN_ERR "%s: ientry=%p ientry->wd=%d ientry->group=%p" " ientry->inode=%p\n", __func__, ientry, ientry->wd, ientry->fsn_entry.group, ientry->fsn_entry.inode); /* we can't really recover with bad ref cnting.. */ -- cgit v1.2.3 From 7b0a04fbfb35650941af87728d4891515b4fc179 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:21 -0500 Subject: fsnotify: provide the data type to should_send_event fanotify is only interested in event types which contain enough information to open the original file in the context of the fanotify listener. Since fanotify may not want to send events if that data isn't present we pass the data type to the should_send_event function call so fanotify can express its lack of interest. Signed-off-by: Eric Paris --- fs/notify/dnotify/dnotify.c | 3 ++- fs/notify/fsnotify.c | 2 +- fs/notify/inotify/inotify_fsnotify.c | 3 ++- include/linux/fsnotify_backend.h | 3 ++- kernel/audit_tree.c | 3 ++- kernel/audit_watch.c | 3 ++- 6 files changed, 11 insertions(+), 6 deletions(-) (limited to 'fs') diff --git a/fs/notify/dnotify/dnotify.c b/fs/notify/dnotify/dnotify.c index 85b97fca14de..6f30f496e235 100644 --- a/fs/notify/dnotify/dnotify.c +++ b/fs/notify/dnotify/dnotify.c @@ -133,7 +133,8 @@ static int dnotify_handle_event(struct fsnotify_group *group, * userspace notification for that pair. */ static bool dnotify_should_send_event(struct fsnotify_group *group, - struct inode *inode, __u32 mask) + struct inode *inode, __u32 mask, + int data_type) { struct fsnotify_mark_entry *entry; bool send; diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index fcc2f064af83..fc06e4789392 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -157,7 +157,7 @@ void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, const idx = srcu_read_lock(&fsnotify_grp_srcu); list_for_each_entry_rcu(group, &fsnotify_groups, group_list) { if (test_mask & group->mask) { - if (!group->ops->should_send_event(group, to_tell, mask)) + if (!group->ops->should_send_event(group, to_tell, mask, data_is)) continue; if (!event) { event = fsnotify_create_event(to_tell, mask, data, diff --git a/fs/notify/inotify/inotify_fsnotify.c b/fs/notify/inotify/inotify_fsnotify.c index e27960cd76ab..fc7c4952e6a2 100644 --- a/fs/notify/inotify/inotify_fsnotify.c +++ b/fs/notify/inotify/inotify_fsnotify.c @@ -86,7 +86,8 @@ static void inotify_freeing_mark(struct fsnotify_mark_entry *entry, struct fsnot inotify_ignored_and_remove_idr(entry, group); } -static bool inotify_should_send_event(struct fsnotify_group *group, struct inode *inode, __u32 mask) +static bool inotify_should_send_event(struct fsnotify_group *group, struct inode *inode, + __u32 mask, int data_type) { struct fsnotify_mark_entry *entry; bool send; diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index e25284371020..61aed0c54fe9 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -84,7 +84,8 @@ struct fsnotify_event_private_data; * valid group and inode to use to clean up. */ struct fsnotify_ops { - bool (*should_send_event)(struct fsnotify_group *group, struct inode *inode, __u32 mask); + bool (*should_send_event)(struct fsnotify_group *group, struct inode *inode, + __u32 mask, int data_type); int (*handle_event)(struct fsnotify_group *group, struct fsnotify_event *event); void (*free_group_priv)(struct fsnotify_group *group); void (*freeing_mark)(struct fsnotify_mark_entry *entry, struct fsnotify_group *group); diff --git a/kernel/audit_tree.c b/kernel/audit_tree.c index a164600dd82e..b5417cd65216 100644 --- a/kernel/audit_tree.c +++ b/kernel/audit_tree.c @@ -919,7 +919,8 @@ static void audit_tree_freeing_mark(struct fsnotify_mark_entry *entry, struct fs fsnotify_put_mark(entry); } -static bool audit_tree_send_event(struct fsnotify_group *group, struct inode *inode, __u32 mask) +static bool audit_tree_send_event(struct fsnotify_group *group, struct inode *inode, + __u32 mask, int data_type) { return 0; } diff --git a/kernel/audit_watch.c b/kernel/audit_watch.c index f8543a41115b..67d8f2f52874 100644 --- a/kernel/audit_watch.c +++ b/kernel/audit_watch.c @@ -505,7 +505,8 @@ void audit_remove_watch_rule(struct audit_krule *krule) } } -static bool audit_watch_should_send_event(struct fsnotify_group *group, struct inode *inode, __u32 mask) +static bool audit_watch_should_send_event(struct fsnotify_group *group, struct inode *inode, + __u32 mask, int data_type) { struct fsnotify_mark_entry *entry; bool send; -- cgit v1.2.3 From 8112e2d6a7356e8c3ff1f7f3c86f375ed0305705 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:21 -0500 Subject: fsnotify: include data in should_send calls fanotify is going to need to look at file->private_data to know if an event should be sent or not. This passes the data (which might be a file, dentry, inode, or none) to the should_send function calls so fanotify can get that information when available Signed-off-by: Eric Paris --- fs/notify/dnotify/dnotify.c | 2 +- fs/notify/fsnotify.c | 3 ++- fs/notify/inotify/inotify_fsnotify.c | 2 +- include/linux/fsnotify_backend.h | 2 +- kernel/audit_tree.c | 2 +- kernel/audit_watch.c | 2 +- 6 files changed, 7 insertions(+), 6 deletions(-) (limited to 'fs') diff --git a/fs/notify/dnotify/dnotify.c b/fs/notify/dnotify/dnotify.c index 6f30f496e235..a213b83a59cf 100644 --- a/fs/notify/dnotify/dnotify.c +++ b/fs/notify/dnotify/dnotify.c @@ -134,7 +134,7 @@ static int dnotify_handle_event(struct fsnotify_group *group, */ static bool dnotify_should_send_event(struct fsnotify_group *group, struct inode *inode, __u32 mask, - int data_type) + void *data, int data_type) { struct fsnotify_mark_entry *entry; bool send; diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index fc06e4789392..523337b600a0 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -157,7 +157,8 @@ void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, const idx = srcu_read_lock(&fsnotify_grp_srcu); list_for_each_entry_rcu(group, &fsnotify_groups, group_list) { if (test_mask & group->mask) { - if (!group->ops->should_send_event(group, to_tell, mask, data_is)) + if (!group->ops->should_send_event(group, to_tell, mask, + data, data_is)) continue; if (!event) { event = fsnotify_create_event(to_tell, mask, data, diff --git a/fs/notify/inotify/inotify_fsnotify.c b/fs/notify/inotify/inotify_fsnotify.c index fc7c4952e6a2..1f33234cc308 100644 --- a/fs/notify/inotify/inotify_fsnotify.c +++ b/fs/notify/inotify/inotify_fsnotify.c @@ -87,7 +87,7 @@ static void inotify_freeing_mark(struct fsnotify_mark_entry *entry, struct fsnot } static bool inotify_should_send_event(struct fsnotify_group *group, struct inode *inode, - __u32 mask, int data_type) + __u32 mask, void *data, int data_type) { struct fsnotify_mark_entry *entry; bool send; diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index 61aed0c54fe9..2766df67f1ec 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -85,7 +85,7 @@ struct fsnotify_event_private_data; */ struct fsnotify_ops { bool (*should_send_event)(struct fsnotify_group *group, struct inode *inode, - __u32 mask, int data_type); + __u32 mask, void *data, int data_type); int (*handle_event)(struct fsnotify_group *group, struct fsnotify_event *event); void (*free_group_priv)(struct fsnotify_group *group); void (*freeing_mark)(struct fsnotify_mark_entry *entry, struct fsnotify_group *group); diff --git a/kernel/audit_tree.c b/kernel/audit_tree.c index b5417cd65216..e3d63b596ef0 100644 --- a/kernel/audit_tree.c +++ b/kernel/audit_tree.c @@ -920,7 +920,7 @@ static void audit_tree_freeing_mark(struct fsnotify_mark_entry *entry, struct fs } static bool audit_tree_send_event(struct fsnotify_group *group, struct inode *inode, - __u32 mask, int data_type) + __u32 mask, void *data, int data_type) { return 0; } diff --git a/kernel/audit_watch.c b/kernel/audit_watch.c index 67d8f2f52874..85c43aa292e0 100644 --- a/kernel/audit_watch.c +++ b/kernel/audit_watch.c @@ -506,7 +506,7 @@ void audit_remove_watch_rule(struct audit_krule *krule) } static bool audit_watch_should_send_event(struct fsnotify_group *group, struct inode *inode, - __u32 mask, int data_type) + __u32 mask, void *data, int data_type) { struct fsnotify_mark_entry *entry; bool send; -- cgit v1.2.3 From 2a12a9d7814631e918dec93abad856e692d5286d Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:21 -0500 Subject: fsnotify: pass a file instead of an inode to open, read, and write fanotify, the upcoming notification system actually needs a struct path so it can do opens in the context of listeners, and it needs a file so it can get f_flags from the original process. Close was the only operation that already was passing a struct file to the notification hook. This patch passes a file for access, modify, and open as well as they are easily available to these hooks. Signed-off-by: Eric Paris --- fs/compat.c | 5 ++--- fs/exec.c | 4 ++-- fs/nfsd/vfs.c | 4 ++-- fs/open.c | 2 +- fs/read_write.c | 8 ++++---- include/linux/fsnotify.h | 15 +++++++++------ 6 files changed, 20 insertions(+), 18 deletions(-) (limited to 'fs') diff --git a/fs/compat.c b/fs/compat.c index 6490d2134ff3..ce02278b9c83 100644 --- a/fs/compat.c +++ b/fs/compat.c @@ -1193,11 +1193,10 @@ out: if (iov != iovstack) kfree(iov); if ((ret + (type == READ)) > 0) { - struct dentry *dentry = file->f_path.dentry; if (type == READ) - fsnotify_access(dentry); + fsnotify_access(file); else - fsnotify_modify(dentry); + fsnotify_modify(file); } return ret; } diff --git a/fs/exec.c b/fs/exec.c index e19de6a80339..f2de04a01a2a 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -129,7 +129,7 @@ SYSCALL_DEFINE1(uselib, const char __user *, library) if (file->f_path.mnt->mnt_flags & MNT_NOEXEC) goto exit; - fsnotify_open(file->f_path.dentry); + fsnotify_open(file); error = -ENOEXEC; if(file->f_op) { @@ -683,7 +683,7 @@ struct file *open_exec(const char *name) if (file->f_path.mnt->mnt_flags & MNT_NOEXEC) goto exit; - fsnotify_open(file->f_path.dentry); + fsnotify_open(file); err = deny_write_access(file); if (err) diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 3c111120b619..16114a8e79d4 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -951,7 +951,7 @@ nfsd_vfs_read(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, nfsdstats.io_read += host_err; *count = host_err; err = 0; - fsnotify_access(file->f_path.dentry); + fsnotify_access(file); } else err = nfserrno(host_err); out: @@ -1062,7 +1062,7 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, goto out_nfserr; *cnt = host_err; nfsdstats.io_write += host_err; - fsnotify_modify(file->f_path.dentry); + fsnotify_modify(file); /* clear setuid/setgid flag after write */ if (inode->i_mode & (S_ISUID | S_ISGID)) diff --git a/fs/open.c b/fs/open.c index 94d54d3efa8b..bf082635e257 100644 --- a/fs/open.c +++ b/fs/open.c @@ -889,7 +889,7 @@ long do_sys_open(int dfd, const char __user *filename, int flags, int mode) put_unused_fd(fd); fd = PTR_ERR(f); } else { - fsnotify_open(f->f_path.dentry); + fsnotify_open(f); fd_install(fd, f); } } diff --git a/fs/read_write.c b/fs/read_write.c index 9c0485236e68..74e36586e4d3 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -311,7 +311,7 @@ ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos) else ret = do_sync_read(file, buf, count, pos); if (ret > 0) { - fsnotify_access(file->f_path.dentry); + fsnotify_access(file); add_rchar(current, ret); } inc_syscr(current); @@ -367,7 +367,7 @@ ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_ else ret = do_sync_write(file, buf, count, pos); if (ret > 0) { - fsnotify_modify(file->f_path.dentry); + fsnotify_modify(file); add_wchar(current, ret); } inc_syscw(current); @@ -675,9 +675,9 @@ out: kfree(iov); if ((ret + (type == READ)) > 0) { if (type == READ) - fsnotify_access(file->f_path.dentry); + fsnotify_access(file); else - fsnotify_modify(file->f_path.dentry); + fsnotify_modify(file); } return ret; } diff --git a/include/linux/fsnotify.h b/include/linux/fsnotify.h index f958e93feb97..845e57abfb86 100644 --- a/include/linux/fsnotify.h +++ b/include/linux/fsnotify.h @@ -153,8 +153,9 @@ static inline void fsnotify_mkdir(struct inode *inode, struct dentry *dentry) /* * fsnotify_access - file was read */ -static inline void fsnotify_access(struct dentry *dentry) +static inline void fsnotify_access(struct file *file) { + struct dentry *dentry = file->f_path.dentry; struct inode *inode = dentry->d_inode; __u32 mask = FS_ACCESS; @@ -162,14 +163,15 @@ static inline void fsnotify_access(struct dentry *dentry) mask |= FS_IN_ISDIR; fsnotify_parent(dentry, mask); - fsnotify(inode, mask, inode, FSNOTIFY_EVENT_INODE, NULL, 0); + fsnotify(inode, mask, file, FSNOTIFY_EVENT_FILE, NULL, 0); } /* * fsnotify_modify - file was modified */ -static inline void fsnotify_modify(struct dentry *dentry) +static inline void fsnotify_modify(struct file *file) { + struct dentry *dentry = file->f_path.dentry; struct inode *inode = dentry->d_inode; __u32 mask = FS_MODIFY; @@ -177,14 +179,15 @@ static inline void fsnotify_modify(struct dentry *dentry) mask |= FS_IN_ISDIR; fsnotify_parent(dentry, mask); - fsnotify(inode, mask, inode, FSNOTIFY_EVENT_INODE, NULL, 0); + fsnotify(inode, mask, file, FSNOTIFY_EVENT_FILE, NULL, 0); } /* * fsnotify_open - file was opened */ -static inline void fsnotify_open(struct dentry *dentry) +static inline void fsnotify_open(struct file *file) { + struct dentry *dentry = file->f_path.dentry; struct inode *inode = dentry->d_inode; __u32 mask = FS_OPEN; @@ -192,7 +195,7 @@ static inline void fsnotify_open(struct dentry *dentry) mask |= FS_IN_ISDIR; fsnotify_parent(dentry, mask); - fsnotify(inode, mask, inode, FSNOTIFY_EVENT_INODE, NULL, 0); + fsnotify(inode, mask, file, FSNOTIFY_EVENT_FILE, NULL, 0); } /* -- cgit v1.2.3 From 28c60e37f874dcbb93c4afc839ba5e4911c4f4bc Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:21 -0500 Subject: fsnotify: send struct file when sending events to parents when possible fanotify needs a path in order to open an fd to the object which changed. Currently notifications to inode's parents are done using only the inode. For some parental notification we have the entire file, send that so fanotify can use it. Signed-off-by: Eric Paris --- fs/notify/fsnotify.c | 13 ++++++++++--- include/linux/fsnotify.h | 40 +++++++++++++++++++++------------------- include/linux/fsnotify_backend.h | 4 ++-- 3 files changed, 33 insertions(+), 24 deletions(-) (limited to 'fs') diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index 523337b600a0..806beede24a3 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -78,13 +78,16 @@ void __fsnotify_update_child_dentry_flags(struct inode *inode) } /* Notify this dentry's parent about a child's events. */ -void __fsnotify_parent(struct dentry *dentry, __u32 mask) +void __fsnotify_parent(struct file *file, struct dentry *dentry, __u32 mask) { struct dentry *parent; struct inode *p_inode; bool send = false; bool should_update_children = false; + if (file) + dentry = file->f_path.dentry; + if (!(dentry->d_flags & DCACHE_FSNOTIFY_PARENT_WATCHED)) return; @@ -115,8 +118,12 @@ void __fsnotify_parent(struct dentry *dentry, __u32 mask) * specifies these are events which came from a child. */ mask |= FS_EVENT_ON_CHILD; - fsnotify(p_inode, mask, dentry->d_inode, FSNOTIFY_EVENT_INODE, - dentry->d_name.name, 0); + if (file) + fsnotify(p_inode, mask, file, FSNOTIFY_EVENT_FILE, + dentry->d_name.name, 0); + else + fsnotify(p_inode, mask, dentry->d_inode, FSNOTIFY_EVENT_INODE, + dentry->d_name.name, 0); dput(parent); } diff --git a/include/linux/fsnotify.h b/include/linux/fsnotify.h index 845e57abfb86..04ea03ea8090 100644 --- a/include/linux/fsnotify.h +++ b/include/linux/fsnotify.h @@ -26,9 +26,14 @@ static inline void fsnotify_d_instantiate(struct dentry *entry, } /* Notify this dentry's parent about a child's events. */ -static inline void fsnotify_parent(struct dentry *dentry, __u32 mask) +static inline void fsnotify_parent(struct file *file, struct dentry *dentry, __u32 mask) { - __fsnotify_parent(dentry, mask); + BUG_ON(file && dentry); + + if (file) + dentry = file->f_path.dentry; + + __fsnotify_parent(file, dentry, mask); } /* @@ -102,7 +107,7 @@ static inline void fsnotify_nameremove(struct dentry *dentry, int isdir) if (isdir) mask |= FS_IN_ISDIR; - fsnotify_parent(dentry, mask); + fsnotify_parent(NULL, dentry, mask); } /* @@ -155,14 +160,13 @@ static inline void fsnotify_mkdir(struct inode *inode, struct dentry *dentry) */ static inline void fsnotify_access(struct file *file) { - struct dentry *dentry = file->f_path.dentry; - struct inode *inode = dentry->d_inode; + struct inode *inode = file->f_path.dentry->d_inode; __u32 mask = FS_ACCESS; if (S_ISDIR(inode->i_mode)) mask |= FS_IN_ISDIR; - fsnotify_parent(dentry, mask); + fsnotify_parent(file, NULL, mask); fsnotify(inode, mask, file, FSNOTIFY_EVENT_FILE, NULL, 0); } @@ -171,14 +175,13 @@ static inline void fsnotify_access(struct file *file) */ static inline void fsnotify_modify(struct file *file) { - struct dentry *dentry = file->f_path.dentry; - struct inode *inode = dentry->d_inode; + struct inode *inode = file->f_path.dentry->d_inode; __u32 mask = FS_MODIFY; if (S_ISDIR(inode->i_mode)) mask |= FS_IN_ISDIR; - fsnotify_parent(dentry, mask); + fsnotify_parent(file, NULL, mask); fsnotify(inode, mask, file, FSNOTIFY_EVENT_FILE, NULL, 0); } @@ -187,14 +190,13 @@ static inline void fsnotify_modify(struct file *file) */ static inline void fsnotify_open(struct file *file) { - struct dentry *dentry = file->f_path.dentry; - struct inode *inode = dentry->d_inode; + struct inode *inode = file->f_path.dentry->d_inode; __u32 mask = FS_OPEN; if (S_ISDIR(inode->i_mode)) mask |= FS_IN_ISDIR; - fsnotify_parent(dentry, mask); + fsnotify_parent(file, NULL, mask); fsnotify(inode, mask, file, FSNOTIFY_EVENT_FILE, NULL, 0); } @@ -203,15 +205,14 @@ static inline void fsnotify_open(struct file *file) */ static inline void fsnotify_close(struct file *file) { - struct dentry *dentry = file->f_path.dentry; - struct inode *inode = dentry->d_inode; + struct inode *inode = file->f_path.dentry->d_inode; fmode_t mode = file->f_mode; __u32 mask = (mode & FMODE_WRITE) ? FS_CLOSE_WRITE : FS_CLOSE_NOWRITE; if (S_ISDIR(inode->i_mode)) mask |= FS_IN_ISDIR; - fsnotify_parent(dentry, mask); + fsnotify_parent(file, NULL, mask); fsnotify(inode, mask, file, FSNOTIFY_EVENT_FILE, NULL, 0); } @@ -226,7 +227,7 @@ static inline void fsnotify_xattr(struct dentry *dentry) if (S_ISDIR(inode->i_mode)) mask |= FS_IN_ISDIR; - fsnotify_parent(dentry, mask); + fsnotify_parent(NULL, dentry, mask); fsnotify(inode, mask, inode, FSNOTIFY_EVENT_INODE, NULL, 0); } @@ -260,7 +261,8 @@ static inline void fsnotify_change(struct dentry *dentry, unsigned int ia_valid) if (mask) { if (S_ISDIR(inode->i_mode)) mask |= FS_IN_ISDIR; - fsnotify_parent(dentry, mask); + + fsnotify_parent(NULL, dentry, mask); fsnotify(inode, mask, inode, FSNOTIFY_EVENT_INODE, NULL, 0); } } @@ -283,7 +285,7 @@ static inline void fsnotify_oldname_free(const char *old_name) kfree(old_name); } -#else /* CONFIG_INOTIFY || CONFIG_FSNOTIFY */ +#else /* CONFIG_FSNOTIFY */ static inline const char *fsnotify_oldname_init(const char *name) { @@ -294,6 +296,6 @@ static inline void fsnotify_oldname_free(const char *old_name) { } -#endif /* ! CONFIG_INOTIFY */ +#endif /* CONFIG_FSNOTIFY */ #endif /* _LINUX_FS_NOTIFY_H */ diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index 2766df67f1ec..0e0c2b76b067 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -259,7 +259,7 @@ struct fsnotify_mark_entry { /* main fsnotify call to send events */ extern void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, const char *name, u32 cookie); -extern void __fsnotify_parent(struct dentry *dentry, __u32 mask); +extern void __fsnotify_parent(struct file *file, struct dentry *dentry, __u32 mask); extern void __fsnotify_inode_delete(struct inode *inode); extern u32 fsnotify_get_cookie(void); @@ -367,7 +367,7 @@ static inline void fsnotify(struct inode *to_tell, __u32 mask, void *data, int d const char *name, u32 cookie) {} -static inline void __fsnotify_parent(struct dentry *dentry, __u32 mask) +static inline void __fsnotify_parent(struct file *file, struct dentry *dentry, __u32 mask) {} static inline void __fsnotify_inode_delete(struct inode *inode) -- cgit v1.2.3 From 74766bbfa99adf8cb8119df6121851edba21c9d9 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:21 -0500 Subject: fsnotify: per group notification queue merge types inotify only wishes to merge a new event with the last event on the notification fifo. fanotify is willing to merge any events including by means of bitwise OR masks of multiple events together. This patch moves the inotify event merging logic out of the generic fsnotify notification.c and into the inotify code. This allows each use of fsnotify to provide their own merge functionality. Signed-off-by: Eric Paris --- fs/notify/inotify/inotify_fsnotify.c | 56 ++++++++++++++++++++++++++- fs/notify/inotify/inotify_user.c | 2 +- fs/notify/notification.c | 73 ++++++++++-------------------------- include/linux/fsnotify_backend.h | 6 ++- 4 files changed, 79 insertions(+), 58 deletions(-) (limited to 'fs') diff --git a/fs/notify/inotify/inotify_fsnotify.c b/fs/notify/inotify/inotify_fsnotify.c index 1f33234cc308..0a0f5d0f0d0a 100644 --- a/fs/notify/inotify/inotify_fsnotify.c +++ b/fs/notify/inotify/inotify_fsnotify.c @@ -32,6 +32,60 @@ #include "inotify.h" +/* + * Check if 2 events contain the same information. We do not compare private data + * but at this moment that isn't a problem for any know fsnotify listeners. + */ +static bool event_compare(struct fsnotify_event *old, struct fsnotify_event *new) +{ + if ((old->mask == new->mask) && + (old->to_tell == new->to_tell) && + (old->data_type == new->data_type) && + (old->name_len == new->name_len)) { + switch (old->data_type) { + case (FSNOTIFY_EVENT_INODE): + /* remember, after old was put on the wait_q we aren't + * allowed to look at the inode any more, only thing + * left to check was if the file_name is the same */ + if (!old->name_len || + !strcmp(old->file_name, new->file_name)) + return true; + break; + case (FSNOTIFY_EVENT_PATH): + if ((old->path.mnt == new->path.mnt) && + (old->path.dentry == new->path.dentry)) + return true; + break; + case (FSNOTIFY_EVENT_NONE): + if (old->mask & FS_Q_OVERFLOW) + return true; + else if (old->mask & FS_IN_IGNORED) + return false; + return true; + }; + } + return false; +} + +static int inotify_merge(struct list_head *list, struct fsnotify_event *event) +{ + struct fsnotify_event_holder *last_holder; + struct fsnotify_event *last_event; + int ret = 0; + + /* and the list better be locked by something too */ + spin_lock(&event->lock); + + last_holder = list_entry(list->prev, struct fsnotify_event_holder, event_list); + last_event = last_holder->event; + if (event_compare(last_event, event)) + ret = -EEXIST; + + spin_unlock(&event->lock); + + return ret; +} + static int inotify_handle_event(struct fsnotify_group *group, struct fsnotify_event *event) { struct fsnotify_mark_entry *entry; @@ -62,7 +116,7 @@ static int inotify_handle_event(struct fsnotify_group *group, struct fsnotify_ev fsn_event_priv->group = group; event_priv->wd = wd; - ret = fsnotify_add_notify_event(group, event, fsn_event_priv); + ret = fsnotify_add_notify_event(group, event, fsn_event_priv, inotify_merge); if (ret) { inotify_free_event_priv(fsn_event_priv); /* EEXIST says we tail matched, EOVERFLOW isn't something diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index f2b542479e91..cbe16df326f8 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -531,7 +531,7 @@ void inotify_ignored_and_remove_idr(struct fsnotify_mark_entry *entry, fsn_event_priv->group = group; event_priv->wd = ientry->wd; - ret = fsnotify_add_notify_event(group, ignored_event, fsn_event_priv); + ret = fsnotify_add_notify_event(group, ignored_event, fsn_event_priv, NULL); if (ret) inotify_free_event_priv(fsn_event_priv); diff --git a/fs/notify/notification.c b/fs/notify/notification.c index b34ce7ad0409..6dc96b35e4a7 100644 --- a/fs/notify/notification.c +++ b/fs/notify/notification.c @@ -104,7 +104,8 @@ struct fsnotify_event_holder *fsnotify_alloc_event_holder(void) void fsnotify_destroy_event_holder(struct fsnotify_event_holder *holder) { - kmem_cache_free(fsnotify_event_holder_cachep, holder); + if (holder) + kmem_cache_free(fsnotify_event_holder_cachep, holder); } /* @@ -128,54 +129,18 @@ struct fsnotify_event_private_data *fsnotify_remove_priv_from_event(struct fsnot return priv; } -/* - * Check if 2 events contain the same information. We do not compare private data - * but at this moment that isn't a problem for any know fsnotify listeners. - */ -static bool event_compare(struct fsnotify_event *old, struct fsnotify_event *new) -{ - if ((old->mask == new->mask) && - (old->to_tell == new->to_tell) && - (old->data_type == new->data_type) && - (old->name_len == new->name_len)) { - switch (old->data_type) { - case (FSNOTIFY_EVENT_INODE): - /* remember, after old was put on the wait_q we aren't - * allowed to look at the inode any more, only thing - * left to check was if the file_name is the same */ - if (!old->name_len || - !strcmp(old->file_name, new->file_name)) - return true; - break; - case (FSNOTIFY_EVENT_PATH): - if ((old->path.mnt == new->path.mnt) && - (old->path.dentry == new->path.dentry)) - return true; - break; - case (FSNOTIFY_EVENT_NONE): - if (old->mask & FS_Q_OVERFLOW) - return true; - else if (old->mask & FS_IN_IGNORED) - return false; - return false; - }; - } - return false; -} - /* * Add an event to the group notification queue. The group can later pull this * event off the queue to deal with. If the event is successfully added to the * group's notification queue, a reference is taken on event. */ int fsnotify_add_notify_event(struct fsnotify_group *group, struct fsnotify_event *event, - struct fsnotify_event_private_data *priv) + struct fsnotify_event_private_data *priv, + int (*merge)(struct list_head *, struct fsnotify_event *)) { struct fsnotify_event_holder *holder = NULL; struct list_head *list = &group->notification_list; - struct fsnotify_event_holder *last_holder; - struct fsnotify_event *last_event; - int ret = 0; + int rc = 0; /* * There is one fsnotify_event_holder embedded inside each fsnotify_event. @@ -196,11 +161,23 @@ alloc_holder: if (group->q_len >= group->max_events) { event = q_overflow_event; - ret = -EOVERFLOW; + rc = -EOVERFLOW; /* sorry, no private data on the overflow event */ priv = NULL; } + if (!list_empty(list) && merge) { + int ret; + + ret = merge(list, event); + if (ret) { + mutex_unlock(&group->notification_mutex); + if (holder != &event->holder) + fsnotify_destroy_event_holder(holder); + return ret; + } + } + spin_lock(&event->lock); if (list_empty(&event->holder.event_list)) { @@ -215,18 +192,6 @@ alloc_holder: goto alloc_holder; } - if (!list_empty(list)) { - last_holder = list_entry(list->prev, struct fsnotify_event_holder, event_list); - last_event = last_holder->event; - if (event_compare(last_event, event)) { - spin_unlock(&event->lock); - mutex_unlock(&group->notification_mutex); - if (holder != &event->holder) - fsnotify_destroy_event_holder(holder); - return -EEXIST; - } - } - group->q_len++; holder->event = event; @@ -238,7 +203,7 @@ alloc_holder: mutex_unlock(&group->notification_mutex); wake_up(&group->notification_waitq); - return ret; + return rc; } /* diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index 0e0c2b76b067..25789d45fad8 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -328,8 +328,10 @@ extern struct fsnotify_event_private_data *fsnotify_remove_priv_from_event(struc struct fsnotify_event *event); /* attach the event to the group notification queue */ -extern int fsnotify_add_notify_event(struct fsnotify_group *group, struct fsnotify_event *event, - struct fsnotify_event_private_data *priv); +extern int fsnotify_add_notify_event(struct fsnotify_group *group, + struct fsnotify_event *event, + struct fsnotify_event_private_data *priv, + int (*merge)(struct list_head *, struct fsnotify_event *)); /* true if the group notification queue is empty */ extern bool fsnotify_notify_queue_is_empty(struct fsnotify_group *group); /* return, but do not dequeue the first event on the notification queue */ -- cgit v1.2.3 From b4e4e1407312ae5a267ed7d716e6d4e7120a8430 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:21 -0500 Subject: fsnotify: clone existing events fsnotify_clone_event will take an event, clone it, and return the cloned event to the caller. Since events may be in use by multiple fsnotify groups simultaneously certain event entries (such as the mask) cannot be changed after the event was created. Since fanotify would like to merge events happening on the same file it needs a new clean event to work with so it can change any fields it wishes. Signed-off-by: Eric Paris --- fs/notify/notification.c | 29 +++++++++++++++++++++++++---- include/linux/fsnotify_backend.h | 3 +++ 2 files changed, 28 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/notify/notification.c b/fs/notify/notification.c index 6dc96b35e4a7..bc9470c7ece7 100644 --- a/fs/notify/notification.c +++ b/fs/notify/notification.c @@ -284,11 +284,33 @@ static void initialize_event(struct fsnotify_event *event) spin_lock_init(&event->lock); - event->data_type = FSNOTIFY_EVENT_NONE; - INIT_LIST_HEAD(&event->private_data_list); } +struct fsnotify_event *fsnotify_clone_event(struct fsnotify_event *old_event) +{ + struct fsnotify_event *event; + + event = kmem_cache_alloc(fsnotify_event_cachep, GFP_KERNEL); + if (!event) + return NULL; + + memcpy(event, old_event, sizeof(*event)); + initialize_event(event); + + if (event->name_len) { + event->file_name = kstrdup(old_event->file_name, GFP_KERNEL); + if (!event->file_name) { + kmem_cache_free(fsnotify_event_cachep, event); + return NULL; + } + } + if (event->data_type == FSNOTIFY_EVENT_PATH) + path_get(&event->path); + + return event; +} + /* * fsnotify_create_event - Allocate a new event which will be sent to each * group's handle_event function if the group was interested in this @@ -324,6 +346,7 @@ struct fsnotify_event *fsnotify_create_event(struct inode *to_tell, __u32 mask, event->sync_cookie = cookie; event->to_tell = to_tell; + event->data_type = data_type; switch (data_type) { case FSNOTIFY_EVENT_FILE: { @@ -340,12 +363,10 @@ struct fsnotify_event *fsnotify_create_event(struct inode *to_tell, __u32 mask, event->path.dentry = path->dentry; event->path.mnt = path->mnt; path_get(&event->path); - event->data_type = FSNOTIFY_EVENT_PATH; break; } case FSNOTIFY_EVENT_INODE: event->inode = data; - event->data_type = FSNOTIFY_EVENT_INODE; break; case FSNOTIFY_EVENT_NONE: event->inode = NULL; diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index 25789d45fad8..3a7fff235539 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -363,6 +363,9 @@ extern struct fsnotify_event *fsnotify_create_event(struct inode *to_tell, __u32 void *data, int data_is, const char *name, u32 cookie, gfp_t gfp); +/* fanotify likes to change events after they are on lists... */ +extern struct fsnotify_event *fsnotify_clone_event(struct fsnotify_event *old_event); + #else static inline void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, -- cgit v1.2.3 From 1201a5361b9bd6512ae01e6f2b7aa79d458cafb1 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:22 -0500 Subject: fsnotify: replace an event on a list fanotify would like to clone events already on its notification list, make changes to the new event, and then replace the old event on the list with the new event. This patch implements the replace functionality of that process. Signed-off-by: Eric Paris --- fs/notify/notification.c | 56 ++++++++++++++++++++++++++++++++++++++++ include/linux/fsnotify_backend.h | 2 ++ 2 files changed, 58 insertions(+) (limited to 'fs') diff --git a/fs/notify/notification.c b/fs/notify/notification.c index bc9470c7ece7..b493c378445f 100644 --- a/fs/notify/notification.c +++ b/fs/notify/notification.c @@ -287,6 +287,62 @@ static void initialize_event(struct fsnotify_event *event) INIT_LIST_HEAD(&event->private_data_list); } +/* + * Caller damn well better be holding whatever mutex is protecting the + * old_holder->event_list. + */ +int fsnotify_replace_event(struct fsnotify_event_holder *old_holder, + struct fsnotify_event *new_event) +{ + struct fsnotify_event *old_event = old_holder->event; + struct fsnotify_event_holder *new_holder = NULL; + + /* + * There is one fsnotify_event_holder embedded inside each fsnotify_event. + * Check if we expect to be able to use that holder. If not alloc a new + * holder. + * For the overflow event it's possible that something will use the in + * event holder before we get the lock so we may need to jump back and + * alloc a new holder, this can't happen for most events... + */ + if (!list_empty(&new_event->holder.event_list)) { +alloc_holder: + new_holder = fsnotify_alloc_event_holder(); + if (!new_holder) + return -ENOMEM; + } + + spin_lock(&old_event->lock); + spin_lock(&new_event->lock); + + if (list_empty(&new_event->holder.event_list)) { + if (unlikely(new_holder)) + fsnotify_destroy_event_holder(new_holder); + new_holder = &new_event->holder; + } else if (unlikely(!new_holder)) { + /* between the time we checked above and got the lock the in + * event holder was used, go back and get a new one */ + spin_unlock(&new_event->lock); + spin_unlock(&old_event->lock); + goto alloc_holder; + } + + new_holder->event = new_event; + list_replace_init(&old_holder->event_list, &new_holder->event_list); + + spin_unlock(&new_event->lock); + spin_unlock(&old_event->lock); + + /* event == holder means we are referenced through the in event holder */ + if (old_holder != &old_event->holder) + fsnotify_destroy_event_holder(old_holder); + + fsnotify_get_event(new_event); /* on the list take reference */ + fsnotify_put_event(old_event); /* off the list, drop reference */ + + return 0; +} + struct fsnotify_event *fsnotify_clone_event(struct fsnotify_event *old_event) { struct fsnotify_event *event; diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index 3a7fff235539..427f6ffab127 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -365,6 +365,8 @@ extern struct fsnotify_event *fsnotify_create_event(struct inode *to_tell, __u32 /* fanotify likes to change events after they are on lists... */ extern struct fsnotify_event *fsnotify_clone_event(struct fsnotify_event *old_event); +extern int fsnotify_replace_event(struct fsnotify_event_holder *old_holder, + struct fsnotify_event *new_event); #else -- cgit v1.2.3 From cac69dad32899c6f4c66bb4f9baf69b0d3c7d3d1 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:22 -0500 Subject: fsnotify: lock annotation for event replacement fsnotify_replace_event need to lock both the old and the new event. This causes lockdep to get all pissed off since it dosn't know this is safe. It's safe in this case since the new event is impossible to be reached from other places in the kernel. Signed-off-by: Eric Paris --- fs/notify/notification.c | 41 +++++++++++++---------------------------- 1 file changed, 13 insertions(+), 28 deletions(-) (limited to 'fs') diff --git a/fs/notify/notification.c b/fs/notify/notification.c index b493c378445f..dafd0b7687b8 100644 --- a/fs/notify/notification.c +++ b/fs/notify/notification.c @@ -289,43 +289,28 @@ static void initialize_event(struct fsnotify_event *event) /* * Caller damn well better be holding whatever mutex is protecting the - * old_holder->event_list. + * old_holder->event_list and the new_event must be a clean event which + * cannot be found anywhere else in the kernel. */ int fsnotify_replace_event(struct fsnotify_event_holder *old_holder, struct fsnotify_event *new_event) { struct fsnotify_event *old_event = old_holder->event; - struct fsnotify_event_holder *new_holder = NULL; + struct fsnotify_event_holder *new_holder = &new_event->holder; + + enum event_spinlock_class { + SPINLOCK_OLD, + SPINLOCK_NEW, + }; /* - * There is one fsnotify_event_holder embedded inside each fsnotify_event. - * Check if we expect to be able to use that holder. If not alloc a new - * holder. - * For the overflow event it's possible that something will use the in - * event holder before we get the lock so we may need to jump back and - * alloc a new holder, this can't happen for most events... + * if the new_event's embedded holder is in use someone + * screwed up and didn't give us a clean new event. */ - if (!list_empty(&new_event->holder.event_list)) { -alloc_holder: - new_holder = fsnotify_alloc_event_holder(); - if (!new_holder) - return -ENOMEM; - } + BUG_ON(!list_empty(&new_holder->event_list)); - spin_lock(&old_event->lock); - spin_lock(&new_event->lock); - - if (list_empty(&new_event->holder.event_list)) { - if (unlikely(new_holder)) - fsnotify_destroy_event_holder(new_holder); - new_holder = &new_event->holder; - } else if (unlikely(!new_holder)) { - /* between the time we checked above and got the lock the in - * event holder was used, go back and get a new one */ - spin_unlock(&new_event->lock); - spin_unlock(&old_event->lock); - goto alloc_holder; - } + spin_lock_nested(&old_event->lock, SPINLOCK_OLD); + spin_lock_nested(&new_event->lock, SPINLOCK_NEW); new_holder->event = new_event; list_replace_init(&old_holder->event_list, &new_holder->event_list); -- cgit v1.2.3 From 74be0cc82835aecad332a29896b0f212ba893403 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:22 -0500 Subject: fsnotify: remove group_num altogether The original fsnotify interface has a group-num which was intended to be able to find a group after it was added. I no longer think this is a necessary thing to do and so we remove the group_num. Signed-off-by: Eric Paris --- fs/notify/dnotify/dnotify.c | 3 +-- fs/notify/group.c | 48 ++-------------------------------------- fs/notify/inotify/inotify_user.c | 11 +-------- include/linux/fsnotify_backend.h | 10 +-------- kernel/audit_tree.c | 3 +-- kernel/audit_watch.c | 2 +- 6 files changed, 7 insertions(+), 70 deletions(-) (limited to 'fs') diff --git a/fs/notify/dnotify/dnotify.c b/fs/notify/dnotify/dnotify.c index a213b83a59cf..1f46aeac3387 100644 --- a/fs/notify/dnotify/dnotify.c +++ b/fs/notify/dnotify/dnotify.c @@ -433,8 +433,7 @@ static int __init dnotify_init(void) dnotify_struct_cache = KMEM_CACHE(dnotify_struct, SLAB_PANIC); dnotify_mark_entry_cache = KMEM_CACHE(dnotify_mark_entry, SLAB_PANIC); - dnotify_group = fsnotify_obtain_group(DNOTIFY_GROUP_NUM, - 0, &dnotify_fsnotify_ops); + dnotify_group = fsnotify_obtain_group(0, &dnotify_fsnotify_ops); if (IS_ERR(dnotify_group)) panic("unable to allocate fsnotify group for dnotify\n"); return 0; diff --git a/fs/notify/group.c b/fs/notify/group.c index 777ca8212255..62fb8961a57b 100644 --- a/fs/notify/group.c +++ b/fs/notify/group.c @@ -77,15 +77,6 @@ void fsnotify_recalc_group_mask(struct fsnotify_group *group) fsnotify_recalc_global_mask(); } -/* - * Take a reference to a group so things found under the fsnotify_grp_mutex - * can't get freed under us - */ -static void fsnotify_get_group(struct fsnotify_group *group) -{ - atomic_inc(&group->refcnt); -} - /* * Final freeing of a group */ @@ -170,41 +161,15 @@ void fsnotify_put_group(struct fsnotify_group *group) fsnotify_destroy_group(group); } -/* - * Simply run the fsnotify_groups list and find a group which matches - * the given parameters. If a group is found we take a reference to that - * group. - */ -static struct fsnotify_group *fsnotify_find_group(unsigned int group_num, __u32 mask, - const struct fsnotify_ops *ops) -{ - struct fsnotify_group *group_iter; - struct fsnotify_group *group = NULL; - - BUG_ON(!mutex_is_locked(&fsnotify_grp_mutex)); - - list_for_each_entry_rcu(group_iter, &fsnotify_groups, group_list) { - if (group_iter->group_num == group_num) { - if ((group_iter->mask == mask) && - (group_iter->ops == ops)) { - fsnotify_get_group(group_iter); - group = group_iter; - } else - group = ERR_PTR(-EEXIST); - } - } - return group; -} - /* * Either finds an existing group which matches the group_num, mask, and ops or * creates a new group and adds it to the global group list. In either case we * take a reference for the group returned. */ -struct fsnotify_group *fsnotify_obtain_group(unsigned int group_num, __u32 mask, +struct fsnotify_group *fsnotify_obtain_group(__u32 mask, const struct fsnotify_ops *ops) { - struct fsnotify_group *group, *tgroup; + struct fsnotify_group *group; /* very low use, simpler locking if we just always alloc */ group = kzalloc(sizeof(struct fsnotify_group), GFP_KERNEL); @@ -214,7 +179,6 @@ struct fsnotify_group *fsnotify_obtain_group(unsigned int group_num, __u32 mask, atomic_set(&group->refcnt, 1); group->on_group_list = 0; - group->group_num = group_num; group->mask = mask; mutex_init(&group->notification_mutex); @@ -230,14 +194,6 @@ struct fsnotify_group *fsnotify_obtain_group(unsigned int group_num, __u32 mask, group->ops = ops; mutex_lock(&fsnotify_grp_mutex); - tgroup = fsnotify_find_group(group_num, mask, ops); - if (tgroup) { - /* group already exists */ - mutex_unlock(&fsnotify_grp_mutex); - /* destroy the new one we made */ - fsnotify_put_group(group); - return tgroup; - } /* group not found, add a new one */ list_add_rcu(&group->group_list, &fsnotify_groups); diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index cbe16df326f8..cae317f5bd9d 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -51,12 +51,6 @@ int inotify_max_user_watches __read_mostly; static struct kmem_cache *inotify_inode_mark_cachep __read_mostly; struct kmem_cache *event_priv_cachep __read_mostly; -/* - * When inotify registers a new group it increments this and uses that - * value as an offset to set the fsnotify group "name" and priority. - */ -static atomic_t inotify_grp_num; - #ifdef CONFIG_SYSCTL #include @@ -700,11 +694,8 @@ retry: static struct fsnotify_group *inotify_new_group(struct user_struct *user, unsigned int max_events) { struct fsnotify_group *group; - unsigned int grp_num; - /* fsnotify_obtain_group took a reference to group, we put this when we kill the file in the end */ - grp_num = (INOTIFY_GROUP_NUM - atomic_inc_return(&inotify_grp_num)); - group = fsnotify_obtain_group(grp_num, 0, &inotify_fsnotify_ops); + group = fsnotify_obtain_group(0, &inotify_fsnotify_ops); if (IS_ERR(group)) return group; diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index 427f6ffab127..57e503d017c8 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -60,12 +60,6 @@ #define FS_MOVE (FS_MOVED_FROM | FS_MOVED_TO) -/* listeners that hard code group numbers near the top */ -#define DNOTIFY_GROUP_NUM UINT_MAX -#define AUDIT_WATCH_GROUP_NUM (DNOTIFY_GROUP_NUM-1) -#define AUDIT_TREE_GROUP_NUM (AUDIT_WATCH_GROUP_NUM-1) -#define INOTIFY_GROUP_NUM (AUDIT_TREE_GROUP_NUM-1) - struct fsnotify_group; struct fsnotify_event; struct fsnotify_mark_entry; @@ -124,7 +118,6 @@ struct fsnotify_group { * closed. */ atomic_t refcnt; /* things with interest in this group */ - unsigned int group_num; /* simply prevents accidental group collision */ const struct fsnotify_ops *ops; /* how this group handles things */ @@ -312,8 +305,7 @@ static inline void __fsnotify_d_instantiate(struct dentry *dentry, struct inode /* must call when a group changes its ->mask */ extern void fsnotify_recalc_global_mask(void); /* get a reference to an existing or create a new group */ -extern struct fsnotify_group *fsnotify_obtain_group(unsigned int group_num, - __u32 mask, +extern struct fsnotify_group *fsnotify_obtain_group(__u32 mask, const struct fsnotify_ops *ops); /* run all marks associated with this group and update group->mask */ extern void fsnotify_recalc_group_mask(struct fsnotify_group *group); diff --git a/kernel/audit_tree.c b/kernel/audit_tree.c index e3d63b596ef0..59065e72a2eb 100644 --- a/kernel/audit_tree.c +++ b/kernel/audit_tree.c @@ -937,8 +937,7 @@ static int __init audit_tree_init(void) { int i; - audit_tree_group = fsnotify_obtain_group(AUDIT_TREE_GROUP_NUM, - 0, &audit_tree_ops); + audit_tree_group = fsnotify_obtain_group(0, &audit_tree_ops); if (IS_ERR(audit_tree_group)) audit_panic("cannot initialize fsnotify group for rectree watches"); diff --git a/kernel/audit_watch.c b/kernel/audit_watch.c index 85c43aa292e0..c500104d38c2 100644 --- a/kernel/audit_watch.c +++ b/kernel/audit_watch.c @@ -577,7 +577,7 @@ static const struct fsnotify_ops audit_watch_fsnotify_ops = { static int __init audit_watch_init(void) { - audit_watch_group = fsnotify_obtain_group(AUDIT_WATCH_GROUP_NUM, AUDIT_FS_WATCH, + audit_watch_group = fsnotify_obtain_group(AUDIT_FS_WATCH, &audit_watch_fsnotify_ops); if (IS_ERR(audit_watch_group)) { audit_watch_group = NULL; -- cgit v1.2.3 From cd7752ce7cac5184ca35aecebffafae9662570bc Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:22 -0500 Subject: fsnotify: fsnotify_obtain_group kzalloc cleanup fsnotify_obtain_group uses kzalloc but then proceedes to set things to 0. This patch just deletes those useless lines. Signed-off-by: Eric Paris --- fs/notify/group.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'fs') diff --git a/fs/notify/group.c b/fs/notify/group.c index 62fb8961a57b..934860e98095 100644 --- a/fs/notify/group.c +++ b/fs/notify/group.c @@ -178,17 +178,14 @@ struct fsnotify_group *fsnotify_obtain_group(__u32 mask, atomic_set(&group->refcnt, 1); - group->on_group_list = 0; group->mask = mask; mutex_init(&group->notification_mutex); INIT_LIST_HEAD(&group->notification_list); init_waitqueue_head(&group->notification_waitq); - group->q_len = 0; group->max_events = UINT_MAX; spin_lock_init(&group->mark_lock); - atomic_set(&group->num_marks, 0); INIT_LIST_HEAD(&group->mark_entries); group->ops = ops; -- cgit v1.2.3 From ffab83402f01555a5fa32efb48a4dd0ce8d12ef5 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:22 -0500 Subject: fsnotify: fsnotify_obtain_group should be fsnotify_alloc_group fsnotify_obtain_group was intended to be able to find an already existing group. Nothing uses that functionality. This just renames it to fsnotify_alloc_group so it is clear what it is doing. Signed-off-by: Eric Paris --- fs/notify/dnotify/dnotify.c | 2 +- fs/notify/group.c | 10 +++------- fs/notify/inotify/inotify_user.c | 2 +- include/linux/fsnotify_backend.h | 4 ++-- kernel/audit_tree.c | 2 +- kernel/audit_watch.c | 4 ++-- 6 files changed, 10 insertions(+), 14 deletions(-) (limited to 'fs') diff --git a/fs/notify/dnotify/dnotify.c b/fs/notify/dnotify/dnotify.c index 1f46aeac3387..51e4fe33d6bb 100644 --- a/fs/notify/dnotify/dnotify.c +++ b/fs/notify/dnotify/dnotify.c @@ -433,7 +433,7 @@ static int __init dnotify_init(void) dnotify_struct_cache = KMEM_CACHE(dnotify_struct, SLAB_PANIC); dnotify_mark_entry_cache = KMEM_CACHE(dnotify_mark_entry, SLAB_PANIC); - dnotify_group = fsnotify_obtain_group(0, &dnotify_fsnotify_ops); + dnotify_group = fsnotify_alloc_group(0, &dnotify_fsnotify_ops); if (IS_ERR(dnotify_group)) panic("unable to allocate fsnotify group for dnotify\n"); return 0; diff --git a/fs/notify/group.c b/fs/notify/group.c index 934860e98095..1d20d26d5fee 100644 --- a/fs/notify/group.c +++ b/fs/notify/group.c @@ -162,16 +162,13 @@ void fsnotify_put_group(struct fsnotify_group *group) } /* - * Either finds an existing group which matches the group_num, mask, and ops or - * creates a new group and adds it to the global group list. In either case we - * take a reference for the group returned. + * Create a new fsnotify_group and hold a reference for the group returned. */ -struct fsnotify_group *fsnotify_obtain_group(__u32 mask, - const struct fsnotify_ops *ops) +struct fsnotify_group *fsnotify_alloc_group(__u32 mask, + const struct fsnotify_ops *ops) { struct fsnotify_group *group; - /* very low use, simpler locking if we just always alloc */ group = kzalloc(sizeof(struct fsnotify_group), GFP_KERNEL); if (!group) return ERR_PTR(-ENOMEM); @@ -192,7 +189,6 @@ struct fsnotify_group *fsnotify_obtain_group(__u32 mask, mutex_lock(&fsnotify_grp_mutex); - /* group not found, add a new one */ list_add_rcu(&group->group_list, &fsnotify_groups); group->on_group_list = 1; /* being on the fsnotify_groups list holds one num_marks */ diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index cae317f5bd9d..25a2854186e9 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -695,7 +695,7 @@ static struct fsnotify_group *inotify_new_group(struct user_struct *user, unsign { struct fsnotify_group *group; - group = fsnotify_obtain_group(0, &inotify_fsnotify_ops); + group = fsnotify_alloc_group(0, &inotify_fsnotify_ops); if (IS_ERR(group)) return group; diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index 57e503d017c8..7d3c03e46862 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -305,11 +305,11 @@ static inline void __fsnotify_d_instantiate(struct dentry *dentry, struct inode /* must call when a group changes its ->mask */ extern void fsnotify_recalc_global_mask(void); /* get a reference to an existing or create a new group */ -extern struct fsnotify_group *fsnotify_obtain_group(__u32 mask, +extern struct fsnotify_group *fsnotify_alloc_group(__u32 mask, const struct fsnotify_ops *ops); /* run all marks associated with this group and update group->mask */ extern void fsnotify_recalc_group_mask(struct fsnotify_group *group); -/* drop reference on a group from fsnotify_obtain_group */ +/* drop reference on a group from fsnotify_alloc_group */ extern void fsnotify_put_group(struct fsnotify_group *group); /* take a reference to an event */ diff --git a/kernel/audit_tree.c b/kernel/audit_tree.c index 59065e72a2eb..813274d4edad 100644 --- a/kernel/audit_tree.c +++ b/kernel/audit_tree.c @@ -937,7 +937,7 @@ static int __init audit_tree_init(void) { int i; - audit_tree_group = fsnotify_obtain_group(0, &audit_tree_ops); + audit_tree_group = fsnotify_alloc_group(0, &audit_tree_ops); if (IS_ERR(audit_tree_group)) audit_panic("cannot initialize fsnotify group for rectree watches"); diff --git a/kernel/audit_watch.c b/kernel/audit_watch.c index c500104d38c2..0f03a6ab96ed 100644 --- a/kernel/audit_watch.c +++ b/kernel/audit_watch.c @@ -577,8 +577,8 @@ static const struct fsnotify_ops audit_watch_fsnotify_ops = { static int __init audit_watch_init(void) { - audit_watch_group = fsnotify_obtain_group(AUDIT_FS_WATCH, - &audit_watch_fsnotify_ops); + audit_watch_group = fsnotify_alloc_group(AUDIT_FS_WATCH, + &audit_watch_fsnotify_ops); if (IS_ERR(audit_watch_group)) { audit_watch_group = NULL; audit_panic("cannot create audit fsnotify group"); -- cgit v1.2.3 From 0d2e2a1d00d7d23e5bd9bb0935cde7c3d5835c56 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:22 -0500 Subject: fsnotify: drop mask argument from fsnotify_alloc_group Nothing uses the mask argument to fsnotify_alloc_group. This patch drops that argument. Signed-off-by: Eric Paris --- fs/notify/dnotify/dnotify.c | 2 +- fs/notify/group.c | 8 +------- fs/notify/inotify/inotify_user.c | 2 +- include/linux/fsnotify_backend.h | 3 +-- kernel/audit_tree.c | 2 +- kernel/audit_watch.c | 2 +- 6 files changed, 6 insertions(+), 13 deletions(-) (limited to 'fs') diff --git a/fs/notify/dnotify/dnotify.c b/fs/notify/dnotify/dnotify.c index 51e4fe33d6bb..e0a847bd53be 100644 --- a/fs/notify/dnotify/dnotify.c +++ b/fs/notify/dnotify/dnotify.c @@ -433,7 +433,7 @@ static int __init dnotify_init(void) dnotify_struct_cache = KMEM_CACHE(dnotify_struct, SLAB_PANIC); dnotify_mark_entry_cache = KMEM_CACHE(dnotify_mark_entry, SLAB_PANIC); - dnotify_group = fsnotify_alloc_group(0, &dnotify_fsnotify_ops); + dnotify_group = fsnotify_alloc_group(&dnotify_fsnotify_ops); if (IS_ERR(dnotify_group)) panic("unable to allocate fsnotify group for dnotify\n"); return 0; diff --git a/fs/notify/group.c b/fs/notify/group.c index 1d20d26d5fee..1657349c30a6 100644 --- a/fs/notify/group.c +++ b/fs/notify/group.c @@ -164,8 +164,7 @@ void fsnotify_put_group(struct fsnotify_group *group) /* * Create a new fsnotify_group and hold a reference for the group returned. */ -struct fsnotify_group *fsnotify_alloc_group(__u32 mask, - const struct fsnotify_ops *ops) +struct fsnotify_group *fsnotify_alloc_group(const struct fsnotify_ops *ops) { struct fsnotify_group *group; @@ -175,8 +174,6 @@ struct fsnotify_group *fsnotify_alloc_group(__u32 mask, atomic_set(&group->refcnt, 1); - group->mask = mask; - mutex_init(&group->notification_mutex); INIT_LIST_HEAD(&group->notification_list); init_waitqueue_head(&group->notification_waitq); @@ -196,8 +193,5 @@ struct fsnotify_group *fsnotify_alloc_group(__u32 mask, mutex_unlock(&fsnotify_grp_mutex); - if (mask) - fsnotify_recalc_global_mask(); - return group; } diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index 25a2854186e9..a48d68a68b25 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -695,7 +695,7 @@ static struct fsnotify_group *inotify_new_group(struct user_struct *user, unsign { struct fsnotify_group *group; - group = fsnotify_alloc_group(0, &inotify_fsnotify_ops); + group = fsnotify_alloc_group(&inotify_fsnotify_ops); if (IS_ERR(group)) return group; diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index 7d3c03e46862..58326049ab29 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -305,8 +305,7 @@ static inline void __fsnotify_d_instantiate(struct dentry *dentry, struct inode /* must call when a group changes its ->mask */ extern void fsnotify_recalc_global_mask(void); /* get a reference to an existing or create a new group */ -extern struct fsnotify_group *fsnotify_alloc_group(__u32 mask, - const struct fsnotify_ops *ops); +extern struct fsnotify_group *fsnotify_alloc_group(const struct fsnotify_ops *ops); /* run all marks associated with this group and update group->mask */ extern void fsnotify_recalc_group_mask(struct fsnotify_group *group); /* drop reference on a group from fsnotify_alloc_group */ diff --git a/kernel/audit_tree.c b/kernel/audit_tree.c index 813274d4edad..04f16887406b 100644 --- a/kernel/audit_tree.c +++ b/kernel/audit_tree.c @@ -937,7 +937,7 @@ static int __init audit_tree_init(void) { int i; - audit_tree_group = fsnotify_alloc_group(0, &audit_tree_ops); + audit_tree_group = fsnotify_alloc_group(&audit_tree_ops); if (IS_ERR(audit_tree_group)) audit_panic("cannot initialize fsnotify group for rectree watches"); diff --git a/kernel/audit_watch.c b/kernel/audit_watch.c index 87408b282118..83d5f9674cec 100644 --- a/kernel/audit_watch.c +++ b/kernel/audit_watch.c @@ -585,7 +585,7 @@ static const struct fsnotify_ops audit_watch_fsnotify_ops = { static int __init audit_watch_init(void) { - audit_watch_group = fsnotify_alloc_group(0, &audit_watch_fsnotify_ops); + audit_watch_group = fsnotify_alloc_group(&audit_watch_fsnotify_ops); if (IS_ERR(audit_watch_group)) { audit_watch_group = NULL; audit_panic("cannot create audit fsnotify group"); -- cgit v1.2.3 From 19c2a0e1a2f60112c158342ba5f568f72b741c2c Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:23 -0500 Subject: fsnotify: rename fsnotify_groups to fsnotify_inode_groups Simple renaming patch. fsnotify is about to support mount point listeners so I am renaming fsnotify_groups and fsnotify_mask to indicate these are lists used only for groups which have watches on inodes. Signed-off-by: Eric Paris --- fs/notify/fsnotify.c | 6 +++--- fs/notify/fsnotify.h | 8 ++++---- fs/notify/group.c | 30 +++++++++++++++++++----------- include/linux/fsnotify_backend.h | 6 +++--- 4 files changed, 29 insertions(+), 21 deletions(-) (limited to 'fs') diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index 806beede24a3..23b5cfbeed50 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -148,10 +148,10 @@ void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, const /* global tests shouldn't care about events on child only the specific event */ __u32 test_mask = (mask & ~FS_EVENT_ON_CHILD); - if (list_empty(&fsnotify_groups)) + if (list_empty(&fsnotify_inode_groups)) return; - if (!(test_mask & fsnotify_mask)) + if (!(test_mask & fsnotify_inode_mask)) return; if (!(test_mask & to_tell->i_fsnotify_mask)) @@ -162,7 +162,7 @@ void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, const * anything other than walk the list so it's crazy to pre-allocate. */ idx = srcu_read_lock(&fsnotify_grp_srcu); - list_for_each_entry_rcu(group, &fsnotify_groups, group_list) { + list_for_each_entry_rcu(group, &fsnotify_inode_groups, inode_group_list) { if (test_mask & group->mask) { if (!group->ops->should_send_event(group, to_tell, mask, data, data_is)) diff --git a/fs/notify/fsnotify.h b/fs/notify/fsnotify.h index 4dc240824b2d..ec5aee43bdc5 100644 --- a/fs/notify/fsnotify.h +++ b/fs/notify/fsnotify.h @@ -8,10 +8,10 @@ /* protects reads of fsnotify_groups */ extern struct srcu_struct fsnotify_grp_srcu; -/* all groups which receive fsnotify events */ -extern struct list_head fsnotify_groups; -/* all bitwise OR of all event types (FS_*) for all fsnotify_groups */ -extern __u32 fsnotify_mask; +/* all groups which receive inode fsnotify events */ +extern struct list_head fsnotify_inode_groups; +/* all bitwise OR of all event types (FS_*) for all fsnotify_inode_groups */ +extern __u32 fsnotify_inode_mask; /* destroy all events sitting in this groups notification queue */ extern void fsnotify_flush_notify(struct fsnotify_group *group); diff --git a/fs/notify/group.c b/fs/notify/group.c index 1657349c30a6..c80809745312 100644 --- a/fs/notify/group.c +++ b/fs/notify/group.c @@ -33,9 +33,9 @@ static DEFINE_MUTEX(fsnotify_grp_mutex); /* protects reads while running the fsnotify_groups list */ struct srcu_struct fsnotify_grp_srcu; /* all groups registered to receive filesystem notifications */ -LIST_HEAD(fsnotify_groups); +LIST_HEAD(fsnotify_inode_groups); /* bitwise OR of all events (FS_*) interesting to some group on this system */ -__u32 fsnotify_mask; +__u32 fsnotify_inode_mask; /* * When a new group registers or changes it's set of interesting events @@ -48,10 +48,10 @@ void fsnotify_recalc_global_mask(void) int idx; idx = srcu_read_lock(&fsnotify_grp_srcu); - list_for_each_entry_rcu(group, &fsnotify_groups, group_list) + list_for_each_entry_rcu(group, &fsnotify_inode_groups, inode_group_list) mask |= group->mask; srcu_read_unlock(&fsnotify_grp_srcu, idx); - fsnotify_mask = mask; + fsnotify_inode_mask = mask; } /* @@ -77,6 +77,17 @@ void fsnotify_recalc_group_mask(struct fsnotify_group *group) fsnotify_recalc_global_mask(); } +static void fsnotify_add_group(struct fsnotify_group *group) +{ + BUG_ON(!mutex_is_locked(&fsnotify_grp_mutex)); + + group->on_inode_group_list = 1; + /* being on the fsnotify_groups list holds one num_marks */ + atomic_inc(&group->num_marks); + + list_add_tail_rcu(&group->inode_group_list, &fsnotify_inode_groups); +} + /* * Final freeing of a group */ @@ -118,9 +129,9 @@ static void __fsnotify_evict_group(struct fsnotify_group *group) { BUG_ON(!mutex_is_locked(&fsnotify_grp_mutex)); - if (group->on_group_list) - list_del_rcu(&group->group_list); - group->on_group_list = 0; + if (group->on_inode_group_list) + list_del_rcu(&group->inode_group_list); + group->on_inode_group_list = 0; } /* @@ -186,10 +197,7 @@ struct fsnotify_group *fsnotify_alloc_group(const struct fsnotify_ops *ops) mutex_lock(&fsnotify_grp_mutex); - list_add_rcu(&group->group_list, &fsnotify_groups); - group->on_group_list = 1; - /* being on the fsnotify_groups list holds one num_marks */ - atomic_inc(&group->num_marks); + fsnotify_add_group(group); mutex_unlock(&fsnotify_grp_mutex); diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index 58326049ab29..21079ade5620 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -95,10 +95,10 @@ struct fsnotify_ops { struct fsnotify_group { /* * global list of all groups receiving events from fsnotify. - * anchored by fsnotify_groups and protected by either fsnotify_grp_mutex + * anchored by fsnotify_inode_groups and protected by either fsnotify_grp_mutex * or fsnotify_grp_srcu depending on write vs read. */ - struct list_head group_list; + struct list_head inode_group_list; /* * Defines all of the event types in which this group is interested. @@ -136,7 +136,7 @@ struct fsnotify_group { struct list_head mark_entries; /* all inode mark entries for this group */ /* prevents double list_del of group_list. protected by global fsnotify_grp_mutex */ - bool on_group_list; + bool on_inode_group_list; /* groups can define private fields here or use the void *private */ union { -- cgit v1.2.3 From 36fddebaa8a9186d4f5817ab798a83400b2fb2e7 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:23 -0500 Subject: fsnotify: initialize the group->num_marks in a better place Currently the comments say that group->num_marks is held because the group is on the fsnotify_group list. This isn't strictly the case, we really just hold the num_marks for the life of the group (any time group->refcnt is != 0) This patch moves the initialization stuff and makes it clear when it is really being held. Signed-off-by: Eric Paris --- fs/notify/group.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/notify/group.c b/fs/notify/group.c index c80809745312..656c534ffb66 100644 --- a/fs/notify/group.c +++ b/fs/notify/group.c @@ -82,9 +82,6 @@ static void fsnotify_add_group(struct fsnotify_group *group) BUG_ON(!mutex_is_locked(&fsnotify_grp_mutex)); group->on_inode_group_list = 1; - /* being on the fsnotify_groups list holds one num_marks */ - atomic_inc(&group->num_marks); - list_add_tail_rcu(&group->inode_group_list, &fsnotify_inode_groups); } @@ -183,7 +180,14 @@ struct fsnotify_group *fsnotify_alloc_group(const struct fsnotify_ops *ops) if (!group) return ERR_PTR(-ENOMEM); + /* set to 0 when there a no external references to this group */ atomic_set(&group->refcnt, 1); + /* + * hits 0 when there are no external references AND no marks for + * this group + */ + atomic_set(&group->num_marks, 1); + mutex_init(&group->notification_mutex); INIT_LIST_HEAD(&group->notification_list); -- cgit v1.2.3 From 4ca763523e040dc61191d4866a82981a5d30a4e9 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:23 -0500 Subject: fsnotify: add groups to fsnotify_inode_groups when registering inode watch Currently all fsnotify groups are added immediately to the fsnotify_inode_groups list upon creation. This means, even groups with no watches (common for audit) will be on the global tracking list and will get checked for every event. This patch adds groups to the global list on when the first inode mark is added to the group. Signed-of-by: Eric Paris --- fs/notify/fsnotify.h | 2 ++ fs/notify/group.c | 18 ++++++++---------- fs/notify/inode_mark.c | 7 +++++++ 3 files changed, 17 insertions(+), 10 deletions(-) (limited to 'fs') diff --git a/fs/notify/fsnotify.h b/fs/notify/fsnotify.h index ec5aee43bdc5..5bd22412017f 100644 --- a/fs/notify/fsnotify.h +++ b/fs/notify/fsnotify.h @@ -16,6 +16,8 @@ extern __u32 fsnotify_inode_mask; /* destroy all events sitting in this groups notification queue */ extern void fsnotify_flush_notify(struct fsnotify_group *group); +/* add a group to the inode group list */ +extern void fsnotify_add_inode_group(struct fsnotify_group *group); /* final kfree of a group */ extern void fsnotify_final_destroy_group(struct fsnotify_group *group); diff --git a/fs/notify/group.c b/fs/notify/group.c index 656c534ffb66..34fccbd2809c 100644 --- a/fs/notify/group.c +++ b/fs/notify/group.c @@ -77,12 +77,15 @@ void fsnotify_recalc_group_mask(struct fsnotify_group *group) fsnotify_recalc_global_mask(); } -static void fsnotify_add_group(struct fsnotify_group *group) +void fsnotify_add_inode_group(struct fsnotify_group *group) { - BUG_ON(!mutex_is_locked(&fsnotify_grp_mutex)); + mutex_lock(&fsnotify_grp_mutex); + if (!group->on_inode_group_list) + list_add_tail_rcu(&group->inode_group_list, &fsnotify_inode_groups); group->on_inode_group_list = 1; - list_add_tail_rcu(&group->inode_group_list, &fsnotify_inode_groups); + + mutex_unlock(&fsnotify_grp_mutex); } /* @@ -188,22 +191,17 @@ struct fsnotify_group *fsnotify_alloc_group(const struct fsnotify_ops *ops) */ atomic_set(&group->num_marks, 1); - mutex_init(&group->notification_mutex); INIT_LIST_HEAD(&group->notification_list); init_waitqueue_head(&group->notification_waitq); group->max_events = UINT_MAX; + INIT_LIST_HEAD(&group->inode_group_list); + spin_lock_init(&group->mark_lock); INIT_LIST_HEAD(&group->mark_entries); group->ops = ops; - mutex_lock(&fsnotify_grp_mutex); - - fsnotify_add_group(group); - - mutex_unlock(&fsnotify_grp_mutex); - return group; } diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c index 7d2962e5328e..a3230c485531 100644 --- a/fs/notify/inode_mark.c +++ b/fs/notify/inode_mark.c @@ -322,6 +322,13 @@ int fsnotify_add_mark(struct fsnotify_mark_entry *entry, if (unlikely(!inode)) return -EINVAL; + /* + * if this group isn't being testing for inode type events we need + * to start testing + */ + if (unlikely(list_empty(&group->inode_group_list))) + fsnotify_add_inode_group(group); + /* * LOCKING ORDER!!!! * entry->lock -- cgit v1.2.3 From 7131485a93679ff9a543b74df280cfd119eb03ca Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:23 -0500 Subject: fsnotify: mount point listeners list and global mask currently all of the notification systems implemented select which inodes they care about and receive messages only about those inodes (or the children of those inodes.) This patch begins to flesh out fsnotify support for the concept of listeners that want to hear notification for an inode accessed below a given monut point. This patch implements a second list of fsnotify groups to hold these types of groups and a second global mask to hold the events of interest for this type of group. The reason we want a second group list and mask is because the inode based notification should_send_event support which makes each group look for a mark on the given inode. With one nfsmount listener that means that every group would have to take the inode->i_lock, look for their mark, not find one, and return for every operation. By seperating vfsmount from inode listeners only when there is a inode listener will the inode groups have to look for their mark and take the inode lock. vfsmount listeners will have to grab the lock and look for a mark but there should be fewer of them, and one vfsmount listener won't cause the i_lock to be grabbed and released for every fsnotify group on every io operation. Signed-off-by: Eric Paris --- fs/notify/fsnotify.c | 92 ++++++++++++++++++++++++++++++---------- fs/notify/fsnotify.h | 6 +++ fs/notify/group.c | 33 ++++++++++++-- fs/notify/inode_mark.c | 7 +++ include/linux/fsnotify_backend.h | 5 +++ 5 files changed, 117 insertions(+), 26 deletions(-) (limited to 'fs') diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index 23b5cfbeed50..a61aaa710825 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -134,6 +135,45 @@ void __fsnotify_parent(struct file *file, struct dentry *dentry, __u32 mask) } EXPORT_SYMBOL_GPL(__fsnotify_parent); +static void send_to_group(__u32 mask, + struct fsnotify_group *group, + void *data, int data_is, const char *file_name, + u32 cookie, struct fsnotify_event **event, + struct inode *to_tell) +{ + if (!group->ops->should_send_event(group, to_tell, mask, + data, data_is)) + return; + if (!*event) { + *event = fsnotify_create_event(to_tell, mask, data, + data_is, file_name, + cookie, GFP_KERNEL); + /* + * shit, we OOM'd and now we can't tell, maybe + * someday someone else will want to do something + * here + */ + if (!*event) + return; + } + group->ops->handle_event(group, *event); +} + +static bool needed_by_vfsmount(__u32 test_mask, void *data, int data_is) +{ + struct path *path; + + if (data_is == FSNOTIFY_EVENT_PATH) + path = (struct path *)data; + else if (data_is == FSNOTIFY_EVENT_FILE) + path = &((struct file *)data)->f_path; + else + return false; + + /* hook in this when mnt->mnt_fsnotify_mask is defined */ + /* return (test_mask & path->mnt->mnt_fsnotify_mask); */ + return false; +} /* * This is the main call to fsnotify. The VFS calls into hook specific functions * in linux/fsnotify.h. Those functions then in turn call here. Here will call @@ -148,38 +188,46 @@ void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, const /* global tests shouldn't care about events on child only the specific event */ __u32 test_mask = (mask & ~FS_EVENT_ON_CHILD); - if (list_empty(&fsnotify_inode_groups)) - return; + /* if no fsnotify listeners, nothing to do */ + if (list_empty(&fsnotify_inode_groups) && + list_empty(&fsnotify_vfsmount_groups)) + return; + + /* if none of the directed listeners or vfsmount listeners care */ + if (!(test_mask & fsnotify_inode_mask) && + !(test_mask & fsnotify_vfsmount_mask)) + return; + + /* if this inode's directed listeners don't care and nothing on the vfsmount + * listeners list cares, nothing to do */ + if (!(test_mask & to_tell->i_fsnotify_mask) && + !needed_by_vfsmount(test_mask, data, data_is)) + return; - if (!(test_mask & fsnotify_inode_mask)) - return; - - if (!(test_mask & to_tell->i_fsnotify_mask)) - return; /* * SRCU!! the groups list is very very much read only and the path is * very hot. The VAST majority of events are not going to need to do * anything other than walk the list so it's crazy to pre-allocate. */ idx = srcu_read_lock(&fsnotify_grp_srcu); - list_for_each_entry_rcu(group, &fsnotify_inode_groups, inode_group_list) { - if (test_mask & group->mask) { - if (!group->ops->should_send_event(group, to_tell, mask, - data, data_is)) - continue; - if (!event) { - event = fsnotify_create_event(to_tell, mask, data, - data_is, file_name, cookie, - GFP_KERNEL); - /* shit, we OOM'd and now we can't tell, maybe - * someday someone else will want to do something - * here */ - if (!event) - break; + + if (test_mask & to_tell->i_fsnotify_mask) { + list_for_each_entry_rcu(group, &fsnotify_inode_groups, inode_group_list) { + if (test_mask & group->mask) { + send_to_group(mask, group, data, data_is, + file_name, cookie, &event, to_tell); } - group->ops->handle_event(group, event); } } + if (needed_by_vfsmount(test_mask, data, data_is)) { + list_for_each_entry_rcu(group, &fsnotify_vfsmount_groups, vfsmount_group_list) { + if (test_mask & group->mask) { + send_to_group(mask, group, data, data_is, + file_name, cookie, &event, to_tell); + } + } + } + srcu_read_unlock(&fsnotify_grp_srcu, idx); /* * fsnotify_create_event() took a reference so the event can't be cleaned diff --git a/fs/notify/fsnotify.h b/fs/notify/fsnotify.h index 5bd22412017f..2ba59158969f 100644 --- a/fs/notify/fsnotify.h +++ b/fs/notify/fsnotify.h @@ -10,14 +10,20 @@ extern struct srcu_struct fsnotify_grp_srcu; /* all groups which receive inode fsnotify events */ extern struct list_head fsnotify_inode_groups; +/* all groups which receive vfsmount fsnotify events */ +extern struct list_head fsnotify_vfsmount_groups; /* all bitwise OR of all event types (FS_*) for all fsnotify_inode_groups */ extern __u32 fsnotify_inode_mask; +/* all bitwise OR of all event types (FS_*) for all fsnotify_vfsmount_groups */ +extern __u32 fsnotify_vfsmount_mask; /* destroy all events sitting in this groups notification queue */ extern void fsnotify_flush_notify(struct fsnotify_group *group); /* add a group to the inode group list */ extern void fsnotify_add_inode_group(struct fsnotify_group *group); +/* add a group to the vfsmount group list */ +extern void fsnotify_add_vfsmount_group(struct fsnotify_group *group); /* final kfree of a group */ extern void fsnotify_final_destroy_group(struct fsnotify_group *group); diff --git a/fs/notify/group.c b/fs/notify/group.c index 34fccbd2809c..aa4654fe6ec2 100644 --- a/fs/notify/group.c +++ b/fs/notify/group.c @@ -32,10 +32,14 @@ static DEFINE_MUTEX(fsnotify_grp_mutex); /* protects reads while running the fsnotify_groups list */ struct srcu_struct fsnotify_grp_srcu; -/* all groups registered to receive filesystem notifications */ +/* all groups registered to receive inode filesystem notifications */ LIST_HEAD(fsnotify_inode_groups); +/* all groups registered to receive mount point filesystem notifications */ +LIST_HEAD(fsnotify_vfsmount_groups); /* bitwise OR of all events (FS_*) interesting to some group on this system */ __u32 fsnotify_inode_mask; +/* bitwise OR of all events (FS_*) interesting to some group on this system */ +__u32 fsnotify_vfsmount_mask; /* * When a new group registers or changes it's set of interesting events @@ -44,14 +48,20 @@ __u32 fsnotify_inode_mask; void fsnotify_recalc_global_mask(void) { struct fsnotify_group *group; - __u32 mask = 0; + __u32 inode_mask = 0; + __u32 vfsmount_mask = 0; int idx; idx = srcu_read_lock(&fsnotify_grp_srcu); list_for_each_entry_rcu(group, &fsnotify_inode_groups, inode_group_list) - mask |= group->mask; + inode_mask |= group->mask; + list_for_each_entry_rcu(group, &fsnotify_vfsmount_groups, vfsmount_group_list) + vfsmount_mask |= group->mask; + srcu_read_unlock(&fsnotify_grp_srcu, idx); - fsnotify_inode_mask = mask; + + fsnotify_inode_mask = inode_mask; + fsnotify_vfsmount_mask = vfsmount_mask; } /* @@ -77,6 +87,17 @@ void fsnotify_recalc_group_mask(struct fsnotify_group *group) fsnotify_recalc_global_mask(); } +void fsnotify_add_vfsmount_group(struct fsnotify_group *group) +{ + mutex_lock(&fsnotify_grp_mutex); + + if (!group->on_vfsmount_group_list) + list_add_tail_rcu(&group->vfsmount_group_list, &fsnotify_vfsmount_groups); + group->on_vfsmount_group_list = 1; + + mutex_unlock(&fsnotify_grp_mutex); +} + void fsnotify_add_inode_group(struct fsnotify_group *group) { mutex_lock(&fsnotify_grp_mutex); @@ -132,6 +153,9 @@ static void __fsnotify_evict_group(struct fsnotify_group *group) if (group->on_inode_group_list) list_del_rcu(&group->inode_group_list); group->on_inode_group_list = 0; + if (group->on_vfsmount_group_list) + list_del_rcu(&group->vfsmount_group_list); + group->on_vfsmount_group_list = 0; } /* @@ -197,6 +221,7 @@ struct fsnotify_group *fsnotify_alloc_group(const struct fsnotify_ops *ops) group->max_events = UINT_MAX; INIT_LIST_HEAD(&group->inode_group_list); + INIT_LIST_HEAD(&group->vfsmount_group_list); spin_lock_init(&group->mark_lock); INIT_LIST_HEAD(&group->mark_entries); diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c index a3230c485531..beffebb64627 100644 --- a/fs/notify/inode_mark.c +++ b/fs/notify/inode_mark.c @@ -328,6 +328,13 @@ int fsnotify_add_mark(struct fsnotify_mark_entry *entry, */ if (unlikely(list_empty(&group->inode_group_list))) fsnotify_add_inode_group(group); + /* + * XXX This is where we could also do the fsnotify_add_vfsmount_group + * if we are setting and vfsmount mark.... + + if (unlikely(list_empty(&group->vfsmount_group_list))) + fsnotify_add_vfsmount_group(group); + */ /* * LOCKING ORDER!!!! diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index 21079ade5620..dea48bee057d 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -99,6 +99,10 @@ struct fsnotify_group { * or fsnotify_grp_srcu depending on write vs read. */ struct list_head inode_group_list; + /* + * same as above except anchored by fsnotify_vfsmount_groups + */ + struct list_head vfsmount_group_list; /* * Defines all of the event types in which this group is interested. @@ -137,6 +141,7 @@ struct fsnotify_group { /* prevents double list_del of group_list. protected by global fsnotify_grp_mutex */ bool on_inode_group_list; + bool on_vfsmount_group_list; /* groups can define private fields here or use the void *private */ union { -- cgit v1.2.3 From 3a9fb89f4cd04c23e16397befba92efb5d989b74 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:23 -0500 Subject: fsnotify: include vfsmount in should_send_event when appropriate To ensure that a group will not duplicate events when it receives it based on the vfsmount and the inode should_send_event test we should distinguish those two cases. We pass a vfsmount to this function so groups can make their own determinations. Signed-off-by: Eric Paris --- fs/notify/dnotify/dnotify.c | 4 ++-- fs/notify/fsnotify.c | 39 ++++++++++++++++++------------------ fs/notify/inotify/inotify_fsnotify.c | 3 ++- include/linux/fsnotify_backend.h | 3 ++- kernel/audit_tree.c | 3 ++- kernel/audit_watch.c | 3 ++- 6 files changed, 29 insertions(+), 26 deletions(-) (limited to 'fs') diff --git a/fs/notify/dnotify/dnotify.c b/fs/notify/dnotify/dnotify.c index e0a847bd53be..9eddafa4c7ba 100644 --- a/fs/notify/dnotify/dnotify.c +++ b/fs/notify/dnotify/dnotify.c @@ -133,8 +133,8 @@ static int dnotify_handle_event(struct fsnotify_group *group, * userspace notification for that pair. */ static bool dnotify_should_send_event(struct fsnotify_group *group, - struct inode *inode, __u32 mask, - void *data, int data_type) + struct inode *inode, struct vfsmount *mnt, + __u32 mask, void *data, int data_type) { struct fsnotify_mark_entry *entry; bool send; diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index a61aaa710825..78c440c343a8 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -135,13 +135,12 @@ void __fsnotify_parent(struct file *file, struct dentry *dentry, __u32 mask) } EXPORT_SYMBOL_GPL(__fsnotify_parent); -static void send_to_group(__u32 mask, - struct fsnotify_group *group, - void *data, int data_is, const char *file_name, - u32 cookie, struct fsnotify_event **event, - struct inode *to_tell) +static void send_to_group(struct fsnotify_group *group, struct inode *to_tell, + struct vfsmount *mnt, __u32 mask, void *data, + int data_is, u32 cookie, const char *file_name, + struct fsnotify_event **event) { - if (!group->ops->should_send_event(group, to_tell, mask, + if (!group->ops->should_send_event(group, to_tell, mnt, mask, data, data_is)) return; if (!*event) { @@ -159,15 +158,9 @@ static void send_to_group(__u32 mask, group->ops->handle_event(group, *event); } -static bool needed_by_vfsmount(__u32 test_mask, void *data, int data_is) +static bool needed_by_vfsmount(__u32 test_mask, struct vfsmount *mnt) { - struct path *path; - - if (data_is == FSNOTIFY_EVENT_PATH) - path = (struct path *)data; - else if (data_is == FSNOTIFY_EVENT_FILE) - path = &((struct file *)data)->f_path; - else + if (!mnt) return false; /* hook in this when mnt->mnt_fsnotify_mask is defined */ @@ -184,6 +177,7 @@ void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, const { struct fsnotify_group *group; struct fsnotify_event *event = NULL; + struct vfsmount *mnt = NULL; int idx; /* global tests shouldn't care about events on child only the specific event */ __u32 test_mask = (mask & ~FS_EVENT_ON_CHILD); @@ -198,10 +192,15 @@ void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, const !(test_mask & fsnotify_vfsmount_mask)) return; + if (data_is == FSNOTIFY_EVENT_PATH) + mnt = ((struct path *)data)->mnt; + else if (data_is == FSNOTIFY_EVENT_FILE) + mnt = ((struct file *)data)->f_path.mnt; + /* if this inode's directed listeners don't care and nothing on the vfsmount * listeners list cares, nothing to do */ if (!(test_mask & to_tell->i_fsnotify_mask) && - !needed_by_vfsmount(test_mask, data, data_is)) + !needed_by_vfsmount(test_mask, mnt)) return; /* @@ -214,16 +213,16 @@ void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, const if (test_mask & to_tell->i_fsnotify_mask) { list_for_each_entry_rcu(group, &fsnotify_inode_groups, inode_group_list) { if (test_mask & group->mask) { - send_to_group(mask, group, data, data_is, - file_name, cookie, &event, to_tell); + send_to_group(group, to_tell, NULL, mask, data, data_is, + cookie, file_name, &event); } } } - if (needed_by_vfsmount(test_mask, data, data_is)) { + if (needed_by_vfsmount(test_mask, mnt)) { list_for_each_entry_rcu(group, &fsnotify_vfsmount_groups, vfsmount_group_list) { if (test_mask & group->mask) { - send_to_group(mask, group, data, data_is, - file_name, cookie, &event, to_tell); + send_to_group(group, to_tell, mnt, mask, data, data_is, + cookie, file_name, &event); } } } diff --git a/fs/notify/inotify/inotify_fsnotify.c b/fs/notify/inotify/inotify_fsnotify.c index 0a0f5d0f0d0a..8075ae708ed4 100644 --- a/fs/notify/inotify/inotify_fsnotify.c +++ b/fs/notify/inotify/inotify_fsnotify.c @@ -141,7 +141,8 @@ static void inotify_freeing_mark(struct fsnotify_mark_entry *entry, struct fsnot } static bool inotify_should_send_event(struct fsnotify_group *group, struct inode *inode, - __u32 mask, void *data, int data_type) + struct vfsmount *mnt, __u32 mask, void *data, + int data_type) { struct fsnotify_mark_entry *entry; bool send; diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index dea48bee057d..c2a04b7e4fca 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -79,7 +79,8 @@ struct fsnotify_event_private_data; */ struct fsnotify_ops { bool (*should_send_event)(struct fsnotify_group *group, struct inode *inode, - __u32 mask, void *data, int data_type); + struct vfsmount *mnt, __u32 mask, void *data, + int data_type); int (*handle_event)(struct fsnotify_group *group, struct fsnotify_event *event); void (*free_group_priv)(struct fsnotify_group *group); void (*freeing_mark)(struct fsnotify_mark_entry *entry, struct fsnotify_group *group); diff --git a/kernel/audit_tree.c b/kernel/audit_tree.c index 04f16887406b..ecf0bf260d09 100644 --- a/kernel/audit_tree.c +++ b/kernel/audit_tree.c @@ -920,7 +920,8 @@ static void audit_tree_freeing_mark(struct fsnotify_mark_entry *entry, struct fs } static bool audit_tree_send_event(struct fsnotify_group *group, struct inode *inode, - __u32 mask, void *data, int data_type) + struct vfsmount *mnt, __u32 mask, void *data, + int data_type) { return 0; } diff --git a/kernel/audit_watch.c b/kernel/audit_watch.c index 83d5f9674cec..6304ee5d7642 100644 --- a/kernel/audit_watch.c +++ b/kernel/audit_watch.c @@ -514,7 +514,8 @@ void audit_remove_watch_rule(struct audit_krule *krule) } static bool audit_watch_should_send_event(struct fsnotify_group *group, struct inode *inode, - __u32 mask, void *data, int data_type) + struct vfsmount *mnt, __u32 mask, void *data, + int data_type) { struct fsnotify_mark_entry *entry; bool send; -- cgit v1.2.3 From 2823e04de4f1a49087b58ff2bb8f61361ffd9321 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:23 -0500 Subject: fsnotify: put inode specific fields in an fsnotify_mark in a union The addition of marks on vfs mounts will be simplified if the inode specific parts of a mark and the vfsmnt specific parts of a mark are actually in a union so naming can be easy. This patch just implements the inode struct and the union. Signed-off-by: Eric Paris --- fs/notify/dnotify/dnotify.c | 4 ++-- fs/notify/inode_mark.c | 28 ++++++++++++++-------------- fs/notify/inotify/inotify_fsnotify.c | 2 +- fs/notify/inotify/inotify_user.c | 10 +++++----- include/linux/fsnotify_backend.h | 17 +++++++++++++---- kernel/audit_tree.c | 16 ++++++++-------- 6 files changed, 43 insertions(+), 34 deletions(-) (limited to 'fs') diff --git a/fs/notify/dnotify/dnotify.c b/fs/notify/dnotify/dnotify.c index 9eddafa4c7ba..fc3a9dc567c5 100644 --- a/fs/notify/dnotify/dnotify.c +++ b/fs/notify/dnotify/dnotify.c @@ -70,8 +70,8 @@ static void dnotify_recalc_inode_mask(struct fsnotify_mark_entry *entry) if (old_mask == new_mask) return; - if (entry->inode) - fsnotify_recalc_inode_mask(entry->inode); + if (entry->i.inode) + fsnotify_recalc_inode_mask(entry->i.inode); } /* diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c index beffebb64627..6731408c49f7 100644 --- a/fs/notify/inode_mark.c +++ b/fs/notify/inode_mark.c @@ -117,7 +117,7 @@ static void fsnotify_recalc_inode_mask_locked(struct inode *inode) assert_spin_locked(&inode->i_lock); - hlist_for_each_entry(entry, pos, &inode->i_fsnotify_mark_entries, i_list) + hlist_for_each_entry(entry, pos, &inode->i_fsnotify_mark_entries, i.i_list) new_mask |= entry->mask; inode->i_fsnotify_mask = new_mask; } @@ -148,7 +148,7 @@ void fsnotify_destroy_mark_by_entry(struct fsnotify_mark_entry *entry) spin_lock(&entry->lock); group = entry->group; - inode = entry->inode; + inode = entry->i.inode; BUG_ON(group && !inode); BUG_ON(!group && inode); @@ -165,8 +165,8 @@ void fsnotify_destroy_mark_by_entry(struct fsnotify_mark_entry *entry) spin_lock(&group->mark_lock); spin_lock(&inode->i_lock); - hlist_del_init(&entry->i_list); - entry->inode = NULL; + hlist_del_init(&entry->i.i_list); + entry->i.inode = NULL; list_del_init(&entry->g_list); entry->group = NULL; @@ -248,14 +248,14 @@ void fsnotify_clear_marks_by_inode(struct inode *inode) LIST_HEAD(free_list); spin_lock(&inode->i_lock); - hlist_for_each_entry_safe(entry, pos, n, &inode->i_fsnotify_mark_entries, i_list) { - list_add(&entry->free_i_list, &free_list); - hlist_del_init(&entry->i_list); + hlist_for_each_entry_safe(entry, pos, n, &inode->i_fsnotify_mark_entries, i.i_list) { + list_add(&entry->i.free_i_list, &free_list); + hlist_del_init(&entry->i.i_list); fsnotify_get_mark(entry); } spin_unlock(&inode->i_lock); - list_for_each_entry_safe(entry, lentry, &free_list, free_i_list) { + list_for_each_entry_safe(entry, lentry, &free_list, i.free_i_list) { fsnotify_destroy_mark_by_entry(entry); fsnotify_put_mark(entry); } @@ -273,7 +273,7 @@ struct fsnotify_mark_entry *fsnotify_find_mark_entry(struct fsnotify_group *grou assert_spin_locked(&inode->i_lock); - hlist_for_each_entry(entry, pos, &inode->i_fsnotify_mark_entries, i_list) { + hlist_for_each_entry(entry, pos, &inode->i_fsnotify_mark_entries, i.i_list) { if (entry->group == group) { fsnotify_get_mark(entry); return entry; @@ -285,7 +285,7 @@ struct fsnotify_mark_entry *fsnotify_find_mark_entry(struct fsnotify_group *grou void fsnotify_duplicate_mark(struct fsnotify_mark_entry *new, struct fsnotify_mark_entry *old) { assert_spin_locked(&old->lock); - new->inode = old->inode; + new->i.inode = old->i.inode; new->group = old->group; new->mask = old->mask; new->free_mark = old->free_mark; @@ -299,10 +299,10 @@ void fsnotify_init_mark(struct fsnotify_mark_entry *entry, { spin_lock_init(&entry->lock); atomic_set(&entry->refcnt, 1); - INIT_HLIST_NODE(&entry->i_list); + INIT_HLIST_NODE(&entry->i.i_list); entry->group = NULL; entry->mask = 0; - entry->inode = NULL; + entry->i.inode = NULL; entry->free_mark = free_mark; } @@ -350,9 +350,9 @@ int fsnotify_add_mark(struct fsnotify_mark_entry *entry, lentry = fsnotify_find_mark_entry(group, inode); if (!lentry) { entry->group = group; - entry->inode = inode; + entry->i.inode = inode; - hlist_add_head(&entry->i_list, &inode->i_fsnotify_mark_entries); + hlist_add_head(&entry->i.i_list, &inode->i_fsnotify_mark_entries); list_add(&entry->g_list, &group->mark_entries); fsnotify_get_mark(entry); /* for i_list and g_list */ diff --git a/fs/notify/inotify/inotify_fsnotify.c b/fs/notify/inotify/inotify_fsnotify.c index 8075ae708ed4..3edb51cfcfbe 100644 --- a/fs/notify/inotify/inotify_fsnotify.c +++ b/fs/notify/inotify/inotify_fsnotify.c @@ -193,7 +193,7 @@ static int idr_callback(int id, void *p, void *data) */ if (entry) printk(KERN_WARNING "entry->group=%p inode=%p wd=%d\n", - entry->group, entry->inode, ientry->wd); + entry->group, entry->i.inode, ientry->wd); return 0; } diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index a48d68a68b25..4b1587f9df3b 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -445,7 +445,7 @@ static void inotify_remove_from_idr(struct fsnotify_group *group, if (wd == -1) { WARN_ONCE(1, "%s: ientry=%p ientry->wd=%d ientry->group=%p" " ientry->inode=%p\n", __func__, ientry, ientry->wd, - ientry->fsn_entry.group, ientry->fsn_entry.inode); + ientry->fsn_entry.group, ientry->fsn_entry.i.inode); goto out; } @@ -454,7 +454,7 @@ static void inotify_remove_from_idr(struct fsnotify_group *group, if (unlikely(!found_ientry)) { WARN_ONCE(1, "%s: ientry=%p ientry->wd=%d ientry->group=%p" " ientry->inode=%p\n", __func__, ientry, ientry->wd, - ientry->fsn_entry.group, ientry->fsn_entry.inode); + ientry->fsn_entry.group, ientry->fsn_entry.i.inode); goto out; } @@ -468,9 +468,9 @@ static void inotify_remove_from_idr(struct fsnotify_group *group, "entry->inode=%p found_ientry=%p found_ientry->wd=%d " "found_ientry->group=%p found_ientry->inode=%p\n", __func__, ientry, ientry->wd, ientry->fsn_entry.group, - ientry->fsn_entry.inode, found_ientry, found_ientry->wd, + ientry->fsn_entry.i.inode, found_ientry, found_ientry->wd, found_ientry->fsn_entry.group, - found_ientry->fsn_entry.inode); + found_ientry->fsn_entry.i.inode); goto out; } @@ -482,7 +482,7 @@ static void inotify_remove_from_idr(struct fsnotify_group *group, if (unlikely(atomic_read(&ientry->fsn_entry.refcnt) < 3)) { printk(KERN_ERR "%s: ientry=%p ientry->wd=%d ientry->group=%p" " ientry->inode=%p\n", __func__, ientry, ientry->wd, - ientry->fsn_entry.group, ientry->fsn_entry.inode); + ientry->fsn_entry.group, ientry->fsn_entry.i.inode); /* we can't really recover with bad ref cnting.. */ BUG(); } diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index c2a04b7e4fca..dca7f2cbde90 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -226,6 +226,15 @@ struct fsnotify_event { struct list_head private_data_list; /* groups can store private data here */ }; +/* + * Inode specific fields in an fsnotify_mark_entry + */ +struct fsnotify_inode_mark { + struct inode *inode; /* inode this entry is associated with */ + struct hlist_node i_list; /* list of mark_entries by inode->i_fsnotify_mark_entries */ + struct list_head free_i_list; /* tmp list used when freeing this mark */ +}; + /* * a mark is simply an entry attached to an in core inode which allows an * fsnotify listener to indicate they are either no longer interested in events @@ -241,12 +250,12 @@ struct fsnotify_mark_entry { /* we hold ref for each i_list and g_list. also one ref for each 'thing' * in kernel that found and may be using this mark. */ atomic_t refcnt; /* active things looking at this mark */ - struct inode *inode; /* inode this entry is associated with */ struct fsnotify_group *group; /* group this mark entry is for */ - struct hlist_node i_list; /* list of mark_entries by inode->i_fsnotify_mark_entries */ struct list_head g_list; /* list of mark_entries by group->i_fsnotify_mark_entries */ - spinlock_t lock; /* protect group, inode, and killme */ - struct list_head free_i_list; /* tmp list used when freeing this mark */ + spinlock_t lock; /* protect group and inode */ + union { + struct fsnotify_inode_mark i; + }; struct list_head free_g_list; /* tmp list used when freeing this mark */ void (*free_mark)(struct fsnotify_mark_entry *entry); /* called on final put+free */ }; diff --git a/kernel/audit_tree.c b/kernel/audit_tree.c index ecf0bf260d09..c21b05d25224 100644 --- a/kernel/audit_tree.c +++ b/kernel/audit_tree.c @@ -179,9 +179,9 @@ static void insert_hash(struct audit_chunk *chunk) struct fsnotify_mark_entry *entry = &chunk->mark; struct list_head *list; - if (!entry->inode) + if (!entry->i.inode) return; - list = chunk_hash(entry->inode); + list = chunk_hash(entry->i.inode); list_add_rcu(&chunk->hash, list); } @@ -193,7 +193,7 @@ struct audit_chunk *audit_tree_lookup(const struct inode *inode) list_for_each_entry_rcu(p, list, hash) { /* mark.inode may have gone NULL, but who cares? */ - if (p->mark.inode == inode) { + if (p->mark.i.inode == inode) { atomic_long_inc(&p->refs); return p; } @@ -233,7 +233,7 @@ static void untag_chunk(struct node *p) spin_unlock(&hash_lock); spin_lock(&entry->lock); - if (chunk->dead || !entry->inode) { + if (chunk->dead || !entry->i.inode) { spin_unlock(&entry->lock); goto out; } @@ -259,7 +259,7 @@ static void untag_chunk(struct node *p) if (!new) goto Fallback; fsnotify_duplicate_mark(&new->mark, entry); - if (fsnotify_add_mark(&new->mark, new->mark.group, new->mark.inode, 1)) { + if (fsnotify_add_mark(&new->mark, new->mark.group, new->mark.i.inode, 1)) { free_chunk(new); goto Fallback; } @@ -388,7 +388,7 @@ static int tag_chunk(struct inode *inode, struct audit_tree *tree) chunk_entry = &chunk->mark; spin_lock(&old_entry->lock); - if (!old_entry->inode) { + if (!old_entry->i.inode) { /* old_entry is being shot, lets just lie */ spin_unlock(&old_entry->lock); fsnotify_put_mark(old_entry); @@ -397,7 +397,7 @@ static int tag_chunk(struct inode *inode, struct audit_tree *tree) } fsnotify_duplicate_mark(chunk_entry, old_entry); - if (fsnotify_add_mark(chunk_entry, chunk_entry->group, chunk_entry->inode, 1)) { + if (fsnotify_add_mark(chunk_entry, chunk_entry->group, chunk_entry->i.inode, 1)) { spin_unlock(&old_entry->lock); free_chunk(chunk); fsnotify_put_mark(old_entry); @@ -605,7 +605,7 @@ void audit_trim_trees(void) list_for_each_entry(node, &tree->chunks, list) { struct audit_chunk *chunk = find_chunk(node); /* this could be NULL if the watch is dieing else where... */ - struct inode *inode = chunk->mark.inode; + struct inode *inode = chunk->mark.i.inode; node->index |= 1U<<31; if (iterate_mounts(compare_root, inode, root_mnt)) node->index &= ~(1U<<31); -- cgit v1.2.3 From 098cf2fc77ee190c92bf9d08d69a13305f2487ec Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:24 -0500 Subject: fsnotify: add flags to fsnotify_mark_entries To differentiate between inode and vfsmount (or other future) types of marks we add a flags field and set the inode bit on inode marks (the only currently supported type of mark) Signed-off-by: Eric Paris --- fs/notify/inode_mark.c | 2 ++ include/linux/fsnotify_backend.h | 3 +++ 2 files changed, 5 insertions(+) (limited to 'fs') diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c index 6731408c49f7..b00065842b3e 100644 --- a/fs/notify/inode_mark.c +++ b/fs/notify/inode_mark.c @@ -322,6 +322,8 @@ int fsnotify_add_mark(struct fsnotify_mark_entry *entry, if (unlikely(!inode)) return -EINVAL; + entry->flags = FSNOTIFY_MARK_FLAG_INODE; + /* * if this group isn't being testing for inode type events we need * to start testing diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index 0c0fd4ee2840..cf165857199b 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -267,6 +267,9 @@ struct fsnotify_mark_entry { struct fsnotify_vfsmount_mark m; }; struct list_head free_g_list; /* tmp list used when freeing this mark */ +#define FSNOTIFY_MARK_FLAG_INODE 0x01 +#define FSNOTIFY_MARK_FLAG_VFSMOUNT 0x02 + unsigned int flags; /* vfsmount or inode mark? */ void (*free_mark)(struct fsnotify_mark_entry *entry); /* called on final put+free */ }; -- cgit v1.2.3 From 72acc854427948efed7a83da27f7dc3239ac9afc Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Thu, 17 Dec 2009 21:24:24 -0500 Subject: fsnotify: kill FSNOTIFY_EVENT_FILE Some fsnotify operations send a struct file. This is more information than we technically need. We instead send a struct path in all cases instead of sometimes a path and sometimes a file. Signed-off-by: Andreas Gruenbacher Signed-off-by: Eric Paris --- fs/notify/fsnotify.c | 12 +++++------- fs/notify/notification.c | 9 --------- include/linux/fsnotify.h | 36 +++++++++++++++++++----------------- include/linux/fsnotify_backend.h | 5 ++--- 4 files changed, 26 insertions(+), 36 deletions(-) (limited to 'fs') diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index 78c440c343a8..60e84fd338dd 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -79,15 +79,15 @@ void __fsnotify_update_child_dentry_flags(struct inode *inode) } /* Notify this dentry's parent about a child's events. */ -void __fsnotify_parent(struct file *file, struct dentry *dentry, __u32 mask) +void __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask) { struct dentry *parent; struct inode *p_inode; bool send = false; bool should_update_children = false; - if (file) - dentry = file->f_path.dentry; + if (!dentry) + dentry = path->dentry; if (!(dentry->d_flags & DCACHE_FSNOTIFY_PARENT_WATCHED)) return; @@ -119,8 +119,8 @@ void __fsnotify_parent(struct file *file, struct dentry *dentry, __u32 mask) * specifies these are events which came from a child. */ mask |= FS_EVENT_ON_CHILD; - if (file) - fsnotify(p_inode, mask, file, FSNOTIFY_EVENT_FILE, + if (path) + fsnotify(p_inode, mask, path, FSNOTIFY_EVENT_PATH, dentry->d_name.name, 0); else fsnotify(p_inode, mask, dentry->d_inode, FSNOTIFY_EVENT_INODE, @@ -194,8 +194,6 @@ void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, const if (data_is == FSNOTIFY_EVENT_PATH) mnt = ((struct path *)data)->mnt; - else if (data_is == FSNOTIFY_EVENT_FILE) - mnt = ((struct file *)data)->f_path.mnt; /* if this inode's directed listeners don't care and nothing on the vfsmount * listeners list cares, nothing to do */ diff --git a/fs/notify/notification.c b/fs/notify/notification.c index dafd0b7687b8..066f1f988bac 100644 --- a/fs/notify/notification.c +++ b/fs/notify/notification.c @@ -390,15 +390,6 @@ struct fsnotify_event *fsnotify_create_event(struct inode *to_tell, __u32 mask, event->data_type = data_type; switch (data_type) { - case FSNOTIFY_EVENT_FILE: { - struct file *file = data; - struct path *path = &file->f_path; - event->path.dentry = path->dentry; - event->path.mnt = path->mnt; - path_get(&event->path); - event->data_type = FSNOTIFY_EVENT_PATH; - break; - } case FSNOTIFY_EVENT_PATH: { struct path *path = data; event->path.dentry = path->dentry; diff --git a/include/linux/fsnotify.h b/include/linux/fsnotify.h index 04ea03ea8090..06d296d85ebf 100644 --- a/include/linux/fsnotify.h +++ b/include/linux/fsnotify.h @@ -26,14 +26,12 @@ static inline void fsnotify_d_instantiate(struct dentry *entry, } /* Notify this dentry's parent about a child's events. */ -static inline void fsnotify_parent(struct file *file, struct dentry *dentry, __u32 mask) +static inline void fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask) { - BUG_ON(file && dentry); + if (!dentry) + dentry = path->dentry; - if (file) - dentry = file->f_path.dentry; - - __fsnotify_parent(file, dentry, mask); + __fsnotify_parent(path, dentry, mask); } /* @@ -160,14 +158,15 @@ static inline void fsnotify_mkdir(struct inode *inode, struct dentry *dentry) */ static inline void fsnotify_access(struct file *file) { - struct inode *inode = file->f_path.dentry->d_inode; + struct path *path = &file->f_path; + struct inode *inode = path->dentry->d_inode; __u32 mask = FS_ACCESS; if (S_ISDIR(inode->i_mode)) mask |= FS_IN_ISDIR; - fsnotify_parent(file, NULL, mask); - fsnotify(inode, mask, file, FSNOTIFY_EVENT_FILE, NULL, 0); + fsnotify_parent(path, NULL, mask); + fsnotify(inode, mask, path, FSNOTIFY_EVENT_PATH, NULL, 0); } /* @@ -175,14 +174,15 @@ static inline void fsnotify_access(struct file *file) */ static inline void fsnotify_modify(struct file *file) { - struct inode *inode = file->f_path.dentry->d_inode; + struct path *path = &file->f_path; + struct inode *inode = path->dentry->d_inode; __u32 mask = FS_MODIFY; if (S_ISDIR(inode->i_mode)) mask |= FS_IN_ISDIR; - fsnotify_parent(file, NULL, mask); - fsnotify(inode, mask, file, FSNOTIFY_EVENT_FILE, NULL, 0); + fsnotify_parent(path, NULL, mask); + fsnotify(inode, mask, path, FSNOTIFY_EVENT_PATH, NULL, 0); } /* @@ -190,14 +190,15 @@ static inline void fsnotify_modify(struct file *file) */ static inline void fsnotify_open(struct file *file) { - struct inode *inode = file->f_path.dentry->d_inode; + struct path *path = &file->f_path; + struct inode *inode = path->dentry->d_inode; __u32 mask = FS_OPEN; if (S_ISDIR(inode->i_mode)) mask |= FS_IN_ISDIR; - fsnotify_parent(file, NULL, mask); - fsnotify(inode, mask, file, FSNOTIFY_EVENT_FILE, NULL, 0); + fsnotify_parent(path, NULL, mask); + fsnotify(inode, mask, path, FSNOTIFY_EVENT_PATH, NULL, 0); } /* @@ -205,6 +206,7 @@ static inline void fsnotify_open(struct file *file) */ static inline void fsnotify_close(struct file *file) { + struct path *path = &file->f_path; struct inode *inode = file->f_path.dentry->d_inode; fmode_t mode = file->f_mode; __u32 mask = (mode & FMODE_WRITE) ? FS_CLOSE_WRITE : FS_CLOSE_NOWRITE; @@ -212,8 +214,8 @@ static inline void fsnotify_close(struct file *file) if (S_ISDIR(inode->i_mode)) mask |= FS_IN_ISDIR; - fsnotify_parent(file, NULL, mask); - fsnotify(inode, mask, file, FSNOTIFY_EVENT_FILE, NULL, 0); + fsnotify_parent(path, NULL, mask); + fsnotify(inode, mask, path, FSNOTIFY_EVENT_PATH, NULL, 0); } /* diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index cf165857199b..7a6ba755acc3 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -214,7 +214,6 @@ struct fsnotify_event { #define FSNOTIFY_EVENT_NONE 0 #define FSNOTIFY_EVENT_PATH 1 #define FSNOTIFY_EVENT_INODE 2 -#define FSNOTIFY_EVENT_FILE 3 int data_type; /* which of the above union we have */ atomic_t refcnt; /* how many groups still are using/need to send this event */ __u32 mask; /* the type of access, bitwise OR for FS_* event types */ @@ -280,7 +279,7 @@ struct fsnotify_mark_entry { /* main fsnotify call to send events */ extern void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, const char *name, u32 cookie); -extern void __fsnotify_parent(struct file *file, struct dentry *dentry, __u32 mask); +extern void __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask); extern void __fsnotify_inode_delete(struct inode *inode); extern u32 fsnotify_get_cookie(void); @@ -393,7 +392,7 @@ static inline void fsnotify(struct inode *to_tell, __u32 mask, void *data, int d const char *name, u32 cookie) {} -static inline void __fsnotify_parent(struct file *file, struct dentry *dentry, __u32 mask) +static inline void __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask) {} static inline void __fsnotify_inode_delete(struct inode *inode) -- cgit v1.2.3 From e61ce86737b4d60521e4e71f9892fe4bdcfb688b Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:24 -0500 Subject: fsnotify: rename fsnotify_mark_entry to just fsnotify_mark The name is long and it serves no real purpose. So rename fsnotify_mark_entry to just fsnotify_mark. Signed-off-by: Eric Paris --- fs/inode.c | 2 +- fs/notify/dnotify/dnotify.c | 24 +++++++++--------- fs/notify/group.c | 8 +++--- fs/notify/inode_mark.c | 48 ++++++++++++++++++------------------ fs/notify/inotify/inotify.h | 6 ++--- fs/notify/inotify/inotify_fsnotify.c | 8 +++--- fs/notify/inotify/inotify_user.c | 8 +++--- include/linux/fs.h | 2 +- include/linux/fsnotify.h | 18 +++++++------- include/linux/fsnotify_backend.h | 38 ++++++++++++++-------------- kernel/audit_tree.c | 14 +++++------ kernel/audit_watch.c | 8 +++--- kernel/auditsc.c | 4 +-- 13 files changed, 94 insertions(+), 94 deletions(-) (limited to 'fs') diff --git a/fs/inode.c b/fs/inode.c index 8e1bee998796..a2da778467bb 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -264,7 +264,7 @@ void inode_init_once(struct inode *inode) INIT_LIST_HEAD(&inode->i_data.i_mmap_nonlinear); i_size_ordered_init(inode); #ifdef CONFIG_FSNOTIFY - INIT_HLIST_HEAD(&inode->i_fsnotify_mark_entries); + INIT_HLIST_HEAD(&inode->i_fsnotify_marks); #endif } EXPORT_SYMBOL(inode_init_once); diff --git a/fs/notify/dnotify/dnotify.c b/fs/notify/dnotify/dnotify.c index fc3a9dc567c5..e6edae60894d 100644 --- a/fs/notify/dnotify/dnotify.c +++ b/fs/notify/dnotify/dnotify.c @@ -34,12 +34,12 @@ static struct fsnotify_group *dnotify_group __read_mostly; static DEFINE_MUTEX(dnotify_mark_mutex); /* - * dnotify will attach one of these to each inode (i_fsnotify_mark_entries) which + * dnotify will attach one of these to each inode (i_fsnotify_marks) which * is being watched by dnotify. If multiple userspace applications are watching * the same directory with dnotify their information is chained in dn */ struct dnotify_mark_entry { - struct fsnotify_mark_entry fsn_entry; + struct fsnotify_mark fsn_entry; struct dnotify_struct *dn; }; @@ -51,7 +51,7 @@ struct dnotify_mark_entry { * it calls the fsnotify function so it can update the set of all events relevant * to this inode. */ -static void dnotify_recalc_inode_mask(struct fsnotify_mark_entry *entry) +static void dnotify_recalc_inode_mask(struct fsnotify_mark *entry) { __u32 new_mask, old_mask; struct dnotify_struct *dn; @@ -85,7 +85,7 @@ static void dnotify_recalc_inode_mask(struct fsnotify_mark_entry *entry) static int dnotify_handle_event(struct fsnotify_group *group, struct fsnotify_event *event) { - struct fsnotify_mark_entry *entry = NULL; + struct fsnotify_mark *entry = NULL; struct dnotify_mark_entry *dnentry; struct inode *to_tell; struct dnotify_struct *dn; @@ -136,7 +136,7 @@ static bool dnotify_should_send_event(struct fsnotify_group *group, struct inode *inode, struct vfsmount *mnt, __u32 mask, void *data, int data_type) { - struct fsnotify_mark_entry *entry; + struct fsnotify_mark *entry; bool send; /* !dir_notify_enable should never get here, don't waste time checking @@ -163,7 +163,7 @@ static bool dnotify_should_send_event(struct fsnotify_group *group, return send; } -static void dnotify_free_mark(struct fsnotify_mark_entry *entry) +static void dnotify_free_mark(struct fsnotify_mark *entry) { struct dnotify_mark_entry *dnentry = container_of(entry, struct dnotify_mark_entry, @@ -184,14 +184,14 @@ static struct fsnotify_ops dnotify_fsnotify_ops = { /* * Called every time a file is closed. Looks first for a dnotify mark on the - * inode. If one is found run all of the ->dn entries attached to that + * inode. If one is found run all of the ->dn structures attached to that * mark for one relevant to this process closing the file and remove that * dnotify_struct. If that was the last dnotify_struct also remove the - * fsnotify_mark_entry. + * fsnotify_mark. */ void dnotify_flush(struct file *filp, fl_owner_t id) { - struct fsnotify_mark_entry *entry; + struct fsnotify_mark *entry; struct dnotify_mark_entry *dnentry; struct dnotify_struct *dn; struct dnotify_struct **prev; @@ -260,7 +260,7 @@ static __u32 convert_arg(unsigned long arg) /* * If multiple processes watch the same inode with dnotify there is only one - * dnotify mark in inode->i_fsnotify_mark_entries but we chain a dnotify_struct + * dnotify mark in inode->i_fsnotify_marks but we chain a dnotify_struct * onto that mark. This function either attaches the new dnotify_struct onto * that list, or it |= the mask onto an existing dnofiy_struct. */ @@ -298,7 +298,7 @@ static int attach_dn(struct dnotify_struct *dn, struct dnotify_mark_entry *dnent int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg) { struct dnotify_mark_entry *new_dnentry, *dnentry; - struct fsnotify_mark_entry *new_entry, *entry; + struct fsnotify_mark *new_entry, *entry; struct dnotify_struct *dn; struct inode *inode; fl_owner_t id = current->files; @@ -378,7 +378,7 @@ int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg) /* if (f != filp) means that we lost a race and another task/thread * actually closed the fd we are still playing with before we grabbed * the dnotify_mark_mutex and entry->lock. Since closing the fd is the - * only time we clean up the mark entries we need to get our mark off + * only time we clean up the marks we need to get our mark off * the list. */ if (f != filp) { /* if we added ourselves, shoot ourselves, it's possible that diff --git a/fs/notify/group.c b/fs/notify/group.c index aa4654fe6ec2..b70e7d21dfde 100644 --- a/fs/notify/group.c +++ b/fs/notify/group.c @@ -74,10 +74,10 @@ void fsnotify_recalc_group_mask(struct fsnotify_group *group) { __u32 mask = 0; __u32 old_mask = group->mask; - struct fsnotify_mark_entry *entry; + struct fsnotify_mark *entry; spin_lock(&group->mark_lock); - list_for_each_entry(entry, &group->mark_entries, g_list) + list_for_each_entry(entry, &group->marks_list, g_list) mask |= entry->mask; spin_unlock(&group->mark_lock); @@ -133,7 +133,7 @@ void fsnotify_final_destroy_group(struct fsnotify_group *group) */ static void fsnotify_destroy_group(struct fsnotify_group *group) { - /* clear all inode mark entries for this group */ + /* clear all inode marks for this group */ fsnotify_clear_marks_by_group(group); /* past the point of no return, matches the initial value of 1 */ @@ -224,7 +224,7 @@ struct fsnotify_group *fsnotify_alloc_group(const struct fsnotify_ops *ops) INIT_LIST_HEAD(&group->vfsmount_group_list); spin_lock_init(&group->mark_lock); - INIT_LIST_HEAD(&group->mark_entries); + INIT_LIST_HEAD(&group->marks_list); group->ops = ops; diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c index b00065842b3e..7e69f6b08d4e 100644 --- a/fs/notify/inode_mark.c +++ b/fs/notify/inode_mark.c @@ -38,12 +38,12 @@ * that lock to dereference either of these things (they could be NULL even with * the lock) * - * group->mark_lock protects the mark_entries list anchored inside a given group + * group->mark_lock protects the marks_list anchored inside a given group * and each entry is hooked via the g_list. It also sorta protects the * free_g_list, which when used is anchored by a private list on the stack of the * task which held the group->mark_lock. * - * inode->i_lock protects the i_fsnotify_mark_entries list anchored inside a + * inode->i_lock protects the i_fsnotify_marks list anchored inside a * given inode and each entry is hooked via the i_list. (and sorta the * free_i_list) * @@ -61,7 +61,7 @@ * need to be cleaned up. (fsnotify_clear_marks_by_group) * * Worst case we are given an inode and need to clean up all the marks on that - * inode. We take i_lock and walk the i_fsnotify_mark_entries safely. For each + * inode. We take i_lock and walk the i_fsnotify_marks safely. For each * mark on the list we take a reference (so the mark can't disappear under us). * We remove that mark form the inode's list of marks and we add this mark to a * private list anchored on the stack using i_free_list; At this point we no @@ -95,12 +95,12 @@ #include #include "fsnotify.h" -void fsnotify_get_mark(struct fsnotify_mark_entry *entry) +void fsnotify_get_mark(struct fsnotify_mark *entry) { atomic_inc(&entry->refcnt); } -void fsnotify_put_mark(struct fsnotify_mark_entry *entry) +void fsnotify_put_mark(struct fsnotify_mark *entry) { if (atomic_dec_and_test(&entry->refcnt)) entry->free_mark(entry); @@ -111,13 +111,13 @@ void fsnotify_put_mark(struct fsnotify_mark_entry *entry) */ static void fsnotify_recalc_inode_mask_locked(struct inode *inode) { - struct fsnotify_mark_entry *entry; + struct fsnotify_mark *entry; struct hlist_node *pos; __u32 new_mask = 0; assert_spin_locked(&inode->i_lock); - hlist_for_each_entry(entry, pos, &inode->i_fsnotify_mark_entries, i.i_list) + hlist_for_each_entry(entry, pos, &inode->i_fsnotify_marks, i.i_list) new_mask |= entry->mask; inode->i_fsnotify_mask = new_mask; } @@ -140,7 +140,7 @@ void fsnotify_recalc_inode_mask(struct inode *inode) * The caller had better be holding a reference to this mark so we don't actually * do the final put under the entry->lock */ -void fsnotify_destroy_mark_by_entry(struct fsnotify_mark_entry *entry) +void fsnotify_destroy_mark_by_entry(struct fsnotify_mark *entry) { struct fsnotify_group *group; struct inode *inode; @@ -174,7 +174,7 @@ void fsnotify_destroy_mark_by_entry(struct fsnotify_mark_entry *entry) fsnotify_put_mark(entry); /* for i_list and g_list */ /* - * this mark is now off the inode->i_fsnotify_mark_entries list and we + * this mark is now off the inode->i_fsnotify_marks list and we * hold the inode->i_lock, so this is the perfect time to update the * inode->i_fsnotify_mask */ @@ -221,11 +221,11 @@ void fsnotify_destroy_mark_by_entry(struct fsnotify_mark_entry *entry) */ void fsnotify_clear_marks_by_group(struct fsnotify_group *group) { - struct fsnotify_mark_entry *lentry, *entry; + struct fsnotify_mark *lentry, *entry; LIST_HEAD(free_list); spin_lock(&group->mark_lock); - list_for_each_entry_safe(entry, lentry, &group->mark_entries, g_list) { + list_for_each_entry_safe(entry, lentry, &group->marks_list, g_list) { list_add(&entry->free_g_list, &free_list); list_del_init(&entry->g_list); fsnotify_get_mark(entry); @@ -243,12 +243,12 @@ void fsnotify_clear_marks_by_group(struct fsnotify_group *group) */ void fsnotify_clear_marks_by_inode(struct inode *inode) { - struct fsnotify_mark_entry *entry, *lentry; + struct fsnotify_mark *entry, *lentry; struct hlist_node *pos, *n; LIST_HEAD(free_list); spin_lock(&inode->i_lock); - hlist_for_each_entry_safe(entry, pos, n, &inode->i_fsnotify_mark_entries, i.i_list) { + hlist_for_each_entry_safe(entry, pos, n, &inode->i_fsnotify_marks, i.i_list) { list_add(&entry->i.free_i_list, &free_list); hlist_del_init(&entry->i.i_list); fsnotify_get_mark(entry); @@ -265,15 +265,15 @@ void fsnotify_clear_marks_by_inode(struct inode *inode) * given a group and inode, find the mark associated with that combination. * if found take a reference to that mark and return it, else return NULL */ -struct fsnotify_mark_entry *fsnotify_find_mark_entry(struct fsnotify_group *group, - struct inode *inode) +struct fsnotify_mark *fsnotify_find_mark_entry(struct fsnotify_group *group, + struct inode *inode) { - struct fsnotify_mark_entry *entry; + struct fsnotify_mark *entry; struct hlist_node *pos; assert_spin_locked(&inode->i_lock); - hlist_for_each_entry(entry, pos, &inode->i_fsnotify_mark_entries, i.i_list) { + hlist_for_each_entry(entry, pos, &inode->i_fsnotify_marks, i.i_list) { if (entry->group == group) { fsnotify_get_mark(entry); return entry; @@ -282,7 +282,7 @@ struct fsnotify_mark_entry *fsnotify_find_mark_entry(struct fsnotify_group *grou return NULL; } -void fsnotify_duplicate_mark(struct fsnotify_mark_entry *new, struct fsnotify_mark_entry *old) +void fsnotify_duplicate_mark(struct fsnotify_mark *new, struct fsnotify_mark *old) { assert_spin_locked(&old->lock); new->i.inode = old->i.inode; @@ -294,8 +294,8 @@ void fsnotify_duplicate_mark(struct fsnotify_mark_entry *new, struct fsnotify_ma /* * Nothing fancy, just initialize lists and locks and counters. */ -void fsnotify_init_mark(struct fsnotify_mark_entry *entry, - void (*free_mark)(struct fsnotify_mark_entry *entry)) +void fsnotify_init_mark(struct fsnotify_mark *entry, + void (*free_mark)(struct fsnotify_mark *entry)) { spin_lock_init(&entry->lock); atomic_set(&entry->refcnt, 1); @@ -311,11 +311,11 @@ void fsnotify_init_mark(struct fsnotify_mark_entry *entry, * These marks may be used for the fsnotify backend to determine which * event types should be delivered to which group and for which inodes. */ -int fsnotify_add_mark(struct fsnotify_mark_entry *entry, +int fsnotify_add_mark(struct fsnotify_mark *entry, struct fsnotify_group *group, struct inode *inode, int allow_dups) { - struct fsnotify_mark_entry *lentry = NULL; + struct fsnotify_mark *lentry = NULL; int ret = 0; inode = igrab(inode); @@ -354,8 +354,8 @@ int fsnotify_add_mark(struct fsnotify_mark_entry *entry, entry->group = group; entry->i.inode = inode; - hlist_add_head(&entry->i.i_list, &inode->i_fsnotify_mark_entries); - list_add(&entry->g_list, &group->mark_entries); + hlist_add_head(&entry->i.i_list, &inode->i_fsnotify_marks); + list_add(&entry->g_list, &group->marks_list); fsnotify_get_mark(entry); /* for i_list and g_list */ diff --git a/fs/notify/inotify/inotify.h b/fs/notify/inotify/inotify.h index f234f3a4c8ca..07be6df2428f 100644 --- a/fs/notify/inotify/inotify.h +++ b/fs/notify/inotify/inotify.h @@ -10,12 +10,12 @@ struct inotify_event_private_data { }; struct inotify_inode_mark_entry { - /* fsnotify_mark_entry MUST be the first thing */ - struct fsnotify_mark_entry fsn_entry; + /* fsnotify_mark MUST be the first thing */ + struct fsnotify_mark fsn_entry; int wd; }; -extern void inotify_ignored_and_remove_idr(struct fsnotify_mark_entry *entry, +extern void inotify_ignored_and_remove_idr(struct fsnotify_mark *entry, struct fsnotify_group *group); extern void inotify_free_event_priv(struct fsnotify_event_private_data *event_priv); diff --git a/fs/notify/inotify/inotify_fsnotify.c b/fs/notify/inotify/inotify_fsnotify.c index 3edb51cfcfbe..f33a9bd32e5d 100644 --- a/fs/notify/inotify/inotify_fsnotify.c +++ b/fs/notify/inotify/inotify_fsnotify.c @@ -88,7 +88,7 @@ static int inotify_merge(struct list_head *list, struct fsnotify_event *event) static int inotify_handle_event(struct fsnotify_group *group, struct fsnotify_event *event) { - struct fsnotify_mark_entry *entry; + struct fsnotify_mark *entry; struct inotify_inode_mark_entry *ientry; struct inode *to_tell; struct inotify_event_private_data *event_priv; @@ -135,7 +135,7 @@ static int inotify_handle_event(struct fsnotify_group *group, struct fsnotify_ev return ret; } -static void inotify_freeing_mark(struct fsnotify_mark_entry *entry, struct fsnotify_group *group) +static void inotify_freeing_mark(struct fsnotify_mark *entry, struct fsnotify_group *group) { inotify_ignored_and_remove_idr(entry, group); } @@ -144,7 +144,7 @@ static bool inotify_should_send_event(struct fsnotify_group *group, struct inode struct vfsmount *mnt, __u32 mask, void *data, int data_type) { - struct fsnotify_mark_entry *entry; + struct fsnotify_mark *entry; bool send; spin_lock(&inode->i_lock); @@ -171,7 +171,7 @@ static bool inotify_should_send_event(struct fsnotify_group *group, struct inode */ static int idr_callback(int id, void *p, void *data) { - struct fsnotify_mark_entry *entry; + struct fsnotify_mark *entry; struct inotify_inode_mark_entry *ientry; static bool warned = false; diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index 4b1587f9df3b..7be5dcf07ac7 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -386,7 +386,7 @@ static struct inotify_inode_mark_entry *inotify_idr_find_locked(struct fsnotify_ ientry = idr_find(idr, wd); if (ientry) { - struct fsnotify_mark_entry *fsn_entry = &ientry->fsn_entry; + struct fsnotify_mark *fsn_entry = &ientry->fsn_entry; fsnotify_get_mark(fsn_entry); /* One ref for being in the idr, one ref we just took */ @@ -499,7 +499,7 @@ out: /* * Send IN_IGNORED for this wd, remove this wd from the idr. */ -void inotify_ignored_and_remove_idr(struct fsnotify_mark_entry *entry, +void inotify_ignored_and_remove_idr(struct fsnotify_mark *entry, struct fsnotify_group *group) { struct inotify_inode_mark_entry *ientry; @@ -541,7 +541,7 @@ skip_send_ignore: } /* ding dong the mark is dead */ -static void inotify_free_mark(struct fsnotify_mark_entry *entry) +static void inotify_free_mark(struct fsnotify_mark *entry) { struct inotify_inode_mark_entry *ientry; @@ -554,7 +554,7 @@ static int inotify_update_existing_watch(struct fsnotify_group *group, struct inode *inode, u32 arg) { - struct fsnotify_mark_entry *entry; + struct fsnotify_mark *entry; struct inotify_inode_mark_entry *ientry; __u32 old_mask, new_mask; __u32 mask; diff --git a/include/linux/fs.h b/include/linux/fs.h index e5598d2f99b9..85fe89c43487 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -768,7 +768,7 @@ struct inode { #ifdef CONFIG_FSNOTIFY __u32 i_fsnotify_mask; /* all events this inode cares about */ - struct hlist_head i_fsnotify_mark_entries; /* fsnotify mark entries */ + struct hlist_head i_fsnotify_marks; #endif unsigned long i_state; diff --git a/include/linux/fsnotify.h b/include/linux/fsnotify.h index 06d296d85ebf..62e93a9dd115 100644 --- a/include/linux/fsnotify.h +++ b/include/linux/fsnotify.h @@ -19,10 +19,10 @@ * fsnotify_d_instantiate - instantiate a dentry for inode * Called with dcache_lock held. */ -static inline void fsnotify_d_instantiate(struct dentry *entry, - struct inode *inode) +static inline void fsnotify_d_instantiate(struct dentry *dentry, + struct inode *inode) { - __fsnotify_d_instantiate(entry, inode); + __fsnotify_d_instantiate(dentry, inode); } /* Notify this dentry's parent about a child's events. */ @@ -35,16 +35,16 @@ static inline void fsnotify_parent(struct path *path, struct dentry *dentry, __u } /* - * fsnotify_d_move - entry has been moved - * Called with dcache_lock and entry->d_lock held. + * fsnotify_d_move - dentry has been moved + * Called with dcache_lock and dentry->d_lock held. */ -static inline void fsnotify_d_move(struct dentry *entry) +static inline void fsnotify_d_move(struct dentry *dentry) { /* - * On move we need to update entry->d_flags to indicate if the new parent - * cares about events from this entry. + * On move we need to update dentry->d_flags to indicate if the new parent + * cares about events from this dentry. */ - __fsnotify_update_dcache_flags(entry); + __fsnotify_update_dcache_flags(dentry); } /* diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index 7a6ba755acc3..59c072e8fddd 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -62,7 +62,7 @@ struct fsnotify_group; struct fsnotify_event; -struct fsnotify_mark_entry; +struct fsnotify_mark; struct fsnotify_event_private_data; /* @@ -83,7 +83,7 @@ struct fsnotify_ops { int data_type); int (*handle_event)(struct fsnotify_group *group, struct fsnotify_event *event); void (*free_group_priv)(struct fsnotify_group *group); - void (*freeing_mark)(struct fsnotify_mark_entry *entry, struct fsnotify_group *group); + void (*freeing_mark)(struct fsnotify_mark *entry, struct fsnotify_group *group); void (*free_event_priv)(struct fsnotify_event_private_data *priv); }; @@ -133,12 +133,12 @@ struct fsnotify_group { unsigned int q_len; /* events on the queue */ unsigned int max_events; /* maximum events allowed on the list */ - /* stores all fastapth entries assoc with this group so they can be cleaned on unregister */ - spinlock_t mark_lock; /* protect mark_entries list */ + /* stores all fastpath marks assoc with this group so they can be cleaned on unregister */ + spinlock_t mark_lock; /* protect marks_list */ atomic_t num_marks; /* 1 for each mark entry and 1 for not being * past the point of no return when freeing * a group */ - struct list_head mark_entries; /* all inode mark entries for this group */ + struct list_head marks_list; /* all inode marks for this group */ /* prevents double list_del of group_list. protected by global fsnotify_grp_mutex */ bool on_inode_group_list; @@ -226,20 +226,20 @@ struct fsnotify_event { }; /* - * Inode specific fields in an fsnotify_mark_entry + * Inode specific fields in an fsnotify_mark */ struct fsnotify_inode_mark { struct inode *inode; /* inode this entry is associated with */ - struct hlist_node i_list; /* list of mark_entries by inode->i_fsnotify_mark_entries */ + struct hlist_node i_list; /* list of marks by inode->i_fsnotify_marks */ struct list_head free_i_list; /* tmp list used when freeing this mark */ }; /* - * Mount point specific fields in an fsnotify_mark_entry + * Mount point specific fields in an fsnotify_mark */ struct fsnotify_vfsmount_mark { struct vfsmount *mnt; /* inode this entry is associated with */ - struct hlist_node m_list; /* list of mark_entries by inode->i_fsnotify_mark_entries */ + struct hlist_node m_list; /* list of marks by inode->i_fsnotify_marks */ struct list_head free_m_list; /* tmp list used when freeing this mark */ }; @@ -253,13 +253,13 @@ struct fsnotify_vfsmount_mark { * (such as dnotify) will flush these when the open fd is closed and not at * inode eviction or modification. */ -struct fsnotify_mark_entry { +struct fsnotify_mark { __u32 mask; /* mask this mark entry is for */ /* we hold ref for each i_list and g_list. also one ref for each 'thing' * in kernel that found and may be using this mark. */ atomic_t refcnt; /* active things looking at this mark */ struct fsnotify_group *group; /* group this mark entry is for */ - struct list_head g_list; /* list of mark_entries by group->i_fsnotify_mark_entries */ + struct list_head g_list; /* list of marks by group->i_fsnotify_marks */ spinlock_t lock; /* protect group and inode */ union { struct fsnotify_inode_mark i; @@ -269,7 +269,7 @@ struct fsnotify_mark_entry { #define FSNOTIFY_MARK_FLAG_INODE 0x01 #define FSNOTIFY_MARK_FLAG_VFSMOUNT 0x02 unsigned int flags; /* vfsmount or inode mark? */ - void (*free_mark)(struct fsnotify_mark_entry *entry); /* called on final put+free */ + void (*free_mark)(struct fsnotify_mark *entry); /* called on final put+free */ }; #ifdef CONFIG_FSNOTIFY @@ -361,19 +361,19 @@ extern struct fsnotify_event *fsnotify_remove_notify_event(struct fsnotify_group /* run all marks associated with an inode and update inode->i_fsnotify_mask */ extern void fsnotify_recalc_inode_mask(struct inode *inode); -extern void fsnotify_init_mark(struct fsnotify_mark_entry *entry, void (*free_mark)(struct fsnotify_mark_entry *entry)); +extern void fsnotify_init_mark(struct fsnotify_mark *entry, void (*free_mark)(struct fsnotify_mark *entry)); /* find (and take a reference) to a mark associated with group and inode */ -extern struct fsnotify_mark_entry *fsnotify_find_mark_entry(struct fsnotify_group *group, struct inode *inode); +extern struct fsnotify_mark *fsnotify_find_mark_entry(struct fsnotify_group *group, struct inode *inode); /* copy the values from old into new */ -extern void fsnotify_duplicate_mark(struct fsnotify_mark_entry *new, struct fsnotify_mark_entry *old); +extern void fsnotify_duplicate_mark(struct fsnotify_mark *new, struct fsnotify_mark *old); /* attach the mark to both the group and the inode */ -extern int fsnotify_add_mark(struct fsnotify_mark_entry *entry, struct fsnotify_group *group, struct inode *inode, int allow_dups); +extern int fsnotify_add_mark(struct fsnotify_mark *entry, struct fsnotify_group *group, struct inode *inode, int allow_dups); /* given a mark, flag it to be freed when all references are dropped */ -extern void fsnotify_destroy_mark_by_entry(struct fsnotify_mark_entry *entry); +extern void fsnotify_destroy_mark_by_entry(struct fsnotify_mark *entry); /* run all the marks in a group, and flag them to be freed */ extern void fsnotify_clear_marks_by_group(struct fsnotify_group *group); -extern void fsnotify_get_mark(struct fsnotify_mark_entry *entry); -extern void fsnotify_put_mark(struct fsnotify_mark_entry *entry); +extern void fsnotify_get_mark(struct fsnotify_mark *entry); +extern void fsnotify_put_mark(struct fsnotify_mark *entry); extern void fsnotify_unmount_inodes(struct list_head *list); /* put here because inotify does some weird stuff when destroying watches */ diff --git a/kernel/audit_tree.c b/kernel/audit_tree.c index c21b05d25224..f16f909fbbc1 100644 --- a/kernel/audit_tree.c +++ b/kernel/audit_tree.c @@ -22,7 +22,7 @@ struct audit_tree { struct audit_chunk { struct list_head hash; - struct fsnotify_mark_entry mark; + struct fsnotify_mark mark; struct list_head trees; /* with root here */ int dead; int count; @@ -134,7 +134,7 @@ static void __put_chunk(struct rcu_head *rcu) audit_put_chunk(chunk); } -static void audit_tree_destroy_watch(struct fsnotify_mark_entry *entry) +static void audit_tree_destroy_watch(struct fsnotify_mark *entry) { struct audit_chunk *chunk = container_of(entry, struct audit_chunk, mark); call_rcu(&chunk->head, __put_chunk); @@ -176,7 +176,7 @@ static inline struct list_head *chunk_hash(const struct inode *inode) /* hash_lock & entry->lock is held by caller */ static void insert_hash(struct audit_chunk *chunk) { - struct fsnotify_mark_entry *entry = &chunk->mark; + struct fsnotify_mark *entry = &chunk->mark; struct list_head *list; if (!entry->i.inode) @@ -222,7 +222,7 @@ static struct audit_chunk *find_chunk(struct node *p) static void untag_chunk(struct node *p) { struct audit_chunk *chunk = find_chunk(p); - struct fsnotify_mark_entry *entry = &chunk->mark; + struct fsnotify_mark *entry = &chunk->mark; struct audit_chunk *new; struct audit_tree *owner; int size = chunk->count - 1; @@ -316,7 +316,7 @@ out: static int create_chunk(struct inode *inode, struct audit_tree *tree) { - struct fsnotify_mark_entry *entry; + struct fsnotify_mark *entry; struct audit_chunk *chunk = alloc_chunk(1); if (!chunk) return -ENOMEM; @@ -354,7 +354,7 @@ static int create_chunk(struct inode *inode, struct audit_tree *tree) /* the first tagged inode becomes root of tree */ static int tag_chunk(struct inode *inode, struct audit_tree *tree) { - struct fsnotify_mark_entry *old_entry, *chunk_entry; + struct fsnotify_mark *old_entry, *chunk_entry; struct audit_tree *owner; struct audit_chunk *chunk, *old; struct node *p; @@ -911,7 +911,7 @@ static int audit_tree_handle_event(struct fsnotify_group *group, struct fsnotify return -EOPNOTSUPP; } -static void audit_tree_freeing_mark(struct fsnotify_mark_entry *entry, struct fsnotify_group *group) +static void audit_tree_freeing_mark(struct fsnotify_mark *entry, struct fsnotify_group *group) { struct audit_chunk *chunk = container_of(entry, struct audit_chunk, mark); diff --git a/kernel/audit_watch.c b/kernel/audit_watch.c index 6304ee5d7642..d8cb55a5c059 100644 --- a/kernel/audit_watch.c +++ b/kernel/audit_watch.c @@ -56,7 +56,7 @@ struct audit_watch { struct audit_parent { struct list_head watches; /* anchor for audit_watch->wlist */ - struct fsnotify_mark_entry mark; /* fsnotify mark on the inode */ + struct fsnotify_mark mark; /* fsnotify mark on the inode */ }; /* fsnotify handle. */ @@ -72,7 +72,7 @@ static void audit_free_parent(struct audit_parent *parent) kfree(parent); } -static void audit_watch_free_mark(struct fsnotify_mark_entry *entry) +static void audit_watch_free_mark(struct fsnotify_mark *entry) { struct audit_parent *parent; @@ -99,7 +99,7 @@ static void audit_put_parent(struct audit_parent *parent) static inline struct audit_parent *audit_find_parent(struct inode *inode) { struct audit_parent *parent = NULL; - struct fsnotify_mark_entry *entry; + struct fsnotify_mark *entry; spin_lock(&inode->i_lock); entry = fsnotify_find_mark_entry(audit_watch_group, inode); @@ -517,7 +517,7 @@ static bool audit_watch_should_send_event(struct fsnotify_group *group, struct i struct vfsmount *mnt, __u32 mask, void *data, int data_type) { - struct fsnotify_mark_entry *entry; + struct fsnotify_mark *entry; bool send; spin_lock(&inode->i_lock); diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 853185f7ba7e..b87a63beb66c 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -1724,7 +1724,7 @@ static inline void handle_one(const struct inode *inode) struct audit_tree_refs *p; struct audit_chunk *chunk; int count; - if (likely(hlist_empty(&inode->i_fsnotify_mark_entries))) + if (likely(hlist_empty(&inode->i_fsnotify_marks))) return; context = current->audit_context; p = context->trees; @@ -1767,7 +1767,7 @@ retry: seq = read_seqbegin(&rename_lock); for(;;) { struct inode *inode = d->d_inode; - if (inode && unlikely(!hlist_empty(&inode->i_fsnotify_mark_entries))) { + if (inode && unlikely(!hlist_empty(&inode->i_fsnotify_marks))) { struct audit_chunk *chunk; chunk = audit_tree_lookup(inode); if (chunk) { -- cgit v1.2.3 From d07754412f9cdc2f4a99318d5ee81ace6715ea99 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:24 -0500 Subject: fsnotify: rename fsnotify_find_mark_entry to fsnotify_find_mark the _entry portion of fsnotify functions is useless. Drop it. Signed-off-by: Eric Paris --- fs/notify/dnotify/dnotify.c | 14 +++++++------- fs/notify/inode_mark.c | 14 +++++++------- fs/notify/inotify/inotify_fsnotify.c | 4 ++-- fs/notify/inotify/inotify_user.c | 6 +++--- include/linux/fsnotify_backend.h | 4 ++-- kernel/audit_tree.c | 12 ++++++------ kernel/audit_watch.c | 8 ++++---- 7 files changed, 31 insertions(+), 31 deletions(-) (limited to 'fs') diff --git a/fs/notify/dnotify/dnotify.c b/fs/notify/dnotify/dnotify.c index e6edae60894d..b202bc590c61 100644 --- a/fs/notify/dnotify/dnotify.c +++ b/fs/notify/dnotify/dnotify.c @@ -96,7 +96,7 @@ static int dnotify_handle_event(struct fsnotify_group *group, to_tell = event->to_tell; spin_lock(&to_tell->i_lock); - entry = fsnotify_find_mark_entry(group, to_tell); + entry = fsnotify_find_mark(group, to_tell); spin_unlock(&to_tell->i_lock); /* unlikely since we alreay passed dnotify_should_send_event() */ @@ -148,7 +148,7 @@ static bool dnotify_should_send_event(struct fsnotify_group *group, return false; spin_lock(&inode->i_lock); - entry = fsnotify_find_mark_entry(group, inode); + entry = fsnotify_find_mark(group, inode); spin_unlock(&inode->i_lock); /* no mark means no dnotify watch */ @@ -158,7 +158,7 @@ static bool dnotify_should_send_event(struct fsnotify_group *group, mask = (mask & ~FS_EVENT_ON_CHILD); send = (mask & entry->mask); - fsnotify_put_mark(entry); /* matches fsnotify_find_mark_entry */ + fsnotify_put_mark(entry); /* matches fsnotify_find_mark */ return send; } @@ -202,7 +202,7 @@ void dnotify_flush(struct file *filp, fl_owner_t id) return; spin_lock(&inode->i_lock); - entry = fsnotify_find_mark_entry(dnotify_group, inode); + entry = fsnotify_find_mark(dnotify_group, inode); spin_unlock(&inode->i_lock); if (!entry) return; @@ -226,7 +226,7 @@ void dnotify_flush(struct file *filp, fl_owner_t id) /* nothing else could have found us thanks to the dnotify_mark_mutex */ if (dnentry->dn == NULL) - fsnotify_destroy_mark_by_entry(entry); + fsnotify_destroy_mark(entry); fsnotify_recalc_group_mask(dnotify_group); @@ -357,7 +357,7 @@ int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg) /* add the new_entry or find an old one. */ spin_lock(&inode->i_lock); - entry = fsnotify_find_mark_entry(dnotify_group, inode); + entry = fsnotify_find_mark(dnotify_group, inode); spin_unlock(&inode->i_lock); if (entry) { dnentry = container_of(entry, struct dnotify_mark_entry, fsn_entry); @@ -414,7 +414,7 @@ out: spin_unlock(&entry->lock); if (destroy) - fsnotify_destroy_mark_by_entry(entry); + fsnotify_destroy_mark(entry); fsnotify_recalc_group_mask(dnotify_group); diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c index 7e69f6b08d4e..01c42632eb2a 100644 --- a/fs/notify/inode_mark.c +++ b/fs/notify/inode_mark.c @@ -56,7 +56,7 @@ * - The inode is unlinked for the last time. (fsnotify_inode_remove) * - The inode is being evicted from cache. (fsnotify_inode_delete) * - The fs the inode is on is unmounted. (fsnotify_inode_delete/fsnotify_unmount_inodes) - * - Something explicitly requests that it be removed. (fsnotify_destroy_mark_by_entry) + * - Something explicitly requests that it be removed. (fsnotify_destroy_mark) * - The fsnotify_group associated with the mark is going away and all such marks * need to be cleaned up. (fsnotify_clear_marks_by_group) * @@ -140,7 +140,7 @@ void fsnotify_recalc_inode_mask(struct inode *inode) * The caller had better be holding a reference to this mark so we don't actually * do the final put under the entry->lock */ -void fsnotify_destroy_mark_by_entry(struct fsnotify_mark *entry) +void fsnotify_destroy_mark(struct fsnotify_mark *entry) { struct fsnotify_group *group; struct inode *inode; @@ -233,7 +233,7 @@ void fsnotify_clear_marks_by_group(struct fsnotify_group *group) spin_unlock(&group->mark_lock); list_for_each_entry_safe(entry, lentry, &free_list, free_g_list) { - fsnotify_destroy_mark_by_entry(entry); + fsnotify_destroy_mark(entry); fsnotify_put_mark(entry); } } @@ -256,7 +256,7 @@ void fsnotify_clear_marks_by_inode(struct inode *inode) spin_unlock(&inode->i_lock); list_for_each_entry_safe(entry, lentry, &free_list, i.free_i_list) { - fsnotify_destroy_mark_by_entry(entry); + fsnotify_destroy_mark(entry); fsnotify_put_mark(entry); } } @@ -265,8 +265,8 @@ void fsnotify_clear_marks_by_inode(struct inode *inode) * given a group and inode, find the mark associated with that combination. * if found take a reference to that mark and return it, else return NULL */ -struct fsnotify_mark *fsnotify_find_mark_entry(struct fsnotify_group *group, - struct inode *inode) +struct fsnotify_mark *fsnotify_find_mark(struct fsnotify_group *group, + struct inode *inode) { struct fsnotify_mark *entry; struct hlist_node *pos; @@ -349,7 +349,7 @@ int fsnotify_add_mark(struct fsnotify_mark *entry, spin_lock(&inode->i_lock); if (!allow_dups) - lentry = fsnotify_find_mark_entry(group, inode); + lentry = fsnotify_find_mark(group, inode); if (!lentry) { entry->group = group; entry->i.inode = inode; diff --git a/fs/notify/inotify/inotify_fsnotify.c b/fs/notify/inotify/inotify_fsnotify.c index f33a9bd32e5d..f8a2a6eda133 100644 --- a/fs/notify/inotify/inotify_fsnotify.c +++ b/fs/notify/inotify/inotify_fsnotify.c @@ -98,7 +98,7 @@ static int inotify_handle_event(struct fsnotify_group *group, struct fsnotify_ev to_tell = event->to_tell; spin_lock(&to_tell->i_lock); - entry = fsnotify_find_mark_entry(group, to_tell); + entry = fsnotify_find_mark(group, to_tell); spin_unlock(&to_tell->i_lock); /* race with watch removal? We already passes should_send */ if (unlikely(!entry)) @@ -148,7 +148,7 @@ static bool inotify_should_send_event(struct fsnotify_group *group, struct inode bool send; spin_lock(&inode->i_lock); - entry = fsnotify_find_mark_entry(group, inode); + entry = fsnotify_find_mark(group, inode); spin_unlock(&inode->i_lock); if (!entry) return false; diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index 7be5dcf07ac7..118085c9d2d9 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -567,7 +567,7 @@ static int inotify_update_existing_watch(struct fsnotify_group *group, return -EINVAL; spin_lock(&inode->i_lock); - entry = fsnotify_find_mark_entry(group, inode); + entry = fsnotify_find_mark(group, inode); spin_unlock(&inode->i_lock); if (!entry) return -ENOENT; @@ -607,7 +607,7 @@ static int inotify_update_existing_watch(struct fsnotify_group *group, /* return the wd */ ret = ientry->wd; - /* match the get from fsnotify_find_mark_entry() */ + /* match the get from fsnotify_find_mark() */ fsnotify_put_mark(entry); return ret; @@ -823,7 +823,7 @@ SYSCALL_DEFINE2(inotify_rm_watch, int, fd, __s32, wd) ret = 0; - fsnotify_destroy_mark_by_entry(&ientry->fsn_entry); + fsnotify_destroy_mark(&ientry->fsn_entry); /* match ref taken by inotify_idr_find */ fsnotify_put_mark(&ientry->fsn_entry); diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index 59c072e8fddd..83b6bfeb2d66 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -363,13 +363,13 @@ extern struct fsnotify_event *fsnotify_remove_notify_event(struct fsnotify_group extern void fsnotify_recalc_inode_mask(struct inode *inode); extern void fsnotify_init_mark(struct fsnotify_mark *entry, void (*free_mark)(struct fsnotify_mark *entry)); /* find (and take a reference) to a mark associated with group and inode */ -extern struct fsnotify_mark *fsnotify_find_mark_entry(struct fsnotify_group *group, struct inode *inode); +extern struct fsnotify_mark *fsnotify_find_mark(struct fsnotify_group *group, struct inode *inode); /* copy the values from old into new */ extern void fsnotify_duplicate_mark(struct fsnotify_mark *new, struct fsnotify_mark *old); /* attach the mark to both the group and the inode */ extern int fsnotify_add_mark(struct fsnotify_mark *entry, struct fsnotify_group *group, struct inode *inode, int allow_dups); /* given a mark, flag it to be freed when all references are dropped */ -extern void fsnotify_destroy_mark_by_entry(struct fsnotify_mark *entry); +extern void fsnotify_destroy_mark(struct fsnotify_mark *entry); /* run all the marks in a group, and flag them to be freed */ extern void fsnotify_clear_marks_by_group(struct fsnotify_group *group); extern void fsnotify_get_mark(struct fsnotify_mark *entry); diff --git a/kernel/audit_tree.c b/kernel/audit_tree.c index f16f909fbbc1..b20fb055d712 100644 --- a/kernel/audit_tree.c +++ b/kernel/audit_tree.c @@ -250,7 +250,7 @@ static void untag_chunk(struct node *p) list_del_rcu(&chunk->hash); spin_unlock(&hash_lock); spin_unlock(&entry->lock); - fsnotify_destroy_mark_by_entry(entry); + fsnotify_destroy_mark(entry); fsnotify_put_mark(entry); goto out; } @@ -293,7 +293,7 @@ static void untag_chunk(struct node *p) owner->root = new; spin_unlock(&hash_lock); spin_unlock(&entry->lock); - fsnotify_destroy_mark_by_entry(entry); + fsnotify_destroy_mark(entry); fsnotify_put_mark(entry); goto out; @@ -333,7 +333,7 @@ static int create_chunk(struct inode *inode, struct audit_tree *tree) spin_unlock(&hash_lock); chunk->dead = 1; spin_unlock(&entry->lock); - fsnotify_destroy_mark_by_entry(entry); + fsnotify_destroy_mark(entry); fsnotify_put_mark(entry); return 0; } @@ -361,7 +361,7 @@ static int tag_chunk(struct inode *inode, struct audit_tree *tree) int n; spin_lock(&inode->i_lock); - old_entry = fsnotify_find_mark_entry(audit_tree_group, inode); + old_entry = fsnotify_find_mark(audit_tree_group, inode); spin_unlock(&inode->i_lock); if (!old_entry) return create_chunk(inode, tree); @@ -415,7 +415,7 @@ static int tag_chunk(struct inode *inode, struct audit_tree *tree) spin_unlock(&chunk_entry->lock); spin_unlock(&old_entry->lock); - fsnotify_destroy_mark_by_entry(chunk_entry); + fsnotify_destroy_mark(chunk_entry); fsnotify_put_mark(chunk_entry); fsnotify_put_mark(old_entry); @@ -446,7 +446,7 @@ static int tag_chunk(struct inode *inode, struct audit_tree *tree) spin_unlock(&hash_lock); spin_unlock(&chunk_entry->lock); spin_unlock(&old_entry->lock); - fsnotify_destroy_mark_by_entry(old_entry); + fsnotify_destroy_mark(old_entry); fsnotify_put_mark(old_entry); /* pair to fsnotify_find mark_entry */ fsnotify_put_mark(old_entry); /* and kill it */ return 0; diff --git a/kernel/audit_watch.c b/kernel/audit_watch.c index d8cb55a5c059..24ecbebf4354 100644 --- a/kernel/audit_watch.c +++ b/kernel/audit_watch.c @@ -102,7 +102,7 @@ static inline struct audit_parent *audit_find_parent(struct inode *inode) struct fsnotify_mark *entry; spin_lock(&inode->i_lock); - entry = fsnotify_find_mark_entry(audit_watch_group, inode); + entry = fsnotify_find_mark(audit_watch_group, inode); spin_unlock(&inode->i_lock); if (entry) @@ -354,7 +354,7 @@ static void audit_remove_parent_watches(struct audit_parent *parent) } mutex_unlock(&audit_filter_mutex); - fsnotify_destroy_mark_by_entry(&parent->mark); + fsnotify_destroy_mark(&parent->mark); fsnotify_recalc_group_mask(audit_watch_group); @@ -504,7 +504,7 @@ void audit_remove_watch_rule(struct audit_krule *krule) if (list_empty(&parent->watches)) { audit_get_parent(parent); - fsnotify_destroy_mark_by_entry(&parent->mark); + fsnotify_destroy_mark(&parent->mark); audit_put_parent(parent); } } @@ -521,7 +521,7 @@ static bool audit_watch_should_send_event(struct fsnotify_group *group, struct i bool send; spin_lock(&inode->i_lock); - entry = fsnotify_find_mark_entry(group, inode); + entry = fsnotify_find_mark(group, inode); spin_unlock(&inode->i_lock); if (!entry) return false; -- cgit v1.2.3 From 841bdc10f573aa010dd5818d35a5690b7d9f73ce Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:24 -0500 Subject: fsnotify: rename mark_entry to just mark previously I used mark_entry when talking about marks on inodes. The _entry is pretty useless. Just use "mark" instead. Signed-off-by: Eric Paris --- fs/notify/group.c | 6 +- fs/notify/inode_mark.c | 148 +++++++++++++++++++-------------------- include/linux/fsnotify_backend.h | 26 +++---- 3 files changed, 90 insertions(+), 90 deletions(-) (limited to 'fs') diff --git a/fs/notify/group.c b/fs/notify/group.c index b70e7d21dfde..9e9eb406afdd 100644 --- a/fs/notify/group.c +++ b/fs/notify/group.c @@ -74,11 +74,11 @@ void fsnotify_recalc_group_mask(struct fsnotify_group *group) { __u32 mask = 0; __u32 old_mask = group->mask; - struct fsnotify_mark *entry; + struct fsnotify_mark *mark; spin_lock(&group->mark_lock); - list_for_each_entry(entry, &group->marks_list, g_list) - mask |= entry->mask; + list_for_each_entry(mark, &group->marks_list, g_list) + mask |= mark->mask; spin_unlock(&group->mark_lock); group->mask = mask; diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c index 01c42632eb2a..27c1b43ad739 100644 --- a/fs/notify/inode_mark.c +++ b/fs/notify/inode_mark.c @@ -30,21 +30,21 @@ * There are 3 spinlocks involved with fsnotify inode marks and they MUST * be taken in order as follows: * - * entry->lock + * mark->lock * group->mark_lock * inode->i_lock * - * entry->lock protects 2 things, entry->group and entry->inode. You must hold + * mark->lock protects 2 things, mark->group and mark->inode. You must hold * that lock to dereference either of these things (they could be NULL even with * the lock) * * group->mark_lock protects the marks_list anchored inside a given group - * and each entry is hooked via the g_list. It also sorta protects the + * and each mark is hooked via the g_list. It also sorta protects the * free_g_list, which when used is anchored by a private list on the stack of the * task which held the group->mark_lock. * * inode->i_lock protects the i_fsnotify_marks list anchored inside a - * given inode and each entry is hooked via the i_list. (and sorta the + * given inode and each mark is hooked via the i_list. (and sorta the * free_i_list) * * @@ -95,15 +95,15 @@ #include #include "fsnotify.h" -void fsnotify_get_mark(struct fsnotify_mark *entry) +void fsnotify_get_mark(struct fsnotify_mark *mark) { - atomic_inc(&entry->refcnt); + atomic_inc(&mark->refcnt); } -void fsnotify_put_mark(struct fsnotify_mark *entry) +void fsnotify_put_mark(struct fsnotify_mark *mark) { - if (atomic_dec_and_test(&entry->refcnt)) - entry->free_mark(entry); + if (atomic_dec_and_test(&mark->refcnt)) + mark->free_mark(mark); } /* @@ -111,14 +111,14 @@ void fsnotify_put_mark(struct fsnotify_mark *entry) */ static void fsnotify_recalc_inode_mask_locked(struct inode *inode) { - struct fsnotify_mark *entry; + struct fsnotify_mark *mark; struct hlist_node *pos; __u32 new_mask = 0; assert_spin_locked(&inode->i_lock); - hlist_for_each_entry(entry, pos, &inode->i_fsnotify_marks, i.i_list) - new_mask |= entry->mask; + hlist_for_each_entry(mark, pos, &inode->i_fsnotify_marks, i.i_list) + new_mask |= mark->mask; inode->i_fsnotify_mask = new_mask; } @@ -138,40 +138,40 @@ void fsnotify_recalc_inode_mask(struct inode *inode) /* * Any time a mark is getting freed we end up here. * The caller had better be holding a reference to this mark so we don't actually - * do the final put under the entry->lock + * do the final put under the mark->lock */ -void fsnotify_destroy_mark(struct fsnotify_mark *entry) +void fsnotify_destroy_mark(struct fsnotify_mark *mark) { struct fsnotify_group *group; struct inode *inode; - spin_lock(&entry->lock); + spin_lock(&mark->lock); - group = entry->group; - inode = entry->i.inode; + group = mark->group; + inode = mark->i.inode; BUG_ON(group && !inode); BUG_ON(!group && inode); /* if !group something else already marked this to die */ if (!group) { - spin_unlock(&entry->lock); + spin_unlock(&mark->lock); return; } /* 1 from caller and 1 for being on i_list/g_list */ - BUG_ON(atomic_read(&entry->refcnt) < 2); + BUG_ON(atomic_read(&mark->refcnt) < 2); spin_lock(&group->mark_lock); spin_lock(&inode->i_lock); - hlist_del_init(&entry->i.i_list); - entry->i.inode = NULL; + hlist_del_init(&mark->i.i_list); + mark->i.inode = NULL; - list_del_init(&entry->g_list); - entry->group = NULL; + list_del_init(&mark->g_list); + mark->group = NULL; - fsnotify_put_mark(entry); /* for i_list and g_list */ + fsnotify_put_mark(mark); /* for i_list and g_list */ /* * this mark is now off the inode->i_fsnotify_marks list and we @@ -182,21 +182,21 @@ void fsnotify_destroy_mark(struct fsnotify_mark *entry) spin_unlock(&inode->i_lock); spin_unlock(&group->mark_lock); - spin_unlock(&entry->lock); + spin_unlock(&mark->lock); /* * Some groups like to know that marks are being freed. This is a - * callback to the group function to let it know that this entry + * callback to the group function to let it know that this mark * is being freed. */ if (group->ops->freeing_mark) - group->ops->freeing_mark(entry, group); + group->ops->freeing_mark(mark, group); /* * __fsnotify_update_child_dentry_flags(inode); * * I really want to call that, but we can't, we have no idea if the inode - * still exists the second we drop the entry->lock. + * still exists the second we drop the mark->lock. * * The next time an event arrive to this inode from one of it's children * __fsnotify_parent will see that the inode doesn't care about it's @@ -221,20 +221,20 @@ void fsnotify_destroy_mark(struct fsnotify_mark *entry) */ void fsnotify_clear_marks_by_group(struct fsnotify_group *group) { - struct fsnotify_mark *lentry, *entry; + struct fsnotify_mark *lmark, *mark; LIST_HEAD(free_list); spin_lock(&group->mark_lock); - list_for_each_entry_safe(entry, lentry, &group->marks_list, g_list) { - list_add(&entry->free_g_list, &free_list); - list_del_init(&entry->g_list); - fsnotify_get_mark(entry); + list_for_each_entry_safe(mark, lmark, &group->marks_list, g_list) { + list_add(&mark->free_g_list, &free_list); + list_del_init(&mark->g_list); + fsnotify_get_mark(mark); } spin_unlock(&group->mark_lock); - list_for_each_entry_safe(entry, lentry, &free_list, free_g_list) { - fsnotify_destroy_mark(entry); - fsnotify_put_mark(entry); + list_for_each_entry_safe(mark, lmark, &free_list, free_g_list) { + fsnotify_destroy_mark(mark); + fsnotify_put_mark(mark); } } @@ -243,21 +243,21 @@ void fsnotify_clear_marks_by_group(struct fsnotify_group *group) */ void fsnotify_clear_marks_by_inode(struct inode *inode) { - struct fsnotify_mark *entry, *lentry; + struct fsnotify_mark *mark, *lmark; struct hlist_node *pos, *n; LIST_HEAD(free_list); spin_lock(&inode->i_lock); - hlist_for_each_entry_safe(entry, pos, n, &inode->i_fsnotify_marks, i.i_list) { - list_add(&entry->i.free_i_list, &free_list); - hlist_del_init(&entry->i.i_list); - fsnotify_get_mark(entry); + hlist_for_each_entry_safe(mark, pos, n, &inode->i_fsnotify_marks, i.i_list) { + list_add(&mark->i.free_i_list, &free_list); + hlist_del_init(&mark->i.i_list); + fsnotify_get_mark(mark); } spin_unlock(&inode->i_lock); - list_for_each_entry_safe(entry, lentry, &free_list, i.free_i_list) { - fsnotify_destroy_mark(entry); - fsnotify_put_mark(entry); + list_for_each_entry_safe(mark, lmark, &free_list, i.free_i_list) { + fsnotify_destroy_mark(mark); + fsnotify_put_mark(mark); } } @@ -268,15 +268,15 @@ void fsnotify_clear_marks_by_inode(struct inode *inode) struct fsnotify_mark *fsnotify_find_mark(struct fsnotify_group *group, struct inode *inode) { - struct fsnotify_mark *entry; + struct fsnotify_mark *mark; struct hlist_node *pos; assert_spin_locked(&inode->i_lock); - hlist_for_each_entry(entry, pos, &inode->i_fsnotify_marks, i.i_list) { - if (entry->group == group) { - fsnotify_get_mark(entry); - return entry; + hlist_for_each_entry(mark, pos, &inode->i_fsnotify_marks, i.i_list) { + if (mark->group == group) { + fsnotify_get_mark(mark); + return mark; } } return NULL; @@ -294,35 +294,35 @@ void fsnotify_duplicate_mark(struct fsnotify_mark *new, struct fsnotify_mark *ol /* * Nothing fancy, just initialize lists and locks and counters. */ -void fsnotify_init_mark(struct fsnotify_mark *entry, - void (*free_mark)(struct fsnotify_mark *entry)) +void fsnotify_init_mark(struct fsnotify_mark *mark, + void (*free_mark)(struct fsnotify_mark *mark)) { - spin_lock_init(&entry->lock); - atomic_set(&entry->refcnt, 1); - INIT_HLIST_NODE(&entry->i.i_list); - entry->group = NULL; - entry->mask = 0; - entry->i.inode = NULL; - entry->free_mark = free_mark; + spin_lock_init(&mark->lock); + atomic_set(&mark->refcnt, 1); + INIT_HLIST_NODE(&mark->i.i_list); + mark->group = NULL; + mark->mask = 0; + mark->i.inode = NULL; + mark->free_mark = free_mark; } /* - * Attach an initialized mark entry to a given group and inode. + * Attach an initialized mark mark to a given group and inode. * These marks may be used for the fsnotify backend to determine which * event types should be delivered to which group and for which inodes. */ -int fsnotify_add_mark(struct fsnotify_mark *entry, +int fsnotify_add_mark(struct fsnotify_mark *mark, struct fsnotify_group *group, struct inode *inode, int allow_dups) { - struct fsnotify_mark *lentry = NULL; + struct fsnotify_mark *lmark = NULL; int ret = 0; inode = igrab(inode); if (unlikely(!inode)) return -EINVAL; - entry->flags = FSNOTIFY_MARK_FLAG_INODE; + mark->flags = FSNOTIFY_MARK_FLAG_INODE; /* * if this group isn't being testing for inode type events we need @@ -340,24 +340,24 @@ int fsnotify_add_mark(struct fsnotify_mark *entry, /* * LOCKING ORDER!!!! - * entry->lock + * mark->lock * group->mark_lock * inode->i_lock */ - spin_lock(&entry->lock); + spin_lock(&mark->lock); spin_lock(&group->mark_lock); spin_lock(&inode->i_lock); if (!allow_dups) - lentry = fsnotify_find_mark(group, inode); - if (!lentry) { - entry->group = group; - entry->i.inode = inode; + lmark = fsnotify_find_mark(group, inode); + if (!lmark) { + mark->group = group; + mark->i.inode = inode; - hlist_add_head(&entry->i.i_list, &inode->i_fsnotify_marks); - list_add(&entry->g_list, &group->marks_list); + hlist_add_head(&mark->i.i_list, &inode->i_fsnotify_marks); + list_add(&mark->g_list, &group->marks_list); - fsnotify_get_mark(entry); /* for i_list and g_list */ + fsnotify_get_mark(mark); /* for i_list and g_list */ atomic_inc(&group->num_marks); @@ -366,12 +366,12 @@ int fsnotify_add_mark(struct fsnotify_mark *entry, spin_unlock(&inode->i_lock); spin_unlock(&group->mark_lock); - spin_unlock(&entry->lock); + spin_unlock(&mark->lock); - if (lentry) { + if (lmark) { ret = -EEXIST; iput(inode); - fsnotify_put_mark(lentry); + fsnotify_put_mark(lmark); } else { __fsnotify_update_child_dentry_flags(inode); } diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index 83b6bfeb2d66..ff654c1932f2 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -83,7 +83,7 @@ struct fsnotify_ops { int data_type); int (*handle_event)(struct fsnotify_group *group, struct fsnotify_event *event); void (*free_group_priv)(struct fsnotify_group *group); - void (*freeing_mark)(struct fsnotify_mark *entry, struct fsnotify_group *group); + void (*freeing_mark)(struct fsnotify_mark *mark, struct fsnotify_group *group); void (*free_event_priv)(struct fsnotify_event_private_data *priv); }; @@ -135,7 +135,7 @@ struct fsnotify_group { /* stores all fastpath marks assoc with this group so they can be cleaned on unregister */ spinlock_t mark_lock; /* protect marks_list */ - atomic_t num_marks; /* 1 for each mark entry and 1 for not being + atomic_t num_marks; /* 1 for each mark and 1 for not being * past the point of no return when freeing * a group */ struct list_head marks_list; /* all inode marks for this group */ @@ -229,7 +229,7 @@ struct fsnotify_event { * Inode specific fields in an fsnotify_mark */ struct fsnotify_inode_mark { - struct inode *inode; /* inode this entry is associated with */ + struct inode *inode; /* inode this mark is associated with */ struct hlist_node i_list; /* list of marks by inode->i_fsnotify_marks */ struct list_head free_i_list; /* tmp list used when freeing this mark */ }; @@ -238,13 +238,13 @@ struct fsnotify_inode_mark { * Mount point specific fields in an fsnotify_mark */ struct fsnotify_vfsmount_mark { - struct vfsmount *mnt; /* inode this entry is associated with */ + struct vfsmount *mnt; /* vfsmount this mark is associated with */ struct hlist_node m_list; /* list of marks by inode->i_fsnotify_marks */ struct list_head free_m_list; /* tmp list used when freeing this mark */ }; /* - * a mark is simply an entry attached to an in core inode which allows an + * a mark is simply an object attached to an in core inode which allows an * fsnotify listener to indicate they are either no longer interested in events * of a type matching mask or only interested in those events. * @@ -254,11 +254,11 @@ struct fsnotify_vfsmount_mark { * inode eviction or modification. */ struct fsnotify_mark { - __u32 mask; /* mask this mark entry is for */ + __u32 mask; /* mask this mark is for */ /* we hold ref for each i_list and g_list. also one ref for each 'thing' * in kernel that found and may be using this mark. */ atomic_t refcnt; /* active things looking at this mark */ - struct fsnotify_group *group; /* group this mark entry is for */ + struct fsnotify_group *group; /* group this mark is for */ struct list_head g_list; /* list of marks by group->i_fsnotify_marks */ spinlock_t lock; /* protect group and inode */ union { @@ -269,7 +269,7 @@ struct fsnotify_mark { #define FSNOTIFY_MARK_FLAG_INODE 0x01 #define FSNOTIFY_MARK_FLAG_VFSMOUNT 0x02 unsigned int flags; /* vfsmount or inode mark? */ - void (*free_mark)(struct fsnotify_mark *entry); /* called on final put+free */ + void (*free_mark)(struct fsnotify_mark *mark); /* called on final put+free */ }; #ifdef CONFIG_FSNOTIFY @@ -361,19 +361,19 @@ extern struct fsnotify_event *fsnotify_remove_notify_event(struct fsnotify_group /* run all marks associated with an inode and update inode->i_fsnotify_mask */ extern void fsnotify_recalc_inode_mask(struct inode *inode); -extern void fsnotify_init_mark(struct fsnotify_mark *entry, void (*free_mark)(struct fsnotify_mark *entry)); +extern void fsnotify_init_mark(struct fsnotify_mark *mark, void (*free_mark)(struct fsnotify_mark *mark)); /* find (and take a reference) to a mark associated with group and inode */ extern struct fsnotify_mark *fsnotify_find_mark(struct fsnotify_group *group, struct inode *inode); /* copy the values from old into new */ extern void fsnotify_duplicate_mark(struct fsnotify_mark *new, struct fsnotify_mark *old); /* attach the mark to both the group and the inode */ -extern int fsnotify_add_mark(struct fsnotify_mark *entry, struct fsnotify_group *group, struct inode *inode, int allow_dups); +extern int fsnotify_add_mark(struct fsnotify_mark *mark, struct fsnotify_group *group, struct inode *inode, int allow_dups); /* given a mark, flag it to be freed when all references are dropped */ -extern void fsnotify_destroy_mark(struct fsnotify_mark *entry); +extern void fsnotify_destroy_mark(struct fsnotify_mark *mark); /* run all the marks in a group, and flag them to be freed */ extern void fsnotify_clear_marks_by_group(struct fsnotify_group *group); -extern void fsnotify_get_mark(struct fsnotify_mark *entry); -extern void fsnotify_put_mark(struct fsnotify_mark *entry); +extern void fsnotify_get_mark(struct fsnotify_mark *mark); +extern void fsnotify_put_mark(struct fsnotify_mark *mark); extern void fsnotify_unmount_inodes(struct list_head *list); /* put here because inotify does some weird stuff when destroying watches */ -- cgit v1.2.3 From 000285deb99a5e0636fdd3c6a2483a5d039ee2c2 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:24 -0500 Subject: inotify: rename mark_entry to just mark rename anything in inotify that deals with mark_entry to just be mark. It makes a lot more sense. Signed-off-by: Eric Paris --- fs/notify/inotify/inotify.h | 7 +- fs/notify/inotify/inotify_fsnotify.c | 48 ++++----- fs/notify/inotify/inotify_user.c | 192 +++++++++++++++++------------------ 3 files changed, 123 insertions(+), 124 deletions(-) (limited to 'fs') diff --git a/fs/notify/inotify/inotify.h b/fs/notify/inotify/inotify.h index 07be6df2428f..b6642e4de4bf 100644 --- a/fs/notify/inotify/inotify.h +++ b/fs/notify/inotify/inotify.h @@ -9,13 +9,12 @@ struct inotify_event_private_data { int wd; }; -struct inotify_inode_mark_entry { - /* fsnotify_mark MUST be the first thing */ - struct fsnotify_mark fsn_entry; +struct inotify_inode_mark { + struct fsnotify_mark fsn_mark; int wd; }; -extern void inotify_ignored_and_remove_idr(struct fsnotify_mark *entry, +extern void inotify_ignored_and_remove_idr(struct fsnotify_mark *fsn_mark, struct fsnotify_group *group); extern void inotify_free_event_priv(struct fsnotify_event_private_data *event_priv); diff --git a/fs/notify/inotify/inotify_fsnotify.c b/fs/notify/inotify/inotify_fsnotify.c index f8a2a6eda133..12dc72be992e 100644 --- a/fs/notify/inotify/inotify_fsnotify.c +++ b/fs/notify/inotify/inotify_fsnotify.c @@ -88,8 +88,8 @@ static int inotify_merge(struct list_head *list, struct fsnotify_event *event) static int inotify_handle_event(struct fsnotify_group *group, struct fsnotify_event *event) { - struct fsnotify_mark *entry; - struct inotify_inode_mark_entry *ientry; + struct fsnotify_mark *fsn_mark; + struct inotify_inode_mark *i_mark; struct inode *to_tell; struct inotify_event_private_data *event_priv; struct fsnotify_event_private_data *fsn_event_priv; @@ -98,14 +98,14 @@ static int inotify_handle_event(struct fsnotify_group *group, struct fsnotify_ev to_tell = event->to_tell; spin_lock(&to_tell->i_lock); - entry = fsnotify_find_mark(group, to_tell); + fsn_mark = fsnotify_find_mark(group, to_tell); spin_unlock(&to_tell->i_lock); /* race with watch removal? We already passes should_send */ - if (unlikely(!entry)) + if (unlikely(!fsn_mark)) return 0; - ientry = container_of(entry, struct inotify_inode_mark_entry, - fsn_entry); - wd = ientry->wd; + i_mark = container_of(fsn_mark, struct inotify_inode_mark, + fsn_mark); + wd = i_mark->wd; event_priv = kmem_cache_alloc(event_priv_cachep, GFP_KERNEL); if (unlikely(!event_priv)) @@ -127,37 +127,37 @@ static int inotify_handle_event(struct fsnotify_group *group, struct fsnotify_ev } /* - * If we hold the entry until after the event is on the queue + * If we hold the fsn_mark until after the event is on the queue * IN_IGNORED won't be able to pass this event in the queue */ - fsnotify_put_mark(entry); + fsnotify_put_mark(fsn_mark); return ret; } -static void inotify_freeing_mark(struct fsnotify_mark *entry, struct fsnotify_group *group) +static void inotify_freeing_mark(struct fsnotify_mark *fsn_mark, struct fsnotify_group *group) { - inotify_ignored_and_remove_idr(entry, group); + inotify_ignored_and_remove_idr(fsn_mark, group); } static bool inotify_should_send_event(struct fsnotify_group *group, struct inode *inode, struct vfsmount *mnt, __u32 mask, void *data, int data_type) { - struct fsnotify_mark *entry; + struct fsnotify_mark *fsn_mark; bool send; spin_lock(&inode->i_lock); - entry = fsnotify_find_mark(group, inode); + fsn_mark = fsnotify_find_mark(group, inode); spin_unlock(&inode->i_lock); - if (!entry) + if (!fsn_mark) return false; mask = (mask & ~FS_EVENT_ON_CHILD); - send = (entry->mask & mask); + send = (fsn_mark->mask & mask); /* find took a reference */ - fsnotify_put_mark(entry); + fsnotify_put_mark(fsn_mark); return send; } @@ -171,18 +171,18 @@ static bool inotify_should_send_event(struct fsnotify_group *group, struct inode */ static int idr_callback(int id, void *p, void *data) { - struct fsnotify_mark *entry; - struct inotify_inode_mark_entry *ientry; + struct fsnotify_mark *fsn_mark; + struct inotify_inode_mark *i_mark; static bool warned = false; if (warned) return 0; warned = true; - entry = p; - ientry = container_of(entry, struct inotify_inode_mark_entry, fsn_entry); + fsn_mark = p; + i_mark = container_of(fsn_mark, struct inotify_inode_mark, fsn_mark); - WARN(1, "inotify closing but id=%d for entry=%p in group=%p still in " + WARN(1, "inotify closing but id=%d for fsn_mark=%p in group=%p still in " "idr. Probably leaking memory\n", id, p, data); /* @@ -191,9 +191,9 @@ static int idr_callback(int id, void *p, void *data) * out why we got here and the panic is no worse than the original * BUG() that was here. */ - if (entry) - printk(KERN_WARNING "entry->group=%p inode=%p wd=%d\n", - entry->group, entry->i.inode, ientry->wd); + if (fsn_mark) + printk(KERN_WARNING "fsn_mark->group=%p inode=%p wd=%d\n", + fsn_mark->group, fsn_mark->i.inode, i_mark->wd); return 0; } diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index 118085c9d2d9..80d102acb86b 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -353,7 +353,7 @@ static int inotify_find_inode(const char __user *dirname, struct path *path, uns static int inotify_add_to_idr(struct idr *idr, spinlock_t *idr_lock, int *last_wd, - struct inotify_inode_mark_entry *ientry) + struct inotify_inode_mark *i_mark) { int ret; @@ -362,12 +362,12 @@ static int inotify_add_to_idr(struct idr *idr, spinlock_t *idr_lock, return -ENOMEM; spin_lock(idr_lock); - ret = idr_get_new_above(idr, ientry, *last_wd + 1, - &ientry->wd); + ret = idr_get_new_above(idr, i_mark, *last_wd + 1, + &i_mark->wd); /* we added the mark to the idr, take a reference */ if (!ret) { - fsnotify_get_mark(&ientry->fsn_entry); - *last_wd = ientry->wd; + *last_wd = i_mark->wd; + fsnotify_get_mark(&i_mark->fsn_mark); } spin_unlock(idr_lock); } while (ret == -EAGAIN); @@ -375,53 +375,53 @@ static int inotify_add_to_idr(struct idr *idr, spinlock_t *idr_lock, return ret; } -static struct inotify_inode_mark_entry *inotify_idr_find_locked(struct fsnotify_group *group, +static struct inotify_inode_mark *inotify_idr_find_locked(struct fsnotify_group *group, int wd) { struct idr *idr = &group->inotify_data.idr; spinlock_t *idr_lock = &group->inotify_data.idr_lock; - struct inotify_inode_mark_entry *ientry; + struct inotify_inode_mark *i_mark; assert_spin_locked(idr_lock); - ientry = idr_find(idr, wd); - if (ientry) { - struct fsnotify_mark *fsn_entry = &ientry->fsn_entry; + i_mark = idr_find(idr, wd); + if (i_mark) { + struct fsnotify_mark *fsn_mark = &i_mark->fsn_mark; - fsnotify_get_mark(fsn_entry); + fsnotify_get_mark(fsn_mark); /* One ref for being in the idr, one ref we just took */ - BUG_ON(atomic_read(&fsn_entry->refcnt) < 2); + BUG_ON(atomic_read(&fsn_mark->refcnt) < 2); } - return ientry; + return i_mark; } -static struct inotify_inode_mark_entry *inotify_idr_find(struct fsnotify_group *group, +static struct inotify_inode_mark *inotify_idr_find(struct fsnotify_group *group, int wd) { - struct inotify_inode_mark_entry *ientry; + struct inotify_inode_mark *i_mark; spinlock_t *idr_lock = &group->inotify_data.idr_lock; spin_lock(idr_lock); - ientry = inotify_idr_find_locked(group, wd); + i_mark = inotify_idr_find_locked(group, wd); spin_unlock(idr_lock); - return ientry; + return i_mark; } static void do_inotify_remove_from_idr(struct fsnotify_group *group, - struct inotify_inode_mark_entry *ientry) + struct inotify_inode_mark *i_mark) { struct idr *idr = &group->inotify_data.idr; spinlock_t *idr_lock = &group->inotify_data.idr_lock; - int wd = ientry->wd; + int wd = i_mark->wd; assert_spin_locked(idr_lock); idr_remove(idr, wd); /* removed from the idr, drop that ref */ - fsnotify_put_mark(&ientry->fsn_entry); + fsnotify_put_mark(&i_mark->fsn_mark); } /* @@ -429,48 +429,48 @@ static void do_inotify_remove_from_idr(struct fsnotify_group *group, * on the mark because it was in the idr. */ static void inotify_remove_from_idr(struct fsnotify_group *group, - struct inotify_inode_mark_entry *ientry) + struct inotify_inode_mark *i_mark) { spinlock_t *idr_lock = &group->inotify_data.idr_lock; - struct inotify_inode_mark_entry *found_ientry = NULL; + struct inotify_inode_mark *found_i_mark = NULL; int wd; spin_lock(idr_lock); - wd = ientry->wd; + wd = i_mark->wd; /* - * does this ientry think it is in the idr? we shouldn't get called + * does this i_mark think it is in the idr? we shouldn't get called * if it wasn't.... */ if (wd == -1) { - WARN_ONCE(1, "%s: ientry=%p ientry->wd=%d ientry->group=%p" - " ientry->inode=%p\n", __func__, ientry, ientry->wd, - ientry->fsn_entry.group, ientry->fsn_entry.i.inode); + WARN_ONCE(1, "%s: i_mark=%p i_mark->wd=%d i_mark->group=%p" + " i_mark->inode=%p\n", __func__, i_mark, i_mark->wd, + i_mark->fsn_mark.group, i_mark->fsn_mark.i.inode); goto out; } /* Lets look in the idr to see if we find it */ - found_ientry = inotify_idr_find_locked(group, wd); - if (unlikely(!found_ientry)) { - WARN_ONCE(1, "%s: ientry=%p ientry->wd=%d ientry->group=%p" - " ientry->inode=%p\n", __func__, ientry, ientry->wd, - ientry->fsn_entry.group, ientry->fsn_entry.i.inode); + found_i_mark = inotify_idr_find_locked(group, wd); + if (unlikely(!found_i_mark)) { + WARN_ONCE(1, "%s: i_mark=%p i_mark->wd=%d i_mark->group=%p" + " i_mark->inode=%p\n", __func__, i_mark, i_mark->wd, + i_mark->fsn_mark.group, i_mark->fsn_mark.i.inode); goto out; } /* - * We found an entry in the idr at the right wd, but it's - * not the entry we were told to remove. eparis seriously + * We found an mark in the idr at the right wd, but it's + * not the mark we were told to remove. eparis seriously * fucked up somewhere. */ - if (unlikely(found_ientry != ientry)) { - WARN_ONCE(1, "%s: ientry=%p ientry->wd=%d ientry->group=%p " - "entry->inode=%p found_ientry=%p found_ientry->wd=%d " - "found_ientry->group=%p found_ientry->inode=%p\n", - __func__, ientry, ientry->wd, ientry->fsn_entry.group, - ientry->fsn_entry.i.inode, found_ientry, found_ientry->wd, - found_ientry->fsn_entry.group, - found_ientry->fsn_entry.i.inode); + if (unlikely(found_i_mark != i_mark)) { + WARN_ONCE(1, "%s: i_mark=%p i_mark->wd=%d i_mark->group=%p " + "mark->inode=%p found_i_mark=%p found_i_mark->wd=%d " + "found_i_mark->group=%p found_i_mark->inode=%p\n", + __func__, i_mark, i_mark->wd, i_mark->fsn_mark.group, + i_mark->fsn_mark.i.inode, found_i_mark, found_i_mark->wd, + found_i_mark->fsn_mark.group, + found_i_mark->fsn_mark.i.inode); goto out; } @@ -479,30 +479,30 @@ static void inotify_remove_from_idr(struct fsnotify_group *group, * one ref held by the caller trying to kill us * one ref grabbed by inotify_idr_find */ - if (unlikely(atomic_read(&ientry->fsn_entry.refcnt) < 3)) { - printk(KERN_ERR "%s: ientry=%p ientry->wd=%d ientry->group=%p" - " ientry->inode=%p\n", __func__, ientry, ientry->wd, - ientry->fsn_entry.group, ientry->fsn_entry.i.inode); + if (unlikely(atomic_read(&i_mark->fsn_mark.refcnt) < 3)) { + printk(KERN_ERR "%s: i_mark=%p i_mark->wd=%d i_mark->group=%p" + " i_mark->inode=%p\n", __func__, i_mark, i_mark->wd, + i_mark->fsn_mark.group, i_mark->fsn_mark.i.inode); /* we can't really recover with bad ref cnting.. */ BUG(); } - do_inotify_remove_from_idr(group, ientry); + do_inotify_remove_from_idr(group, i_mark); out: /* match the ref taken by inotify_idr_find_locked() */ - if (found_ientry) - fsnotify_put_mark(&found_ientry->fsn_entry); - ientry->wd = -1; + if (found_i_mark) + fsnotify_put_mark(&found_i_mark->fsn_mark); + i_mark->wd = -1; spin_unlock(idr_lock); } /* * Send IN_IGNORED for this wd, remove this wd from the idr. */ -void inotify_ignored_and_remove_idr(struct fsnotify_mark *entry, +void inotify_ignored_and_remove_idr(struct fsnotify_mark *fsn_mark, struct fsnotify_group *group) { - struct inotify_inode_mark_entry *ientry; + struct inotify_inode_mark *i_mark; struct fsnotify_event *ignored_event; struct inotify_event_private_data *event_priv; struct fsnotify_event_private_data *fsn_event_priv; @@ -514,7 +514,7 @@ void inotify_ignored_and_remove_idr(struct fsnotify_mark *entry, if (!ignored_event) return; - ientry = container_of(entry, struct inotify_inode_mark_entry, fsn_entry); + i_mark = container_of(fsn_mark, struct inotify_inode_mark, fsn_mark); event_priv = kmem_cache_alloc(event_priv_cachep, GFP_NOFS); if (unlikely(!event_priv)) @@ -523,7 +523,7 @@ void inotify_ignored_and_remove_idr(struct fsnotify_mark *entry, fsn_event_priv = &event_priv->fsnotify_event_priv_data; fsn_event_priv->group = group; - event_priv->wd = ientry->wd; + event_priv->wd = i_mark->wd; ret = fsnotify_add_notify_event(group, ignored_event, fsn_event_priv, NULL); if (ret) @@ -534,28 +534,28 @@ skip_send_ignore: /* matches the reference taken when the event was created */ fsnotify_put_event(ignored_event); - /* remove this entry from the idr */ - inotify_remove_from_idr(group, ientry); + /* remove this mark from the idr */ + inotify_remove_from_idr(group, i_mark); atomic_dec(&group->inotify_data.user->inotify_watches); } /* ding dong the mark is dead */ -static void inotify_free_mark(struct fsnotify_mark *entry) +static void inotify_free_mark(struct fsnotify_mark *fsn_mark) { - struct inotify_inode_mark_entry *ientry; + struct inotify_inode_mark *i_mark; - ientry = container_of(entry, struct inotify_inode_mark_entry, fsn_entry); + i_mark = container_of(fsn_mark, struct inotify_inode_mark, fsn_mark); - kmem_cache_free(inotify_inode_mark_cachep, ientry); + kmem_cache_free(inotify_inode_mark_cachep, i_mark); } static int inotify_update_existing_watch(struct fsnotify_group *group, struct inode *inode, u32 arg) { - struct fsnotify_mark *entry; - struct inotify_inode_mark_entry *ientry; + struct fsnotify_mark *fsn_mark; + struct inotify_inode_mark *i_mark; __u32 old_mask, new_mask; __u32 mask; int add = (arg & IN_MASK_ADD); @@ -567,35 +567,35 @@ static int inotify_update_existing_watch(struct fsnotify_group *group, return -EINVAL; spin_lock(&inode->i_lock); - entry = fsnotify_find_mark(group, inode); + fsn_mark = fsnotify_find_mark(group, inode); spin_unlock(&inode->i_lock); - if (!entry) + if (!fsn_mark) return -ENOENT; - ientry = container_of(entry, struct inotify_inode_mark_entry, fsn_entry); + i_mark = container_of(fsn_mark, struct inotify_inode_mark, fsn_mark); - spin_lock(&entry->lock); + spin_lock(&fsn_mark->lock); - old_mask = entry->mask; + old_mask = fsn_mark->mask; if (add) { - entry->mask |= mask; - new_mask = entry->mask; + fsn_mark->mask |= mask; + new_mask = fsn_mark->mask; } else { - entry->mask = mask; - new_mask = entry->mask; + fsn_mark->mask = mask; + new_mask = fsn_mark->mask; } - spin_unlock(&entry->lock); + spin_unlock(&fsn_mark->lock); if (old_mask != new_mask) { /* more bits in old than in new? */ int dropped = (old_mask & ~new_mask); - /* more bits in this entry than the inode's mask? */ + /* more bits in this fsn_mark than the inode's mask? */ int do_inode = (new_mask & ~inode->i_fsnotify_mask); - /* more bits in this entry than the group? */ + /* more bits in this fsn_mark than the group? */ int do_group = (new_mask & ~group->mask); - /* update the inode with this new entry */ + /* update the inode with this new fsn_mark */ if (dropped || do_inode) fsnotify_recalc_inode_mask(inode); @@ -605,10 +605,10 @@ static int inotify_update_existing_watch(struct fsnotify_group *group, } /* return the wd */ - ret = ientry->wd; + ret = i_mark->wd; /* match the get from fsnotify_find_mark() */ - fsnotify_put_mark(entry); + fsnotify_put_mark(fsn_mark); return ret; } @@ -617,7 +617,7 @@ static int inotify_new_watch(struct fsnotify_group *group, struct inode *inode, u32 arg) { - struct inotify_inode_mark_entry *tmp_ientry; + struct inotify_inode_mark *tmp_i_mark; __u32 mask; int ret; struct idr *idr = &group->inotify_data.idr; @@ -628,44 +628,44 @@ static int inotify_new_watch(struct fsnotify_group *group, if (unlikely(!mask)) return -EINVAL; - tmp_ientry = kmem_cache_alloc(inotify_inode_mark_cachep, GFP_KERNEL); - if (unlikely(!tmp_ientry)) + tmp_i_mark = kmem_cache_alloc(inotify_inode_mark_cachep, GFP_KERNEL); + if (unlikely(!tmp_i_mark)) return -ENOMEM; - fsnotify_init_mark(&tmp_ientry->fsn_entry, inotify_free_mark); - tmp_ientry->fsn_entry.mask = mask; - tmp_ientry->wd = -1; + fsnotify_init_mark(&tmp_i_mark->fsn_mark, inotify_free_mark); + tmp_i_mark->fsn_mark.mask = mask; + tmp_i_mark->wd = -1; ret = -ENOSPC; if (atomic_read(&group->inotify_data.user->inotify_watches) >= inotify_max_user_watches) goto out_err; ret = inotify_add_to_idr(idr, idr_lock, &group->inotify_data.last_wd, - tmp_ientry); + tmp_i_mark); if (ret) goto out_err; /* we are on the idr, now get on the inode */ - ret = fsnotify_add_mark(&tmp_ientry->fsn_entry, group, inode, 0); + ret = fsnotify_add_mark(&tmp_i_mark->fsn_mark, group, inode, 0); if (ret) { /* we failed to get on the inode, get off the idr */ - inotify_remove_from_idr(group, tmp_ientry); + inotify_remove_from_idr(group, tmp_i_mark); goto out_err; } /* increment the number of watches the user has */ atomic_inc(&group->inotify_data.user->inotify_watches); - /* return the watch descriptor for this new entry */ - ret = tmp_ientry->wd; + /* return the watch descriptor for this new mark */ + ret = tmp_i_mark->wd; /* if this mark added a new event update the group mask */ if (mask & ~group->mask) fsnotify_recalc_group_mask(group); out_err: - /* match the ref from fsnotify_init_markentry() */ - fsnotify_put_mark(&tmp_ientry->fsn_entry); + /* match the ref from fsnotify_init_mark() */ + fsnotify_put_mark(&tmp_i_mark->fsn_mark); return ret; } @@ -801,7 +801,7 @@ fput_and_out: SYSCALL_DEFINE2(inotify_rm_watch, int, fd, __s32, wd) { struct fsnotify_group *group; - struct inotify_inode_mark_entry *ientry; + struct inotify_inode_mark *i_mark; struct file *filp; int ret = 0, fput_needed; @@ -817,16 +817,16 @@ SYSCALL_DEFINE2(inotify_rm_watch, int, fd, __s32, wd) group = filp->private_data; ret = -EINVAL; - ientry = inotify_idr_find(group, wd); - if (unlikely(!ientry)) + i_mark = inotify_idr_find(group, wd); + if (unlikely(!i_mark)) goto out; ret = 0; - fsnotify_destroy_mark(&ientry->fsn_entry); + fsnotify_destroy_mark(&i_mark->fsn_mark); /* match ref taken by inotify_idr_find */ - fsnotify_put_mark(&ientry->fsn_entry); + fsnotify_put_mark(&i_mark->fsn_mark); out: fput_light(filp, fput_needed); @@ -840,7 +840,7 @@ out: */ static int __init inotify_user_setup(void) { - inotify_inode_mark_cachep = KMEM_CACHE(inotify_inode_mark_entry, SLAB_PANIC); + inotify_inode_mark_cachep = KMEM_CACHE(inotify_inode_mark, SLAB_PANIC); event_priv_cachep = KMEM_CACHE(inotify_event_private_data, SLAB_PANIC); inotify_max_queued_events = 16384; -- cgit v1.2.3 From ef5e2b785fb3216269e6d0656d38ec286b98dbe5 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:24 -0500 Subject: dnotify: rename mark_entry to mark nomenclature change. Used to call things 'entries' but now we just call them 'marks.' Do those changes for dnotify. Signed-off-by: Eric Paris --- fs/notify/dnotify/dnotify.c | 170 ++++++++++++++++++++++---------------------- 1 file changed, 85 insertions(+), 85 deletions(-) (limited to 'fs') diff --git a/fs/notify/dnotify/dnotify.c b/fs/notify/dnotify/dnotify.c index b202bc590c61..3efb8b9a572d 100644 --- a/fs/notify/dnotify/dnotify.c +++ b/fs/notify/dnotify/dnotify.c @@ -29,7 +29,7 @@ int dir_notify_enable __read_mostly = 1; static struct kmem_cache *dnotify_struct_cache __read_mostly; -static struct kmem_cache *dnotify_mark_entry_cache __read_mostly; +static struct kmem_cache *dnotify_mark_cache __read_mostly; static struct fsnotify_group *dnotify_group __read_mostly; static DEFINE_MUTEX(dnotify_mark_mutex); @@ -38,8 +38,8 @@ static DEFINE_MUTEX(dnotify_mark_mutex); * is being watched by dnotify. If multiple userspace applications are watching * the same directory with dnotify their information is chained in dn */ -struct dnotify_mark_entry { - struct fsnotify_mark fsn_entry; +struct dnotify_mark { + struct fsnotify_mark fsn_mark; struct dnotify_struct *dn; }; @@ -51,27 +51,27 @@ struct dnotify_mark_entry { * it calls the fsnotify function so it can update the set of all events relevant * to this inode. */ -static void dnotify_recalc_inode_mask(struct fsnotify_mark *entry) +static void dnotify_recalc_inode_mask(struct fsnotify_mark *fsn_mark) { __u32 new_mask, old_mask; struct dnotify_struct *dn; - struct dnotify_mark_entry *dnentry = container_of(entry, - struct dnotify_mark_entry, - fsn_entry); + struct dnotify_mark *dn_mark = container_of(fsn_mark, + struct dnotify_mark, + fsn_mark); - assert_spin_locked(&entry->lock); + assert_spin_locked(&fsn_mark->lock); - old_mask = entry->mask; + old_mask = fsn_mark->mask; new_mask = 0; - for (dn = dnentry->dn; dn != NULL; dn = dn->dn_next) + for (dn = dn_mark->dn; dn != NULL; dn = dn->dn_next) new_mask |= (dn->dn_mask & ~FS_DN_MULTISHOT); - entry->mask = new_mask; + fsn_mark->mask = new_mask; if (old_mask == new_mask) return; - if (entry->i.inode) - fsnotify_recalc_inode_mask(entry->i.inode); + if (fsn_mark->i.inode) + fsnotify_recalc_inode_mask(fsn_mark->i.inode); } /* @@ -85,8 +85,8 @@ static void dnotify_recalc_inode_mask(struct fsnotify_mark *entry) static int dnotify_handle_event(struct fsnotify_group *group, struct fsnotify_event *event) { - struct fsnotify_mark *entry = NULL; - struct dnotify_mark_entry *dnentry; + struct fsnotify_mark *fsn_mark = NULL; + struct dnotify_mark *dn_mark; struct inode *to_tell; struct dnotify_struct *dn; struct dnotify_struct **prev; @@ -96,16 +96,16 @@ static int dnotify_handle_event(struct fsnotify_group *group, to_tell = event->to_tell; spin_lock(&to_tell->i_lock); - entry = fsnotify_find_mark(group, to_tell); + fsn_mark = fsnotify_find_mark(group, to_tell); spin_unlock(&to_tell->i_lock); /* unlikely since we alreay passed dnotify_should_send_event() */ - if (unlikely(!entry)) + if (unlikely(!fsn_mark)) return 0; - dnentry = container_of(entry, struct dnotify_mark_entry, fsn_entry); + dn_mark = container_of(fsn_mark, struct dnotify_mark, fsn_mark); - spin_lock(&entry->lock); - prev = &dnentry->dn; + spin_lock(&fsn_mark->lock); + prev = &dn_mark->dn; while ((dn = *prev) != NULL) { if ((dn->dn_mask & test_mask) == 0) { prev = &dn->dn_next; @@ -118,12 +118,12 @@ static int dnotify_handle_event(struct fsnotify_group *group, else { *prev = dn->dn_next; kmem_cache_free(dnotify_struct_cache, dn); - dnotify_recalc_inode_mask(entry); + dnotify_recalc_inode_mask(fsn_mark); } } - spin_unlock(&entry->lock); - fsnotify_put_mark(entry); + spin_unlock(&fsn_mark->lock); + fsnotify_put_mark(fsn_mark); return 0; } @@ -136,7 +136,7 @@ static bool dnotify_should_send_event(struct fsnotify_group *group, struct inode *inode, struct vfsmount *mnt, __u32 mask, void *data, int data_type) { - struct fsnotify_mark *entry; + struct fsnotify_mark *fsn_mark; bool send; /* !dir_notify_enable should never get here, don't waste time checking @@ -148,30 +148,30 @@ static bool dnotify_should_send_event(struct fsnotify_group *group, return false; spin_lock(&inode->i_lock); - entry = fsnotify_find_mark(group, inode); + fsn_mark = fsnotify_find_mark(group, inode); spin_unlock(&inode->i_lock); /* no mark means no dnotify watch */ - if (!entry) + if (!fsn_mark) return false; mask = (mask & ~FS_EVENT_ON_CHILD); - send = (mask & entry->mask); + send = (mask & fsn_mark->mask); - fsnotify_put_mark(entry); /* matches fsnotify_find_mark */ + fsnotify_put_mark(fsn_mark); /* matches fsnotify_find_mark */ return send; } -static void dnotify_free_mark(struct fsnotify_mark *entry) +static void dnotify_free_mark(struct fsnotify_mark *fsn_mark) { - struct dnotify_mark_entry *dnentry = container_of(entry, - struct dnotify_mark_entry, - fsn_entry); + struct dnotify_mark *dn_mark = container_of(fsn_mark, + struct dnotify_mark, + fsn_mark); - BUG_ON(dnentry->dn); + BUG_ON(dn_mark->dn); - kmem_cache_free(dnotify_mark_entry_cache, dnentry); + kmem_cache_free(dnotify_mark_cache, dn_mark); } static struct fsnotify_ops dnotify_fsnotify_ops = { @@ -191,8 +191,8 @@ static struct fsnotify_ops dnotify_fsnotify_ops = { */ void dnotify_flush(struct file *filp, fl_owner_t id) { - struct fsnotify_mark *entry; - struct dnotify_mark_entry *dnentry; + struct fsnotify_mark *fsn_mark; + struct dnotify_mark *dn_mark; struct dnotify_struct *dn; struct dnotify_struct **prev; struct inode *inode; @@ -202,37 +202,37 @@ void dnotify_flush(struct file *filp, fl_owner_t id) return; spin_lock(&inode->i_lock); - entry = fsnotify_find_mark(dnotify_group, inode); + fsn_mark = fsnotify_find_mark(dnotify_group, inode); spin_unlock(&inode->i_lock); - if (!entry) + if (!fsn_mark) return; - dnentry = container_of(entry, struct dnotify_mark_entry, fsn_entry); + dn_mark = container_of(fsn_mark, struct dnotify_mark, fsn_mark); mutex_lock(&dnotify_mark_mutex); - spin_lock(&entry->lock); - prev = &dnentry->dn; + spin_lock(&fsn_mark->lock); + prev = &dn_mark->dn; while ((dn = *prev) != NULL) { if ((dn->dn_owner == id) && (dn->dn_filp == filp)) { *prev = dn->dn_next; kmem_cache_free(dnotify_struct_cache, dn); - dnotify_recalc_inode_mask(entry); + dnotify_recalc_inode_mask(fsn_mark); break; } prev = &dn->dn_next; } - spin_unlock(&entry->lock); + spin_unlock(&fsn_mark->lock); /* nothing else could have found us thanks to the dnotify_mark_mutex */ - if (dnentry->dn == NULL) - fsnotify_destroy_mark(entry); + if (dn_mark->dn == NULL) + fsnotify_destroy_mark(fsn_mark); fsnotify_recalc_group_mask(dnotify_group); mutex_unlock(&dnotify_mark_mutex); - fsnotify_put_mark(entry); + fsnotify_put_mark(fsn_mark); } /* this conversion is done only at watch creation */ @@ -264,12 +264,12 @@ static __u32 convert_arg(unsigned long arg) * onto that mark. This function either attaches the new dnotify_struct onto * that list, or it |= the mask onto an existing dnofiy_struct. */ -static int attach_dn(struct dnotify_struct *dn, struct dnotify_mark_entry *dnentry, +static int attach_dn(struct dnotify_struct *dn, struct dnotify_mark *dn_mark, fl_owner_t id, int fd, struct file *filp, __u32 mask) { struct dnotify_struct *odn; - odn = dnentry->dn; + odn = dn_mark->dn; while (odn != NULL) { /* adding more events to existing dnofiy_struct? */ if ((odn->dn_owner == id) && (odn->dn_filp == filp)) { @@ -284,8 +284,8 @@ static int attach_dn(struct dnotify_struct *dn, struct dnotify_mark_entry *dnent dn->dn_fd = fd; dn->dn_filp = filp; dn->dn_owner = id; - dn->dn_next = dnentry->dn; - dnentry->dn = dn; + dn->dn_next = dn_mark->dn; + dn_mark->dn = dn; return 0; } @@ -297,8 +297,8 @@ static int attach_dn(struct dnotify_struct *dn, struct dnotify_mark_entry *dnent */ int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg) { - struct dnotify_mark_entry *new_dnentry, *dnentry; - struct fsnotify_mark *new_entry, *entry; + struct dnotify_mark *new_dn_mark, *dn_mark; + struct fsnotify_mark *new_fsn_mark, *fsn_mark; struct dnotify_struct *dn; struct inode *inode; fl_owner_t id = current->files; @@ -307,7 +307,7 @@ int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg) __u32 mask; /* we use these to tell if we need to kfree */ - new_entry = NULL; + new_fsn_mark = NULL; dn = NULL; if (!dir_notify_enable) { @@ -337,8 +337,8 @@ int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg) } /* new fsnotify mark, we expect most fcntl calls to add a new mark */ - new_dnentry = kmem_cache_alloc(dnotify_mark_entry_cache, GFP_KERNEL); - if (!new_dnentry) { + new_dn_mark = kmem_cache_alloc(dnotify_mark_cache, GFP_KERNEL); + if (!new_dn_mark) { error = -ENOMEM; goto out_err; } @@ -346,29 +346,29 @@ int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg) /* convert the userspace DN_* "arg" to the internal FS_* defines in fsnotify */ mask = convert_arg(arg); - /* set up the new_entry and new_dnentry */ - new_entry = &new_dnentry->fsn_entry; - fsnotify_init_mark(new_entry, dnotify_free_mark); - new_entry->mask = mask; - new_dnentry->dn = NULL; + /* set up the new_fsn_mark and new_dn_mark */ + new_fsn_mark = &new_dn_mark->fsn_mark; + fsnotify_init_mark(new_fsn_mark, dnotify_free_mark); + new_fsn_mark->mask = mask; + new_dn_mark->dn = NULL; /* this is needed to prevent the fcntl/close race described below */ mutex_lock(&dnotify_mark_mutex); - /* add the new_entry or find an old one. */ + /* add the new_fsn_mark or find an old one. */ spin_lock(&inode->i_lock); - entry = fsnotify_find_mark(dnotify_group, inode); + fsn_mark = fsnotify_find_mark(dnotify_group, inode); spin_unlock(&inode->i_lock); - if (entry) { - dnentry = container_of(entry, struct dnotify_mark_entry, fsn_entry); - spin_lock(&entry->lock); + if (fsn_mark) { + dn_mark = container_of(fsn_mark, struct dnotify_mark, fsn_mark); + spin_lock(&fsn_mark->lock); } else { - fsnotify_add_mark(new_entry, dnotify_group, inode, 0); - spin_lock(&new_entry->lock); - entry = new_entry; - dnentry = new_dnentry; - /* we used new_entry, so don't free it */ - new_entry = NULL; + fsnotify_add_mark(new_fsn_mark, dnotify_group, inode, 0); + spin_lock(&new_fsn_mark->lock); + fsn_mark = new_fsn_mark; + dn_mark = new_dn_mark; + /* we used new_fsn_mark, so don't free it */ + new_fsn_mark = NULL; } rcu_read_lock(); @@ -377,17 +377,17 @@ int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg) /* if (f != filp) means that we lost a race and another task/thread * actually closed the fd we are still playing with before we grabbed - * the dnotify_mark_mutex and entry->lock. Since closing the fd is the + * the dnotify_mark_mutex and fsn_mark->lock. Since closing the fd is the * only time we clean up the marks we need to get our mark off * the list. */ if (f != filp) { /* if we added ourselves, shoot ourselves, it's possible that - * the flush actually did shoot this entry. That's fine too + * the flush actually did shoot this fsn_mark. That's fine too * since multiple calls to destroy_mark is perfectly safe, if - * we found a dnentry already attached to the inode, just sod + * we found a dn_mark already attached to the inode, just sod * off silently as the flush at close time dealt with it. */ - if (dnentry == new_dnentry) + if (dn_mark == new_dn_mark) destroy = 1; goto out; } @@ -395,13 +395,13 @@ int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg) error = __f_setown(filp, task_pid(current), PIDTYPE_PID, 0); if (error) { /* if we added, we must shoot */ - if (dnentry == new_dnentry) + if (dn_mark == new_dn_mark) destroy = 1; goto out; } - error = attach_dn(dn, dnentry, id, fd, filp, mask); - /* !error means that we attached the dn to the dnentry, so don't free it */ + error = attach_dn(dn, dn_mark, id, fd, filp, mask); + /* !error means that we attached the dn to the dn_mark, so don't free it */ if (!error) dn = NULL; /* -EEXIST means that we didn't add this new dn and used an old one. @@ -409,20 +409,20 @@ int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg) else if (error == -EEXIST) error = 0; - dnotify_recalc_inode_mask(entry); + dnotify_recalc_inode_mask(fsn_mark); out: - spin_unlock(&entry->lock); + spin_unlock(&fsn_mark->lock); if (destroy) - fsnotify_destroy_mark(entry); + fsnotify_destroy_mark(fsn_mark); fsnotify_recalc_group_mask(dnotify_group); mutex_unlock(&dnotify_mark_mutex); - fsnotify_put_mark(entry); + fsnotify_put_mark(fsn_mark); out_err: - if (new_entry) - fsnotify_put_mark(new_entry); + if (new_fsn_mark) + fsnotify_put_mark(new_fsn_mark); if (dn) kmem_cache_free(dnotify_struct_cache, dn); return error; @@ -431,7 +431,7 @@ out_err: static int __init dnotify_init(void) { dnotify_struct_cache = KMEM_CACHE(dnotify_struct, SLAB_PANIC); - dnotify_mark_entry_cache = KMEM_CACHE(dnotify_mark_entry, SLAB_PANIC); + dnotify_mark_cache = KMEM_CACHE(dnotify_mark, SLAB_PANIC); dnotify_group = fsnotify_alloc_group(&dnotify_fsnotify_ops); if (IS_ERR(dnotify_group)) -- cgit v1.2.3 From 35566087099c3ff8901d65ee98af56347ee66e5a Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Thu, 17 Dec 2009 21:24:25 -0500 Subject: fsnotify: take inode->i_lock inside fsnotify_find_mark_entry() All callers to fsnotify_find_mark_entry() except one take and release inode->i_lock around the call. Take the lock inside fsnotify_find_mark_entry() instead. Signed-off-by: Andreas Gruenbacher Signed-off-by: Eric Paris --- fs/notify/dnotify/dnotify.c | 12 ------------ fs/notify/inode_mark.c | 26 +++++++++++++++++++------- fs/notify/inotify/inotify_fsnotify.c | 4 ---- fs/notify/inotify/inotify_user.c | 2 -- kernel/audit_tree.c | 2 -- kernel/audit_watch.c | 5 ----- 6 files changed, 19 insertions(+), 32 deletions(-) (limited to 'fs') diff --git a/fs/notify/dnotify/dnotify.c b/fs/notify/dnotify/dnotify.c index 3efb8b9a572d..cac2eb896639 100644 --- a/fs/notify/dnotify/dnotify.c +++ b/fs/notify/dnotify/dnotify.c @@ -95,11 +95,7 @@ static int dnotify_handle_event(struct fsnotify_group *group, to_tell = event->to_tell; - spin_lock(&to_tell->i_lock); fsn_mark = fsnotify_find_mark(group, to_tell); - spin_unlock(&to_tell->i_lock); - - /* unlikely since we alreay passed dnotify_should_send_event() */ if (unlikely(!fsn_mark)) return 0; dn_mark = container_of(fsn_mark, struct dnotify_mark, fsn_mark); @@ -147,11 +143,7 @@ static bool dnotify_should_send_event(struct fsnotify_group *group, if (!S_ISDIR(inode->i_mode)) return false; - spin_lock(&inode->i_lock); fsn_mark = fsnotify_find_mark(group, inode); - spin_unlock(&inode->i_lock); - - /* no mark means no dnotify watch */ if (!fsn_mark) return false; @@ -201,9 +193,7 @@ void dnotify_flush(struct file *filp, fl_owner_t id) if (!S_ISDIR(inode->i_mode)) return; - spin_lock(&inode->i_lock); fsn_mark = fsnotify_find_mark(dnotify_group, inode); - spin_unlock(&inode->i_lock); if (!fsn_mark) return; dn_mark = container_of(fsn_mark, struct dnotify_mark, fsn_mark); @@ -356,9 +346,7 @@ int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg) mutex_lock(&dnotify_mark_mutex); /* add the new_fsn_mark or find an old one. */ - spin_lock(&inode->i_lock); fsn_mark = fsnotify_find_mark(dnotify_group, inode); - spin_unlock(&inode->i_lock); if (fsn_mark) { dn_mark = container_of(fsn_mark, struct dnotify_mark, fsn_mark); spin_lock(&fsn_mark->lock); diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c index 27c1b43ad739..ba6f9833561b 100644 --- a/fs/notify/inode_mark.c +++ b/fs/notify/inode_mark.c @@ -261,12 +261,8 @@ void fsnotify_clear_marks_by_inode(struct inode *inode) } } -/* - * given a group and inode, find the mark associated with that combination. - * if found take a reference to that mark and return it, else return NULL - */ -struct fsnotify_mark *fsnotify_find_mark(struct fsnotify_group *group, - struct inode *inode) +static struct fsnotify_mark *fsnotify_find_mark_locked(struct fsnotify_group *group, + struct inode *inode) { struct fsnotify_mark *mark; struct hlist_node *pos; @@ -282,6 +278,22 @@ struct fsnotify_mark *fsnotify_find_mark(struct fsnotify_group *group, return NULL; } +/* + * given a group and inode, find the mark associated with that combination. + * if found take a reference to that mark and return it, else return NULL + */ +struct fsnotify_mark *fsnotify_find_mark(struct fsnotify_group *group, + struct inode *inode) +{ + struct fsnotify_mark *mark; + + spin_lock(&inode->i_lock); + mark = fsnotify_find_mark_locked(group, inode); + spin_unlock(&inode->i_lock); + + return mark; +} + void fsnotify_duplicate_mark(struct fsnotify_mark *new, struct fsnotify_mark *old) { assert_spin_locked(&old->lock); @@ -349,7 +361,7 @@ int fsnotify_add_mark(struct fsnotify_mark *mark, spin_lock(&inode->i_lock); if (!allow_dups) - lmark = fsnotify_find_mark(group, inode); + lmark = fsnotify_find_mark_locked(group, inode); if (!lmark) { mark->group = group; mark->i.inode = inode; diff --git a/fs/notify/inotify/inotify_fsnotify.c b/fs/notify/inotify/inotify_fsnotify.c index 12dc72be992e..cc8f6bcbb4a3 100644 --- a/fs/notify/inotify/inotify_fsnotify.c +++ b/fs/notify/inotify/inotify_fsnotify.c @@ -97,9 +97,7 @@ static int inotify_handle_event(struct fsnotify_group *group, struct fsnotify_ev to_tell = event->to_tell; - spin_lock(&to_tell->i_lock); fsn_mark = fsnotify_find_mark(group, to_tell); - spin_unlock(&to_tell->i_lock); /* race with watch removal? We already passes should_send */ if (unlikely(!fsn_mark)) return 0; @@ -147,9 +145,7 @@ static bool inotify_should_send_event(struct fsnotify_group *group, struct inode struct fsnotify_mark *fsn_mark; bool send; - spin_lock(&inode->i_lock); fsn_mark = fsnotify_find_mark(group, inode); - spin_unlock(&inode->i_lock); if (!fsn_mark) return false; diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index 80d102acb86b..ad5a1ea7827e 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -566,9 +566,7 @@ static int inotify_update_existing_watch(struct fsnotify_group *group, if (unlikely(!mask)) return -EINVAL; - spin_lock(&inode->i_lock); fsn_mark = fsnotify_find_mark(group, inode); - spin_unlock(&inode->i_lock); if (!fsn_mark) return -ENOENT; diff --git a/kernel/audit_tree.c b/kernel/audit_tree.c index b20fb055d712..80f8ac328aad 100644 --- a/kernel/audit_tree.c +++ b/kernel/audit_tree.c @@ -360,9 +360,7 @@ static int tag_chunk(struct inode *inode, struct audit_tree *tree) struct node *p; int n; - spin_lock(&inode->i_lock); old_entry = fsnotify_find_mark(audit_tree_group, inode); - spin_unlock(&inode->i_lock); if (!old_entry) return create_chunk(inode, tree); diff --git a/kernel/audit_watch.c b/kernel/audit_watch.c index 24ecbebf4354..d85fa538a722 100644 --- a/kernel/audit_watch.c +++ b/kernel/audit_watch.c @@ -101,10 +101,7 @@ static inline struct audit_parent *audit_find_parent(struct inode *inode) struct audit_parent *parent = NULL; struct fsnotify_mark *entry; - spin_lock(&inode->i_lock); entry = fsnotify_find_mark(audit_watch_group, inode); - spin_unlock(&inode->i_lock); - if (entry) parent = container_of(entry, struct audit_parent, mark); @@ -520,9 +517,7 @@ static bool audit_watch_should_send_event(struct fsnotify_group *group, struct i struct fsnotify_mark *entry; bool send; - spin_lock(&inode->i_lock); entry = fsnotify_find_mark(group, inode); - spin_unlock(&inode->i_lock); if (!entry) return false; -- cgit v1.2.3 From ff0b16a9850e8a240ad59e10b0a1291a8fcf7cbc Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:25 -0500 Subject: fanotify: fscking all notification system fanotify is a novel file notification system which bases notification on giving userspace both an event type (open, close, read, write) and an open file descriptor to the object in question. This should address a number of races and problems with other notification systems like inotify and dnotify and should allow the future implementation of blocking or access controlled notification. These are useful for on access scanners or hierachical storage management schemes. This patch just implements the basics of the fsnotify functions. Signed-off-by: Eric Paris --- fs/notify/Kconfig | 1 + fs/notify/Makefile | 1 + fs/notify/fanotify/Kconfig | 11 ++++++ fs/notify/fanotify/Makefile | 1 + fs/notify/fanotify/fanotify.c | 78 +++++++++++++++++++++++++++++++++++++++++++ fs/notify/fanotify/fanotify.h | 12 +++++++ include/linux/Kbuild | 1 + include/linux/fanotify.h | 40 ++++++++++++++++++++++ 8 files changed, 145 insertions(+) create mode 100644 fs/notify/fanotify/Kconfig create mode 100644 fs/notify/fanotify/Makefile create mode 100644 fs/notify/fanotify/fanotify.c create mode 100644 fs/notify/fanotify/fanotify.h create mode 100644 include/linux/fanotify.h (limited to 'fs') diff --git a/fs/notify/Kconfig b/fs/notify/Kconfig index dffbb0911d02..22c629eedd82 100644 --- a/fs/notify/Kconfig +++ b/fs/notify/Kconfig @@ -3,3 +3,4 @@ config FSNOTIFY source "fs/notify/dnotify/Kconfig" source "fs/notify/inotify/Kconfig" +source "fs/notify/fanotify/Kconfig" diff --git a/fs/notify/Makefile b/fs/notify/Makefile index 0922cc826c46..396a38779371 100644 --- a/fs/notify/Makefile +++ b/fs/notify/Makefile @@ -2,3 +2,4 @@ obj-$(CONFIG_FSNOTIFY) += fsnotify.o notification.o group.o inode_mark.o obj-y += dnotify/ obj-y += inotify/ +obj-y += fanotify/ diff --git a/fs/notify/fanotify/Kconfig b/fs/notify/fanotify/Kconfig new file mode 100644 index 000000000000..f9d7ae081f85 --- /dev/null +++ b/fs/notify/fanotify/Kconfig @@ -0,0 +1,11 @@ +config FANOTIFY + bool "Filesystem wide access notification" + select FSNOTIFY + default y + ---help--- + Say Y here to enable fanotify suport. fanotify is a file access + notification system which differs from inotify in that it sends + and open file descriptor to the userspace listener along with + the event. + + If unsure, say Y. diff --git a/fs/notify/fanotify/Makefile b/fs/notify/fanotify/Makefile new file mode 100644 index 000000000000..e7d39c05b0fe --- /dev/null +++ b/fs/notify/fanotify/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_FANOTIFY) += fanotify.o diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c new file mode 100644 index 000000000000..3ffb9dbcab08 --- /dev/null +++ b/fs/notify/fanotify/fanotify.c @@ -0,0 +1,78 @@ +#include +#include +#include +#include /* UINT_MAX */ +#include + +#include "fanotify.h" + +static int fanotify_handle_event(struct fsnotify_group *group, struct fsnotify_event *event) +{ + int ret; + + + BUILD_BUG_ON(FAN_ACCESS != FS_ACCESS); + BUILD_BUG_ON(FAN_MODIFY != FS_MODIFY); + BUILD_BUG_ON(FAN_CLOSE_NOWRITE != FS_CLOSE_NOWRITE); + BUILD_BUG_ON(FAN_CLOSE_WRITE != FS_CLOSE_WRITE); + BUILD_BUG_ON(FAN_OPEN != FS_OPEN); + BUILD_BUG_ON(FAN_EVENT_ON_CHILD != FS_EVENT_ON_CHILD); + BUILD_BUG_ON(FAN_Q_OVERFLOW != FS_Q_OVERFLOW); + + pr_debug("%s: group=%p event=%p\n", __func__, group, event); + + ret = fsnotify_add_notify_event(group, event, NULL, NULL); + + return ret; +} + +static bool fanotify_should_send_event(struct fsnotify_group *group, struct inode *inode, + struct vfsmount *mnt, __u32 mask, void *data, + int data_type) +{ + struct fsnotify_mark *fsn_mark; + bool send; + + pr_debug("%s: group=%p inode=%p mask=%x data=%p data_type=%d\n", + __func__, group, inode, mask, data, data_type); + + /* sorry, fanotify only gives a damn about files and dirs */ + if (!S_ISREG(inode->i_mode) && + !S_ISDIR(inode->i_mode)) + return false; + + /* if we don't have enough info to send an event to userspace say no */ + if (data_type != FSNOTIFY_EVENT_PATH) + return false; + + fsn_mark = fsnotify_find_mark(group, inode); + if (!fsn_mark) + return false; + + /* if the event is for a child and this inode doesn't care about + * events on the child, don't send it! */ + if ((mask & FS_EVENT_ON_CHILD) && + !(fsn_mark->mask & FS_EVENT_ON_CHILD)) { + send = false; + } else { + /* + * We care about children, but do we care about this particular + * type of event? + */ + mask = (mask & ~FS_EVENT_ON_CHILD); + send = (fsn_mark->mask & mask); + } + + /* find took a reference */ + fsnotify_put_mark(fsn_mark); + + return send; +} + +const struct fsnotify_ops fanotify_fsnotify_ops = { + .handle_event = fanotify_handle_event, + .should_send_event = fanotify_should_send_event, + .free_group_priv = NULL, + .free_event_priv = NULL, + .freeing_mark = NULL, +}; diff --git a/fs/notify/fanotify/fanotify.h b/fs/notify/fanotify/fanotify.h new file mode 100644 index 000000000000..50765eb30fe4 --- /dev/null +++ b/fs/notify/fanotify/fanotify.h @@ -0,0 +1,12 @@ +#include +#include +#include +#include +#include + +static inline bool fanotify_mask_valid(__u32 mask) +{ + if (mask & ~((__u32)FAN_ALL_INCOMING_EVENTS)) + return false; + return true; +} diff --git a/include/linux/Kbuild b/include/linux/Kbuild index 2fc8e14cc24a..d5cca9a05f14 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -210,6 +210,7 @@ unifdef-y += ethtool.h unifdef-y += eventpoll.h unifdef-y += signalfd.h unifdef-y += ext2_fs.h +unifdef-y += fanotify.h unifdef-y += fb.h unifdef-y += fcntl.h unifdef-y += filter.h diff --git a/include/linux/fanotify.h b/include/linux/fanotify.h new file mode 100644 index 000000000000..b560f86d1401 --- /dev/null +++ b/include/linux/fanotify.h @@ -0,0 +1,40 @@ +#ifndef _LINUX_FANOTIFY_H +#define _LINUX_FANOTIFY_H + +#include + +/* the following events that user-space can register for */ +#define FAN_ACCESS 0x00000001 /* File was accessed */ +#define FAN_MODIFY 0x00000002 /* File was modified */ +#define FAN_CLOSE_WRITE 0x00000008 /* Unwrittable file closed */ +#define FAN_CLOSE_NOWRITE 0x00000010 /* Writtable file closed */ +#define FAN_OPEN 0x00000020 /* File was opened */ + +#define FAN_EVENT_ON_CHILD 0x08000000 /* interested in child events */ + +/* FIXME currently Q's have no limit.... */ +#define FAN_Q_OVERFLOW 0x00004000 /* Event queued overflowed */ + +/* helper events */ +#define FAN_CLOSE (FAN_CLOSE_WRITE | FAN_CLOSE_NOWRITE) /* close */ + +/* + * All of the events - we build the list by hand so that we can add flags in + * the future and not break backward compatibility. Apps will get only the + * events that they originally wanted. Be sure to add new events here! + */ +#define FAN_ALL_EVENTS (FAN_ACCESS |\ + FAN_MODIFY |\ + FAN_CLOSE |\ + FAN_OPEN) + +/* + * All legal FAN bits userspace can request (although possibly not all + * at the same time. + */ +#define FAN_ALL_INCOMING_EVENTS (FAN_ALL_EVENTS |\ + FAN_EVENT_ON_CHILD) +#ifdef __KERNEL__ + +#endif /* __KERNEL__ */ +#endif /* _LINUX_FANOTIFY_H */ -- cgit v1.2.3 From 767cd46c332d1360cdbe46ef18d80c3ade06fdc1 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:25 -0500 Subject: fanotify:drop notification if they exist in the outgoing queue fanotify listeners get an open file descriptor to the object in question so the ordering of operations is not as important as in other notification systems. inotify will drop events if the last event in the event FIFO is the same as the current event. This patch will drop fanotify events if they are the same as another event anywhere in the event FIFO. Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify.c | 45 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index 3ffb9dbcab08..c35c1175c4cf 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -6,6 +6,45 @@ #include "fanotify.h" +static bool should_merge(struct fsnotify_event *old, struct fsnotify_event *new) +{ + pr_debug("%s: old=%p new=%p\n", __func__, old, new); + + if ((old->mask == new->mask) && + (old->to_tell == new->to_tell) && + (old->data_type == new->data_type)) { + switch (old->data_type) { + case (FSNOTIFY_EVENT_PATH): + if ((old->path.mnt == new->path.mnt) && + (old->path.dentry == new->path.dentry)) + return true; + case (FSNOTIFY_EVENT_NONE): + return true; + default: + BUG(); + }; + } + return false; +} + +static int fanotify_merge(struct list_head *list, struct fsnotify_event *event) +{ + struct fsnotify_event_holder *holder; + struct fsnotify_event *test_event; + + pr_debug("%s: list=%p event=%p\n", __func__, list, event); + + /* and the list better be locked by something too! */ + + list_for_each_entry_reverse(holder, list, event_list) { + test_event = holder->event; + if (should_merge(test_event, event)) + return -EEXIST; + } + + return 0; +} + static int fanotify_handle_event(struct fsnotify_group *group, struct fsnotify_event *event) { int ret; @@ -21,8 +60,10 @@ static int fanotify_handle_event(struct fsnotify_group *group, struct fsnotify_e pr_debug("%s: group=%p event=%p\n", __func__, group, event); - ret = fsnotify_add_notify_event(group, event, NULL, NULL); - + ret = fsnotify_add_notify_event(group, event, NULL, fanotify_merge); + /* -EEXIST means this event was merged with another, not that it was an error */ + if (ret == -EEXIST) + ret = 0; return ret; } -- cgit v1.2.3 From a12a7dd3284f5644326af1ea53b35030f205dd29 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:25 -0500 Subject: fanotify: merge notification events with different masks Instead of just merging fanotify events if they are exactly the same, merge notification events with different masks. To do this we have to clone the old event, update the mask in the new event with the new merged mask, and put the new event in place of the old event. Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify.c | 39 ++++++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 9 deletions(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index c35c1175c4cf..8e574d6f6a80 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -10,8 +10,7 @@ static bool should_merge(struct fsnotify_event *old, struct fsnotify_event *new) { pr_debug("%s: old=%p new=%p\n", __func__, old, new); - if ((old->mask == new->mask) && - (old->to_tell == new->to_tell) && + if ((old->to_tell == new->to_tell) && (old->data_type == new->data_type)) { switch (old->data_type) { case (FSNOTIFY_EVENT_PATH): @@ -29,20 +28,42 @@ static bool should_merge(struct fsnotify_event *old, struct fsnotify_event *new) static int fanotify_merge(struct list_head *list, struct fsnotify_event *event) { - struct fsnotify_event_holder *holder; + struct fsnotify_event_holder *test_holder; struct fsnotify_event *test_event; + struct fsnotify_event *new_event; + int ret = 0; pr_debug("%s: list=%p event=%p\n", __func__, list, event); /* and the list better be locked by something too! */ - list_for_each_entry_reverse(holder, list, event_list) { - test_event = holder->event; - if (should_merge(test_event, event)) - return -EEXIST; + list_for_each_entry_reverse(test_holder, list, event_list) { + test_event = test_holder->event; + if (should_merge(test_event, event)) { + ret = -EEXIST; + + /* if they are exactly the same we are done */ + if (test_event->mask == event->mask) + goto out; + + /* can't allocate memory, merge was no possible */ + new_event = fsnotify_clone_event(test_event); + if (unlikely(!new_event)) { + ret = 0; + goto out; + } + + /* build new event and replace it on the list */ + new_event->mask = (test_event->mask | event->mask); + fsnotify_replace_event(test_holder, new_event); + /* match ref from fsnotify_clone_event() */ + fsnotify_put_event(new_event); + + break; + } } - - return 0; +out: + return ret; } static int fanotify_handle_event(struct fsnotify_group *group, struct fsnotify_event *event) -- cgit v1.2.3 From 9dced01a0939f3e952eca8c21427ceec1f473dcf Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:25 -0500 Subject: fanotify: do not clone on merge unless needed Currently if 2 events are going to be merged on the notication queue with different masks the second event will be cloned and will replace the first event. However if this notification queue is the only place referencing the event in question there is no reason not to just update the event in place. We can tell this if the event->refcnt == 1. Since we hold a reference for each queue this event is on we know that when refcnt == 1 this is the only queue. The other concern is that it might be about to be added to a new queue, but this can't be the case since fsnotify holds a reference on the event until it is finished adding it to queues. Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index 8e574d6f6a80..5b0b6b485a9c 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -46,6 +46,16 @@ static int fanotify_merge(struct list_head *list, struct fsnotify_event *event) if (test_event->mask == event->mask) goto out; + /* + * if the refcnt == 1 this is the only queue + * for this event and so we can update the mask + * in place. + */ + if (atomic_read(&test_event->refcnt) == 1) { + test_event->mask |= event->mask; + goto out; + } + /* can't allocate memory, merge was no possible */ new_event = fsnotify_clone_event(test_event); if (unlikely(!new_event)) { -- cgit v1.2.3 From 11637e4b7dc098e9a863f0a619d55ebc60f5949e Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:25 -0500 Subject: fanotify: fanotify_init syscall declaration This patch defines a new syscall fanotify_init() of the form: int sys_fanotify_init(unsigned int flags, unsigned int event_f_flags, unsigned int priority) This syscall is used to create and fanotify group. This is very similar to the inotify_init() syscall. Signed-off-by: Eric Paris --- arch/x86/ia32/ia32entry.S | 1 + arch/x86/include/asm/unistd_32.h | 3 ++- arch/x86/include/asm/unistd_64.h | 2 ++ arch/x86/kernel/syscall_table_32.S | 1 + fs/notify/fanotify/Makefile | 2 +- fs/notify/fanotify/fanotify_user.c | 13 +++++++++++++ include/linux/syscalls.h | 2 ++ kernel/sys_ni.c | 3 +++ 8 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 fs/notify/fanotify/fanotify_user.c (limited to 'fs') diff --git a/arch/x86/ia32/ia32entry.S b/arch/x86/ia32/ia32entry.S index e790bc1fbfa3..586cb3be2e32 100644 --- a/arch/x86/ia32/ia32entry.S +++ b/arch/x86/ia32/ia32entry.S @@ -842,4 +842,5 @@ ia32_sys_call_table: .quad compat_sys_rt_tgsigqueueinfo /* 335 */ .quad sys_perf_event_open .quad compat_sys_recvmmsg + .quad sys_fanotify_init ia32_syscall_end: diff --git a/arch/x86/include/asm/unistd_32.h b/arch/x86/include/asm/unistd_32.h index beb9b5f8f8a4..981c7e7ad804 100644 --- a/arch/x86/include/asm/unistd_32.h +++ b/arch/x86/include/asm/unistd_32.h @@ -343,10 +343,11 @@ #define __NR_rt_tgsigqueueinfo 335 #define __NR_perf_event_open 336 #define __NR_recvmmsg 337 +#define __NR_fanotify_init 338 #ifdef __KERNEL__ -#define NR_syscalls 338 +#define NR_syscalls 339 #define __ARCH_WANT_IPC_PARSE_VERSION #define __ARCH_WANT_OLD_READDIR diff --git a/arch/x86/include/asm/unistd_64.h b/arch/x86/include/asm/unistd_64.h index ff4307b0e81e..4f23e04bdb34 100644 --- a/arch/x86/include/asm/unistd_64.h +++ b/arch/x86/include/asm/unistd_64.h @@ -663,6 +663,8 @@ __SYSCALL(__NR_rt_tgsigqueueinfo, sys_rt_tgsigqueueinfo) __SYSCALL(__NR_perf_event_open, sys_perf_event_open) #define __NR_recvmmsg 299 __SYSCALL(__NR_recvmmsg, sys_recvmmsg) +#define __NR_fanotify_init 300 +__SYSCALL(__NR_fanotify_init, sys_fanotify_init) #ifndef __NO_STUBS #define __ARCH_WANT_OLD_READDIR diff --git a/arch/x86/kernel/syscall_table_32.S b/arch/x86/kernel/syscall_table_32.S index 8b3729341216..e38793b50e1d 100644 --- a/arch/x86/kernel/syscall_table_32.S +++ b/arch/x86/kernel/syscall_table_32.S @@ -337,3 +337,4 @@ ENTRY(sys_call_table) .long sys_rt_tgsigqueueinfo /* 335 */ .long sys_perf_event_open .long sys_recvmmsg + .long sys_fanotify_init diff --git a/fs/notify/fanotify/Makefile b/fs/notify/fanotify/Makefile index e7d39c05b0fe..0999213e7e6e 100644 --- a/fs/notify/fanotify/Makefile +++ b/fs/notify/fanotify/Makefile @@ -1 +1 @@ -obj-$(CONFIG_FANOTIFY) += fanotify.o +obj-$(CONFIG_FANOTIFY) += fanotify.o fanotify_user.o diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c new file mode 100644 index 000000000000..cf176fc7086b --- /dev/null +++ b/fs/notify/fanotify/fanotify_user.c @@ -0,0 +1,13 @@ +#include +#include +#include +#include +#include + +#include "fanotify.h" + +SYSCALL_DEFINE3(fanotify_init, unsigned int, flags, unsigned int, event_f_flags, + unsigned int, priority) +{ + return -ENOSYS; +} diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 13ebb5413a79..198dcc9bd025 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -813,6 +813,8 @@ asmlinkage long sys_pselect6(int, fd_set __user *, fd_set __user *, asmlinkage long sys_ppoll(struct pollfd __user *, unsigned int, struct timespec __user *, const sigset_t __user *, size_t); +asmlinkage long sys_fanotify_init(unsigned int flags, unsigned int event_f_flags, + unsigned int priority); int kernel_execve(const char *filename, char *const argv[], char *const envp[]); diff --git a/kernel/sys_ni.c b/kernel/sys_ni.c index 70f2ea758ffe..2c4adc2decc3 100644 --- a/kernel/sys_ni.c +++ b/kernel/sys_ni.c @@ -181,3 +181,6 @@ cond_syscall(sys_eventfd2); /* performance counters: */ cond_syscall(sys_perf_event_open); + +/* fanotify! */ +cond_syscall(sys_fanotify_init); -- cgit v1.2.3 From 52c923dd079df49f58016a9e56df184b132611d6 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:26 -0500 Subject: fanotify: fanotify_init syscall implementation NAME fanotify_init - initialize an fanotify group SYNOPSIS int fanotify_init(unsigned int flags, unsigned int event_f_flags, int priority); DESCRIPTION fanotify_init() initializes a new fanotify instance and returns a file descriptor associated with the new fanotify event queue. The following values can be OR'd into the flags field: FAN_NONBLOCK Set the O_NONBLOCK file status flag on the new open file description. Using this flag saves extra calls to fcntl(2) to achieve the same result. FAN_CLOEXEC Set the close-on-exec (FD_CLOEXEC) flag on the new file descriptor. See the description of the O_CLOEXEC flag in open(2) for reasons why this may be useful. The event_f_flags argument is unused and must be set to 0 The priority argument is unused and must be set to 0 RETURN VALUE On success, this system call return a new file descriptor. On error, -1 is returned, and errno is set to indicate the error. ERRORS EINVAL An invalid value was specified in flags. EINVAL A non-zero valid was passed in event_f_flags or in priority ENFILE The system limit on the total number of file descriptors has been reached. ENOMEM Insufficient kernel memory is available. CONFORMING TO These system calls are Linux-specific. Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify.h | 2 ++ fs/notify/fanotify/fanotify_user.c | 61 +++++++++++++++++++++++++++++++++++++- include/linux/fanotify.h | 4 +++ 3 files changed, 66 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify.h b/fs/notify/fanotify/fanotify.h index 50765eb30fe4..dd656cfab1ba 100644 --- a/fs/notify/fanotify/fanotify.h +++ b/fs/notify/fanotify/fanotify.h @@ -4,6 +4,8 @@ #include #include +extern const struct fsnotify_ops fanotify_fsnotify_ops; + static inline bool fanotify_mask_valid(__u32 mask) { if (mask & ~((__u32)FAN_ALL_INCOMING_EVENTS)) diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index cf176fc7086b..67c0b5e4a488 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -1,13 +1,72 @@ #include #include +#include #include #include #include #include "fanotify.h" +static int fanotify_release(struct inode *ignored, struct file *file) +{ + struct fsnotify_group *group = file->private_data; + + pr_debug("%s: file=%p group=%p\n", __func__, file, group); + + /* matches the fanotify_init->fsnotify_alloc_group */ + fsnotify_put_group(group); + + return 0; +} + +static const struct file_operations fanotify_fops = { + .poll = NULL, + .read = NULL, + .fasync = NULL, + .release = fanotify_release, + .unlocked_ioctl = NULL, + .compat_ioctl = NULL, +}; + +/* fanotify syscalls */ SYSCALL_DEFINE3(fanotify_init, unsigned int, flags, unsigned int, event_f_flags, unsigned int, priority) { - return -ENOSYS; + struct fsnotify_group *group; + int f_flags, fd; + + pr_debug("%s: flags=%d event_f_flags=%d priority=%d\n", + __func__, flags, event_f_flags, priority); + + if (event_f_flags) + return -EINVAL; + if (priority) + return -EINVAL; + + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + + if (flags & ~FAN_ALL_INIT_FLAGS) + return -EINVAL; + + f_flags = (O_RDONLY | FMODE_NONOTIFY); + if (flags & FAN_CLOEXEC) + f_flags |= O_CLOEXEC; + if (flags & FAN_NONBLOCK) + f_flags |= O_NONBLOCK; + + /* fsnotify_alloc_group takes a ref. Dropped in fanotify_release */ + group = fsnotify_alloc_group(&fanotify_fsnotify_ops); + if (IS_ERR(group)) + return PTR_ERR(group); + + fd = anon_inode_getfd("[fanotify]", &fanotify_fops, group, f_flags); + if (fd < 0) + goto out_put_group; + + return fd; + +out_put_group: + fsnotify_put_group(group); + return fd; } diff --git a/include/linux/fanotify.h b/include/linux/fanotify.h index b560f86d1401..00bc6d4fbb58 100644 --- a/include/linux/fanotify.h +++ b/include/linux/fanotify.h @@ -18,6 +18,10 @@ /* helper events */ #define FAN_CLOSE (FAN_CLOSE_WRITE | FAN_CLOSE_NOWRITE) /* close */ +#define FAN_CLOEXEC 0x00000001 +#define FAN_NONBLOCK 0x00000002 + +#define FAN_ALL_INIT_FLAGS (FAN_CLOEXEC | FAN_NONBLOCK) /* * All of the events - we build the list by hand so that we can add flags in * the future and not break backward compatibility. Apps will get only the -- cgit v1.2.3 From bbaa4168b2d2d8cc674e6d35806e8426aef464b8 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:26 -0500 Subject: fanotify: sys_fanotify_mark declartion This patch simply declares the new sys_fanotify_mark syscall int fanotify_mark(int fanotify_fd, unsigned int flags, u64_mask, int dfd const char *pathname) Signed-off-by: Eric Paris --- arch/x86/ia32/ia32entry.S | 1 + arch/x86/ia32/sys_ia32.c | 9 +++++++++ arch/x86/include/asm/sys_ia32.h | 3 +++ arch/x86/include/asm/unistd_32.h | 3 ++- arch/x86/include/asm/unistd_64.h | 2 ++ arch/x86/kernel/syscall_table_32.S | 1 + fs/notify/fanotify/fanotify_user.c | 6 ++++++ include/linux/syscalls.h | 3 +++ kernel/sys_ni.c | 1 + 9 files changed, 28 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/arch/x86/ia32/ia32entry.S b/arch/x86/ia32/ia32entry.S index 586cb3be2e32..17cf65c94804 100644 --- a/arch/x86/ia32/ia32entry.S +++ b/arch/x86/ia32/ia32entry.S @@ -843,4 +843,5 @@ ia32_sys_call_table: .quad sys_perf_event_open .quad compat_sys_recvmmsg .quad sys_fanotify_init + .quad sys32_fanotify_mark ia32_syscall_end: diff --git a/arch/x86/ia32/sys_ia32.c b/arch/x86/ia32/sys_ia32.c index 626be156d88d..3d093311d5e2 100644 --- a/arch/x86/ia32/sys_ia32.c +++ b/arch/x86/ia32/sys_ia32.c @@ -546,3 +546,12 @@ asmlinkage long sys32_fallocate(int fd, int mode, unsigned offset_lo, return sys_fallocate(fd, mode, ((u64)offset_hi << 32) | offset_lo, ((u64)len_hi << 32) | len_lo); } + +asmlinkage long sys32_fanotify_mark(int fanotify_fd, unsigned int flags, + u32 mask_lo, u32 mask_hi, + int fd, const char __user *pathname) +{ + return sys_fanotify_mark(fanotify_fd, flags, + ((u64)mask_hi << 32) | mask_lo, + fd, pathname); +} diff --git a/arch/x86/include/asm/sys_ia32.h b/arch/x86/include/asm/sys_ia32.h index 3ad421784ae7..cf4e2e381cba 100644 --- a/arch/x86/include/asm/sys_ia32.h +++ b/arch/x86/include/asm/sys_ia32.h @@ -80,4 +80,7 @@ asmlinkage long sys32_rt_sigreturn(struct pt_regs *); /* ia32/ipc32.c */ asmlinkage long sys32_ipc(u32, int, int, int, compat_uptr_t, u32); + +asmlinkage long sys32_fanotify_mark(int, unsigned int, u32, u32, int, + const char __user *); #endif /* _ASM_X86_SYS_IA32_H */ diff --git a/arch/x86/include/asm/unistd_32.h b/arch/x86/include/asm/unistd_32.h index 981c7e7ad804..80b799cd74f7 100644 --- a/arch/x86/include/asm/unistd_32.h +++ b/arch/x86/include/asm/unistd_32.h @@ -344,10 +344,11 @@ #define __NR_perf_event_open 336 #define __NR_recvmmsg 337 #define __NR_fanotify_init 338 +#define __NR_fanotify_mark 339 #ifdef __KERNEL__ -#define NR_syscalls 339 +#define NR_syscalls 340 #define __ARCH_WANT_IPC_PARSE_VERSION #define __ARCH_WANT_OLD_READDIR diff --git a/arch/x86/include/asm/unistd_64.h b/arch/x86/include/asm/unistd_64.h index 4f23e04bdb34..5b7b1d585616 100644 --- a/arch/x86/include/asm/unistd_64.h +++ b/arch/x86/include/asm/unistd_64.h @@ -665,6 +665,8 @@ __SYSCALL(__NR_perf_event_open, sys_perf_event_open) __SYSCALL(__NR_recvmmsg, sys_recvmmsg) #define __NR_fanotify_init 300 __SYSCALL(__NR_fanotify_init, sys_fanotify_init) +#define __NR_fanotify_mark 301 +__SYSCALL(__NR_fanotify_mark, sys_fanotify_mark) #ifndef __NO_STUBS #define __ARCH_WANT_OLD_READDIR diff --git a/arch/x86/kernel/syscall_table_32.S b/arch/x86/kernel/syscall_table_32.S index e38793b50e1d..07ad5eb7cc5c 100644 --- a/arch/x86/kernel/syscall_table_32.S +++ b/arch/x86/kernel/syscall_table_32.S @@ -338,3 +338,4 @@ ENTRY(sys_call_table) .long sys_perf_event_open .long sys_recvmmsg .long sys_fanotify_init + .long sys_fanotify_mark diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 67c0b5e4a488..55d6e379f2b6 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -70,3 +70,9 @@ out_put_group: fsnotify_put_group(group); return fd; } + +SYSCALL_DEFINE5(fanotify_mark, int, fanotify_fd, unsigned int, flags, + __u64, mask, int, dfd, const char __user *, pathname) +{ + return -ENOSYS; +} diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 198dcc9bd025..5b05c37059e9 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -815,6 +815,9 @@ asmlinkage long sys_ppoll(struct pollfd __user *, unsigned int, size_t); asmlinkage long sys_fanotify_init(unsigned int flags, unsigned int event_f_flags, unsigned int priority); +asmlinkage long sys_fanotify_mark(int fanotify_fd, unsigned int flags, + u64 mask, int fd, + const char __user *pathname); int kernel_execve(const char *filename, char *const argv[], char *const envp[]); diff --git a/kernel/sys_ni.c b/kernel/sys_ni.c index 2c4adc2decc3..bad369ec5403 100644 --- a/kernel/sys_ni.c +++ b/kernel/sys_ni.c @@ -184,3 +184,4 @@ cond_syscall(sys_perf_event_open); /* fanotify! */ cond_syscall(sys_fanotify_init); +cond_syscall(sys_fanotify_mark); -- cgit v1.2.3 From 2a3edf86040a7e15684525a2aadc29f532c51325 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:26 -0500 Subject: fanotify: fanotify_mark syscall implementation NAME fanotify_mark - add, remove, or modify an fanotify mark on a filesystem object SYNOPSIS int fanotify_mark(int fanotify_fd, unsigned int flags, u64 mask, int dfd, const char *pathname) DESCRIPTION fanotify_mark() is used to add remove or modify a mark on a filesystem object. Marks are used to indicate that the fanotify group is interested in events which occur on that object. At this point in time marks may only be added to files and directories. fanotify_fd must be a file descriptor returned by fanotify_init() The flags field must contain exactly one of the following: FAN_MARK_ADD - or the bits in mask and ignored mask into the mark FAN_MARK_REMOVE - bitwise remove the bits in mask and ignored mark from the mark The following values can be OR'd into the flags field: FAN_MARK_DONT_FOLLOW - same meaning as O_NOFOLLOW as described in open(2) FAN_MARK_ONLYDIR - same meaning as O_DIRECTORY as described in open(2) dfd may be any of the following: AT_FDCWD: the object will be lookup up based on pathname similar to open(2) file descriptor of a directory: if pathname is not NULL the object to modify will be lookup up similar to openat(2) file descriptor of the final object: if pathname is NULL the object to modify will be the object referenced by dfd The mask is the bitwise OR of the set of events of interest such as: FAN_ACCESS - object was accessed (read) FAN_MODIFY - object was modified (write) FAN_CLOSE_WRITE - object was writable and was closed FAN_CLOSE_NOWRITE - object was read only and was closed FAN_OPEN - object was opened FAN_EVENT_ON_CHILD - interested in objected that happen to children. Only relavent when the object is a directory FAN_Q_OVERFLOW - event queue overflowed (not implemented) RETURN VALUE On success, this system call returns 0. On error, -1 is returned, and errno is set to indicate the error. ERRORS EINVAL An invalid value was specified in flags. EINVAL An invalid value was specified in mask. EINVAL An invalid value was specified in ignored_mask. EINVAL fanotify_fd is not a file descriptor as returned by fanotify_init() EBADF fanotify_fd is not a valid file descriptor EBADF dfd is not a valid file descriptor and path is NULL. ENOTDIR dfd is not a directory and path is not NULL EACCESS no search permissions on some part of the path ENENT file not found ENOMEM Insufficient kernel memory is available. CONFORMING TO These system calls are Linux-specific. Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify.h | 18 +++ fs/notify/fanotify/fanotify_user.c | 239 ++++++++++++++++++++++++++++++++++++- include/linux/fanotify.h | 13 ++ 3 files changed, 269 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify.h b/fs/notify/fanotify/fanotify.h index dd656cfab1ba..59c3331a0e81 100644 --- a/fs/notify/fanotify/fanotify.h +++ b/fs/notify/fanotify/fanotify.h @@ -6,6 +6,24 @@ extern const struct fsnotify_ops fanotify_fsnotify_ops; +static inline bool fanotify_mark_flags_valid(unsigned int flags) +{ + /* must be either and add or a remove */ + if (!(flags & (FAN_MARK_ADD | FAN_MARK_REMOVE))) + return false; + + /* cannot be both add and remove */ + if ((flags & FAN_MARK_ADD) && + (flags & FAN_MARK_REMOVE)) + return false; + + /* cannot have more flags than we know about */ + if (flags & ~FAN_ALL_MARK_FLAGS) + return false; + + return true; +} + static inline bool fanotify_mask_valid(__u32 mask) { if (mask & ~((__u32)FAN_ALL_INCOMING_EVENTS)) diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 55d6e379f2b6..bc4fa48157f1 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -1,12 +1,18 @@ #include +#include #include #include #include +#include +#include #include #include +#include #include "fanotify.h" +static struct kmem_cache *fanotify_mark_cache __read_mostly; + static int fanotify_release(struct inode *ignored, struct file *file) { struct fsnotify_group *group = file->private_data; @@ -28,6 +34,185 @@ static const struct file_operations fanotify_fops = { .compat_ioctl = NULL, }; +static void fanotify_free_mark(struct fsnotify_mark *fsn_mark) +{ + kmem_cache_free(fanotify_mark_cache, fsn_mark); +} + +static int fanotify_find_path(int dfd, const char __user *filename, + struct path *path, unsigned int flags) +{ + int ret; + + pr_debug("%s: dfd=%d filename=%p flags=%x\n", __func__, + dfd, filename, flags); + + if (filename == NULL) { + struct file *file; + int fput_needed; + + ret = -EBADF; + file = fget_light(dfd, &fput_needed); + if (!file) + goto out; + + ret = -ENOTDIR; + if ((flags & FAN_MARK_ONLYDIR) && + !(S_ISDIR(file->f_path.dentry->d_inode->i_mode))) { + fput_light(file, fput_needed); + goto out; + } + + *path = file->f_path; + path_get(path); + fput_light(file, fput_needed); + } else { + unsigned int lookup_flags = 0; + + if (!(flags & FAN_MARK_DONT_FOLLOW)) + lookup_flags |= LOOKUP_FOLLOW; + if (flags & FAN_MARK_ONLYDIR) + lookup_flags |= LOOKUP_DIRECTORY; + + ret = user_path_at(dfd, filename, lookup_flags, path); + if (ret) + goto out; + } + + /* you can only watch an inode if you have read permissions on it */ + ret = inode_permission(path->dentry->d_inode, MAY_READ); + if (ret) + path_put(path); +out: + return ret; +} + +static int fanotify_remove_mark(struct fsnotify_group *group, + struct inode *inode, + __u32 mask) +{ + struct fsnotify_mark *fsn_mark; + __u32 new_mask; + + pr_debug("%s: group=%p inode=%p mask=%x\n", __func__, + group, inode, mask); + + fsn_mark = fsnotify_find_mark(group, inode); + if (!fsn_mark) + return -ENOENT; + + spin_lock(&fsn_mark->lock); + fsn_mark->mask &= ~mask; + new_mask = fsn_mark->mask; + spin_unlock(&fsn_mark->lock); + + if (!new_mask) + fsnotify_destroy_mark(fsn_mark); + else + fsnotify_recalc_inode_mask(inode); + + fsnotify_recalc_group_mask(group); + + /* matches the fsnotify_find_mark() */ + fsnotify_put_mark(fsn_mark); + + return 0; +} + +static int fanotify_add_mark(struct fsnotify_group *group, + struct inode *inode, + __u32 mask) +{ + struct fsnotify_mark *fsn_mark; + __u32 old_mask, new_mask; + int ret; + + pr_debug("%s: group=%p inode=%p mask=%x\n", __func__, + group, inode, mask); + + fsn_mark = fsnotify_find_mark(group, inode); + if (!fsn_mark) { + struct fsnotify_mark *new_fsn_mark; + + ret = -ENOMEM; + new_fsn_mark = kmem_cache_alloc(fanotify_mark_cache, GFP_KERNEL); + if (!new_fsn_mark) + goto out; + + fsnotify_init_mark(new_fsn_mark, fanotify_free_mark); + ret = fsnotify_add_mark(new_fsn_mark, group, inode, 0); + if (ret) { + fanotify_free_mark(new_fsn_mark); + goto out; + } + + fsn_mark = new_fsn_mark; + } + + ret = 0; + + spin_lock(&fsn_mark->lock); + old_mask = fsn_mark->mask; + fsn_mark->mask |= mask; + new_mask = fsn_mark->mask; + spin_unlock(&fsn_mark->lock); + + /* we made changes to a mask, update the group mask and the inode mask + * so things happen quickly. */ + if (old_mask != new_mask) { + /* more bits in old than in new? */ + int dropped = (old_mask & ~new_mask); + /* more bits in this mark than the inode's mask? */ + int do_inode = (new_mask & ~inode->i_fsnotify_mask); + /* more bits in this mark than the group? */ + int do_group = (new_mask & ~group->mask); + + /* update the inode with this new mark */ + if (dropped || do_inode) + fsnotify_recalc_inode_mask(inode); + + /* update the group mask with the new mask */ + if (dropped || do_group) + fsnotify_recalc_group_mask(group); + } + + /* match the init or the find.... */ + fsnotify_put_mark(fsn_mark); +out: + return ret; +} + +static int fanotify_update_mark(struct fsnotify_group *group, + struct inode *inode, int flags, + __u32 mask) +{ + pr_debug("%s: group=%p inode=%p flags=%x mask=%x\n", __func__, + group, inode, flags, mask); + + if (flags & FAN_MARK_ADD) + fanotify_add_mark(group, inode, mask); + else if (flags & FAN_MARK_REMOVE) + fanotify_remove_mark(group, inode, mask); + else + BUG(); + + return 0; +} + +static bool fanotify_mark_validate_input(int flags, + __u32 mask) +{ + pr_debug("%s: flags=%x mask=%x\n", __func__, flags, mask); + + /* are flags valid of this operation? */ + if (!fanotify_mark_flags_valid(flags)) + return false; + /* is the mask valid? */ + if (!fanotify_mask_valid(mask)) + return false; + return true; +} + /* fanotify syscalls */ SYSCALL_DEFINE3(fanotify_init, unsigned int, flags, unsigned int, event_f_flags, unsigned int, priority) @@ -74,5 +259,57 @@ out_put_group: SYSCALL_DEFINE5(fanotify_mark, int, fanotify_fd, unsigned int, flags, __u64, mask, int, dfd, const char __user *, pathname) { - return -ENOSYS; + struct inode *inode; + struct fsnotify_group *group; + struct file *filp; + struct path path; + int ret, fput_needed; + + pr_debug("%s: fanotify_fd=%d flags=%x dfd=%d pathname=%p mask=%llx\n", + __func__, fanotify_fd, flags, dfd, pathname, mask); + + /* we only use the lower 32 bits as of right now. */ + if (mask & ((__u64)0xffffffff << 32)) + return -EINVAL; + + if (!fanotify_mark_validate_input(flags, mask)) + return -EINVAL; + + filp = fget_light(fanotify_fd, &fput_needed); + if (unlikely(!filp)) + return -EBADF; + + /* verify that this is indeed an fanotify instance */ + ret = -EINVAL; + if (unlikely(filp->f_op != &fanotify_fops)) + goto fput_and_out; + + ret = fanotify_find_path(dfd, pathname, &path, flags); + if (ret) + goto fput_and_out; + + /* inode held in place by reference to path; group by fget on fd */ + inode = path.dentry->d_inode; + group = filp->private_data; + + /* create/update an inode mark */ + ret = fanotify_update_mark(group, inode, flags, mask); + + path_put(&path); +fput_and_out: + fput_light(filp, fput_needed); + return ret; +} + +/* + * fanotify_user_setup - Our initialization function. Note that we cannnot return + * error because we have compiled-in VFS hooks. So an (unlikely) failure here + * must result in panic(). + */ +static int __init fanotify_user_setup(void) +{ + fanotify_mark_cache = KMEM_CACHE(fsnotify_mark, SLAB_PANIC); + + return 0; } +device_initcall(fanotify_user_setup); diff --git a/include/linux/fanotify.h b/include/linux/fanotify.h index 00bc6d4fbb58..95aeea2a3ca6 100644 --- a/include/linux/fanotify.h +++ b/include/linux/fanotify.h @@ -18,10 +18,23 @@ /* helper events */ #define FAN_CLOSE (FAN_CLOSE_WRITE | FAN_CLOSE_NOWRITE) /* close */ +/* flags used for fanotify_init() */ #define FAN_CLOEXEC 0x00000001 #define FAN_NONBLOCK 0x00000002 #define FAN_ALL_INIT_FLAGS (FAN_CLOEXEC | FAN_NONBLOCK) + +/* flags used for fanotify_modify_mark() */ +#define FAN_MARK_ADD 0x00000001 +#define FAN_MARK_REMOVE 0x00000002 +#define FAN_MARK_DONT_FOLLOW 0x00000004 +#define FAN_MARK_ONLYDIR 0x00000008 + +#define FAN_ALL_MARK_FLAGS (FAN_MARK_ADD |\ + FAN_MARK_REMOVE |\ + FAN_MARK_DONT_FOLLOW |\ + FAN_MARK_ONLYDIR) + /* * All of the events - we build the list by hand so that we can add flags in * the future and not break backward compatibility. Apps will get only the -- cgit v1.2.3 From a1014f102322398e67524b68b3300acf384e6c1f Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:26 -0500 Subject: fanotify: send events using read Send events to userspace by reading the file descriptor from fanotify_init(). One will get blocks of data which look like: struct fanotify_event_metadata { __u32 event_len; __u32 vers; __s32 fd; __u64 mask; __s64 pid; __u64 cookie; } __attribute__ ((packed)); Simple code to retrieve and deal with events is below while ((len = read(fan_fd, buf, sizeof(buf))) > 0) { struct fanotify_event_metadata *metadata; metadata = (void *)buf; while(FAN_EVENT_OK(metadata, len)) { [PROCESS HERE!!] if (metadata->fd >= 0 && close(metadata->fd) != 0) goto fail; metadata = FAN_EVENT_NEXT(metadata, len); } } Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify.h | 5 + fs/notify/fanotify/fanotify_user.c | 220 ++++++++++++++++++++++++++++++++++++- include/linux/fanotify.h | 24 ++++ 3 files changed, 245 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify.h b/fs/notify/fanotify/fanotify.h index 59c3331a0e81..5608783c6bca 100644 --- a/fs/notify/fanotify/fanotify.h +++ b/fs/notify/fanotify/fanotify.h @@ -30,3 +30,8 @@ static inline bool fanotify_mask_valid(__u32 mask) return false; return true; } + +static inline __u32 fanotify_outgoing_mask(__u32 mask) +{ + return mask & FAN_ALL_OUTGOING_EVENTS; +} diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index bc4fa48157f1..a99550f83f8a 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -4,15 +4,202 @@ #include #include #include +#include #include +#include #include #include #include +#include + +#include #include "fanotify.h" static struct kmem_cache *fanotify_mark_cache __read_mostly; +/* + * Get an fsnotify notification event if one exists and is small + * enough to fit in "count". Return an error pointer if the count + * is not large enough. + * + * Called with the group->notification_mutex held. + */ +static struct fsnotify_event *get_one_event(struct fsnotify_group *group, + size_t count) +{ + BUG_ON(!mutex_is_locked(&group->notification_mutex)); + + pr_debug("%s: group=%p count=%zd\n", __func__, group, count); + + if (fsnotify_notify_queue_is_empty(group)) + return NULL; + + if (FAN_EVENT_METADATA_LEN > count) + return ERR_PTR(-EINVAL); + + /* held the notification_mutex the whole time, so this is the + * same event we peeked above */ + return fsnotify_remove_notify_event(group); +} + +static int create_and_fill_fd(struct fsnotify_group *group, + struct fanotify_event_metadata *metadata, + struct fsnotify_event *event) +{ + int client_fd; + struct dentry *dentry; + struct vfsmount *mnt; + struct file *new_file; + + pr_debug("%s: group=%p metadata=%p event=%p\n", __func__, group, + metadata, event); + + client_fd = get_unused_fd(); + if (client_fd < 0) + return client_fd; + + if (event->data_type != FSNOTIFY_EVENT_PATH) { + WARN_ON(1); + put_unused_fd(client_fd); + return -EINVAL; + } + + /* + * we need a new file handle for the userspace program so it can read even if it was + * originally opened O_WRONLY. + */ + dentry = dget(event->path.dentry); + mnt = mntget(event->path.mnt); + /* it's possible this event was an overflow event. in that case dentry and mnt + * are NULL; That's fine, just don't call dentry open */ + if (dentry && mnt) + new_file = dentry_open(dentry, mnt, + O_RDONLY | O_LARGEFILE | FMODE_NONOTIFY, + current_cred()); + else + new_file = ERR_PTR(-EOVERFLOW); + if (IS_ERR(new_file)) { + /* + * we still send an event even if we can't open the file. this + * can happen when say tasks are gone and we try to open their + * /proc files or we try to open a WRONLY file like in sysfs + * we just send the errno to userspace since there isn't much + * else we can do. + */ + put_unused_fd(client_fd); + client_fd = PTR_ERR(new_file); + } else { + fd_install(client_fd, new_file); + } + + metadata->fd = client_fd; + + return 0; +} + +static ssize_t fill_event_metadata(struct fsnotify_group *group, + struct fanotify_event_metadata *metadata, + struct fsnotify_event *event) +{ + pr_debug("%s: group=%p metadata=%p event=%p\n", __func__, + group, metadata, event); + + metadata->event_len = FAN_EVENT_METADATA_LEN; + metadata->vers = FANOTIFY_METADATA_VERSION; + metadata->mask = fanotify_outgoing_mask(event->mask); + + return create_and_fill_fd(group, metadata, event); + +} + +static ssize_t copy_event_to_user(struct fsnotify_group *group, + struct fsnotify_event *event, + char __user *buf) +{ + struct fanotify_event_metadata fanotify_event_metadata; + int ret; + + pr_debug("%s: group=%p event=%p\n", __func__, group, event); + + ret = fill_event_metadata(group, &fanotify_event_metadata, event); + if (ret) + return ret; + + if (copy_to_user(buf, &fanotify_event_metadata, FAN_EVENT_METADATA_LEN)) + return -EFAULT; + + return FAN_EVENT_METADATA_LEN; +} + +/* intofiy userspace file descriptor functions */ +static unsigned int fanotify_poll(struct file *file, poll_table *wait) +{ + struct fsnotify_group *group = file->private_data; + int ret = 0; + + poll_wait(file, &group->notification_waitq, wait); + mutex_lock(&group->notification_mutex); + if (!fsnotify_notify_queue_is_empty(group)) + ret = POLLIN | POLLRDNORM; + mutex_unlock(&group->notification_mutex); + + return ret; +} + +static ssize_t fanotify_read(struct file *file, char __user *buf, + size_t count, loff_t *pos) +{ + struct fsnotify_group *group; + struct fsnotify_event *kevent; + char __user *start; + int ret; + DEFINE_WAIT(wait); + + start = buf; + group = file->private_data; + + pr_debug("%s: group=%p\n", __func__, group); + + while (1) { + prepare_to_wait(&group->notification_waitq, &wait, TASK_INTERRUPTIBLE); + + mutex_lock(&group->notification_mutex); + kevent = get_one_event(group, count); + mutex_unlock(&group->notification_mutex); + + if (kevent) { + ret = PTR_ERR(kevent); + if (IS_ERR(kevent)) + break; + ret = copy_event_to_user(group, kevent, buf); + fsnotify_put_event(kevent); + if (ret < 0) + break; + buf += ret; + count -= ret; + continue; + } + + ret = -EAGAIN; + if (file->f_flags & O_NONBLOCK) + break; + ret = -EINTR; + if (signal_pending(current)) + break; + + if (start != buf) + break; + + schedule(); + } + + finish_wait(&group->notification_waitq, &wait); + if (start != buf && ret != -EFAULT) + ret = buf - start; + return ret; +} + static int fanotify_release(struct inode *ignored, struct file *file) { struct fsnotify_group *group = file->private_data; @@ -25,13 +212,38 @@ static int fanotify_release(struct inode *ignored, struct file *file) return 0; } +static long fanotify_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct fsnotify_group *group; + struct fsnotify_event_holder *holder; + void __user *p; + int ret = -ENOTTY; + size_t send_len = 0; + + group = file->private_data; + + p = (void __user *) arg; + + switch (cmd) { + case FIONREAD: + mutex_lock(&group->notification_mutex); + list_for_each_entry(holder, &group->notification_list, event_list) + send_len += FAN_EVENT_METADATA_LEN; + mutex_unlock(&group->notification_mutex); + ret = put_user(send_len, (int __user *) p); + break; + } + + return ret; +} + static const struct file_operations fanotify_fops = { - .poll = NULL, - .read = NULL, + .poll = fanotify_poll, + .read = fanotify_read, .fasync = NULL, .release = fanotify_release, - .unlocked_ioctl = NULL, - .compat_ioctl = NULL, + .unlocked_ioctl = fanotify_ioctl, + .compat_ioctl = fanotify_ioctl, }; static void fanotify_free_mark(struct fsnotify_mark *fsn_mark) diff --git a/include/linux/fanotify.h b/include/linux/fanotify.h index 95aeea2a3ca6..c1c66162a46c 100644 --- a/include/linux/fanotify.h +++ b/include/linux/fanotify.h @@ -51,6 +51,30 @@ */ #define FAN_ALL_INCOMING_EVENTS (FAN_ALL_EVENTS |\ FAN_EVENT_ON_CHILD) + +#define FAN_ALL_OUTGOING_EVENTS (FAN_ALL_EVENTS |\ + FAN_Q_OVERFLOW) + +#define FANOTIFY_METADATA_VERSION 1 + +struct fanotify_event_metadata { + __u32 event_len; + __u32 vers; + __s32 fd; + __u64 mask; +} __attribute__ ((packed)); + +/* Helper functions to deal with fanotify_event_metadata buffers */ +#define FAN_EVENT_METADATA_LEN (sizeof(struct fanotify_event_metadata)) + +#define FAN_EVENT_NEXT(meta, len) ((len) -= (meta)->event_len, \ + (struct fanotify_event_metadata*)(((char *)(meta)) + \ + (meta)->event_len)) + +#define FAN_EVENT_OK(meta, len) ((long)(len) >= (long)FAN_EVENT_METADATA_LEN && \ + (long)(meta)->event_len >= (long)FAN_EVENT_METADATA_LEN && \ + (long)(meta)->event_len <= (long)(len)) + #ifdef __KERNEL__ #endif /* __KERNEL__ */ -- cgit v1.2.3 From ef601a9cfd21fe9ce57e0ee3f4a31552ffb96366 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Thu, 17 Dec 2009 21:24:26 -0500 Subject: fanotify: select ANON_INODES. fanotify references anon_inode_getfd(), which is only available with ANON_INODES enabled. Presently this bails out with the following: LD vmlinux fs/built-in.o: In function `sys_fanotify_init': (.text+0x26d1c): undefined reference to `anon_inode_getfd' make: *** [vmlinux] Error 1 which is trivially corrected by adding an ANON_INODES select. Signed-off-by: Paul Mundt Signed-off-by: Eric Paris --- fs/notify/fanotify/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'fs') diff --git a/fs/notify/fanotify/Kconfig b/fs/notify/fanotify/Kconfig index f9d7ae081f85..668e5df28e28 100644 --- a/fs/notify/fanotify/Kconfig +++ b/fs/notify/fanotify/Kconfig @@ -1,6 +1,7 @@ config FANOTIFY bool "Filesystem wide access notification" select FSNOTIFY + select ANON_INODES default y ---help--- Say Y here to enable fanotify suport. fanotify is a file access -- cgit v1.2.3 From 9bbfc964b89009d0cadcec7027afc92ee742e95e Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Thu, 17 Dec 2009 21:24:26 -0500 Subject: fanotify: CONFIG_HAVE_SYSCALL_WRAPPERS for sys_fanotify_mark Please note that you need the patch below in addition, otherwise the syscall wrapper stuff won't work on those 32 bit architectures which enable the wrappers. When enabled the syscall wrapper defines always take long parameters and then cast them to whatever is needed. This approach doesn't work for the 32 bit case where the original syscall takes a long long parameter, since we would lose the upper 32 bits. So syscalls with 64 bit arguments are special cases wrt to syscall wrappers and enp up in the ugliness below (see also sys_fallocate). In addition these special cased syscall wrappers have the drawback that ftrace syscall tracing doesn't work on them, since they don't get defined by using the usual macros. Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify_user.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index a99550f83f8a..a9ced3feb0bb 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -468,8 +468,9 @@ out_put_group: return fd; } -SYSCALL_DEFINE5(fanotify_mark, int, fanotify_fd, unsigned int, flags, - __u64, mask, int, dfd, const char __user *, pathname) +SYSCALL_DEFINE(fanotify_mark)(int fanotify_fd, unsigned int flags, + __u64 mask, int dfd, + const char __user * pathname) { struct inode *inode; struct fsnotify_group *group; @@ -513,6 +514,17 @@ fput_and_out: return ret; } +#ifdef CONFIG_HAVE_SYSCALL_WRAPPERS +asmlinkage long SyS_fanotify_mark(long fanotify_fd, long flags, __u64 mask, + long dfd, long pathname) +{ + return SYSC_fanotify_mark((int) fanotify_fd, (unsigned int) flags, + mask, (int) dfd, + (const char __user *) pathname); +} +SYSCALL_ALIAS(sys_fanotify_mark, SyS_fanotify_mark); +#endif + /* * fanotify_user_setup - Our initialization function. Note that we cannnot return * error because we have compiled-in VFS hooks. So an (unlikely) failure here -- cgit v1.2.3 From 22aa425dec9e47051624714ae283eb2b6a473013 Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Thu, 17 Dec 2009 21:24:26 -0500 Subject: fanotify: create_fd cleanup Code cleanup which does the fd creation work seperately from the userspace metadata creation. It fits better with the other code. Signed-off-by: Andreas Gruenbacher Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify_user.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index a9ced3feb0bb..cf9c30009825 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -43,17 +43,14 @@ static struct fsnotify_event *get_one_event(struct fsnotify_group *group, return fsnotify_remove_notify_event(group); } -static int create_and_fill_fd(struct fsnotify_group *group, - struct fanotify_event_metadata *metadata, - struct fsnotify_event *event) +static int create_fd(struct fsnotify_group *group, struct fsnotify_event *event) { int client_fd; struct dentry *dentry; struct vfsmount *mnt; struct file *new_file; - pr_debug("%s: group=%p metadata=%p event=%p\n", __func__, group, - metadata, event); + pr_debug("%s: group=%p event=%p\n", __func__, group, event); client_fd = get_unused_fd(); if (client_fd < 0) @@ -93,9 +90,7 @@ static int create_and_fill_fd(struct fsnotify_group *group, fd_install(client_fd, new_file); } - metadata->fd = client_fd; - - return 0; + return client_fd; } static ssize_t fill_event_metadata(struct fsnotify_group *group, @@ -108,9 +103,9 @@ static ssize_t fill_event_metadata(struct fsnotify_group *group, metadata->event_len = FAN_EVENT_METADATA_LEN; metadata->vers = FANOTIFY_METADATA_VERSION; metadata->mask = fanotify_outgoing_mask(event->mask); + metadata->fd = create_fd(group, event); - return create_and_fill_fd(group, metadata, event); - + return metadata->fd; } static ssize_t copy_event_to_user(struct fsnotify_group *group, @@ -123,7 +118,7 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group, pr_debug("%s: group=%p event=%p\n", __func__, group, event); ret = fill_event_metadata(group, &fanotify_event_metadata, event); - if (ret) + if (ret < 0) return ret; if (copy_to_user(buf, &fanotify_event_metadata, FAN_EVENT_METADATA_LEN)) -- cgit v1.2.3 From 32c3263221bd63316815286dccacdc7abfd7f3c4 Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Thu, 17 Dec 2009 21:24:27 -0500 Subject: fanotify: Add pids to events Pass the process identifiers of the triggering processes to fanotify listeners: this information is useful for event filtering and logging. Signed-off-by: Andreas Gruenbacher Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify.c | 5 +++-- fs/notify/fanotify/fanotify_user.c | 1 + fs/notify/notification.c | 3 +++ include/linux/fanotify.h | 1 + include/linux/fsnotify_backend.h | 1 + 5 files changed, 9 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index 5b0b6b485a9c..881067dc7923 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -10,8 +10,9 @@ static bool should_merge(struct fsnotify_event *old, struct fsnotify_event *new) { pr_debug("%s: old=%p new=%p\n", __func__, old, new); - if ((old->to_tell == new->to_tell) && - (old->data_type == new->data_type)) { + if (old->to_tell == new->to_tell && + old->data_type == new->data_type && + old->tgid == new->tgid) { switch (old->data_type) { case (FSNOTIFY_EVENT_PATH): if ((old->path.mnt == new->path.mnt) && diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index cf9c30009825..66e38fc052b2 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -103,6 +103,7 @@ static ssize_t fill_event_metadata(struct fsnotify_group *group, metadata->event_len = FAN_EVENT_METADATA_LEN; metadata->vers = FANOTIFY_METADATA_VERSION; metadata->mask = fanotify_outgoing_mask(event->mask); + metadata->pid = pid_vnr(event->tgid); metadata->fd = create_fd(group, event); return metadata->fd; diff --git a/fs/notify/notification.c b/fs/notify/notification.c index 066f1f988bac..7fc8d004084c 100644 --- a/fs/notify/notification.c +++ b/fs/notify/notification.c @@ -93,6 +93,7 @@ void fsnotify_put_event(struct fsnotify_event *event) BUG_ON(!list_empty(&event->private_data_list)); kfree(event->file_name); + put_pid(event->tgid); kmem_cache_free(fsnotify_event_cachep, event); } } @@ -346,6 +347,7 @@ struct fsnotify_event *fsnotify_clone_event(struct fsnotify_event *old_event) return NULL; } } + event->tgid = get_pid(old_event->tgid); if (event->data_type == FSNOTIFY_EVENT_PATH) path_get(&event->path); @@ -385,6 +387,7 @@ struct fsnotify_event *fsnotify_create_event(struct inode *to_tell, __u32 mask, event->name_len = strlen(event->file_name); } + event->tgid = get_pid(task_tgid(current)); event->sync_cookie = cookie; event->to_tell = to_tell; event->data_type = data_type; diff --git a/include/linux/fanotify.h b/include/linux/fanotify.h index c1c66162a46c..5f633af4d1b0 100644 --- a/include/linux/fanotify.h +++ b/include/linux/fanotify.h @@ -62,6 +62,7 @@ struct fanotify_event_metadata { __u32 vers; __s32 fd; __u64 mask; + __s64 pid; } __attribute__ ((packed)); /* Helper functions to deal with fanotify_event_metadata buffers */ diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index ff654c1932f2..7d93572ec568 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -221,6 +221,7 @@ struct fsnotify_event { u32 sync_cookie; /* used to corrolate events, namely inotify mv events */ char *file_name; size_t name_len; + struct pid *tgid; struct list_head private_data_list; /* groups can store private data here */ }; -- cgit v1.2.3 From 5444e2981c31d0ed7465475e451b8437084337e5 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:27 -0500 Subject: fsnotify: split generic and inode specific mark code currently all marking is done by functions in inode-mark.c. Some of this is pretty generic and should be instead done in a generic function and we should only put the inode specific code in inode-mark.c Signed-off-by: Eric Paris --- fs/notify/Makefile | 3 +- fs/notify/dnotify/dnotify.c | 12 +- fs/notify/fanotify/fanotify.c | 2 +- fs/notify/fanotify/fanotify_user.c | 8 +- fs/notify/fsnotify.h | 7 + fs/notify/inode_mark.c | 246 +++-------------------------- fs/notify/inotify/inotify_fsnotify.c | 4 +- fs/notify/inotify/inotify_user.c | 4 +- fs/notify/mark.c | 294 +++++++++++++++++++++++++++++++++++ include/linux/fsnotify_backend.h | 5 +- kernel/audit_tree.c | 8 +- kernel/audit_watch.c | 6 +- 12 files changed, 347 insertions(+), 252 deletions(-) create mode 100644 fs/notify/mark.c (limited to 'fs') diff --git a/fs/notify/Makefile b/fs/notify/Makefile index 396a38779371..8f7f3b024a2e 100644 --- a/fs/notify/Makefile +++ b/fs/notify/Makefile @@ -1,4 +1,5 @@ -obj-$(CONFIG_FSNOTIFY) += fsnotify.o notification.o group.o inode_mark.o +obj-$(CONFIG_FSNOTIFY) += fsnotify.o notification.o group.o inode_mark.o \ + mark.o obj-y += dnotify/ obj-y += inotify/ diff --git a/fs/notify/dnotify/dnotify.c b/fs/notify/dnotify/dnotify.c index cac2eb896639..69f42df9ba45 100644 --- a/fs/notify/dnotify/dnotify.c +++ b/fs/notify/dnotify/dnotify.c @@ -95,7 +95,7 @@ static int dnotify_handle_event(struct fsnotify_group *group, to_tell = event->to_tell; - fsn_mark = fsnotify_find_mark(group, to_tell); + fsn_mark = fsnotify_find_inode_mark(group, to_tell); if (unlikely(!fsn_mark)) return 0; dn_mark = container_of(fsn_mark, struct dnotify_mark, fsn_mark); @@ -143,14 +143,14 @@ static bool dnotify_should_send_event(struct fsnotify_group *group, if (!S_ISDIR(inode->i_mode)) return false; - fsn_mark = fsnotify_find_mark(group, inode); + fsn_mark = fsnotify_find_inode_mark(group, inode); if (!fsn_mark) return false; mask = (mask & ~FS_EVENT_ON_CHILD); send = (mask & fsn_mark->mask); - fsnotify_put_mark(fsn_mark); /* matches fsnotify_find_mark */ + fsnotify_put_mark(fsn_mark); /* matches fsnotify_find_inode_mark */ return send; } @@ -193,7 +193,7 @@ void dnotify_flush(struct file *filp, fl_owner_t id) if (!S_ISDIR(inode->i_mode)) return; - fsn_mark = fsnotify_find_mark(dnotify_group, inode); + fsn_mark = fsnotify_find_inode_mark(dnotify_group, inode); if (!fsn_mark) return; dn_mark = container_of(fsn_mark, struct dnotify_mark, fsn_mark); @@ -346,12 +346,12 @@ int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg) mutex_lock(&dnotify_mark_mutex); /* add the new_fsn_mark or find an old one. */ - fsn_mark = fsnotify_find_mark(dnotify_group, inode); + fsn_mark = fsnotify_find_inode_mark(dnotify_group, inode); if (fsn_mark) { dn_mark = container_of(fsn_mark, struct dnotify_mark, fsn_mark); spin_lock(&fsn_mark->lock); } else { - fsnotify_add_mark(new_fsn_mark, dnotify_group, inode, 0); + fsnotify_add_mark(new_fsn_mark, dnotify_group, inode, NULL, 0); spin_lock(&new_fsn_mark->lock); fsn_mark = new_fsn_mark; dn_mark = new_dn_mark; diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index 881067dc7923..aa5e92661142 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -118,7 +118,7 @@ static bool fanotify_should_send_event(struct fsnotify_group *group, struct inod if (data_type != FSNOTIFY_EVENT_PATH) return false; - fsn_mark = fsnotify_find_mark(group, inode); + fsn_mark = fsnotify_find_inode_mark(group, inode); if (!fsn_mark) return false; diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 66e38fc052b2..05351936a725 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -305,7 +305,7 @@ static int fanotify_remove_mark(struct fsnotify_group *group, pr_debug("%s: group=%p inode=%p mask=%x\n", __func__, group, inode, mask); - fsn_mark = fsnotify_find_mark(group, inode); + fsn_mark = fsnotify_find_inode_mark(group, inode); if (!fsn_mark) return -ENOENT; @@ -321,7 +321,7 @@ static int fanotify_remove_mark(struct fsnotify_group *group, fsnotify_recalc_group_mask(group); - /* matches the fsnotify_find_mark() */ + /* matches the fsnotify_find_inode_mark() */ fsnotify_put_mark(fsn_mark); return 0; @@ -338,7 +338,7 @@ static int fanotify_add_mark(struct fsnotify_group *group, pr_debug("%s: group=%p inode=%p mask=%x\n", __func__, group, inode, mask); - fsn_mark = fsnotify_find_mark(group, inode); + fsn_mark = fsnotify_find_inode_mark(group, inode); if (!fsn_mark) { struct fsnotify_mark *new_fsn_mark; @@ -348,7 +348,7 @@ static int fanotify_add_mark(struct fsnotify_group *group, goto out; fsnotify_init_mark(new_fsn_mark, fanotify_free_mark); - ret = fsnotify_add_mark(new_fsn_mark, group, inode, 0); + ret = fsnotify_add_mark(new_fsn_mark, group, inode, NULL, 0); if (ret) { fanotify_free_mark(new_fsn_mark); goto out; diff --git a/fs/notify/fsnotify.h b/fs/notify/fsnotify.h index 2ba59158969f..7c7a904b802d 100644 --- a/fs/notify/fsnotify.h +++ b/fs/notify/fsnotify.h @@ -20,6 +20,11 @@ extern __u32 fsnotify_vfsmount_mask; /* destroy all events sitting in this groups notification queue */ extern void fsnotify_flush_notify(struct fsnotify_group *group); +/* add a mark to an inode */ +extern int fsnotify_add_inode_mark(struct fsnotify_mark *mark, + struct fsnotify_group *group, struct inode *inode, + int allow_dups); + /* add a group to the inode group list */ extern void fsnotify_add_inode_group(struct fsnotify_group *group); /* add a group to the vfsmount group list */ @@ -27,6 +32,8 @@ extern void fsnotify_add_vfsmount_group(struct fsnotify_group *group); /* final kfree of a group */ extern void fsnotify_final_destroy_group(struct fsnotify_group *group); +/* inode specific destruction of a mark */ +extern void fsnotify_destroy_inode_mark(struct fsnotify_mark *mark); /* run the list of all marks associated with inode and flag them to be freed */ extern void fsnotify_clear_marks_by_inode(struct inode *inode); /* diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c index ba6f9833561b..c925579ba011 100644 --- a/fs/notify/inode_mark.c +++ b/fs/notify/inode_mark.c @@ -16,72 +16,6 @@ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ -/* - * fsnotify inode mark locking/lifetime/and refcnting - * - * REFCNT: - * The mark->refcnt tells how many "things" in the kernel currently are - * referencing this object. The object typically will live inside the kernel - * with a refcnt of 2, one for each list it is on (i_list, g_list). Any task - * which can find this object holding the appropriete locks, can take a reference - * and the object itself is guarenteed to survive until the reference is dropped. - * - * LOCKING: - * There are 3 spinlocks involved with fsnotify inode marks and they MUST - * be taken in order as follows: - * - * mark->lock - * group->mark_lock - * inode->i_lock - * - * mark->lock protects 2 things, mark->group and mark->inode. You must hold - * that lock to dereference either of these things (they could be NULL even with - * the lock) - * - * group->mark_lock protects the marks_list anchored inside a given group - * and each mark is hooked via the g_list. It also sorta protects the - * free_g_list, which when used is anchored by a private list on the stack of the - * task which held the group->mark_lock. - * - * inode->i_lock protects the i_fsnotify_marks list anchored inside a - * given inode and each mark is hooked via the i_list. (and sorta the - * free_i_list) - * - * - * LIFETIME: - * Inode marks survive between when they are added to an inode and when their - * refcnt==0. - * - * The inode mark can be cleared for a number of different reasons including: - * - The inode is unlinked for the last time. (fsnotify_inode_remove) - * - The inode is being evicted from cache. (fsnotify_inode_delete) - * - The fs the inode is on is unmounted. (fsnotify_inode_delete/fsnotify_unmount_inodes) - * - Something explicitly requests that it be removed. (fsnotify_destroy_mark) - * - The fsnotify_group associated with the mark is going away and all such marks - * need to be cleaned up. (fsnotify_clear_marks_by_group) - * - * Worst case we are given an inode and need to clean up all the marks on that - * inode. We take i_lock and walk the i_fsnotify_marks safely. For each - * mark on the list we take a reference (so the mark can't disappear under us). - * We remove that mark form the inode's list of marks and we add this mark to a - * private list anchored on the stack using i_free_list; At this point we no - * longer fear anything finding the mark using the inode's list of marks. - * - * We can safely and locklessly run the private list on the stack of everything - * we just unattached from the original inode. For each mark on the private list - * we grab the mark-> and can thus dereference mark->group and mark->inode. If - * we see the group and inode are not NULL we take those locks. Now holding all - * 3 locks we can completely remove the mark from other tasks finding it in the - * future. Remember, 10 things might already be referencing this mark, but they - * better be holding a ref. We drop our reference we took before we unhooked it - * from the inode. When the ref hits 0 we can free the mark. - * - * Very similarly for freeing by group, except we use free_g_list. - * - * This has the very interesting property of being able to run concurrently with - * any (or all) other directions. - */ - #include #include #include @@ -95,17 +29,6 @@ #include #include "fsnotify.h" -void fsnotify_get_mark(struct fsnotify_mark *mark) -{ - atomic_inc(&mark->refcnt); -} - -void fsnotify_put_mark(struct fsnotify_mark *mark) -{ - if (atomic_dec_and_test(&mark->refcnt)) - mark->free_mark(mark); -} - /* * Recalculate the mask of events relevant to a given inode locked. */ @@ -135,44 +58,18 @@ void fsnotify_recalc_inode_mask(struct inode *inode) __fsnotify_update_child_dentry_flags(inode); } -/* - * Any time a mark is getting freed we end up here. - * The caller had better be holding a reference to this mark so we don't actually - * do the final put under the mark->lock - */ -void fsnotify_destroy_mark(struct fsnotify_mark *mark) +void fsnotify_destroy_inode_mark(struct fsnotify_mark *mark) { - struct fsnotify_group *group; - struct inode *inode; - - spin_lock(&mark->lock); + struct inode *inode = mark->i.inode; - group = mark->group; - inode = mark->i.inode; + assert_spin_locked(&mark->lock); + assert_spin_locked(&mark->group->mark_lock); - BUG_ON(group && !inode); - BUG_ON(!group && inode); - - /* if !group something else already marked this to die */ - if (!group) { - spin_unlock(&mark->lock); - return; - } - - /* 1 from caller and 1 for being on i_list/g_list */ - BUG_ON(atomic_read(&mark->refcnt) < 2); - - spin_lock(&group->mark_lock); spin_lock(&inode->i_lock); hlist_del_init(&mark->i.i_list); mark->i.inode = NULL; - list_del_init(&mark->g_list); - mark->group = NULL; - - fsnotify_put_mark(mark); /* for i_list and g_list */ - /* * this mark is now off the inode->i_fsnotify_marks list and we * hold the inode->i_lock, so this is the perfect time to update the @@ -181,61 +78,6 @@ void fsnotify_destroy_mark(struct fsnotify_mark *mark) fsnotify_recalc_inode_mask_locked(inode); spin_unlock(&inode->i_lock); - spin_unlock(&group->mark_lock); - spin_unlock(&mark->lock); - - /* - * Some groups like to know that marks are being freed. This is a - * callback to the group function to let it know that this mark - * is being freed. - */ - if (group->ops->freeing_mark) - group->ops->freeing_mark(mark, group); - - /* - * __fsnotify_update_child_dentry_flags(inode); - * - * I really want to call that, but we can't, we have no idea if the inode - * still exists the second we drop the mark->lock. - * - * The next time an event arrive to this inode from one of it's children - * __fsnotify_parent will see that the inode doesn't care about it's - * children and will update all of these flags then. So really this - * is just a lazy update (and could be a perf win...) - */ - - - iput(inode); - - /* - * it's possible that this group tried to destroy itself, but this - * this mark was simultaneously being freed by inode. If that's the - * case, we finish freeing the group here. - */ - if (unlikely(atomic_dec_and_test(&group->num_marks))) - fsnotify_final_destroy_group(group); -} - -/* - * Given a group, destroy all of the marks associated with that group. - */ -void fsnotify_clear_marks_by_group(struct fsnotify_group *group) -{ - struct fsnotify_mark *lmark, *mark; - LIST_HEAD(free_list); - - spin_lock(&group->mark_lock); - list_for_each_entry_safe(mark, lmark, &group->marks_list, g_list) { - list_add(&mark->free_g_list, &free_list); - list_del_init(&mark->g_list); - fsnotify_get_mark(mark); - } - spin_unlock(&group->mark_lock); - - list_for_each_entry_safe(mark, lmark, &free_list, free_g_list) { - fsnotify_destroy_mark(mark); - fsnotify_put_mark(mark); - } } /* @@ -261,8 +103,12 @@ void fsnotify_clear_marks_by_inode(struct inode *inode) } } -static struct fsnotify_mark *fsnotify_find_mark_locked(struct fsnotify_group *group, - struct inode *inode) +/* + * given a group and inode, find the mark associated with that combination. + * if found take a reference to that mark and return it, else return NULL + */ +struct fsnotify_mark *fsnotify_find_inode_mark_locked(struct fsnotify_group *group, + struct inode *inode) { struct fsnotify_mark *mark; struct hlist_node *pos; @@ -282,50 +128,26 @@ static struct fsnotify_mark *fsnotify_find_mark_locked(struct fsnotify_group *gr * given a group and inode, find the mark associated with that combination. * if found take a reference to that mark and return it, else return NULL */ -struct fsnotify_mark *fsnotify_find_mark(struct fsnotify_group *group, - struct inode *inode) +struct fsnotify_mark *fsnotify_find_inode_mark(struct fsnotify_group *group, + struct inode *inode) { struct fsnotify_mark *mark; spin_lock(&inode->i_lock); - mark = fsnotify_find_mark_locked(group, inode); + mark = fsnotify_find_inode_mark_locked(group, inode); spin_unlock(&inode->i_lock); return mark; } -void fsnotify_duplicate_mark(struct fsnotify_mark *new, struct fsnotify_mark *old) -{ - assert_spin_locked(&old->lock); - new->i.inode = old->i.inode; - new->group = old->group; - new->mask = old->mask; - new->free_mark = old->free_mark; -} - -/* - * Nothing fancy, just initialize lists and locks and counters. - */ -void fsnotify_init_mark(struct fsnotify_mark *mark, - void (*free_mark)(struct fsnotify_mark *mark)) -{ - spin_lock_init(&mark->lock); - atomic_set(&mark->refcnt, 1); - INIT_HLIST_NODE(&mark->i.i_list); - mark->group = NULL; - mark->mask = 0; - mark->i.inode = NULL; - mark->free_mark = free_mark; -} - /* * Attach an initialized mark mark to a given group and inode. * These marks may be used for the fsnotify backend to determine which * event types should be delivered to which group and for which inodes. */ -int fsnotify_add_mark(struct fsnotify_mark *mark, - struct fsnotify_group *group, struct inode *inode, - int allow_dups) +int fsnotify_add_inode_mark(struct fsnotify_mark *mark, + struct fsnotify_group *group, struct inode *inode, + int allow_dups) { struct fsnotify_mark *lmark = NULL; int ret = 0; @@ -336,56 +158,26 @@ int fsnotify_add_mark(struct fsnotify_mark *mark, mark->flags = FSNOTIFY_MARK_FLAG_INODE; - /* - * if this group isn't being testing for inode type events we need - * to start testing - */ - if (unlikely(list_empty(&group->inode_group_list))) - fsnotify_add_inode_group(group); - /* - * XXX This is where we could also do the fsnotify_add_vfsmount_group - * if we are setting and vfsmount mark.... - - if (unlikely(list_empty(&group->vfsmount_group_list))) - fsnotify_add_vfsmount_group(group); - */ + assert_spin_locked(&mark->lock); + assert_spin_locked(&group->mark_lock); - /* - * LOCKING ORDER!!!! - * mark->lock - * group->mark_lock - * inode->i_lock - */ - spin_lock(&mark->lock); - spin_lock(&group->mark_lock); spin_lock(&inode->i_lock); if (!allow_dups) - lmark = fsnotify_find_mark_locked(group, inode); + lmark = fsnotify_find_inode_mark_locked(group, inode); if (!lmark) { - mark->group = group; mark->i.inode = inode; hlist_add_head(&mark->i.i_list, &inode->i_fsnotify_marks); - list_add(&mark->g_list, &group->marks_list); - - fsnotify_get_mark(mark); /* for i_list and g_list */ - - atomic_inc(&group->num_marks); fsnotify_recalc_inode_mask_locked(inode); } spin_unlock(&inode->i_lock); - spin_unlock(&group->mark_lock); - spin_unlock(&mark->lock); if (lmark) { ret = -EEXIST; iput(inode); - fsnotify_put_mark(lmark); - } else { - __fsnotify_update_child_dentry_flags(inode); } return ret; diff --git a/fs/notify/inotify/inotify_fsnotify.c b/fs/notify/inotify/inotify_fsnotify.c index cc8f6bcbb4a3..1d237e1bf7b1 100644 --- a/fs/notify/inotify/inotify_fsnotify.c +++ b/fs/notify/inotify/inotify_fsnotify.c @@ -97,7 +97,7 @@ static int inotify_handle_event(struct fsnotify_group *group, struct fsnotify_ev to_tell = event->to_tell; - fsn_mark = fsnotify_find_mark(group, to_tell); + fsn_mark = fsnotify_find_inode_mark(group, to_tell); /* race with watch removal? We already passes should_send */ if (unlikely(!fsn_mark)) return 0; @@ -145,7 +145,7 @@ static bool inotify_should_send_event(struct fsnotify_group *group, struct inode struct fsnotify_mark *fsn_mark; bool send; - fsn_mark = fsnotify_find_mark(group, inode); + fsn_mark = fsnotify_find_inode_mark(group, inode); if (!fsn_mark) return false; diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index ad5a1ea7827e..a12315a7553d 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -566,7 +566,7 @@ static int inotify_update_existing_watch(struct fsnotify_group *group, if (unlikely(!mask)) return -EINVAL; - fsn_mark = fsnotify_find_mark(group, inode); + fsn_mark = fsnotify_find_inode_mark(group, inode); if (!fsn_mark) return -ENOENT; @@ -644,7 +644,7 @@ static int inotify_new_watch(struct fsnotify_group *group, goto out_err; /* we are on the idr, now get on the inode */ - ret = fsnotify_add_mark(&tmp_i_mark->fsn_mark, group, inode, 0); + ret = fsnotify_add_mark(&tmp_i_mark->fsn_mark, group, inode, NULL, 0); if (ret) { /* we failed to get on the inode, get off the idr */ inotify_remove_from_idr(group, tmp_i_mark); diff --git a/fs/notify/mark.c b/fs/notify/mark.c new file mode 100644 index 000000000000..e56e8768d676 --- /dev/null +++ b/fs/notify/mark.c @@ -0,0 +1,294 @@ +/* + * Copyright (C) 2008 Red Hat, Inc., Eric Paris + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * fsnotify inode mark locking/lifetime/and refcnting + * + * REFCNT: + * The mark->refcnt tells how many "things" in the kernel currently are + * referencing this object. The object typically will live inside the kernel + * with a refcnt of 2, one for each list it is on (i_list, g_list). Any task + * which can find this object holding the appropriete locks, can take a reference + * and the object itself is guarenteed to survive until the reference is dropped. + * + * LOCKING: + * There are 3 spinlocks involved with fsnotify inode marks and they MUST + * be taken in order as follows: + * + * mark->lock + * group->mark_lock + * inode->i_lock + * + * mark->lock protects 2 things, mark->group and mark->inode. You must hold + * that lock to dereference either of these things (they could be NULL even with + * the lock) + * + * group->mark_lock protects the marks_list anchored inside a given group + * and each mark is hooked via the g_list. It also sorta protects the + * free_g_list, which when used is anchored by a private list on the stack of the + * task which held the group->mark_lock. + * + * inode->i_lock protects the i_fsnotify_marks list anchored inside a + * given inode and each mark is hooked via the i_list. (and sorta the + * free_i_list) + * + * + * LIFETIME: + * Inode marks survive between when they are added to an inode and when their + * refcnt==0. + * + * The inode mark can be cleared for a number of different reasons including: + * - The inode is unlinked for the last time. (fsnotify_inode_remove) + * - The inode is being evicted from cache. (fsnotify_inode_delete) + * - The fs the inode is on is unmounted. (fsnotify_inode_delete/fsnotify_unmount_inodes) + * - Something explicitly requests that it be removed. (fsnotify_destroy_mark) + * - The fsnotify_group associated with the mark is going away and all such marks + * need to be cleaned up. (fsnotify_clear_marks_by_group) + * + * Worst case we are given an inode and need to clean up all the marks on that + * inode. We take i_lock and walk the i_fsnotify_marks safely. For each + * mark on the list we take a reference (so the mark can't disappear under us). + * We remove that mark form the inode's list of marks and we add this mark to a + * private list anchored on the stack using i_free_list; At this point we no + * longer fear anything finding the mark using the inode's list of marks. + * + * We can safely and locklessly run the private list on the stack of everything + * we just unattached from the original inode. For each mark on the private list + * we grab the mark-> and can thus dereference mark->group and mark->inode. If + * we see the group and inode are not NULL we take those locks. Now holding all + * 3 locks we can completely remove the mark from other tasks finding it in the + * future. Remember, 10 things might already be referencing this mark, but they + * better be holding a ref. We drop our reference we took before we unhooked it + * from the inode. When the ref hits 0 we can free the mark. + * + * Very similarly for freeing by group, except we use free_g_list. + * + * This has the very interesting property of being able to run concurrently with + * any (or all) other directions. + */ + +#include +#include +#include +#include +#include +#include +#include +#include /* for inode_lock */ + +#include + +#include +#include "fsnotify.h" + +void fsnotify_get_mark(struct fsnotify_mark *mark) +{ + atomic_inc(&mark->refcnt); +} + +void fsnotify_put_mark(struct fsnotify_mark *mark) +{ + if (atomic_dec_and_test(&mark->refcnt)) + mark->free_mark(mark); +} + +/* + * Any time a mark is getting freed we end up here. + * The caller had better be holding a reference to this mark so we don't actually + * do the final put under the mark->lock + */ +void fsnotify_destroy_mark(struct fsnotify_mark *mark) +{ + struct fsnotify_group *group; + struct inode *inode; + + spin_lock(&mark->lock); + + group = mark->group; + inode = mark->i.inode; + + BUG_ON(group && !inode); + BUG_ON(!group && inode); + + /* if !group something else already marked this to die */ + if (!group) { + spin_unlock(&mark->lock); + return; + } + + /* 1 from caller and 1 for being on i_list/g_list */ + BUG_ON(atomic_read(&mark->refcnt) < 2); + + spin_lock(&group->mark_lock); + + if (mark->flags & FSNOTIFY_MARK_FLAG_INODE) + fsnotify_destroy_inode_mark(mark); + else + BUG(); + + list_del_init(&mark->g_list); + mark->group = NULL; + + fsnotify_put_mark(mark); /* for i_list and g_list */ + + spin_unlock(&group->mark_lock); + spin_unlock(&mark->lock); + + /* + * Some groups like to know that marks are being freed. This is a + * callback to the group function to let it know that this mark + * is being freed. + */ + if (group->ops->freeing_mark) + group->ops->freeing_mark(mark, group); + + /* + * __fsnotify_update_child_dentry_flags(inode); + * + * I really want to call that, but we can't, we have no idea if the inode + * still exists the second we drop the mark->lock. + * + * The next time an event arrive to this inode from one of it's children + * __fsnotify_parent will see that the inode doesn't care about it's + * children and will update all of these flags then. So really this + * is just a lazy update (and could be a perf win...) + */ + + + iput(inode); + + /* + * it's possible that this group tried to destroy itself, but this + * this mark was simultaneously being freed by inode. If that's the + * case, we finish freeing the group here. + */ + if (unlikely(atomic_dec_and_test(&group->num_marks))) + fsnotify_final_destroy_group(group); +} + +/* + * Attach an initialized mark to a given group and fs object. + * These marks may be used for the fsnotify backend to determine which + * event types should be delivered to which group. + */ +int fsnotify_add_mark(struct fsnotify_mark *mark, + struct fsnotify_group *group, struct inode *inode, + struct vfsmount *mnt, int allow_dups) +{ + int ret = 0; + + BUG_ON(mnt); + BUG_ON(inode && mnt); + BUG_ON(!inode && !mnt); + + /* + * if this group isn't being testing for inode type events we need + * to start testing + */ + if (inode && unlikely(list_empty(&group->inode_group_list))) + fsnotify_add_inode_group(group); + else if (mnt && unlikely(list_empty(&group->vfsmount_group_list))) + fsnotify_add_vfsmount_group(group); + + /* + * LOCKING ORDER!!!! + * mark->lock + * group->mark_lock + * inode->i_lock + */ + spin_lock(&mark->lock); + spin_lock(&group->mark_lock); + + mark->group = group; + list_add(&mark->g_list, &group->marks_list); + atomic_inc(&group->num_marks); + fsnotify_get_mark(mark); /* for i_list and g_list */ + + if (inode) { + ret = fsnotify_add_inode_mark(mark, group, inode, allow_dups); + if (ret) + goto err; + } else { + BUG(); + } + + spin_unlock(&group->mark_lock); + spin_unlock(&mark->lock); + + if (inode) + __fsnotify_update_child_dentry_flags(inode); + + return ret; +err: + mark->group = NULL; + list_del_init(&mark->g_list); + atomic_dec(&group->num_marks); + fsnotify_put_mark(mark); + + spin_unlock(&group->mark_lock); + spin_unlock(&mark->lock); + + return ret; +} + +/* + * Given a group, destroy all of the marks associated with that group. + */ +void fsnotify_clear_marks_by_group(struct fsnotify_group *group) +{ + struct fsnotify_mark *lmark, *mark; + LIST_HEAD(free_list); + + spin_lock(&group->mark_lock); + list_for_each_entry_safe(mark, lmark, &group->marks_list, g_list) { + list_add(&mark->free_g_list, &free_list); + list_del_init(&mark->g_list); + fsnotify_get_mark(mark); + } + spin_unlock(&group->mark_lock); + + list_for_each_entry_safe(mark, lmark, &free_list, free_g_list) { + fsnotify_destroy_mark(mark); + fsnotify_put_mark(mark); + } +} + +void fsnotify_duplicate_mark(struct fsnotify_mark *new, struct fsnotify_mark *old) +{ + assert_spin_locked(&old->lock); + new->i.inode = old->i.inode; + new->m.mnt = old->m.mnt; + new->group = old->group; + new->mask = old->mask; + new->free_mark = old->free_mark; +} + +/* + * Nothing fancy, just initialize lists and locks and counters. + */ +void fsnotify_init_mark(struct fsnotify_mark *mark, + void (*free_mark)(struct fsnotify_mark *mark)) +{ + spin_lock_init(&mark->lock); + atomic_set(&mark->refcnt, 1); + INIT_HLIST_NODE(&mark->i.i_list); + mark->group = NULL; + mark->mask = 0; + mark->i.inode = NULL; + mark->free_mark = free_mark; +} diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index 7d93572ec568..27cccbecbf23 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -364,11 +364,12 @@ extern struct fsnotify_event *fsnotify_remove_notify_event(struct fsnotify_group extern void fsnotify_recalc_inode_mask(struct inode *inode); extern void fsnotify_init_mark(struct fsnotify_mark *mark, void (*free_mark)(struct fsnotify_mark *mark)); /* find (and take a reference) to a mark associated with group and inode */ -extern struct fsnotify_mark *fsnotify_find_mark(struct fsnotify_group *group, struct inode *inode); +extern struct fsnotify_mark *fsnotify_find_inode_mark(struct fsnotify_group *group, struct inode *inode); /* copy the values from old into new */ extern void fsnotify_duplicate_mark(struct fsnotify_mark *new, struct fsnotify_mark *old); /* attach the mark to both the group and the inode */ -extern int fsnotify_add_mark(struct fsnotify_mark *mark, struct fsnotify_group *group, struct inode *inode, int allow_dups); +extern int fsnotify_add_mark(struct fsnotify_mark *mark, struct fsnotify_group *group, + struct inode *inode, struct vfsmount *mnt, int allow_dups); /* given a mark, flag it to be freed when all references are dropped */ extern void fsnotify_destroy_mark(struct fsnotify_mark *mark); /* run all the marks in a group, and flag them to be freed */ diff --git a/kernel/audit_tree.c b/kernel/audit_tree.c index 80f8ac328aad..cfb97d752a61 100644 --- a/kernel/audit_tree.c +++ b/kernel/audit_tree.c @@ -259,7 +259,7 @@ static void untag_chunk(struct node *p) if (!new) goto Fallback; fsnotify_duplicate_mark(&new->mark, entry); - if (fsnotify_add_mark(&new->mark, new->mark.group, new->mark.i.inode, 1)) { + if (fsnotify_add_mark(&new->mark, new->mark.group, new->mark.i.inode, NULL, 1)) { free_chunk(new); goto Fallback; } @@ -322,7 +322,7 @@ static int create_chunk(struct inode *inode, struct audit_tree *tree) return -ENOMEM; entry = &chunk->mark; - if (fsnotify_add_mark(entry, audit_tree_group, inode, 0)) { + if (fsnotify_add_mark(entry, audit_tree_group, inode, NULL, 0)) { free_chunk(chunk); return -ENOSPC; } @@ -360,7 +360,7 @@ static int tag_chunk(struct inode *inode, struct audit_tree *tree) struct node *p; int n; - old_entry = fsnotify_find_mark(audit_tree_group, inode); + old_entry = fsnotify_find_inode_mark(audit_tree_group, inode); if (!old_entry) return create_chunk(inode, tree); @@ -395,7 +395,7 @@ static int tag_chunk(struct inode *inode, struct audit_tree *tree) } fsnotify_duplicate_mark(chunk_entry, old_entry); - if (fsnotify_add_mark(chunk_entry, chunk_entry->group, chunk_entry->i.inode, 1)) { + if (fsnotify_add_mark(chunk_entry, chunk_entry->group, chunk_entry->i.inode, NULL, 1)) { spin_unlock(&old_entry->lock); free_chunk(chunk); fsnotify_put_mark(old_entry); diff --git a/kernel/audit_watch.c b/kernel/audit_watch.c index d85fa538a722..7499397a6100 100644 --- a/kernel/audit_watch.c +++ b/kernel/audit_watch.c @@ -101,7 +101,7 @@ static inline struct audit_parent *audit_find_parent(struct inode *inode) struct audit_parent *parent = NULL; struct fsnotify_mark *entry; - entry = fsnotify_find_mark(audit_watch_group, inode); + entry = fsnotify_find_inode_mark(audit_watch_group, inode); if (entry) parent = container_of(entry, struct audit_parent, mark); @@ -158,7 +158,7 @@ static struct audit_parent *audit_init_parent(struct nameidata *ndp) fsnotify_init_mark(&parent->mark, audit_watch_free_mark); parent->mark.mask = AUDIT_FS_WATCH; - ret = fsnotify_add_mark(&parent->mark, audit_watch_group, inode, 0); + ret = fsnotify_add_mark(&parent->mark, audit_watch_group, inode, NULL, 0); if (ret < 0) { audit_free_parent(parent); return ERR_PTR(ret); @@ -517,7 +517,7 @@ static bool audit_watch_should_send_event(struct fsnotify_group *group, struct i struct fsnotify_mark *entry; bool send; - entry = fsnotify_find_mark(group, inode); + entry = fsnotify_find_inode_mark(group, inode); if (!entry) return false; -- cgit v1.2.3 From ba643f04cdda170215c8820acd3e201936fc512d Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:27 -0500 Subject: fsnotify: clear marks to 0 in fsnotify_init_mark Currently fsnotify_init_mark sets some fields to 0/NULL. Some users already used some sorts of zalloc, some didn't. This patch uses memset to explicitly zero everything in the fsnotify_mark when it is initialized so we don't have to be careful if fields are later added to marks. Signed-off-by: Eric Paris --- fs/notify/mark.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/notify/mark.c b/fs/notify/mark.c index e56e8768d676..57bb1d74a2b6 100644 --- a/fs/notify/mark.c +++ b/fs/notify/mark.c @@ -284,11 +284,8 @@ void fsnotify_duplicate_mark(struct fsnotify_mark *new, struct fsnotify_mark *ol void fsnotify_init_mark(struct fsnotify_mark *mark, void (*free_mark)(struct fsnotify_mark *mark)) { + memset(mark, 0, sizeof(*mark)); spin_lock_init(&mark->lock); atomic_set(&mark->refcnt, 1); - INIT_HLIST_NODE(&mark->i.i_list); - mark->group = NULL; - mark->mask = 0; - mark->i.inode = NULL; mark->free_mark = free_mark; } -- cgit v1.2.3 From 2504c5d63b811b71bbaa8d5d5af163e698f4df1f Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Thu, 17 Dec 2009 21:24:27 -0500 Subject: fsnotify/vfsmount: add fsnotify fields to struct vfsmount This patch adds the list and mask fields needed to support vfsmount marks. These are the same fields fsnotify needs on an inode. They are not used, just declared and we note where the cleanup hook should be (the function is not yet defined) Signed-off-by: Andreas Gruenbacher Signed-off-by: Eric Paris --- fs/namespace.c | 4 ++++ fs/notify/fsnotify.c | 4 +--- include/linux/mount.h | 6 +++++- 3 files changed, 10 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/namespace.c b/fs/namespace.c index 88058de59c7c..a2d681a6b5e9 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include "pnode.h" @@ -150,6 +151,9 @@ struct vfsmount *alloc_vfsmnt(const char *name) INIT_LIST_HEAD(&mnt->mnt_share); INIT_LIST_HEAD(&mnt->mnt_slave_list); INIT_LIST_HEAD(&mnt->mnt_slave); +#ifdef CONFIG_FSNOTIFY + INIT_HLIST_HEAD(&mnt->mnt_fsnotify_marks); +#endif #ifdef CONFIG_SMP mnt->mnt_writers = alloc_percpu(int); if (!mnt->mnt_writers) diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index 60e84fd338dd..e0bf86953e1b 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -163,9 +163,7 @@ static bool needed_by_vfsmount(__u32 test_mask, struct vfsmount *mnt) if (!mnt) return false; - /* hook in this when mnt->mnt_fsnotify_mask is defined */ - /* return (test_mask & path->mnt->mnt_fsnotify_mask); */ - return false; + return (test_mask & mnt->mnt_fsnotify_mask); } /* * This is the main call to fsnotify. The VFS calls into hook specific functions diff --git a/include/linux/mount.h b/include/linux/mount.h index 4bd05474d11d..907210bd9f9c 100644 --- a/include/linux/mount.h +++ b/include/linux/mount.h @@ -56,7 +56,11 @@ struct vfsmount { struct list_head mnt_mounts; /* list of children, anchored here */ struct list_head mnt_child; /* and going through their mnt_child */ int mnt_flags; - /* 4 bytes hole on 64bits arches */ + /* 4 bytes hole on 64bits arches without fsnotify */ +#ifdef CONFIG_FSNOTIFY + __u32 mnt_fsnotify_mask; + struct hlist_head mnt_fsnotify_marks; +#endif const char *mnt_devname; /* Name of device e.g. /dev/dsk/hda1 */ struct list_head mnt_list; struct list_head mnt_expire; /* link in fs-specific expiry list */ -- cgit v1.2.3 From 0d48b7f01f442bc88a69aa98f3b6b015f2817608 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:27 -0500 Subject: fsnotify: vfsmount marks generic functions Much like inode-mark.c has all of the code dealing with marks on inodes this patch adds a vfsmount-mark.c which has similar code but is intended for marks on vfsmounts. Signed-off-by: Eric Paris --- fs/notify/Makefile | 2 +- fs/notify/fsnotify.h | 6 ++ fs/notify/mark.c | 20 ++--- fs/notify/vfsmount_mark.c | 171 +++++++++++++++++++++++++++++++++++++++ include/linux/fsnotify_backend.h | 2 + 5 files changed, 191 insertions(+), 10 deletions(-) create mode 100644 fs/notify/vfsmount_mark.c (limited to 'fs') diff --git a/fs/notify/Makefile b/fs/notify/Makefile index 8f7f3b024a2e..ae5f33a6d868 100644 --- a/fs/notify/Makefile +++ b/fs/notify/Makefile @@ -1,5 +1,5 @@ obj-$(CONFIG_FSNOTIFY) += fsnotify.o notification.o group.o inode_mark.o \ - mark.o + mark.o vfsmount_mark.o obj-y += dnotify/ obj-y += inotify/ diff --git a/fs/notify/fsnotify.h b/fs/notify/fsnotify.h index 7c7a904b802d..38f3fb5cef28 100644 --- a/fs/notify/fsnotify.h +++ b/fs/notify/fsnotify.h @@ -24,6 +24,10 @@ extern void fsnotify_flush_notify(struct fsnotify_group *group); extern int fsnotify_add_inode_mark(struct fsnotify_mark *mark, struct fsnotify_group *group, struct inode *inode, int allow_dups); +/* add a mark to a vfsmount */ +extern int fsnotify_add_vfsmount_mark(struct fsnotify_mark *mark, + struct fsnotify_group *group, struct vfsmount *mnt, + int allow_dups); /* add a group to the inode group list */ extern void fsnotify_add_inode_group(struct fsnotify_group *group); @@ -32,6 +36,8 @@ extern void fsnotify_add_vfsmount_group(struct fsnotify_group *group); /* final kfree of a group */ extern void fsnotify_final_destroy_group(struct fsnotify_group *group); +/* vfsmount specific destruction of a mark */ +extern void fsnotify_destroy_vfsmount_mark(struct fsnotify_mark *mark); /* inode specific destruction of a mark */ extern void fsnotify_destroy_inode_mark(struct fsnotify_mark *mark); /* run the list of all marks associated with inode and flag them to be freed */ diff --git a/fs/notify/mark.c b/fs/notify/mark.c index 57bb1d74a2b6..d296ec9ffb2a 100644 --- a/fs/notify/mark.c +++ b/fs/notify/mark.c @@ -115,15 +115,11 @@ void fsnotify_put_mark(struct fsnotify_mark *mark) void fsnotify_destroy_mark(struct fsnotify_mark *mark) { struct fsnotify_group *group; - struct inode *inode; + struct inode *inode = NULL; spin_lock(&mark->lock); group = mark->group; - inode = mark->i.inode; - - BUG_ON(group && !inode); - BUG_ON(!group && inode); /* if !group something else already marked this to die */ if (!group) { @@ -136,8 +132,11 @@ void fsnotify_destroy_mark(struct fsnotify_mark *mark) spin_lock(&group->mark_lock); - if (mark->flags & FSNOTIFY_MARK_FLAG_INODE) + if (mark->flags & FSNOTIFY_MARK_FLAG_INODE) { fsnotify_destroy_inode_mark(mark); + inode = mark->i.inode; + } else if (mark->flags & FSNOTIFY_MARK_FLAG_VFSMOUNT) + fsnotify_destroy_vfsmount_mark(mark); else BUG(); @@ -169,8 +168,8 @@ void fsnotify_destroy_mark(struct fsnotify_mark *mark) * is just a lazy update (and could be a perf win...) */ - - iput(inode); + if (inode) + iput(inode); /* * it's possible that this group tried to destroy itself, but this @@ -192,7 +191,6 @@ int fsnotify_add_mark(struct fsnotify_mark *mark, { int ret = 0; - BUG_ON(mnt); BUG_ON(inode && mnt); BUG_ON(!inode && !mnt); @@ -223,6 +221,10 @@ int fsnotify_add_mark(struct fsnotify_mark *mark, ret = fsnotify_add_inode_mark(mark, group, inode, allow_dups); if (ret) goto err; + } else if (mnt) { + ret = fsnotify_add_vfsmount_mark(mark, group, mnt, allow_dups); + if (ret) + goto err; } else { BUG(); } diff --git a/fs/notify/vfsmount_mark.c b/fs/notify/vfsmount_mark.c new file mode 100644 index 000000000000..1b61d0a942de --- /dev/null +++ b/fs/notify/vfsmount_mark.c @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2008 Red Hat, Inc., Eric Paris + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for inode_lock */ + +#include + +#include +#include "fsnotify.h" + +void fsnotify_clear_marks_by_mount(struct vfsmount *mnt) +{ + struct fsnotify_mark *mark, *lmark; + struct hlist_node *pos, *n; + LIST_HEAD(free_list); + + spin_lock(&mnt->mnt_root->d_lock); + hlist_for_each_entry_safe(mark, pos, n, &mnt->mnt_fsnotify_marks, m.m_list) { + list_add(&mark->m.free_m_list, &free_list); + hlist_del_init(&mark->m.m_list); + fsnotify_get_mark(mark); + } + spin_unlock(&mnt->mnt_root->d_lock); + + list_for_each_entry_safe(mark, lmark, &free_list, m.free_m_list) { + fsnotify_destroy_mark(mark); + fsnotify_put_mark(mark); + } +} + +/* + * Recalculate the mask of events relevant to a given vfsmount locked. + */ +static void fsnotify_recalc_vfsmount_mask_locked(struct vfsmount *mnt) +{ + struct fsnotify_mark *mark; + struct hlist_node *pos; + __u32 new_mask = 0; + + assert_spin_locked(&mnt->mnt_root->d_lock); + + hlist_for_each_entry(mark, pos, &mnt->mnt_fsnotify_marks, m.m_list) + new_mask |= mark->mask; + mnt->mnt_fsnotify_mask = new_mask; +} + +/* + * Recalculate the mnt->mnt_fsnotify_mask, or the mask of all FS_* event types + * any notifier is interested in hearing for this mount point + */ +void fsnotify_recalc_vfsmount_mask(struct vfsmount *mnt) +{ + spin_lock(&mnt->mnt_root->d_lock); + fsnotify_recalc_vfsmount_mask_locked(mnt); + spin_unlock(&mnt->mnt_root->d_lock); +} + +void fsnotify_destroy_vfsmount_mark(struct fsnotify_mark *mark) +{ + struct vfsmount *mnt = mark->m.mnt; + + assert_spin_locked(&mark->lock); + assert_spin_locked(&mark->group->mark_lock); + + spin_lock(&mnt->mnt_root->d_lock); + + hlist_del_init(&mark->m.m_list); + mark->m.mnt = NULL; + + fsnotify_recalc_vfsmount_mask_locked(mnt); + + spin_unlock(&mnt->mnt_root->d_lock); +} + +static struct fsnotify_mark *fsnotify_find_vfsmount_mark_locked(struct fsnotify_group *group, + struct vfsmount *mnt) +{ + struct fsnotify_mark *mark; + struct hlist_node *pos; + + assert_spin_locked(&mnt->mnt_root->d_lock); + + hlist_for_each_entry(mark, pos, &mnt->mnt_fsnotify_marks, m.m_list) { + if (mark->group == group) { + fsnotify_get_mark(mark); + return mark; + } + } + return NULL; +} + +/* + * given a group and vfsmount, find the mark associated with that combination. + * if found take a reference to that mark and return it, else return NULL + */ +struct fsnotify_mark *fsnotify_find_vfsmount_mark(struct fsnotify_group *group, + struct vfsmount *mnt) +{ + struct fsnotify_mark *mark; + + spin_lock(&mnt->mnt_root->d_lock); + mark = fsnotify_find_vfsmount_mark_locked(group, mnt); + spin_unlock(&mnt->mnt_root->d_lock); + + return mark; +} + +/* + * Attach an initialized mark to a given group and vfsmount. + * These marks may be used for the fsnotify backend to determine which + * event types should be delivered to which groups. + */ +int fsnotify_add_vfsmount_mark(struct fsnotify_mark *mark, + struct fsnotify_group *group, struct vfsmount *mnt, + int allow_dups) +{ + struct fsnotify_mark *lmark = NULL; + int ret = 0; + + mark->flags = FSNOTIFY_MARK_FLAG_VFSMOUNT; + + /* + * LOCKING ORDER!!!! + * mark->lock + * group->mark_lock + * mnt->mnt_root->d_lock + */ + assert_spin_locked(&mark->lock); + assert_spin_locked(&group->mark_lock); + + spin_lock(&mnt->mnt_root->d_lock); + + if (!allow_dups) + lmark = fsnotify_find_vfsmount_mark_locked(group, mnt); + if (!lmark) { + mark->m.mnt = mnt; + + hlist_add_head(&mark->m.m_list, &mnt->mnt_fsnotify_marks); + + fsnotify_recalc_vfsmount_mask_locked(mnt); + } else { + ret = -EEXIST; + } + + spin_unlock(&mnt->mnt_root->d_lock); + + return ret; +} diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index 27cccbecbf23..f21ff1bd4b5a 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -360,6 +360,8 @@ extern struct fsnotify_event *fsnotify_remove_notify_event(struct fsnotify_group /* functions used to manipulate the marks attached to inodes */ +/* run all marks associated with a vfsmount and update mnt->mnt_fsnotify_mask */ +extern void fsnotify_recalc_vfsmount_mask(struct vfsmount *mnt); /* run all marks associated with an inode and update inode->i_fsnotify_mask */ extern void fsnotify_recalc_inode_mask(struct inode *inode); extern void fsnotify_init_mark(struct fsnotify_mark *mark, void (*free_mark)(struct fsnotify_mark *mark)); -- cgit v1.2.3 From ca9c726eea013394d1e846331b117effb21ead83 Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Thu, 17 Dec 2009 21:24:27 -0500 Subject: fsnotify: Infrastructure for per-mount watches Per-mount watches allow groups to listen to fsnotify events on an entire mount. This patch simply adds and initializes the fields needed in the vfsmount struct to make this happen. Signed-off-by: Andreas Gruenbacher Signed-off-by: Eric Paris --- fs/namespace.c | 1 + fs/notify/fsnotify.c | 5 +++++ fs/notify/fsnotify.h | 2 ++ include/linux/fsnotify.h | 8 ++++++++ include/linux/fsnotify_backend.h | 4 ++++ 5 files changed, 20 insertions(+) (limited to 'fs') diff --git a/fs/namespace.c b/fs/namespace.c index a2d681a6b5e9..1969d6b2571e 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -614,6 +614,7 @@ static inline void __mntput(struct vfsmount *mnt) * provides barriers, so count_mnt_writers() below is safe. AV */ WARN_ON(count_mnt_writers(mnt)); + fsnotify_vfsmount_delete(mnt); dput(mnt->mnt_root); free_vfsmnt(mnt); deactivate_super(sb); diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index e0bf86953e1b..7f14ddc3efc2 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -36,6 +36,11 @@ void __fsnotify_inode_delete(struct inode *inode) } EXPORT_SYMBOL_GPL(__fsnotify_inode_delete); +void __fsnotify_vfsmount_delete(struct vfsmount *mnt) +{ + fsnotify_clear_marks_by_mount(mnt); +} + /* * Given an inode, first check if we care what happens to our children. Inotify * and dnotify both tell their parents about events. If we care about any event diff --git a/fs/notify/fsnotify.h b/fs/notify/fsnotify.h index 38f3fb5cef28..204353c0f663 100644 --- a/fs/notify/fsnotify.h +++ b/fs/notify/fsnotify.h @@ -42,6 +42,8 @@ extern void fsnotify_destroy_vfsmount_mark(struct fsnotify_mark *mark); extern void fsnotify_destroy_inode_mark(struct fsnotify_mark *mark); /* run the list of all marks associated with inode and flag them to be freed */ extern void fsnotify_clear_marks_by_inode(struct inode *inode); +/* run the list of all marks associated with vfsmount and flag them to be freed */ +extern void fsnotify_clear_marks_by_mount(struct vfsmount *mnt); /* * update the dentry->d_flags of all of inode's children to indicate if inode cares * about events that happen to its children. diff --git a/include/linux/fsnotify.h b/include/linux/fsnotify.h index 5184a2b786c1..06c0e50c7968 100644 --- a/include/linux/fsnotify.h +++ b/include/linux/fsnotify.h @@ -95,6 +95,14 @@ static inline void fsnotify_inode_delete(struct inode *inode) __fsnotify_inode_delete(inode); } +/* + * fsnotify_vfsmount_delete - a vfsmount is being destroyed, clean up is needed + */ +static inline void fsnotify_vfsmount_delete(struct vfsmount *mnt) +{ + __fsnotify_vfsmount_delete(mnt); +} + /* * fsnotify_nameremove - a filename was removed from a directory */ diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index f21ff1bd4b5a..1af42cbfc429 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -282,6 +282,7 @@ extern void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, const char *name, u32 cookie); extern void __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask); extern void __fsnotify_inode_delete(struct inode *inode); +extern void __fsnotify_vfsmount_delete(struct vfsmount *mnt); extern u32 fsnotify_get_cookie(void); static inline int fsnotify_inode_watches_children(struct inode *inode) @@ -402,6 +403,9 @@ static inline void __fsnotify_parent(struct path *path, struct dentry *dentry, _ static inline void __fsnotify_inode_delete(struct inode *inode) {} +static inline void __fsnotify_vfsmount_delete(struct vfsmount *mnt) +{} + static inline void __fsnotify_update_dcache_flags(struct dentry *dentry) {} -- cgit v1.2.3 From 1c529063a3e4c15eaae28db31326a7aaab7091b5 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:28 -0500 Subject: fanotify: should_send_event needs to handle vfsmounts currently should_send_event in fanotify only cares about marks on inodes. This patch extends that interface to indicate that it cares about events that happened on vfsmounts. Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify.c | 56 ++++++++++++++++++++++++++++++++-------- include/linux/fsnotify_backend.h | 2 ++ 2 files changed, 47 insertions(+), 11 deletions(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index aa5e92661142..202be8adb2ec 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -2,6 +2,7 @@ #include #include #include /* UINT_MAX */ +#include #include #include "fanotify.h" @@ -99,24 +100,35 @@ static int fanotify_handle_event(struct fsnotify_group *group, struct fsnotify_e return ret; } -static bool fanotify_should_send_event(struct fsnotify_group *group, struct inode *inode, - struct vfsmount *mnt, __u32 mask, void *data, - int data_type) +static bool should_send_vfsmount_event(struct fsnotify_group *group, struct vfsmount *mnt, + __u32 mask) { struct fsnotify_mark *fsn_mark; bool send; - pr_debug("%s: group=%p inode=%p mask=%x data=%p data_type=%d\n", - __func__, group, inode, mask, data, data_type); + pr_debug("%s: group=%p vfsmount=%p mask=%x\n", + __func__, group, mnt, mask); - /* sorry, fanotify only gives a damn about files and dirs */ - if (!S_ISREG(inode->i_mode) && - !S_ISDIR(inode->i_mode)) + fsn_mark = fsnotify_find_vfsmount_mark(group, mnt); + if (!fsn_mark) return false; - /* if we don't have enough info to send an event to userspace say no */ - if (data_type != FSNOTIFY_EVENT_PATH) - return false; + send = (mask & fsn_mark->mask); + + /* find took a reference */ + fsnotify_put_mark(fsn_mark); + + return send; +} + +static bool should_send_inode_event(struct fsnotify_group *group, struct inode *inode, + __u32 mask) +{ + struct fsnotify_mark *fsn_mark; + bool send; + + pr_debug("%s: group=%p inode=%p mask=%x\n", + __func__, group, inode, mask); fsn_mark = fsnotify_find_inode_mark(group, inode); if (!fsn_mark) @@ -142,6 +154,28 @@ static bool fanotify_should_send_event(struct fsnotify_group *group, struct inod return send; } +static bool fanotify_should_send_event(struct fsnotify_group *group, struct inode *to_tell, + struct vfsmount *mnt, __u32 mask, void *data, + int data_type) +{ + pr_debug("%s: group=%p to_tell=%p mnt=%p mask=%x data=%p data_type=%d\n", + __func__, group, to_tell, mnt, mask, data, data_type); + + /* sorry, fanotify only gives a damn about files and dirs */ + if (!S_ISREG(to_tell->i_mode) && + !S_ISDIR(to_tell->i_mode)) + return false; + + /* if we don't have enough info to send an event to userspace say no */ + if (data_type != FSNOTIFY_EVENT_PATH) + return false; + + if (mnt) + return should_send_vfsmount_event(group, mnt, mask); + else + return should_send_inode_event(group, to_tell, mask); +} + const struct fsnotify_ops fanotify_fsnotify_ops = { .handle_event = fanotify_handle_event, .should_send_event = fanotify_should_send_event, diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index 1af42cbfc429..2d2f015fb700 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -368,6 +368,8 @@ extern void fsnotify_recalc_inode_mask(struct inode *inode); extern void fsnotify_init_mark(struct fsnotify_mark *mark, void (*free_mark)(struct fsnotify_mark *mark)); /* find (and take a reference) to a mark associated with group and inode */ extern struct fsnotify_mark *fsnotify_find_inode_mark(struct fsnotify_group *group, struct inode *inode); +/* find (and take a reference) to a mark associated with group and vfsmount */ +extern struct fsnotify_mark *fsnotify_find_vfsmount_mark(struct fsnotify_group *group, struct vfsmount *mnt); /* copy the values from old into new */ extern void fsnotify_duplicate_mark(struct fsnotify_mark *new, struct fsnotify_mark *old); /* attach the mark to both the group and the inode */ -- cgit v1.2.3 From 88826276dcaf4cef9cc7c2695ff15c6d20d4a74d Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:28 -0500 Subject: fanotify: infrastructure to add an remove marks on vfsmounts infrastructure work to add and remove marks on vfsmounts. This should get every set up except wiring the functions to the syscalls. Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify_user.c | 185 ++++++++++++++++++++++++++----------- 1 file changed, 133 insertions(+), 52 deletions(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 05351936a725..cb7a0c5ff854 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -295,31 +295,83 @@ out: return ret; } -static int fanotify_remove_mark(struct fsnotify_group *group, - struct inode *inode, - __u32 mask) +static void fanotify_update_object_mask(struct fsnotify_group *group, + struct inode *inode, + struct vfsmount *mnt, + struct fsnotify_mark *fsn_mark, + unsigned int flags, + __u32 mask) { - struct fsnotify_mark *fsn_mark; - __u32 new_mask; - - pr_debug("%s: group=%p inode=%p mask=%x\n", __func__, - group, inode, mask); + __u32 old_mask, new_mask; - fsn_mark = fsnotify_find_inode_mark(group, inode); - if (!fsn_mark) - return -ENOENT; + pr_debug("%s: group=%p inode=%p mnt=%p fsn_mark=%p flags=%x mask=%x\n", + __func__, group, inode, mnt, fsn_mark, flags, mask); spin_lock(&fsn_mark->lock); - fsn_mark->mask &= ~mask; + old_mask = fsn_mark->mask; + if (flags & FAN_MARK_ADD) + fsn_mark->mask |= mask; + else if (flags & FAN_MARK_REMOVE) + fsn_mark->mask &= ~mask; + else + BUG(); new_mask = fsn_mark->mask; spin_unlock(&fsn_mark->lock); if (!new_mask) fsnotify_destroy_mark(fsn_mark); + + /* we made changes to a mask, update the group mask and the object mask + * so things happen quickly. */ + if (old_mask != new_mask) { + __u32 dropped, do_object, do_group; + + /* more bits in old than in new? */ + dropped = (old_mask & ~new_mask); + /* more bits in this fsn_mark than the group? */ + do_group = (new_mask & ~group->mask); + + if (inode) { + /* more bits in this fsn_mark than the object's mask? */ + do_object = (new_mask & ~inode->i_fsnotify_mask); + /* update the object with this new fsn_mark */ + if (dropped || do_object) + fsnotify_recalc_inode_mask(inode); + } else if (mnt) { + /* more bits in this fsn_mark than the object's mask? */ + do_object = (new_mask & ~mnt->mnt_fsnotify_mask); + /* update the object with this new fsn_mark */ + if (dropped || do_object) + fsnotify_recalc_vfsmount_mask(mnt); + } else { + BUG(); + } + + /* update the group mask with the new mask */ + if (dropped || do_group) + fsnotify_recalc_group_mask(group); + } +} + +static int fanotify_remove_mark(struct fsnotify_group *group, struct inode *inode, + struct vfsmount *mnt, unsigned int flags, __u32 mask) +{ + struct fsnotify_mark *fsn_mark = NULL; + + BUG_ON(inode && mnt); + BUG_ON(!inode && !mnt); + + if (inode) + fsn_mark = fsnotify_find_inode_mark(group, inode); + else if (mnt) + fsn_mark = fsnotify_find_vfsmount_mark(group, mnt); else - fsnotify_recalc_inode_mask(inode); + BUG(); - fsnotify_recalc_group_mask(group); + if (!fsn_mark) + return -ENOENT; + + fanotify_update_object_mask(group, inode, mnt, fsn_mark, flags, mask); /* matches the fsnotify_find_inode_mark() */ fsnotify_put_mark(fsn_mark); @@ -327,22 +379,48 @@ static int fanotify_remove_mark(struct fsnotify_group *group, return 0; } -static int fanotify_add_mark(struct fsnotify_group *group, - struct inode *inode, - __u32 mask) +static struct fsnotify_mark *fanotify_add_vfsmount_mark(struct fsnotify_group *group, + struct vfsmount *mnt) { struct fsnotify_mark *fsn_mark; - __u32 old_mask, new_mask; - int ret; - pr_debug("%s: group=%p inode=%p mask=%x\n", __func__, - group, inode, mask); + fsn_mark = fsnotify_find_vfsmount_mark(group, mnt); + if (!fsn_mark) { + struct fsnotify_mark *new_fsn_mark; + int ret; + + fsn_mark = ERR_PTR(-ENOMEM); + new_fsn_mark = kmem_cache_alloc(fanotify_mark_cache, GFP_KERNEL); + if (!new_fsn_mark) + goto out; + + fsnotify_init_mark(new_fsn_mark, fanotify_free_mark); + ret = fsnotify_add_mark(new_fsn_mark, group, NULL, mnt, 0); + if (ret) { + fsn_mark = ERR_PTR(ret); + fanotify_free_mark(new_fsn_mark); + goto out; + } + + fsn_mark = new_fsn_mark; + } +out: + return fsn_mark; +} + +static struct fsnotify_mark *fanotify_add_inode_mark(struct fsnotify_group *group, + struct inode *inode) +{ + struct fsnotify_mark *fsn_mark; + + pr_debug("%s: group=%p inode=%p\n", __func__, group, inode); fsn_mark = fsnotify_find_inode_mark(group, inode); if (!fsn_mark) { struct fsnotify_mark *new_fsn_mark; + int ret; - ret = -ENOMEM; + fsn_mark = ERR_PTR(-ENOMEM); new_fsn_mark = kmem_cache_alloc(fanotify_mark_cache, GFP_KERNEL); if (!new_fsn_mark) goto out; @@ -350,57 +428,60 @@ static int fanotify_add_mark(struct fsnotify_group *group, fsnotify_init_mark(new_fsn_mark, fanotify_free_mark); ret = fsnotify_add_mark(new_fsn_mark, group, inode, NULL, 0); if (ret) { + fsn_mark = ERR_PTR(ret); fanotify_free_mark(new_fsn_mark); goto out; } fsn_mark = new_fsn_mark; } +out: + return fsn_mark; +} - ret = 0; +static int fanotify_add_mark(struct fsnotify_group *group, struct inode *inode, + struct vfsmount *mnt, unsigned int flags, __u32 mask) +{ + struct fsnotify_mark *fsn_mark; - spin_lock(&fsn_mark->lock); - old_mask = fsn_mark->mask; - fsn_mark->mask |= mask; - new_mask = fsn_mark->mask; - spin_unlock(&fsn_mark->lock); + pr_debug("%s: group=%p inode=%p mnt=%p flags=%x mask=%x\n", + __func__, group, inode, mnt, flags, mask); - /* we made changes to a mask, update the group mask and the inode mask - * so things happen quickly. */ - if (old_mask != new_mask) { - /* more bits in old than in new? */ - int dropped = (old_mask & ~new_mask); - /* more bits in this mark than the inode's mask? */ - int do_inode = (new_mask & ~inode->i_fsnotify_mask); - /* more bits in this mark than the group? */ - int do_group = (new_mask & ~group->mask); + BUG_ON(inode && mnt); + BUG_ON(!inode && !mnt); - /* update the inode with this new mark */ - if (dropped || do_inode) - fsnotify_recalc_inode_mask(inode); + if (inode) + fsn_mark = fanotify_add_inode_mark(group, inode); + else if (mnt) + fsn_mark = fanotify_add_vfsmount_mark(group, mnt); + else + BUG(); - /* update the group mask with the new mask */ - if (dropped || do_group) - fsnotify_recalc_group_mask(group); - } + if (IS_ERR(fsn_mark)) + goto out; + + fanotify_update_object_mask(group, inode, mnt, fsn_mark, flags, mask); /* match the init or the find.... */ fsnotify_put_mark(fsn_mark); out: - return ret; + return PTR_ERR(fsn_mark); } static int fanotify_update_mark(struct fsnotify_group *group, - struct inode *inode, int flags, - __u32 mask) + struct inode *inode, struct vfsmount *mnt, + int flags, __u32 mask) { - pr_debug("%s: group=%p inode=%p flags=%x mask=%x\n", __func__, - group, inode, flags, mask); + pr_debug("%s: group=%p inode=%p mnt=%p flags=%x mask=%x\n", + __func__, group, inode, mnt, flags, mask); + + BUG_ON(inode && mnt); + BUG_ON(!inode && !mnt); if (flags & FAN_MARK_ADD) - fanotify_add_mark(group, inode, mask); + fanotify_add_mark(group, inode, mnt, flags, mask); else if (flags & FAN_MARK_REMOVE) - fanotify_remove_mark(group, inode, mask); + fanotify_remove_mark(group, inode, mnt, flags, mask); else BUG(); @@ -502,7 +583,7 @@ SYSCALL_DEFINE(fanotify_mark)(int fanotify_fd, unsigned int flags, group = filp->private_data; /* create/update an inode mark */ - ret = fanotify_update_mark(group, inode, flags, mask); + ret = fanotify_update_mark(group, inode, NULL, flags, mask); path_put(&path); fput_and_out: -- cgit v1.2.3 From c6223f464927cab9f4b10169b78c51d84228faf8 Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Thu, 17 Dec 2009 21:24:28 -0500 Subject: fanotify: remove fanotify_update_mark fanotify_update_mark() doesn't do much useful; remove it. Signed-off-by: Andreas Gruenbacher Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify_user.c | 31 ++++++++++--------------------- 1 file changed, 10 insertions(+), 21 deletions(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index cb7a0c5ff854..0f25fc20a6a7 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -468,26 +468,6 @@ out: return PTR_ERR(fsn_mark); } -static int fanotify_update_mark(struct fsnotify_group *group, - struct inode *inode, struct vfsmount *mnt, - int flags, __u32 mask) -{ - pr_debug("%s: group=%p inode=%p mnt=%p flags=%x mask=%x\n", - __func__, group, inode, mnt, flags, mask); - - BUG_ON(inode && mnt); - BUG_ON(!inode && !mnt); - - if (flags & FAN_MARK_ADD) - fanotify_add_mark(group, inode, mnt, flags, mask); - else if (flags & FAN_MARK_REMOVE) - fanotify_remove_mark(group, inode, mnt, flags, mask); - else - BUG(); - - return 0; -} - static bool fanotify_mark_validate_input(int flags, __u32 mask) { @@ -583,7 +563,16 @@ SYSCALL_DEFINE(fanotify_mark)(int fanotify_fd, unsigned int flags, group = filp->private_data; /* create/update an inode mark */ - ret = fanotify_update_mark(group, inode, NULL, flags, mask); + switch (flags & (FAN_MARK_ADD | FAN_MARK_REMOVE)) { + case FAN_MARK_ADD: + ret = fanotify_add_mark(group, inode, NULL, flags, mask); + break; + case FAN_MARK_REMOVE: + ret = fanotify_remove_mark(group, inode, NULL, flags, mask); + break; + default: + ret = -EINVAL; + } path_put(&path); fput_and_out: -- cgit v1.2.3 From 088b09b0ac7a866a35962eeaea5d5607bd1840b7 Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Thu, 17 Dec 2009 21:24:28 -0500 Subject: fanotify: do not call fanotify_update_object_mask in fanotify_remove_mark Recalculate masks in fanotify_remove_mark, don't use fanotify_update_object_mask. This gets us one step closers to readable code. Signed-off-by: Andreas Gruenbacher Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify_user.c | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 0f25fc20a6a7..96d4ffd72519 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -353,10 +353,26 @@ static void fanotify_update_object_mask(struct fsnotify_group *group, } } +static __u32 fanotify_mark_remove_from_mask(struct fsnotify_mark *fsn_mark, __u32 mask) +{ + __u32 oldmask; + + spin_lock(&fsn_mark->lock); + oldmask = fsn_mark->mask; + fsn_mark->mask = oldmask & ~mask; + spin_unlock(&fsn_mark->lock); + + if (!(oldmask & ~mask)) + fsnotify_destroy_mark(fsn_mark); + + return mask & oldmask; +} + static int fanotify_remove_mark(struct fsnotify_group *group, struct inode *inode, - struct vfsmount *mnt, unsigned int flags, __u32 mask) + struct vfsmount *mnt, __u32 mask) { struct fsnotify_mark *fsn_mark = NULL; + __u32 removed; BUG_ON(inode && mnt); BUG_ON(!inode && !mnt); @@ -371,11 +387,20 @@ static int fanotify_remove_mark(struct fsnotify_group *group, struct inode *inod if (!fsn_mark) return -ENOENT; - fanotify_update_object_mask(group, inode, mnt, fsn_mark, flags, mask); - + removed = fanotify_mark_remove_from_mask(fsn_mark, mask); /* matches the fsnotify_find_inode_mark() */ fsnotify_put_mark(fsn_mark); + if (removed & group->mask) + fsnotify_recalc_group_mask(group); + if (inode) { + if (removed & inode->i_fsnotify_mask) + fsnotify_recalc_inode_mask(inode); + } else if (mnt) { + if (removed & mnt->mnt_fsnotify_mask) + fsnotify_recalc_vfsmount_mask(mnt); + } + return 0; } @@ -568,7 +593,7 @@ SYSCALL_DEFINE(fanotify_mark)(int fanotify_fd, unsigned int flags, ret = fanotify_add_mark(group, inode, NULL, flags, mask); break; case FAN_MARK_REMOVE: - ret = fanotify_remove_mark(group, inode, NULL, flags, mask); + ret = fanotify_remove_mark(group, inode, NULL, mask); break; default: ret = -EINVAL; -- cgit v1.2.3 From 912ee3946c5e57de0d05baf3b60b65ce6bf3ff96 Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Thu, 17 Dec 2009 21:24:28 -0500 Subject: fanotify: do not call fanotify_update_object_mask in fanotify_add_mark Recalculate masks in fanotify_add_mark, don't use fanotify_update_object_mask. This gets us one step closers to readable code. Signed-off-by: Andreas Gruenbacher Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify_user.c | 144 +++++++++++++------------------------ 1 file changed, 50 insertions(+), 94 deletions(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 96d4ffd72519..db7467782e8c 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -295,64 +295,6 @@ out: return ret; } -static void fanotify_update_object_mask(struct fsnotify_group *group, - struct inode *inode, - struct vfsmount *mnt, - struct fsnotify_mark *fsn_mark, - unsigned int flags, - __u32 mask) -{ - __u32 old_mask, new_mask; - - pr_debug("%s: group=%p inode=%p mnt=%p fsn_mark=%p flags=%x mask=%x\n", - __func__, group, inode, mnt, fsn_mark, flags, mask); - - spin_lock(&fsn_mark->lock); - old_mask = fsn_mark->mask; - if (flags & FAN_MARK_ADD) - fsn_mark->mask |= mask; - else if (flags & FAN_MARK_REMOVE) - fsn_mark->mask &= ~mask; - else - BUG(); - new_mask = fsn_mark->mask; - spin_unlock(&fsn_mark->lock); - - if (!new_mask) - fsnotify_destroy_mark(fsn_mark); - - /* we made changes to a mask, update the group mask and the object mask - * so things happen quickly. */ - if (old_mask != new_mask) { - __u32 dropped, do_object, do_group; - - /* more bits in old than in new? */ - dropped = (old_mask & ~new_mask); - /* more bits in this fsn_mark than the group? */ - do_group = (new_mask & ~group->mask); - - if (inode) { - /* more bits in this fsn_mark than the object's mask? */ - do_object = (new_mask & ~inode->i_fsnotify_mask); - /* update the object with this new fsn_mark */ - if (dropped || do_object) - fsnotify_recalc_inode_mask(inode); - } else if (mnt) { - /* more bits in this fsn_mark than the object's mask? */ - do_object = (new_mask & ~mnt->mnt_fsnotify_mask); - /* update the object with this new fsn_mark */ - if (dropped || do_object) - fsnotify_recalc_vfsmount_mask(mnt); - } else { - BUG(); - } - - /* update the group mask with the new mask */ - if (dropped || do_group) - fsnotify_recalc_group_mask(group); - } -} - static __u32 fanotify_mark_remove_from_mask(struct fsnotify_mark *fsn_mark, __u32 mask) { __u32 oldmask; @@ -404,89 +346,103 @@ static int fanotify_remove_mark(struct fsnotify_group *group, struct inode *inod return 0; } +static __u32 fanotify_mark_add_to_mask(struct fsnotify_mark *fsn_mark, __u32 mask) +{ + __u32 oldmask; + + spin_lock(&fsn_mark->lock); + oldmask = fsn_mark->mask; + fsn_mark->mask = oldmask | mask; + spin_unlock(&fsn_mark->lock); + + return mask & ~oldmask; +} + static struct fsnotify_mark *fanotify_add_vfsmount_mark(struct fsnotify_group *group, - struct vfsmount *mnt) + struct vfsmount *mnt, __u32 mask) { struct fsnotify_mark *fsn_mark; + __u32 added; fsn_mark = fsnotify_find_vfsmount_mark(group, mnt); if (!fsn_mark) { - struct fsnotify_mark *new_fsn_mark; int ret; - fsn_mark = ERR_PTR(-ENOMEM); - new_fsn_mark = kmem_cache_alloc(fanotify_mark_cache, GFP_KERNEL); - if (!new_fsn_mark) - goto out; + fsn_mark = kmem_cache_alloc(fanotify_mark_cache, GFP_KERNEL); + if (!fsn_mark) + return ERR_PTR(-ENOMEM); - fsnotify_init_mark(new_fsn_mark, fanotify_free_mark); - ret = fsnotify_add_mark(new_fsn_mark, group, NULL, mnt, 0); + fsnotify_init_mark(fsn_mark, fanotify_free_mark); + ret = fsnotify_add_mark(fsn_mark, group, NULL, mnt, 0); if (ret) { - fsn_mark = ERR_PTR(ret); - fanotify_free_mark(new_fsn_mark); - goto out; + fanotify_free_mark(fsn_mark); + return ERR_PTR(ret); } - - fsn_mark = new_fsn_mark; } -out: + added = fanotify_mark_add_to_mask(fsn_mark, mask); + if (added) { + if (added & ~group->mask) + fsnotify_recalc_group_mask(group); + if (added & ~mnt->mnt_fsnotify_mask) + fsnotify_recalc_vfsmount_mask(mnt); + } return fsn_mark; } static struct fsnotify_mark *fanotify_add_inode_mark(struct fsnotify_group *group, - struct inode *inode) + struct inode *inode, __u32 mask) { struct fsnotify_mark *fsn_mark; + __u32 added; pr_debug("%s: group=%p inode=%p\n", __func__, group, inode); fsn_mark = fsnotify_find_inode_mark(group, inode); if (!fsn_mark) { - struct fsnotify_mark *new_fsn_mark; int ret; - fsn_mark = ERR_PTR(-ENOMEM); - new_fsn_mark = kmem_cache_alloc(fanotify_mark_cache, GFP_KERNEL); - if (!new_fsn_mark) - goto out; + fsn_mark = kmem_cache_alloc(fanotify_mark_cache, GFP_KERNEL); + if (!fsn_mark) + return ERR_PTR(-ENOMEM); - fsnotify_init_mark(new_fsn_mark, fanotify_free_mark); - ret = fsnotify_add_mark(new_fsn_mark, group, inode, NULL, 0); + fsnotify_init_mark(fsn_mark, fanotify_free_mark); + ret = fsnotify_add_mark(fsn_mark, group, inode, NULL, 0); if (ret) { - fsn_mark = ERR_PTR(ret); - fanotify_free_mark(new_fsn_mark); - goto out; + fanotify_free_mark(fsn_mark); + return ERR_PTR(ret); } - - fsn_mark = new_fsn_mark; } -out: + added = fanotify_mark_add_to_mask(fsn_mark, mask); + if (added) { + if (added & ~group->mask) + fsnotify_recalc_group_mask(group); + if (added & ~inode->i_fsnotify_mask) + fsnotify_recalc_inode_mask(inode); + } return fsn_mark; } static int fanotify_add_mark(struct fsnotify_group *group, struct inode *inode, - struct vfsmount *mnt, unsigned int flags, __u32 mask) + struct vfsmount *mnt, __u32 mask) { struct fsnotify_mark *fsn_mark; - pr_debug("%s: group=%p inode=%p mnt=%p flags=%x mask=%x\n", - __func__, group, inode, mnt, flags, mask); + pr_debug("%s: group=%p inode=%p mnt=%p mask=%x\n", + __func__, group, inode, mnt, mask); BUG_ON(inode && mnt); BUG_ON(!inode && !mnt); if (inode) - fsn_mark = fanotify_add_inode_mark(group, inode); + fsn_mark = fanotify_add_inode_mark(group, inode, mask); else if (mnt) - fsn_mark = fanotify_add_vfsmount_mark(group, mnt); + fsn_mark = fanotify_add_vfsmount_mark(group, mnt, mask); else BUG(); if (IS_ERR(fsn_mark)) goto out; - fanotify_update_object_mask(group, inode, mnt, fsn_mark, flags, mask); - /* match the init or the find.... */ fsnotify_put_mark(fsn_mark); out: @@ -590,7 +546,7 @@ SYSCALL_DEFINE(fanotify_mark)(int fanotify_fd, unsigned int flags, /* create/update an inode mark */ switch (flags & (FAN_MARK_ADD | FAN_MARK_REMOVE)) { case FAN_MARK_ADD: - ret = fanotify_add_mark(group, inode, NULL, flags, mask); + ret = fanotify_add_mark(group, inode, NULL, mask); break; case FAN_MARK_REMOVE: ret = fanotify_remove_mark(group, inode, NULL, mask); -- cgit v1.2.3 From 52202dfbd9107787dc68a2019cc7be4e79f52e5c Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Thu, 17 Dec 2009 21:24:28 -0500 Subject: fanotify: do not return pointer from fanotify_add_*_mark No need to return the mark from fanotify_add_*_mark to fanotify_add_mark Signed-off-by: Andreas Gruenbacher Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify_user.c | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index db7467782e8c..7d7c13872852 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -358,8 +358,8 @@ static __u32 fanotify_mark_add_to_mask(struct fsnotify_mark *fsn_mark, __u32 mas return mask & ~oldmask; } -static struct fsnotify_mark *fanotify_add_vfsmount_mark(struct fsnotify_group *group, - struct vfsmount *mnt, __u32 mask) +static int fanotify_add_vfsmount_mark(struct fsnotify_group *group, + struct vfsmount *mnt, __u32 mask) { struct fsnotify_mark *fsn_mark; __u32 added; @@ -370,27 +370,28 @@ static struct fsnotify_mark *fanotify_add_vfsmount_mark(struct fsnotify_group *g fsn_mark = kmem_cache_alloc(fanotify_mark_cache, GFP_KERNEL); if (!fsn_mark) - return ERR_PTR(-ENOMEM); + return -ENOMEM; fsnotify_init_mark(fsn_mark, fanotify_free_mark); ret = fsnotify_add_mark(fsn_mark, group, NULL, mnt, 0); if (ret) { fanotify_free_mark(fsn_mark); - return ERR_PTR(ret); + return ret; } } added = fanotify_mark_add_to_mask(fsn_mark, mask); + fsnotify_put_mark(fsn_mark); if (added) { if (added & ~group->mask) fsnotify_recalc_group_mask(group); if (added & ~mnt->mnt_fsnotify_mask) fsnotify_recalc_vfsmount_mask(mnt); } - return fsn_mark; + return 0; } -static struct fsnotify_mark *fanotify_add_inode_mark(struct fsnotify_group *group, - struct inode *inode, __u32 mask) +static int fanotify_add_inode_mark(struct fsnotify_group *group, + struct inode *inode, __u32 mask) { struct fsnotify_mark *fsn_mark; __u32 added; @@ -403,29 +404,30 @@ static struct fsnotify_mark *fanotify_add_inode_mark(struct fsnotify_group *grou fsn_mark = kmem_cache_alloc(fanotify_mark_cache, GFP_KERNEL); if (!fsn_mark) - return ERR_PTR(-ENOMEM); + return -ENOMEM; fsnotify_init_mark(fsn_mark, fanotify_free_mark); ret = fsnotify_add_mark(fsn_mark, group, inode, NULL, 0); if (ret) { fanotify_free_mark(fsn_mark); - return ERR_PTR(ret); + return ret; } } added = fanotify_mark_add_to_mask(fsn_mark, mask); + fsnotify_put_mark(fsn_mark); if (added) { if (added & ~group->mask) fsnotify_recalc_group_mask(group); if (added & ~inode->i_fsnotify_mask) fsnotify_recalc_inode_mask(inode); } - return fsn_mark; + return 0; } static int fanotify_add_mark(struct fsnotify_group *group, struct inode *inode, struct vfsmount *mnt, __u32 mask) { - struct fsnotify_mark *fsn_mark; + int ret; pr_debug("%s: group=%p inode=%p mnt=%p mask=%x\n", __func__, group, inode, mnt, mask); @@ -434,19 +436,13 @@ static int fanotify_add_mark(struct fsnotify_group *group, struct inode *inode, BUG_ON(!inode && !mnt); if (inode) - fsn_mark = fanotify_add_inode_mark(group, inode, mask); + ret = fanotify_add_inode_mark(group, inode, mask); else if (mnt) - fsn_mark = fanotify_add_vfsmount_mark(group, mnt, mask); + ret = fanotify_add_vfsmount_mark(group, mnt, mask); else BUG(); - if (IS_ERR(fsn_mark)) - goto out; - - /* match the init or the find.... */ - fsnotify_put_mark(fsn_mark); -out: - return PTR_ERR(fsn_mark); + return ret; } static bool fanotify_mark_validate_input(int flags, -- cgit v1.2.3 From 90dd201d1ab064512078a77762a793e0bf5f3040 Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Thu, 17 Dec 2009 21:24:28 -0500 Subject: fanotify: remove fanotify_add_mark fanotify_add_mark now does nothing useful anymore, drop it. Signed-off-by: Andreas Gruenbacher Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify_user.c | 23 +---------------------- 1 file changed, 1 insertion(+), 22 deletions(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 7d7c13872852..db80a0d89d24 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -424,27 +424,6 @@ static int fanotify_add_inode_mark(struct fsnotify_group *group, return 0; } -static int fanotify_add_mark(struct fsnotify_group *group, struct inode *inode, - struct vfsmount *mnt, __u32 mask) -{ - int ret; - - pr_debug("%s: group=%p inode=%p mnt=%p mask=%x\n", - __func__, group, inode, mnt, mask); - - BUG_ON(inode && mnt); - BUG_ON(!inode && !mnt); - - if (inode) - ret = fanotify_add_inode_mark(group, inode, mask); - else if (mnt) - ret = fanotify_add_vfsmount_mark(group, mnt, mask); - else - BUG(); - - return ret; -} - static bool fanotify_mark_validate_input(int flags, __u32 mask) { @@ -542,7 +521,7 @@ SYSCALL_DEFINE(fanotify_mark)(int fanotify_fd, unsigned int flags, /* create/update an inode mark */ switch (flags & (FAN_MARK_ADD | FAN_MARK_REMOVE)) { case FAN_MARK_ADD: - ret = fanotify_add_mark(group, inode, NULL, mask); + ret = fanotify_add_inode_mark(group, inode, mask); break; case FAN_MARK_REMOVE: ret = fanotify_remove_mark(group, inode, NULL, mask); -- cgit v1.2.3 From 0ff21db9fcc39042b814dad8a4b7508710a75235 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:29 -0500 Subject: fanotify: hooks the fanotify_mark syscall to the vfsmount code Create a new fanotify_mark flag which indicates we should attach the mark to the vfsmount holding the object referenced by dfd and pathname rather than the inode itself. Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify_user.c | 15 +++++++++++---- include/linux/fanotify.h | 4 +++- 2 files changed, 14 insertions(+), 5 deletions(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index db80a0d89d24..81267260d1b9 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -485,7 +485,8 @@ SYSCALL_DEFINE(fanotify_mark)(int fanotify_fd, unsigned int flags, __u64 mask, int dfd, const char __user * pathname) { - struct inode *inode; + struct inode *inode = NULL; + struct vfsmount *mnt = NULL; struct fsnotify_group *group; struct file *filp; struct path path; @@ -515,16 +516,22 @@ SYSCALL_DEFINE(fanotify_mark)(int fanotify_fd, unsigned int flags, goto fput_and_out; /* inode held in place by reference to path; group by fget on fd */ - inode = path.dentry->d_inode; + if (!(flags & FAN_MARK_ON_VFSMOUNT)) + inode = path.dentry->d_inode; + else + mnt = path.mnt; group = filp->private_data; /* create/update an inode mark */ switch (flags & (FAN_MARK_ADD | FAN_MARK_REMOVE)) { case FAN_MARK_ADD: - ret = fanotify_add_inode_mark(group, inode, mask); + if (flags & FAN_MARK_ON_VFSMOUNT) + ret = fanotify_add_vfsmount_mark(group, mnt, mask); + else + ret = fanotify_add_inode_mark(group, inode, mask); break; case FAN_MARK_REMOVE: - ret = fanotify_remove_mark(group, inode, NULL, mask); + ret = fanotify_remove_mark(group, inode, mnt, mask); break; default: ret = -EINVAL; diff --git a/include/linux/fanotify.h b/include/linux/fanotify.h index 5f633af4d1b0..e25d348188ca 100644 --- a/include/linux/fanotify.h +++ b/include/linux/fanotify.h @@ -29,11 +29,13 @@ #define FAN_MARK_REMOVE 0x00000002 #define FAN_MARK_DONT_FOLLOW 0x00000004 #define FAN_MARK_ONLYDIR 0x00000008 +#define FAN_MARK_ON_VFSMOUNT 0x00000010 #define FAN_ALL_MARK_FLAGS (FAN_MARK_ADD |\ FAN_MARK_REMOVE |\ FAN_MARK_DONT_FOLLOW |\ - FAN_MARK_ONLYDIR) + FAN_MARK_ONLYDIR |\ + FAN_MARK_ON_VFSMOUNT) /* * All of the events - we build the list by hand so that we can add flags in -- cgit v1.2.3 From eac8e9e80ccbd30801b7b76a2ee4c6c5a681e53c Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Thu, 17 Dec 2009 21:24:29 -0500 Subject: fanotify: rename FAN_MARK_ON_VFSMOUNT to FAN_MARK_MOUNT the term 'vfsmount' isn't sensicle to userspace. instead call is 'mount. Signed-off-by: Andreas Gruenbacher Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify_user.c | 4 ++-- include/linux/fanotify.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 81267260d1b9..091371e1bde3 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -516,7 +516,7 @@ SYSCALL_DEFINE(fanotify_mark)(int fanotify_fd, unsigned int flags, goto fput_and_out; /* inode held in place by reference to path; group by fget on fd */ - if (!(flags & FAN_MARK_ON_VFSMOUNT)) + if (!(flags & FAN_MARK_MOUNT)) inode = path.dentry->d_inode; else mnt = path.mnt; @@ -525,7 +525,7 @@ SYSCALL_DEFINE(fanotify_mark)(int fanotify_fd, unsigned int flags, /* create/update an inode mark */ switch (flags & (FAN_MARK_ADD | FAN_MARK_REMOVE)) { case FAN_MARK_ADD: - if (flags & FAN_MARK_ON_VFSMOUNT) + if (flags & FAN_MARK_MOUNT) ret = fanotify_add_vfsmount_mark(group, mnt, mask); else ret = fanotify_add_inode_mark(group, inode, mask); diff --git a/include/linux/fanotify.h b/include/linux/fanotify.h index e25d348188ca..5ee22fb274e5 100644 --- a/include/linux/fanotify.h +++ b/include/linux/fanotify.h @@ -29,13 +29,13 @@ #define FAN_MARK_REMOVE 0x00000002 #define FAN_MARK_DONT_FOLLOW 0x00000004 #define FAN_MARK_ONLYDIR 0x00000008 -#define FAN_MARK_ON_VFSMOUNT 0x00000010 +#define FAN_MARK_MOUNT 0x00000010 #define FAN_ALL_MARK_FLAGS (FAN_MARK_ADD |\ FAN_MARK_REMOVE |\ FAN_MARK_DONT_FOLLOW |\ FAN_MARK_ONLYDIR |\ - FAN_MARK_ON_VFSMOUNT) + FAN_MARK_MOUNT) /* * All of the events - we build the list by hand so that we can add flags in -- cgit v1.2.3 From f3640192c0a177506ec08ab07ed3178b912574da Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Thu, 17 Dec 2009 21:24:29 -0500 Subject: fanotify: split fanotify_remove_mark split fanotify_remove_mark into fanotify_remove_inode_mark and fanotify_remove_vfsmount_mark. Signed-off-by: Andreas Gruenbacher Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify_user.c | 45 +++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 18 deletions(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 091371e1bde3..00628d3ce5a2 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -310,22 +310,33 @@ static __u32 fanotify_mark_remove_from_mask(struct fsnotify_mark *fsn_mark, __u3 return mask & oldmask; } -static int fanotify_remove_mark(struct fsnotify_group *group, struct inode *inode, - struct vfsmount *mnt, __u32 mask) +static int fanotify_remove_vfsmount_mark(struct fsnotify_group *group, + struct vfsmount *mnt, __u32 mask) { struct fsnotify_mark *fsn_mark = NULL; __u32 removed; - BUG_ON(inode && mnt); - BUG_ON(!inode && !mnt); + fsn_mark = fsnotify_find_vfsmount_mark(group, mnt); + if (!fsn_mark) + return -ENOENT; - if (inode) - fsn_mark = fsnotify_find_inode_mark(group, inode); - else if (mnt) - fsn_mark = fsnotify_find_vfsmount_mark(group, mnt); - else - BUG(); + removed = fanotify_mark_remove_from_mask(fsn_mark, mask); + fsnotify_put_mark(fsn_mark); + if (removed & group->mask) + fsnotify_recalc_group_mask(group); + if (removed & mnt->mnt_fsnotify_mask) + fsnotify_recalc_vfsmount_mask(mnt); + + return 0; +} +static int fanotify_remove_inode_mark(struct fsnotify_group *group, + struct inode *inode, __u32 mask) +{ + struct fsnotify_mark *fsn_mark = NULL; + __u32 removed; + + fsn_mark = fsnotify_find_inode_mark(group, inode); if (!fsn_mark) return -ENOENT; @@ -335,13 +346,8 @@ static int fanotify_remove_mark(struct fsnotify_group *group, struct inode *inod if (removed & group->mask) fsnotify_recalc_group_mask(group); - if (inode) { - if (removed & inode->i_fsnotify_mask) - fsnotify_recalc_inode_mask(inode); - } else if (mnt) { - if (removed & mnt->mnt_fsnotify_mask) - fsnotify_recalc_vfsmount_mask(mnt); - } + if (removed & inode->i_fsnotify_mask) + fsnotify_recalc_inode_mask(inode); return 0; } @@ -531,7 +537,10 @@ SYSCALL_DEFINE(fanotify_mark)(int fanotify_fd, unsigned int flags, ret = fanotify_add_inode_mark(group, inode, mask); break; case FAN_MARK_REMOVE: - ret = fanotify_remove_mark(group, inode, mnt, mask); + if (flags & FAN_MARK_MOUNT) + ret = fanotify_remove_vfsmount_mark(group, mnt, mask); + else + ret = fanotify_remove_inode_mark(group, inode, mask); break; default: ret = -EINVAL; -- cgit v1.2.3 From 88380fe66e0ac22529f5426ab27d67da00ed2628 Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Thu, 17 Dec 2009 21:24:29 -0500 Subject: fanotify: remove fanotify.h declarations fanotify_mark_validate functions are all needlessly declared in headers as static inlines. Instead just do the checks where they are needed for code readability. Signed-off-by: Andreas Gruenbacher Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify.h | 25 ------------------------- fs/notify/fanotify/fanotify_user.c | 25 ++++++++++--------------- include/linux/fanotify.h | 7 ------- 3 files changed, 10 insertions(+), 47 deletions(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify.h b/fs/notify/fanotify/fanotify.h index 5608783c6bca..4d5723a74a8e 100644 --- a/fs/notify/fanotify/fanotify.h +++ b/fs/notify/fanotify/fanotify.h @@ -6,31 +6,6 @@ extern const struct fsnotify_ops fanotify_fsnotify_ops; -static inline bool fanotify_mark_flags_valid(unsigned int flags) -{ - /* must be either and add or a remove */ - if (!(flags & (FAN_MARK_ADD | FAN_MARK_REMOVE))) - return false; - - /* cannot be both add and remove */ - if ((flags & FAN_MARK_ADD) && - (flags & FAN_MARK_REMOVE)) - return false; - - /* cannot have more flags than we know about */ - if (flags & ~FAN_ALL_MARK_FLAGS) - return false; - - return true; -} - -static inline bool fanotify_mask_valid(__u32 mask) -{ - if (mask & ~((__u32)FAN_ALL_INCOMING_EVENTS)) - return false; - return true; -} - static inline __u32 fanotify_outgoing_mask(__u32 mask) { return mask & FAN_ALL_OUTGOING_EVENTS; diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 00628d3ce5a2..618867e4d30f 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -430,20 +430,6 @@ static int fanotify_add_inode_mark(struct fsnotify_group *group, return 0; } -static bool fanotify_mark_validate_input(int flags, - __u32 mask) -{ - pr_debug("%s: flags=%x mask=%x\n", __func__, flags, mask); - - /* are flags valid of this operation? */ - if (!fanotify_mark_flags_valid(flags)) - return false; - /* is the mask valid? */ - if (!fanotify_mask_valid(mask)) - return false; - return true; -} - /* fanotify syscalls */ SYSCALL_DEFINE3(fanotify_init, unsigned int, flags, unsigned int, event_f_flags, unsigned int, priority) @@ -505,7 +491,16 @@ SYSCALL_DEFINE(fanotify_mark)(int fanotify_fd, unsigned int flags, if (mask & ((__u64)0xffffffff << 32)) return -EINVAL; - if (!fanotify_mark_validate_input(flags, mask)) + if (flags & ~FAN_ALL_MARK_FLAGS) + return -EINVAL; + switch (flags & (FAN_MARK_ADD | FAN_MARK_REMOVE)) { + case FAN_MARK_ADD: + case FAN_MARK_REMOVE: + break; + default: + return -EINVAL; + } + if (mask & ~(FAN_ALL_EVENTS | FAN_EVENT_ON_CHILD)) return -EINVAL; filp = fget_light(fanotify_fd, &fput_needed); diff --git a/include/linux/fanotify.h b/include/linux/fanotify.h index 5ee22fb274e5..90e59b24fd04 100644 --- a/include/linux/fanotify.h +++ b/include/linux/fanotify.h @@ -47,13 +47,6 @@ FAN_CLOSE |\ FAN_OPEN) -/* - * All legal FAN bits userspace can request (although possibly not all - * at the same time. - */ -#define FAN_ALL_INCOMING_EVENTS (FAN_ALL_EVENTS |\ - FAN_EVENT_ON_CHILD) - #define FAN_ALL_OUTGOING_EVENTS (FAN_ALL_EVENTS |\ FAN_Q_OVERFLOW) -- cgit v1.2.3 From 33d3dfff451a2ab6fe2f6aaabed9b24e91aad109 Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Thu, 17 Dec 2009 21:24:29 -0500 Subject: fanotify: remove outgoing function checks in fanotify.h A number of validity checks on outgoing data are done in static inlines but are only used in one place. Instead just do them where they are used for readability. Signed-off-by: Andreas Gruenbacher Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify.c | 3 +-- fs/notify/fanotify/fanotify.h | 12 ------------ fs/notify/fanotify/fanotify_user.c | 5 +++-- 3 files changed, 4 insertions(+), 16 deletions(-) delete mode 100644 fs/notify/fanotify/fanotify.h (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index 202be8adb2ec..f6900022f69e 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -5,8 +6,6 @@ #include #include -#include "fanotify.h" - static bool should_merge(struct fsnotify_event *old, struct fsnotify_event *new) { pr_debug("%s: old=%p new=%p\n", __func__, old, new); diff --git a/fs/notify/fanotify/fanotify.h b/fs/notify/fanotify/fanotify.h deleted file mode 100644 index 4d5723a74a8e..000000000000 --- a/fs/notify/fanotify/fanotify.h +++ /dev/null @@ -1,12 +0,0 @@ -#include -#include -#include -#include -#include - -extern const struct fsnotify_ops fanotify_fsnotify_ops; - -static inline __u32 fanotify_outgoing_mask(__u32 mask) -{ - return mask & FAN_ALL_OUTGOING_EVENTS; -} diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 618867e4d30f..84155841a025 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -14,7 +15,7 @@ #include -#include "fanotify.h" +extern const struct fsnotify_ops fanotify_fsnotify_ops; static struct kmem_cache *fanotify_mark_cache __read_mostly; @@ -102,7 +103,7 @@ static ssize_t fill_event_metadata(struct fsnotify_group *group, metadata->event_len = FAN_EVENT_METADATA_LEN; metadata->vers = FANOTIFY_METADATA_VERSION; - metadata->mask = fanotify_outgoing_mask(event->mask); + metadata->mask = event->mask & FAN_ALL_OUTGOING_EVENTS; metadata->pid = pid_vnr(event->tgid); metadata->fd = create_fd(group, event); -- cgit v1.2.3 From 90b1e7a57880fb66437ab7db39e1e65ca0372822 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:33 -0500 Subject: fsnotify: allow marks to not pin inodes in core inotify marks must pin inodes in core. dnotify doesn't technically need to since they are closed when the directory is closed. fanotify also need to pin inodes in core as it works today. But the next step is to introduce the concept of 'ignored masks' which is actually a mask of events for an inode of no interest. I claim that these should be liberally sent to the kernel and should not pin the inode in core. If the inode is brought back in the listener will get an event it may have thought excluded, but this is not a serious situation and one any listener should deal with. This patch lays the ground work for non-pinning inode marks by using lazy inode pinning. We do not pin a mark until it has a non-zero mask entry. If a listener new sets a mask we never pin the inode. Signed-off-by: Eric Paris --- fs/notify/dnotify/dnotify.c | 2 +- fs/notify/fanotify/fanotify_user.c | 4 ++-- fs/notify/fsnotify.h | 2 ++ fs/notify/inode_mark.c | 35 +++++++++++++++++++++++++++-------- fs/notify/inotify/inotify_user.c | 12 +++++------- fs/notify/mark.c | 17 ++++++++++++++++- include/linux/fsnotify_backend.h | 7 +++++-- 7 files changed, 58 insertions(+), 21 deletions(-) (limited to 'fs') diff --git a/fs/notify/dnotify/dnotify.c b/fs/notify/dnotify/dnotify.c index 69f42df9ba45..6624c2ee8786 100644 --- a/fs/notify/dnotify/dnotify.c +++ b/fs/notify/dnotify/dnotify.c @@ -65,7 +65,7 @@ static void dnotify_recalc_inode_mask(struct fsnotify_mark *fsn_mark) new_mask = 0; for (dn = dn_mark->dn; dn != NULL; dn = dn->dn_next) new_mask |= (dn->dn_mask & ~FS_DN_MULTISHOT); - fsn_mark->mask = new_mask; + fsnotify_set_mark_mask_locked(fsn_mark, new_mask); if (old_mask == new_mask) return; diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 84155841a025..3320f0c57e31 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -302,7 +302,7 @@ static __u32 fanotify_mark_remove_from_mask(struct fsnotify_mark *fsn_mark, __u3 spin_lock(&fsn_mark->lock); oldmask = fsn_mark->mask; - fsn_mark->mask = oldmask & ~mask; + fsnotify_set_mark_mask_locked(fsn_mark, (oldmask & ~mask)); spin_unlock(&fsn_mark->lock); if (!(oldmask & ~mask)) @@ -359,7 +359,7 @@ static __u32 fanotify_mark_add_to_mask(struct fsnotify_mark *fsn_mark, __u32 mas spin_lock(&fsn_mark->lock); oldmask = fsn_mark->mask; - fsn_mark->mask = oldmask | mask; + fsnotify_set_mark_mask_locked(fsn_mark, (oldmask | mask)); spin_unlock(&fsn_mark->lock); return mask & ~oldmask; diff --git a/fs/notify/fsnotify.h b/fs/notify/fsnotify.h index 204353c0f663..1be54f6f9e7d 100644 --- a/fs/notify/fsnotify.h +++ b/fs/notify/fsnotify.h @@ -20,6 +20,8 @@ extern __u32 fsnotify_vfsmount_mask; /* destroy all events sitting in this groups notification queue */ extern void fsnotify_flush_notify(struct fsnotify_group *group); +extern void fsnotify_set_inode_mark_mask_locked(struct fsnotify_mark *fsn_mark, + __u32 mask); /* add a mark to an inode */ extern int fsnotify_add_inode_mark(struct fsnotify_mark *mark, struct fsnotify_group *group, struct inode *inode, diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c index c925579ba011..4292f9e23ae8 100644 --- a/fs/notify/inode_mark.c +++ b/fs/notify/inode_mark.c @@ -141,7 +141,32 @@ struct fsnotify_mark *fsnotify_find_inode_mark(struct fsnotify_group *group, } /* - * Attach an initialized mark mark to a given group and inode. + * If we are setting a mark mask on an inode mark we should pin the inode + * in memory. + */ +void fsnotify_set_inode_mark_mask_locked(struct fsnotify_mark *mark, + __u32 mask) +{ + struct inode *inode; + + assert_spin_locked(&mark->lock); + + if (mask && + mark->i.inode && + !(mark->flags & FSNOTIFY_MARK_FLAG_OBJECT_PINNED)) { + mark->flags |= FSNOTIFY_MARK_FLAG_OBJECT_PINNED; + inode = igrab(mark->i.inode); + /* + * we shouldn't be able to get here if the inode wasn't + * already safely held in memory. But bug in case it + * ever is wrong. + */ + BUG_ON(!inode); + } +} + +/* + * Attach an initialized mark to a given group and inode. * These marks may be used for the fsnotify backend to determine which * event types should be delivered to which group and for which inodes. */ @@ -152,10 +177,6 @@ int fsnotify_add_inode_mark(struct fsnotify_mark *mark, struct fsnotify_mark *lmark = NULL; int ret = 0; - inode = igrab(inode); - if (unlikely(!inode)) - return -EINVAL; - mark->flags = FSNOTIFY_MARK_FLAG_INODE; assert_spin_locked(&mark->lock); @@ -175,10 +196,8 @@ int fsnotify_add_inode_mark(struct fsnotify_mark *mark, spin_unlock(&inode->i_lock); - if (lmark) { + if (lmark) ret = -EEXIST; - iput(inode); - } return ret; } diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index a12315a7553d..19d274057bfa 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -575,13 +575,11 @@ static int inotify_update_existing_watch(struct fsnotify_group *group, spin_lock(&fsn_mark->lock); old_mask = fsn_mark->mask; - if (add) { - fsn_mark->mask |= mask; - new_mask = fsn_mark->mask; - } else { - fsn_mark->mask = mask; - new_mask = fsn_mark->mask; - } + if (add) + fsnotify_set_mark_mask_locked(fsn_mark, (fsn_mark->mask | mask)); + else + fsnotify_set_mark_mask_locked(fsn_mark, mask); + new_mask = fsn_mark->mask; spin_unlock(&fsn_mark->lock); diff --git a/fs/notify/mark.c b/fs/notify/mark.c index d296ec9ffb2a..0ebc3fd7089b 100644 --- a/fs/notify/mark.c +++ b/fs/notify/mark.c @@ -168,7 +168,7 @@ void fsnotify_destroy_mark(struct fsnotify_mark *mark) * is just a lazy update (and could be a perf win...) */ - if (inode) + if (inode && (mark->flags & FSNOTIFY_MARK_FLAG_OBJECT_PINNED)) iput(inode); /* @@ -180,6 +180,17 @@ void fsnotify_destroy_mark(struct fsnotify_mark *mark) fsnotify_final_destroy_group(group); } +void fsnotify_set_mark_mask_locked(struct fsnotify_mark *mark, __u32 mask) +{ + assert_spin_locked(&mark->lock); + + mark->mask = mask; + + if (mark->flags & FSNOTIFY_MARK_FLAG_INODE) + fsnotify_set_inode_mark_mask_locked(mark, mask); +} + + /* * Attach an initialized mark to a given group and fs object. * These marks may be used for the fsnotify backend to determine which @@ -230,6 +241,10 @@ int fsnotify_add_mark(struct fsnotify_mark *mark, } spin_unlock(&group->mark_lock); + + /* this will pin the object if appropriate */ + fsnotify_set_mark_mask_locked(mark, mark->mask); + spin_unlock(&mark->lock); if (inode) diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index 2d2f015fb700..489c881ed4ec 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -267,8 +267,9 @@ struct fsnotify_mark { struct fsnotify_vfsmount_mark m; }; struct list_head free_g_list; /* tmp list used when freeing this mark */ -#define FSNOTIFY_MARK_FLAG_INODE 0x01 -#define FSNOTIFY_MARK_FLAG_VFSMOUNT 0x02 +#define FSNOTIFY_MARK_FLAG_INODE 0x01 +#define FSNOTIFY_MARK_FLAG_VFSMOUNT 0x02 +#define FSNOTIFY_MARK_FLAG_OBJECT_PINNED 0x04 unsigned int flags; /* vfsmount or inode mark? */ void (*free_mark)(struct fsnotify_mark *mark); /* called on final put+free */ }; @@ -372,6 +373,8 @@ extern struct fsnotify_mark *fsnotify_find_inode_mark(struct fsnotify_group *gro extern struct fsnotify_mark *fsnotify_find_vfsmount_mark(struct fsnotify_group *group, struct vfsmount *mnt); /* copy the values from old into new */ extern void fsnotify_duplicate_mark(struct fsnotify_mark *new, struct fsnotify_mark *old); +/* set the mask of a mark (might pin the object into memory */ +extern void fsnotify_set_mark_mask_locked(struct fsnotify_mark *mark, __u32 mask); /* attach the mark to both the group and the inode */ extern int fsnotify_add_mark(struct fsnotify_mark *mark, struct fsnotify_group *group, struct inode *inode, struct vfsmount *mnt, int allow_dups); -- cgit v1.2.3 From 33af5e32e0bb73c704b5e156f4411cdb53e6cc59 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:33 -0500 Subject: fsnotify: ignored_mask - excluding notification The ignored_mask is a new mask which is part of fsnotify marks. A group's should_send_event() function can use the ignored mask to determine that certain events are not of interest. In particular if a group registers a mask including FS_OPEN on a vfsmount they could add FS_OPEN to the ignored_mask for individual inodes and not send open events for those inodes. Signed-off-by: Eric Paris --- fs/notify/mark.c | 6 ++++++ include/linux/fsnotify_backend.h | 3 +++ 2 files changed, 9 insertions(+) (limited to 'fs') diff --git a/fs/notify/mark.c b/fs/notify/mark.c index 0ebc3fd7089b..cb1d822f227f 100644 --- a/fs/notify/mark.c +++ b/fs/notify/mark.c @@ -190,6 +190,12 @@ void fsnotify_set_mark_mask_locked(struct fsnotify_mark *mark, __u32 mask) fsnotify_set_inode_mark_mask_locked(mark, mask); } +void fsnotify_set_mark_ignored_mask_locked(struct fsnotify_mark *mark, __u32 mask) +{ + assert_spin_locked(&mark->lock); + + mark->ignored_mask = mask; +} /* * Attach an initialized mark to a given group and fs object. diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index 489c881ed4ec..018416ec5ce4 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -266,6 +266,7 @@ struct fsnotify_mark { struct fsnotify_inode_mark i; struct fsnotify_vfsmount_mark m; }; + __u32 ignored_mask; /* events types to ignore */ struct list_head free_g_list; /* tmp list used when freeing this mark */ #define FSNOTIFY_MARK_FLAG_INODE 0x01 #define FSNOTIFY_MARK_FLAG_VFSMOUNT 0x02 @@ -373,6 +374,8 @@ extern struct fsnotify_mark *fsnotify_find_inode_mark(struct fsnotify_group *gro extern struct fsnotify_mark *fsnotify_find_vfsmount_mark(struct fsnotify_group *group, struct vfsmount *mnt); /* copy the values from old into new */ extern void fsnotify_duplicate_mark(struct fsnotify_mark *new, struct fsnotify_mark *old); +/* set the ignored_mask of a mark */ +extern void fsnotify_set_mark_ignored_mask_locked(struct fsnotify_mark *mark, __u32 mask); /* set the mask of a mark (might pin the object into memory */ extern void fsnotify_set_mark_mask_locked(struct fsnotify_mark *mark, __u32 mask); /* attach the mark to both the group and the inode */ -- cgit v1.2.3 From 32a4df13b88afef2a7d869bb7586a7beba90961f Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:33 -0500 Subject: fanotify: ignored_mask to ignore events When fanotify receives an event it will check event->mask & ~ignored_mask. If no bits are left the event will not be sent. Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify.c | 37 +++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index f6900022f69e..060b177146e8 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -100,31 +100,39 @@ static int fanotify_handle_event(struct fsnotify_group *group, struct fsnotify_e } static bool should_send_vfsmount_event(struct fsnotify_group *group, struct vfsmount *mnt, - __u32 mask) + struct inode *inode, __u32 mask) { - struct fsnotify_mark *fsn_mark; - bool send; + struct fsnotify_mark *mnt_mark; + struct fsnotify_mark *inode_mark; pr_debug("%s: group=%p vfsmount=%p mask=%x\n", __func__, group, mnt, mask); - fsn_mark = fsnotify_find_vfsmount_mark(group, mnt); - if (!fsn_mark) + mnt_mark = fsnotify_find_vfsmount_mark(group, mnt); + if (!mnt_mark) return false; - send = (mask & fsn_mark->mask); + mask &= mnt_mark->mask; + mask &= ~mnt_mark->ignored_mask; + + if (mask) { + inode_mark = fsnotify_find_inode_mark(group, inode); + if (inode_mark) { + mask &= ~inode_mark->ignored_mask; + fsnotify_put_mark(inode_mark); + } + } /* find took a reference */ - fsnotify_put_mark(fsn_mark); + fsnotify_put_mark(mnt_mark); - return send; + return mask; } static bool should_send_inode_event(struct fsnotify_group *group, struct inode *inode, __u32 mask) { struct fsnotify_mark *fsn_mark; - bool send; pr_debug("%s: group=%p inode=%p mask=%x\n", __func__, group, inode, mask); @@ -137,20 +145,21 @@ static bool should_send_inode_event(struct fsnotify_group *group, struct inode * * events on the child, don't send it! */ if ((mask & FS_EVENT_ON_CHILD) && !(fsn_mark->mask & FS_EVENT_ON_CHILD)) { - send = false; + mask = 0; } else { /* * We care about children, but do we care about this particular * type of event? */ - mask = (mask & ~FS_EVENT_ON_CHILD); - send = (fsn_mark->mask & mask); + mask &= ~FS_EVENT_ON_CHILD; + mask &= fsn_mark->mask; + mask &= ~fsn_mark->ignored_mask; } /* find took a reference */ fsnotify_put_mark(fsn_mark); - return send; + return mask; } static bool fanotify_should_send_event(struct fsnotify_group *group, struct inode *to_tell, @@ -170,7 +179,7 @@ static bool fanotify_should_send_event(struct fsnotify_group *group, struct inod return false; if (mnt) - return should_send_vfsmount_event(group, mnt, mask); + return should_send_vfsmount_event(group, mnt, to_tell, mask); else return should_send_inode_event(group, to_tell, mask); } -- cgit v1.2.3 From b9e4e3bd0495fea9e8f8e712889c9cd8ffa43c94 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:33 -0500 Subject: fanotify: allow users to set an ignored_mask Change the sys_fanotify_mark() system call so users can set ignored_masks on inodes. Remember, if a user new sets a real mask, and only sets ignored masks, the ignore will never be pinned in memory. Thus ignored_masks can be lost under memory pressure and the user may again get events they previously thought were ignored. Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify_user.c | 54 +++++++++++++++++++++++++------------- include/linux/fanotify.h | 4 ++- 2 files changed, 39 insertions(+), 19 deletions(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 3320f0c57e31..ad02d475770f 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -296,13 +296,20 @@ out: return ret; } -static __u32 fanotify_mark_remove_from_mask(struct fsnotify_mark *fsn_mark, __u32 mask) +static __u32 fanotify_mark_remove_from_mask(struct fsnotify_mark *fsn_mark, + __u32 mask, + unsigned int flags) { __u32 oldmask; spin_lock(&fsn_mark->lock); - oldmask = fsn_mark->mask; - fsnotify_set_mark_mask_locked(fsn_mark, (oldmask & ~mask)); + if (!(flags & FAN_MARK_IGNORED_MASK)) { + oldmask = fsn_mark->mask; + fsnotify_set_mark_mask_locked(fsn_mark, (oldmask & ~mask)); + } else { + oldmask = fsn_mark->ignored_mask; + fsnotify_set_mark_ignored_mask_locked(fsn_mark, (oldmask & ~mask)); + } spin_unlock(&fsn_mark->lock); if (!(oldmask & ~mask)) @@ -312,7 +319,8 @@ static __u32 fanotify_mark_remove_from_mask(struct fsnotify_mark *fsn_mark, __u3 } static int fanotify_remove_vfsmount_mark(struct fsnotify_group *group, - struct vfsmount *mnt, __u32 mask) + struct vfsmount *mnt, __u32 mask, + unsigned int flags) { struct fsnotify_mark *fsn_mark = NULL; __u32 removed; @@ -321,7 +329,7 @@ static int fanotify_remove_vfsmount_mark(struct fsnotify_group *group, if (!fsn_mark) return -ENOENT; - removed = fanotify_mark_remove_from_mask(fsn_mark, mask); + removed = fanotify_mark_remove_from_mask(fsn_mark, mask, flags); fsnotify_put_mark(fsn_mark); if (removed & group->mask) fsnotify_recalc_group_mask(group); @@ -332,7 +340,8 @@ static int fanotify_remove_vfsmount_mark(struct fsnotify_group *group, } static int fanotify_remove_inode_mark(struct fsnotify_group *group, - struct inode *inode, __u32 mask) + struct inode *inode, __u32 mask, + unsigned int flags) { struct fsnotify_mark *fsn_mark = NULL; __u32 removed; @@ -341,7 +350,7 @@ static int fanotify_remove_inode_mark(struct fsnotify_group *group, if (!fsn_mark) return -ENOENT; - removed = fanotify_mark_remove_from_mask(fsn_mark, mask); + removed = fanotify_mark_remove_from_mask(fsn_mark, mask, flags); /* matches the fsnotify_find_inode_mark() */ fsnotify_put_mark(fsn_mark); @@ -353,20 +362,28 @@ static int fanotify_remove_inode_mark(struct fsnotify_group *group, return 0; } -static __u32 fanotify_mark_add_to_mask(struct fsnotify_mark *fsn_mark, __u32 mask) +static __u32 fanotify_mark_add_to_mask(struct fsnotify_mark *fsn_mark, + __u32 mask, + unsigned int flags) { __u32 oldmask; spin_lock(&fsn_mark->lock); - oldmask = fsn_mark->mask; - fsnotify_set_mark_mask_locked(fsn_mark, (oldmask | mask)); + if (!(flags & FAN_MARK_IGNORED_MASK)) { + oldmask = fsn_mark->mask; + fsnotify_set_mark_mask_locked(fsn_mark, (oldmask | mask)); + } else { + oldmask = fsn_mark->ignored_mask; + fsnotify_set_mark_ignored_mask_locked(fsn_mark, (oldmask | mask)); + } spin_unlock(&fsn_mark->lock); return mask & ~oldmask; } static int fanotify_add_vfsmount_mark(struct fsnotify_group *group, - struct vfsmount *mnt, __u32 mask) + struct vfsmount *mnt, __u32 mask, + unsigned int flags) { struct fsnotify_mark *fsn_mark; __u32 added; @@ -386,7 +403,7 @@ static int fanotify_add_vfsmount_mark(struct fsnotify_group *group, return ret; } } - added = fanotify_mark_add_to_mask(fsn_mark, mask); + added = fanotify_mark_add_to_mask(fsn_mark, mask, flags); fsnotify_put_mark(fsn_mark); if (added) { if (added & ~group->mask) @@ -398,7 +415,8 @@ static int fanotify_add_vfsmount_mark(struct fsnotify_group *group, } static int fanotify_add_inode_mark(struct fsnotify_group *group, - struct inode *inode, __u32 mask) + struct inode *inode, __u32 mask, + unsigned int flags) { struct fsnotify_mark *fsn_mark; __u32 added; @@ -420,7 +438,7 @@ static int fanotify_add_inode_mark(struct fsnotify_group *group, return ret; } } - added = fanotify_mark_add_to_mask(fsn_mark, mask); + added = fanotify_mark_add_to_mask(fsn_mark, mask, flags); fsnotify_put_mark(fsn_mark); if (added) { if (added & ~group->mask) @@ -528,15 +546,15 @@ SYSCALL_DEFINE(fanotify_mark)(int fanotify_fd, unsigned int flags, switch (flags & (FAN_MARK_ADD | FAN_MARK_REMOVE)) { case FAN_MARK_ADD: if (flags & FAN_MARK_MOUNT) - ret = fanotify_add_vfsmount_mark(group, mnt, mask); + ret = fanotify_add_vfsmount_mark(group, mnt, mask, flags); else - ret = fanotify_add_inode_mark(group, inode, mask); + ret = fanotify_add_inode_mark(group, inode, mask, flags); break; case FAN_MARK_REMOVE: if (flags & FAN_MARK_MOUNT) - ret = fanotify_remove_vfsmount_mark(group, mnt, mask); + ret = fanotify_remove_vfsmount_mark(group, mnt, mask, flags); else - ret = fanotify_remove_inode_mark(group, inode, mask); + ret = fanotify_remove_inode_mark(group, inode, mask, flags); break; default: ret = -EINVAL; diff --git a/include/linux/fanotify.h b/include/linux/fanotify.h index 90e59b24fd04..b8daa9f9b560 100644 --- a/include/linux/fanotify.h +++ b/include/linux/fanotify.h @@ -30,12 +30,14 @@ #define FAN_MARK_DONT_FOLLOW 0x00000004 #define FAN_MARK_ONLYDIR 0x00000008 #define FAN_MARK_MOUNT 0x00000010 +#define FAN_MARK_IGNORED_MASK 0x00000020 #define FAN_ALL_MARK_FLAGS (FAN_MARK_ADD |\ FAN_MARK_REMOVE |\ FAN_MARK_DONT_FOLLOW |\ FAN_MARK_ONLYDIR |\ - FAN_MARK_MOUNT) + FAN_MARK_MOUNT |\ + FAN_MARK_IGNORED_MASK) /* * All of the events - we build the list by hand so that we can add flags in -- cgit v1.2.3 From e898386146deb49a0b45ff1887d9da149c003209 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:33 -0500 Subject: fsnotify: clear ignored mask on modify On inode modification we clear the ignored mask for all of the marks on the inode. This allows userspace to ignore accesses to inodes until there is something different. Signed-off-by: Eric Paris --- fs/notify/fsnotify.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) (limited to 'fs') diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index 7f14ddc3efc2..3ad940d0bac1 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -140,6 +140,33 @@ void __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask) } EXPORT_SYMBOL_GPL(__fsnotify_parent); +void __fsnotify_flush_ignored_mask(struct inode *inode, void *data, int data_is) +{ + struct fsnotify_mark *mark; + struct hlist_node *node; + + if (!hlist_empty(&inode->i_fsnotify_marks)) { + spin_lock(&inode->i_lock); + hlist_for_each_entry(mark, node, &inode->i_fsnotify_marks, i.i_list) { + mark->ignored_mask = 0; + } + spin_unlock(&inode->i_lock); + } + + if (data_is == FSNOTIFY_EVENT_PATH) { + struct vfsmount *mnt; + + mnt = ((struct path *)data)->mnt; + if (mnt && !hlist_empty(&mnt->mnt_fsnotify_marks)) { + spin_lock(&mnt->mnt_root->d_lock); + hlist_for_each_entry(mark, node, &mnt->mnt_fsnotify_marks, m.m_list) { + mark->ignored_mask = 0; + } + spin_unlock(&mnt->mnt_root->d_lock); + } + } +} + static void send_to_group(struct fsnotify_group *group, struct inode *to_tell, struct vfsmount *mnt, __u32 mask, void *data, int data_is, u32 cookie, const char *file_name, @@ -170,6 +197,7 @@ static bool needed_by_vfsmount(__u32 test_mask, struct vfsmount *mnt) return (test_mask & mnt->mnt_fsnotify_mask); } + /* * This is the main call to fsnotify. The VFS calls into hook specific functions * in linux/fsnotify.h. Those functions then in turn call here. Here will call @@ -190,6 +218,9 @@ void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, const list_empty(&fsnotify_vfsmount_groups)) return; + if (mask & FS_MODIFY) + __fsnotify_flush_ignored_mask(to_tell, data, data_is); + /* if none of the directed listeners or vfsmount listeners care */ if (!(test_mask & fsnotify_inode_mask) && !(test_mask & fsnotify_vfsmount_mask)) -- cgit v1.2.3 From c908370fc1ac27fd7e1fc0f34c693047b26564ce Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:33 -0500 Subject: fsnotify: allow ignored_mask to survive modification Some inodes a group may want to never hear about a set of events even if the inode is modified. We add a new mark flag which indicates that these marks should not have their ignored_mask cleared on modification. Signed-off-by: Eric Paris --- fs/notify/fsnotify.c | 6 ++++-- include/linux/fsnotify_backend.h | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index 3ad940d0bac1..54d58d5f72c1 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -148,7 +148,8 @@ void __fsnotify_flush_ignored_mask(struct inode *inode, void *data, int data_is) if (!hlist_empty(&inode->i_fsnotify_marks)) { spin_lock(&inode->i_lock); hlist_for_each_entry(mark, node, &inode->i_fsnotify_marks, i.i_list) { - mark->ignored_mask = 0; + if (!(mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY)) + mark->ignored_mask = 0; } spin_unlock(&inode->i_lock); } @@ -160,7 +161,8 @@ void __fsnotify_flush_ignored_mask(struct inode *inode, void *data, int data_is) if (mnt && !hlist_empty(&mnt->mnt_fsnotify_marks)) { spin_lock(&mnt->mnt_root->d_lock); hlist_for_each_entry(mark, node, &mnt->mnt_fsnotify_marks, m.m_list) { - mark->ignored_mask = 0; + if (!(mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY)) + mark->ignored_mask = 0; } spin_unlock(&mnt->mnt_root->d_lock); } diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index 018416ec5ce4..8ca19df8a171 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -271,6 +271,7 @@ struct fsnotify_mark { #define FSNOTIFY_MARK_FLAG_INODE 0x01 #define FSNOTIFY_MARK_FLAG_VFSMOUNT 0x02 #define FSNOTIFY_MARK_FLAG_OBJECT_PINNED 0x04 +#define FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY 0x08 unsigned int flags; /* vfsmount or inode mark? */ void (*free_mark)(struct fsnotify_mark *mark); /* called on final put+free */ }; -- cgit v1.2.3 From c9778a98e7440fb73e0d27b8155a688663a0d493 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:33 -0500 Subject: fanotify: allow ignored_masks to survive modify Some users may want to truely ignore an inode even if it has been modified. Say you are wanting a mount which contains a log file and you really don't want any notification about that file. This patch allows the listener to do that. Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify_user.c | 2 ++ include/linux/fanotify.h | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index ad02d475770f..3e275f17e7b7 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -375,6 +375,8 @@ static __u32 fanotify_mark_add_to_mask(struct fsnotify_mark *fsn_mark, } else { oldmask = fsn_mark->ignored_mask; fsnotify_set_mark_ignored_mask_locked(fsn_mark, (oldmask | mask)); + if (flags & FAN_MARK_IGNORED_SURV_MODIFY) + fsn_mark->flags |= FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY; } spin_unlock(&fsn_mark->lock); diff --git a/include/linux/fanotify.h b/include/linux/fanotify.h index b8daa9f9b560..e43934d0b74c 100644 --- a/include/linux/fanotify.h +++ b/include/linux/fanotify.h @@ -31,13 +31,15 @@ #define FAN_MARK_ONLYDIR 0x00000008 #define FAN_MARK_MOUNT 0x00000010 #define FAN_MARK_IGNORED_MASK 0x00000020 +#define FAN_MARK_IGNORED_SURV_MODIFY 0x00000040 #define FAN_ALL_MARK_FLAGS (FAN_MARK_ADD |\ FAN_MARK_REMOVE |\ FAN_MARK_DONT_FOLLOW |\ FAN_MARK_ONLYDIR |\ FAN_MARK_MOUNT |\ - FAN_MARK_IGNORED_MASK) + FAN_MARK_IGNORED_MASK |\ + FAN_MARK_IGNORED_SURV_MODIFY) /* * All of the events - we build the list by hand so that we can add flags in -- cgit v1.2.3 From 4d92604cc90aa18bbbe0f6e23b7a9fdb612836d3 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:34 -0500 Subject: fanotify: clear all fanotify marks fanotify listeners may want to clear all marks. They may want to do this to destroy all of their inode marks which have nothing but ignores. Realistically this is useful for av vendors who update policy and want to clear all of their cached allows. Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify_user.c | 12 ++++++++++-- fs/notify/inode_mark.c | 8 ++++++++ fs/notify/mark.c | 21 ++++++++++++++++----- fs/notify/vfsmount_mark.c | 5 +++++ include/linux/fanotify.h | 1 + include/linux/fsnotify_backend.h | 6 ++++++ 6 files changed, 46 insertions(+), 7 deletions(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 3e275f17e7b7..9fe760baf69f 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -514,9 +514,10 @@ SYSCALL_DEFINE(fanotify_mark)(int fanotify_fd, unsigned int flags, if (flags & ~FAN_ALL_MARK_FLAGS) return -EINVAL; - switch (flags & (FAN_MARK_ADD | FAN_MARK_REMOVE)) { + switch (flags & (FAN_MARK_ADD | FAN_MARK_REMOVE | FAN_MARK_FLUSH)) { case FAN_MARK_ADD: case FAN_MARK_REMOVE: + case FAN_MARK_FLUSH: break; default: return -EINVAL; @@ -545,7 +546,7 @@ SYSCALL_DEFINE(fanotify_mark)(int fanotify_fd, unsigned int flags, group = filp->private_data; /* create/update an inode mark */ - switch (flags & (FAN_MARK_ADD | FAN_MARK_REMOVE)) { + switch (flags & (FAN_MARK_ADD | FAN_MARK_REMOVE | FAN_MARK_FLUSH)) { case FAN_MARK_ADD: if (flags & FAN_MARK_MOUNT) ret = fanotify_add_vfsmount_mark(group, mnt, mask, flags); @@ -558,6 +559,13 @@ SYSCALL_DEFINE(fanotify_mark)(int fanotify_fd, unsigned int flags, else ret = fanotify_remove_inode_mark(group, inode, mask, flags); break; + case FAN_MARK_FLUSH: + if (flags & FAN_MARK_MOUNT) + fsnotify_clear_vfsmount_marks_by_group(group); + else + fsnotify_clear_inode_marks_by_group(group); + fsnotify_recalc_group_mask(group); + break; default: ret = -EINVAL; } diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c index 4292f9e23ae8..0c0a48b1659f 100644 --- a/fs/notify/inode_mark.c +++ b/fs/notify/inode_mark.c @@ -103,6 +103,14 @@ void fsnotify_clear_marks_by_inode(struct inode *inode) } } +/* + * Given a group clear all of the inode marks associated with that group. + */ +void fsnotify_clear_inode_marks_by_group(struct fsnotify_group *group) +{ + fsnotify_clear_marks_by_group_flags(group, FSNOTIFY_MARK_FLAG_INODE); +} + /* * given a group and inode, find the mark associated with that combination. * if found take a reference to that mark and return it, else return NULL diff --git a/fs/notify/mark.c b/fs/notify/mark.c index cb1d822f227f..1e824e64441d 100644 --- a/fs/notify/mark.c +++ b/fs/notify/mark.c @@ -270,18 +270,21 @@ err: } /* - * Given a group, destroy all of the marks associated with that group. + * clear any marks in a group in which mark->flags & flags is true */ -void fsnotify_clear_marks_by_group(struct fsnotify_group *group) +void fsnotify_clear_marks_by_group_flags(struct fsnotify_group *group, + unsigned int flags) { struct fsnotify_mark *lmark, *mark; LIST_HEAD(free_list); spin_lock(&group->mark_lock); list_for_each_entry_safe(mark, lmark, &group->marks_list, g_list) { - list_add(&mark->free_g_list, &free_list); - list_del_init(&mark->g_list); - fsnotify_get_mark(mark); + if (mark->flags & flags) { + list_add(&mark->free_g_list, &free_list); + list_del_init(&mark->g_list); + fsnotify_get_mark(mark); + } } spin_unlock(&group->mark_lock); @@ -291,6 +294,14 @@ void fsnotify_clear_marks_by_group(struct fsnotify_group *group) } } +/* + * Given a group, destroy all of the marks associated with that group. + */ +void fsnotify_clear_marks_by_group(struct fsnotify_group *group) +{ + fsnotify_clear_marks_by_group_flags(group, (unsigned int)-1); +} + void fsnotify_duplicate_mark(struct fsnotify_mark *new, struct fsnotify_mark *old) { assert_spin_locked(&old->lock); diff --git a/fs/notify/vfsmount_mark.c b/fs/notify/vfsmount_mark.c index 1b61d0a942de..8f1aa02f4f02 100644 --- a/fs/notify/vfsmount_mark.c +++ b/fs/notify/vfsmount_mark.c @@ -51,6 +51,11 @@ void fsnotify_clear_marks_by_mount(struct vfsmount *mnt) } } +void fsnotify_clear_vfsmount_marks_by_group(struct fsnotify_group *group) +{ + fsnotify_clear_marks_by_group_flags(group, FSNOTIFY_MARK_FLAG_VFSMOUNT); +} + /* * Recalculate the mask of events relevant to a given vfsmount locked. */ diff --git a/include/linux/fanotify.h b/include/linux/fanotify.h index e43934d0b74c..385896c9f828 100644 --- a/include/linux/fanotify.h +++ b/include/linux/fanotify.h @@ -32,6 +32,7 @@ #define FAN_MARK_MOUNT 0x00000010 #define FAN_MARK_IGNORED_MASK 0x00000020 #define FAN_MARK_IGNORED_SURV_MODIFY 0x00000040 +#define FAN_MARK_FLUSH 0x00000080 #define FAN_ALL_MARK_FLAGS (FAN_MARK_ADD |\ FAN_MARK_REMOVE |\ diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index 8ca19df8a171..be4a36ed2008 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -384,6 +384,12 @@ extern int fsnotify_add_mark(struct fsnotify_mark *mark, struct fsnotify_group * struct inode *inode, struct vfsmount *mnt, int allow_dups); /* given a mark, flag it to be freed when all references are dropped */ extern void fsnotify_destroy_mark(struct fsnotify_mark *mark); +/* run all the marks in a group, and clear all of the vfsmount marks */ +extern void fsnotify_clear_vfsmount_marks_by_group(struct fsnotify_group *group); +/* run all the marks in a group, and clear all of the inode marks */ +extern void fsnotify_clear_inode_marks_by_group(struct fsnotify_group *group); +/* run all the marks in a group, and clear all of the marks where mark->flags & flags is true*/ +extern void fsnotify_clear_marks_by_group_flags(struct fsnotify_group *group, unsigned int flags); /* run all the marks in a group, and flag them to be freed */ extern void fsnotify_clear_marks_by_group(struct fsnotify_group *group); extern void fsnotify_get_mark(struct fsnotify_mark *mark); -- cgit v1.2.3 From cb2d429faf2cae62d3c51e28099a181d5fe8c244 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:34 -0500 Subject: fsnotify: add group priorities This introduces an ordering to fsnotify groups. With purely asynchronous notification based "things" implementing fsnotify (inotify, dnotify) ordering isn't particularly important. But if people want to use fsnotify for the basis of sycronous notification or blocking notification ordering becomes important. eg. A Hierarchical Storage Management listener would need to get its event before an AV scanner could get its event (since the HSM would need to bring the data in for the AV scanner to scan.) Typically asynchronous notification would want to run after the AV scanner made any relevant access decisions so as to not send notification about an event that was denied. Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify_user.c | 4 ++-- fs/notify/group.c | 40 ++++++++++++++++++++++++++++++++++++-- include/linux/fsnotify_backend.h | 1 + 3 files changed, 41 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 9fe760baf69f..84d3e2047de3 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -463,8 +463,6 @@ SYSCALL_DEFINE3(fanotify_init, unsigned int, flags, unsigned int, event_f_flags, if (event_f_flags) return -EINVAL; - if (priority) - return -EINVAL; if (!capable(CAP_SYS_ADMIN)) return -EACCES; @@ -483,6 +481,8 @@ SYSCALL_DEFINE3(fanotify_init, unsigned int, flags, unsigned int, event_f_flags, if (IS_ERR(group)) return PTR_ERR(group); + group->priority = priority; + fd = anon_inode_getfd("[fanotify]", &fanotify_fops, group, f_flags); if (fd < 0) goto out_put_group; diff --git a/fs/notify/group.c b/fs/notify/group.c index 9e9eb406afdd..ada913fd4f7f 100644 --- a/fs/notify/group.c +++ b/fs/notify/group.c @@ -89,10 +89,27 @@ void fsnotify_recalc_group_mask(struct fsnotify_group *group) void fsnotify_add_vfsmount_group(struct fsnotify_group *group) { + struct fsnotify_group *group_iter; + unsigned int priority = group->priority; + mutex_lock(&fsnotify_grp_mutex); - if (!group->on_vfsmount_group_list) + if (!group->on_vfsmount_group_list) { + list_for_each_entry(group_iter, &fsnotify_vfsmount_groups, + vfsmount_group_list) { + /* insert in front of this one? */ + if (priority < group_iter->priority) { + /* list_add_tail() insert in front of group_iter */ + list_add_tail_rcu(&group->inode_group_list, + &group_iter->inode_group_list); + goto out; + } + } + + /* apparently we need to be the last entry */ list_add_tail_rcu(&group->vfsmount_group_list, &fsnotify_vfsmount_groups); + } +out: group->on_vfsmount_group_list = 1; mutex_unlock(&fsnotify_grp_mutex); @@ -100,10 +117,27 @@ void fsnotify_add_vfsmount_group(struct fsnotify_group *group) void fsnotify_add_inode_group(struct fsnotify_group *group) { + struct fsnotify_group *group_iter; + unsigned int priority = group->priority; + mutex_lock(&fsnotify_grp_mutex); - if (!group->on_inode_group_list) + /* add to global group list, priority 0 first, UINT_MAX last */ + if (!group->on_inode_group_list) { + list_for_each_entry(group_iter, &fsnotify_inode_groups, + inode_group_list) { + if (priority < group_iter->priority) { + /* list_add_tail() insert in front of group_iter */ + list_add_tail_rcu(&group->inode_group_list, + &group_iter->inode_group_list); + goto out; + } + } + + /* apparently we need to be the last entry */ list_add_tail_rcu(&group->inode_group_list, &fsnotify_inode_groups); + } +out: group->on_inode_group_list = 1; mutex_unlock(&fsnotify_grp_mutex); @@ -226,6 +260,8 @@ struct fsnotify_group *fsnotify_alloc_group(const struct fsnotify_ops *ops) spin_lock_init(&group->mark_lock); INIT_LIST_HEAD(&group->marks_list); + group->priority = UINT_MAX; + group->ops = ops; return group; diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index be4a36ed2008..8b2e095e5907 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -140,6 +140,7 @@ struct fsnotify_group { * a group */ struct list_head marks_list; /* all inode marks for this group */ + unsigned int priority; /* order of this group compared to others */ /* prevents double list_del of group_list. protected by global fsnotify_grp_mutex */ bool on_inode_group_list; bool on_vfsmount_group_list; -- cgit v1.2.3 From 6e5f77b32e9097a8a68a8d453799676cacf70cad Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:34 -0500 Subject: fsnotify: intoduce a notification merge argument Each group can define their own notification (and secondary_q) merge function. Inotify does tail drop, fanotify does matching and drop which can actually allocate a completely new event. But for fanotify to properly deal with permissions events it needs to know the new event which was ultimately added to the notification queue. This patch just implements a void ** argument which is passed to the merge function. fanotify can use this field to pass the new event back to higher layers. Signed-off-by: Eric Paris for fanotify to properly deal with permissions events --- fs/notify/fanotify/fanotify.c | 6 ++++-- fs/notify/inotify/inotify_fsnotify.c | 6 ++++-- fs/notify/inotify/inotify_user.c | 2 +- fs/notify/notification.c | 7 +++++-- include/linux/fsnotify_backend.h | 5 ++++- 5 files changed, 18 insertions(+), 8 deletions(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index 060b177146e8..95a330d2f8a1 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -27,7 +27,9 @@ static bool should_merge(struct fsnotify_event *old, struct fsnotify_event *new) return false; } -static int fanotify_merge(struct list_head *list, struct fsnotify_event *event) +static int fanotify_merge(struct list_head *list, + struct fsnotify_event *event, + void **arg) { struct fsnotify_event_holder *test_holder; struct fsnotify_event *test_event; @@ -92,7 +94,7 @@ static int fanotify_handle_event(struct fsnotify_group *group, struct fsnotify_e pr_debug("%s: group=%p event=%p\n", __func__, group, event); - ret = fsnotify_add_notify_event(group, event, NULL, fanotify_merge); + ret = fsnotify_add_notify_event(group, event, NULL, fanotify_merge, NULL); /* -EEXIST means this event was merged with another, not that it was an error */ if (ret == -EEXIST) ret = 0; diff --git a/fs/notify/inotify/inotify_fsnotify.c b/fs/notify/inotify/inotify_fsnotify.c index 1d237e1bf7b1..daa666a6e6c9 100644 --- a/fs/notify/inotify/inotify_fsnotify.c +++ b/fs/notify/inotify/inotify_fsnotify.c @@ -67,7 +67,9 @@ static bool event_compare(struct fsnotify_event *old, struct fsnotify_event *new return false; } -static int inotify_merge(struct list_head *list, struct fsnotify_event *event) +static int inotify_merge(struct list_head *list, + struct fsnotify_event *event, + void **arg) { struct fsnotify_event_holder *last_holder; struct fsnotify_event *last_event; @@ -114,7 +116,7 @@ static int inotify_handle_event(struct fsnotify_group *group, struct fsnotify_ev fsn_event_priv->group = group; event_priv->wd = wd; - ret = fsnotify_add_notify_event(group, event, fsn_event_priv, inotify_merge); + ret = fsnotify_add_notify_event(group, event, fsn_event_priv, inotify_merge, NULL); if (ret) { inotify_free_event_priv(fsn_event_priv); /* EEXIST says we tail matched, EOVERFLOW isn't something diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index 19d274057bfa..1ce71f5b9589 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -525,7 +525,7 @@ void inotify_ignored_and_remove_idr(struct fsnotify_mark *fsn_mark, fsn_event_priv->group = group; event_priv->wd = i_mark->wd; - ret = fsnotify_add_notify_event(group, ignored_event, fsn_event_priv, NULL); + ret = fsnotify_add_notify_event(group, ignored_event, fsn_event_priv, NULL, NULL); if (ret) inotify_free_event_priv(fsn_event_priv); diff --git a/fs/notify/notification.c b/fs/notify/notification.c index 7fc8d004084c..2d50a40ab1e4 100644 --- a/fs/notify/notification.c +++ b/fs/notify/notification.c @@ -137,7 +137,10 @@ struct fsnotify_event_private_data *fsnotify_remove_priv_from_event(struct fsnot */ int fsnotify_add_notify_event(struct fsnotify_group *group, struct fsnotify_event *event, struct fsnotify_event_private_data *priv, - int (*merge)(struct list_head *, struct fsnotify_event *)) + int (*merge)(struct list_head *, + struct fsnotify_event *, + void **arg), + void **arg) { struct fsnotify_event_holder *holder = NULL; struct list_head *list = &group->notification_list; @@ -170,7 +173,7 @@ alloc_holder: if (!list_empty(list) && merge) { int ret; - ret = merge(list, event); + ret = merge(list, event, arg); if (ret) { mutex_unlock(&group->notification_mutex); if (holder != &event->holder) diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index 8b2e095e5907..afc690192972 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -355,7 +355,10 @@ extern struct fsnotify_event_private_data *fsnotify_remove_priv_from_event(struc extern int fsnotify_add_notify_event(struct fsnotify_group *group, struct fsnotify_event *event, struct fsnotify_event_private_data *priv, - int (*merge)(struct list_head *, struct fsnotify_event *)); + int (*merge)(struct list_head *, + struct fsnotify_event *, + void **), + void **arg); /* true if the group notification queue is empty */ extern bool fsnotify_notify_queue_is_empty(struct fsnotify_group *group); /* return, but do not dequeue the first event on the notification queue */ -- cgit v1.2.3 From 43ed7e16a8b47059d7f6ff67ba76f383a2421de3 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:34 -0500 Subject: fanotify: use merge argument to determine actual event added to queue fanotify needs to know the actual event added to queues so it can be correctly checked for return values from userspace. To do this we need to pass that information from the merger code back to the main even handling routine. Currently that information is unused, but it will be. Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index 95a330d2f8a1..4feed8601e29 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -27,6 +27,7 @@ static bool should_merge(struct fsnotify_event *old, struct fsnotify_event *new) return false; } +/* Note, if we return an event in *arg that a reference is being held... */ static int fanotify_merge(struct list_head *list, struct fsnotify_event *event, void **arg) @@ -34,17 +35,22 @@ static int fanotify_merge(struct list_head *list, struct fsnotify_event_holder *test_holder; struct fsnotify_event *test_event; struct fsnotify_event *new_event; + struct fsnotify_event **return_event = (struct fsnotify_event **)arg; int ret = 0; pr_debug("%s: list=%p event=%p\n", __func__, list, event); + *return_event = NULL; + /* and the list better be locked by something too! */ list_for_each_entry_reverse(test_holder, list, event_list) { test_event = test_holder->event; if (should_merge(test_event, event)) { - ret = -EEXIST; + fsnotify_get_event(test_event); + *return_event = test_event; + ret = -EEXIST; /* if they are exactly the same we are done */ if (test_event->mask == event->mask) goto out; @@ -66,11 +72,14 @@ static int fanotify_merge(struct list_head *list, goto out; } + /* we didn't return the test_event, so drop that ref */ + fsnotify_put_event(test_event); + /* the reference we return on new_event is from clone */ + *return_event = new_event; + /* build new event and replace it on the list */ new_event->mask = (test_event->mask | event->mask); fsnotify_replace_event(test_holder, new_event); - /* match ref from fsnotify_clone_event() */ - fsnotify_put_event(new_event); break; } @@ -82,7 +91,7 @@ out: static int fanotify_handle_event(struct fsnotify_group *group, struct fsnotify_event *event) { int ret; - + struct fsnotify_event *used_event; BUILD_BUG_ON(FAN_ACCESS != FS_ACCESS); BUILD_BUG_ON(FAN_MODIFY != FS_MODIFY); @@ -94,10 +103,12 @@ static int fanotify_handle_event(struct fsnotify_group *group, struct fsnotify_e pr_debug("%s: group=%p event=%p\n", __func__, group, event); - ret = fsnotify_add_notify_event(group, event, NULL, fanotify_merge, NULL); + ret = fsnotify_add_notify_event(group, event, NULL, fanotify_merge, (void **)&used_event); /* -EEXIST means this event was merged with another, not that it was an error */ if (ret == -EEXIST) ret = 0; + if (used_event) + fsnotify_put_event(used_event); return ret; } -- cgit v1.2.3 From 59b0df211bd9699d7e0d01fcf9345a149f75b033 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Mon, 8 Feb 2010 12:53:52 -0500 Subject: fsnotify: use unsigned char * for dentry->d_name.name fsnotify was using char * when it passed around the d_name.name string internally but it is actually an unsigned char *. This patch switches fsnotify to use unsigned and should silence some pointer signess warnings which have popped out of xfs. I do not add -Wpointer-sign to the fsnotify code as there are still issues with kstrdup and strlen which would pop out needless warnings. Signed-off-by: Eric Paris --- fs/namei.c | 2 +- fs/notify/fsnotify.c | 5 +++-- fs/notify/notification.c | 4 ++-- include/linux/fsnotify.h | 12 ++++++------ include/linux/fsnotify_backend.h | 9 +++++---- 5 files changed, 17 insertions(+), 15 deletions(-) (limited to 'fs') diff --git a/fs/namei.c b/fs/namei.c index 868d0cb9d473..3479b176a4cd 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2635,7 +2635,7 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry, { int error; int is_dir = S_ISDIR(old_dentry->d_inode->i_mode); - const char *old_name; + const unsigned char *old_name; if (old_dentry->d_inode == new_dentry->d_inode) return 0; diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index 54d58d5f72c1..c5adf833bf6a 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -171,7 +171,7 @@ void __fsnotify_flush_ignored_mask(struct inode *inode, void *data, int data_is) static void send_to_group(struct fsnotify_group *group, struct inode *to_tell, struct vfsmount *mnt, __u32 mask, void *data, - int data_is, u32 cookie, const char *file_name, + int data_is, u32 cookie, const unsigned char *file_name, struct fsnotify_event **event) { if (!group->ops->should_send_event(group, to_tell, mnt, mask, @@ -206,7 +206,8 @@ static bool needed_by_vfsmount(__u32 test_mask, struct vfsmount *mnt) * out to all of the registered fsnotify_group. Those groups can then use the * notification event in whatever means they feel necessary. */ -void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, const char *file_name, u32 cookie) +void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, + const unsigned char *file_name, u32 cookie) { struct fsnotify_group *group; struct fsnotify_event *event = NULL; diff --git a/fs/notify/notification.c b/fs/notify/notification.c index 2d50a40ab1e4..b35faafacd38 100644 --- a/fs/notify/notification.c +++ b/fs/notify/notification.c @@ -370,8 +370,8 @@ struct fsnotify_event *fsnotify_clone_event(struct fsnotify_event *old_event) * @name the filename, if available */ struct fsnotify_event *fsnotify_create_event(struct inode *to_tell, __u32 mask, void *data, - int data_type, const char *name, u32 cookie, - gfp_t gfp) + int data_type, const unsigned char *name, + u32 cookie, gfp_t gfp) { struct fsnotify_event *event; diff --git a/include/linux/fsnotify.h b/include/linux/fsnotify.h index 06c0e50c7968..b8cf161f5a6d 100644 --- a/include/linux/fsnotify.h +++ b/include/linux/fsnotify.h @@ -59,14 +59,14 @@ static inline void fsnotify_link_count(struct inode *inode) * fsnotify_move - file old_name at old_dir was moved to new_name at new_dir */ static inline void fsnotify_move(struct inode *old_dir, struct inode *new_dir, - const char *old_name, + const unsigned char *old_name, int isdir, struct inode *target, struct dentry *moved) { struct inode *source = moved->d_inode; u32 fs_cookie = fsnotify_get_cookie(); __u32 old_dir_mask = (FS_EVENT_ON_CHILD | FS_MOVED_FROM); __u32 new_dir_mask = (FS_EVENT_ON_CHILD | FS_MOVED_TO); - const char *new_name = moved->d_name.name; + const unsigned char *new_name = moved->d_name.name; if (old_dir == new_dir) old_dir_mask |= FS_DN_RENAME; @@ -290,7 +290,7 @@ static inline void fsnotify_change(struct dentry *dentry, unsigned int ia_valid) /* * fsnotify_oldname_init - save off the old filename before we change it */ -static inline const char *fsnotify_oldname_init(const char *name) +static inline const unsigned char *fsnotify_oldname_init(const unsigned char *name) { return kstrdup(name, GFP_KERNEL); } @@ -298,19 +298,19 @@ static inline const char *fsnotify_oldname_init(const char *name) /* * fsnotify_oldname_free - free the name we got from fsnotify_oldname_init */ -static inline void fsnotify_oldname_free(const char *old_name) +static inline void fsnotify_oldname_free(const unsigned char *old_name) { kfree(old_name); } #else /* CONFIG_FSNOTIFY */ -static inline const char *fsnotify_oldname_init(const char *name) +static inline const char *fsnotify_oldname_init(const unsigned char *name) { return NULL; } -static inline void fsnotify_oldname_free(const char *old_name) +static inline void fsnotify_oldname_free(const unsigned char *old_name) { } diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index afc690192972..efe9ba321cf2 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -220,7 +220,7 @@ struct fsnotify_event { __u32 mask; /* the type of access, bitwise OR for FS_* event types */ u32 sync_cookie; /* used to corrolate events, namely inotify mv events */ - char *file_name; + const unsigned char *file_name; size_t name_len; struct pid *tgid; @@ -283,7 +283,7 @@ struct fsnotify_mark { /* main fsnotify call to send events */ extern void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, - const char *name, u32 cookie); + const unsigned char *name, u32 cookie); extern void __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask); extern void __fsnotify_inode_delete(struct inode *inode); extern void __fsnotify_vfsmount_delete(struct vfsmount *mnt); @@ -402,7 +402,8 @@ extern void fsnotify_unmount_inodes(struct list_head *list); /* put here because inotify does some weird stuff when destroying watches */ extern struct fsnotify_event *fsnotify_create_event(struct inode *to_tell, __u32 mask, - void *data, int data_is, const char *name, + void *data, int data_is, + const unsigned char *name, u32 cookie, gfp_t gfp); /* fanotify likes to change events after they are on lists... */ @@ -413,7 +414,7 @@ extern int fsnotify_replace_event(struct fsnotify_event_holder *old_holder, #else static inline void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, - const char *name, u32 cookie) + const unsigned char *name, u32 cookie) {} static inline void __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask) -- cgit v1.2.3 From c4ec54b40d33f8016fea970a383cc584dd0e6019 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:34 -0500 Subject: fsnotify: new fsnotify hooks and events types for access decisions introduce a new fsnotify hook, fsnotify_perm(), which is called from the security code. This hook is used to allow fsnotify groups to make access control decisions about events on the system. We also must change the generic fsnotify function to return an error code if we intend these hooks to be in any way useful. Signed-off-by: Eric Paris --- fs/notify/fsnotify.c | 47 ++++++++++++++++++++-------------------- include/linux/fsnotify.h | 19 ++++++++++++++++ include/linux/fsnotify_backend.h | 15 ++++++++----- include/linux/security.h | 1 + security/security.c | 16 ++++++++++++-- 5 files changed, 68 insertions(+), 30 deletions(-) (limited to 'fs') diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index c5adf833bf6a..668268627894 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -169,27 +169,22 @@ void __fsnotify_flush_ignored_mask(struct inode *inode, void *data, int data_is) } } -static void send_to_group(struct fsnotify_group *group, struct inode *to_tell, - struct vfsmount *mnt, __u32 mask, void *data, - int data_is, u32 cookie, const unsigned char *file_name, - struct fsnotify_event **event) +static int send_to_group(struct fsnotify_group *group, struct inode *to_tell, + struct vfsmount *mnt, __u32 mask, void *data, + int data_is, u32 cookie, const unsigned char *file_name, + struct fsnotify_event **event) { if (!group->ops->should_send_event(group, to_tell, mnt, mask, data, data_is)) - return; + return 0; if (!*event) { *event = fsnotify_create_event(to_tell, mask, data, data_is, file_name, cookie, GFP_KERNEL); - /* - * shit, we OOM'd and now we can't tell, maybe - * someday someone else will want to do something - * here - */ if (!*event) - return; + return -ENOMEM; } - group->ops->handle_event(group, *event); + return group->ops->handle_event(group, *event); } static bool needed_by_vfsmount(__u32 test_mask, struct vfsmount *mnt) @@ -206,20 +201,20 @@ static bool needed_by_vfsmount(__u32 test_mask, struct vfsmount *mnt) * out to all of the registered fsnotify_group. Those groups can then use the * notification event in whatever means they feel necessary. */ -void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, - const unsigned char *file_name, u32 cookie) +int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, + const unsigned char *file_name, u32 cookie) { struct fsnotify_group *group; struct fsnotify_event *event = NULL; struct vfsmount *mnt = NULL; - int idx; + int idx, ret = 0; /* global tests shouldn't care about events on child only the specific event */ __u32 test_mask = (mask & ~FS_EVENT_ON_CHILD); /* if no fsnotify listeners, nothing to do */ if (list_empty(&fsnotify_inode_groups) && list_empty(&fsnotify_vfsmount_groups)) - return; + return 0; if (mask & FS_MODIFY) __fsnotify_flush_ignored_mask(to_tell, data, data_is); @@ -227,7 +222,7 @@ void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, /* if none of the directed listeners or vfsmount listeners care */ if (!(test_mask & fsnotify_inode_mask) && !(test_mask & fsnotify_vfsmount_mask)) - return; + return 0; if (data_is == FSNOTIFY_EVENT_PATH) mnt = ((struct path *)data)->mnt; @@ -236,7 +231,7 @@ void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, * listeners list cares, nothing to do */ if (!(test_mask & to_tell->i_fsnotify_mask) && !needed_by_vfsmount(test_mask, mnt)) - return; + return 0; /* * SRCU!! the groups list is very very much read only and the path is @@ -248,20 +243,24 @@ void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, if (test_mask & to_tell->i_fsnotify_mask) { list_for_each_entry_rcu(group, &fsnotify_inode_groups, inode_group_list) { if (test_mask & group->mask) { - send_to_group(group, to_tell, NULL, mask, data, data_is, - cookie, file_name, &event); + ret = send_to_group(group, to_tell, NULL, mask, data, data_is, + cookie, file_name, &event); + if (ret) + goto out; } } } if (needed_by_vfsmount(test_mask, mnt)) { list_for_each_entry_rcu(group, &fsnotify_vfsmount_groups, vfsmount_group_list) { if (test_mask & group->mask) { - send_to_group(group, to_tell, mnt, mask, data, data_is, - cookie, file_name, &event); + ret = send_to_group(group, to_tell, mnt, mask, data, data_is, + cookie, file_name, &event); + if (ret) + goto out; } } } - +out: srcu_read_unlock(&fsnotify_grp_srcu, idx); /* * fsnotify_create_event() took a reference so the event can't be cleaned @@ -269,6 +268,8 @@ void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, */ if (event) fsnotify_put_event(event); + + return 0; } EXPORT_SYMBOL_GPL(fsnotify); diff --git a/include/linux/fsnotify.h b/include/linux/fsnotify.h index b8cf161f5a6d..64efda9aae62 100644 --- a/include/linux/fsnotify.h +++ b/include/linux/fsnotify.h @@ -34,6 +34,25 @@ static inline void fsnotify_parent(struct path *path, struct dentry *dentry, __u __fsnotify_parent(path, dentry, mask); } +/* simple call site for access decisions */ +static inline int fsnotify_perm(struct file *file, int mask) +{ + struct path *path = &file->f_path; + struct inode *inode = path->dentry->d_inode; + __u32 fsnotify_mask; + + if (file->f_mode & FMODE_NONOTIFY) + return 0; + if (!(mask & (MAY_READ | MAY_OPEN))) + return 0; + if (mask & MAY_READ) + fsnotify_mask = FS_ACCESS_PERM; + if (mask & MAY_OPEN) + fsnotify_mask = FS_OPEN_PERM; + + return fsnotify(inode, fsnotify_mask, path, FSNOTIFY_EVENT_PATH, NULL, 0); +} + /* * fsnotify_d_move - dentry has been moved * Called with dcache_lock and dentry->d_lock held. diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index efe9ba321cf2..c34728e7d8cb 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -41,6 +41,9 @@ #define FS_Q_OVERFLOW 0x00004000 /* Event queued overflowed */ #define FS_IN_IGNORED 0x00008000 /* last inotify event here */ +#define FS_OPEN_PERM 0x00010000 /* open event in an permission hook */ +#define FS_ACCESS_PERM 0x00020000 /* access event in a permissions hook */ + #define FS_IN_ISDIR 0x40000000 /* event occurred against dir */ #define FS_IN_ONESHOT 0x80000000 /* only send event once */ @@ -282,8 +285,8 @@ struct fsnotify_mark { /* called from the vfs helpers */ /* main fsnotify call to send events */ -extern void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, - const unsigned char *name, u32 cookie); +extern int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, + const unsigned char *name, u32 cookie); extern void __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask); extern void __fsnotify_inode_delete(struct inode *inode); extern void __fsnotify_vfsmount_delete(struct vfsmount *mnt); @@ -413,9 +416,11 @@ extern int fsnotify_replace_event(struct fsnotify_event_holder *old_holder, #else -static inline void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, - const unsigned char *name, u32 cookie) -{} +static inline int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, + const unsigned char *name, u32 cookie) +{ + return 0; +} static inline void __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask) {} diff --git a/include/linux/security.h b/include/linux/security.h index 0c8819170463..24fc29540aa3 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -23,6 +23,7 @@ #define __LINUX_SECURITY_H #include +#include #include #include #include diff --git a/security/security.c b/security/security.c index 351942a4ca0e..f6ac27cd3452 100644 --- a/security/security.c +++ b/security/security.c @@ -620,7 +620,13 @@ void security_inode_getsecid(const struct inode *inode, u32 *secid) int security_file_permission(struct file *file, int mask) { - return security_ops->file_permission(file, mask); + int ret; + + ret = security_ops->file_permission(file, mask); + if (ret) + return ret; + + return fsnotify_perm(file, mask); } int security_file_alloc(struct file *file) @@ -684,7 +690,13 @@ int security_file_receive(struct file *file) int security_dentry_open(struct file *file, const struct cred *cred) { - return security_ops->dentry_open(file, cred); + int ret; + + ret = security_ops->dentry_open(file, cred); + if (ret) + return ret; + + return fsnotify_perm(file, MAY_OPEN); } int security_task_create(unsigned long clone_flags) -- cgit v1.2.3 From 9e66e4233db9c7e31e9ee706be2c9ddd54cf99b3 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:34 -0500 Subject: fanotify: permissions and blocking This is the backend work needed for fanotify to support the new FS_OPEN_PERM and FS_ACCESS_PERM fsnotify events. This is done using the new fsnotify secondary queue. No userspace interface is provided actually respond to or request these events. Signed-off-by: Eric Paris --- fs/notify/fanotify/Kconfig | 14 ++++++++++ fs/notify/fanotify/fanotify.c | 54 +++++++++++++++++++++++++++++++++++--- fs/notify/fanotify/fanotify_user.c | 5 ++++ include/linux/fanotify.h | 18 +++++++++++++ include/linux/fsnotify_backend.h | 12 +++++++++ 5 files changed, 99 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/notify/fanotify/Kconfig b/fs/notify/fanotify/Kconfig index 668e5df28e28..566de30395c2 100644 --- a/fs/notify/fanotify/Kconfig +++ b/fs/notify/fanotify/Kconfig @@ -10,3 +10,17 @@ config FANOTIFY the event. If unsure, say Y. + +config FANOTIFY_ACCESS_PERMISSIONS + bool "fanotify permissions checking" + depends on FANOTIFY + depends on SECURITY + default n + ---help--- + Say Y here is you want fanotify listeners to be able to make permissions + decisions concerning filesystem events. This is used by some fanotify + listeners which need to scan files before allowing the system access to + use those files. This is used by some anti-malware vendors and by some + hierarchical storage managent systems. + + If unsure, say N. diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index 4feed8601e29..52d0a55a249e 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -2,9 +2,12 @@ #include #include #include +#include #include /* UINT_MAX */ #include +#include #include +#include static bool should_merge(struct fsnotify_event *old, struct fsnotify_event *new) { @@ -88,10 +91,37 @@ out: return ret; } +#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS +static int fanotify_get_response_from_access(struct fsnotify_group *group, + struct fsnotify_event *event) +{ + int ret; + + pr_debug("%s: group=%p event=%p\n", __func__, group, event); + + wait_event(group->fanotify_data.access_waitq, event->response); + + /* userspace responded, convert to something usable */ + spin_lock(&event->lock); + switch (event->response) { + case FAN_ALLOW: + ret = 0; + break; + case FAN_DENY: + default: + ret = -EPERM; + } + event->response = 0; + spin_unlock(&event->lock); + + return ret; +} +#endif + static int fanotify_handle_event(struct fsnotify_group *group, struct fsnotify_event *event) { int ret; - struct fsnotify_event *used_event; + struct fsnotify_event *notify_event = NULL; BUILD_BUG_ON(FAN_ACCESS != FS_ACCESS); BUILD_BUG_ON(FAN_MODIFY != FS_MODIFY); @@ -100,15 +130,31 @@ static int fanotify_handle_event(struct fsnotify_group *group, struct fsnotify_e BUILD_BUG_ON(FAN_OPEN != FS_OPEN); BUILD_BUG_ON(FAN_EVENT_ON_CHILD != FS_EVENT_ON_CHILD); BUILD_BUG_ON(FAN_Q_OVERFLOW != FS_Q_OVERFLOW); + BUILD_BUG_ON(FAN_OPEN_PERM != FS_OPEN_PERM); + BUILD_BUG_ON(FAN_ACCESS_PERM != FS_ACCESS_PERM); pr_debug("%s: group=%p event=%p\n", __func__, group, event); - ret = fsnotify_add_notify_event(group, event, NULL, fanotify_merge, (void **)&used_event); + ret = fsnotify_add_notify_event(group, event, NULL, fanotify_merge, + (void **)¬ify_event); /* -EEXIST means this event was merged with another, not that it was an error */ if (ret == -EEXIST) ret = 0; - if (used_event) - fsnotify_put_event(used_event); + if (ret) + goto out; + +#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS + if (event->mask & FAN_ALL_PERM_EVENTS) { + /* if we merged we need to wait on the new event */ + if (notify_event) + event = notify_event; + ret = fanotify_get_response_from_access(group, event); + } +#endif + +out: + if (notify_event) + fsnotify_put_event(notify_event); return ret; } diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 84d3e2047de3..09d9bdb62af3 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -482,6 +482,11 @@ SYSCALL_DEFINE3(fanotify_init, unsigned int, flags, unsigned int, event_f_flags, return PTR_ERR(group); group->priority = priority; +#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS + mutex_init(&group->fanotify_data.access_mutex); + init_waitqueue_head(&group->fanotify_data.access_waitq); + INIT_LIST_HEAD(&group->fanotify_data.access_list); +#endif fd = anon_inode_getfd("[fanotify]", &fanotify_fops, group, f_flags); if (fd < 0) diff --git a/include/linux/fanotify.h b/include/linux/fanotify.h index 385896c9f828..02f80676c238 100644 --- a/include/linux/fanotify.h +++ b/include/linux/fanotify.h @@ -15,6 +15,9 @@ /* FIXME currently Q's have no limit.... */ #define FAN_Q_OVERFLOW 0x00004000 /* Event queued overflowed */ +#define FAN_OPEN_PERM 0x00010000 /* File open in perm check */ +#define FAN_ACCESS_PERM 0x00020000 /* File accessed in perm check */ + /* helper events */ #define FAN_CLOSE (FAN_CLOSE_WRITE | FAN_CLOSE_NOWRITE) /* close */ @@ -52,7 +55,14 @@ FAN_CLOSE |\ FAN_OPEN) +/* + * All events which require a permission response from userspace + */ +#define FAN_ALL_PERM_EVENTS (FAN_OPEN_PERM |\ + FAN_ACCESS_PERM) + #define FAN_ALL_OUTGOING_EVENTS (FAN_ALL_EVENTS |\ + FAN_ALL_PERM_EVENTS |\ FAN_Q_OVERFLOW) #define FANOTIFY_METADATA_VERSION 1 @@ -65,6 +75,10 @@ struct fanotify_event_metadata { __s64 pid; } __attribute__ ((packed)); +/* Legit userspace responses to a _PERM event */ +#define FAN_ALLOW 0x01 +#define FAN_DENY 0x02 + /* Helper functions to deal with fanotify_event_metadata buffers */ #define FAN_EVENT_METADATA_LEN (sizeof(struct fanotify_event_metadata)) @@ -78,5 +92,9 @@ struct fanotify_event_metadata { #ifdef __KERNEL__ +struct fanotify_wait { + struct fsnotify_event *event; + __s32 fd; +}; #endif /* __KERNEL__ */ #endif /* _LINUX_FANOTIFY_H */ diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index c34728e7d8cb..b0d00fd6bfad 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -159,6 +159,14 @@ struct fsnotify_group { struct fasync_struct *fa; /* async notification */ struct user_struct *user; } inotify_data; +#endif +#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS + struct fanotify_group_private_data { + /* allows a group to block waiting for a userspace response */ + struct mutex access_mutex; + struct list_head access_list; + wait_queue_head_t access_waitq; + } fanotify_data; #endif }; }; @@ -227,6 +235,10 @@ struct fsnotify_event { size_t name_len; struct pid *tgid; +#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS + __u32 response; /* userspace answer to question */ +#endif /* CONFIG_FANOTIFY_ACCESS_PERMISSIONS */ + struct list_head private_data_list; /* groups can store private data here */ }; -- cgit v1.2.3 From b2d879096ac799722e6017ee82c0586f0d101c9c Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:34 -0500 Subject: fanotify: userspace interface for permission responses fanotify groups need to respond to events which include permissions types. To do so groups will send a response using write() on the fanotify_fd they have open. Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify.c | 3 + fs/notify/fanotify/fanotify_user.c | 182 +++++++++++++++++++++++++++++++++++-- include/linux/fanotify.h | 5 + 3 files changed, 184 insertions(+), 6 deletions(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index 52d0a55a249e..bbcfccd4a8ea 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -114,6 +114,9 @@ static int fanotify_get_response_from_access(struct fsnotify_group *group, event->response = 0; spin_unlock(&event->lock); + pr_debug("%s: group=%p event=%p about to return ret=%d\n", __func__, + group, event, ret); + return ret; } #endif diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 09d9bdb62af3..87f0be852f71 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -18,6 +18,13 @@ extern const struct fsnotify_ops fanotify_fsnotify_ops; static struct kmem_cache *fanotify_mark_cache __read_mostly; +static struct kmem_cache *fanotify_response_event_cache __read_mostly; + +struct fanotify_response_event { + struct list_head list; + __s32 fd; + struct fsnotify_event *event; +}; /* * Get an fsnotify notification event if one exists and is small @@ -110,23 +117,152 @@ static ssize_t fill_event_metadata(struct fsnotify_group *group, return metadata->fd; } +#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS +static struct fanotify_response_event *dequeue_re(struct fsnotify_group *group, + __s32 fd) +{ + struct fanotify_response_event *re, *return_re = NULL; + + mutex_lock(&group->fanotify_data.access_mutex); + list_for_each_entry(re, &group->fanotify_data.access_list, list) { + if (re->fd != fd) + continue; + + list_del_init(&re->list); + return_re = re; + break; + } + mutex_unlock(&group->fanotify_data.access_mutex); + + pr_debug("%s: found return_re=%p\n", __func__, return_re); + + return return_re; +} + +static int process_access_response(struct fsnotify_group *group, + struct fanotify_response *response_struct) +{ + struct fanotify_response_event *re; + __s32 fd = response_struct->fd; + __u32 response = response_struct->response; + + pr_debug("%s: group=%p fd=%d response=%d\n", __func__, group, + fd, response); + /* + * make sure the response is valid, if invalid we do nothing and either + * userspace can send a valid responce or we will clean it up after the + * timeout + */ + switch (response) { + case FAN_ALLOW: + case FAN_DENY: + break; + default: + return -EINVAL; + } + + if (fd < 0) + return -EINVAL; + + re = dequeue_re(group, fd); + if (!re) + return -ENOENT; + + re->event->response = response; + + wake_up(&group->fanotify_data.access_waitq); + + kmem_cache_free(fanotify_response_event_cache, re); + + return 0; +} + +static int prepare_for_access_response(struct fsnotify_group *group, + struct fsnotify_event *event, + __s32 fd) +{ + struct fanotify_response_event *re; + + if (!(event->mask & FAN_ALL_PERM_EVENTS)) + return 0; + + re = kmem_cache_alloc(fanotify_response_event_cache, GFP_KERNEL); + if (!re) + return -ENOMEM; + + re->event = event; + re->fd = fd; + + mutex_lock(&group->fanotify_data.access_mutex); + list_add_tail(&re->list, &group->fanotify_data.access_list); + mutex_unlock(&group->fanotify_data.access_mutex); + + return 0; +} + +static void remove_access_response(struct fsnotify_group *group, + struct fsnotify_event *event, + __s32 fd) +{ + struct fanotify_response_event *re; + + if (!(event->mask & FAN_ALL_PERM_EVENTS)) + return; + + re = dequeue_re(group, fd); + if (!re) + return; + + BUG_ON(re->event != event); + + kmem_cache_free(fanotify_response_event_cache, re); + + return; +} +#else +static int prepare_for_access_response(struct fsnotify_group *group, + struct fsnotify_event *event, + __s32 fd) +{ + return 0; +} + +static void remove_access_response(struct fsnotify_group *group, + struct fsnotify_event *event, + __s32 fd) +{ + return 0; +} +#endif + static ssize_t copy_event_to_user(struct fsnotify_group *group, struct fsnotify_event *event, char __user *buf) { struct fanotify_event_metadata fanotify_event_metadata; - int ret; + int fd, ret; pr_debug("%s: group=%p event=%p\n", __func__, group, event); - ret = fill_event_metadata(group, &fanotify_event_metadata, event); - if (ret < 0) - return ret; + fd = fill_event_metadata(group, &fanotify_event_metadata, event); + if (fd < 0) + return fd; + + ret = prepare_for_access_response(group, event, fd); + if (ret) + goto out_close_fd; + ret = -EFAULT; if (copy_to_user(buf, &fanotify_event_metadata, FAN_EVENT_METADATA_LEN)) - return -EFAULT; + goto out_kill_access_response; return FAN_EVENT_METADATA_LEN; + +out_kill_access_response: + remove_access_response(group, event, fd); +out_close_fd: + sys_close(fd); + return ret; } /* intofiy userspace file descriptor functions */ @@ -197,6 +333,33 @@ static ssize_t fanotify_read(struct file *file, char __user *buf, return ret; } +static ssize_t fanotify_write(struct file *file, const char __user *buf, size_t count, loff_t *pos) +{ +#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS + struct fanotify_response response = { .fd = -1, .response = -1 }; + struct fsnotify_group *group; + int ret; + + group = file->private_data; + + if (count > sizeof(response)) + count = sizeof(response); + + pr_debug("%s: group=%p count=%zu\n", __func__, group, count); + + if (copy_from_user(&response, buf, count)) + return -EFAULT; + + ret = process_access_response(group, &response); + if (ret < 0) + count = ret; + + return count; +#else + return -EINVAL; +#endif +} + static int fanotify_release(struct inode *ignored, struct file *file) { struct fsnotify_group *group = file->private_data; @@ -237,6 +400,7 @@ static long fanotify_ioctl(struct file *file, unsigned int cmd, unsigned long ar static const struct file_operations fanotify_fops = { .poll = fanotify_poll, .read = fanotify_read, + .write = fanotify_write, .fasync = NULL, .release = fanotify_release, .unlocked_ioctl = fanotify_ioctl, @@ -470,7 +634,7 @@ SYSCALL_DEFINE3(fanotify_init, unsigned int, flags, unsigned int, event_f_flags, if (flags & ~FAN_ALL_INIT_FLAGS) return -EINVAL; - f_flags = (O_RDONLY | FMODE_NONOTIFY); + f_flags = O_RDWR | FMODE_NONOTIFY; if (flags & FAN_CLOEXEC) f_flags |= O_CLOEXEC; if (flags & FAN_NONBLOCK) @@ -527,7 +691,11 @@ SYSCALL_DEFINE(fanotify_mark)(int fanotify_fd, unsigned int flags, default: return -EINVAL; } +#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS + if (mask & ~(FAN_ALL_EVENTS | FAN_ALL_PERM_EVENTS | FAN_EVENT_ON_CHILD)) +#else if (mask & ~(FAN_ALL_EVENTS | FAN_EVENT_ON_CHILD)) +#endif return -EINVAL; filp = fget_light(fanotify_fd, &fput_needed); @@ -600,6 +768,8 @@ SYSCALL_ALIAS(sys_fanotify_mark, SyS_fanotify_mark); static int __init fanotify_user_setup(void) { fanotify_mark_cache = KMEM_CACHE(fsnotify_mark, SLAB_PANIC); + fanotify_response_event_cache = KMEM_CACHE(fanotify_response_event, + SLAB_PANIC); return 0; } diff --git a/include/linux/fanotify.h b/include/linux/fanotify.h index 02f80676c238..f0949a57ca9d 100644 --- a/include/linux/fanotify.h +++ b/include/linux/fanotify.h @@ -75,6 +75,11 @@ struct fanotify_event_metadata { __s64 pid; } __attribute__ ((packed)); +struct fanotify_response { + __s32 fd; + __u32 response; +} __attribute__ ((packed)); + /* Legit userspace responses to a _PERM event */ #define FAN_ALLOW 0x01 #define FAN_DENY 0x02 -- cgit v1.2.3 From 8860f060e473dce1a0873d92105d536f72b05908 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 23 Dec 2009 00:10:25 -0500 Subject: fanotify: do not return 0 in a void function remove_access_response() is supposed to have a void return, but was returning 0; Reported-by: Stephen Rothwell Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify_user.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 87f0be852f71..7c869fa23ec6 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -231,7 +231,7 @@ static void remove_access_response(struct fsnotify_group *group, struct fsnotify_event *event, __s32 fd) { - return 0; + return; } #endif -- cgit v1.2.3 From 98b5c10d320adfa250c1c18f3ccaec2f78e5e11d Mon Sep 17 00:00:00 2001 From: Jean-Christophe Dubois Date: Tue, 23 Mar 2010 08:08:09 +0100 Subject: fanotify: do not always return 0 in fsnotify It seems to me you are always returning 0 in fsnotify, when you should return the error (EPERM) returned by fanotify. Signed-off-by: Jean-Christophe DUBOIS Signed-off-by: Eric Paris --- fs/notify/fsnotify.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index 668268627894..9810babb1a3b 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -269,7 +269,7 @@ out: if (event) fsnotify_put_event(event); - return 0; + return ret; } EXPORT_SYMBOL_GPL(fsnotify); -- cgit v1.2.3 From b31d397e430a90cbe9d3656929a7d5f96e986666 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 21 Apr 2010 16:49:38 -0400 Subject: fsnotify: call iput on inodes when no longer marked fsnotify takes an igrab on an inode when it adds a mark. The code was supposed to drop the reference when the mark was removed but didn't. This caused problems when an fs was unmounted because those inodes would clearly not be gone. Thus resulting in the most devistating of messages: VFS: Busy inodes after unmount of loop0. Self-destruct in 5 seconds. >>> Have a nice day... Jiri Slaby bisected the problem to a patch in the fsnotify tree. The code snippets below show my stupidity quite clearly. void fsnotify_destroy_inode_mark(struct fsnotify_mark *mark) { ... mark->inode = NULL; ... } void fsnotify_destroy_mark(struct fsnotify_mark *mark) { struct inode *inode = NULL; ... if (mark->flags & FSNOTIFY_MARK_FLAG_INODE) { fsnotify_destroy_inode_mark(mark); inode = mark->i.inode; } ... if (inode) iput(inode); ... } Obviously the intent was to capture the inode before it was set to NULL in fsnotify_destory_inode_mark() so we wouldn't be leaking inodes forever. Instead we leaked them (and exploded on umount) Reported-by: Jiri Slaby Signed-off-by: Eric Paris --- fs/notify/mark.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/notify/mark.c b/fs/notify/mark.c index 1e824e64441d..8f3b0e7a543d 100644 --- a/fs/notify/mark.c +++ b/fs/notify/mark.c @@ -133,8 +133,8 @@ void fsnotify_destroy_mark(struct fsnotify_mark *mark) spin_lock(&group->mark_lock); if (mark->flags & FSNOTIFY_MARK_FLAG_INODE) { - fsnotify_destroy_inode_mark(mark); inode = mark->i.inode; + fsnotify_destroy_inode_mark(mark); } else if (mark->flags & FSNOTIFY_MARK_FLAG_VFSMOUNT) fsnotify_destroy_vfsmount_mark(mark); else -- cgit v1.2.3 From 0a24887afacefbe2c44e0eee4150b43959a60665 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Fri, 14 May 2010 15:35:21 -0500 Subject: inotify_user.c: make local symbol static The symbol inotify_max_user_watches is not used outside this file and should be static. Signed-off-by: H Hartley Sweeten Cc: John McCutchan Cc: Robert Love Cc: Eric Paris Signed-off-by: Eric Paris --- fs/notify/inotify/inotify_user.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index 1ce71f5b9589..44aeb0f1b222 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -46,7 +46,7 @@ /* these are configurable via /proc/sys/fs/inotify/ */ static int inotify_max_user_instances __read_mostly; static int inotify_max_queued_events __read_mostly; -int inotify_max_user_watches __read_mostly; +static int inotify_max_user_watches __read_mostly; static struct kmem_cache *inotify_inode_mark_cachep __read_mostly; struct kmem_cache *event_priv_cachep __read_mostly; -- cgit v1.2.3 From 269ed32a9ce00132b9372e9c00014532e054d6b2 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 27 May 2010 09:29:37 -0400 Subject: fanotify: default Kconfig to n fanotify has default to y in linux-next since it's inception but default to n in the final push to Linus. Signed-off-by: Eric Paris --- fs/notify/fanotify/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/notify/fanotify/Kconfig b/fs/notify/fanotify/Kconfig index 566de30395c2..3ac36b7bf6b9 100644 --- a/fs/notify/fanotify/Kconfig +++ b/fs/notify/fanotify/Kconfig @@ -2,7 +2,7 @@ config FANOTIFY bool "Filesystem wide access notification" select FSNOTIFY select ANON_INODES - default y + default n ---help--- Say Y here to enable fanotify suport. fanotify is a file access notification system which differs from inotify in that it sends -- cgit v1.2.3 From 08ae89380a8210a9965d04083e1de78cb8bca4b1 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 27 May 2010 09:41:40 -0400 Subject: fanotify: drop the useless priority argument The priority argument in fanotify is useless. Kill it. Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify_user.c | 8 +++----- fs/notify/group.c | 10 +++------- include/linux/fsnotify_backend.h | 1 - include/linux/syscalls.h | 3 +-- 4 files changed, 7 insertions(+), 15 deletions(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 7c869fa23ec6..664102084766 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -616,14 +616,13 @@ static int fanotify_add_inode_mark(struct fsnotify_group *group, } /* fanotify syscalls */ -SYSCALL_DEFINE3(fanotify_init, unsigned int, flags, unsigned int, event_f_flags, - unsigned int, priority) +SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags) { struct fsnotify_group *group; int f_flags, fd; - pr_debug("%s: flags=%d event_f_flags=%d priority=%d\n", - __func__, flags, event_f_flags, priority); + pr_debug("%s: flags=%d event_f_flags=%d\n", + __func__, flags, event_f_flags); if (event_f_flags) return -EINVAL; @@ -645,7 +644,6 @@ SYSCALL_DEFINE3(fanotify_init, unsigned int, flags, unsigned int, event_f_flags, if (IS_ERR(group)) return PTR_ERR(group); - group->priority = priority; #ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS mutex_init(&group->fanotify_data.access_mutex); init_waitqueue_head(&group->fanotify_data.access_waitq); diff --git a/fs/notify/group.c b/fs/notify/group.c index ada913fd4f7f..7ac65ed4735b 100644 --- a/fs/notify/group.c +++ b/fs/notify/group.c @@ -90,7 +90,6 @@ void fsnotify_recalc_group_mask(struct fsnotify_group *group) void fsnotify_add_vfsmount_group(struct fsnotify_group *group) { struct fsnotify_group *group_iter; - unsigned int priority = group->priority; mutex_lock(&fsnotify_grp_mutex); @@ -98,7 +97,7 @@ void fsnotify_add_vfsmount_group(struct fsnotify_group *group) list_for_each_entry(group_iter, &fsnotify_vfsmount_groups, vfsmount_group_list) { /* insert in front of this one? */ - if (priority < group_iter->priority) { + if (group < group_iter) { /* list_add_tail() insert in front of group_iter */ list_add_tail_rcu(&group->inode_group_list, &group_iter->inode_group_list); @@ -118,15 +117,14 @@ out: void fsnotify_add_inode_group(struct fsnotify_group *group) { struct fsnotify_group *group_iter; - unsigned int priority = group->priority; mutex_lock(&fsnotify_grp_mutex); - /* add to global group list, priority 0 first, UINT_MAX last */ + /* add to global group list */ if (!group->on_inode_group_list) { list_for_each_entry(group_iter, &fsnotify_inode_groups, inode_group_list) { - if (priority < group_iter->priority) { + if (group < group_iter) { /* list_add_tail() insert in front of group_iter */ list_add_tail_rcu(&group->inode_group_list, &group_iter->inode_group_list); @@ -260,8 +258,6 @@ struct fsnotify_group *fsnotify_alloc_group(const struct fsnotify_ops *ops) spin_lock_init(&group->mark_lock); INIT_LIST_HEAD(&group->marks_list); - group->priority = UINT_MAX; - group->ops = ops; return group; diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index b0d00fd6bfad..b9b3f24ad4fc 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -143,7 +143,6 @@ struct fsnotify_group { * a group */ struct list_head marks_list; /* all inode marks for this group */ - unsigned int priority; /* order of this group compared to others */ /* prevents double list_del of group_list. protected by global fsnotify_grp_mutex */ bool on_inode_group_list; bool on_vfsmount_group_list; diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 5b05c37059e9..0ec26a74f20a 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -813,8 +813,7 @@ asmlinkage long sys_pselect6(int, fd_set __user *, fd_set __user *, asmlinkage long sys_ppoll(struct pollfd __user *, unsigned int, struct timespec __user *, const sigset_t __user *, size_t); -asmlinkage long sys_fanotify_init(unsigned int flags, unsigned int event_f_flags, - unsigned int priority); +asmlinkage long sys_fanotify_init(unsigned int flags, unsigned int event_f_flags); asmlinkage long sys_fanotify_mark(int fanotify_fd, unsigned int flags, u64 mask, int fd, const char __user *pathname); -- cgit v1.2.3 From e4e047a22058f48544b16728e0f15a3fc12bb0cf Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 20 May 2010 01:36:28 +1000 Subject: fsnotify: update gfp/slab.h includes Implicit slab.h inclusion via percpu.h is about to go away. Make sure gfp.h or slab.h is included as necessary. Signed-off-by: Tejun Heo Cc: Stephen Rothwell Cc: Eric Paris Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify_user.c | 1 + fs/notify/vfsmount_mark.c | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 664102084766..da01091f93eb 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include diff --git a/fs/notify/vfsmount_mark.c b/fs/notify/vfsmount_mark.c index 8f1aa02f4f02..ec580a25d293 100644 --- a/fs/notify/vfsmount_mark.c +++ b/fs/notify/vfsmount_mark.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include /* for inode_lock */ -- cgit v1.2.3 From ff311008ab8d2f2cfdbbefd407d1b05acc8164b2 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 28 Jul 2010 10:18:37 -0400 Subject: inotify: fix inotify oneshot support During the large inotify rewrite to fsnotify I completely dropped support for IN_ONESHOT. Reimplement that support. Signed-off-by: Eric Paris --- fs/notify/inotify/inotify_fsnotify.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'fs') diff --git a/fs/notify/inotify/inotify_fsnotify.c b/fs/notify/inotify/inotify_fsnotify.c index daa666a6e6c9..388a150c3d4a 100644 --- a/fs/notify/inotify/inotify_fsnotify.c +++ b/fs/notify/inotify/inotify_fsnotify.c @@ -126,6 +126,9 @@ static int inotify_handle_event(struct fsnotify_group *group, struct fsnotify_ev ret = 0; } + if (fsn_mark->mask & IN_ONESHOT) + fsnotify_destroy_mark(fsn_mark); + /* * If we hold the fsn_mark until after the event is on the queue * IN_IGNORED won't be able to pass this event in the queue -- cgit v1.2.3 From 611da04f7a31b2208e838be55a42c7a1310ae321 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 28 Jul 2010 10:18:37 -0400 Subject: inotify: send IN_UNMOUNT events Since the .31 or so notify rewrite inotify has not sent events about inodes which are unmounted. This patch restores those events. Signed-off-by: Eric Paris --- fs/notify/inotify/inotify_user.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index 44aeb0f1b222..f381dafe8efb 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -90,8 +90,11 @@ static inline __u32 inotify_arg_to_mask(u32 arg) { __u32 mask; - /* everything should accept their own ignored and cares about children */ - mask = (FS_IN_IGNORED | FS_EVENT_ON_CHILD); + /* + * everything should accept their own ignored, cares about children, + * and should receive events when the inode is unmounted + */ + mask = (FS_IN_IGNORED | FS_EVENT_ON_CHILD | FS_UNMOUNT); /* mask off the flags used to open the fd */ mask |= (arg & (IN_ALL_EVENTS | IN_ONESHOT)); -- cgit v1.2.3 From 8c1934c8d70b22ca8333b216aec6c7d09fdbd6a6 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 28 Jul 2010 10:18:37 -0400 Subject: inotify: allow users to request not to recieve events on unlinked children An inotify watch on a directory will send events for children even if those children have been unlinked. This patch add a new inotify flag IN_EXCL_UNLINK which allows a watch to specificy they don't care about unlinked children. This should fix performance problems seen by tasks which add a watch to /tmp and then are overrun with events when other processes are reading and writing to unlinked files they created in /tmp. https://bugzilla.kernel.org/show_bug.cgi?id=16296 Requested-by: Matthias Clasen Signed-off-by: Eric Paris --- fs/notify/inotify/inotify_fsnotify.c | 9 +++++++++ fs/notify/inotify/inotify_user.c | 2 +- include/linux/fsnotify_backend.h | 1 + include/linux/inotify.h | 1 + 4 files changed, 12 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/notify/inotify/inotify_fsnotify.c b/fs/notify/inotify/inotify_fsnotify.c index 388a150c3d4a..9d332e7f5a5c 100644 --- a/fs/notify/inotify/inotify_fsnotify.c +++ b/fs/notify/inotify/inotify_fsnotify.c @@ -22,6 +22,7 @@ * General Public License for more details. */ +#include /* d_unlinked */ #include /* struct inode */ #include #include @@ -157,6 +158,14 @@ static bool inotify_should_send_event(struct fsnotify_group *group, struct inode mask = (mask & ~FS_EVENT_ON_CHILD); send = (fsn_mark->mask & mask); + if (send && (fsn_mark->mask & FS_EXCL_UNLINK) && + (data_type == FSNOTIFY_EVENT_PATH)) { + struct path *path = data; + + if (d_unlinked(path->dentry)) + send = false; + } + /* find took a reference */ fsnotify_put_mark(fsn_mark); diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index f381dafe8efb..dfc80f70e517 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -97,7 +97,7 @@ static inline __u32 inotify_arg_to_mask(u32 arg) mask = (FS_IN_IGNORED | FS_EVENT_ON_CHILD | FS_UNMOUNT); /* mask off the flags used to open the fd */ - mask |= (arg & (IN_ALL_EVENTS | IN_ONESHOT)); + mask |= (arg & (IN_ALL_EVENTS | IN_ONESHOT | IN_EXCL_UNLINK)); return mask; } diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index b9b3f24ad4fc..4b809fcd4996 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -44,6 +44,7 @@ #define FS_OPEN_PERM 0x00010000 /* open event in an permission hook */ #define FS_ACCESS_PERM 0x00020000 /* access event in a permissions hook */ +#define FS_EXCL_UNLINK 0x04000000 /* do not send events if object is unlinked */ #define FS_IN_ISDIR 0x40000000 /* event occurred against dir */ #define FS_IN_ONESHOT 0x80000000 /* only send event once */ diff --git a/include/linux/inotify.h b/include/linux/inotify.h index 94d209a1b689..b74f2ef2c368 100644 --- a/include/linux/inotify.h +++ b/include/linux/inotify.h @@ -51,6 +51,7 @@ struct inotify_event { /* special flags */ #define IN_ONLYDIR 0x01000000 /* only watch the path if it is a directory */ #define IN_DONT_FOLLOW 0x02000000 /* don't follow a sym link */ +#define IN_EXCL_UNLINK 0x04000000 /* exclude events on unlinked objects */ #define IN_MASK_ADD 0x20000000 /* add to the mask of an already existing watch */ #define IN_ISDIR 0x40000000 /* event occurred against dir */ #define IN_ONESHOT 0x80000000 /* only send event once */ -- cgit v1.2.3 From f874e1ac21d7708464dc656a10312542c54719f1 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 28 Jul 2010 10:18:37 -0400 Subject: inotify: force inotify and fsnotify use same bits inotify uses bits called IN_* and fsnotify uses bits called FS_*. These need to line up. This patch adds build time checks to make sure noone can change these bits so they are not the same. Signed-off-by: Eric Paris --- fs/notify/inotify/inotify_user.c | 21 +++++++++++++++++++++ include/linux/inotify.h | 9 +++++++++ 2 files changed, 30 insertions(+) (limited to 'fs') diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index dfc80f70e517..c8203ce28ab7 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -839,6 +839,27 @@ out: */ static int __init inotify_user_setup(void) { + BUILD_BUG_ON(IN_ACCESS != FS_ACCESS); + BUILD_BUG_ON(IN_MODIFY != FS_MODIFY); + BUILD_BUG_ON(IN_ATTRIB != FS_ATTRIB); + BUILD_BUG_ON(IN_CLOSE_WRITE != FS_CLOSE_WRITE); + BUILD_BUG_ON(IN_CLOSE_NOWRITE != FS_CLOSE_NOWRITE); + BUILD_BUG_ON(IN_OPEN != FS_OPEN); + BUILD_BUG_ON(IN_MOVED_FROM != FS_MOVED_FROM); + BUILD_BUG_ON(IN_MOVED_TO != FS_MOVED_TO); + BUILD_BUG_ON(IN_CREATE != FS_CREATE); + BUILD_BUG_ON(IN_DELETE != FS_DELETE); + BUILD_BUG_ON(IN_DELETE_SELF != FS_DELETE_SELF); + BUILD_BUG_ON(IN_MOVE_SELF != FS_MOVE_SELF); + BUILD_BUG_ON(IN_UNMOUNT != FS_UNMOUNT); + BUILD_BUG_ON(IN_Q_OVERFLOW != FS_Q_OVERFLOW); + BUILD_BUG_ON(IN_IGNORED != FS_IN_IGNORED); + BUILD_BUG_ON(IN_EXCL_UNLINK != FS_EXCL_UNLINK); + BUILD_BUG_ON(IN_ISDIR != FS_IN_ISDIR); + BUILD_BUG_ON(IN_ONESHOT != FS_IN_ONESHOT); + + BUG_ON(hweight32(ALL_INOTIFY_BITS) != 21); + inotify_inode_mark_cachep = KMEM_CACHE(inotify_inode_mark, SLAB_PANIC); event_priv_cachep = KMEM_CACHE(inotify_event_private_data, SLAB_PANIC); diff --git a/include/linux/inotify.h b/include/linux/inotify.h index b74f2ef2c368..d33041e2a42a 100644 --- a/include/linux/inotify.h +++ b/include/linux/inotify.h @@ -73,6 +73,15 @@ struct inotify_event { #ifdef __KERNEL__ #include extern struct ctl_table inotify_table[]; /* for sysctl */ + +#define ALL_INOTIFY_BITS (IN_ACCESS | IN_MODIFY | IN_ATTRIB | IN_CLOSE_WRITE | \ + IN_CLOSE_NOWRITE | IN_OPEN | IN_MOVED_FROM | \ + IN_MOVED_TO | IN_CREATE | IN_DELETE | \ + IN_DELETE_SELF | IN_MOVE_SELF | IN_UNMOUNT | \ + IN_Q_OVERFLOW | IN_IGNORED | IN_ONLYDIR | \ + IN_DONT_FOLLOW | IN_EXCL_UNLINK | IN_MASK_ADD | \ + IN_ISDIR | IN_ONESHOT) + #endif #endif /* _LINUX_INOTIFY_H */ -- cgit v1.2.3 From 44b350fc23e36e95c8e042b7ded66217ea2b9d72 Mon Sep 17 00:00:00 2001 From: Jerome Marchand Date: Wed, 28 Jul 2010 10:18:37 -0400 Subject: inotify: Fix mask checks The mask checks in inotify_update_existing_watch() and inotify_new_watch() are useless because inotify_arg_to_mask() sets FS_IN_IGNORED and FS_EVENT_ON_CHILD bits anyway. Signed-off-by: Eric Paris --- fs/notify/inotify/inotify_user.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index c8203ce28ab7..7dc940c869b6 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -566,7 +566,7 @@ static int inotify_update_existing_watch(struct fsnotify_group *group, /* don't allow invalid bits: we don't want flags set */ mask = inotify_arg_to_mask(arg); - if (unlikely(!mask)) + if (unlikely(!(mask & IN_ALL_EVENTS))) return -EINVAL; fsn_mark = fsnotify_find_inode_mark(group, inode); @@ -624,7 +624,7 @@ static int inotify_new_watch(struct fsnotify_group *group, /* don't allow invalid bits: we don't want flags set */ mask = inotify_arg_to_mask(arg); - if (unlikely(!mask)) + if (unlikely(!(mask & IN_ALL_EVENTS))) return -EINVAL; tmp_i_mark = kmem_cache_alloc(inotify_inode_mark_cachep, GFP_KERNEL); -- cgit v1.2.3 From 20dee624ca40db227aa70cb3f44d2d6cb4fdbab4 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 28 Jul 2010 10:18:37 -0400 Subject: fsnotify: check to make sure all fsnotify bits are unique This patch adds a check to make sure that all fsnotify bits are unique and we cannot accidentally use the same bit for 2 different fsnotify event types. Signed-off-by: Eric Paris --- fs/notify/fsnotify.c | 2 ++ include/linux/fsnotify_backend.h | 9 +++++++++ 2 files changed, 11 insertions(+) (limited to 'fs') diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index 9810babb1a3b..076c10e959d5 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -275,6 +275,8 @@ EXPORT_SYMBOL_GPL(fsnotify); static __init int fsnotify_init(void) { + BUG_ON(hweight32(ALL_FSNOTIFY_EVENTS) != 23); + return init_srcu_struct(&fsnotify_grp_srcu); } subsys_initcall(fsnotify_init); diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index 4b809fcd4996..a46355db1e47 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -64,6 +64,15 @@ #define FS_MOVE (FS_MOVED_FROM | FS_MOVED_TO) +#define ALL_FSNOTIFY_EVENTS (FS_ACCESS | FS_MODIFY | FS_ATTRIB | \ + FS_CLOSE_WRITE | FS_CLOSE_NOWRITE | FS_OPEN | \ + FS_MOVED_FROM | FS_MOVED_TO | FS_CREATE | \ + FS_DELETE | FS_DELETE_SELF | FS_MOVE_SELF | \ + FS_UNMOUNT | FS_Q_OVERFLOW | FS_IN_IGNORED | \ + FS_OPEN_PERM | FS_ACCESS_PERM | FS_EXCL_UNLINK | \ + FS_IN_ISDIR | FS_IN_ONESHOT | FS_DN_RENAME | \ + FS_DN_MULTISHOT | FS_EVENT_ON_CHILD) + struct fsnotify_group; struct fsnotify_event; struct fsnotify_mark; -- cgit v1.2.3 From 80af2588676483ac4e998b5092e9d008dab3ab62 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 28 Jul 2010 10:18:37 -0400 Subject: fanotify: groups can specify their f_flags for new fd Currently fanotify fds opened for thier listeners are done with f_flags equal to O_RDONLY | O_LARGEFILE. This patch instead takes f_flags from the fanotify_init syscall and uses those when opening files in the context of the listener. Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify_user.c | 6 ++---- include/linux/fsnotify_backend.h | 7 +++++-- 2 files changed, 7 insertions(+), 6 deletions(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index da01091f93eb..7182c83be90e 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -81,7 +81,7 @@ static int create_fd(struct fsnotify_group *group, struct fsnotify_event *event) * are NULL; That's fine, just don't call dentry open */ if (dentry && mnt) new_file = dentry_open(dentry, mnt, - O_RDONLY | O_LARGEFILE | FMODE_NONOTIFY, + group->fanotify_data.f_flags | FMODE_NONOTIFY, current_cred()); else new_file = ERR_PTR(-EOVERFLOW); @@ -625,9 +625,6 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags) pr_debug("%s: flags=%d event_f_flags=%d\n", __func__, flags, event_f_flags); - if (event_f_flags) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) return -EACCES; @@ -645,6 +642,7 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags) if (IS_ERR(group)) return PTR_ERR(group); + group->fanotify_data.f_flags = event_f_flags; #ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS mutex_init(&group->fanotify_data.access_mutex); init_waitqueue_head(&group->fanotify_data.access_waitq); diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index a46355db1e47..a83859d7d36e 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -169,14 +169,17 @@ struct fsnotify_group { struct user_struct *user; } inotify_data; #endif -#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS +#ifdef CONFIG_FANOTIFY struct fanotify_group_private_data { +#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS /* allows a group to block waiting for a userspace response */ struct mutex access_mutex; struct list_head access_list; wait_queue_head_t access_waitq; +#endif /* CONFIG_FANOTIFY_ACCESS_PERMISSIONS */ + int f_flags; } fanotify_data; -#endif +#endif /* CONFIG_FANOTIFY */ }; }; -- cgit v1.2.3 From 5ba08e2eeb06355f66ed62ae97bb87d145973a9a Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 28 Jul 2010 10:18:37 -0400 Subject: fsnotify: add pr_debug throughout It can be hard to debug fsnotify since there are so few printks. Use pr_debug to allow for dynamic debugging. Signed-off-by: Eric Paris --- fs/notify/fsnotify.c | 4 ++++ fs/notify/inotify/inotify_fsnotify.c | 6 ++++++ fs/notify/inotify/inotify_user.c | 10 ++++++++++ fs/notify/notification.c | 13 +++++++++++++ 4 files changed, 33 insertions(+) (limited to 'fs') diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index 076c10e959d5..72aae4045314 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -174,6 +174,10 @@ static int send_to_group(struct fsnotify_group *group, struct inode *to_tell, int data_is, u32 cookie, const unsigned char *file_name, struct fsnotify_event **event) { + pr_debug("%s: group=%p to_tell=%p mnt=%p mask=%x data=%p data_is=%d" + " cookie=%d event=%p\n", __func__, group, to_tell, mnt, + mask, data, data_is, cookie, *event); + if (!group->ops->should_send_event(group, to_tell, mnt, mask, data, data_is)) return 0; diff --git a/fs/notify/inotify/inotify_fsnotify.c b/fs/notify/inotify/inotify_fsnotify.c index 9d332e7f5a5c..906b72761b17 100644 --- a/fs/notify/inotify/inotify_fsnotify.c +++ b/fs/notify/inotify/inotify_fsnotify.c @@ -98,6 +98,9 @@ static int inotify_handle_event(struct fsnotify_group *group, struct fsnotify_ev struct fsnotify_event_private_data *fsn_event_priv; int wd, ret; + pr_debug("%s: group=%p event=%p to_tell=%p mask=%x\n", __func__, group, + event, event->to_tell, event->mask); + to_tell = event->to_tell; fsn_mark = fsnotify_find_inode_mark(group, to_tell); @@ -151,6 +154,9 @@ static bool inotify_should_send_event(struct fsnotify_group *group, struct inode struct fsnotify_mark *fsn_mark; bool send; + pr_debug("%s: group=%p inode=%p mask=%x data=%p data_type=%d\n", + __func__, group, inode, mask, data, data_type); + fsn_mark = fsnotify_find_inode_mark(group, inode); if (!fsn_mark) return false; diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index 7dc940c869b6..1068e1ca9cb0 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -141,6 +141,8 @@ static struct fsnotify_event *get_one_event(struct fsnotify_group *group, event = fsnotify_peek_notify_event(group); + pr_debug("%s: group=%p event=%p\n", __func__, group, event); + if (event->name_len) event_size += roundup(event->name_len + 1, event_size); @@ -170,6 +172,8 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group, size_t event_size = sizeof(struct inotify_event); size_t name_len = 0; + pr_debug("%s: group=%p event=%p\n", __func__, group, event); + /* we get the inotify watch descriptor from the event private data */ spin_lock(&event->lock); fsn_priv = fsnotify_remove_priv_from_event(group, event); @@ -242,6 +246,8 @@ static ssize_t inotify_read(struct file *file, char __user *buf, kevent = get_one_event(group, count); mutex_unlock(&group->notification_mutex); + pr_debug("%s: group=%p kevent=%p\n", __func__, group, kevent); + if (kevent) { ret = PTR_ERR(kevent); if (IS_ERR(kevent)) @@ -286,6 +292,8 @@ static int inotify_release(struct inode *ignored, struct file *file) struct fsnotify_group *group = file->private_data; struct user_struct *user = group->inotify_data.user; + pr_debug("%s: group=%p\n", __func__, group); + fsnotify_clear_marks_by_group(group); /* free this group, matching get was inotify_init->fsnotify_obtain_group */ @@ -309,6 +317,8 @@ static long inotify_ioctl(struct file *file, unsigned int cmd, group = file->private_data; p = (void __user *) arg; + pr_debug("%s: group=%p cmd=%u\n", __func__, group, cmd); + switch (cmd) { case FIONREAD: mutex_lock(&group->notification_mutex); diff --git a/fs/notify/notification.c b/fs/notify/notification.c index b35faafacd38..e6dde25fb99b 100644 --- a/fs/notify/notification.c +++ b/fs/notify/notification.c @@ -87,6 +87,8 @@ void fsnotify_put_event(struct fsnotify_event *event) return; if (atomic_dec_and_test(&event->refcnt)) { + pr_debug("%s: event=%p\n", __func__, event); + if (event->data_type == FSNOTIFY_EVENT_PATH) path_put(&event->path); @@ -146,6 +148,8 @@ int fsnotify_add_notify_event(struct fsnotify_group *group, struct fsnotify_even struct list_head *list = &group->notification_list; int rc = 0; + pr_debug("%s: group=%p event=%p priv=%p\n", __func__, group, event, priv); + /* * There is one fsnotify_event_holder embedded inside each fsnotify_event. * Check if we expect to be able to use that holder. If not alloc a new @@ -222,6 +226,8 @@ struct fsnotify_event *fsnotify_remove_notify_event(struct fsnotify_group *group BUG_ON(!mutex_is_locked(&group->notification_mutex)); + pr_debug("%s: group=%p\n", __func__, group); + holder = list_first_entry(&group->notification_list, struct fsnotify_event_holder, event_list); event = holder->event; @@ -307,6 +313,8 @@ int fsnotify_replace_event(struct fsnotify_event_holder *old_holder, SPINLOCK_NEW, }; + pr_debug("%s: old_event=%p new_event=%p\n", __func__, old_event, new_event); + /* * if the new_event's embedded holder is in use someone * screwed up and didn't give us a clean new event. @@ -340,6 +348,8 @@ struct fsnotify_event *fsnotify_clone_event(struct fsnotify_event *old_event) if (!event) return NULL; + pr_debug("%s: old_event=%p new_event=%p\n", __func__, old_event, event); + memcpy(event, old_event, sizeof(*event)); initialize_event(event); @@ -379,6 +389,9 @@ struct fsnotify_event *fsnotify_create_event(struct inode *to_tell, __u32 mask, if (!event) return NULL; + pr_debug("%s: event=%p to_tell=%p mask=%x data=%p data_type=%d\n", + __func__, event, to_tell, mask, data, data_type); + initialize_event(event); if (name) { -- cgit v1.2.3 From f70ab54cc6c3907b0727ba332b3976f80f3846d0 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 28 Jul 2010 10:18:37 -0400 Subject: fsnotify: fsnotify_add_notify_event should return an event Rather than the horrific void ** argument and such just to pass the fanotify_merge event back to the caller of fsnotify_add_notify_event() have those things return an event if it was different than the event suggusted to be added. Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify.c | 103 ++++++++++++++++------------------- fs/notify/inotify/inotify_fsnotify.c | 28 +++++----- fs/notify/inotify/inotify_user.c | 11 +++- fs/notify/notification.c | 42 +++++++++----- include/linux/fsnotify_backend.h | 12 ++-- 5 files changed, 101 insertions(+), 95 deletions(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index bbcfccd4a8ea..f3c40c0e2b86 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -30,65 +30,58 @@ static bool should_merge(struct fsnotify_event *old, struct fsnotify_event *new) return false; } -/* Note, if we return an event in *arg that a reference is being held... */ -static int fanotify_merge(struct list_head *list, - struct fsnotify_event *event, - void **arg) +/* and the list better be locked by something too! */ +static struct fsnotify_event *fanotify_merge(struct list_head *list, + struct fsnotify_event *event) { struct fsnotify_event_holder *test_holder; - struct fsnotify_event *test_event; + struct fsnotify_event *test_event = NULL; struct fsnotify_event *new_event; - struct fsnotify_event **return_event = (struct fsnotify_event **)arg; - int ret = 0; pr_debug("%s: list=%p event=%p\n", __func__, list, event); - *return_event = NULL; - - /* and the list better be locked by something too! */ list_for_each_entry_reverse(test_holder, list, event_list) { - test_event = test_holder->event; - if (should_merge(test_event, event)) { - fsnotify_get_event(test_event); - *return_event = test_event; - - ret = -EEXIST; - /* if they are exactly the same we are done */ - if (test_event->mask == event->mask) - goto out; - - /* - * if the refcnt == 1 this is the only queue - * for this event and so we can update the mask - * in place. - */ - if (atomic_read(&test_event->refcnt) == 1) { - test_event->mask |= event->mask; - goto out; - } - - /* can't allocate memory, merge was no possible */ - new_event = fsnotify_clone_event(test_event); - if (unlikely(!new_event)) { - ret = 0; - goto out; - } - - /* we didn't return the test_event, so drop that ref */ - fsnotify_put_event(test_event); - /* the reference we return on new_event is from clone */ - *return_event = new_event; - - /* build new event and replace it on the list */ - new_event->mask = (test_event->mask | event->mask); - fsnotify_replace_event(test_holder, new_event); - + if (should_merge(test_holder->event, event)) { + test_event = test_holder->event; break; } } -out: - return ret; + + if (!test_event) + return NULL; + + fsnotify_get_event(test_event); + + /* if they are exactly the same we are done */ + if (test_event->mask == event->mask) + return test_event; + + /* + * if the refcnt == 2 this is the only queue + * for this event and so we can update the mask + * in place. + */ + if (atomic_read(&test_event->refcnt) == 2) { + test_event->mask |= event->mask; + return test_event; + } + + new_event = fsnotify_clone_event(test_event); + + /* done with test_event */ + fsnotify_put_event(test_event); + + /* couldn't allocate memory, merge was not possible */ + if (unlikely(!new_event)) + return ERR_PTR(-ENOMEM); + + /* build new event and replace it on the list */ + new_event->mask = (test_event->mask | event->mask); + fsnotify_replace_event(test_holder, new_event); + + /* we hold a reference on new_event from clone_event */ + return new_event; } #ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS @@ -123,7 +116,7 @@ static int fanotify_get_response_from_access(struct fsnotify_group *group, static int fanotify_handle_event(struct fsnotify_group *group, struct fsnotify_event *event) { - int ret; + int ret = 0; struct fsnotify_event *notify_event = NULL; BUILD_BUG_ON(FAN_ACCESS != FS_ACCESS); @@ -138,13 +131,9 @@ static int fanotify_handle_event(struct fsnotify_group *group, struct fsnotify_e pr_debug("%s: group=%p event=%p\n", __func__, group, event); - ret = fsnotify_add_notify_event(group, event, NULL, fanotify_merge, - (void **)¬ify_event); - /* -EEXIST means this event was merged with another, not that it was an error */ - if (ret == -EEXIST) - ret = 0; - if (ret) - goto out; + notify_event = fsnotify_add_notify_event(group, event, NULL, fanotify_merge); + if (IS_ERR(notify_event)) + return PTR_ERR(notify_event); #ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS if (event->mask & FAN_ALL_PERM_EVENTS) { @@ -155,9 +144,9 @@ static int fanotify_handle_event(struct fsnotify_group *group, struct fsnotify_e } #endif -out: if (notify_event) fsnotify_put_event(notify_event); + return ret; } diff --git a/fs/notify/inotify/inotify_fsnotify.c b/fs/notify/inotify/inotify_fsnotify.c index 906b72761b17..73a1106b3542 100644 --- a/fs/notify/inotify/inotify_fsnotify.c +++ b/fs/notify/inotify/inotify_fsnotify.c @@ -68,13 +68,11 @@ static bool event_compare(struct fsnotify_event *old, struct fsnotify_event *new return false; } -static int inotify_merge(struct list_head *list, - struct fsnotify_event *event, - void **arg) +static struct fsnotify_event *inotify_merge(struct list_head *list, + struct fsnotify_event *event) { struct fsnotify_event_holder *last_holder; struct fsnotify_event *last_event; - int ret = 0; /* and the list better be locked by something too */ spin_lock(&event->lock); @@ -82,11 +80,13 @@ static int inotify_merge(struct list_head *list, last_holder = list_entry(list->prev, struct fsnotify_event_holder, event_list); last_event = last_holder->event; if (event_compare(last_event, event)) - ret = -EEXIST; + fsnotify_get_event(last_event); + else + last_event = NULL; spin_unlock(&event->lock); - return ret; + return last_event; } static int inotify_handle_event(struct fsnotify_group *group, struct fsnotify_event *event) @@ -96,7 +96,8 @@ static int inotify_handle_event(struct fsnotify_group *group, struct fsnotify_ev struct inode *to_tell; struct inotify_event_private_data *event_priv; struct fsnotify_event_private_data *fsn_event_priv; - int wd, ret; + struct fsnotify_event *added_event; + int wd, ret = 0; pr_debug("%s: group=%p event=%p to_tell=%p mask=%x\n", __func__, group, event, event->to_tell, event->mask); @@ -120,14 +121,13 @@ static int inotify_handle_event(struct fsnotify_group *group, struct fsnotify_ev fsn_event_priv->group = group; event_priv->wd = wd; - ret = fsnotify_add_notify_event(group, event, fsn_event_priv, inotify_merge, NULL); - if (ret) { + added_event = fsnotify_add_notify_event(group, event, fsn_event_priv, inotify_merge); + if (added_event) { inotify_free_event_priv(fsn_event_priv); - /* EEXIST says we tail matched, EOVERFLOW isn't something - * to report up the stack. */ - if ((ret == -EEXIST) || - (ret == -EOVERFLOW)) - ret = 0; + if (!IS_ERR(added_event)) + fsnotify_put_event(added_event); + else + ret = PTR_ERR(added_event); } if (fsn_mark->mask & IN_ONESHOT) diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index 1068e1ca9cb0..a4cd227c4c76 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -516,7 +516,7 @@ void inotify_ignored_and_remove_idr(struct fsnotify_mark *fsn_mark, struct fsnotify_group *group) { struct inotify_inode_mark *i_mark; - struct fsnotify_event *ignored_event; + struct fsnotify_event *ignored_event, *notify_event; struct inotify_event_private_data *event_priv; struct fsnotify_event_private_data *fsn_event_priv; int ret; @@ -538,9 +538,14 @@ void inotify_ignored_and_remove_idr(struct fsnotify_mark *fsn_mark, fsn_event_priv->group = group; event_priv->wd = i_mark->wd; - ret = fsnotify_add_notify_event(group, ignored_event, fsn_event_priv, NULL, NULL); - if (ret) + notify_event = fsnotify_add_notify_event(group, ignored_event, fsn_event_priv, NULL); + if (notify_event) { + if (IS_ERR(notify_event)) + ret = PTR_ERR(notify_event); + else + fsnotify_put_event(notify_event); inotify_free_event_priv(fsn_event_priv); + } skip_send_ignore: diff --git a/fs/notify/notification.c b/fs/notify/notification.c index e6dde25fb99b..f39260f8f865 100644 --- a/fs/notify/notification.c +++ b/fs/notify/notification.c @@ -137,16 +137,14 @@ struct fsnotify_event_private_data *fsnotify_remove_priv_from_event(struct fsnot * event off the queue to deal with. If the event is successfully added to the * group's notification queue, a reference is taken on event. */ -int fsnotify_add_notify_event(struct fsnotify_group *group, struct fsnotify_event *event, - struct fsnotify_event_private_data *priv, - int (*merge)(struct list_head *, - struct fsnotify_event *, - void **arg), - void **arg) +struct fsnotify_event *fsnotify_add_notify_event(struct fsnotify_group *group, struct fsnotify_event *event, + struct fsnotify_event_private_data *priv, + struct fsnotify_event *(*merge)(struct list_head *, + struct fsnotify_event *)) { + struct fsnotify_event *return_event = NULL; struct fsnotify_event_holder *holder = NULL; struct list_head *list = &group->notification_list; - int rc = 0; pr_debug("%s: group=%p event=%p priv=%p\n", __func__, group, event, priv); @@ -162,27 +160,37 @@ int fsnotify_add_notify_event(struct fsnotify_group *group, struct fsnotify_even alloc_holder: holder = fsnotify_alloc_event_holder(); if (!holder) - return -ENOMEM; + return ERR_PTR(-ENOMEM); } mutex_lock(&group->notification_mutex); if (group->q_len >= group->max_events) { event = q_overflow_event; - rc = -EOVERFLOW; + + /* + * we need to return the overflow event + * which means we need a ref + */ + fsnotify_get_event(event); + return_event = event; + /* sorry, no private data on the overflow event */ priv = NULL; } if (!list_empty(list) && merge) { - int ret; + struct fsnotify_event *tmp; - ret = merge(list, event, arg); - if (ret) { + tmp = merge(list, event); + if (tmp) { mutex_unlock(&group->notification_mutex); + + if (return_event) + fsnotify_put_event(return_event); if (holder != &event->holder) fsnotify_destroy_event_holder(holder); - return ret; + return tmp; } } @@ -197,6 +205,12 @@ alloc_holder: * event holder was used, go back and get a new one */ spin_unlock(&event->lock); mutex_unlock(&group->notification_mutex); + + if (return_event) { + fsnotify_put_event(return_event); + return_event = NULL; + } + goto alloc_holder; } @@ -211,7 +225,7 @@ alloc_holder: mutex_unlock(&group->notification_mutex); wake_up(&group->notification_waitq); - return rc; + return return_event; } /* diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index a83859d7d36e..564b5ea4a831 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -379,13 +379,11 @@ extern struct fsnotify_event_private_data *fsnotify_remove_priv_from_event(struc struct fsnotify_event *event); /* attach the event to the group notification queue */ -extern int fsnotify_add_notify_event(struct fsnotify_group *group, - struct fsnotify_event *event, - struct fsnotify_event_private_data *priv, - int (*merge)(struct list_head *, - struct fsnotify_event *, - void **), - void **arg); +extern struct fsnotify_event *fsnotify_add_notify_event(struct fsnotify_group *group, + struct fsnotify_event *event, + struct fsnotify_event_private_data *priv, + struct fsnotify_event *(*merge)(struct list_head *, + struct fsnotify_event *)); /* true if the group notification queue is empty */ extern bool fsnotify_notify_queue_is_empty(struct fsnotify_group *group); /* return, but do not dequeue the first event on the notification queue */ -- cgit v1.2.3 From 3bcf3860a4ff9bbc522820b4b765e65e4deceb3e Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 28 Jul 2010 10:18:37 -0400 Subject: fsnotify: store struct file not struct path Al explains that calling dentry_open() with a mnt/dentry pair is only garunteed to be safe if they are already used in an open struct file. To make sure this is the case don't store and use a struct path in fsnotify, always use a struct file. Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify.c | 8 ++++---- fs/notify/fanotify/fanotify_user.c | 6 +++--- fs/notify/fsnotify.c | 16 ++++++++-------- fs/notify/inotify/inotify_fsnotify.c | 12 ++++++------ fs/notify/notification.c | 20 +++++++++---------- include/linux/fsnotify.h | 37 ++++++++++++++++-------------------- include/linux/fsnotify_backend.h | 16 ++++++++-------- kernel/audit_watch.c | 4 ++-- 8 files changed, 56 insertions(+), 63 deletions(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index f3c40c0e2b86..c2a3029052bc 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -17,9 +17,9 @@ static bool should_merge(struct fsnotify_event *old, struct fsnotify_event *new) old->data_type == new->data_type && old->tgid == new->tgid) { switch (old->data_type) { - case (FSNOTIFY_EVENT_PATH): - if ((old->path.mnt == new->path.mnt) && - (old->path.dentry == new->path.dentry)) + case (FSNOTIFY_EVENT_FILE): + if ((old->file->f_path.mnt == new->file->f_path.mnt) && + (old->file->f_path.dentry == new->file->f_path.dentry)) return true; case (FSNOTIFY_EVENT_NONE): return true; @@ -226,7 +226,7 @@ static bool fanotify_should_send_event(struct fsnotify_group *group, struct inod return false; /* if we don't have enough info to send an event to userspace say no */ - if (data_type != FSNOTIFY_EVENT_PATH) + if (data_type != FSNOTIFY_EVENT_FILE) return false; if (mnt) diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 7182c83be90e..50cea74bf1c8 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -65,7 +65,7 @@ static int create_fd(struct fsnotify_group *group, struct fsnotify_event *event) if (client_fd < 0) return client_fd; - if (event->data_type != FSNOTIFY_EVENT_PATH) { + if (event->data_type != FSNOTIFY_EVENT_FILE) { WARN_ON(1); put_unused_fd(client_fd); return -EINVAL; @@ -75,8 +75,8 @@ static int create_fd(struct fsnotify_group *group, struct fsnotify_event *event) * we need a new file handle for the userspace program so it can read even if it was * originally opened O_WRONLY. */ - dentry = dget(event->path.dentry); - mnt = mntget(event->path.mnt); + dentry = dget(event->file->f_path.dentry); + mnt = mntget(event->file->f_path.mnt); /* it's possible this event was an overflow event. in that case dentry and mnt * are NULL; That's fine, just don't call dentry open */ if (dentry && mnt) diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index 72aae4045314..4788c866473a 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -84,7 +84,7 @@ void __fsnotify_update_child_dentry_flags(struct inode *inode) } /* Notify this dentry's parent about a child's events. */ -void __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask) +void __fsnotify_parent(struct file *file, struct dentry *dentry, __u32 mask) { struct dentry *parent; struct inode *p_inode; @@ -92,7 +92,7 @@ void __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask) bool should_update_children = false; if (!dentry) - dentry = path->dentry; + dentry = file->f_path.dentry; if (!(dentry->d_flags & DCACHE_FSNOTIFY_PARENT_WATCHED)) return; @@ -124,8 +124,8 @@ void __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask) * specifies these are events which came from a child. */ mask |= FS_EVENT_ON_CHILD; - if (path) - fsnotify(p_inode, mask, path, FSNOTIFY_EVENT_PATH, + if (file) + fsnotify(p_inode, mask, file, FSNOTIFY_EVENT_FILE, dentry->d_name.name, 0); else fsnotify(p_inode, mask, dentry->d_inode, FSNOTIFY_EVENT_INODE, @@ -154,10 +154,10 @@ void __fsnotify_flush_ignored_mask(struct inode *inode, void *data, int data_is) spin_unlock(&inode->i_lock); } - if (data_is == FSNOTIFY_EVENT_PATH) { + if (data_is == FSNOTIFY_EVENT_FILE) { struct vfsmount *mnt; - mnt = ((struct path *)data)->mnt; + mnt = ((struct file *)data)->f_path.mnt; if (mnt && !hlist_empty(&mnt->mnt_fsnotify_marks)) { spin_lock(&mnt->mnt_root->d_lock); hlist_for_each_entry(mark, node, &mnt->mnt_fsnotify_marks, m.m_list) { @@ -228,8 +228,8 @@ int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, !(test_mask & fsnotify_vfsmount_mask)) return 0; - if (data_is == FSNOTIFY_EVENT_PATH) - mnt = ((struct path *)data)->mnt; + if (data_is == FSNOTIFY_EVENT_FILE) + mnt = ((struct file *)data)->f_path.mnt; /* if this inode's directed listeners don't care and nothing on the vfsmount * listeners list cares, nothing to do */ diff --git a/fs/notify/inotify/inotify_fsnotify.c b/fs/notify/inotify/inotify_fsnotify.c index 73a1106b3542..3c506e0364cc 100644 --- a/fs/notify/inotify/inotify_fsnotify.c +++ b/fs/notify/inotify/inotify_fsnotify.c @@ -52,9 +52,9 @@ static bool event_compare(struct fsnotify_event *old, struct fsnotify_event *new !strcmp(old->file_name, new->file_name)) return true; break; - case (FSNOTIFY_EVENT_PATH): - if ((old->path.mnt == new->path.mnt) && - (old->path.dentry == new->path.dentry)) + case (FSNOTIFY_EVENT_FILE): + if ((old->file->f_path.mnt == new->file->f_path.mnt) && + (old->file->f_path.dentry == new->file->f_path.dentry)) return true; break; case (FSNOTIFY_EVENT_NONE): @@ -165,10 +165,10 @@ static bool inotify_should_send_event(struct fsnotify_group *group, struct inode send = (fsn_mark->mask & mask); if (send && (fsn_mark->mask & FS_EXCL_UNLINK) && - (data_type == FSNOTIFY_EVENT_PATH)) { - struct path *path = data; + (data_type == FSNOTIFY_EVENT_FILE)) { + struct file *file = data; - if (d_unlinked(path->dentry)) + if (d_unlinked(file->f_path.dentry)) send = false; } diff --git a/fs/notify/notification.c b/fs/notify/notification.c index f39260f8f865..c106cdd7ff5e 100644 --- a/fs/notify/notification.c +++ b/fs/notify/notification.c @@ -31,6 +31,7 @@ * allocated and used. */ +#include #include #include #include @@ -89,8 +90,8 @@ void fsnotify_put_event(struct fsnotify_event *event) if (atomic_dec_and_test(&event->refcnt)) { pr_debug("%s: event=%p\n", __func__, event); - if (event->data_type == FSNOTIFY_EVENT_PATH) - path_put(&event->path); + if (event->data_type == FSNOTIFY_EVENT_FILE) + fput(event->file); BUG_ON(!list_empty(&event->private_data_list)); @@ -375,8 +376,8 @@ struct fsnotify_event *fsnotify_clone_event(struct fsnotify_event *old_event) } } event->tgid = get_pid(old_event->tgid); - if (event->data_type == FSNOTIFY_EVENT_PATH) - path_get(&event->path); + if (event->data_type == FSNOTIFY_EVENT_FILE) + get_file(event->file); return event; } @@ -423,11 +424,9 @@ struct fsnotify_event *fsnotify_create_event(struct inode *to_tell, __u32 mask, event->data_type = data_type; switch (data_type) { - case FSNOTIFY_EVENT_PATH: { - struct path *path = data; - event->path.dentry = path->dentry; - event->path.mnt = path->mnt; - path_get(&event->path); + case FSNOTIFY_EVENT_FILE: { + event->file = data; + get_file(event->file); break; } case FSNOTIFY_EVENT_INODE: @@ -435,8 +434,7 @@ struct fsnotify_event *fsnotify_create_event(struct inode *to_tell, __u32 mask, break; case FSNOTIFY_EVENT_NONE: event->inode = NULL; - event->path.dentry = NULL; - event->path.mnt = NULL; + event->file = NULL; break; default: BUG(); diff --git a/include/linux/fsnotify.h b/include/linux/fsnotify.h index 59d0df43ff9d..e4e2204187ee 100644 --- a/include/linux/fsnotify.h +++ b/include/linux/fsnotify.h @@ -26,19 +26,18 @@ static inline void fsnotify_d_instantiate(struct dentry *dentry, } /* Notify this dentry's parent about a child's events. */ -static inline void fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask) +static inline void fsnotify_parent(struct file *file, struct dentry *dentry, __u32 mask) { if (!dentry) - dentry = path->dentry; + dentry = file->f_path.dentry; - __fsnotify_parent(path, dentry, mask); + __fsnotify_parent(file, dentry, mask); } /* simple call site for access decisions */ static inline int fsnotify_perm(struct file *file, int mask) { - struct path *path = &file->f_path; - struct inode *inode = path->dentry->d_inode; + struct inode *inode = file->f_path.dentry->d_inode; __u32 fsnotify_mask = 0; if (file->f_mode & FMODE_NONOTIFY) @@ -52,7 +51,7 @@ static inline int fsnotify_perm(struct file *file, int mask) else BUG(); - return fsnotify(inode, fsnotify_mask, path, FSNOTIFY_EVENT_PATH, NULL, 0); + return fsnotify(inode, fsnotify_mask, file, FSNOTIFY_EVENT_FILE, NULL, 0); } /* @@ -187,16 +186,15 @@ static inline void fsnotify_mkdir(struct inode *inode, struct dentry *dentry) */ static inline void fsnotify_access(struct file *file) { - struct path *path = &file->f_path; - struct inode *inode = path->dentry->d_inode; + struct inode *inode = file->f_path.dentry->d_inode; __u32 mask = FS_ACCESS; if (S_ISDIR(inode->i_mode)) mask |= FS_IN_ISDIR; if (!(file->f_mode & FMODE_NONOTIFY)) { - fsnotify_parent(path, NULL, mask); - fsnotify(inode, mask, path, FSNOTIFY_EVENT_PATH, NULL, 0); + fsnotify_parent(file, NULL, mask); + fsnotify(inode, mask, file, FSNOTIFY_EVENT_FILE, NULL, 0); } } @@ -205,16 +203,15 @@ static inline void fsnotify_access(struct file *file) */ static inline void fsnotify_modify(struct file *file) { - struct path *path = &file->f_path; - struct inode *inode = path->dentry->d_inode; + struct inode *inode = file->f_path.dentry->d_inode; __u32 mask = FS_MODIFY; if (S_ISDIR(inode->i_mode)) mask |= FS_IN_ISDIR; if (!(file->f_mode & FMODE_NONOTIFY)) { - fsnotify_parent(path, NULL, mask); - fsnotify(inode, mask, path, FSNOTIFY_EVENT_PATH, NULL, 0); + fsnotify_parent(file, NULL, mask); + fsnotify(inode, mask, file, FSNOTIFY_EVENT_FILE, NULL, 0); } } @@ -223,16 +220,15 @@ static inline void fsnotify_modify(struct file *file) */ static inline void fsnotify_open(struct file *file) { - struct path *path = &file->f_path; - struct inode *inode = path->dentry->d_inode; + struct inode *inode = file->f_path.dentry->d_inode; __u32 mask = FS_OPEN; if (S_ISDIR(inode->i_mode)) mask |= FS_IN_ISDIR; if (!(file->f_mode & FMODE_NONOTIFY)) { - fsnotify_parent(path, NULL, mask); - fsnotify(inode, mask, path, FSNOTIFY_EVENT_PATH, NULL, 0); + fsnotify_parent(file, NULL, mask); + fsnotify(inode, mask, file, FSNOTIFY_EVENT_FILE, NULL, 0); } } @@ -241,7 +237,6 @@ static inline void fsnotify_open(struct file *file) */ static inline void fsnotify_close(struct file *file) { - struct path *path = &file->f_path; struct inode *inode = file->f_path.dentry->d_inode; fmode_t mode = file->f_mode; __u32 mask = (mode & FMODE_WRITE) ? FS_CLOSE_WRITE : FS_CLOSE_NOWRITE; @@ -250,8 +245,8 @@ static inline void fsnotify_close(struct file *file) mask |= FS_IN_ISDIR; if (!(file->f_mode & FMODE_NONOTIFY)) { - fsnotify_parent(path, NULL, mask); - fsnotify(inode, mask, path, FSNOTIFY_EVENT_PATH, NULL, 0); + fsnotify_parent(file, NULL, mask); + fsnotify(inode, mask, file, FSNOTIFY_EVENT_FILE, NULL, 0); } } diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index 564b5ea4a831..3410d388163e 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -223,20 +223,20 @@ struct fsnotify_event { /* to_tell may ONLY be dereferenced during handle_event(). */ struct inode *to_tell; /* either the inode the event happened to or its parent */ /* - * depending on the event type we should have either a path or inode - * We hold a reference on path, but NOT on inode. Since we have the ref on - * the path, it may be dereferenced at any point during this object's + * depending on the event type we should have either a file or inode + * We hold a reference on file, but NOT on inode. Since we have the ref on + * the file, it may be dereferenced at any point during this object's * lifetime. That reference is dropped when this object's refcnt hits - * 0. If this event contains an inode instead of a path, the inode may + * 0. If this event contains an inode instead of a file, the inode may * ONLY be used during handle_event(). */ union { - struct path path; + struct file *file; struct inode *inode; }; /* when calling fsnotify tell it if the data is a path or inode */ #define FSNOTIFY_EVENT_NONE 0 -#define FSNOTIFY_EVENT_PATH 1 +#define FSNOTIFY_EVENT_FILE 1 #define FSNOTIFY_EVENT_INODE 2 int data_type; /* which of the above union we have */ atomic_t refcnt; /* how many groups still are using/need to send this event */ @@ -311,7 +311,7 @@ struct fsnotify_mark { /* main fsnotify call to send events */ extern int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, const unsigned char *name, u32 cookie); -extern void __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask); +extern void __fsnotify_parent(struct file *file, struct dentry *dentry, __u32 mask); extern void __fsnotify_inode_delete(struct inode *inode); extern void __fsnotify_vfsmount_delete(struct vfsmount *mnt); extern u32 fsnotify_get_cookie(void); @@ -444,7 +444,7 @@ static inline int fsnotify(struct inode *to_tell, __u32 mask, void *data, int da return 0; } -static inline void __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask) +static inline void __fsnotify_parent(struct file *file, struct dentry *dentry, __u32 mask) {} static inline void __fsnotify_inode_delete(struct inode *inode) diff --git a/kernel/audit_watch.c b/kernel/audit_watch.c index 7499397a6100..b955a22d8ff1 100644 --- a/kernel/audit_watch.c +++ b/kernel/audit_watch.c @@ -545,8 +545,8 @@ static int audit_watch_handle_event(struct fsnotify_group *group, struct fsnotif return 0; switch (event->data_type) { - case (FSNOTIFY_EVENT_PATH): - inode = event->path.dentry->d_inode; + case (FSNOTIFY_EVENT_FILE): + inode = event->file->f_path.dentry->d_inode; break; case (FSNOTIFY_EVENT_INODE): inode = event->inode; -- cgit v1.2.3 From c1e5c954020e123d30b4abf4038ce501861bcf9f Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 28 Jul 2010 10:18:38 -0400 Subject: vfs/fsnotify: fsnotify_close can delay the final work in fput fanotify almost works like so: user context calls fsnotify_* function with a struct file. fsnotify takes a reference on the struct path user context goes about it's buissiness at some later point in time the fsnotify listener gets the struct path fanotify listener calls dentry_open() to create a file which userspace can deal with listener drops the reference on the struct path at some later point the listener calls close() on it's new file With the switch from struct path to struct file this presents a problem for fput() and fsnotify_close(). fsnotify_close() is called when the filp has already reached 0 and __fput() wants to do it's cleanup. The solution presented here is a bit odd. If an event is created from a struct file we take a reference on the file. We check however if the f_count was already 0 and if so we take an EXTRA reference EVEN THOUGH IT WAS ZERO. In __fput() (where we know the f_count hit 0 once) we check if the f_count is non-zero and if so we drop that 'extra' ref and return without destroying the file. Signed-off-by: Eric Paris --- fs/file_table.c | 9 +++++++++ fs/notify/notification.c | 13 +++++++++++++ 2 files changed, 22 insertions(+) (limited to 'fs') diff --git a/fs/file_table.c b/fs/file_table.c index 5c7d10ead4ad..b8a0bb63cbd7 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -230,6 +230,15 @@ static void __fput(struct file *file) might_sleep(); fsnotify_close(file); + + /* + * fsnotify_create_event may have taken one or more references on this + * file. If it did so it left one reference for us to drop to make sure + * its calls to fput could not prematurely destroy the file. + */ + if (atomic_long_read(&file->f_count)) + return fput(file); + /* * The function eventpoll_release() should be the first called * in the file cleanup chain. diff --git a/fs/notify/notification.c b/fs/notify/notification.c index c106cdd7ff5e..d6c435adc7a2 100644 --- a/fs/notify/notification.c +++ b/fs/notify/notification.c @@ -426,6 +426,19 @@ struct fsnotify_event *fsnotify_create_event(struct inode *to_tell, __u32 mask, switch (data_type) { case FSNOTIFY_EVENT_FILE: { event->file = data; + /* + * if this file is about to disappear hold an extra reference + * until we return to __fput so we don't have to worry about + * future get/put destroying the file under us or generating + * additional events. Notice that we change f_mode without + * holding f_lock. This is safe since this is the only possible + * reference to this object in the kernel (it was about to be + * freed, remember?) + */ + if (!atomic_long_read(&event->file->f_count)) { + event->file->f_mode |= FMODE_NONOTIFY; + get_file(event->file); + } get_file(event->file); break; } -- cgit v1.2.3 From 0c6532e4e3b0c8bd18dd0a5cc1894a1944997cc6 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 28 Jul 2010 10:18:38 -0400 Subject: fsnotify: place marks on object in order of group memory address fsnotify_marks currently are placed on objects (inodes or vfsmounts) in arbitrary order. This patch places them in order of the group memory address. Signed-off-by: Eric Paris --- fs/notify/inode_mark.c | 40 +++++++++++++++++++++++++++++----------- fs/notify/vfsmount_mark.c | 40 ++++++++++++++++++++++++++-------------- 2 files changed, 55 insertions(+), 25 deletions(-) (limited to 'fs') diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c index 0c0a48b1659f..83ce6db34039 100644 --- a/fs/notify/inode_mark.c +++ b/fs/notify/inode_mark.c @@ -174,15 +174,17 @@ void fsnotify_set_inode_mark_mask_locked(struct fsnotify_mark *mark, } /* - * Attach an initialized mark to a given group and inode. + * Attach an initialized mark to a given inode. * These marks may be used for the fsnotify backend to determine which - * event types should be delivered to which group and for which inodes. + * event types should be delivered to which group and for which inodes. These + * marks are ordered according to the group's location in memory. */ int fsnotify_add_inode_mark(struct fsnotify_mark *mark, struct fsnotify_group *group, struct inode *inode, int allow_dups) { - struct fsnotify_mark *lmark = NULL; + struct fsnotify_mark *lmark; + struct hlist_node *node, *last = NULL; int ret = 0; mark->flags = FSNOTIFY_MARK_FLAG_INODE; @@ -192,21 +194,37 @@ int fsnotify_add_inode_mark(struct fsnotify_mark *mark, spin_lock(&inode->i_lock); - if (!allow_dups) - lmark = fsnotify_find_inode_mark_locked(group, inode); - if (!lmark) { - mark->i.inode = inode; + mark->i.inode = inode; + /* is mark the first mark? */ + if (hlist_empty(&inode->i_fsnotify_marks)) { hlist_add_head(&mark->i.i_list, &inode->i_fsnotify_marks); + goto out; + } + + /* should mark be in the middle of the current list? */ + hlist_for_each_entry(lmark, node, &inode->i_fsnotify_marks, i.i_list) { + last = node; + + if ((lmark->group == group) && !allow_dups) { + ret = -EEXIST; + goto out; + } - fsnotify_recalc_inode_mask_locked(inode); + if (mark->group < lmark->group) + continue; + + hlist_add_before(&mark->i.i_list, &lmark->i.i_list); + goto out; } + BUG_ON(last == NULL); + /* mark should be the last entry. last is the current last entry */ + hlist_add_after(last, &mark->i.i_list); +out: + fsnotify_recalc_inode_mask_locked(inode); spin_unlock(&inode->i_lock); - if (lmark) - ret = -EEXIST; - return ret; } diff --git a/fs/notify/vfsmount_mark.c b/fs/notify/vfsmount_mark.c index ec580a25d293..c4b3f14d2530 100644 --- a/fs/notify/vfsmount_mark.c +++ b/fs/notify/vfsmount_mark.c @@ -141,34 +141,46 @@ int fsnotify_add_vfsmount_mark(struct fsnotify_mark *mark, struct fsnotify_group *group, struct vfsmount *mnt, int allow_dups) { - struct fsnotify_mark *lmark = NULL; + struct fsnotify_mark *lmark; + struct hlist_node *node, *last = NULL; int ret = 0; mark->flags = FSNOTIFY_MARK_FLAG_VFSMOUNT; - /* - * LOCKING ORDER!!!! - * mark->lock - * group->mark_lock - * mnt->mnt_root->d_lock - */ assert_spin_locked(&mark->lock); assert_spin_locked(&group->mark_lock); spin_lock(&mnt->mnt_root->d_lock); - if (!allow_dups) - lmark = fsnotify_find_vfsmount_mark_locked(group, mnt); - if (!lmark) { - mark->m.mnt = mnt; + mark->m.mnt = mnt; + /* is mark the first mark? */ + if (hlist_empty(&mnt->mnt_fsnotify_marks)) { hlist_add_head(&mark->m.m_list, &mnt->mnt_fsnotify_marks); + goto out; + } + + /* should mark be in the middle of the current list? */ + hlist_for_each_entry(lmark, node, &mnt->mnt_fsnotify_marks, m.m_list) { + last = node; + + if ((lmark->group == group) && !allow_dups) { + ret = -EEXIST; + goto out; + } + + if (mark->group < lmark->group) + continue; - fsnotify_recalc_vfsmount_mask_locked(mnt); - } else { - ret = -EEXIST; + hlist_add_before(&mark->m.m_list, &lmark->m.m_list); + goto out; } + BUG_ON(last == NULL); + /* mark should be the last entry. last is the current last entry */ + hlist_add_after(last, &mark->m.m_list); +out: + fsnotify_recalc_vfsmount_mask_locked(mnt); spin_unlock(&mnt->mnt_root->d_lock); return ret; -- cgit v1.2.3 From a4c6e9961fcb9da54648d98978d33c6fdcb7bb45 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 28 Jul 2010 10:18:38 -0400 Subject: fsnotify: use _rcu functions for mark list traversal In preparation for srcu locking use all _rcu appropiete functions for mark list addition, removal, and traversal. The operations are still done under a spinlock at the end of this patch. Signed-off-by: Eric Paris --- fs/notify/inode_mark.c | 10 +++++----- fs/notify/vfsmount_mark.c | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) (limited to 'fs') diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c index 83ce6db34039..455cb41c729b 100644 --- a/fs/notify/inode_mark.c +++ b/fs/notify/inode_mark.c @@ -67,7 +67,7 @@ void fsnotify_destroy_inode_mark(struct fsnotify_mark *mark) spin_lock(&inode->i_lock); - hlist_del_init(&mark->i.i_list); + hlist_del_init_rcu(&mark->i.i_list); mark->i.inode = NULL; /* @@ -92,7 +92,7 @@ void fsnotify_clear_marks_by_inode(struct inode *inode) spin_lock(&inode->i_lock); hlist_for_each_entry_safe(mark, pos, n, &inode->i_fsnotify_marks, i.i_list) { list_add(&mark->i.free_i_list, &free_list); - hlist_del_init(&mark->i.i_list); + hlist_del_init_rcu(&mark->i.i_list); fsnotify_get_mark(mark); } spin_unlock(&inode->i_lock); @@ -198,7 +198,7 @@ int fsnotify_add_inode_mark(struct fsnotify_mark *mark, /* is mark the first mark? */ if (hlist_empty(&inode->i_fsnotify_marks)) { - hlist_add_head(&mark->i.i_list, &inode->i_fsnotify_marks); + hlist_add_head_rcu(&mark->i.i_list, &inode->i_fsnotify_marks); goto out; } @@ -214,13 +214,13 @@ int fsnotify_add_inode_mark(struct fsnotify_mark *mark, if (mark->group < lmark->group) continue; - hlist_add_before(&mark->i.i_list, &lmark->i.i_list); + hlist_add_before_rcu(&mark->i.i_list, &lmark->i.i_list); goto out; } BUG_ON(last == NULL); /* mark should be the last entry. last is the current last entry */ - hlist_add_after(last, &mark->i.i_list); + hlist_add_after_rcu(last, &mark->i.i_list); out: fsnotify_recalc_inode_mask_locked(inode); spin_unlock(&inode->i_lock); diff --git a/fs/notify/vfsmount_mark.c b/fs/notify/vfsmount_mark.c index c4b3f14d2530..b7ae64030021 100644 --- a/fs/notify/vfsmount_mark.c +++ b/fs/notify/vfsmount_mark.c @@ -39,7 +39,7 @@ void fsnotify_clear_marks_by_mount(struct vfsmount *mnt) spin_lock(&mnt->mnt_root->d_lock); hlist_for_each_entry_safe(mark, pos, n, &mnt->mnt_fsnotify_marks, m.m_list) { list_add(&mark->m.free_m_list, &free_list); - hlist_del_init(&mark->m.m_list); + hlist_del_init_rcu(&mark->m.m_list); fsnotify_get_mark(mark); } spin_unlock(&mnt->mnt_root->d_lock); @@ -91,7 +91,7 @@ void fsnotify_destroy_vfsmount_mark(struct fsnotify_mark *mark) spin_lock(&mnt->mnt_root->d_lock); - hlist_del_init(&mark->m.m_list); + hlist_del_init_rcu(&mark->m.m_list); mark->m.mnt = NULL; fsnotify_recalc_vfsmount_mask_locked(mnt); @@ -156,7 +156,7 @@ int fsnotify_add_vfsmount_mark(struct fsnotify_mark *mark, /* is mark the first mark? */ if (hlist_empty(&mnt->mnt_fsnotify_marks)) { - hlist_add_head(&mark->m.m_list, &mnt->mnt_fsnotify_marks); + hlist_add_head_rcu(&mark->m.m_list, &mnt->mnt_fsnotify_marks); goto out; } @@ -172,13 +172,13 @@ int fsnotify_add_vfsmount_mark(struct fsnotify_mark *mark, if (mark->group < lmark->group) continue; - hlist_add_before(&mark->m.m_list, &lmark->m.m_list); + hlist_add_before_rcu(&mark->m.m_list, &lmark->m.m_list); goto out; } BUG_ON(last == NULL); /* mark should be the last entry. last is the current last entry */ - hlist_add_after(last, &mark->m.m_list); + hlist_add_after_rcu(last, &mark->m.m_list); out: fsnotify_recalc_vfsmount_mask_locked(mnt); spin_unlock(&mnt->mnt_root->d_lock); -- cgit v1.2.3 From 700307a29ad61090dcf1d45f8f4a135f5e9211ae Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 28 Jul 2010 10:18:38 -0400 Subject: fsnotify: use an explicit flag to indicate fsnotify_destroy_mark has been called Currently fsnotify check is mark->group is NULL to decide if fsnotify_destroy_mark() has already been called or not. With the upcoming rcu work it is a heck of a lot easier to use an explicit flag than worry about group being set to NULL. Signed-off-by: Eric Paris --- fs/notify/inode_mark.c | 2 +- fs/notify/mark.c | 11 +++++++---- fs/notify/vfsmount_mark.c | 2 +- include/linux/fsnotify_backend.h | 1 + 4 files changed, 10 insertions(+), 6 deletions(-) (limited to 'fs') diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c index 455cb41c729b..37b460f302b7 100644 --- a/fs/notify/inode_mark.c +++ b/fs/notify/inode_mark.c @@ -187,7 +187,7 @@ int fsnotify_add_inode_mark(struct fsnotify_mark *mark, struct hlist_node *node, *last = NULL; int ret = 0; - mark->flags = FSNOTIFY_MARK_FLAG_INODE; + mark->flags |= FSNOTIFY_MARK_FLAG_INODE; assert_spin_locked(&mark->lock); assert_spin_locked(&group->mark_lock); diff --git a/fs/notify/mark.c b/fs/notify/mark.c index 8f3b0e7a543d..69c5a166930c 100644 --- a/fs/notify/mark.c +++ b/fs/notify/mark.c @@ -121,12 +121,14 @@ void fsnotify_destroy_mark(struct fsnotify_mark *mark) group = mark->group; - /* if !group something else already marked this to die */ - if (!group) { + /* something else already called this function on this mark */ + if (!(mark->flags & FSNOTIFY_MARK_FLAG_ALIVE)) { spin_unlock(&mark->lock); return; } + mark->flags &= ~FSNOTIFY_MARK_FLAG_ALIVE; + /* 1 from caller and 1 for being on i_list/g_list */ BUG_ON(atomic_read(&mark->refcnt) < 2); @@ -141,7 +143,6 @@ void fsnotify_destroy_mark(struct fsnotify_mark *mark) BUG(); list_del_init(&mark->g_list); - mark->group = NULL; fsnotify_put_mark(mark); /* for i_list and g_list */ @@ -229,6 +230,8 @@ int fsnotify_add_mark(struct fsnotify_mark *mark, spin_lock(&mark->lock); spin_lock(&group->mark_lock); + mark->flags |= FSNOTIFY_MARK_FLAG_ALIVE; + mark->group = group; list_add(&mark->g_list, &group->marks_list); atomic_inc(&group->num_marks); @@ -258,7 +261,7 @@ int fsnotify_add_mark(struct fsnotify_mark *mark, return ret; err: - mark->group = NULL; + mark->flags &= ~FSNOTIFY_MARK_FLAG_ALIVE; list_del_init(&mark->g_list); atomic_dec(&group->num_marks); fsnotify_put_mark(mark); diff --git a/fs/notify/vfsmount_mark.c b/fs/notify/vfsmount_mark.c index b7ae64030021..56772b578fbd 100644 --- a/fs/notify/vfsmount_mark.c +++ b/fs/notify/vfsmount_mark.c @@ -145,7 +145,7 @@ int fsnotify_add_vfsmount_mark(struct fsnotify_mark *mark, struct hlist_node *node, *last = NULL; int ret = 0; - mark->flags = FSNOTIFY_MARK_FLAG_VFSMOUNT; + mark->flags |= FSNOTIFY_MARK_FLAG_VFSMOUNT; assert_spin_locked(&mark->lock); assert_spin_locked(&group->mark_lock); diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index 3410d388163e..8e24cdf72928 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -300,6 +300,7 @@ struct fsnotify_mark { #define FSNOTIFY_MARK_FLAG_VFSMOUNT 0x02 #define FSNOTIFY_MARK_FLAG_OBJECT_PINNED 0x04 #define FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY 0x08 +#define FSNOTIFY_MARK_FLAG_ALIVE 0x10 unsigned int flags; /* vfsmount or inode mark? */ void (*free_mark)(struct fsnotify_mark *mark); /* called on final put+free */ }; -- cgit v1.2.3 From 75c1be487a690db43da2c1234fcacd84c982803c Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 28 Jul 2010 10:18:38 -0400 Subject: fsnotify: srcu to protect read side of inode and vfsmount locks Currently reading the inode->i_fsnotify_marks or vfsmount->mnt_fsnotify_marks lists are protected by a spinlock on both the read and the write side. This patch protects the read side of those lists with a new single srcu. Signed-off-by: Eric Paris --- fs/notify/fsnotify.c | 69 ++++++++++++++++++++++++++-------------- fs/notify/fsnotify.h | 5 +-- fs/notify/group.c | 16 +++------- fs/notify/mark.c | 60 ++++++++++++++++++++++++++++++++-- include/linux/fsnotify_backend.h | 1 + 5 files changed, 111 insertions(+), 40 deletions(-) (limited to 'fs') diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index 4788c866473a..4678b416241e 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -144,14 +144,15 @@ void __fsnotify_flush_ignored_mask(struct inode *inode, void *data, int data_is) { struct fsnotify_mark *mark; struct hlist_node *node; + int idx; + + idx = srcu_read_lock(&fsnotify_mark_srcu); if (!hlist_empty(&inode->i_fsnotify_marks)) { - spin_lock(&inode->i_lock); - hlist_for_each_entry(mark, node, &inode->i_fsnotify_marks, i.i_list) { + hlist_for_each_entry_rcu(mark, node, &inode->i_fsnotify_marks, i.i_list) { if (!(mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY)) mark->ignored_mask = 0; } - spin_unlock(&inode->i_lock); } if (data_is == FSNOTIFY_EVENT_FILE) { @@ -159,14 +160,14 @@ void __fsnotify_flush_ignored_mask(struct inode *inode, void *data, int data_is) mnt = ((struct file *)data)->f_path.mnt; if (mnt && !hlist_empty(&mnt->mnt_fsnotify_marks)) { - spin_lock(&mnt->mnt_root->d_lock); - hlist_for_each_entry(mark, node, &mnt->mnt_fsnotify_marks, m.m_list) { + hlist_for_each_entry_rcu(mark, node, &mnt->mnt_fsnotify_marks, m.m_list) { if (!(mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY)) mark->ignored_mask = 0; } - spin_unlock(&mnt->mnt_root->d_lock); } } + + srcu_read_unlock(&fsnotify_mark_srcu, idx); } static int send_to_group(struct fsnotify_group *group, struct inode *to_tell, @@ -208,8 +209,10 @@ static bool needed_by_vfsmount(__u32 test_mask, struct vfsmount *mnt) int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, const unsigned char *file_name, u32 cookie) { + struct fsnotify_mark *mark; struct fsnotify_group *group; struct fsnotify_event *event = NULL; + struct hlist_node *node; struct vfsmount *mnt = NULL; int idx, ret = 0; /* global tests shouldn't care about events on child only the specific event */ @@ -237,35 +240,47 @@ int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, !needed_by_vfsmount(test_mask, mnt)) return 0; - /* - * SRCU!! the groups list is very very much read only and the path is - * very hot. The VAST majority of events are not going to need to do - * anything other than walk the list so it's crazy to pre-allocate. - */ - idx = srcu_read_lock(&fsnotify_grp_srcu); + idx = srcu_read_lock(&fsnotify_mark_srcu); if (test_mask & to_tell->i_fsnotify_mask) { - list_for_each_entry_rcu(group, &fsnotify_inode_groups, inode_group_list) { - if (test_mask & group->mask) { - ret = send_to_group(group, to_tell, NULL, mask, data, data_is, - cookie, file_name, &event); + hlist_for_each_entry_rcu(mark, node, &to_tell->i_fsnotify_marks, i.i_list) { + + pr_debug("%s: inode_loop: mark=%p mark->mask=%x mark->ignored_mask=%x\n", + __func__, mark, mark->mask, mark->ignored_mask); + + if (test_mask & mark->mask & ~mark->ignored_mask) { + group = mark->group; + if (!group) + continue; + ret = send_to_group(group, to_tell, NULL, mask, + data, data_is, cookie, file_name, + &event); if (ret) goto out; } } } - if (needed_by_vfsmount(test_mask, mnt)) { - list_for_each_entry_rcu(group, &fsnotify_vfsmount_groups, vfsmount_group_list) { - if (test_mask & group->mask) { - ret = send_to_group(group, to_tell, mnt, mask, data, data_is, - cookie, file_name, &event); + + if (mnt && (test_mask & mnt->mnt_fsnotify_mask)) { + hlist_for_each_entry_rcu(mark, node, &mnt->mnt_fsnotify_marks, m.m_list) { + + pr_debug("%s: mnt_loop: mark=%p mark->mask=%x mark->ignored_mask=%x\n", + __func__, mark, mark->mask, mark->ignored_mask); + + if (test_mask & mark->mask & ~mark->ignored_mask) { + group = mark->group; + if (!group) + continue; + ret = send_to_group(group, to_tell, mnt, mask, + data, data_is, cookie, file_name, + &event); if (ret) goto out; } } } out: - srcu_read_unlock(&fsnotify_grp_srcu, idx); + srcu_read_unlock(&fsnotify_mark_srcu, idx); /* * fsnotify_create_event() took a reference so the event can't be cleaned * up while we are still trying to add it to lists, drop that one. @@ -279,8 +294,14 @@ EXPORT_SYMBOL_GPL(fsnotify); static __init int fsnotify_init(void) { + int ret; + BUG_ON(hweight32(ALL_FSNOTIFY_EVENTS) != 23); - return init_srcu_struct(&fsnotify_grp_srcu); + ret = init_srcu_struct(&fsnotify_mark_srcu); + if (ret) + panic("initializing fsnotify_mark_srcu"); + + return 0; } -subsys_initcall(fsnotify_init); +core_initcall(fsnotify_init); diff --git a/fs/notify/fsnotify.h b/fs/notify/fsnotify.h index 1be54f6f9e7d..7eed86f942ba 100644 --- a/fs/notify/fsnotify.h +++ b/fs/notify/fsnotify.h @@ -6,8 +6,6 @@ #include #include -/* protects reads of fsnotify_groups */ -extern struct srcu_struct fsnotify_grp_srcu; /* all groups which receive inode fsnotify events */ extern struct list_head fsnotify_inode_groups; /* all groups which receive vfsmount fsnotify events */ @@ -20,6 +18,9 @@ extern __u32 fsnotify_vfsmount_mask; /* destroy all events sitting in this groups notification queue */ extern void fsnotify_flush_notify(struct fsnotify_group *group); +/* protects reads of inode and vfsmount marks list */ +extern struct srcu_struct fsnotify_mark_srcu; + extern void fsnotify_set_inode_mark_mask_locked(struct fsnotify_mark *fsn_mark, __u32 mask); /* add a mark to an inode */ diff --git a/fs/notify/group.c b/fs/notify/group.c index 7ac65ed4735b..48d3a6d6e47a 100644 --- a/fs/notify/group.c +++ b/fs/notify/group.c @@ -30,8 +30,6 @@ /* protects writes to fsnotify_groups and fsnotify_mask */ static DEFINE_MUTEX(fsnotify_grp_mutex); -/* protects reads while running the fsnotify_groups list */ -struct srcu_struct fsnotify_grp_srcu; /* all groups registered to receive inode filesystem notifications */ LIST_HEAD(fsnotify_inode_groups); /* all groups registered to receive mount point filesystem notifications */ @@ -50,18 +48,17 @@ void fsnotify_recalc_global_mask(void) struct fsnotify_group *group; __u32 inode_mask = 0; __u32 vfsmount_mask = 0; - int idx; - idx = srcu_read_lock(&fsnotify_grp_srcu); + mutex_lock(&fsnotify_grp_mutex); list_for_each_entry_rcu(group, &fsnotify_inode_groups, inode_group_list) inode_mask |= group->mask; list_for_each_entry_rcu(group, &fsnotify_vfsmount_groups, vfsmount_group_list) vfsmount_mask |= group->mask; - - srcu_read_unlock(&fsnotify_grp_srcu, idx); fsnotify_inode_mask = inode_mask; fsnotify_vfsmount_mask = vfsmount_mask; + + mutex_unlock(&fsnotify_grp_mutex); } /* @@ -168,6 +165,8 @@ static void fsnotify_destroy_group(struct fsnotify_group *group) /* clear all inode marks for this group */ fsnotify_clear_marks_by_group(group); + synchronize_srcu(&fsnotify_mark_srcu); + /* past the point of no return, matches the initial value of 1 */ if (atomic_dec_and_test(&group->num_marks)) fsnotify_final_destroy_group(group); @@ -216,12 +215,7 @@ void fsnotify_put_group(struct fsnotify_group *group) */ __fsnotify_evict_group(group); - /* - * now it's off the list, so the only thing we might care about is - * srcu access.... - */ mutex_unlock(&fsnotify_grp_mutex); - synchronize_srcu(&fsnotify_grp_srcu); /* and now it is really dead. _Nothing_ could be seeing it */ fsnotify_recalc_global_mask(); diff --git a/fs/notify/mark.c b/fs/notify/mark.c index 69c5a166930c..41f3990f900b 100644 --- a/fs/notify/mark.c +++ b/fs/notify/mark.c @@ -85,10 +85,12 @@ #include #include #include +#include #include #include #include #include +#include #include /* for inode_lock */ #include @@ -96,6 +98,11 @@ #include #include "fsnotify.h" +struct srcu_struct fsnotify_mark_srcu; +static DEFINE_SPINLOCK(destroy_lock); +static LIST_HEAD(destroy_list); +static DECLARE_WAIT_QUEUE_HEAD(destroy_waitq); + void fsnotify_get_mark(struct fsnotify_mark *mark) { atomic_inc(&mark->refcnt); @@ -144,11 +151,14 @@ void fsnotify_destroy_mark(struct fsnotify_mark *mark) list_del_init(&mark->g_list); - fsnotify_put_mark(mark); /* for i_list and g_list */ - spin_unlock(&group->mark_lock); spin_unlock(&mark->lock); + spin_lock(&destroy_lock); + list_add(&mark->destroy_list, &destroy_list); + spin_unlock(&destroy_lock); + wake_up(&destroy_waitq); + /* * Some groups like to know that marks are being freed. This is a * callback to the group function to let it know that this mark @@ -263,12 +273,17 @@ int fsnotify_add_mark(struct fsnotify_mark *mark, err: mark->flags &= ~FSNOTIFY_MARK_FLAG_ALIVE; list_del_init(&mark->g_list); + mark->group = NULL; atomic_dec(&group->num_marks); - fsnotify_put_mark(mark); spin_unlock(&group->mark_lock); spin_unlock(&mark->lock); + spin_lock(&destroy_lock); + list_add(&mark->destroy_list, &destroy_list); + spin_unlock(&destroy_lock); + wake_up(&destroy_waitq); + return ret; } @@ -326,3 +341,42 @@ void fsnotify_init_mark(struct fsnotify_mark *mark, atomic_set(&mark->refcnt, 1); mark->free_mark = free_mark; } + +static int fsnotify_mark_destroy(void *ignored) +{ + struct fsnotify_mark *mark, *next; + LIST_HEAD(private_destroy_list); + + for (;;) { + spin_lock(&destroy_lock); + list_for_each_entry_safe(mark, next, &destroy_list, destroy_list) { + list_del(&mark->destroy_list); + list_add(&mark->destroy_list, &private_destroy_list); + } + spin_unlock(&destroy_lock); + + synchronize_srcu(&fsnotify_mark_srcu); + + list_for_each_entry_safe(mark, next, &private_destroy_list, destroy_list) { + list_del_init(&mark->destroy_list); + fsnotify_put_mark(mark); + } + + wait_event_interruptible(destroy_waitq, !list_empty(&destroy_list)); + } + + return 0; +} + +static int __init fsnotify_mark_init(void) +{ + struct task_struct *thread; + + thread = kthread_run(fsnotify_mark_destroy, NULL, + "fsnotify_mark"); + if (IS_ERR(thread)) + panic("unable to start fsnotify mark destruction thread."); + + return 0; +} +device_initcall(fsnotify_mark_init); diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index 8e24cdf72928..84159390969f 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -302,6 +302,7 @@ struct fsnotify_mark { #define FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY 0x08 #define FSNOTIFY_MARK_FLAG_ALIVE 0x10 unsigned int flags; /* vfsmount or inode mark? */ + struct list_head destroy_list; void (*free_mark)(struct fsnotify_mark *mark); /* called on final put+free */ }; -- cgit v1.2.3 From 8778abb9a88fc4a74d8776ffaadf7214cf33c61e Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Wed, 28 Jul 2010 10:18:38 -0400 Subject: fsnotify: Exchange list heads instead of moving elements Instead of moving list elements from destroy_list to &private_destroy_list, exchange the list heads. Signed-off-by: Andreas Gruenbacher Signed-off-by: Eric Paris --- fs/notify/mark.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/notify/mark.c b/fs/notify/mark.c index 41f3990f900b..236f29b066ed 100644 --- a/fs/notify/mark.c +++ b/fs/notify/mark.c @@ -349,10 +349,8 @@ static int fsnotify_mark_destroy(void *ignored) for (;;) { spin_lock(&destroy_lock); - list_for_each_entry_safe(mark, next, &destroy_list, destroy_list) { - list_del(&mark->destroy_list); - list_add(&mark->destroy_list, &private_destroy_list); - } + /* exchange the list head */ + list_replace_init(&destroy_list, &private_destroy_list); spin_unlock(&destroy_lock); synchronize_srcu(&fsnotify_mark_srcu); -- cgit v1.2.3 From 3a9b16b407f10b2a771bcae13fb5791e527d6bcf Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 28 Jul 2010 10:18:38 -0400 Subject: fsnotify: send fsnotify_mark to groups in event handling functions With the change of fsnotify to use srcu walking the marks list instead of walking the global groups list we now know the mark in question. The code can send the mark to the group's handling functions and the groups won't have to find those marks themselves. Signed-off-by: Eric Paris --- fs/notify/dnotify/dnotify.c | 4 +++- fs/notify/fanotify/fanotify.c | 8 +++++--- fs/notify/fsnotify.c | 19 ++++++++++--------- fs/notify/inotify/inotify_fsnotify.c | 8 +++++--- include/linux/fsnotify_backend.h | 7 ++++--- kernel/audit_tree.c | 8 +++++--- kernel/audit_watch.c | 8 +++++--- 7 files changed, 37 insertions(+), 25 deletions(-) (limited to 'fs') diff --git a/fs/notify/dnotify/dnotify.c b/fs/notify/dnotify/dnotify.c index 6624c2ee8786..2cae9be120db 100644 --- a/fs/notify/dnotify/dnotify.c +++ b/fs/notify/dnotify/dnotify.c @@ -83,6 +83,7 @@ static void dnotify_recalc_inode_mask(struct fsnotify_mark *fsn_mark) * events. */ static int dnotify_handle_event(struct fsnotify_group *group, + struct fsnotify_mark *mark, struct fsnotify_event *event) { struct fsnotify_mark *fsn_mark = NULL; @@ -130,7 +131,8 @@ static int dnotify_handle_event(struct fsnotify_group *group, */ static bool dnotify_should_send_event(struct fsnotify_group *group, struct inode *inode, struct vfsmount *mnt, - __u32 mask, void *data, int data_type) + struct fsnotify_mark *mark, __u32 mask, + void *data, int data_type) { struct fsnotify_mark *fsn_mark; bool send; diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index c2a3029052bc..abfba45abe2c 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -114,7 +114,9 @@ static int fanotify_get_response_from_access(struct fsnotify_group *group, } #endif -static int fanotify_handle_event(struct fsnotify_group *group, struct fsnotify_event *event) +static int fanotify_handle_event(struct fsnotify_group *group, + struct fsnotify_mark *mark, + struct fsnotify_event *event) { int ret = 0; struct fsnotify_event *notify_event = NULL; @@ -214,8 +216,8 @@ static bool should_send_inode_event(struct fsnotify_group *group, struct inode * } static bool fanotify_should_send_event(struct fsnotify_group *group, struct inode *to_tell, - struct vfsmount *mnt, __u32 mask, void *data, - int data_type) + struct vfsmount *mnt, struct fsnotify_mark *mark, + __u32 mask, void *data, int data_type) { pr_debug("%s: group=%p to_tell=%p mnt=%p mask=%x data=%p data_type=%d\n", __func__, group, to_tell, mnt, mask, data, data_type); diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index 4678b416241e..59d639996cad 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -171,15 +171,16 @@ void __fsnotify_flush_ignored_mask(struct inode *inode, void *data, int data_is) } static int send_to_group(struct fsnotify_group *group, struct inode *to_tell, - struct vfsmount *mnt, __u32 mask, void *data, - int data_is, u32 cookie, const unsigned char *file_name, + struct vfsmount *mnt, struct fsnotify_mark *mark, + __u32 mask, void *data, int data_is, u32 cookie, + const unsigned char *file_name, struct fsnotify_event **event) { - pr_debug("%s: group=%p to_tell=%p mnt=%p mask=%x data=%p data_is=%d" - " cookie=%d event=%p\n", __func__, group, to_tell, mnt, - mask, data, data_is, cookie, *event); + pr_debug("%s: group=%p to_tell=%p mnt=%p mark=%p mask=%x data=%p" + " data_is=%d cookie=%d event=%p\n", __func__, group, to_tell, + mnt, mark, mask, data, data_is, cookie, *event); - if (!group->ops->should_send_event(group, to_tell, mnt, mask, + if (!group->ops->should_send_event(group, to_tell, mnt, mark, mask, data, data_is)) return 0; if (!*event) { @@ -189,7 +190,7 @@ static int send_to_group(struct fsnotify_group *group, struct inode *to_tell, if (!*event) return -ENOMEM; } - return group->ops->handle_event(group, *event); + return group->ops->handle_event(group, mark, *event); } static bool needed_by_vfsmount(__u32 test_mask, struct vfsmount *mnt) @@ -252,7 +253,7 @@ int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, group = mark->group; if (!group) continue; - ret = send_to_group(group, to_tell, NULL, mask, + ret = send_to_group(group, to_tell, NULL, mark, mask, data, data_is, cookie, file_name, &event); if (ret) @@ -271,7 +272,7 @@ int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, group = mark->group; if (!group) continue; - ret = send_to_group(group, to_tell, mnt, mask, + ret = send_to_group(group, to_tell, mnt, mark, mask, data, data_is, cookie, file_name, &event); if (ret) diff --git a/fs/notify/inotify/inotify_fsnotify.c b/fs/notify/inotify/inotify_fsnotify.c index 3c506e0364cc..dbd76bbb3e21 100644 --- a/fs/notify/inotify/inotify_fsnotify.c +++ b/fs/notify/inotify/inotify_fsnotify.c @@ -89,7 +89,9 @@ static struct fsnotify_event *inotify_merge(struct list_head *list, return last_event; } -static int inotify_handle_event(struct fsnotify_group *group, struct fsnotify_event *event) +static int inotify_handle_event(struct fsnotify_group *group, + struct fsnotify_mark *mark, + struct fsnotify_event *event) { struct fsnotify_mark *fsn_mark; struct inotify_inode_mark *i_mark; @@ -148,8 +150,8 @@ static void inotify_freeing_mark(struct fsnotify_mark *fsn_mark, struct fsnotify } static bool inotify_should_send_event(struct fsnotify_group *group, struct inode *inode, - struct vfsmount *mnt, __u32 mask, void *data, - int data_type) + struct vfsmount *mnt, struct fsnotify_mark *mark, + __u32 mask, void *data, int data_type) { struct fsnotify_mark *fsn_mark; bool send; diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index 84159390969f..225dc0c3a48c 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -92,9 +92,10 @@ struct fsnotify_event_private_data; */ struct fsnotify_ops { bool (*should_send_event)(struct fsnotify_group *group, struct inode *inode, - struct vfsmount *mnt, __u32 mask, void *data, - int data_type); - int (*handle_event)(struct fsnotify_group *group, struct fsnotify_event *event); + struct vfsmount *mnt, struct fsnotify_mark *mark, + __u32 mask, void *data, int data_type); + int (*handle_event)(struct fsnotify_group *group, struct fsnotify_mark *mark, + struct fsnotify_event *event); void (*free_group_priv)(struct fsnotify_group *group); void (*freeing_mark)(struct fsnotify_mark *mark, struct fsnotify_group *group); void (*free_event_priv)(struct fsnotify_event_private_data *priv); diff --git a/kernel/audit_tree.c b/kernel/audit_tree.c index cfb97d752a61..584b94360217 100644 --- a/kernel/audit_tree.c +++ b/kernel/audit_tree.c @@ -903,7 +903,9 @@ static void evict_chunk(struct audit_chunk *chunk) mutex_unlock(&audit_filter_mutex); } -static int audit_tree_handle_event(struct fsnotify_group *group, struct fsnotify_event *event) +static int audit_tree_handle_event(struct fsnotify_group *group, + struct fsnotify_mark *mark, + struct fsnotify_event *event) { BUG(); return -EOPNOTSUPP; @@ -918,8 +920,8 @@ static void audit_tree_freeing_mark(struct fsnotify_mark *entry, struct fsnotify } static bool audit_tree_send_event(struct fsnotify_group *group, struct inode *inode, - struct vfsmount *mnt, __u32 mask, void *data, - int data_type) + struct vfsmount *mnt, struct fsnotify_mark *mark, + __u32 mask, void *data, int data_type) { return 0; } diff --git a/kernel/audit_watch.c b/kernel/audit_watch.c index b955a22d8ff1..4d5ea0319a6c 100644 --- a/kernel/audit_watch.c +++ b/kernel/audit_watch.c @@ -511,8 +511,8 @@ void audit_remove_watch_rule(struct audit_krule *krule) } static bool audit_watch_should_send_event(struct fsnotify_group *group, struct inode *inode, - struct vfsmount *mnt, __u32 mask, void *data, - int data_type) + struct vfsmount *mnt, struct fsnotify_mark *mark, + __u32 mask, void *data, int data_type) { struct fsnotify_mark *entry; bool send; @@ -531,7 +531,9 @@ static bool audit_watch_should_send_event(struct fsnotify_group *group, struct i } /* Update watch data in audit rules based on fsnotify events. */ -static int audit_watch_handle_event(struct fsnotify_group *group, struct fsnotify_event *event) +static int audit_watch_handle_event(struct fsnotify_group *group, + struct fsnotify_mark *mark, + struct fsnotify_event *event) { struct inode *inode; __u32 mask = event->mask; -- cgit v1.2.3 From 7f6b6117e1803777fcf48fe31bd236a7fbf740db Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 28 Jul 2010 10:18:38 -0400 Subject: inotify: use the mark in handler functions inotify now gets a mark in the should_send_event and handle_event functions. Rather than look up the mark themselves inotify should just use the mark it was handed. Signed-off-by: Eric Paris --- fs/notify/inotify/inotify_fsnotify.c | 29 +++++------------------------ 1 file changed, 5 insertions(+), 24 deletions(-) (limited to 'fs') diff --git a/fs/notify/inotify/inotify_fsnotify.c b/fs/notify/inotify/inotify_fsnotify.c index dbd76bbb3e21..aa3f93c03e0f 100644 --- a/fs/notify/inotify/inotify_fsnotify.c +++ b/fs/notify/inotify/inotify_fsnotify.c @@ -93,7 +93,6 @@ static int inotify_handle_event(struct fsnotify_group *group, struct fsnotify_mark *mark, struct fsnotify_event *event) { - struct fsnotify_mark *fsn_mark; struct inotify_inode_mark *i_mark; struct inode *to_tell; struct inotify_event_private_data *event_priv; @@ -106,11 +105,7 @@ static int inotify_handle_event(struct fsnotify_group *group, to_tell = event->to_tell; - fsn_mark = fsnotify_find_inode_mark(group, to_tell); - /* race with watch removal? We already passes should_send */ - if (unlikely(!fsn_mark)) - return 0; - i_mark = container_of(fsn_mark, struct inotify_inode_mark, + i_mark = container_of(mark, struct inotify_inode_mark, fsn_mark); wd = i_mark->wd; @@ -132,14 +127,8 @@ static int inotify_handle_event(struct fsnotify_group *group, ret = PTR_ERR(added_event); } - if (fsn_mark->mask & IN_ONESHOT) - fsnotify_destroy_mark(fsn_mark); - - /* - * If we hold the fsn_mark until after the event is on the queue - * IN_IGNORED won't be able to pass this event in the queue - */ - fsnotify_put_mark(fsn_mark); + if (mark->mask & IN_ONESHOT) + fsnotify_destroy_mark(mark); return ret; } @@ -153,20 +142,15 @@ static bool inotify_should_send_event(struct fsnotify_group *group, struct inode struct vfsmount *mnt, struct fsnotify_mark *mark, __u32 mask, void *data, int data_type) { - struct fsnotify_mark *fsn_mark; bool send; pr_debug("%s: group=%p inode=%p mask=%x data=%p data_type=%d\n", __func__, group, inode, mask, data, data_type); - fsn_mark = fsnotify_find_inode_mark(group, inode); - if (!fsn_mark) - return false; - mask = (mask & ~FS_EVENT_ON_CHILD); - send = (fsn_mark->mask & mask); + send = (mark->mask & mask); - if (send && (fsn_mark->mask & FS_EXCL_UNLINK) && + if (send && (mark->mask & FS_EXCL_UNLINK) && (data_type == FSNOTIFY_EVENT_FILE)) { struct file *file = data; @@ -174,9 +158,6 @@ static bool inotify_should_send_event(struct fsnotify_group *group, struct inode send = false; } - /* find took a reference */ - fsnotify_put_mark(fsn_mark); - return send; } -- cgit v1.2.3 From c496313fcc35a41e176e3f19cdda2544ea3a32a6 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 28 Jul 2010 10:18:38 -0400 Subject: dnotify: use the mark in handler functions dnotify now gets a mark in the should_send_event and handle_event functions. Rather than look up the mark themselves dnotify should just use the mark it was handed. Signed-off-by: Eric Paris --- fs/notify/dnotify/dnotify.c | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) (limited to 'fs') diff --git a/fs/notify/dnotify/dnotify.c b/fs/notify/dnotify/dnotify.c index 2cae9be120db..e3e855ff0dd8 100644 --- a/fs/notify/dnotify/dnotify.c +++ b/fs/notify/dnotify/dnotify.c @@ -86,7 +86,6 @@ static int dnotify_handle_event(struct fsnotify_group *group, struct fsnotify_mark *mark, struct fsnotify_event *event) { - struct fsnotify_mark *fsn_mark = NULL; struct dnotify_mark *dn_mark; struct inode *to_tell; struct dnotify_struct *dn; @@ -96,12 +95,9 @@ static int dnotify_handle_event(struct fsnotify_group *group, to_tell = event->to_tell; - fsn_mark = fsnotify_find_inode_mark(group, to_tell); - if (unlikely(!fsn_mark)) - return 0; - dn_mark = container_of(fsn_mark, struct dnotify_mark, fsn_mark); + dn_mark = container_of(mark, struct dnotify_mark, fsn_mark); - spin_lock(&fsn_mark->lock); + spin_lock(&mark->lock); prev = &dn_mark->dn; while ((dn = *prev) != NULL) { if ((dn->dn_mask & test_mask) == 0) { @@ -115,12 +111,11 @@ static int dnotify_handle_event(struct fsnotify_group *group, else { *prev = dn->dn_next; kmem_cache_free(dnotify_struct_cache, dn); - dnotify_recalc_inode_mask(fsn_mark); + dnotify_recalc_inode_mask(mark); } } - spin_unlock(&fsn_mark->lock); - fsnotify_put_mark(fsn_mark); + spin_unlock(&mark->lock); return 0; } @@ -134,7 +129,6 @@ static bool dnotify_should_send_event(struct fsnotify_group *group, struct fsnotify_mark *mark, __u32 mask, void *data, int data_type) { - struct fsnotify_mark *fsn_mark; bool send; /* !dir_notify_enable should never get here, don't waste time checking @@ -145,14 +139,8 @@ static bool dnotify_should_send_event(struct fsnotify_group *group, if (!S_ISDIR(inode->i_mode)) return false; - fsn_mark = fsnotify_find_inode_mark(group, inode); - if (!fsn_mark) - return false; - mask = (mask & ~FS_EVENT_ON_CHILD); - send = (mask & fsn_mark->mask); - - fsnotify_put_mark(fsn_mark); /* matches fsnotify_find_inode_mark */ + send = (mask & mark->mask); return send; } -- cgit v1.2.3 From 0215054f377ce5ac4ffc27b26b13b3f10e6410e6 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 28 Jul 2010 10:18:38 -0400 Subject: fanotify: use the mark in handler functions fanotify now gets a mark in the should_send_event and handle_event functions. Rather than look up the mark themselves fanotify should just use the mark it was handed. Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify.c | 46 ++++++++++++++++--------------------------- 1 file changed, 17 insertions(+), 29 deletions(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index abfba45abe2c..666ccb733066 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -152,18 +152,16 @@ static int fanotify_handle_event(struct fsnotify_group *group, return ret; } -static bool should_send_vfsmount_event(struct fsnotify_group *group, struct vfsmount *mnt, - struct inode *inode, __u32 mask) +static bool should_send_vfsmount_event(struct fsnotify_group *group, + struct vfsmount *mnt, + struct inode *inode, + struct fsnotify_mark *mnt_mark, + __u32 mask) { - struct fsnotify_mark *mnt_mark; struct fsnotify_mark *inode_mark; - pr_debug("%s: group=%p vfsmount=%p mask=%x\n", - __func__, group, mnt, mask); - - mnt_mark = fsnotify_find_vfsmount_mark(group, mnt); - if (!mnt_mark) - return false; + pr_debug("%s: group=%p vfsmount=%p mark=%p mask=%x\n", + __func__, group, mnt, mnt_mark, mask); mask &= mnt_mark->mask; mask &= ~mnt_mark->ignored_mask; @@ -176,28 +174,21 @@ static bool should_send_vfsmount_event(struct fsnotify_group *group, struct vfsm } } - /* find took a reference */ - fsnotify_put_mark(mnt_mark); - return mask; } -static bool should_send_inode_event(struct fsnotify_group *group, struct inode *inode, +static bool should_send_inode_event(struct fsnotify_group *group, + struct inode *inode, + struct fsnotify_mark *mark, __u32 mask) { - struct fsnotify_mark *fsn_mark; - - pr_debug("%s: group=%p inode=%p mask=%x\n", - __func__, group, inode, mask); - - fsn_mark = fsnotify_find_inode_mark(group, inode); - if (!fsn_mark) - return false; + pr_debug("%s: group=%p inode=%p mark=%p mask=%x\n", + __func__, group, inode, mark, mask); /* if the event is for a child and this inode doesn't care about * events on the child, don't send it! */ if ((mask & FS_EVENT_ON_CHILD) && - !(fsn_mark->mask & FS_EVENT_ON_CHILD)) { + !(mark->mask & FS_EVENT_ON_CHILD)) { mask = 0; } else { /* @@ -205,13 +196,10 @@ static bool should_send_inode_event(struct fsnotify_group *group, struct inode * * type of event? */ mask &= ~FS_EVENT_ON_CHILD; - mask &= fsn_mark->mask; - mask &= ~fsn_mark->ignored_mask; + mask &= mark->mask; + mask &= ~mark->ignored_mask; } - /* find took a reference */ - fsnotify_put_mark(fsn_mark); - return mask; } @@ -232,9 +220,9 @@ static bool fanotify_should_send_event(struct fsnotify_group *group, struct inod return false; if (mnt) - return should_send_vfsmount_event(group, mnt, to_tell, mask); + return should_send_vfsmount_event(group, mnt, to_tell, mark, mask); else - return should_send_inode_event(group, to_tell, mask); + return should_send_inode_event(group, to_tell, mark, mask); } const struct fsnotify_ops fanotify_fsnotify_ops = { -- cgit v1.2.3 From 2612abb51b11ffd2d75c472b11178115f5808909 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 28 Jul 2010 10:18:39 -0400 Subject: fsnotify: cleanup should_send_event The change to use srcu and walk the object list rather than the global fsnotify_group list means that should_send_event is no longer needed for a number of groups and can be simplified for others. Do that. Signed-off-by: Eric Paris --- fs/notify/dnotify/dnotify.c | 11 +---------- fs/notify/fanotify/fanotify.c | 23 ++++++++--------------- fs/notify/fsnotify.c | 4 ++-- fs/notify/inotify/inotify_fsnotify.c | 14 +++----------- kernel/audit_tree.c | 2 +- kernel/audit_watch.c | 7 +------ 6 files changed, 16 insertions(+), 45 deletions(-) (limited to 'fs') diff --git a/fs/notify/dnotify/dnotify.c b/fs/notify/dnotify/dnotify.c index e3e855ff0dd8..c3dc15879a52 100644 --- a/fs/notify/dnotify/dnotify.c +++ b/fs/notify/dnotify/dnotify.c @@ -129,20 +129,11 @@ static bool dnotify_should_send_event(struct fsnotify_group *group, struct fsnotify_mark *mark, __u32 mask, void *data, int data_type) { - bool send; - - /* !dir_notify_enable should never get here, don't waste time checking - if (!dir_notify_enable) - return 0; */ - /* not a dir, dnotify doesn't care */ if (!S_ISDIR(inode->i_mode)) return false; - mask = (mask & ~FS_EVENT_ON_CHILD); - send = (mask & mark->mask); - - return send; + return true; } static void dnotify_free_mark(struct fsnotify_mark *fsn_mark) diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index 666ccb733066..fbd7f35c6134 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -185,22 +185,15 @@ static bool should_send_inode_event(struct fsnotify_group *group, pr_debug("%s: group=%p inode=%p mark=%p mask=%x\n", __func__, group, inode, mark, mask); - /* if the event is for a child and this inode doesn't care about - * events on the child, don't send it! */ + /* + * if the event is for a child and this inode doesn't care about + * events on the child, don't send it! + */ if ((mask & FS_EVENT_ON_CHILD) && - !(mark->mask & FS_EVENT_ON_CHILD)) { - mask = 0; - } else { - /* - * We care about children, but do we care about this particular - * type of event? - */ - mask &= ~FS_EVENT_ON_CHILD; - mask &= mark->mask; - mask &= ~mark->ignored_mask; - } - - return mask; + !(mark->mask & FS_EVENT_ON_CHILD)) + return false; + else + return true; } static bool fanotify_should_send_event(struct fsnotify_group *group, struct inode *to_tell, diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index 59d639996cad..53b31f46d698 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -180,8 +180,8 @@ static int send_to_group(struct fsnotify_group *group, struct inode *to_tell, " data_is=%d cookie=%d event=%p\n", __func__, group, to_tell, mnt, mark, mask, data, data_is, cookie, *event); - if (!group->ops->should_send_event(group, to_tell, mnt, mark, mask, - data, data_is)) + if (group->ops->should_send_event(group, to_tell, mnt, mark, mask, + data, data_is) == false) return 0; if (!*event) { *event = fsnotify_create_event(to_tell, mask, data, diff --git a/fs/notify/inotify/inotify_fsnotify.c b/fs/notify/inotify/inotify_fsnotify.c index aa3f93c03e0f..7cf518b25daa 100644 --- a/fs/notify/inotify/inotify_fsnotify.c +++ b/fs/notify/inotify/inotify_fsnotify.c @@ -142,23 +142,15 @@ static bool inotify_should_send_event(struct fsnotify_group *group, struct inode struct vfsmount *mnt, struct fsnotify_mark *mark, __u32 mask, void *data, int data_type) { - bool send; - - pr_debug("%s: group=%p inode=%p mask=%x data=%p data_type=%d\n", - __func__, group, inode, mask, data, data_type); - - mask = (mask & ~FS_EVENT_ON_CHILD); - send = (mark->mask & mask); - - if (send && (mark->mask & FS_EXCL_UNLINK) && + if ((mark->mask & FS_EXCL_UNLINK) && (data_type == FSNOTIFY_EVENT_FILE)) { struct file *file = data; if (d_unlinked(file->f_path.dentry)) - send = false; + return false; } - return send; + return true; } /* diff --git a/kernel/audit_tree.c b/kernel/audit_tree.c index 584b94360217..2abb99f3459d 100644 --- a/kernel/audit_tree.c +++ b/kernel/audit_tree.c @@ -923,7 +923,7 @@ static bool audit_tree_send_event(struct fsnotify_group *group, struct inode *in struct vfsmount *mnt, struct fsnotify_mark *mark, __u32 mask, void *data, int data_type) { - return 0; + return false; } static const struct fsnotify_ops audit_tree_ops = { diff --git a/kernel/audit_watch.c b/kernel/audit_watch.c index 9173bcf33763..097a61c65fe0 100644 --- a/kernel/audit_watch.c +++ b/kernel/audit_watch.c @@ -514,12 +514,7 @@ static bool audit_watch_should_send_event(struct fsnotify_group *group, struct i struct vfsmount *mnt, struct fsnotify_mark *mark, __u32 mask, void *data, int data_type) { - bool send; - - mask = (mask & ~FS_EVENT_ON_CHILD); - send = (mark->mask & mask); - - return send; + return true; } /* Update watch data in audit rules based on fsnotify events. */ -- cgit v1.2.3 From 03930979afa63e079e9aefd4d3dd429240711027 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 28 Jul 2010 10:18:39 -0400 Subject: fsnotify: remove the global masks Because we walk the object->fsnotify_marks list instead of the global fsnotify groups list we don't need the fsnotify_inode_mask and fsnotify_vfsmount_mask as these were simply shortcuts in fsnotify() for performance. They are now extra checks, rip them out. Signed-off-by: Eric Paris --- fs/notify/fsnotify.c | 5 ----- fs/notify/fsnotify.h | 4 ---- fs/notify/group.c | 39 ++------------------------------------- include/linux/fsnotify_backend.h | 2 -- 4 files changed, 2 insertions(+), 48 deletions(-) (limited to 'fs') diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index 53b31f46d698..9ba29ee747cf 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -227,11 +227,6 @@ int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, if (mask & FS_MODIFY) __fsnotify_flush_ignored_mask(to_tell, data, data_is); - /* if none of the directed listeners or vfsmount listeners care */ - if (!(test_mask & fsnotify_inode_mask) && - !(test_mask & fsnotify_vfsmount_mask)) - return 0; - if (data_is == FSNOTIFY_EVENT_FILE) mnt = ((struct file *)data)->f_path.mnt; diff --git a/fs/notify/fsnotify.h b/fs/notify/fsnotify.h index 7eed86f942ba..b41dbf5a125c 100644 --- a/fs/notify/fsnotify.h +++ b/fs/notify/fsnotify.h @@ -10,10 +10,6 @@ extern struct list_head fsnotify_inode_groups; /* all groups which receive vfsmount fsnotify events */ extern struct list_head fsnotify_vfsmount_groups; -/* all bitwise OR of all event types (FS_*) for all fsnotify_inode_groups */ -extern __u32 fsnotify_inode_mask; -/* all bitwise OR of all event types (FS_*) for all fsnotify_vfsmount_groups */ -extern __u32 fsnotify_vfsmount_mask; /* destroy all events sitting in this groups notification queue */ extern void fsnotify_flush_notify(struct fsnotify_group *group); diff --git a/fs/notify/group.c b/fs/notify/group.c index 48d3a6d6e47a..8da532dd6026 100644 --- a/fs/notify/group.c +++ b/fs/notify/group.c @@ -34,54 +34,21 @@ static DEFINE_MUTEX(fsnotify_grp_mutex); LIST_HEAD(fsnotify_inode_groups); /* all groups registered to receive mount point filesystem notifications */ LIST_HEAD(fsnotify_vfsmount_groups); -/* bitwise OR of all events (FS_*) interesting to some group on this system */ -__u32 fsnotify_inode_mask; -/* bitwise OR of all events (FS_*) interesting to some group on this system */ -__u32 fsnotify_vfsmount_mask; - -/* - * When a new group registers or changes it's set of interesting events - * this function updates the fsnotify_mask to contain all interesting events - */ -void fsnotify_recalc_global_mask(void) -{ - struct fsnotify_group *group; - __u32 inode_mask = 0; - __u32 vfsmount_mask = 0; - - mutex_lock(&fsnotify_grp_mutex); - list_for_each_entry_rcu(group, &fsnotify_inode_groups, inode_group_list) - inode_mask |= group->mask; - list_for_each_entry_rcu(group, &fsnotify_vfsmount_groups, vfsmount_group_list) - vfsmount_mask |= group->mask; - - fsnotify_inode_mask = inode_mask; - fsnotify_vfsmount_mask = vfsmount_mask; - - mutex_unlock(&fsnotify_grp_mutex); -} /* * Update the group->mask by running all of the marks associated with this - * group and finding the bitwise | of all of the mark->mask. If we change - * the group->mask we need to update the global mask of events interesting - * to the system. + * group and finding the bitwise | of all of the mark->mask. */ void fsnotify_recalc_group_mask(struct fsnotify_group *group) { __u32 mask = 0; - __u32 old_mask = group->mask; struct fsnotify_mark *mark; spin_lock(&group->mark_lock); list_for_each_entry(mark, &group->marks_list, g_list) mask |= mark->mask; - spin_unlock(&group->mark_lock); - group->mask = mask; - - if (old_mask != mask) - fsnotify_recalc_global_mask(); + spin_unlock(&group->mark_lock); } void fsnotify_add_vfsmount_group(struct fsnotify_group *group) @@ -217,8 +184,6 @@ void fsnotify_put_group(struct fsnotify_group *group) mutex_unlock(&fsnotify_grp_mutex); - /* and now it is really dead. _Nothing_ could be seeing it */ - fsnotify_recalc_global_mask(); fsnotify_destroy_group(group); } diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index 225dc0c3a48c..07d3c8954721 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -365,8 +365,6 @@ static inline void __fsnotify_d_instantiate(struct dentry *dentry, struct inode /* called from fsnotify listeners, such as fanotify or dnotify */ -/* must call when a group changes its ->mask */ -extern void fsnotify_recalc_global_mask(void); /* get a reference to an existing or create a new group */ extern struct fsnotify_group *fsnotify_alloc_group(const struct fsnotify_ops *ops); /* run all marks associated with this group and update group->mask */ -- cgit v1.2.3 From 43709a288ed03aa0e2979ab63dd089b3889645c4 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 28 Jul 2010 10:18:39 -0400 Subject: fsnotify: remove group->mask group->mask is now useless. It was originally a shortcut for fsnotify to save on performance. These checks are now redundant, so we remove them. Signed-off-by: Eric Paris --- fs/notify/dnotify/dnotify.c | 4 ---- fs/notify/fanotify/fanotify_user.c | 23 +++++------------------ fs/notify/group.c | 16 ---------------- fs/notify/inotify/inotify_user.c | 9 --------- include/linux/fsnotify_backend.h | 11 ----------- kernel/audit_watch.c | 8 -------- 6 files changed, 5 insertions(+), 66 deletions(-) (limited to 'fs') diff --git a/fs/notify/dnotify/dnotify.c b/fs/notify/dnotify/dnotify.c index c3dc15879a52..e92b2c87ae94 100644 --- a/fs/notify/dnotify/dnotify.c +++ b/fs/notify/dnotify/dnotify.c @@ -199,8 +199,6 @@ void dnotify_flush(struct file *filp, fl_owner_t id) if (dn_mark->dn == NULL) fsnotify_destroy_mark(fsn_mark); - fsnotify_recalc_group_mask(dnotify_group); - mutex_unlock(&dnotify_mark_mutex); fsnotify_put_mark(fsn_mark); @@ -385,8 +383,6 @@ out: if (destroy) fsnotify_destroy_mark(fsn_mark); - fsnotify_recalc_group_mask(dnotify_group); - mutex_unlock(&dnotify_mark_mutex); fsnotify_put_mark(fsn_mark); out_err: diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 50cea74bf1c8..25a3b4dfcf61 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -496,8 +496,6 @@ static int fanotify_remove_vfsmount_mark(struct fsnotify_group *group, removed = fanotify_mark_remove_from_mask(fsn_mark, mask, flags); fsnotify_put_mark(fsn_mark); - if (removed & group->mask) - fsnotify_recalc_group_mask(group); if (removed & mnt->mnt_fsnotify_mask) fsnotify_recalc_vfsmount_mask(mnt); @@ -518,9 +516,6 @@ static int fanotify_remove_inode_mark(struct fsnotify_group *group, removed = fanotify_mark_remove_from_mask(fsn_mark, mask, flags); /* matches the fsnotify_find_inode_mark() */ fsnotify_put_mark(fsn_mark); - - if (removed & group->mask) - fsnotify_recalc_group_mask(group); if (removed & inode->i_fsnotify_mask) fsnotify_recalc_inode_mask(inode); @@ -572,12 +567,9 @@ static int fanotify_add_vfsmount_mark(struct fsnotify_group *group, } added = fanotify_mark_add_to_mask(fsn_mark, mask, flags); fsnotify_put_mark(fsn_mark); - if (added) { - if (added & ~group->mask) - fsnotify_recalc_group_mask(group); - if (added & ~mnt->mnt_fsnotify_mask) - fsnotify_recalc_vfsmount_mask(mnt); - } + if (added & ~mnt->mnt_fsnotify_mask) + fsnotify_recalc_vfsmount_mask(mnt); + return 0; } @@ -607,12 +599,8 @@ static int fanotify_add_inode_mark(struct fsnotify_group *group, } added = fanotify_mark_add_to_mask(fsn_mark, mask, flags); fsnotify_put_mark(fsn_mark); - if (added) { - if (added & ~group->mask) - fsnotify_recalc_group_mask(group); - if (added & ~inode->i_fsnotify_mask) - fsnotify_recalc_inode_mask(inode); - } + if (added & ~inode->i_fsnotify_mask) + fsnotify_recalc_inode_mask(inode); return 0; } @@ -734,7 +722,6 @@ SYSCALL_DEFINE(fanotify_mark)(int fanotify_fd, unsigned int flags, fsnotify_clear_vfsmount_marks_by_group(group); else fsnotify_clear_inode_marks_by_group(group); - fsnotify_recalc_group_mask(group); break; default: ret = -EINVAL; diff --git a/fs/notify/group.c b/fs/notify/group.c index 8da532dd6026..fc0d966b270f 100644 --- a/fs/notify/group.c +++ b/fs/notify/group.c @@ -35,22 +35,6 @@ LIST_HEAD(fsnotify_inode_groups); /* all groups registered to receive mount point filesystem notifications */ LIST_HEAD(fsnotify_vfsmount_groups); -/* - * Update the group->mask by running all of the marks associated with this - * group and finding the bitwise | of all of the mark->mask. - */ -void fsnotify_recalc_group_mask(struct fsnotify_group *group) -{ - __u32 mask = 0; - struct fsnotify_mark *mark; - - spin_lock(&group->mark_lock); - list_for_each_entry(mark, &group->marks_list, g_list) - mask |= mark->mask; - group->mask = mask; - spin_unlock(&group->mark_lock); -} - void fsnotify_add_vfsmount_group(struct fsnotify_group *group) { struct fsnotify_group *group_iter; diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index a4cd227c4c76..bf7f6d776c31 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -606,16 +606,11 @@ static int inotify_update_existing_watch(struct fsnotify_group *group, int dropped = (old_mask & ~new_mask); /* more bits in this fsn_mark than the inode's mask? */ int do_inode = (new_mask & ~inode->i_fsnotify_mask); - /* more bits in this fsn_mark than the group? */ - int do_group = (new_mask & ~group->mask); /* update the inode with this new fsn_mark */ if (dropped || do_inode) fsnotify_recalc_inode_mask(inode); - /* update the group mask with the new mask */ - if (dropped || do_group) - fsnotify_recalc_group_mask(group); } /* return the wd */ @@ -673,10 +668,6 @@ static int inotify_new_watch(struct fsnotify_group *group, /* return the watch descriptor for this new mark */ ret = tmp_i_mark->wd; - /* if this mark added a new event update the group mask */ - if (mask & ~group->mask) - fsnotify_recalc_group_mask(group); - out_err: /* match the ref from fsnotify_init_mark() */ fsnotify_put_mark(&tmp_i_mark->fsn_mark); diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index 07d3c8954721..c4e7aab87461 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -119,15 +119,6 @@ struct fsnotify_group { */ struct list_head vfsmount_group_list; - /* - * Defines all of the event types in which this group is interested. - * This mask is a bitwise OR of the FS_* events from above. Each time - * this mask changes for a group (if it changes) the correct functions - * must be called to update the global structures which indicate global - * interest in event types. - */ - __u32 mask; - /* * How the refcnt is used is up to each group. When the refcnt hits 0 * fsnotify will clean up all of the resources associated with this group. @@ -367,8 +358,6 @@ static inline void __fsnotify_d_instantiate(struct dentry *dentry, struct inode /* get a reference to an existing or create a new group */ extern struct fsnotify_group *fsnotify_alloc_group(const struct fsnotify_ops *ops); -/* run all marks associated with this group and update group->mask */ -extern void fsnotify_recalc_group_mask(struct fsnotify_group *group); /* drop reference on a group from fsnotify_alloc_group */ extern void fsnotify_put_group(struct fsnotify_group *group); diff --git a/kernel/audit_watch.c b/kernel/audit_watch.c index 097a61c65fe0..1b87e757845d 100644 --- a/kernel/audit_watch.c +++ b/kernel/audit_watch.c @@ -164,8 +164,6 @@ static struct audit_parent *audit_init_parent(struct nameidata *ndp) return ERR_PTR(ret); } - fsnotify_recalc_group_mask(audit_watch_group); - return parent; } @@ -352,9 +350,6 @@ static void audit_remove_parent_watches(struct audit_parent *parent) mutex_unlock(&audit_filter_mutex); fsnotify_destroy_mark(&parent->mark); - - fsnotify_recalc_group_mask(audit_watch_group); - } /* Get path information necessary for adding watches. */ @@ -505,9 +500,6 @@ void audit_remove_watch_rule(struct audit_krule *krule) audit_put_parent(parent); } } - - fsnotify_recalc_group_mask(audit_watch_group); - } static bool audit_watch_should_send_event(struct fsnotify_group *group, struct inode *inode, -- cgit v1.2.3 From 02436668d98385f5b5d9ffb695a37dadf98ed8a8 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 28 Jul 2010 10:18:39 -0400 Subject: fsnotify: remove global fsnotify groups lists The global fsnotify groups lists were invented as a way to increase the performance of fsnotify by shortcutting events which were not interesting. With the changes to walk the object lists rather than global groups lists these shortcuts are not useful. Signed-off-by: Eric Paris --- fs/notify/fsnotify.c | 5 -- fs/notify/fsnotify.h | 9 ---- fs/notify/group.c | 107 +-------------------------------------- fs/notify/mark.c | 9 ---- include/linux/fsnotify_backend.h | 15 ------ 5 files changed, 2 insertions(+), 143 deletions(-) (limited to 'fs') diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index 9ba29ee747cf..1dd1fde1da08 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -219,11 +219,6 @@ int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, /* global tests shouldn't care about events on child only the specific event */ __u32 test_mask = (mask & ~FS_EVENT_ON_CHILD); - /* if no fsnotify listeners, nothing to do */ - if (list_empty(&fsnotify_inode_groups) && - list_empty(&fsnotify_vfsmount_groups)) - return 0; - if (mask & FS_MODIFY) __fsnotify_flush_ignored_mask(to_tell, data, data_is); diff --git a/fs/notify/fsnotify.h b/fs/notify/fsnotify.h index b41dbf5a125c..85e7d2b431d9 100644 --- a/fs/notify/fsnotify.h +++ b/fs/notify/fsnotify.h @@ -6,11 +6,6 @@ #include #include -/* all groups which receive inode fsnotify events */ -extern struct list_head fsnotify_inode_groups; -/* all groups which receive vfsmount fsnotify events */ -extern struct list_head fsnotify_vfsmount_groups; - /* destroy all events sitting in this groups notification queue */ extern void fsnotify_flush_notify(struct fsnotify_group *group); @@ -28,10 +23,6 @@ extern int fsnotify_add_vfsmount_mark(struct fsnotify_mark *mark, struct fsnotify_group *group, struct vfsmount *mnt, int allow_dups); -/* add a group to the inode group list */ -extern void fsnotify_add_inode_group(struct fsnotify_group *group); -/* add a group to the vfsmount group list */ -extern void fsnotify_add_vfsmount_group(struct fsnotify_group *group); /* final kfree of a group */ extern void fsnotify_final_destroy_group(struct fsnotify_group *group); diff --git a/fs/notify/group.c b/fs/notify/group.c index fc0d966b270f..d309f38449cb 100644 --- a/fs/notify/group.c +++ b/fs/notify/group.c @@ -28,67 +28,6 @@ #include -/* protects writes to fsnotify_groups and fsnotify_mask */ -static DEFINE_MUTEX(fsnotify_grp_mutex); -/* all groups registered to receive inode filesystem notifications */ -LIST_HEAD(fsnotify_inode_groups); -/* all groups registered to receive mount point filesystem notifications */ -LIST_HEAD(fsnotify_vfsmount_groups); - -void fsnotify_add_vfsmount_group(struct fsnotify_group *group) -{ - struct fsnotify_group *group_iter; - - mutex_lock(&fsnotify_grp_mutex); - - if (!group->on_vfsmount_group_list) { - list_for_each_entry(group_iter, &fsnotify_vfsmount_groups, - vfsmount_group_list) { - /* insert in front of this one? */ - if (group < group_iter) { - /* list_add_tail() insert in front of group_iter */ - list_add_tail_rcu(&group->inode_group_list, - &group_iter->inode_group_list); - goto out; - } - } - - /* apparently we need to be the last entry */ - list_add_tail_rcu(&group->vfsmount_group_list, &fsnotify_vfsmount_groups); - } -out: - group->on_vfsmount_group_list = 1; - - mutex_unlock(&fsnotify_grp_mutex); -} - -void fsnotify_add_inode_group(struct fsnotify_group *group) -{ - struct fsnotify_group *group_iter; - - mutex_lock(&fsnotify_grp_mutex); - - /* add to global group list */ - if (!group->on_inode_group_list) { - list_for_each_entry(group_iter, &fsnotify_inode_groups, - inode_group_list) { - if (group < group_iter) { - /* list_add_tail() insert in front of group_iter */ - list_add_tail_rcu(&group->inode_group_list, - &group_iter->inode_group_list); - goto out; - } - } - - /* apparently we need to be the last entry */ - list_add_tail_rcu(&group->inode_group_list, &fsnotify_inode_groups); - } -out: - group->on_inode_group_list = 1; - - mutex_unlock(&fsnotify_grp_mutex); -} - /* * Final freeing of a group */ @@ -123,52 +62,13 @@ static void fsnotify_destroy_group(struct fsnotify_group *group) fsnotify_final_destroy_group(group); } -/* - * Remove this group from the global list of groups that will get events - * this can be done even if there are still references and things still using - * this group. This just stops the group from getting new events. - */ -static void __fsnotify_evict_group(struct fsnotify_group *group) -{ - BUG_ON(!mutex_is_locked(&fsnotify_grp_mutex)); - - if (group->on_inode_group_list) - list_del_rcu(&group->inode_group_list); - group->on_inode_group_list = 0; - if (group->on_vfsmount_group_list) - list_del_rcu(&group->vfsmount_group_list); - group->on_vfsmount_group_list = 0; -} - -/* - * Called when a group is no longer interested in getting events. This can be - * used if a group is misbehaving or if for some reason a group should no longer - * get any filesystem events. - */ -void fsnotify_evict_group(struct fsnotify_group *group) -{ - mutex_lock(&fsnotify_grp_mutex); - __fsnotify_evict_group(group); - mutex_unlock(&fsnotify_grp_mutex); -} - /* * Drop a reference to a group. Free it if it's through. */ void fsnotify_put_group(struct fsnotify_group *group) { - if (!atomic_dec_and_mutex_lock(&group->refcnt, &fsnotify_grp_mutex)) - return; - - /* - * OK, now we know that there's no other users *and* we hold mutex, - * so no new references will appear - */ - __fsnotify_evict_group(group); - - mutex_unlock(&fsnotify_grp_mutex); - - fsnotify_destroy_group(group); + if (atomic_dec_and_test(&group->refcnt)) + fsnotify_destroy_group(group); } /* @@ -195,9 +95,6 @@ struct fsnotify_group *fsnotify_alloc_group(const struct fsnotify_ops *ops) init_waitqueue_head(&group->notification_waitq); group->max_events = UINT_MAX; - INIT_LIST_HEAD(&group->inode_group_list); - INIT_LIST_HEAD(&group->vfsmount_group_list); - spin_lock_init(&group->mark_lock); INIT_LIST_HEAD(&group->marks_list); diff --git a/fs/notify/mark.c b/fs/notify/mark.c index 236f29b066ed..325185e514bb 100644 --- a/fs/notify/mark.c +++ b/fs/notify/mark.c @@ -222,15 +222,6 @@ int fsnotify_add_mark(struct fsnotify_mark *mark, BUG_ON(inode && mnt); BUG_ON(!inode && !mnt); - /* - * if this group isn't being testing for inode type events we need - * to start testing - */ - if (inode && unlikely(list_empty(&group->inode_group_list))) - fsnotify_add_inode_group(group); - else if (mnt && unlikely(list_empty(&group->vfsmount_group_list))) - fsnotify_add_vfsmount_group(group); - /* * LOCKING ORDER!!!! * mark->lock diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index c4e7aab87461..2e7cc8c2a151 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -108,17 +108,6 @@ struct fsnotify_ops { * everything will be cleaned up. */ struct fsnotify_group { - /* - * global list of all groups receiving events from fsnotify. - * anchored by fsnotify_inode_groups and protected by either fsnotify_grp_mutex - * or fsnotify_grp_srcu depending on write vs read. - */ - struct list_head inode_group_list; - /* - * same as above except anchored by fsnotify_vfsmount_groups - */ - struct list_head vfsmount_group_list; - /* * How the refcnt is used is up to each group. When the refcnt hits 0 * fsnotify will clean up all of the resources associated with this group. @@ -145,10 +134,6 @@ struct fsnotify_group { * a group */ struct list_head marks_list; /* all inode marks for this group */ - /* prevents double list_del of group_list. protected by global fsnotify_grp_mutex */ - bool on_inode_group_list; - bool on_vfsmount_group_list; - /* groups can define private fields here or use the void *private */ union { void *private; -- cgit v1.2.3 From 84a5b68e8da1490906c11129756490a556ae2c19 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 28 Jul 2010 10:18:39 -0400 Subject: fsnotify: rework ignored mark flushing currently ignored_mark clearing is done in a seperate list traversal before the actual list traversal to send events. There is no need for this. Do them at the same time. Signed-off-by: Eric Paris --- fs/notify/fsnotify.c | 60 ++++++++++------------------------------------------ 1 file changed, 11 insertions(+), 49 deletions(-) (limited to 'fs') diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index 1dd1fde1da08..0bb4aeb8e00f 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -140,36 +140,6 @@ void __fsnotify_parent(struct file *file, struct dentry *dentry, __u32 mask) } EXPORT_SYMBOL_GPL(__fsnotify_parent); -void __fsnotify_flush_ignored_mask(struct inode *inode, void *data, int data_is) -{ - struct fsnotify_mark *mark; - struct hlist_node *node; - int idx; - - idx = srcu_read_lock(&fsnotify_mark_srcu); - - if (!hlist_empty(&inode->i_fsnotify_marks)) { - hlist_for_each_entry_rcu(mark, node, &inode->i_fsnotify_marks, i.i_list) { - if (!(mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY)) - mark->ignored_mask = 0; - } - } - - if (data_is == FSNOTIFY_EVENT_FILE) { - struct vfsmount *mnt; - - mnt = ((struct file *)data)->f_path.mnt; - if (mnt && !hlist_empty(&mnt->mnt_fsnotify_marks)) { - hlist_for_each_entry_rcu(mark, node, &mnt->mnt_fsnotify_marks, m.m_list) { - if (!(mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY)) - mark->ignored_mask = 0; - } - } - } - - srcu_read_unlock(&fsnotify_mark_srcu, idx); -} - static int send_to_group(struct fsnotify_group *group, struct inode *to_tell, struct vfsmount *mnt, struct fsnotify_mark *mark, __u32 mask, void *data, int data_is, u32 cookie, @@ -193,14 +163,6 @@ static int send_to_group(struct fsnotify_group *group, struct inode *to_tell, return group->ops->handle_event(group, mark, *event); } -static bool needed_by_vfsmount(__u32 test_mask, struct vfsmount *mnt) -{ - if (!mnt) - return false; - - return (test_mask & mnt->mnt_fsnotify_mask); -} - /* * This is the main call to fsnotify. The VFS calls into hook specific functions * in linux/fsnotify.h. Those functions then in turn call here. Here will call @@ -219,26 +181,21 @@ int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, /* global tests shouldn't care about events on child only the specific event */ __u32 test_mask = (mask & ~FS_EVENT_ON_CHILD); - if (mask & FS_MODIFY) - __fsnotify_flush_ignored_mask(to_tell, data, data_is); - if (data_is == FSNOTIFY_EVENT_FILE) mnt = ((struct file *)data)->f_path.mnt; - /* if this inode's directed listeners don't care and nothing on the vfsmount - * listeners list cares, nothing to do */ - if (!(test_mask & to_tell->i_fsnotify_mask) && - !needed_by_vfsmount(test_mask, mnt)) - return 0; - idx = srcu_read_lock(&fsnotify_mark_srcu); - if (test_mask & to_tell->i_fsnotify_mask) { + if ((test_mask & to_tell->i_fsnotify_mask) || (mask & FS_MODIFY)) { hlist_for_each_entry_rcu(mark, node, &to_tell->i_fsnotify_marks, i.i_list) { pr_debug("%s: inode_loop: mark=%p mark->mask=%x mark->ignored_mask=%x\n", __func__, mark, mark->mask, mark->ignored_mask); + if ((mask & FS_MODIFY) && + !(mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY)) + mark->ignored_mask = 0; + if (test_mask & mark->mask & ~mark->ignored_mask) { group = mark->group; if (!group) @@ -252,12 +209,17 @@ int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, } } - if (mnt && (test_mask & mnt->mnt_fsnotify_mask)) { + if (mnt && ((test_mask & mnt->mnt_fsnotify_mask) || + (mask & FS_MODIFY))) { hlist_for_each_entry_rcu(mark, node, &mnt->mnt_fsnotify_marks, m.m_list) { pr_debug("%s: mnt_loop: mark=%p mark->mask=%x mark->ignored_mask=%x\n", __func__, mark, mark->mask, mark->ignored_mask); + if ((mask & FS_MODIFY) && + !(mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY)) + mark->ignored_mask = 0; + if (test_mask & mark->mask & ~mark->ignored_mask) { group = mark->group; if (!group) -- cgit v1.2.3 From 613a807fe7c793ceb7d6f059773527a5a6c84a96 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 28 Jul 2010 10:18:39 -0400 Subject: fsnotify: walk the inode and vfsmount lists simultaneously We currently walk the list of marks on an inode followed by the list of marks on the vfsmount. These are in order (by the memory address of the group) so lets walk them both together. Eventually we can pass both the inode mark and the vfsmount mark to helpers simultaneously. Signed-off-by: Eric Paris --- fs/notify/fsnotify.c | 134 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 84 insertions(+), 50 deletions(-) (limited to 'fs') diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index 0bb4aeb8e00f..cdaa51cb698c 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -140,19 +140,31 @@ void __fsnotify_parent(struct file *file, struct dentry *dentry, __u32 mask) } EXPORT_SYMBOL_GPL(__fsnotify_parent); -static int send_to_group(struct fsnotify_group *group, struct inode *to_tell, - struct vfsmount *mnt, struct fsnotify_mark *mark, - __u32 mask, void *data, int data_is, u32 cookie, +static int send_to_group(struct inode *to_tell, struct vfsmount *mnt, + struct fsnotify_mark *mark, + __u32 mask, void *data, + int data_is, u32 cookie, const unsigned char *file_name, struct fsnotify_event **event) { + struct fsnotify_group *group = mark->group; + __u32 test_mask = (mask & ~FS_EVENT_ON_CHILD); + pr_debug("%s: group=%p to_tell=%p mnt=%p mark=%p mask=%x data=%p" " data_is=%d cookie=%d event=%p\n", __func__, group, to_tell, mnt, mark, mask, data, data_is, cookie, *event); + if ((mask & FS_MODIFY) && + !(mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY)) + mark->ignored_mask = 0; + + if (!(test_mask & mark->mask & ~mark->ignored_mask)) + return 0; + if (group->ops->should_send_event(group, to_tell, mnt, mark, mask, data, data_is) == false) return 0; + if (!*event) { *event = fsnotify_create_event(to_tell, mask, data, data_is, file_name, @@ -172,67 +184,89 @@ static int send_to_group(struct fsnotify_group *group, struct inode *to_tell, int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, const unsigned char *file_name, u32 cookie) { - struct fsnotify_mark *mark; - struct fsnotify_group *group; + struct hlist_node *inode_node, *vfsmount_node; + struct fsnotify_mark *inode_mark = NULL, *vfsmount_mark = NULL; + struct fsnotify_group *inode_group, *vfsmount_group; struct fsnotify_event *event = NULL; - struct hlist_node *node; - struct vfsmount *mnt = NULL; + struct vfsmount *mnt; int idx, ret = 0; + bool used_inode = false, used_vfsmount = false; /* global tests shouldn't care about events on child only the specific event */ __u32 test_mask = (mask & ~FS_EVENT_ON_CHILD); if (data_is == FSNOTIFY_EVENT_FILE) mnt = ((struct file *)data)->f_path.mnt; + else + mnt = NULL; + + /* + * if this is a modify event we may need to clear the ignored masks + * otherwise return if neither the inode nor the vfsmount care about + * this type of event. + */ + if (!(mask & FS_MODIFY) && + !(test_mask & to_tell->i_fsnotify_mask) && + !(mnt && test_mask & mnt->mnt_fsnotify_mask)) + return 0; idx = srcu_read_lock(&fsnotify_mark_srcu); - if ((test_mask & to_tell->i_fsnotify_mask) || (mask & FS_MODIFY)) { - hlist_for_each_entry_rcu(mark, node, &to_tell->i_fsnotify_marks, i.i_list) { - - pr_debug("%s: inode_loop: mark=%p mark->mask=%x mark->ignored_mask=%x\n", - __func__, mark, mark->mask, mark->ignored_mask); - - if ((mask & FS_MODIFY) && - !(mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY)) - mark->ignored_mask = 0; - - if (test_mask & mark->mask & ~mark->ignored_mask) { - group = mark->group; - if (!group) - continue; - ret = send_to_group(group, to_tell, NULL, mark, mask, - data, data_is, cookie, file_name, - &event); - if (ret) - goto out; - } - } + if ((mask & FS_MODIFY) || + (test_mask & to_tell->i_fsnotify_mask)) + inode_node = to_tell->i_fsnotify_marks.first; + else + inode_node = NULL; + + if (mnt) { + if ((mask & FS_MODIFY) || + (test_mask & mnt->mnt_fsnotify_mask)) + vfsmount_node = mnt->mnt_fsnotify_marks.first; + else + vfsmount_node = NULL; + } else { + mnt = NULL; + vfsmount_node = NULL; } - if (mnt && ((test_mask & mnt->mnt_fsnotify_mask) || - (mask & FS_MODIFY))) { - hlist_for_each_entry_rcu(mark, node, &mnt->mnt_fsnotify_marks, m.m_list) { - - pr_debug("%s: mnt_loop: mark=%p mark->mask=%x mark->ignored_mask=%x\n", - __func__, mark, mark->mask, mark->ignored_mask); - - if ((mask & FS_MODIFY) && - !(mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY)) - mark->ignored_mask = 0; - - if (test_mask & mark->mask & ~mark->ignored_mask) { - group = mark->group; - if (!group) - continue; - ret = send_to_group(group, to_tell, mnt, mark, mask, - data, data_is, cookie, file_name, - &event); - if (ret) - goto out; - } + while (inode_node || vfsmount_node) { + if (inode_node) { + inode_mark = hlist_entry(srcu_dereference(inode_node, &fsnotify_mark_srcu), + struct fsnotify_mark, i.i_list); + inode_group = inode_mark->group; + } else + inode_group = (void *)-1; + + if (vfsmount_node) { + vfsmount_mark = hlist_entry(srcu_dereference(vfsmount_node, &fsnotify_mark_srcu), + struct fsnotify_mark, m.m_list); + vfsmount_group = vfsmount_mark->group; + } else + vfsmount_group = (void *)-1; + + if (inode_group < vfsmount_group) { + /* handle inode */ + send_to_group(to_tell, NULL, inode_mark, mask, data, + data_is, cookie, file_name, &event); + used_inode = true; + } else if (vfsmount_group < inode_group) { + send_to_group(to_tell, mnt, vfsmount_mark, mask, data, + data_is, cookie, file_name, &event); + used_vfsmount = true; + } else { + send_to_group(to_tell, mnt, vfsmount_mark, mask, data, + data_is, cookie, file_name, &event); + used_vfsmount = true; + send_to_group(to_tell, NULL, inode_mark, mask, data, + data_is, cookie, file_name, &event); + used_inode = true; } + + if (used_inode) + inode_node = inode_node->next; + if (used_vfsmount) + vfsmount_node = vfsmount_node->next; } -out: + srcu_read_unlock(&fsnotify_mark_srcu, idx); /* * fsnotify_create_event() took a reference so the event can't be cleaned -- cgit v1.2.3 From ce8f76fb7320297ccbe7c950fd9a2d727dd6a5a0 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 28 Jul 2010 10:18:39 -0400 Subject: fsnotify: pass both the vfsmount mark and inode mark should_send_event() and handle_event() will both need to look up the inode event if they get a vfsmount event. Lets just pass both at the same time since we have them both after walking the lists in lockstep. Signed-off-by: Eric Paris --- fs/notify/dnotify/dnotify.c | 18 ++++++---- fs/notify/fanotify/fanotify.c | 15 +++++--- fs/notify/fsnotify.c | 70 ++++++++++++++++++++++++------------ fs/notify/inotify/inotify_fsnotify.c | 12 ++++--- include/linux/fsnotify_backend.h | 7 ++-- kernel/audit_tree.c | 6 ++-- kernel/audit_watch.c | 8 +++-- 7 files changed, 91 insertions(+), 45 deletions(-) (limited to 'fs') diff --git a/fs/notify/dnotify/dnotify.c b/fs/notify/dnotify/dnotify.c index e92b2c87ae94..bda588b831ad 100644 --- a/fs/notify/dnotify/dnotify.c +++ b/fs/notify/dnotify/dnotify.c @@ -83,7 +83,8 @@ static void dnotify_recalc_inode_mask(struct fsnotify_mark *fsn_mark) * events. */ static int dnotify_handle_event(struct fsnotify_group *group, - struct fsnotify_mark *mark, + struct fsnotify_mark *inode_mark, + struct fsnotify_mark *vfsmount_mark, struct fsnotify_event *event) { struct dnotify_mark *dn_mark; @@ -93,11 +94,13 @@ static int dnotify_handle_event(struct fsnotify_group *group, struct fown_struct *fown; __u32 test_mask = event->mask & ~FS_EVENT_ON_CHILD; + BUG_ON(vfsmount_mark); + to_tell = event->to_tell; - dn_mark = container_of(mark, struct dnotify_mark, fsn_mark); + dn_mark = container_of(inode_mark, struct dnotify_mark, fsn_mark); - spin_lock(&mark->lock); + spin_lock(&inode_mark->lock); prev = &dn_mark->dn; while ((dn = *prev) != NULL) { if ((dn->dn_mask & test_mask) == 0) { @@ -111,11 +114,11 @@ static int dnotify_handle_event(struct fsnotify_group *group, else { *prev = dn->dn_next; kmem_cache_free(dnotify_struct_cache, dn); - dnotify_recalc_inode_mask(mark); + dnotify_recalc_inode_mask(inode_mark); } } - spin_unlock(&mark->lock); + spin_unlock(&inode_mark->lock); return 0; } @@ -126,8 +129,9 @@ static int dnotify_handle_event(struct fsnotify_group *group, */ static bool dnotify_should_send_event(struct fsnotify_group *group, struct inode *inode, struct vfsmount *mnt, - struct fsnotify_mark *mark, __u32 mask, - void *data, int data_type) + struct fsnotify_mark *inode_mark, + struct fsnotify_mark *vfsmount_mark, + __u32 mask, void *data, int data_type) { /* not a dir, dnotify doesn't care */ if (!S_ISDIR(inode->i_mode)) diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index fbd7f35c6134..ef4fa4a45c94 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -115,7 +115,8 @@ static int fanotify_get_response_from_access(struct fsnotify_group *group, #endif static int fanotify_handle_event(struct fsnotify_group *group, - struct fsnotify_mark *mark, + struct fsnotify_mark *inode_mark, + struct fsnotify_mark *fanotify_mark, struct fsnotify_event *event) { int ret = 0; @@ -196,8 +197,11 @@ static bool should_send_inode_event(struct fsnotify_group *group, return true; } -static bool fanotify_should_send_event(struct fsnotify_group *group, struct inode *to_tell, - struct vfsmount *mnt, struct fsnotify_mark *mark, +static bool fanotify_should_send_event(struct fsnotify_group *group, + struct inode *to_tell, + struct vfsmount *mnt, + struct fsnotify_mark *inode_mark, + struct fsnotify_mark *vfsmount_mark, __u32 mask, void *data, int data_type) { pr_debug("%s: group=%p to_tell=%p mnt=%p mask=%x data=%p data_type=%d\n", @@ -213,9 +217,10 @@ static bool fanotify_should_send_event(struct fsnotify_group *group, struct inod return false; if (mnt) - return should_send_vfsmount_event(group, mnt, to_tell, mark, mask); + return should_send_vfsmount_event(group, mnt, to_tell, + vfsmount_mark, mask); else - return should_send_inode_event(group, to_tell, mark, mask); + return should_send_inode_event(group, to_tell, inode_mark, mask); } const struct fsnotify_ops fanotify_fsnotify_ops = { diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index cdaa51cb698c..090b64c3b4f9 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -141,28 +141,51 @@ void __fsnotify_parent(struct file *file, struct dentry *dentry, __u32 mask) EXPORT_SYMBOL_GPL(__fsnotify_parent); static int send_to_group(struct inode *to_tell, struct vfsmount *mnt, - struct fsnotify_mark *mark, - __u32 mask, void *data, + struct fsnotify_mark *inode_mark, + struct fsnotify_mark *vfsmount_mark, + __u32 mask, void *data, int data_is, u32 cookie, const unsigned char *file_name, struct fsnotify_event **event) { - struct fsnotify_group *group = mark->group; - __u32 test_mask = (mask & ~FS_EVENT_ON_CHILD); + struct fsnotify_group *group = inode_mark->group; + __u32 inode_test_mask = (mask & ~FS_EVENT_ON_CHILD); + __u32 vfsmount_test_mask = (mask & ~FS_EVENT_ON_CHILD); pr_debug("%s: group=%p to_tell=%p mnt=%p mark=%p mask=%x data=%p" " data_is=%d cookie=%d event=%p\n", __func__, group, to_tell, - mnt, mark, mask, data, data_is, cookie, *event); + mnt, inode_mark, mask, data, data_is, cookie, *event); + + /* clear ignored on inode modification */ + if (mask & FS_MODIFY) { + if (inode_mark && + !(inode_mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY)) + inode_mark->ignored_mask = 0; + if (vfsmount_mark && + !(vfsmount_mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY)) + vfsmount_mark->ignored_mask = 0; + } - if ((mask & FS_MODIFY) && - !(mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY)) - mark->ignored_mask = 0; + /* does the inode mark tell us to do something? */ + if (inode_mark) { + inode_test_mask &= inode_mark->mask; + inode_test_mask &= ~inode_mark->ignored_mask; + } - if (!(test_mask & mark->mask & ~mark->ignored_mask)) + /* does the vfsmount_mark tell us to do something? */ + if (vfsmount_mark) { + vfsmount_test_mask &= vfsmount_mark->mask; + vfsmount_test_mask &= ~vfsmount_mark->ignored_mask; + if (inode_mark) + vfsmount_test_mask &= ~inode_mark->ignored_mask; + } + + if (!inode_test_mask && !vfsmount_test_mask) return 0; - if (group->ops->should_send_event(group, to_tell, mnt, mark, mask, - data, data_is) == false) + if (group->ops->should_send_event(group, to_tell, mnt, inode_mark, + vfsmount_mark, mask, data, + data_is) == false) return 0; if (!*event) { @@ -172,7 +195,7 @@ static int send_to_group(struct inode *to_tell, struct vfsmount *mnt, if (!*event) return -ENOMEM; } - return group->ops->handle_event(group, mark, *event); + return group->ops->handle_event(group, inode_mark, vfsmount_mark, *event); } /* @@ -213,14 +236,16 @@ int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, if ((mask & FS_MODIFY) || (test_mask & to_tell->i_fsnotify_mask)) - inode_node = to_tell->i_fsnotify_marks.first; + inode_node = srcu_dereference(to_tell->i_fsnotify_marks.first, + &fsnotify_mark_srcu); else inode_node = NULL; if (mnt) { if ((mask & FS_MODIFY) || (test_mask & mnt->mnt_fsnotify_mask)) - vfsmount_node = mnt->mnt_fsnotify_marks.first; + vfsmount_node = srcu_dereference(mnt->mnt_fsnotify_marks.first, + &fsnotify_mark_srcu); else vfsmount_node = NULL; } else { @@ -245,26 +270,27 @@ int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, if (inode_group < vfsmount_group) { /* handle inode */ - send_to_group(to_tell, NULL, inode_mark, mask, data, + send_to_group(to_tell, NULL, inode_mark, NULL, mask, data, data_is, cookie, file_name, &event); used_inode = true; } else if (vfsmount_group < inode_group) { - send_to_group(to_tell, mnt, vfsmount_mark, mask, data, + send_to_group(to_tell, mnt, NULL, vfsmount_mark, mask, data, data_is, cookie, file_name, &event); used_vfsmount = true; } else { - send_to_group(to_tell, mnt, vfsmount_mark, mask, data, - data_is, cookie, file_name, &event); + send_to_group(to_tell, mnt, inode_mark, vfsmount_mark, + mask, data, data_is, cookie, file_name, + &event); used_vfsmount = true; - send_to_group(to_tell, NULL, inode_mark, mask, data, - data_is, cookie, file_name, &event); used_inode = true; } if (used_inode) - inode_node = inode_node->next; + inode_node = srcu_dereference(inode_node->next, + &fsnotify_mark_srcu); if (used_vfsmount) - vfsmount_node = vfsmount_node->next; + vfsmount_node = srcu_dereference(vfsmount_node->next, + &fsnotify_mark_srcu); } srcu_read_unlock(&fsnotify_mark_srcu, idx); diff --git a/fs/notify/inotify/inotify_fsnotify.c b/fs/notify/inotify/inotify_fsnotify.c index 7cf518b25daa..e53f49731b6e 100644 --- a/fs/notify/inotify/inotify_fsnotify.c +++ b/fs/notify/inotify/inotify_fsnotify.c @@ -90,7 +90,8 @@ static struct fsnotify_event *inotify_merge(struct list_head *list, } static int inotify_handle_event(struct fsnotify_group *group, - struct fsnotify_mark *mark, + struct fsnotify_mark *inode_mark, + struct fsnotify_mark *vfsmount_mark, struct fsnotify_event *event) { struct inotify_inode_mark *i_mark; @@ -100,12 +101,14 @@ static int inotify_handle_event(struct fsnotify_group *group, struct fsnotify_event *added_event; int wd, ret = 0; + BUG_ON(vfsmount_mark); + pr_debug("%s: group=%p event=%p to_tell=%p mask=%x\n", __func__, group, event, event->to_tell, event->mask); to_tell = event->to_tell; - i_mark = container_of(mark, struct inotify_inode_mark, + i_mark = container_of(inode_mark, struct inotify_inode_mark, fsn_mark); wd = i_mark->wd; @@ -127,8 +130,8 @@ static int inotify_handle_event(struct fsnotify_group *group, ret = PTR_ERR(added_event); } - if (mark->mask & IN_ONESHOT) - fsnotify_destroy_mark(mark); + if (inode_mark->mask & IN_ONESHOT) + fsnotify_destroy_mark(inode_mark); return ret; } @@ -140,6 +143,7 @@ static void inotify_freeing_mark(struct fsnotify_mark *fsn_mark, struct fsnotify static bool inotify_should_send_event(struct fsnotify_group *group, struct inode *inode, struct vfsmount *mnt, struct fsnotify_mark *mark, + struct fsnotify_mark *vfsmount_mark, __u32 mask, void *data, int data_type) { if ((mark->mask & FS_EXCL_UNLINK) && diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index 2e7cc8c2a151..d38f922977f9 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -92,9 +92,12 @@ struct fsnotify_event_private_data; */ struct fsnotify_ops { bool (*should_send_event)(struct fsnotify_group *group, struct inode *inode, - struct vfsmount *mnt, struct fsnotify_mark *mark, + struct vfsmount *mnt, struct fsnotify_mark *inode_mark, + struct fsnotify_mark *vfsmount_mark, __u32 mask, void *data, int data_type); - int (*handle_event)(struct fsnotify_group *group, struct fsnotify_mark *mark, + int (*handle_event)(struct fsnotify_group *group, + struct fsnotify_mark *inode_mark, + struct fsnotify_mark *vfsmount_mark, struct fsnotify_event *event); void (*free_group_priv)(struct fsnotify_group *group); void (*freeing_mark)(struct fsnotify_mark *mark, struct fsnotify_group *group); diff --git a/kernel/audit_tree.c b/kernel/audit_tree.c index 2abb99f3459d..781ab7f4e35c 100644 --- a/kernel/audit_tree.c +++ b/kernel/audit_tree.c @@ -904,7 +904,8 @@ static void evict_chunk(struct audit_chunk *chunk) } static int audit_tree_handle_event(struct fsnotify_group *group, - struct fsnotify_mark *mark, + struct fsnotify_mark *inode_mark, + struct fsnotify_mark *vfsmonut_mark, struct fsnotify_event *event) { BUG(); @@ -920,7 +921,8 @@ static void audit_tree_freeing_mark(struct fsnotify_mark *entry, struct fsnotify } static bool audit_tree_send_event(struct fsnotify_group *group, struct inode *inode, - struct vfsmount *mnt, struct fsnotify_mark *mark, + struct vfsmount *mnt, struct fsnotify_mark *inode_mark, + struct fsnotify_mark *vfsmount_mark, __u32 mask, void *data, int data_type) { return false; diff --git a/kernel/audit_watch.c b/kernel/audit_watch.c index 1b87e757845d..a273cf340527 100644 --- a/kernel/audit_watch.c +++ b/kernel/audit_watch.c @@ -503,7 +503,8 @@ void audit_remove_watch_rule(struct audit_krule *krule) } static bool audit_watch_should_send_event(struct fsnotify_group *group, struct inode *inode, - struct vfsmount *mnt, struct fsnotify_mark *mark, + struct vfsmount *mnt, struct fsnotify_mark *inode_mark, + struct fsnotify_mark *vfsmount_mark, __u32 mask, void *data, int data_type) { return true; @@ -511,7 +512,8 @@ static bool audit_watch_should_send_event(struct fsnotify_group *group, struct i /* Update watch data in audit rules based on fsnotify events. */ static int audit_watch_handle_event(struct fsnotify_group *group, - struct fsnotify_mark *mark, + struct fsnotify_mark *inode_mark, + struct fsnotify_mark *vfsmount_mark, struct fsnotify_event *event) { struct inode *inode; @@ -519,7 +521,7 @@ static int audit_watch_handle_event(struct fsnotify_group *group, const char *dname = event->file_name; struct audit_parent *parent; - parent = container_of(mark, struct audit_parent, mark); + parent = container_of(inode_mark, struct audit_parent, mark); BUG_ON(group != audit_watch_group); -- cgit v1.2.3 From 1968f5eed54ce47bde488fd9a450912e4a2d7138 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 28 Jul 2010 10:18:39 -0400 Subject: fanotify: use both marks when possible fanotify currently, when given a vfsmount_mark will look up (if it exists) the corresponding inode mark. This patch drops that lookup and uses the mark provided. Signed-off-by: Eric Paris --- fs/notify/dnotify/dnotify.c | 2 +- fs/notify/fanotify/fanotify.c | 88 ++++++++++++++---------------------- fs/notify/fsnotify.c | 2 +- fs/notify/inotify/inotify_fsnotify.c | 4 +- include/linux/fsnotify_backend.h | 2 +- kernel/audit_tree.c | 2 +- kernel/audit_watch.c | 2 +- 7 files changed, 41 insertions(+), 61 deletions(-) (limited to 'fs') diff --git a/fs/notify/dnotify/dnotify.c b/fs/notify/dnotify/dnotify.c index bda588b831ad..3344bdd5506e 100644 --- a/fs/notify/dnotify/dnotify.c +++ b/fs/notify/dnotify/dnotify.c @@ -128,7 +128,7 @@ static int dnotify_handle_event(struct fsnotify_group *group, * userspace notification for that pair. */ static bool dnotify_should_send_event(struct fsnotify_group *group, - struct inode *inode, struct vfsmount *mnt, + struct inode *inode, struct fsnotify_mark *inode_mark, struct fsnotify_mark *vfsmount_mark, __u32 mask, void *data, int data_type) diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index ef4fa4a45c94..eb8f73c9c131 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -153,59 +153,20 @@ static int fanotify_handle_event(struct fsnotify_group *group, return ret; } -static bool should_send_vfsmount_event(struct fsnotify_group *group, - struct vfsmount *mnt, - struct inode *inode, - struct fsnotify_mark *mnt_mark, - __u32 mask) -{ - struct fsnotify_mark *inode_mark; - - pr_debug("%s: group=%p vfsmount=%p mark=%p mask=%x\n", - __func__, group, mnt, mnt_mark, mask); - - mask &= mnt_mark->mask; - mask &= ~mnt_mark->ignored_mask; - - if (mask) { - inode_mark = fsnotify_find_inode_mark(group, inode); - if (inode_mark) { - mask &= ~inode_mark->ignored_mask; - fsnotify_put_mark(inode_mark); - } - } - - return mask; -} - -static bool should_send_inode_event(struct fsnotify_group *group, - struct inode *inode, - struct fsnotify_mark *mark, - __u32 mask) -{ - pr_debug("%s: group=%p inode=%p mark=%p mask=%x\n", - __func__, group, inode, mark, mask); - - /* - * if the event is for a child and this inode doesn't care about - * events on the child, don't send it! - */ - if ((mask & FS_EVENT_ON_CHILD) && - !(mark->mask & FS_EVENT_ON_CHILD)) - return false; - else - return true; -} - static bool fanotify_should_send_event(struct fsnotify_group *group, struct inode *to_tell, - struct vfsmount *mnt, struct fsnotify_mark *inode_mark, - struct fsnotify_mark *vfsmount_mark, - __u32 mask, void *data, int data_type) + struct fsnotify_mark *vfsmnt_mark, + __u32 event_mask, void *data, int data_type) { - pr_debug("%s: group=%p to_tell=%p mnt=%p mask=%x data=%p data_type=%d\n", - __func__, group, to_tell, mnt, mask, data, data_type); + __u32 marks_mask, marks_ignored_mask; + + pr_debug("%s: group=%p to_tell=%p inode_mark=%p vfsmnt_mark=%p " + "mask=%x data=%p data_type=%d\n", __func__, group, to_tell, + inode_mark, vfsmnt_mark, event_mask, data, data_type); + + pr_debug("%s: group=%p vfsmount_mark=%p inode_mark=%p mask=%x\n", + __func__, group, vfsmnt_mark, inode_mark, event_mask); /* sorry, fanotify only gives a damn about files and dirs */ if (!S_ISREG(to_tell->i_mode) && @@ -216,11 +177,30 @@ static bool fanotify_should_send_event(struct fsnotify_group *group, if (data_type != FSNOTIFY_EVENT_FILE) return false; - if (mnt) - return should_send_vfsmount_event(group, mnt, to_tell, - vfsmount_mark, mask); - else - return should_send_inode_event(group, to_tell, inode_mark, mask); + if (inode_mark && vfsmnt_mark) { + marks_mask = (vfsmnt_mark->mask | inode_mark->mask); + marks_ignored_mask = (vfsmnt_mark->ignored_mask | inode_mark->ignored_mask); + } else if (inode_mark) { + /* + * if the event is for a child and this inode doesn't care about + * events on the child, don't send it! + */ + if ((event_mask & FS_EVENT_ON_CHILD) && + !(inode_mark->mask & FS_EVENT_ON_CHILD)) + return false; + marks_mask = inode_mark->mask; + marks_ignored_mask = inode_mark->ignored_mask; + } else if (vfsmnt_mark) { + marks_mask = vfsmnt_mark->mask; + marks_ignored_mask = vfsmnt_mark->ignored_mask; + } else { + BUG(); + } + + if (event_mask & marks_mask & ~marks_ignored_mask) + return true; + + return false; } const struct fsnotify_ops fanotify_fsnotify_ops = { diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index 090b64c3b4f9..4d2a82c1ceb1 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -183,7 +183,7 @@ static int send_to_group(struct inode *to_tell, struct vfsmount *mnt, if (!inode_test_mask && !vfsmount_test_mask) return 0; - if (group->ops->should_send_event(group, to_tell, mnt, inode_mark, + if (group->ops->should_send_event(group, to_tell, inode_mark, vfsmount_mark, mask, data, data_is) == false) return 0; diff --git a/fs/notify/inotify/inotify_fsnotify.c b/fs/notify/inotify/inotify_fsnotify.c index e53f49731b6e..5e73eeb2c697 100644 --- a/fs/notify/inotify/inotify_fsnotify.c +++ b/fs/notify/inotify/inotify_fsnotify.c @@ -142,11 +142,11 @@ static void inotify_freeing_mark(struct fsnotify_mark *fsn_mark, struct fsnotify } static bool inotify_should_send_event(struct fsnotify_group *group, struct inode *inode, - struct vfsmount *mnt, struct fsnotify_mark *mark, + struct fsnotify_mark *inode_mark, struct fsnotify_mark *vfsmount_mark, __u32 mask, void *data, int data_type) { - if ((mark->mask & FS_EXCL_UNLINK) && + if ((inode_mark->mask & FS_EXCL_UNLINK) && (data_type == FSNOTIFY_EVENT_FILE)) { struct file *file = data; diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index d38f922977f9..9bbfd7204b04 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -92,7 +92,7 @@ struct fsnotify_event_private_data; */ struct fsnotify_ops { bool (*should_send_event)(struct fsnotify_group *group, struct inode *inode, - struct vfsmount *mnt, struct fsnotify_mark *inode_mark, + struct fsnotify_mark *inode_mark, struct fsnotify_mark *vfsmount_mark, __u32 mask, void *data, int data_type); int (*handle_event)(struct fsnotify_group *group, diff --git a/kernel/audit_tree.c b/kernel/audit_tree.c index 781ab7f4e35c..7f18d3a4527e 100644 --- a/kernel/audit_tree.c +++ b/kernel/audit_tree.c @@ -921,7 +921,7 @@ static void audit_tree_freeing_mark(struct fsnotify_mark *entry, struct fsnotify } static bool audit_tree_send_event(struct fsnotify_group *group, struct inode *inode, - struct vfsmount *mnt, struct fsnotify_mark *inode_mark, + struct fsnotify_mark *inode_mark, struct fsnotify_mark *vfsmount_mark, __u32 mask, void *data, int data_type) { diff --git a/kernel/audit_watch.c b/kernel/audit_watch.c index a273cf340527..6bf2306be7d6 100644 --- a/kernel/audit_watch.c +++ b/kernel/audit_watch.c @@ -503,7 +503,7 @@ void audit_remove_watch_rule(struct audit_krule *krule) } static bool audit_watch_should_send_event(struct fsnotify_group *group, struct inode *inode, - struct vfsmount *mnt, struct fsnotify_mark *inode_mark, + struct fsnotify_mark *inode_mark, struct fsnotify_mark *vfsmount_mark, __u32 mask, void *data, int data_type) { -- cgit v1.2.3 From d2a97a4e99ff0ffdccd1fc46f22fb34270ef1e56 Mon Sep 17 00:00:00 2001 From: Steven Whitehouse Date: Wed, 28 Jul 2010 17:56:23 +0100 Subject: GFS2: Use kmalloc when possible for ->readdir() If we don't need a huge amount of memory in ->readdir() then we can use kmalloc rather than vmalloc to allocate it. This should cut down on the greater overheads associated with vmalloc for smaller directories. We may be able to eliminate vmalloc entirely at some stage, but this is easy to do right away. Also using GFP_NOFS to avoid any issues wrt to deleting inodes while under a glock, and suggestion from Linus to factor out the alloc/dealloc. I've given this a test with a variety of different sized directories and it seems to work ok. Cc: Andrew Morton Cc: Nick Piggin Cc: Prarit Bhargava Signed-off-by: Steven Whitehouse Signed-off-by: Linus Torvalds --- fs/gfs2/dir.c | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) (limited to 'fs') diff --git a/fs/gfs2/dir.c b/fs/gfs2/dir.c index 26ca3361a8bc..6b48d7c268b2 100644 --- a/fs/gfs2/dir.c +++ b/fs/gfs2/dir.c @@ -1231,6 +1231,25 @@ static int do_filldir_main(struct gfs2_inode *dip, u64 *offset, return 0; } +static void *gfs2_alloc_sort_buffer(unsigned size) +{ + void *ptr = NULL; + + if (size < KMALLOC_MAX_SIZE) + ptr = kmalloc(size, GFP_NOFS | __GFP_NOWARN); + if (!ptr) + ptr = __vmalloc(size, GFP_NOFS, PAGE_KERNEL); + return ptr; +} + +static void gfs2_free_sort_buffer(void *ptr) +{ + if (is_vmalloc_addr(ptr)) + vfree(ptr); + else + kfree(ptr); +} + static int gfs2_dir_read_leaf(struct inode *inode, u64 *offset, void *opaque, filldir_t filldir, int *copied, unsigned *depth, u64 leaf_no) @@ -1271,7 +1290,7 @@ static int gfs2_dir_read_leaf(struct inode *inode, u64 *offset, void *opaque, * 99 is the maximum number of entries that can fit in a single * leaf block. */ - larr = vmalloc((leaves + entries + 99) * sizeof(void *)); + larr = gfs2_alloc_sort_buffer((leaves + entries + 99) * sizeof(void *)); if (!larr) goto out; darr = (const struct gfs2_dirent **)(larr + leaves); @@ -1282,7 +1301,7 @@ static int gfs2_dir_read_leaf(struct inode *inode, u64 *offset, void *opaque, do { error = get_leaf(ip, lfn, &bh); if (error) - goto out_kfree; + goto out_free; lf = (struct gfs2_leaf *)bh->b_data; lfn = be64_to_cpu(lf->lf_next); if (lf->lf_entries) { @@ -1291,7 +1310,7 @@ static int gfs2_dir_read_leaf(struct inode *inode, u64 *offset, void *opaque, gfs2_dirent_gather, NULL, &g); error = PTR_ERR(dent); if (IS_ERR(dent)) - goto out_kfree; + goto out_free; if (entries2 != g.offset) { fs_warn(sdp, "Number of entries corrupt in dir " "leaf %llu, entries2 (%u) != " @@ -1300,7 +1319,7 @@ static int gfs2_dir_read_leaf(struct inode *inode, u64 *offset, void *opaque, entries2, g.offset); error = -EIO; - goto out_kfree; + goto out_free; } error = 0; larr[leaf++] = bh; @@ -1312,10 +1331,10 @@ static int gfs2_dir_read_leaf(struct inode *inode, u64 *offset, void *opaque, BUG_ON(entries2 != entries); error = do_filldir_main(ip, offset, opaque, filldir, darr, entries, copied); -out_kfree: +out_free: for(i = 0; i < leaf; i++) brelse(larr[i]); - vfree(larr); + gfs2_free_sort_buffer(larr); out: return error; } -- cgit v1.2.3 From a6f80fb7b5986fda663d94079d3bba0937a6b6ff Mon Sep 17 00:00:00 2001 From: Andre Osterhues Date: Tue, 13 Jul 2010 15:59:17 -0500 Subject: ecryptfs: Bugfix for error related to ecryptfs_hash_buckets The function ecryptfs_uid_hash wrongly assumes that the second parameter to hash_long() is the number of hash buckets instead of the number of hash bits. This patch fixes that and renames the variable ecryptfs_hash_buckets to ecryptfs_hash_bits to make it clearer. Fixes: CVE-2010-2492 Signed-off-by: Andre Osterhues Signed-off-by: Tyler Hicks Signed-off-by: Linus Torvalds --- fs/ecryptfs/messaging.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'fs') diff --git a/fs/ecryptfs/messaging.c b/fs/ecryptfs/messaging.c index 2d8dbce9d485..46c4dd8dfcc3 100644 --- a/fs/ecryptfs/messaging.c +++ b/fs/ecryptfs/messaging.c @@ -31,9 +31,9 @@ static struct mutex ecryptfs_msg_ctx_lists_mux; static struct hlist_head *ecryptfs_daemon_hash; struct mutex ecryptfs_daemon_hash_mux; -static int ecryptfs_hash_buckets; +static int ecryptfs_hash_bits; #define ecryptfs_uid_hash(uid) \ - hash_long((unsigned long)uid, ecryptfs_hash_buckets) + hash_long((unsigned long)uid, ecryptfs_hash_bits) static u32 ecryptfs_msg_counter; static struct ecryptfs_msg_ctx *ecryptfs_msg_ctx_arr; @@ -486,18 +486,19 @@ int ecryptfs_init_messaging(void) } mutex_init(&ecryptfs_daemon_hash_mux); mutex_lock(&ecryptfs_daemon_hash_mux); - ecryptfs_hash_buckets = 1; - while (ecryptfs_number_of_users >> ecryptfs_hash_buckets) - ecryptfs_hash_buckets++; + ecryptfs_hash_bits = 1; + while (ecryptfs_number_of_users >> ecryptfs_hash_bits) + ecryptfs_hash_bits++; ecryptfs_daemon_hash = kmalloc((sizeof(struct hlist_head) - * ecryptfs_hash_buckets), GFP_KERNEL); + * (1 << ecryptfs_hash_bits)), + GFP_KERNEL); if (!ecryptfs_daemon_hash) { rc = -ENOMEM; printk(KERN_ERR "%s: Failed to allocate memory\n", __func__); mutex_unlock(&ecryptfs_daemon_hash_mux); goto out; } - for (i = 0; i < ecryptfs_hash_buckets; i++) + for (i = 0; i < (1 << ecryptfs_hash_bits); i++) INIT_HLIST_HEAD(&ecryptfs_daemon_hash[i]); mutex_unlock(&ecryptfs_daemon_hash_mux); ecryptfs_msg_ctx_arr = kmalloc((sizeof(struct ecryptfs_msg_ctx) @@ -554,7 +555,7 @@ void ecryptfs_release_messaging(void) int i; mutex_lock(&ecryptfs_daemon_hash_mux); - for (i = 0; i < ecryptfs_hash_buckets; i++) { + for (i = 0; i < (1 << ecryptfs_hash_bits); i++) { int rc; hlist_for_each_entry(daemon, elem, -- cgit v1.2.3 From 30116ff6c6d140bc696cc624e6d8e38f018c886e Mon Sep 17 00:00:00 2001 From: Steven Whitehouse Date: Mon, 14 Jun 2010 09:58:41 +0100 Subject: GFS2: Use nobh_writepage Use nobh_writepage rather than calling mpage_writepage directly. Signed-off-by: Steven Whitehouse Cc: Christoph Hellwig --- fs/gfs2/aops.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/gfs2/aops.c b/fs/gfs2/aops.c index 9f8b52500d63..9485a882d80b 100644 --- a/fs/gfs2/aops.c +++ b/fs/gfs2/aops.c @@ -136,10 +136,7 @@ static int gfs2_writeback_writepage(struct page *page, if (ret <= 0) return ret; - ret = mpage_writepage(page, gfs2_get_block_noalloc, wbc); - if (ret == -EAGAIN) - ret = block_write_full_page(page, gfs2_get_block_noalloc, wbc); - return ret; + return nobh_writepage(page, gfs2_get_block_noalloc, wbc); } /** -- cgit v1.2.3 From ba6e93645f039bd357e04b7b9d18f4e67757725e Mon Sep 17 00:00:00 2001 From: Steven Whitehouse Date: Mon, 14 Jun 2010 10:01:30 +0100 Subject: GFS2: Wait for journal id on mount if not specified on mount command line This patch implements a wait for the journal id in the case that it has not been specified on the command line. This is to allow the future removal of the mount.gfs2 helper. The journal id would instead be directly communicated by gfs_controld to the file system. Here is a comparison of the two systems: Current: 1. mount calls mount.gfs2 2. mount.gfs2 connects to gfs_controld to retrieve the journal id 3. mount.gfs2 adds the journal id to the mount command line and calls the mount system call 4. gfs_controld receives the status of the mount request via a uevent Proposed: 1. mount calls the mount system call (no mount.gfs2 helper) 2. gfs_controld receives a uevent for a gfs2 fs which it doesn't know about already 3. gfs_controld assigns a journal id to it via sysfs 4. the mount system call then completes as normal (sending a uevent according to status) The advantage of the proposed system is that it is completely backward compatible with the current system both at the kernel and at the userland levels. The "first" parameter can also be set the same way, with the restriction that it must be set before the journal id is assigned. In addition, if mount becomes stuck waiting for a reply from gfs_controld which never arrives, then it is killable and will abort the mount gracefully. Signed-off-by: Steven Whitehouse --- fs/gfs2/incore.h | 1 + fs/gfs2/ops_fstype.c | 27 +++++++++++++++++++++++-- fs/gfs2/sys.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 80 insertions(+), 5 deletions(-) (limited to 'fs') diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h index b5d7363b22da..8fcbce48a128 100644 --- a/fs/gfs2/incore.h +++ b/fs/gfs2/incore.h @@ -460,6 +460,7 @@ enum { SDF_NOBARRIERS = 3, SDF_NORECOVERY = 4, SDF_DEMOTE = 5, + SDF_NOJOURNALID = 6, }; #define GFS2_FSNAME_LEN 256 diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c index 3593b3a7290e..45a4a36195d8 100644 --- a/fs/gfs2/ops_fstype.c +++ b/fs/gfs2/ops_fstype.c @@ -76,7 +76,7 @@ static struct gfs2_sbd *init_sbd(struct super_block *sb) sb->s_fs_info = sdp; sdp->sd_vfs = sb; - + set_bit(SDF_NOJOURNALID, &sdp->sd_flags); gfs2_tune_init(&sdp->sd_tune); init_waitqueue_head(&sdp->sd_glock_wait); @@ -1050,7 +1050,8 @@ static int gfs2_lm_mount(struct gfs2_sbd *sdp, int silent) ret = match_int(&tmp[0], &option); if (ret || option < 0) goto hostdata_error; - ls->ls_jid = option; + if (test_and_clear_bit(SDF_NOJOURNALID, &sdp->sd_flags)) + ls->ls_jid = option; break; case Opt_id: /* Obsolete, but left for backward compat purposes */ @@ -1102,6 +1103,24 @@ void gfs2_lm_unmount(struct gfs2_sbd *sdp) lm->lm_unmount(sdp); } +static int gfs2_journalid_wait(void *word) +{ + if (signal_pending(current)) + return -EINTR; + schedule(); + return 0; +} + +static int wait_on_journal(struct gfs2_sbd *sdp) +{ + if (sdp->sd_args.ar_spectator) + return 0; + if (sdp->sd_lockstruct.ls_ops->lm_mount == NULL) + return 0; + + return wait_on_bit(&sdp->sd_flags, SDF_NOJOURNALID, gfs2_journalid_wait, TASK_INTERRUPTIBLE); +} + void gfs2_online_uevent(struct gfs2_sbd *sdp) { struct super_block *sb = sdp->sd_vfs; @@ -1194,6 +1213,10 @@ static int fill_super(struct super_block *sb, struct gfs2_args *args, int silent if (error) goto fail_locking; + error = wait_on_journal(sdp); + if (error) + goto fail_sb; + error = init_inodes(sdp, DO); if (error) goto fail_sb; diff --git a/fs/gfs2/sys.c b/fs/gfs2/sys.c index 37f5393e68e6..d019d0d55e00 100644 --- a/fs/gfs2/sys.c +++ b/fs/gfs2/sys.c @@ -325,6 +325,30 @@ static ssize_t lkfirst_show(struct gfs2_sbd *sdp, char *buf) return sprintf(buf, "%d\n", ls->ls_first); } +static ssize_t lkfirst_store(struct gfs2_sbd *sdp, const char *buf, size_t len) +{ + unsigned first; + int rv; + + rv = sscanf(buf, "%u", &first); + if (rv != 1 || first > 1) + return -EINVAL; + spin_lock(&sdp->sd_jindex_spin); + rv = -EBUSY; + if (test_bit(SDF_NOJOURNALID, &sdp->sd_flags) == 0) + goto out; + rv = -EINVAL; + if (sdp->sd_args.ar_spectator) + goto out; + if (sdp->sd_lockstruct.ls_ops->lm_mount == NULL) + goto out; + sdp->sd_lockstruct.ls_first = first; + rv = 0; +out: + spin_unlock(&sdp->sd_jindex_spin); + return rv ? rv : len; +} + static ssize_t first_done_show(struct gfs2_sbd *sdp, char *buf) { struct lm_lockstruct *ls = &sdp->sd_lockstruct; @@ -377,14 +401,41 @@ static ssize_t jid_show(struct gfs2_sbd *sdp, char *buf) return sprintf(buf, "%u\n", sdp->sd_lockstruct.ls_jid); } +static ssize_t jid_store(struct gfs2_sbd *sdp, const char *buf, size_t len) +{ + unsigned jid; + int rv; + + rv = sscanf(buf, "%u", &jid); + if (rv != 1) + return -EINVAL; + + spin_lock(&sdp->sd_jindex_spin); + rv = -EINVAL; + if (sdp->sd_args.ar_spectator) + goto out; + if (sdp->sd_lockstruct.ls_ops->lm_mount == NULL) + goto out; + rv = -EBUSY; + if (test_and_clear_bit(SDF_NOJOURNALID, &sdp->sd_flags) == 0) + goto out; + sdp->sd_lockstruct.ls_jid = jid; + smp_mb__after_clear_bit(); + wake_up_bit(&sdp->sd_flags, SDF_NOJOURNALID); + rv = 0; +out: + spin_unlock(&sdp->sd_jindex_spin); + return rv ? rv : len; +} + #define GDLM_ATTR(_name,_mode,_show,_store) \ static struct gfs2_attr gdlm_attr_##_name = __ATTR(_name,_mode,_show,_store) GDLM_ATTR(proto_name, 0444, proto_name_show, NULL); GDLM_ATTR(block, 0644, block_show, block_store); GDLM_ATTR(withdraw, 0644, withdraw_show, withdraw_store); -GDLM_ATTR(jid, 0444, jid_show, NULL); -GDLM_ATTR(first, 0444, lkfirst_show, NULL); +GDLM_ATTR(jid, 0644, jid_show, jid_store); +GDLM_ATTR(first, 0644, lkfirst_show, lkfirst_store); GDLM_ATTR(first_done, 0444, first_done_show, NULL); GDLM_ATTR(recover, 0600, NULL, recover_store); GDLM_ATTR(recover_done, 0444, recover_done_show, NULL); @@ -564,7 +615,7 @@ static int gfs2_uevent(struct kset *kset, struct kobject *kobj, add_uevent_var(env, "LOCKTABLE=%s", sdp->sd_table_name); add_uevent_var(env, "LOCKPROTO=%s", sdp->sd_proto_name); - if (!sdp->sd_args.ar_spectator) + if (!test_bit(SDF_NOJOURNALID, &sdp->sd_flags)) add_uevent_var(env, "JOURNALID=%u", sdp->sd_lockstruct.ls_jid); if (gfs2_uuid_valid(uuid)) add_uevent_var(env, "UUID=%pUB", uuid); -- cgit v1.2.3 From 461cb419f074aab16836a660efb8e855b6c1609c Mon Sep 17 00:00:00 2001 From: Bob Peterson Date: Thu, 24 Jun 2010 19:21:20 -0400 Subject: GFS2: Simplify gfs2_write_alloc_required Function gfs2_write_alloc_required always returned zero as its return code. Therefore, it doesn't need to return a return code at all. Given that, we can use the return value to return whether or not the dinode needs block allocations rather than passing that value in, which in turn simplifies a bunch of error checking. Signed-off-by: Bob Peterson Signed-off-by: Steven Whitehouse --- fs/gfs2/aops.c | 4 +--- fs/gfs2/bmap.c | 15 +++++---------- fs/gfs2/bmap.h | 2 +- fs/gfs2/file.c | 4 +--- fs/gfs2/quota.c | 15 +++------------ fs/gfs2/super.c | 9 +++------ 6 files changed, 14 insertions(+), 35 deletions(-) (limited to 'fs') diff --git a/fs/gfs2/aops.c b/fs/gfs2/aops.c index 9485a882d80b..5e96cbd8a454 100644 --- a/fs/gfs2/aops.c +++ b/fs/gfs2/aops.c @@ -634,9 +634,7 @@ static int gfs2_write_begin(struct file *file, struct address_space *mapping, } } - error = gfs2_write_alloc_required(ip, pos, len, &alloc_required); - if (error) - goto out_unlock; + alloc_required = gfs2_write_alloc_required(ip, pos, len); if (alloc_required || gfs2_is_jdata(ip)) gfs2_write_calc_reserv(ip, len, &data_blocks, &ind_blocks); diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c index 84da64b551b2..744c29e2dcf4 100644 --- a/fs/gfs2/bmap.c +++ b/fs/gfs2/bmap.c @@ -1244,13 +1244,12 @@ int gfs2_file_dealloc(struct gfs2_inode *ip) * @ip: the file being written to * @offset: the offset to write to * @len: the number of bytes being written - * @alloc_required: set to 1 if an alloc is required, 0 otherwise * - * Returns: errno + * Returns: 1 if an alloc is required, 0 otherwise */ int gfs2_write_alloc_required(struct gfs2_inode *ip, u64 offset, - unsigned int len, int *alloc_required) + unsigned int len) { struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); struct buffer_head bh; @@ -1258,26 +1257,23 @@ int gfs2_write_alloc_required(struct gfs2_inode *ip, u64 offset, u64 lblock, lblock_stop, size; u64 end_of_file; - *alloc_required = 0; - if (!len) return 0; if (gfs2_is_stuffed(ip)) { if (offset + len > sdp->sd_sb.sb_bsize - sizeof(struct gfs2_dinode)) - *alloc_required = 1; + return 1; return 0; } - *alloc_required = 1; shift = sdp->sd_sb.sb_bsize_shift; BUG_ON(gfs2_is_dir(ip)); end_of_file = (ip->i_disksize + sdp->sd_sb.sb_bsize - 1) >> shift; lblock = offset >> shift; lblock_stop = (offset + len + sdp->sd_sb.sb_bsize - 1) >> shift; if (lblock_stop > end_of_file) - return 0; + return 1; size = (lblock_stop - lblock) << shift; do { @@ -1285,12 +1281,11 @@ int gfs2_write_alloc_required(struct gfs2_inode *ip, u64 offset, bh.b_size = size; gfs2_block_map(&ip->i_inode, lblock, &bh, 0); if (!buffer_mapped(&bh)) - return 0; + return 1; size -= bh.b_size; lblock += (bh.b_size >> ip->i_inode.i_blkbits); } while(size > 0); - *alloc_required = 0; return 0; } diff --git a/fs/gfs2/bmap.h b/fs/gfs2/bmap.h index c983177e05ac..a20a5213135a 100644 --- a/fs/gfs2/bmap.h +++ b/fs/gfs2/bmap.h @@ -52,6 +52,6 @@ int gfs2_truncatei(struct gfs2_inode *ip, u64 size); int gfs2_truncatei_resume(struct gfs2_inode *ip); int gfs2_file_dealloc(struct gfs2_inode *ip); int gfs2_write_alloc_required(struct gfs2_inode *ip, u64 offset, - unsigned int len, int *alloc_required); + unsigned int len); #endif /* __BMAP_DOT_H__ */ diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c index ed9a94f0ef15..4edd662c8232 100644 --- a/fs/gfs2/file.c +++ b/fs/gfs2/file.c @@ -351,7 +351,6 @@ static int gfs2_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) unsigned long last_index; u64 pos = page->index << PAGE_CACHE_SHIFT; unsigned int data_blocks, ind_blocks, rblocks; - int alloc_required = 0; struct gfs2_holder gh; struct gfs2_alloc *al; int ret; @@ -364,8 +363,7 @@ static int gfs2_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) set_bit(GLF_DIRTY, &ip->i_gl->gl_flags); set_bit(GIF_SW_PAGED, &ip->i_flags); - ret = gfs2_write_alloc_required(ip, pos, PAGE_CACHE_SIZE, &alloc_required); - if (ret || !alloc_required) + if (!gfs2_write_alloc_required(ip, pos, PAGE_CACHE_SIZE)) goto out_unlock; ret = -ENOMEM; al = gfs2_alloc_get(ip); diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index 8f02d3db8f42..8bb643cb2658 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c @@ -787,15 +787,9 @@ static int do_sync(unsigned int num_qd, struct gfs2_quota_data **qda) goto out; for (x = 0; x < num_qd; x++) { - int alloc_required; - offset = qd2offset(qda[x]); - error = gfs2_write_alloc_required(ip, offset, - sizeof(struct gfs2_quota), - &alloc_required); - if (error) - goto out_gunlock; - if (alloc_required) + if (gfs2_write_alloc_required(ip, offset, + sizeof(struct gfs2_quota))) nalloc++; } @@ -1584,10 +1578,7 @@ static int gfs2_set_dqblk(struct super_block *sb, int type, qid_t id, goto out_i; offset = qd2offset(qd); - error = gfs2_write_alloc_required(ip, offset, sizeof(struct gfs2_quota), - &alloc_required); - if (error) - goto out_i; + alloc_required = gfs2_write_alloc_required(ip, offset, sizeof(struct gfs2_quota)); if (alloc_required) { al = gfs2_alloc_get(ip); if (al == NULL) diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c index 4d1aad38f1b1..4140811a921c 100644 --- a/fs/gfs2/super.c +++ b/fs/gfs2/super.c @@ -342,8 +342,6 @@ int gfs2_jdesc_check(struct gfs2_jdesc *jd) { struct gfs2_inode *ip = GFS2_I(jd->jd_inode); struct gfs2_sbd *sdp = GFS2_SB(jd->jd_inode); - int ar; - int error; if (ip->i_disksize < (8 << 20) || ip->i_disksize > (1 << 30) || (ip->i_disksize & (sdp->sd_sb.sb_bsize - 1))) { @@ -352,13 +350,12 @@ int gfs2_jdesc_check(struct gfs2_jdesc *jd) } jd->jd_blocks = ip->i_disksize >> sdp->sd_sb.sb_bsize_shift; - error = gfs2_write_alloc_required(ip, 0, ip->i_disksize, &ar); - if (!error && ar) { + if (gfs2_write_alloc_required(ip, 0, ip->i_disksize)) { gfs2_consist_inode(ip); - error = -EIO; + return -EIO; } - return error; + return 0; } /** -- cgit v1.2.3 From 4244b52e18be959ced77b984f268e46a0a7654e3 Mon Sep 17 00:00:00 2001 From: David Rientjes Date: Tue, 20 Jul 2010 19:45:03 -0700 Subject: GFS2: remove dependency on __GFP_NOFAIL The k[mc]allocs in dr_split_leaf() and dir_double_exhash() are failable, so remove __GFP_NOFAIL from their masks. Cc: Bob Peterson Signed-off-by: David Rientjes Signed-off-by: Steven Whitehouse --- fs/gfs2/dir.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/gfs2/dir.c b/fs/gfs2/dir.c index 6b48d7c268b2..b9dd88a78dd4 100644 --- a/fs/gfs2/dir.c +++ b/fs/gfs2/dir.c @@ -955,7 +955,12 @@ static int dir_split_leaf(struct inode *inode, const struct qstr *name) /* Change the pointers. Don't bother distinguishing stuffed from non-stuffed. This code is complicated enough already. */ - lp = kmalloc(half_len * sizeof(__be64), GFP_NOFS | __GFP_NOFAIL); + lp = kmalloc(half_len * sizeof(__be64), GFP_NOFS); + if (!lp) { + error = -ENOMEM; + goto fail_brelse; + } + /* Change the pointers */ for (x = 0; x < half_len; x++) lp[x] = cpu_to_be64(bn); @@ -1063,7 +1068,9 @@ static int dir_double_exhash(struct gfs2_inode *dip) /* Allocate both the "from" and "to" buffers in one big chunk */ - buf = kcalloc(3, sdp->sd_hash_bsize, GFP_NOFS | __GFP_NOFAIL); + buf = kcalloc(3, sdp->sd_hash_bsize, GFP_NOFS); + if (!buf) + return -ENOMEM; for (block = dip->i_disksize >> sdp->sd_hash_bsize_shift; block--;) { error = gfs2_dir_read_data(dip, (char *)buf, -- cgit v1.2.3 From d5341a92416706808dc5cd847826f28c08063c8c Mon Sep 17 00:00:00 2001 From: Steven Whitehouse Date: Fri, 23 Jul 2010 14:05:51 +0100 Subject: GFS2: Make "try" lock not try quite so hard This looks like a big change, but in reality its only a single line of actual code change, the rest is just moving a function to before its new caller. The "try" flag for glocks is a rather subtle and delicate setting since it requires that the state machine tries just hard enough to ensure that it has a good chance of getting the requested lock, but no so hard that the request can land up blocked behind another. The patch adds in an additional check which will fail any queued try locks if there is another request blocking the try lock request which is not granted and compatible, nor in progress already. The check is made only after all pending locks which may be granted have been granted. I've checked this with the reproducer for the reported flock bug which this is intended to fix, and it now passes. Signed-off-by: Steven Whitehouse --- fs/gfs2/glock.c | 49 +++++++++++++++++++++++++------------------------ 1 file changed, 25 insertions(+), 24 deletions(-) (limited to 'fs') diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c index 0898f3ec8212..717531d1b2a8 100644 --- a/fs/gfs2/glock.c +++ b/fs/gfs2/glock.c @@ -327,6 +327,30 @@ static void gfs2_holder_wake(struct gfs2_holder *gh) wake_up_bit(&gh->gh_iflags, HIF_WAIT); } +/** + * do_error - Something unexpected has happened during a lock request + * + */ + +static inline void do_error(struct gfs2_glock *gl, const int ret) +{ + struct gfs2_holder *gh, *tmp; + + list_for_each_entry_safe(gh, tmp, &gl->gl_holders, gh_list) { + if (test_bit(HIF_HOLDER, &gh->gh_iflags)) + continue; + if (ret & LM_OUT_ERROR) + gh->gh_error = -EIO; + else if (gh->gh_flags & (LM_FLAG_TRY | LM_FLAG_TRY_1CB)) + gh->gh_error = GLR_TRYFAILED; + else + continue; + list_del_init(&gh->gh_list); + trace_gfs2_glock_queue(gh, 0); + gfs2_holder_wake(gh); + } +} + /** * do_promote - promote as many requests as possible on the current queue * @gl: The glock @@ -375,35 +399,12 @@ restart: } if (gh->gh_list.prev == &gl->gl_holders) return 1; + do_error(gl, 0); break; } return 0; } -/** - * do_error - Something unexpected has happened during a lock request - * - */ - -static inline void do_error(struct gfs2_glock *gl, const int ret) -{ - struct gfs2_holder *gh, *tmp; - - list_for_each_entry_safe(gh, tmp, &gl->gl_holders, gh_list) { - if (test_bit(HIF_HOLDER, &gh->gh_iflags)) - continue; - if (ret & LM_OUT_ERROR) - gh->gh_error = -EIO; - else if (gh->gh_flags & (LM_FLAG_TRY | LM_FLAG_TRY_1CB)) - gh->gh_error = GLR_TRYFAILED; - else - continue; - list_del_init(&gh->gh_list); - trace_gfs2_glock_queue(gh, 0); - gfs2_holder_wake(gh); - } -} - /** * find_first_waiter - find the first gh that's waiting for the glock * @gl: the glock -- cgit v1.2.3 From 7cdee5dbf477409e4afc6c9063492dc2577b41ea Mon Sep 17 00:00:00 2001 From: Steven Whitehouse Date: Thu, 29 Jul 2010 14:39:29 +0100 Subject: Revert "GFS2: recovery stuck on transaction lock" This reverts commit b7dc2df5725fe7355fd76000ead7e39728e1b8a9. The initial patch didn't quite work since it doesn't cover all the possible routes by which the GLF_FROZEN flag might be set. A revised fix is coming up in the next patch. Signed-off-by: Steven Whitehouse --- fs/gfs2/glock.c | 10 ---------- 1 file changed, 10 deletions(-) (limited to 'fs') diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c index 717531d1b2a8..2b3d8f8a8393 100644 --- a/fs/gfs2/glock.c +++ b/fs/gfs2/glock.c @@ -707,18 +707,8 @@ static void glock_work_func(struct work_struct *work) { unsigned long delay = 0; struct gfs2_glock *gl = container_of(work, struct gfs2_glock, gl_work.work); - struct gfs2_holder *gh; int drop_ref = 0; - if (unlikely(test_bit(GLF_FROZEN, &gl->gl_flags))) { - spin_lock(&gl->gl_spin); - gh = find_first_waiter(gl); - if (gh && (gh->gh_flags & LM_FLAG_NOEXP) && - test_and_clear_bit(GLF_FROZEN, &gl->gl_flags)) - set_bit(GLF_REPLY_PENDING, &gl->gl_flags); - spin_unlock(&gl->gl_spin); - } - if (test_and_clear_bit(GLF_REPLY_PENDING, &gl->gl_flags)) { finish_xmote(gl, gl->gl_reply); drop_ref = 1; -- cgit v1.2.3 From 4538821993f4486c76090dfb377c60c0a0e71ba3 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Thu, 29 Jul 2010 15:06:10 -0400 Subject: ext4: drop inode from orphan list if ext4_delete_inode() fails There were some error paths in ext4_delete_inode() which was not dropping the inode from the orphan list. This could lead to a BUG_ON on umount when the orphan list is discovered to be non-empty. Signed-off-by: "Theodore Ts'o" --- fs/ext4/inode.c | 1 + 1 file changed, 1 insertion(+) (limited to 'fs') diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index a52d5af99187..533b607f9cb5 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -221,6 +221,7 @@ void ext4_delete_inode(struct inode *inode) "couldn't extend journal (err %d)", err); stop_handle: ext4_journal_stop(handle); + ext4_orphan_del(NULL, inode); goto no_delete; } } -- cgit v1.2.3 From de09a9771a5346029f4d11e4ac886be7f9bfdd75 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 29 Jul 2010 12:45:49 +0100 Subject: CRED: Fix get_task_cred() and task_state() to not resurrect dead credentials It's possible for get_task_cred() as it currently stands to 'corrupt' a set of credentials by incrementing their usage count after their replacement by the task being accessed. What happens is that get_task_cred() can race with commit_creds(): TASK_1 TASK_2 RCU_CLEANER -->get_task_cred(TASK_2) rcu_read_lock() __cred = __task_cred(TASK_2) -->commit_creds() old_cred = TASK_2->real_cred TASK_2->real_cred = ... put_cred(old_cred) call_rcu(old_cred) [__cred->usage == 0] get_cred(__cred) [__cred->usage == 1] rcu_read_unlock() -->put_cred_rcu() [__cred->usage == 1] panic() However, since a tasks credentials are generally not changed very often, we can reasonably make use of a loop involving reading the creds pointer and using atomic_inc_not_zero() to attempt to increment it if it hasn't already hit zero. If successful, we can safely return the credentials in the knowledge that, even if the task we're accessing has released them, they haven't gone to the RCU cleanup code. We then change task_state() in procfs to use get_task_cred() rather than calling get_cred() on the result of __task_cred(), as that suffers from the same problem. Without this change, a BUG_ON in __put_cred() or in put_cred_rcu() can be tripped when it is noticed that the usage count is not zero as it ought to be, for example: kernel BUG at kernel/cred.c:168! invalid opcode: 0000 [#1] SMP last sysfs file: /sys/kernel/mm/ksm/run CPU 0 Pid: 2436, comm: master Not tainted 2.6.33.3-85.fc13.x86_64 #1 0HR330/OptiPlex 745 RIP: 0010:[] [] __put_cred+0xc/0x45 RSP: 0018:ffff88019e7e9eb8 EFLAGS: 00010202 RAX: 0000000000000001 RBX: ffff880161514480 RCX: 00000000ffffffff RDX: 00000000ffffffff RSI: ffff880140c690c0 RDI: ffff880140c690c0 RBP: ffff88019e7e9eb8 R08: 00000000000000d0 R09: 0000000000000000 R10: 0000000000000001 R11: 0000000000000040 R12: ffff880140c690c0 R13: ffff88019e77aea0 R14: 00007fff336b0a5c R15: 0000000000000001 FS: 00007f12f50d97c0(0000) GS:ffff880007400000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007f8f461bc000 CR3: 00000001b26ce000 CR4: 00000000000006f0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 Process master (pid: 2436, threadinfo ffff88019e7e8000, task ffff88019e77aea0) Stack: ffff88019e7e9ec8 ffffffff810698cd ffff88019e7e9ef8 ffffffff81069b45 <0> ffff880161514180 ffff880161514480 ffff880161514180 0000000000000000 <0> ffff88019e7e9f28 ffffffff8106aace 0000000000000001 0000000000000246 Call Trace: [] put_cred+0x13/0x15 [] commit_creds+0x16b/0x175 [] set_current_groups+0x47/0x4e [] sys_setgroups+0xf6/0x105 [] system_call_fastpath+0x16/0x1b Code: 48 8d 71 ff e8 7e 4e 15 00 85 c0 78 0b 8b 75 ec 48 89 df e8 ef 4a 15 00 48 83 c4 18 5b c9 c3 55 8b 07 8b 07 48 89 e5 85 c0 74 04 <0f> 0b eb fe 65 48 8b 04 25 00 cc 00 00 48 3b b8 58 04 00 00 75 RIP [] __put_cred+0xc/0x45 RSP ---[ end trace df391256a100ebdd ]--- Signed-off-by: David Howells Acked-by: Jiri Olsa Signed-off-by: Linus Torvalds --- fs/proc/array.c | 2 +- include/linux/cred.h | 21 +-------------------- kernel/cred.c | 25 +++++++++++++++++++++++++ 3 files changed, 27 insertions(+), 21 deletions(-) (limited to 'fs') diff --git a/fs/proc/array.c b/fs/proc/array.c index 9b58d38bc911..fff6572676ae 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -176,7 +176,7 @@ static inline void task_state(struct seq_file *m, struct pid_namespace *ns, if (tracer) tpid = task_pid_nr_ns(tracer, ns); } - cred = get_cred((struct cred *) __task_cred(p)); + cred = get_task_cred(p); seq_printf(m, "State:\t%s\n" "Tgid:\t%d\n" diff --git a/include/linux/cred.h b/include/linux/cred.h index 75c0fa881308..ce40cbc791e2 100644 --- a/include/linux/cred.h +++ b/include/linux/cred.h @@ -153,6 +153,7 @@ struct cred { extern void __put_cred(struct cred *); extern void exit_creds(struct task_struct *); extern int copy_creds(struct task_struct *, unsigned long); +extern const struct cred *get_task_cred(struct task_struct *); extern struct cred *cred_alloc_blank(void); extern struct cred *prepare_creds(void); extern struct cred *prepare_exec_creds(void); @@ -281,26 +282,6 @@ static inline void put_cred(const struct cred *_cred) #define __task_cred(task) \ ((const struct cred *)(rcu_dereference_check((task)->real_cred, rcu_read_lock_held() || lockdep_tasklist_lock_is_held()))) -/** - * get_task_cred - Get another task's objective credentials - * @task: The task to query - * - * Get the objective credentials of a task, pinning them so that they can't go - * away. Accessing a task's credentials directly is not permitted. - * - * The caller must make sure task doesn't go away, either by holding a ref on - * task or by holding tasklist_lock to prevent it from being unlinked. - */ -#define get_task_cred(task) \ -({ \ - struct cred *__cred; \ - rcu_read_lock(); \ - __cred = (struct cred *) __task_cred((task)); \ - get_cred(__cred); \ - rcu_read_unlock(); \ - __cred; \ -}) - /** * get_current_cred - Get the current task's subjective credentials * diff --git a/kernel/cred.c b/kernel/cred.c index a2d5504fbcc2..60bc8b1e32e6 100644 --- a/kernel/cred.c +++ b/kernel/cred.c @@ -209,6 +209,31 @@ void exit_creds(struct task_struct *tsk) } } +/** + * get_task_cred - Get another task's objective credentials + * @task: The task to query + * + * Get the objective credentials of a task, pinning them so that they can't go + * away. Accessing a task's credentials directly is not permitted. + * + * The caller must also make sure task doesn't get deleted, either by holding a + * ref on task or by holding tasklist_lock to prevent it from being unlinked. + */ +const struct cred *get_task_cred(struct task_struct *task) +{ + const struct cred *cred; + + rcu_read_lock(); + + do { + cred = __task_cred((task)); + BUG_ON(!cred); + } while (!atomic_inc_not_zero(&((struct cred *)cred)->usage)); + + rcu_read_unlock(); + return cred; +} + /* * Allocate blank credentials, such that the credentials can be filled in at a * later date without risk of ENOMEM. -- cgit v1.2.3 From c639d5d8f69f37e24ed0354373f61fcbde4b9354 Mon Sep 17 00:00:00 2001 From: Abhijith Das Date: Fri, 30 Jul 2010 11:34:52 -0400 Subject: GFS2: Fix typo in stuffed file data copy handling trunc_start() in bmap.c incorrectly uses sizeof(struct gfs2_inode) instead of sizeof(struct gfs2_dinode). Signed-off-by: Abhi Das Signed-off-by: Steven Whitehouse --- fs/gfs2/bmap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c index 744c29e2dcf4..6f482809d1a3 100644 --- a/fs/gfs2/bmap.c +++ b/fs/gfs2/bmap.c @@ -1040,7 +1040,7 @@ static int trunc_start(struct gfs2_inode *ip, u64 size) goto out; if (gfs2_is_stuffed(ip)) { - u64 dsize = size + sizeof(struct gfs2_inode); + u64 dsize = size + sizeof(struct gfs2_dinode); ip->i_disksize = size; ip->i_inode.i_mtime = ip->i_inode.i_ctime = CURRENT_TIME; gfs2_trans_add_bh(ip->i_gl, dibh, 1); -- cgit v1.2.3 From f11ac8db5d07b6e99d41ff4aa39d878ee5cef1c5 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Fri, 25 Jun 2010 16:35:53 -0400 Subject: NFSv4: Ensure that we track the NFSv4 lock state in read/write requests. This patch fixes bugzilla entry 14501: https://bugzilla.kernel.org/show_bug.cgi?id=14501 Signed-off-by: Trond Myklebust --- fs/nfs/direct.c | 29 +++++++++++++++----- fs/nfs/inode.c | 70 +++++++++++++++++++++++++++++++++++++++++++++--- fs/nfs/nfs4xdr.c | 8 +++--- fs/nfs/pagelist.c | 8 +++++- fs/nfs/read.c | 1 + fs/nfs/write.c | 5 +++- include/linux/nfs_fs.h | 13 +++++++-- include/linux/nfs_page.h | 1 + include/linux/nfs_xdr.h | 2 ++ 9 files changed, 118 insertions(+), 19 deletions(-) (limited to 'fs') diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c index ad4cd31d6050..064a80961677 100644 --- a/fs/nfs/direct.c +++ b/fs/nfs/direct.c @@ -69,6 +69,7 @@ struct nfs_direct_req { /* I/O parameters */ struct nfs_open_context *ctx; /* file open context info */ + struct nfs_lock_context *l_ctx; /* Lock context info */ struct kiocb * iocb; /* controlling i/o request */ struct inode * inode; /* target file of i/o */ @@ -160,6 +161,7 @@ static inline struct nfs_direct_req *nfs_direct_req_alloc(void) INIT_LIST_HEAD(&dreq->rewrite_list); dreq->iocb = NULL; dreq->ctx = NULL; + dreq->l_ctx = NULL; spin_lock_init(&dreq->lock); atomic_set(&dreq->io_count, 0); dreq->count = 0; @@ -173,6 +175,8 @@ static void nfs_direct_req_free(struct kref *kref) { struct nfs_direct_req *dreq = container_of(kref, struct nfs_direct_req, kref); + if (dreq->l_ctx != NULL) + nfs_put_lock_context(dreq->l_ctx); if (dreq->ctx != NULL) put_nfs_open_context(dreq->ctx); kmem_cache_free(nfs_direct_cachep, dreq); @@ -336,6 +340,7 @@ static ssize_t nfs_direct_read_schedule_segment(struct nfs_direct_req *dreq, data->cred = msg.rpc_cred; data->args.fh = NFS_FH(inode); data->args.context = ctx; + data->args.lock_context = dreq->l_ctx; data->args.offset = pos; data->args.pgbase = pgbase; data->args.pages = data->pagevec; @@ -416,24 +421,28 @@ static ssize_t nfs_direct_read_schedule_iovec(struct nfs_direct_req *dreq, static ssize_t nfs_direct_read(struct kiocb *iocb, const struct iovec *iov, unsigned long nr_segs, loff_t pos) { - ssize_t result = 0; + ssize_t result = -ENOMEM; struct inode *inode = iocb->ki_filp->f_mapping->host; struct nfs_direct_req *dreq; dreq = nfs_direct_req_alloc(); - if (!dreq) - return -ENOMEM; + if (dreq == NULL) + goto out; dreq->inode = inode; dreq->ctx = get_nfs_open_context(nfs_file_open_context(iocb->ki_filp)); + dreq->l_ctx = nfs_get_lock_context(dreq->ctx); + if (dreq->l_ctx == NULL) + goto out_release; if (!is_sync_kiocb(iocb)) dreq->iocb = iocb; result = nfs_direct_read_schedule_iovec(dreq, iov, nr_segs, pos); if (!result) result = nfs_direct_wait(dreq); +out_release: nfs_direct_req_release(dreq); - +out: return result; } @@ -574,6 +583,7 @@ static void nfs_direct_commit_schedule(struct nfs_direct_req *dreq) data->args.offset = 0; data->args.count = 0; data->args.context = dreq->ctx; + data->args.lock_context = dreq->l_ctx; data->res.count = 0; data->res.fattr = &data->fattr; data->res.verf = &data->verf; @@ -761,6 +771,7 @@ static ssize_t nfs_direct_write_schedule_segment(struct nfs_direct_req *dreq, data->cred = msg.rpc_cred; data->args.fh = NFS_FH(inode); data->args.context = ctx; + data->args.lock_context = dreq->l_ctx; data->args.offset = pos; data->args.pgbase = pgbase; data->args.pages = data->pagevec; @@ -845,7 +856,7 @@ static ssize_t nfs_direct_write(struct kiocb *iocb, const struct iovec *iov, unsigned long nr_segs, loff_t pos, size_t count) { - ssize_t result = 0; + ssize_t result = -ENOMEM; struct inode *inode = iocb->ki_filp->f_mapping->host; struct nfs_direct_req *dreq; size_t wsize = NFS_SERVER(inode)->wsize; @@ -853,7 +864,7 @@ static ssize_t nfs_direct_write(struct kiocb *iocb, const struct iovec *iov, dreq = nfs_direct_req_alloc(); if (!dreq) - return -ENOMEM; + goto out; nfs_alloc_commit_data(dreq); if (dreq->commit_data == NULL || count < wsize) @@ -861,14 +872,18 @@ static ssize_t nfs_direct_write(struct kiocb *iocb, const struct iovec *iov, dreq->inode = inode; dreq->ctx = get_nfs_open_context(nfs_file_open_context(iocb->ki_filp)); + dreq->l_ctx = nfs_get_lock_context(dreq->ctx); + if (dreq->l_ctx != NULL) + goto out_release; if (!is_sync_kiocb(iocb)) dreq->iocb = iocb; result = nfs_direct_write_schedule_iovec(dreq, iov, nr_segs, pos, sync); if (!result) result = nfs_direct_wait(dreq); +out_release: nfs_direct_req_release(dreq); - +out: return result; } diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 099b3518feea..ec7a8f96a2c2 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -530,6 +530,68 @@ out: return err; } +static void nfs_init_lock_context(struct nfs_lock_context *l_ctx) +{ + atomic_set(&l_ctx->count, 1); + l_ctx->lockowner = current->files; + l_ctx->pid = current->tgid; + INIT_LIST_HEAD(&l_ctx->list); +} + +static struct nfs_lock_context *__nfs_find_lock_context(struct nfs_open_context *ctx) +{ + struct nfs_lock_context *pos; + + list_for_each_entry(pos, &ctx->lock_context.list, list) { + if (pos->lockowner != current->files) + continue; + if (pos->pid != current->tgid) + continue; + atomic_inc(&pos->count); + return pos; + } + return NULL; +} + +struct nfs_lock_context *nfs_get_lock_context(struct nfs_open_context *ctx) +{ + struct nfs_lock_context *res, *new = NULL; + struct inode *inode = ctx->path.dentry->d_inode; + + spin_lock(&inode->i_lock); + res = __nfs_find_lock_context(ctx); + if (res == NULL) { + spin_unlock(&inode->i_lock); + new = kmalloc(sizeof(*new), GFP_KERNEL); + if (new == NULL) + return NULL; + nfs_init_lock_context(new); + spin_lock(&inode->i_lock); + res = __nfs_find_lock_context(ctx); + if (res == NULL) { + list_add_tail(&new->list, &ctx->lock_context.list); + new->open_context = ctx; + res = new; + new = NULL; + } + } + spin_unlock(&inode->i_lock); + kfree(new); + return res; +} + +void nfs_put_lock_context(struct nfs_lock_context *l_ctx) +{ + struct nfs_open_context *ctx = l_ctx->open_context; + struct inode *inode = ctx->path.dentry->d_inode; + + if (!atomic_dec_and_lock(&l_ctx->count, &inode->i_lock)) + return; + list_del(&l_ctx->list); + spin_unlock(&inode->i_lock); + kfree(l_ctx); +} + /** * nfs_close_context - Common close_context() routine NFSv2/v3 * @ctx: pointer to context @@ -566,11 +628,11 @@ static struct nfs_open_context *alloc_nfs_open_context(struct path *path, struct path_get(&ctx->path); ctx->cred = get_rpccred(cred); ctx->state = NULL; - ctx->lockowner = current->files; ctx->flags = 0; ctx->error = 0; ctx->dir_cookie = 0; - atomic_set(&ctx->count, 1); + nfs_init_lock_context(&ctx->lock_context); + ctx->lock_context.open_context = ctx; } return ctx; } @@ -578,7 +640,7 @@ static struct nfs_open_context *alloc_nfs_open_context(struct path *path, struct struct nfs_open_context *get_nfs_open_context(struct nfs_open_context *ctx) { if (ctx != NULL) - atomic_inc(&ctx->count); + atomic_inc(&ctx->lock_context.count); return ctx; } @@ -586,7 +648,7 @@ static void __put_nfs_open_context(struct nfs_open_context *ctx, int is_sync) { struct inode *inode = ctx->path.dentry->d_inode; - if (!atomic_dec_and_lock(&ctx->count, &inode->i_lock)) + if (!atomic_dec_and_lock(&ctx->lock_context.count, &inode->i_lock)) return; list_del(&ctx->list); spin_unlock(&inode->i_lock); diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 1f7781d636ae..873b62f209ea 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -1324,14 +1324,14 @@ static void encode_putrootfh(struct xdr_stream *xdr, struct compound_hdr *hdr) hdr->replen += decode_putrootfh_maxsz; } -static void encode_stateid(struct xdr_stream *xdr, const struct nfs_open_context *ctx) +static void encode_stateid(struct xdr_stream *xdr, const struct nfs_open_context *ctx, const struct nfs_lock_context *l_ctx) { nfs4_stateid stateid; __be32 *p; p = reserve_space(xdr, NFS4_STATEID_SIZE); if (ctx->state != NULL) { - nfs4_copy_stateid(&stateid, ctx->state, ctx->lockowner); + nfs4_copy_stateid(&stateid, ctx->state, l_ctx->lockowner); xdr_encode_opaque_fixed(p, stateid.data, NFS4_STATEID_SIZE); } else xdr_encode_opaque_fixed(p, zero_stateid.data, NFS4_STATEID_SIZE); @@ -1344,7 +1344,7 @@ static void encode_read(struct xdr_stream *xdr, const struct nfs_readargs *args, p = reserve_space(xdr, 4); *p = cpu_to_be32(OP_READ); - encode_stateid(xdr, args->context); + encode_stateid(xdr, args->context, args->lock_context); p = reserve_space(xdr, 12); p = xdr_encode_hyper(p, args->offset); @@ -1523,7 +1523,7 @@ static void encode_write(struct xdr_stream *xdr, const struct nfs_writeargs *arg p = reserve_space(xdr, 4); *p = cpu_to_be32(OP_WRITE); - encode_stateid(xdr, args->context); + encode_stateid(xdr, args->context, args->lock_context); p = reserve_space(xdr, 16); p = xdr_encode_hyper(p, args->offset); diff --git a/fs/nfs/pagelist.c b/fs/nfs/pagelist.c index a3654e57b589..919490232e17 100644 --- a/fs/nfs/pagelist.c +++ b/fs/nfs/pagelist.c @@ -79,6 +79,7 @@ nfs_create_request(struct nfs_open_context *ctx, struct inode *inode, req->wb_pgbase = offset; req->wb_bytes = count; req->wb_context = get_nfs_open_context(ctx); + req->wb_lock_context = nfs_get_lock_context(ctx); kref_init(&req->wb_kref); return req; } @@ -141,11 +142,16 @@ void nfs_clear_request(struct nfs_page *req) { struct page *page = req->wb_page; struct nfs_open_context *ctx = req->wb_context; + struct nfs_lock_context *l_ctx = req->wb_lock_context; if (page != NULL) { page_cache_release(page); req->wb_page = NULL; } + if (l_ctx != NULL) { + nfs_put_lock_context(l_ctx); + req->wb_lock_context = NULL; + } if (ctx != NULL) { put_nfs_open_context(ctx); req->wb_context = NULL; @@ -235,7 +241,7 @@ static int nfs_can_coalesce_requests(struct nfs_page *prev, { if (req->wb_context->cred != prev->wb_context->cred) return 0; - if (req->wb_context->lockowner != prev->wb_context->lockowner) + if (req->wb_lock_context->lockowner != prev->wb_lock_context->lockowner) return 0; if (req->wb_context->state != prev->wb_context->state) return 0; diff --git a/fs/nfs/read.c b/fs/nfs/read.c index 5a33a92e8168..87adc2744246 100644 --- a/fs/nfs/read.c +++ b/fs/nfs/read.c @@ -190,6 +190,7 @@ static int nfs_read_rpcsetup(struct nfs_page *req, struct nfs_read_data *data, data->args.pages = data->pagevec; data->args.count = count; data->args.context = get_nfs_open_context(req->wb_context); + data->args.lock_context = req->wb_lock_context; data->res.fattr = &data->fattr; data->res.count = count; diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 03df22822c4c..5eccea127cac 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -689,7 +689,9 @@ int nfs_flush_incompatible(struct file *file, struct page *page) req = nfs_page_find_request(page); if (req == NULL) return 0; - do_flush = req->wb_page != page || req->wb_context != ctx; + do_flush = req->wb_page != page || req->wb_context != ctx || + req->wb_lock_context->lockowner != current->files || + req->wb_lock_context->pid != current->tgid; nfs_release_request(req); if (!do_flush) return 0; @@ -813,6 +815,7 @@ static int nfs_write_rpcsetup(struct nfs_page *req, data->args.pages = data->pagevec; data->args.count = count; data->args.context = get_nfs_open_context(req->wb_context); + data->args.lock_context = req->wb_lock_context; data->args.stable = NFS_UNSTABLE; if (how & FLUSH_STABLE) { data->args.stable = NFS_DATA_SYNC; diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 77c2ae53431c..a9d802615082 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -72,13 +72,20 @@ struct nfs_access_entry { int mask; }; +struct nfs_lock_context { + atomic_t count; + struct list_head list; + struct nfs_open_context *open_context; + fl_owner_t lockowner; + pid_t pid; +}; + struct nfs4_state; struct nfs_open_context { - atomic_t count; + struct nfs_lock_context lock_context; struct path path; struct rpc_cred *cred; struct nfs4_state *state; - fl_owner_t lockowner; fmode_t mode; unsigned long flags; @@ -353,6 +360,8 @@ extern void nfs_setattr_update_inode(struct inode *inode, struct iattr *attr); extern struct nfs_open_context *get_nfs_open_context(struct nfs_open_context *ctx); extern void put_nfs_open_context(struct nfs_open_context *ctx); extern struct nfs_open_context *nfs_find_open_context(struct inode *inode, struct rpc_cred *cred, fmode_t mode); +extern struct nfs_lock_context *nfs_get_lock_context(struct nfs_open_context *ctx); +extern void nfs_put_lock_context(struct nfs_lock_context *l_ctx); extern u64 nfs_compat_user_ino64(u64 fileid); extern void nfs_fattr_init(struct nfs_fattr *fattr); diff --git a/include/linux/nfs_page.h b/include/linux/nfs_page.h index 3c60685d972b..f8b60e7f4c44 100644 --- a/include/linux/nfs_page.h +++ b/include/linux/nfs_page.h @@ -39,6 +39,7 @@ struct nfs_page { struct list_head wb_list; /* Defines state of page: */ struct page *wb_page; /* page to read in/write out */ struct nfs_open_context *wb_context; /* File state context info */ + struct nfs_lock_context *wb_lock_context; /* lock context info */ atomic_t wb_complete; /* i/os we're waiting for */ pgoff_t wb_index; /* Offset >> PAGE_CACHE_SHIFT */ unsigned int wb_offset, /* Offset & ~PAGE_CACHE_MASK */ diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index a319cb926abf..87202c7026e3 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -334,6 +334,7 @@ struct nfs4_delegreturnres { struct nfs_readargs { struct nfs_fh * fh; struct nfs_open_context *context; + struct nfs_lock_context *lock_context; __u64 offset; __u32 count; unsigned int pgbase; @@ -354,6 +355,7 @@ struct nfs_readres { struct nfs_writeargs { struct nfs_fh * fh; struct nfs_open_context *context; + struct nfs_lock_context *lock_context; __u64 offset; __u32 count; enum nfs3_stable_how stable; -- cgit v1.2.3 From daccbded7f153ec84a3baf3136052e41d0eab555 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Fri, 25 Jun 2010 18:11:43 -0400 Subject: NFSv4: Clean up for lockowner XDR encoding Signed-off-by: Trond Myklebust --- fs/nfs/nfs4xdr.c | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) (limited to 'fs') diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 873b62f209ea..49df05afdc64 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -202,14 +202,17 @@ static int nfs4_stat_to_errno(int); #define encode_link_maxsz (op_encode_hdr_maxsz + \ nfs4_name_maxsz) #define decode_link_maxsz (op_decode_hdr_maxsz + decode_change_info_maxsz) +#define encode_lockowner_maxsz (7) #define encode_lock_maxsz (op_encode_hdr_maxsz + \ 7 + \ - 1 + encode_stateid_maxsz + 8) + 1 + encode_stateid_maxsz + 1 + \ + encode_lockowner_maxsz) #define decode_lock_denied_maxsz \ (8 + decode_lockowner_maxsz) #define decode_lock_maxsz (op_decode_hdr_maxsz + \ decode_lock_denied_maxsz) -#define encode_lockt_maxsz (op_encode_hdr_maxsz + 12) +#define encode_lockt_maxsz (op_encode_hdr_maxsz + 5 + \ + encode_lockowner_maxsz) #define decode_lockt_maxsz (op_decode_hdr_maxsz + \ decode_lock_denied_maxsz) #define encode_locku_maxsz (op_encode_hdr_maxsz + 3 + \ @@ -1042,6 +1045,17 @@ static inline uint64_t nfs4_lock_length(struct file_lock *fl) return fl->fl_end - fl->fl_start + 1; } +static void encode_lockowner(struct xdr_stream *xdr, const struct nfs_lowner *lowner) +{ + __be32 *p; + + p = reserve_space(xdr, 28); + p = xdr_encode_hyper(p, lowner->clientid); + *p++ = cpu_to_be32(16); + p = xdr_encode_opaque_fixed(p, "lock id:", 8); + xdr_encode_hyper(p, lowner->id); +} + /* * opcode,type,reclaim,offset,length,new_lock_owner = 32 * open_seqid,open_stateid,lock_seqid,lock_owner.clientid, lock_owner.id = 40 @@ -1058,14 +1072,11 @@ static void encode_lock(struct xdr_stream *xdr, const struct nfs_lock_args *args p = xdr_encode_hyper(p, nfs4_lock_length(args->fl)); *p = cpu_to_be32(args->new_lock_owner); if (args->new_lock_owner){ - p = reserve_space(xdr, 4+NFS4_STATEID_SIZE+32); + p = reserve_space(xdr, 4+NFS4_STATEID_SIZE+4); *p++ = cpu_to_be32(args->open_seqid->sequence->counter); p = xdr_encode_opaque_fixed(p, args->open_stateid->data, NFS4_STATEID_SIZE); *p++ = cpu_to_be32(args->lock_seqid->sequence->counter); - p = xdr_encode_hyper(p, args->lock_owner.clientid); - *p++ = cpu_to_be32(16); - p = xdr_encode_opaque_fixed(p, "lock id:", 8); - xdr_encode_hyper(p, args->lock_owner.id); + encode_lockowner(xdr, &args->lock_owner); } else { p = reserve_space(xdr, NFS4_STATEID_SIZE+4); @@ -1080,15 +1091,12 @@ static void encode_lockt(struct xdr_stream *xdr, const struct nfs_lockt_args *ar { __be32 *p; - p = reserve_space(xdr, 52); + p = reserve_space(xdr, 24); *p++ = cpu_to_be32(OP_LOCKT); *p++ = cpu_to_be32(nfs4_lock_type(args->fl, 0)); p = xdr_encode_hyper(p, args->fl->fl_start); p = xdr_encode_hyper(p, nfs4_lock_length(args->fl)); - p = xdr_encode_hyper(p, args->lock_owner.clientid); - *p++ = cpu_to_be32(16); - p = xdr_encode_opaque_fixed(p, "lock id:", 8); - xdr_encode_hyper(p, args->lock_owner.id); + encode_lockowner(xdr, &args->lock_owner); hdr->nops++; hdr->replen += decode_lockt_maxsz; } -- cgit v1.2.3 From d3c7b7ccc199ee564177ee914c04771d6bc00295 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Thu, 1 Jul 2010 12:49:01 -0400 Subject: NFSv4: Add support for the RELEASE_LOCKOWNER operation This is needed by NFSv4.0 servers in order to keep the number of locking stateids at a manageable level. Signed-off-by: Trond Myklebust --- fs/nfs/nfs4_fs.h | 1 + fs/nfs/nfs4proc.c | 28 +++++++++++++++++++++++++ fs/nfs/nfs4state.c | 2 ++ fs/nfs/nfs4xdr.c | 55 +++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/nfs4.h | 1 + include/linux/nfs_xdr.h | 4 ++++ 6 files changed, 91 insertions(+) (limited to 'fs') diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index cee871471e8d..deaf37f5a7a9 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -236,6 +236,7 @@ extern int nfs4_open_revalidate(struct inode *, struct dentry *, int, struct nam extern int nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle); extern int nfs4_proc_fs_locations(struct inode *dir, const struct qstr *name, struct nfs4_fs_locations *fs_locations, struct page *page); +extern void nfs4_release_lockowner(const struct nfs4_lock_state *); #if defined(CONFIG_NFS_V4_1) static inline struct nfs4_session *nfs4_get_session(const struct nfs_server *server) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index de9ff1505a24..5d3e8a2db99f 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -4414,6 +4414,34 @@ out: return err; } +static void nfs4_release_lockowner_release(void *calldata) +{ + kfree(calldata); +} + +const struct rpc_call_ops nfs4_release_lockowner_ops = { + .rpc_release = nfs4_release_lockowner_release, +}; + +void nfs4_release_lockowner(const struct nfs4_lock_state *lsp) +{ + struct nfs_server *server = lsp->ls_state->owner->so_server; + struct nfs_release_lockowner_args *args; + struct rpc_message msg = { + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_RELEASE_LOCKOWNER], + }; + + if (server->nfs_client->cl_mvops->minor_version != 0) + return; + args = kmalloc(sizeof(*args), GFP_NOFS); + if (!args) + return; + args->lock_owner.clientid = server->nfs_client->cl_clientid; + args->lock_owner.id = lsp->ls_id.id; + msg.rpc_argp = args; + rpc_call_async(server->client, &msg, 0, &nfs4_release_lockowner_ops, args); +} + #define XATTR_NAME_NFSV4_ACL "system.nfs4_acl" int nfs4_setxattr(struct dentry *dentry, const char *key, const void *buf, diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 13e17e32e3e4..13a4f27e7271 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -701,6 +701,8 @@ void nfs4_put_lock_state(struct nfs4_lock_state *lsp) if (list_empty(&state->lock_states)) clear_bit(LK_STATE_IN_USE, &state->flags); spin_unlock(&state->state_lock); + if (lsp->ls_flags & NFS_LOCK_INITIALIZED) + nfs4_release_lockowner(lsp); nfs4_free_lock_state(lsp); } diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 49df05afdc64..15185c2abd11 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -220,6 +220,11 @@ static int nfs4_stat_to_errno(int); 4) #define decode_locku_maxsz (op_decode_hdr_maxsz + \ decode_stateid_maxsz) +#define encode_release_lockowner_maxsz \ + (op_encode_hdr_maxsz + \ + encode_lockowner_maxsz) +#define decode_release_lockowner_maxsz \ + (op_decode_hdr_maxsz) #define encode_access_maxsz (op_encode_hdr_maxsz + 1) #define decode_access_maxsz (op_decode_hdr_maxsz + 2) #define encode_symlink_maxsz (op_encode_hdr_maxsz + \ @@ -474,6 +479,12 @@ static int nfs4_stat_to_errno(int); decode_sequence_maxsz + \ decode_putfh_maxsz + \ decode_locku_maxsz) +#define NFS4_enc_release_lockowner_sz \ + (compound_encode_hdr_maxsz + \ + encode_lockowner_maxsz) +#define NFS4_dec_release_lockowner_sz \ + (compound_decode_hdr_maxsz + \ + decode_lockowner_maxsz) #define NFS4_enc_access_sz (compound_encode_hdr_maxsz + \ encode_sequence_maxsz + \ encode_putfh_maxsz + \ @@ -1116,6 +1127,17 @@ static void encode_locku(struct xdr_stream *xdr, const struct nfs_locku_args *ar hdr->replen += decode_locku_maxsz; } +static void encode_release_lockowner(struct xdr_stream *xdr, const struct nfs_lowner *lowner, struct compound_hdr *hdr) +{ + __be32 *p; + + p = reserve_space(xdr, 4); + *p = cpu_to_be32(OP_RELEASE_LOCKOWNER); + encode_lockowner(xdr, lowner); + hdr->nops++; + hdr->replen += decode_release_lockowner_maxsz; +} + static void encode_lookup(struct xdr_stream *xdr, const struct qstr *name, struct compound_hdr *hdr) { int len = name->len; @@ -2056,6 +2078,20 @@ static int nfs4_xdr_enc_locku(struct rpc_rqst *req, __be32 *p, struct nfs_locku_ return 0; } +static int nfs4_xdr_enc_release_lockowner(struct rpc_rqst *req, __be32 *p, struct nfs_release_lockowner_args *args) +{ + struct xdr_stream xdr; + struct compound_hdr hdr = { + .minorversion = 0, + }; + + xdr_init_encode(&xdr, &req->rq_snd_buf, p); + encode_compound_hdr(&xdr, req, &hdr); + encode_release_lockowner(&xdr, &args->lock_owner, &hdr); + encode_nops(&hdr); + return 0; +} + /* * Encode a READLINK request */ @@ -3981,6 +4017,11 @@ static int decode_locku(struct xdr_stream *xdr, struct nfs_locku_res *res) return status; } +static int decode_release_lockowner(struct xdr_stream *xdr) +{ + return decode_op_hdr(xdr, OP_RELEASE_LOCKOWNER); +} + static int decode_lookup(struct xdr_stream *xdr) { return decode_op_hdr(xdr, OP_LOOKUP); @@ -5267,6 +5308,19 @@ out: return status; } +static int nfs4_xdr_dec_release_lockowner(struct rpc_rqst *rqstp, __be32 *p, void *dummy) +{ + struct xdr_stream xdr; + struct compound_hdr hdr; + int status; + + xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); + status = decode_compound_hdr(&xdr, &hdr); + if (!status) + status = decode_release_lockowner(&xdr); + return status; +} + /* * Decode READLINK response */ @@ -5874,6 +5928,7 @@ struct rpc_procinfo nfs4_procedures[] = { PROC(GETACL, enc_getacl, dec_getacl), PROC(SETACL, enc_setacl, dec_setacl), PROC(FS_LOCATIONS, enc_fs_locations, dec_fs_locations), + PROC(RELEASE_LOCKOWNER, enc_release_lockowner, dec_release_lockowner), #if defined(CONFIG_NFS_V4_1) PROC(EXCHANGE_ID, enc_exchange_id, dec_exchange_id), PROC(CREATE_SESSION, enc_create_session, dec_create_session), diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h index 9b8299af3741..07e40c625972 100644 --- a/include/linux/nfs4.h +++ b/include/linux/nfs4.h @@ -523,6 +523,7 @@ enum { NFSPROC4_CLNT_GETACL, NFSPROC4_CLNT_SETACL, NFSPROC4_CLNT_FS_LOCATIONS, + NFSPROC4_CLNT_RELEASE_LOCKOWNER, /* nfs41 */ NFSPROC4_CLNT_EXCHANGE_ID, diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 87202c7026e3..fc461926c412 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -315,6 +315,10 @@ struct nfs_lockt_res { struct nfs4_sequence_res seq_res; }; +struct nfs_release_lockowner_args { + struct nfs_lowner lock_owner; +}; + struct nfs4_delegreturnargs { const struct nfs_fh *fhandle; const nfs4_stateid *stateid; -- cgit v1.2.3 From 77041ed9b49a9e10f374bfa6e482d30ee7a3d46e Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Thu, 1 Jul 2010 12:49:11 -0400 Subject: NFSv4: Ensure the lockowners are labelled using the fl_owner and/or fl_pid flock locks want to be labelled using the process pid, while posix locks want to be labelled using the fl_owner. Signed-off-by: Trond Myklebust --- fs/nfs/nfs4_fs.h | 15 +++++++++++++-- fs/nfs/nfs4proc.c | 2 +- fs/nfs/nfs4state.c | 45 +++++++++++++++++++++++++++++++++++---------- fs/nfs/nfs4xdr.c | 2 +- 4 files changed, 50 insertions(+), 14 deletions(-) (limited to 'fs') diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index deaf37f5a7a9..311e15cc8af0 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -142,10 +142,20 @@ enum { * LOCK: one nfs4_state (LOCK) to hold the lock stateid nfs4_state(OPEN) */ +struct nfs4_lock_owner { + unsigned int lo_type; +#define NFS4_ANY_LOCK_TYPE (0U) +#define NFS4_FLOCK_LOCK_TYPE (1U << 0) +#define NFS4_POSIX_LOCK_TYPE (1U << 1) + union { + fl_owner_t posix_owner; + pid_t flock_owner; + } lo_u; +}; + struct nfs4_lock_state { struct list_head ls_locks; /* Other lock stateids */ struct nfs4_state * ls_state; /* Pointer to open state */ - fl_owner_t ls_owner; /* POSIX lock owner */ #define NFS_LOCK_INITIALIZED 1 int ls_flags; struct nfs_seqid_counter ls_seqid; @@ -153,6 +163,7 @@ struct nfs4_lock_state { struct nfs_unique_id ls_id; nfs4_stateid ls_stateid; atomic_t ls_count; + struct nfs4_lock_owner ls_owner; }; /* bits for nfs4_state->flags */ @@ -310,7 +321,7 @@ extern void nfs41_handle_sequence_flag_errors(struct nfs_client *clp, u32 flags) extern void nfs41_handle_recall_slot(struct nfs_client *clp); extern void nfs4_put_lock_state(struct nfs4_lock_state *lsp); extern int nfs4_set_lock_state(struct nfs4_state *state, struct file_lock *fl); -extern void nfs4_copy_stateid(nfs4_stateid *, struct nfs4_state *, fl_owner_t); +extern void nfs4_copy_stateid(nfs4_stateid *, struct nfs4_state *, fl_owner_t, pid_t); extern struct nfs_seqid *nfs_alloc_seqid(struct nfs_seqid_counter *counter, gfp_t gfp_mask); extern int nfs_wait_on_sequence(struct nfs_seqid *seqid, struct rpc_task *task); diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 5d3e8a2db99f..d6413b48b057 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -1767,7 +1767,7 @@ static int _nfs4_do_setattr(struct inode *inode, struct rpc_cred *cred, if (nfs4_copy_delegation_stateid(&arg.stateid, inode)) { /* Use that stateid */ } else if (state != NULL) { - nfs4_copy_stateid(&arg.stateid, state, current->files); + nfs4_copy_stateid(&arg.stateid, state, current->files, current->tgid); } else memcpy(&arg.stateid, &zero_stateid, sizeof(arg.stateid)); diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 13a4f27e7271..3e2f19b04c06 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -602,12 +602,21 @@ void nfs4_close_sync(struct path *path, struct nfs4_state *state, fmode_t fmode) * that is compatible with current->files */ static struct nfs4_lock_state * -__nfs4_find_lock_state(struct nfs4_state *state, fl_owner_t fl_owner) +__nfs4_find_lock_state(struct nfs4_state *state, fl_owner_t fl_owner, pid_t fl_pid, unsigned int type) { struct nfs4_lock_state *pos; list_for_each_entry(pos, &state->lock_states, ls_locks) { - if (pos->ls_owner != fl_owner) + if (type != NFS4_ANY_LOCK_TYPE && pos->ls_owner.lo_type != type) continue; + switch (pos->ls_owner.lo_type) { + case NFS4_POSIX_LOCK_TYPE: + if (pos->ls_owner.lo_u.posix_owner != fl_owner) + continue; + break; + case NFS4_FLOCK_LOCK_TYPE: + if (pos->ls_owner.lo_u.flock_owner != fl_pid) + continue; + } atomic_inc(&pos->ls_count); return pos; } @@ -619,7 +628,7 @@ __nfs4_find_lock_state(struct nfs4_state *state, fl_owner_t fl_owner) * exists, return an uninitialized one. * */ -static struct nfs4_lock_state *nfs4_alloc_lock_state(struct nfs4_state *state, fl_owner_t fl_owner) +static struct nfs4_lock_state *nfs4_alloc_lock_state(struct nfs4_state *state, fl_owner_t fl_owner, pid_t fl_pid, unsigned int type) { struct nfs4_lock_state *lsp; struct nfs_client *clp = state->owner->so_server->nfs_client; @@ -633,7 +642,18 @@ static struct nfs4_lock_state *nfs4_alloc_lock_state(struct nfs4_state *state, f lsp->ls_seqid.sequence = &lsp->ls_sequence; atomic_set(&lsp->ls_count, 1); lsp->ls_state = state; - lsp->ls_owner = fl_owner; + lsp->ls_owner.lo_type = type; + switch (lsp->ls_owner.lo_type) { + case NFS4_FLOCK_LOCK_TYPE: + lsp->ls_owner.lo_u.flock_owner = fl_pid; + break; + case NFS4_POSIX_LOCK_TYPE: + lsp->ls_owner.lo_u.posix_owner = fl_owner; + break; + default: + kfree(lsp); + return NULL; + } spin_lock(&clp->cl_lock); nfs_alloc_unique_id(&clp->cl_lockowner_id, &lsp->ls_id, 1, 64); spin_unlock(&clp->cl_lock); @@ -657,13 +677,13 @@ static void nfs4_free_lock_state(struct nfs4_lock_state *lsp) * exists, return an uninitialized one. * */ -static struct nfs4_lock_state *nfs4_get_lock_state(struct nfs4_state *state, fl_owner_t owner) +static struct nfs4_lock_state *nfs4_get_lock_state(struct nfs4_state *state, fl_owner_t owner, pid_t pid, unsigned int type) { struct nfs4_lock_state *lsp, *new = NULL; for(;;) { spin_lock(&state->state_lock); - lsp = __nfs4_find_lock_state(state, owner); + lsp = __nfs4_find_lock_state(state, owner, pid, type); if (lsp != NULL) break; if (new != NULL) { @@ -674,7 +694,7 @@ static struct nfs4_lock_state *nfs4_get_lock_state(struct nfs4_state *state, fl_ break; } spin_unlock(&state->state_lock); - new = nfs4_alloc_lock_state(state, owner); + new = nfs4_alloc_lock_state(state, owner, pid, type); if (new == NULL) return NULL; } @@ -730,7 +750,12 @@ int nfs4_set_lock_state(struct nfs4_state *state, struct file_lock *fl) if (fl->fl_ops != NULL) return 0; - lsp = nfs4_get_lock_state(state, fl->fl_owner); + if (fl->fl_flags & FL_POSIX) + lsp = nfs4_get_lock_state(state, fl->fl_owner, 0, NFS4_POSIX_LOCK_TYPE); + else if (fl->fl_flags & FL_FLOCK) + lsp = nfs4_get_lock_state(state, 0, fl->fl_pid, NFS4_FLOCK_LOCK_TYPE); + else + return -EINVAL; if (lsp == NULL) return -ENOMEM; fl->fl_u.nfs4_fl.owner = lsp; @@ -742,7 +767,7 @@ int nfs4_set_lock_state(struct nfs4_state *state, struct file_lock *fl) * Byte-range lock aware utility to initialize the stateid of read/write * requests. */ -void nfs4_copy_stateid(nfs4_stateid *dst, struct nfs4_state *state, fl_owner_t fl_owner) +void nfs4_copy_stateid(nfs4_stateid *dst, struct nfs4_state *state, fl_owner_t fl_owner, pid_t fl_pid) { struct nfs4_lock_state *lsp; int seq; @@ -755,7 +780,7 @@ void nfs4_copy_stateid(nfs4_stateid *dst, struct nfs4_state *state, fl_owner_t f return; spin_lock(&state->state_lock); - lsp = __nfs4_find_lock_state(state, fl_owner); + lsp = __nfs4_find_lock_state(state, fl_owner, fl_pid, NFS4_ANY_LOCK_TYPE); if (lsp != NULL && (lsp->ls_flags & NFS_LOCK_INITIALIZED) != 0) memcpy(dst, &lsp->ls_stateid, sizeof(*dst)); spin_unlock(&state->state_lock); diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 15185c2abd11..257c1811feb4 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -1361,7 +1361,7 @@ static void encode_stateid(struct xdr_stream *xdr, const struct nfs_open_context p = reserve_space(xdr, NFS4_STATEID_SIZE); if (ctx->state != NULL) { - nfs4_copy_stateid(&stateid, ctx->state, l_ctx->lockowner); + nfs4_copy_stateid(&stateid, ctx->state, l_ctx->lockowner, l_ctx->pid); xdr_encode_opaque_fixed(p, stateid.data, NFS4_STATEID_SIZE); } else xdr_encode_opaque_fixed(p, zero_stateid.data, NFS4_STATEID_SIZE); -- cgit v1.2.3 From 674b2222920012244ca59978b356b25412a8dcc7 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 13 Jul 2010 13:34:59 +0200 Subject: nfs: include space for the NUL in root path In root_nfs_name() it does the following: if (strlen(buf) + strlen(cp) > NFS_MAXPATHLEN) { printk(KERN_ERR "Root-NFS: Pathname for remote directory too long.\n"); return -1; } sprintf(nfs_export_path, buf, cp); In the original code if (strlen(buf) + strlen(cp) == NFS_MAXPATHLEN) then the sprintf() would lead to an overflow. Generally the rest of the code assumes that the path can have NFS_MAXPATHLEN (1024) characters and a NUL terminator so the fix is to add space to the nfs_export_path[] buffer. Signed-off-by: Dan Carpenter Signed-off-by: Trond Myklebust --- fs/nfs/nfsroot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/nfs/nfsroot.c b/fs/nfs/nfsroot.c index 6bd19d843af7..df101d9f546a 100644 --- a/fs/nfs/nfsroot.c +++ b/fs/nfs/nfsroot.c @@ -105,7 +105,7 @@ static char nfs_root_name[256] __initdata = ""; static __be32 servaddr __initdata = 0; /* Name of directory to mount */ -static char nfs_export_path[NFS_MAXPATHLEN] __initdata = { 0, }; +static char nfs_export_path[NFS_MAXPATHLEN + 1] __initdata = { 0, }; /* NFS-related data */ static struct nfs_mount_data nfs_data __initdata = { 0, };/* NFS mount info */ -- cgit v1.2.3 From b608b283a962caaa280756bc8563016a71712acf Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Fri, 30 Jul 2010 15:31:54 -0400 Subject: NFS: kswapd must not block in nfs_release_page See https://bugzilla.kernel.org/show_bug.cgi?id=16056 If other processes are blocked waiting for kswapd to free up some memory so that they can make progress, then we cannot allow kswapd to block on those processes. Signed-off-by: Trond Myklebust Cc: stable@kernel.org --- fs/nfs/file.c | 13 +++++++++++-- fs/nfs/write.c | 4 ++-- include/linux/nfs_fs.h | 1 + 3 files changed, 14 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/nfs/file.c b/fs/nfs/file.c index 36a5e74f51b4..f036153d9f50 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -493,11 +494,19 @@ static void nfs_invalidate_page(struct page *page, unsigned long offset) */ static int nfs_release_page(struct page *page, gfp_t gfp) { + struct address_space *mapping = page->mapping; + dfprintk(PAGECACHE, "NFS: release_page(%p)\n", page); /* Only do I/O if gfp is a superset of GFP_KERNEL */ - if ((gfp & GFP_KERNEL) == GFP_KERNEL) - nfs_wb_page(page->mapping->host, page); + if (mapping && (gfp & GFP_KERNEL) == GFP_KERNEL) { + int how = FLUSH_SYNC; + + /* Don't let kswapd deadlock waiting for OOM RPC calls */ + if (current_is_kswapd()) + how = 0; + nfs_commit_inode(mapping->host, how); + } /* If PagePrivate() is set, then the page is not freeable */ if (PagePrivate(page)) return 0; diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 91679e2631ee..0a6c65a1f9d7 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -1379,7 +1379,7 @@ static const struct rpc_call_ops nfs_commit_ops = { .rpc_release = nfs_commit_release, }; -static int nfs_commit_inode(struct inode *inode, int how) +int nfs_commit_inode(struct inode *inode, int how) { LIST_HEAD(head); int may_wait = how & FLUSH_SYNC; @@ -1443,7 +1443,7 @@ out_mark_dirty: return ret; } #else -static int nfs_commit_inode(struct inode *inode, int how) +int nfs_commit_inode(struct inode *inode, int how) { return 0; } diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 77c2ae53431c..f6e2455f13d1 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -493,6 +493,7 @@ extern int nfs_wb_all(struct inode *inode); extern int nfs_wb_page(struct inode *inode, struct page* page); extern int nfs_wb_page_cancel(struct inode *inode, struct page* page); #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) +extern int nfs_commit_inode(struct inode *, int); extern struct nfs_write_data *nfs_commitdata_alloc(void); extern void nfs_commit_free(struct nfs_write_data *wdata); #endif -- cgit v1.2.3 From cfb506e1d330387dfaf334dd493b3773d388863d Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Fri, 30 Jul 2010 15:31:57 -0400 Subject: NFS: Ensure that writepage respects the nonblock flag Signed-off-by: Trond Myklebust --- fs/nfs/write.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) (limited to 'fs') diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 0a6c65a1f9d7..bb72ad34d51d 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -222,7 +222,7 @@ static void nfs_end_page_writeback(struct page *page) clear_bdi_congested(&nfss->backing_dev_info, BLK_RW_ASYNC); } -static struct nfs_page *nfs_find_and_lock_request(struct page *page) +static struct nfs_page *nfs_find_and_lock_request(struct page *page, bool nonblock) { struct inode *inode = page->mapping->host; struct nfs_page *req; @@ -241,7 +241,10 @@ static struct nfs_page *nfs_find_and_lock_request(struct page *page) * request as dirty (in which case we don't care). */ spin_unlock(&inode->i_lock); - ret = nfs_wait_on_request(req); + if (!nonblock) + ret = nfs_wait_on_request(req); + else + ret = -EAGAIN; nfs_release_request(req); if (ret != 0) return ERR_PTR(ret); @@ -256,12 +259,12 @@ static struct nfs_page *nfs_find_and_lock_request(struct page *page) * May return an error if the user signalled nfs_wait_on_request(). */ static int nfs_page_async_flush(struct nfs_pageio_descriptor *pgio, - struct page *page) + struct page *page, bool nonblock) { struct nfs_page *req; int ret = 0; - req = nfs_find_and_lock_request(page); + req = nfs_find_and_lock_request(page, nonblock); if (!req) goto out; ret = PTR_ERR(req); @@ -283,12 +286,20 @@ out: static int nfs_do_writepage(struct page *page, struct writeback_control *wbc, struct nfs_pageio_descriptor *pgio) { struct inode *inode = page->mapping->host; + int ret; nfs_inc_stats(inode, NFSIOS_VFSWRITEPAGE); nfs_add_stats(inode, NFSIOS_WRITEPAGES, 1); nfs_pageio_cond_complete(pgio, page->index); - return nfs_page_async_flush(pgio, page); + ret = nfs_page_async_flush(pgio, page, + wbc->sync_mode == WB_SYNC_NONE || + wbc->nonblocking != 0); + if (ret == -EAGAIN) { + redirty_page_for_writepage(wbc, page); + ret = 0; + } + return ret; } /* @@ -1546,7 +1557,7 @@ int nfs_migrate_page(struct address_space *mapping, struct page *newpage, nfs_fscache_release_page(page, GFP_KERNEL); - req = nfs_find_and_lock_request(page); + req = nfs_find_and_lock_request(page, false); ret = PTR_ERR(req); if (IS_ERR(req)) goto out; -- cgit v1.2.3 From 51c20fcced5badee0e2021c6c89f44aa3cbd72aa Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 30 Jul 2010 15:25:19 +0100 Subject: CIFS: Remove __exit mark from cifs_exit_dns_resolver() Remove the __exit mark from cifs_exit_dns_resolver() as it's called by the module init routine in case of error, and so may have been discarded during linkage. Signed-off-by: David Howells Acked-by: Jeff Layton Signed-off-by: Linus Torvalds --- fs/cifs/dns_resolve.c | 2 +- fs/cifs/dns_resolve.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/cifs/dns_resolve.c b/fs/cifs/dns_resolve.c index 49315cbf742d..853a968e82d7 100644 --- a/fs/cifs/dns_resolve.c +++ b/fs/cifs/dns_resolve.c @@ -227,7 +227,7 @@ failed_put_cred: return ret; } -void __exit cifs_exit_dns_resolver(void) +void cifs_exit_dns_resolver(void) { key_revoke(dns_resolver_cache->thread_keyring); unregister_key_type(&key_type_dns_resolver); diff --git a/fs/cifs/dns_resolve.h b/fs/cifs/dns_resolve.h index 26b9eaa9f5ee..5d7f291df162 100644 --- a/fs/cifs/dns_resolve.h +++ b/fs/cifs/dns_resolve.h @@ -25,7 +25,7 @@ #ifdef __KERNEL__ extern int __init cifs_init_dns_resolver(void); -extern void __exit cifs_exit_dns_resolver(void); +extern void cifs_exit_dns_resolver(void); extern int dns_resolve_server_name_to_ip(const char *unc, char **ip_addr); #endif /* KERNEL */ -- cgit v1.2.3 From 437f88cc031ffe7f37f3e705367f4fe1f4be8b0f Mon Sep 17 00:00:00 2001 From: Eric Sandeen Date: Sun, 1 Aug 2010 17:33:29 -0400 Subject: ext4: fix freeze deadlock under IO Commit 6b0310fbf087ad6 caused a regression resulting in deadlocks when freezing a filesystem which had active IO; the vfs_check_frozen level (SB_FREEZE_WRITE) did not let the freeze-related IO syncing through. Duh. Changing the test to FREEZE_TRANS should let the normal freeze syncing get through the fs, but still block any transactions from starting once the fs is completely frozen. I tested this by running fsstress in the background while periodically snapshotting the fs and running fsck on the result. I ran into occasional deadlocks, but different ones. I think this is a fine fix for the problem at hand, and the other deadlocky things will need more investigation. Reported-by: Phillip Susi Signed-off-by: Eric Sandeen Signed-off-by: "Theodore Ts'o" --- fs/ext4/super.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/ext4/super.c b/fs/ext4/super.c index e046eba24782..282a2704be23 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -241,7 +241,7 @@ handle_t *ext4_journal_start_sb(struct super_block *sb, int nblocks) if (sb->s_flags & MS_RDONLY) return ERR_PTR(-EROFS); - vfs_check_frozen(sb, SB_FREEZE_WRITE); + vfs_check_frozen(sb, SB_FREEZE_TRANS); /* Special case here: if the journal has aborted behind our * backs (eg. EIO in the commit thread), then we still need to * take the FS itself readonly cleanly. */ @@ -3608,7 +3608,7 @@ int ext4_force_commit(struct super_block *sb) journal = EXT4_SB(sb)->s_journal; if (journal) { - vfs_check_frozen(sb, SB_FREEZE_WRITE); + vfs_check_frozen(sb, SB_FREEZE_TRANS); ret = ext4_journal_force_commit(journal); } -- cgit v1.2.3 From ca0e05e4b15193aeba72b995e90de990db7f8304 Mon Sep 17 00:00:00 2001 From: Dmitry Monakhov Date: Sun, 1 Aug 2010 17:48:36 -0400 Subject: ext4: force block allocation on quota_off Perform full sync procedure so that any delayed allocation blocks are allocated so quota will be consistent. Signed-off-by: Dmitry Monakhov Signed-off-by: "Theodore Ts'o" --- fs/ext4/super.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 282a2704be23..3e3f6484c223 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -1118,6 +1118,7 @@ static int ext4_mark_dquot_dirty(struct dquot *dquot); static int ext4_write_info(struct super_block *sb, int type); static int ext4_quota_on(struct super_block *sb, int type, int format_id, char *path); +static int ext4_quota_off(struct super_block *sb, int type); static int ext4_quota_on_mount(struct super_block *sb, int type); static ssize_t ext4_quota_read(struct super_block *sb, int type, char *data, size_t len, loff_t off); @@ -1139,7 +1140,7 @@ static const struct dquot_operations ext4_quota_operations = { static const struct quotactl_ops ext4_qctl_operations = { .quota_on = ext4_quota_on, - .quota_off = dquot_quota_off, + .quota_off = ext4_quota_off, .quota_sync = dquot_quota_sync, .get_info = dquot_get_dqinfo, .set_info = dquot_set_dqinfo, @@ -4098,6 +4099,18 @@ static int ext4_quota_on(struct super_block *sb, int type, int format_id, return err; } +static int ext4_quota_off(struct super_block *sb, int type) +{ + /* Force all delayed allocation blocks to be allocated */ + if (test_opt(sb, DELALLOC)) { + down_read(&sb->s_umount); + sync_filesystem(sb); + up_read(&sb->s_umount); + } + + return dquot_quota_off(sb, type); +} + /* Read data from quotafile - avoid pagecache and such because we cannot afford * acquiring the locks... As quota files are never truncated and quota code * itself serializes the operations (and noone else should touch the files) -- cgit v1.2.3 From 77a63f3d1e0a3e7ede8d10f569e8481b13ff47c5 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sun, 1 Aug 2010 13:40:40 -0400 Subject: NFS: Fix a typo in include/linux/nfs_fs.h nfs_commit_inode() needs to be defined irrespectively of whether or not we are supporting NFSv3 and NFSv4. Allow the compiler to optimise away code in the NFSv2-only case by converting it into an inlined stub function. Reported-and-tested-by: Ingo Molnar Signed-off-by: Trond Myklebust Signed-off-by: Linus Torvalds --- fs/nfs/write.c | 5 ----- include/linux/nfs_fs.h | 6 ++++++ 2 files changed, 6 insertions(+), 5 deletions(-) (limited to 'fs') diff --git a/fs/nfs/write.c b/fs/nfs/write.c index bb72ad34d51d..9f81bdd91c55 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c @@ -1454,11 +1454,6 @@ out_mark_dirty: return ret; } #else -int nfs_commit_inode(struct inode *inode, int how) -{ - return 0; -} - static int nfs_commit_unstable_pages(struct inode *inode, struct writeback_control *wbc) { return 0; diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index f6e2455f13d1..bad4d121b16e 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -496,6 +496,12 @@ extern int nfs_wb_page_cancel(struct inode *inode, struct page* page); extern int nfs_commit_inode(struct inode *, int); extern struct nfs_write_data *nfs_commitdata_alloc(void); extern void nfs_commit_free(struct nfs_write_data *wdata); +#else +static inline int +nfs_commit_inode(struct inode *inode, int how) +{ + return 0; +} #endif static inline int -- cgit v1.2.3 From 84d9509234c46481aded977193fcf23f892d715f Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Tue, 25 May 2010 16:46:24 -0700 Subject: ceph: request FILE_LAZYIO cap when LAZY file mode is set Also clean up the file flags -> file mode -> wanted caps functions while we're at it. This resyncs this file with userspace. Signed-off-by: Sage Weil --- fs/ceph/ceph_fs.c | 50 ++++++++++++++++++++++++-------------------------- fs/ceph/ceph_fs.h | 3 ++- 2 files changed, 26 insertions(+), 27 deletions(-) (limited to 'fs') diff --git a/fs/ceph/ceph_fs.c b/fs/ceph/ceph_fs.c index 79d76bc4303f..3ac6cc7c1156 100644 --- a/fs/ceph/ceph_fs.c +++ b/fs/ceph/ceph_fs.c @@ -29,46 +29,44 @@ int ceph_file_layout_is_valid(const struct ceph_file_layout *layout) int ceph_flags_to_mode(int flags) { + int mode; + #ifdef O_DIRECTORY /* fixme */ if ((flags & O_DIRECTORY) == O_DIRECTORY) return CEPH_FILE_MODE_PIN; #endif + if ((flags & O_APPEND) == O_APPEND) + flags |= O_WRONLY; + + if ((flags & O_ACCMODE) == O_RDWR) + mode = CEPH_FILE_MODE_RDWR; + else if ((flags & O_ACCMODE) == O_WRONLY) + mode = CEPH_FILE_MODE_WR; + else + mode = CEPH_FILE_MODE_RD; + #ifdef O_LAZY if (flags & O_LAZY) - return CEPH_FILE_MODE_LAZY; + mode |= CEPH_FILE_MODE_LAZY; #endif - if ((flags & O_APPEND) == O_APPEND) - flags |= O_WRONLY; - flags &= O_ACCMODE; - if ((flags & O_RDWR) == O_RDWR) - return CEPH_FILE_MODE_RDWR; - if ((flags & O_WRONLY) == O_WRONLY) - return CEPH_FILE_MODE_WR; - return CEPH_FILE_MODE_RD; + return mode; } int ceph_caps_for_mode(int mode) { - switch (mode) { - case CEPH_FILE_MODE_PIN: - return CEPH_CAP_PIN; - case CEPH_FILE_MODE_RD: - return CEPH_CAP_PIN | CEPH_CAP_FILE_SHARED | + int caps = CEPH_CAP_PIN; + + if (mode & CEPH_FILE_MODE_RD) + caps |= CEPH_CAP_FILE_SHARED | CEPH_CAP_FILE_RD | CEPH_CAP_FILE_CACHE; - case CEPH_FILE_MODE_RDWR: - return CEPH_CAP_PIN | CEPH_CAP_FILE_SHARED | - CEPH_CAP_FILE_EXCL | - CEPH_CAP_FILE_RD | CEPH_CAP_FILE_CACHE | - CEPH_CAP_FILE_WR | CEPH_CAP_FILE_BUFFER | - CEPH_CAP_AUTH_SHARED | CEPH_CAP_AUTH_EXCL | - CEPH_CAP_XATTR_SHARED | CEPH_CAP_XATTR_EXCL; - case CEPH_FILE_MODE_WR: - return CEPH_CAP_PIN | CEPH_CAP_FILE_SHARED | - CEPH_CAP_FILE_EXCL | + if (mode & CEPH_FILE_MODE_WR) + caps |= CEPH_CAP_FILE_EXCL | CEPH_CAP_FILE_WR | CEPH_CAP_FILE_BUFFER | CEPH_CAP_AUTH_SHARED | CEPH_CAP_AUTH_EXCL | CEPH_CAP_XATTR_SHARED | CEPH_CAP_XATTR_EXCL; - } - return 0; + if (mode & CEPH_FILE_MODE_LAZY) + caps |= CEPH_CAP_FILE_LAZYIO; + + return caps; } diff --git a/fs/ceph/ceph_fs.h b/fs/ceph/ceph_fs.h index 2fa992eaf7da..c2e4c61a99a3 100644 --- a/fs/ceph/ceph_fs.h +++ b/fs/ceph/ceph_fs.h @@ -563,7 +563,8 @@ int ceph_flags_to_mode(int flags); CEPH_CAP_FILE_EXCL) #define CEPH_CAP_ANY_WR (CEPH_CAP_ANY_EXCL | CEPH_CAP_ANY_FILE_WR) #define CEPH_CAP_ANY (CEPH_CAP_ANY_RD | CEPH_CAP_ANY_EXCL | \ - CEPH_CAP_ANY_FILE_WR | CEPH_CAP_PIN) + CEPH_CAP_ANY_FILE_WR | CEPH_CAP_FILE_LAZYIO | \ + CEPH_CAP_PIN) #define CEPH_CAP_LOCKS (CEPH_LOCK_IFILE | CEPH_LOCK_IAUTH | CEPH_LOCK_ILINK | \ CEPH_LOCK_IXATTR) -- cgit v1.2.3 From 8c6e9229fc1989cf263a6fcd4ff406d7f473f966 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Fri, 16 Apr 2010 09:53:43 -0700 Subject: ceph: add LAZYIO ioctl to mark a file description for lazy consistency Allow an application to mark a file descriptor for lazy file consistency semantics, allowing buffered reads and writes when multiple clients are accessing the same file. Signed-off-by: Sage Weil --- fs/ceph/ioctl.c | 24 ++++++++++++++++++++++++ fs/ceph/ioctl.h | 2 ++ 2 files changed, 26 insertions(+) (limited to 'fs') diff --git a/fs/ceph/ioctl.c b/fs/ceph/ioctl.c index d085f07756b4..76e307d2aba1 100644 --- a/fs/ceph/ioctl.c +++ b/fs/ceph/ioctl.c @@ -143,6 +143,27 @@ static long ceph_ioctl_get_dataloc(struct file *file, void __user *arg) return 0; } +static long ceph_ioctl_lazyio(struct file *file) +{ + struct ceph_file_info *fi = file->private_data; + struct inode *inode = file->f_dentry->d_inode; + struct ceph_inode_info *ci = ceph_inode(inode); + + if ((fi->fmode & CEPH_FILE_MODE_LAZY) == 0) { + spin_lock(&inode->i_lock); + ci->i_nr_by_mode[fi->fmode]--; + fi->fmode |= CEPH_FILE_MODE_LAZY; + ci->i_nr_by_mode[fi->fmode]++; + spin_unlock(&inode->i_lock); + dout("ioctl_layzio: file %p marked lazy\n", file); + + ceph_check_caps(ci, 0, NULL); + } else { + dout("ioctl_layzio: file %p already lazy\n", file); + } + return 0; +} + long ceph_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { dout("ioctl file %p cmd %u arg %lu\n", file, cmd, arg); @@ -155,6 +176,9 @@ long ceph_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case CEPH_IOC_GET_DATALOC: return ceph_ioctl_get_dataloc(file, (void __user *)arg); + + case CEPH_IOC_LAZYIO: + return ceph_ioctl_lazyio(file); } return -ENOTTY; } diff --git a/fs/ceph/ioctl.h b/fs/ceph/ioctl.h index 25e4f1a9d059..88451a3b6857 100644 --- a/fs/ceph/ioctl.h +++ b/fs/ceph/ioctl.h @@ -37,4 +37,6 @@ struct ceph_ioctl_dataloc { #define CEPH_IOC_GET_DATALOC _IOWR(CEPH_IOCTL_MAGIC, 3, \ struct ceph_ioctl_dataloc) +#define CEPH_IOC_LAZYIO _IO(CEPH_IOCTL_MAGIC, 4) + #endif -- cgit v1.2.3 From 33caad324b88f75f42d836735d86feaafb3b40cf Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Wed, 26 May 2010 14:31:27 -0700 Subject: ceph: perform lazy writes when file mode and caps permit If we have marked a file as "lazy" (using the ceph ioctl), perform buffered writes when the MDS caps allow it. Signed-off-by: Sage Weil --- fs/ceph/caps.c | 7 +++---- fs/ceph/file.c | 12 ++++++++---- 2 files changed, 11 insertions(+), 8 deletions(-) (limited to 'fs') diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index b81be9a56487..1a70a3ebf013 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -337,8 +337,7 @@ static struct ceph_cap *__get_cap_for_mds(struct ceph_inode_info *ci, int mds) } /* - * Return id of any MDS with a cap, preferably FILE_WR|WRBUFFER|EXCL, else - * -1. + * Return id of any MDS with a cap, preferably FILE_WR|BUFFER|EXCL, else -1. */ static int __ceph_get_cap_mds(struct ceph_inode_info *ci, u32 *mseq) { @@ -346,7 +345,7 @@ static int __ceph_get_cap_mds(struct ceph_inode_info *ci, u32 *mseq) int mds = -1; struct rb_node *p; - /* prefer mds with WR|WRBUFFER|EXCL caps */ + /* prefer mds with WR|BUFFER|EXCL caps */ for (p = rb_first(&ci->i_caps); p; p = rb_next(p)) { cap = rb_entry(p, struct ceph_cap, ci_node); mds = cap->mds; @@ -831,7 +830,7 @@ int __ceph_caps_file_wanted(struct ceph_inode_info *ci) { int want = 0; int mode; - for (mode = 0; mode < 4; mode++) + for (mode = 0; mode < CEPH_FILE_MODE_NUM; mode++) if (ci->i_nr_by_mode[mode]) want |= ceph_caps_for_mode(mode); return want; diff --git a/fs/ceph/file.c b/fs/ceph/file.c index 7c08698fad3e..85c86ed5f4c0 100644 --- a/fs/ceph/file.c +++ b/fs/ceph/file.c @@ -807,11 +807,12 @@ static ssize_t ceph_aio_write(struct kiocb *iocb, const struct iovec *iov, unsigned long nr_segs, loff_t pos) { struct file *file = iocb->ki_filp; + struct ceph_file_info *fi = file->private_data; struct inode *inode = file->f_dentry->d_inode; struct ceph_inode_info *ci = ceph_inode(inode); struct ceph_osd_client *osdc = &ceph_sb_to_client(inode->i_sb)->osdc; loff_t endoff = pos + iov->iov_len; - int got = 0; + int want, got = 0; int ret, err; if (ceph_snap(inode) != CEPH_NOSNAP) @@ -824,8 +825,11 @@ retry_snap: dout("aio_write %p %llx.%llx %llu~%u getting caps. i_size %llu\n", inode, ceph_vinop(inode), pos, (unsigned)iov->iov_len, inode->i_size); - ret = ceph_get_caps(ci, CEPH_CAP_FILE_WR, CEPH_CAP_FILE_BUFFER, - &got, endoff); + if (fi->fmode & CEPH_FILE_MODE_LAZY) + want = CEPH_CAP_FILE_BUFFER | CEPH_CAP_FILE_LAZYIO; + else + want = CEPH_CAP_FILE_BUFFER; + ret = ceph_get_caps(ci, CEPH_CAP_FILE_WR, want, &got, endoff); if (ret < 0) goto out; @@ -833,7 +837,7 @@ retry_snap: inode, ceph_vinop(inode), pos, (unsigned)iov->iov_len, ceph_cap_string(got)); - if ((got & CEPH_CAP_FILE_BUFFER) == 0 || + if ((got & (CEPH_CAP_FILE_BUFFER|CEPH_CAP_FILE_LAZYIO)) == 0 || (iocb->ki_filp->f_flags & O_DIRECT) || (inode->i_sb->s_flags & MS_SYNCHRONOUS)) { ret = ceph_sync_write(file, iov->iov_base, iov->iov_len, -- cgit v1.2.3 From 2962507ca204f886967e1a089d9bec206d427c22 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Thu, 27 May 2010 10:40:43 -0700 Subject: ceph: perform lazy reads when file mode and caps permit If the file mode is marked as "lazy," perform cached/buffered reads when the caps permit it. Adjust the rdcache_gen and invalidation logic accordingly so that we manage our cache based on the FILE_CACHE -or- FILE_LAZYIO cap bits. Signed-off-by: Sage Weil --- fs/ceph/addr.c | 2 +- fs/ceph/caps.c | 16 ++++++++++------ fs/ceph/file.c | 12 ++++++++---- fs/ceph/inode.c | 5 +++-- 4 files changed, 22 insertions(+), 13 deletions(-) (limited to 'fs') diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c index d9c60b84949a..e00797eed29c 100644 --- a/fs/ceph/addr.c +++ b/fs/ceph/addr.c @@ -552,7 +552,7 @@ static void writepages_finish(struct ceph_osd_request *req, * page truncation thread, possibly losing some data that * raced its way in */ - if ((issued & CEPH_CAP_FILE_CACHE) == 0) + if ((issued & (CEPH_CAP_FILE_CACHE|CEPH_CAP_FILE_LAZYIO)) == 0) generic_error_remove_page(inode->i_mapping, page); unlock_page(page); diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index 1a70a3ebf013..b28915d5f404 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -482,8 +482,8 @@ static void __check_cap_issue(struct ceph_inode_info *ci, struct ceph_cap *cap, * Each time we receive FILE_CACHE anew, we increment * i_rdcache_gen. */ - if ((issued & CEPH_CAP_FILE_CACHE) && - (had & CEPH_CAP_FILE_CACHE) == 0) + if ((issued & (CEPH_CAP_FILE_CACHE|CEPH_CAP_FILE_LAZYIO)) && + (had & (CEPH_CAP_FILE_CACHE|CEPH_CAP_FILE_LAZYIO)) == 0) ci->i_rdcache_gen++; /* @@ -1509,11 +1509,13 @@ retry_locked: ci->i_wrbuffer_ref == 0 && /* no dirty pages... */ ci->i_rdcache_gen && /* may have cached pages */ (file_wanted == 0 || /* no open files */ - (revoking & CEPH_CAP_FILE_CACHE)) && /* or revoking cache */ + (revoking & (CEPH_CAP_FILE_CACHE| + CEPH_CAP_FILE_LAZYIO))) && /* or revoking cache */ !tried_invalidate) { dout("check_caps trying to invalidate on %p\n", inode); if (try_nonblocking_invalidate(inode) < 0) { - if (revoking & CEPH_CAP_FILE_CACHE) { + if (revoking & (CEPH_CAP_FILE_CACHE| + CEPH_CAP_FILE_LAZYIO)) { dout("check_caps queuing invalidate\n"); queue_invalidate = 1; ci->i_rdcache_revoking = ci->i_rdcache_gen; @@ -2276,7 +2278,8 @@ static void handle_cap_grant(struct inode *inode, struct ceph_mds_caps *grant, * try to invalidate (once). (If there are dirty buffers, we * will invalidate _after_ writeback.) */ - if (((cap->issued & ~newcaps) & CEPH_CAP_FILE_CACHE) && + if (((cap->issued & ~newcaps) & (CEPH_CAP_FILE_CACHE| + CEPH_CAP_FILE_LAZYIO)) && !ci->i_wrbuffer_ref) { if (try_nonblocking_invalidate(inode) == 0) { revoked_rdcache = 1; @@ -2374,7 +2377,8 @@ static void handle_cap_grant(struct inode *inode, struct ceph_mds_caps *grant, writeback = 1; /* will delay ack */ else if (dirty & ~newcaps) check_caps = 1; /* initiate writeback in check_caps */ - else if (((used & ~newcaps) & CEPH_CAP_FILE_CACHE) == 0 || + else if (((used & ~newcaps) & (CEPH_CAP_FILE_CACHE| + CEPH_CAP_FILE_LAZYIO)) == 0 || revoked_rdcache) check_caps = 2; /* send revoke ack in check_caps */ cap->issued = newcaps; diff --git a/fs/ceph/file.c b/fs/ceph/file.c index 85c86ed5f4c0..2329244f427b 100644 --- a/fs/ceph/file.c +++ b/fs/ceph/file.c @@ -740,28 +740,32 @@ static ssize_t ceph_aio_read(struct kiocb *iocb, const struct iovec *iov, unsigned long nr_segs, loff_t pos) { struct file *filp = iocb->ki_filp; + struct ceph_file_info *fi = filp->private_data; loff_t *ppos = &iocb->ki_pos; size_t len = iov->iov_len; struct inode *inode = filp->f_dentry->d_inode; struct ceph_inode_info *ci = ceph_inode(inode); void *base = iov->iov_base; ssize_t ret; - int got = 0; + int want, got = 0; int checkeof = 0, read = 0; dout("aio_read %p %llx.%llx %llu~%u trying to get caps on %p\n", inode, ceph_vinop(inode), pos, (unsigned)len, inode); again: __ceph_do_pending_vmtruncate(inode); - ret = ceph_get_caps(ci, CEPH_CAP_FILE_RD, CEPH_CAP_FILE_CACHE, - &got, -1); + if (fi->fmode & CEPH_FILE_MODE_LAZY) + want = CEPH_CAP_FILE_CACHE | CEPH_CAP_FILE_LAZYIO; + else + want = CEPH_CAP_FILE_CACHE; + ret = ceph_get_caps(ci, CEPH_CAP_FILE_RD, want, &got, -1); if (ret < 0) goto out; dout("aio_read %p %llx.%llx %llu~%u got cap refs on %s\n", inode, ceph_vinop(inode), pos, (unsigned)len, ceph_cap_string(got)); - if ((got & CEPH_CAP_FILE_CACHE) == 0 || + if ((got & (CEPH_CAP_FILE_CACHE|CEPH_CAP_FILE_LAZYIO)) == 0 || (iocb->ki_filp->f_flags & O_DIRECT) || (inode->i_sb->s_flags & MS_SYNCHRONOUS)) /* hmm, this isn't really async... */ diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c index 389f9dbd9949..5d893d31e399 100644 --- a/fs/ceph/inode.c +++ b/fs/ceph/inode.c @@ -442,8 +442,9 @@ int ceph_fill_file_size(struct inode *inode, int issued, * the file is either opened or mmaped */ if ((issued & (CEPH_CAP_FILE_CACHE|CEPH_CAP_FILE_RD| - CEPH_CAP_FILE_WR|CEPH_CAP_FILE_BUFFER| - CEPH_CAP_FILE_EXCL)) || + CEPH_CAP_FILE_WR|CEPH_CAP_FILE_BUFFER| + CEPH_CAP_FILE_EXCL| + CEPH_CAP_FILE_LAZYIO)) || mapping_mapped(inode->i_mapping) || __ceph_caps_file_wanted(ci)) { ci->i_truncate_pending++; -- cgit v1.2.3 From ee6b272b9c3447a78fa831e37b925aefd5587ec9 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Thu, 10 Jun 2010 12:55:52 -0700 Subject: ceph: drop unused argument Signed-off-by: Sage Weil --- fs/ceph/caps.c | 2 +- fs/ceph/mds_client.c | 10 ++++------ fs/ceph/mds_client.h | 3 +-- 3 files changed, 6 insertions(+), 9 deletions(-) (limited to 'fs') diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index b28915d5f404..a5b5725931bf 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -2717,7 +2717,7 @@ void ceph_handle_caps(struct ceph_mds_session *session, * along for the mds (who clearly thinks we still have this * cap). */ - ceph_add_cap_releases(mdsc, session, -1); + ceph_add_cap_releases(mdsc, session); ceph_send_cap_releases(mdsc, session); goto done; } diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index dd440bd438a9..26a5368e91f2 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -1067,15 +1067,13 @@ static int trim_caps(struct ceph_mds_client *mdsc, * Called under s_mutex. */ int ceph_add_cap_releases(struct ceph_mds_client *mdsc, - struct ceph_mds_session *session, - int extra) + struct ceph_mds_session *session) { struct ceph_msg *msg; struct ceph_mds_cap_release *head; int err = -ENOMEM; + int extra = mdsc->client->mount_args->cap_release_safety; - if (extra < 0) - extra = mdsc->client->mount_args->cap_release_safety; spin_lock(&session->s_cap_lock); @@ -2005,7 +2003,7 @@ out_err: } mutex_unlock(&mdsc->mutex); - ceph_add_cap_releases(mdsc, req->r_session, -1); + ceph_add_cap_releases(mdsc, req->r_session); mutex_unlock(&session->s_mutex); /* kick calling process */ @@ -2715,7 +2713,7 @@ static void delayed_work(struct work_struct *work) send_renew_caps(mdsc, s); else ceph_con_keepalive(&s->s_con); - ceph_add_cap_releases(mdsc, s, -1); + ceph_add_cap_releases(mdsc, s); if (s->s_state == CEPH_MDS_SESSION_OPEN || s->s_state == CEPH_MDS_SESSION_HUNG) ceph_send_cap_releases(mdsc, s); diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h index 952410c60d09..e389902db131 100644 --- a/fs/ceph/mds_client.h +++ b/fs/ceph/mds_client.h @@ -324,8 +324,7 @@ static inline void ceph_mdsc_put_request(struct ceph_mds_request *req) } extern int ceph_add_cap_releases(struct ceph_mds_client *mdsc, - struct ceph_mds_session *session, - int extra); + struct ceph_mds_session *session); extern void ceph_send_cap_releases(struct ceph_mds_client *mdsc, struct ceph_mds_session *session); -- cgit v1.2.3 From 38e8883ee31667d901feb9106f4863af35948c91 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Thu, 10 Jun 2010 12:57:11 -0700 Subject: ceph: simplify add_cap_releases No functional change, aside from more useful debug output. Signed-off-by: Sage Weil --- fs/ceph/mds_client.c | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) (limited to 'fs') diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 26a5368e91f2..774407f96ff1 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -1069,11 +1069,14 @@ static int trim_caps(struct ceph_mds_client *mdsc, int ceph_add_cap_releases(struct ceph_mds_client *mdsc, struct ceph_mds_session *session) { - struct ceph_msg *msg; + struct ceph_msg *msg, *partial = NULL; struct ceph_mds_cap_release *head; int err = -ENOMEM; int extra = mdsc->client->mount_args->cap_release_safety; + int num; + dout("add_cap_releases %p mds%d extra %d\n", session, session->s_mds, + extra); spin_lock(&session->s_cap_lock); @@ -1082,9 +1085,14 @@ int ceph_add_cap_releases(struct ceph_mds_client *mdsc, struct ceph_msg, list_head); head = msg->front.iov_base; - extra += CEPH_CAPS_PER_RELEASE - le32_to_cpu(head->num); + num = le32_to_cpu(head->num); + if (num) { + dout(" partial %p with (%d/%d)\n", msg, num, + (int)CEPH_CAPS_PER_RELEASE); + extra += CEPH_CAPS_PER_RELEASE - num; + partial = msg; + } } - while (session->s_num_cap_releases < session->s_nr_caps + extra) { spin_unlock(&session->s_cap_lock); msg = ceph_msg_new(CEPH_MSG_CLIENT_CAPRELEASE, PAGE_CACHE_SIZE, @@ -1101,19 +1109,14 @@ int ceph_add_cap_releases(struct ceph_mds_client *mdsc, session->s_num_cap_releases += CEPH_CAPS_PER_RELEASE; } - if (!list_empty(&session->s_cap_releases)) { - msg = list_first_entry(&session->s_cap_releases, - struct ceph_msg, - list_head); - head = msg->front.iov_base; - if (head->num) { - dout(" queueing non-full %p (%d)\n", msg, - le32_to_cpu(head->num)); - list_move_tail(&msg->list_head, - &session->s_cap_releases_done); - session->s_num_cap_releases -= - CEPH_CAPS_PER_RELEASE - le32_to_cpu(head->num); - } + if (partial) { + head = partial->front.iov_base; + num = le32_to_cpu(head->num); + dout(" queueing partial %p with %d/%d\n", partial, num, + (int)CEPH_CAPS_PER_RELEASE); + list_move_tail(&partial->list_head, + &session->s_cap_releases_done); + session->s_num_cap_releases -= CEPH_CAPS_PER_RELEASE - num; } err = 0; spin_unlock(&session->s_cap_lock); -- cgit v1.2.3 From 3b454c4945c756686e91d77eeefac80cb5d21baf Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Thu, 10 Jun 2010 13:20:33 -0700 Subject: ceph: simplify caps revocation, fix for multimds The caps revocation should either initiate writeback, invalidateion, or call check_caps to ack or do the dirty work. The primary question is whether we can get away with only checking the auth cap or whether all caps need to be checked. The old code was doing...something else. At the very least, revocations from non-auth MDSs could break by triggering the "check auth cap only" case. Signed-off-by: Sage Weil --- fs/ceph/caps.c | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) (limited to 'fs') diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index a5b5725931bf..82ffde618af0 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -2278,8 +2278,8 @@ static void handle_cap_grant(struct inode *inode, struct ceph_mds_caps *grant, * try to invalidate (once). (If there are dirty buffers, we * will invalidate _after_ writeback.) */ - if (((cap->issued & ~newcaps) & (CEPH_CAP_FILE_CACHE| - CEPH_CAP_FILE_LAZYIO)) && + if (((cap->issued & ~newcaps) & CEPH_CAP_FILE_CACHE) && + (newcaps & CEPH_CAP_FILE_LAZYIO) == 0 && !ci->i_wrbuffer_ref) { if (try_nonblocking_invalidate(inode) == 0) { revoked_rdcache = 1; @@ -2371,16 +2371,22 @@ static void handle_cap_grant(struct inode *inode, struct ceph_mds_caps *grant, /* revocation, grant, or no-op? */ if (cap->issued & ~newcaps) { - dout("revocation: %s -> %s\n", ceph_cap_string(cap->issued), - ceph_cap_string(newcaps)); - if ((used & ~newcaps) & CEPH_CAP_FILE_BUFFER) - writeback = 1; /* will delay ack */ - else if (dirty & ~newcaps) - check_caps = 1; /* initiate writeback in check_caps */ - else if (((used & ~newcaps) & (CEPH_CAP_FILE_CACHE| - CEPH_CAP_FILE_LAZYIO)) == 0 || - revoked_rdcache) - check_caps = 2; /* send revoke ack in check_caps */ + int revoking = cap->issued & ~newcaps; + + dout("revocation: %s -> %s (revoking %s)\n", + ceph_cap_string(cap->issued), + ceph_cap_string(newcaps), + ceph_cap_string(revoking)); + if (revoking & CEPH_CAP_FILE_BUFFER) + writeback = 1; /* initiate writeback; will delay ack */ + else if (revoking == CEPH_CAP_FILE_CACHE && + (newcaps & CEPH_CAP_FILE_LAZYIO) == 0 && + queue_invalidate) + ; /* do nothing yet, invalidation will be queued */ + else if (cap == ci->i_auth_cap) + check_caps = 1; /* check auth cap only */ + else + check_caps = 2; /* check all caps */ cap->issued = newcaps; cap->implemented |= newcaps; } else if (cap->issued == newcaps) { -- cgit v1.2.3 From ca81f3f6bd759f90a4b940cddda1f8bc61a7725a Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Thu, 10 Jun 2010 14:21:36 -0700 Subject: ceph: skip if no auth cap in flush_snaps If we have a capsnap but no auth cap (e.g. because it is migrating to another mds), bail out and do nothing for now. Do NOT remove the capsnap from the flush list. Signed-off-by: Sage Weil --- fs/ceph/caps.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) (limited to 'fs') diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index 82ffde618af0..dbf0d6a02a77 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -339,7 +339,7 @@ static struct ceph_cap *__get_cap_for_mds(struct ceph_inode_info *ci, int mds) /* * Return id of any MDS with a cap, preferably FILE_WR|BUFFER|EXCL, else -1. */ -static int __ceph_get_cap_mds(struct ceph_inode_info *ci, u32 *mseq) +static int __ceph_get_cap_mds(struct ceph_inode_info *ci) { struct ceph_cap *cap; int mds = -1; @@ -349,8 +349,6 @@ static int __ceph_get_cap_mds(struct ceph_inode_info *ci, u32 *mseq) for (p = rb_first(&ci->i_caps); p; p = rb_next(p)) { cap = rb_entry(p, struct ceph_cap, ci_node); mds = cap->mds; - if (mseq) - *mseq = cap->mseq; if (cap->issued & (CEPH_CAP_FILE_WR | CEPH_CAP_FILE_BUFFER | CEPH_CAP_FILE_EXCL)) @@ -363,7 +361,7 @@ int ceph_get_cap_mds(struct inode *inode) { int mds; spin_lock(&inode->i_lock); - mds = __ceph_get_cap_mds(ceph_inode(inode), NULL); + mds = __ceph_get_cap_mds(ceph_inode(inode)); spin_unlock(&inode->i_lock); return mds; } @@ -1231,7 +1229,13 @@ retry: BUG_ON(capsnap->dirty == 0); /* pick mds, take s_mutex */ - mds = __ceph_get_cap_mds(ci, &mseq); + if (ci->i_auth_cap == NULL) { + dout("no auth cap (migrating?), doing nothing\n"); + goto out; + } + mds = ci->i_auth_cap->session->s_mds; + mseq = ci->i_auth_cap->mseq; + if (session && session->s_mds != mds) { dout("oops, wrong session %p mutex\n", session); mutex_unlock(&session->s_mutex); @@ -1250,8 +1254,8 @@ retry: } /* * if session == NULL, we raced against a cap - * deletion. retry, and we'll get a better - * @mds value next time. + * deletion or migration. retry, and we'll + * get a better @mds value next time. */ spin_lock(&inode->i_lock); goto retry; @@ -1289,6 +1293,7 @@ retry: list_del_init(&ci->i_snap_flush_item); spin_unlock(&mdsc->snap_flush_lock); +out: if (psession) *psession = session; else if (session) { -- cgit v1.2.3 From cd84db6e4051a9fb7941d49d31a0193a3371fd61 Mon Sep 17 00:00:00 2001 From: Yehuda Sadeh Date: Fri, 11 Jun 2010 16:58:48 -0700 Subject: ceph: code cleanup Mainly fixing minor issues reported by sparse. Signed-off-by: Yehuda Sadeh Signed-off-by: Sage Weil --- fs/ceph/armor.c | 6 +++++- fs/ceph/auth.c | 6 +++--- fs/ceph/auth_x.c | 6 +++--- fs/ceph/buffer.c | 16 ---------------- fs/ceph/caps.c | 6 +++--- fs/ceph/crypto.c | 27 +++++++++++++++------------ fs/ceph/crypto.h | 4 ++-- fs/ceph/decode.h | 6 ++++-- fs/ceph/dir.c | 2 ++ fs/ceph/file.c | 4 ++-- fs/ceph/messenger.c | 8 ++++---- fs/ceph/osdmap.c | 2 +- fs/ceph/xattr.c | 2 ++ 13 files changed, 46 insertions(+), 49 deletions(-) (limited to 'fs') diff --git a/fs/ceph/armor.c b/fs/ceph/armor.c index 67b2c030924b..eb2a666b0be7 100644 --- a/fs/ceph/armor.c +++ b/fs/ceph/armor.c @@ -1,11 +1,15 @@ #include +int ceph_armor(char *dst, const char *src, const char *end); +int ceph_unarmor(char *dst, const char *src, const char *end); + /* * base64 encode/decode. */ -const char *pem_key = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +static const char *pem_key = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; static int encode_bits(int c) { diff --git a/fs/ceph/auth.c b/fs/ceph/auth.c index 89490beaf537..6d2e30600627 100644 --- a/fs/ceph/auth.c +++ b/fs/ceph/auth.c @@ -20,7 +20,7 @@ static u32 supported_protocols[] = { CEPH_AUTH_CEPHX }; -int ceph_auth_init_protocol(struct ceph_auth_client *ac, int protocol) +static int ceph_auth_init_protocol(struct ceph_auth_client *ac, int protocol) { switch (protocol) { case CEPH_AUTH_NONE: @@ -133,8 +133,8 @@ bad: return -ERANGE; } -int ceph_build_auth_request(struct ceph_auth_client *ac, - void *msg_buf, size_t msg_len) +static int ceph_build_auth_request(struct ceph_auth_client *ac, + void *msg_buf, size_t msg_len) { struct ceph_mon_request_header *monhdr = msg_buf; void *p = monhdr + 1; diff --git a/fs/ceph/auth_x.c b/fs/ceph/auth_x.c index 6d44053ecff1..582e0b2caf8a 100644 --- a/fs/ceph/auth_x.c +++ b/fs/ceph/auth_x.c @@ -87,8 +87,8 @@ static int ceph_x_decrypt(struct ceph_crypto_key *secret, /* * get existing (or insert new) ticket handler */ -struct ceph_x_ticket_handler *get_ticket_handler(struct ceph_auth_client *ac, - int service) +static struct ceph_x_ticket_handler * +get_ticket_handler(struct ceph_auth_client *ac, int service) { struct ceph_x_ticket_handler *th; struct ceph_x_info *xi = ac->private; @@ -429,7 +429,7 @@ static int ceph_x_build_request(struct ceph_auth_client *ac, auth->struct_v = 1; auth->key = 0; for (u = (u64 *)tmp_enc; u + 1 <= (u64 *)(tmp_enc + ret); u++) - auth->key ^= *u; + auth->key ^= *(__le64 *)u; dout(" server_challenge %llx client_challenge %llx key %llx\n", xi->server_challenge, le64_to_cpu(auth->client_challenge), le64_to_cpu(auth->key)); diff --git a/fs/ceph/buffer.c b/fs/ceph/buffer.c index c67535d70aa6..cd39f17021de 100644 --- a/fs/ceph/buffer.c +++ b/fs/ceph/buffer.c @@ -47,22 +47,6 @@ void ceph_buffer_release(struct kref *kref) kfree(b); } -int ceph_buffer_alloc(struct ceph_buffer *b, int len, gfp_t gfp) -{ - b->vec.iov_base = kmalloc(len, gfp | __GFP_NOWARN); - if (b->vec.iov_base) { - b->is_vmalloc = false; - } else { - b->vec.iov_base = __vmalloc(len, gfp, PAGE_KERNEL); - b->is_vmalloc = true; - } - if (!b->vec.iov_base) - return -ENOMEM; - b->alloc_len = len; - b->vec.iov_len = len; - return 0; -} - int ceph_decode_buffer(struct ceph_buffer **b, void **p, void *end) { size_t len; diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index dbf0d6a02a77..d992880d21d4 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -1194,6 +1194,8 @@ static int __send_cap(struct ceph_mds_client *mdsc, struct ceph_cap *cap, */ void __ceph_flush_snaps(struct ceph_inode_info *ci, struct ceph_mds_session **psession) + __releases(ci->vfs_inode->i_lock) + __acquires(ci->vfs_inode->i_lock) { struct inode *inode = &ci->vfs_inode; int mds; @@ -1439,7 +1441,6 @@ static int try_nonblocking_invalidate(struct inode *inode) */ void ceph_check_caps(struct ceph_inode_info *ci, int flags, struct ceph_mds_session *session) - __releases(session->s_mutex) { struct ceph_client *client = ceph_inode_to_client(&ci->vfs_inode); struct ceph_mds_client *mdsc = &client->mdsc; @@ -2256,8 +2257,7 @@ static void handle_cap_grant(struct inode *inode, struct ceph_mds_caps *grant, struct ceph_mds_session *session, struct ceph_cap *cap, struct ceph_buffer *xattr_buf) - __releases(inode->i_lock) - __releases(session->s_mutex) + __releases(inode->i_lock) { struct ceph_inode_info *ci = ceph_inode(inode); int mds = session->s_mds; diff --git a/fs/ceph/crypto.c b/fs/ceph/crypto.c index f704b3b62424..c9318278f419 100644 --- a/fs/ceph/crypto.c +++ b/fs/ceph/crypto.c @@ -75,10 +75,11 @@ static struct crypto_blkcipher *ceph_crypto_alloc_cipher(void) return crypto_alloc_blkcipher("cbc(aes)", 0, CRYPTO_ALG_ASYNC); } -const u8 *aes_iv = "cephsageyudagreg"; +static const u8 *aes_iv = "cephsageyudagreg"; -int ceph_aes_encrypt(const void *key, int key_len, void *dst, size_t *dst_len, - const void *src, size_t src_len) +static int ceph_aes_encrypt(const void *key, int key_len, + void *dst, size_t *dst_len, + const void *src, size_t src_len) { struct scatterlist sg_in[2], sg_out[1]; struct crypto_blkcipher *tfm = ceph_crypto_alloc_cipher(); @@ -126,9 +127,10 @@ int ceph_aes_encrypt(const void *key, int key_len, void *dst, size_t *dst_len, return 0; } -int ceph_aes_encrypt2(const void *key, int key_len, void *dst, size_t *dst_len, - const void *src1, size_t src1_len, - const void *src2, size_t src2_len) +static int ceph_aes_encrypt2(const void *key, int key_len, void *dst, + size_t *dst_len, + const void *src1, size_t src1_len, + const void *src2, size_t src2_len) { struct scatterlist sg_in[3], sg_out[1]; struct crypto_blkcipher *tfm = ceph_crypto_alloc_cipher(); @@ -179,8 +181,9 @@ int ceph_aes_encrypt2(const void *key, int key_len, void *dst, size_t *dst_len, return 0; } -int ceph_aes_decrypt(const void *key, int key_len, void *dst, size_t *dst_len, - const void *src, size_t src_len) +static int ceph_aes_decrypt(const void *key, int key_len, + void *dst, size_t *dst_len, + const void *src, size_t src_len) { struct scatterlist sg_in[1], sg_out[2]; struct crypto_blkcipher *tfm = ceph_crypto_alloc_cipher(); @@ -238,10 +241,10 @@ int ceph_aes_decrypt(const void *key, int key_len, void *dst, size_t *dst_len, return 0; } -int ceph_aes_decrypt2(const void *key, int key_len, - void *dst1, size_t *dst1_len, - void *dst2, size_t *dst2_len, - const void *src, size_t src_len) +static int ceph_aes_decrypt2(const void *key, int key_len, + void *dst1, size_t *dst1_len, + void *dst2, size_t *dst2_len, + const void *src, size_t src_len) { struct scatterlist sg_in[1], sg_out[3]; struct crypto_blkcipher *tfm = ceph_crypto_alloc_cipher(); diff --git a/fs/ceph/crypto.h b/fs/ceph/crypto.h index 40b502e6bd89..bdf38607323c 100644 --- a/fs/ceph/crypto.h +++ b/fs/ceph/crypto.h @@ -42,7 +42,7 @@ extern int ceph_encrypt2(struct ceph_crypto_key *secret, const void *src2, size_t src2_len); /* armor.c */ -extern int ceph_armor(char *dst, const void *src, const void *end); -extern int ceph_unarmor(void *dst, const char *src, const char *end); +extern int ceph_armor(char *dst, const char *src, const char *end); +extern int ceph_unarmor(char *dst, const char *src, const char *end); #endif diff --git a/fs/ceph/decode.h b/fs/ceph/decode.h index 65b3e022eaf5..3d25415afe63 100644 --- a/fs/ceph/decode.h +++ b/fs/ceph/decode.h @@ -99,11 +99,13 @@ static inline void ceph_encode_timespec(struct ceph_timespec *tv, */ static inline void ceph_encode_addr(struct ceph_entity_addr *a) { - a->in_addr.ss_family = htons(a->in_addr.ss_family); + __be16 ss_family = htons(a->in_addr.ss_family); + a->in_addr.ss_family = *(__u16 *)&ss_family; } static inline void ceph_decode_addr(struct ceph_entity_addr *a) { - a->in_addr.ss_family = ntohs(a->in_addr.ss_family); + __be16 ss_family = *(__be16 *)&a->in_addr.ss_family; + a->in_addr.ss_family = ntohs(ss_family); WARN_ON(a->in_addr.ss_family == 512); } diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c index f94ed3c7f6a5..0ec6e67533ef 100644 --- a/fs/ceph/dir.c +++ b/fs/ceph/dir.c @@ -94,6 +94,8 @@ static unsigned fpos_off(loff_t p) */ static int __dcache_readdir(struct file *filp, void *dirent, filldir_t filldir) + __releases(inode->i_lock) + __acquires(inode->i_lock) { struct inode *inode = filp->f_dentry->d_inode; struct ceph_file_info *fi = filp->private_data; diff --git a/fs/ceph/file.c b/fs/ceph/file.c index 2329244f427b..5fe50ffa2deb 100644 --- a/fs/ceph/file.c +++ b/fs/ceph/file.c @@ -317,7 +317,7 @@ void ceph_release_page_vector(struct page **pages, int num_pages) /* * allocate a vector new pages */ -struct page **ceph_alloc_page_vector(int num_pages, gfp_t flags) +static struct page **ceph_alloc_page_vector(int num_pages, gfp_t flags) { struct page **pages; int i; @@ -745,7 +745,7 @@ static ssize_t ceph_aio_read(struct kiocb *iocb, const struct iovec *iov, size_t len = iov->iov_len; struct inode *inode = filp->f_dentry->d_inode; struct ceph_inode_info *ci = ceph_inode(inode); - void *base = iov->iov_base; + void __user *base = iov->iov_base; ssize_t ret; int want, got = 0; int checkeof = 0, read = 0; diff --git a/fs/ceph/messenger.c b/fs/ceph/messenger.c index 15167b2daa55..71263a2a78d7 100644 --- a/fs/ceph/messenger.c +++ b/fs/ceph/messenger.c @@ -108,7 +108,7 @@ void ceph_msgr_exit(void) destroy_workqueue(ceph_msgr_wq); } -void ceph_msgr_flush() +void ceph_msgr_flush(void) { flush_workqueue(ceph_msgr_wq); } @@ -1081,11 +1081,11 @@ static int process_banner(struct ceph_connection *con) sizeof(con->peer_addr)) != 0 && !(addr_is_blank(&con->actual_peer_addr.in_addr) && con->actual_peer_addr.nonce == con->peer_addr.nonce)) { - pr_warning("wrong peer, want %s/%lld, got %s/%lld\n", + pr_warning("wrong peer, want %s/%d, got %s/%d\n", pr_addr(&con->peer_addr.in_addr), - le64_to_cpu(con->peer_addr.nonce), + (int)le32_to_cpu(con->peer_addr.nonce), pr_addr(&con->actual_peer_addr.in_addr), - le64_to_cpu(con->actual_peer_addr.nonce)); + (int)le32_to_cpu(con->actual_peer_addr.nonce)); con->error_msg = "wrong peer at address"; return -1; } diff --git a/fs/ceph/osdmap.c b/fs/ceph/osdmap.c index 416d46adbf87..46b391d8e86c 100644 --- a/fs/ceph/osdmap.c +++ b/fs/ceph/osdmap.c @@ -424,7 +424,7 @@ static void __remove_pg_pool(struct rb_root *root, struct ceph_pg_pool_info *pi) kfree(pi); } -void __decode_pool(void **p, struct ceph_pg_pool_info *pi) +static void __decode_pool(void **p, struct ceph_pg_pool_info *pi) { ceph_decode_copy(p, &pi->v, sizeof(pi->v)); calc_pg_masks(pi); diff --git a/fs/ceph/xattr.c b/fs/ceph/xattr.c index 68aeebc69681..097a2654c00f 100644 --- a/fs/ceph/xattr.c +++ b/fs/ceph/xattr.c @@ -337,6 +337,8 @@ void __ceph_destroy_xattrs(struct ceph_inode_info *ci) } static int __build_xattrs(struct inode *inode) + __releases(inode->i_lock) + __acquires(inode->i_lock) { u32 namelen; u32 numattr = 0; -- cgit v1.2.3 From 0deb01c9998f8112c5e478e3fe3a930131abbc0a Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Thu, 17 Jun 2010 14:19:01 -0700 Subject: ceph: track laggy state of mds from mdsmap Signed-off-by: Sage Weil --- fs/ceph/mds_client.c | 4 +++- fs/ceph/mdsmap.c | 6 +++++- fs/ceph/mdsmap.h | 8 ++++++++ 3 files changed, 16 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 774407f96ff1..6e40db2a0014 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -2377,9 +2377,11 @@ static void check_new_map(struct ceph_mds_client *mdsc, oldstate = ceph_mdsmap_get_state(oldmap, i); newstate = ceph_mdsmap_get_state(newmap, i); - dout("check_new_map mds%d state %s -> %s (session %s)\n", + dout("check_new_map mds%d state %s%s -> %s%s (session %s)\n", i, ceph_mds_state_name(oldstate), + ceph_mdsmap_is_laggy(oldmap, i) ? " (laggy)" : "", ceph_mds_state_name(newstate), + ceph_mdsmap_is_laggy(newmap, i) ? " (laggy)" : "", session_state_name(s->s_state)); if (memcmp(ceph_mdsmap_get_addr(oldmap, i), diff --git a/fs/ceph/mdsmap.c b/fs/ceph/mdsmap.c index c4c498e6dfef..040be6d1150b 100644 --- a/fs/ceph/mdsmap.c +++ b/fs/ceph/mdsmap.c @@ -85,6 +85,7 @@ struct ceph_mdsmap *ceph_mdsmap_decode(void **p, void *end) struct ceph_entity_addr addr; u32 num_export_targets; void *pexport_targets = NULL; + struct ceph_timespec laggy_since; ceph_decode_need(p, end, sizeof(u64)*2 + 1 + sizeof(u32), bad); global_id = ceph_decode_64(p); @@ -103,7 +104,7 @@ struct ceph_mdsmap *ceph_mdsmap_decode(void **p, void *end) state_seq = ceph_decode_64(p); ceph_decode_copy(p, &addr, sizeof(addr)); ceph_decode_addr(&addr); - *p += sizeof(struct ceph_timespec); + ceph_decode_copy(p, &laggy_since, sizeof(laggy_since)); *p += sizeof(u32); ceph_decode_32_safe(p, end, namelen, bad); *p += namelen; @@ -122,6 +123,9 @@ struct ceph_mdsmap *ceph_mdsmap_decode(void **p, void *end) m->m_info[mds].global_id = global_id; m->m_info[mds].state = state; m->m_info[mds].addr = addr; + m->m_info[mds].laggy = + (laggy_since.tv_sec != 0 || + laggy_since.tv_nsec != 0); m->m_info[mds].num_export_targets = num_export_targets; if (num_export_targets) { m->m_info[mds].export_targets = diff --git a/fs/ceph/mdsmap.h b/fs/ceph/mdsmap.h index eacc131aa5cb..4c5cb0880bba 100644 --- a/fs/ceph/mdsmap.h +++ b/fs/ceph/mdsmap.h @@ -13,6 +13,7 @@ struct ceph_mds_info { struct ceph_entity_addr addr; s32 state; int num_export_targets; + bool laggy; u32 *export_targets; }; @@ -47,6 +48,13 @@ static inline int ceph_mdsmap_get_state(struct ceph_mdsmap *m, int w) return m->m_info[w].state; } +static inline bool ceph_mdsmap_is_laggy(struct ceph_mdsmap *m, int w) +{ + if (w >= 0 && w < m->m_max_mds) + return m->m_info[w].laggy; + return false; +} + extern int ceph_mdsmap_get_random_mds(struct ceph_mdsmap *m); extern struct ceph_mdsmap *ceph_mdsmap_decode(void **p, void *end); extern void ceph_mdsmap_destroy(struct ceph_mdsmap *m); -- cgit v1.2.3 From 37151668bad3fd058368752bee476f2ba3645596 Mon Sep 17 00:00:00 2001 From: Yehuda Sadeh Date: Thu, 17 Jun 2010 16:16:12 -0700 Subject: ceph: do caps accounting per mds_client Caps related accounting is now being done per mds client instead of just being global. This prepares ground work for a later revision of the caps preallocated reservation list. Signed-off-by: Yehuda Sadeh Signed-off-by: Sage Weil --- fs/ceph/caps.c | 187 ++++++++++++++++++++++++--------------------------- fs/ceph/mds_client.c | 16 +++-- fs/ceph/mds_client.h | 22 ++++++ fs/ceph/super.c | 6 -- fs/ceph/super.h | 15 +++-- 5 files changed, 131 insertions(+), 115 deletions(-) (limited to 'fs') diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index d992880d21d4..47068b10baf8 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -113,58 +113,41 @@ const char *ceph_cap_string(int caps) return cap_str[i]; } -/* - * Cap reservations - * - * Maintain a global pool of preallocated struct ceph_caps, referenced - * by struct ceph_caps_reservations. This ensures that we preallocate - * memory needed to successfully process an MDS response. (If an MDS - * sends us cap information and we fail to process it, we will have - * problems due to the client and MDS being out of sync.) - * - * Reservations are 'owned' by a ceph_cap_reservation context. - */ -static spinlock_t caps_list_lock; -static struct list_head caps_list; /* unused (reserved or unreserved) */ -static int caps_total_count; /* total caps allocated */ -static int caps_use_count; /* in use */ -static int caps_reserve_count; /* unused, reserved */ -static int caps_avail_count; /* unused, unreserved */ -static int caps_min_count; /* keep at least this many (unreserved) */ - -void __init ceph_caps_init(void) +void ceph_caps_init(struct ceph_mds_client *mdsc) { - INIT_LIST_HEAD(&caps_list); - spin_lock_init(&caps_list_lock); + INIT_LIST_HEAD(&mdsc->caps_list); + spin_lock_init(&mdsc->caps_list_lock); } -void ceph_caps_finalize(void) +void ceph_caps_finalize(struct ceph_mds_client *mdsc) { struct ceph_cap *cap; - spin_lock(&caps_list_lock); - while (!list_empty(&caps_list)) { - cap = list_first_entry(&caps_list, struct ceph_cap, caps_item); + spin_lock(&mdsc->caps_list_lock); + while (!list_empty(&mdsc->caps_list)) { + cap = list_first_entry(&mdsc->caps_list, + struct ceph_cap, caps_item); list_del(&cap->caps_item); kmem_cache_free(ceph_cap_cachep, cap); } - caps_total_count = 0; - caps_avail_count = 0; - caps_use_count = 0; - caps_reserve_count = 0; - caps_min_count = 0; - spin_unlock(&caps_list_lock); + mdsc->caps_total_count = 0; + mdsc->caps_avail_count = 0; + mdsc->caps_use_count = 0; + mdsc->caps_reserve_count = 0; + mdsc->caps_min_count = 0; + spin_unlock(&mdsc->caps_list_lock); } -void ceph_adjust_min_caps(int delta) +void ceph_adjust_min_caps(struct ceph_mds_client *mdsc, int delta) { - spin_lock(&caps_list_lock); - caps_min_count += delta; - BUG_ON(caps_min_count < 0); - spin_unlock(&caps_list_lock); + spin_lock(&mdsc->caps_list_lock); + mdsc->caps_min_count += delta; + BUG_ON(mdsc->caps_min_count < 0); + spin_unlock(&mdsc->caps_list_lock); } -int ceph_reserve_caps(struct ceph_cap_reservation *ctx, int need) +int ceph_reserve_caps(struct ceph_mds_client *mdsc, + struct ceph_cap_reservation *ctx, int need) { int i; struct ceph_cap *cap; @@ -176,16 +159,17 @@ int ceph_reserve_caps(struct ceph_cap_reservation *ctx, int need) dout("reserve caps ctx=%p need=%d\n", ctx, need); /* first reserve any caps that are already allocated */ - spin_lock(&caps_list_lock); - if (caps_avail_count >= need) + spin_lock(&mdsc->caps_list_lock); + if (mdsc->caps_avail_count >= need) have = need; else - have = caps_avail_count; - caps_avail_count -= have; - caps_reserve_count += have; - BUG_ON(caps_total_count != caps_use_count + caps_reserve_count + - caps_avail_count); - spin_unlock(&caps_list_lock); + have = mdsc->caps_avail_count; + mdsc->caps_avail_count -= have; + mdsc->caps_reserve_count += have; + BUG_ON(mdsc->caps_total_count != mdsc->caps_use_count + + mdsc->caps_reserve_count + + mdsc->caps_avail_count); + spin_unlock(&mdsc->caps_list_lock); for (i = have; i < need; i++) { cap = kmem_cache_alloc(ceph_cap_cachep, GFP_NOFS); @@ -198,19 +182,20 @@ int ceph_reserve_caps(struct ceph_cap_reservation *ctx, int need) } BUG_ON(have + alloc != need); - spin_lock(&caps_list_lock); - caps_total_count += alloc; - caps_reserve_count += alloc; - list_splice(&newcaps, &caps_list); + spin_lock(&mdsc->caps_list_lock); + mdsc->caps_total_count += alloc; + mdsc->caps_reserve_count += alloc; + list_splice(&newcaps, &mdsc->caps_list); - BUG_ON(caps_total_count != caps_use_count + caps_reserve_count + - caps_avail_count); - spin_unlock(&caps_list_lock); + BUG_ON(mdsc->caps_total_count != mdsc->caps_use_count + + mdsc->caps_reserve_count + + mdsc->caps_avail_count); + spin_unlock(&mdsc->caps_list_lock); ctx->count = need; dout("reserve caps ctx=%p %d = %d used + %d resv + %d avail\n", - ctx, caps_total_count, caps_use_count, caps_reserve_count, - caps_avail_count); + ctx, mdsc->caps_total_count, mdsc->caps_use_count, + mdsc->caps_reserve_count, mdsc->caps_avail_count); return 0; out_alloc_count: @@ -220,26 +205,29 @@ out_alloc_count: return ret; } -int ceph_unreserve_caps(struct ceph_cap_reservation *ctx) +int ceph_unreserve_caps(struct ceph_mds_client *mdsc, + struct ceph_cap_reservation *ctx) { dout("unreserve caps ctx=%p count=%d\n", ctx, ctx->count); if (ctx->count) { - spin_lock(&caps_list_lock); - BUG_ON(caps_reserve_count < ctx->count); - caps_reserve_count -= ctx->count; - caps_avail_count += ctx->count; + spin_lock(&mdsc->caps_list_lock); + BUG_ON(mdsc->caps_reserve_count < ctx->count); + mdsc->caps_reserve_count -= ctx->count; + mdsc->caps_avail_count += ctx->count; ctx->count = 0; dout("unreserve caps %d = %d used + %d resv + %d avail\n", - caps_total_count, caps_use_count, caps_reserve_count, - caps_avail_count); - BUG_ON(caps_total_count != caps_use_count + caps_reserve_count + - caps_avail_count); - spin_unlock(&caps_list_lock); + mdsc->caps_total_count, mdsc->caps_use_count, + mdsc->caps_reserve_count, mdsc->caps_avail_count); + BUG_ON(mdsc->caps_total_count != mdsc->caps_use_count + + mdsc->caps_reserve_count + + mdsc->caps_avail_count); + spin_unlock(&mdsc->caps_list_lock); } return 0; } -static struct ceph_cap *get_cap(struct ceph_cap_reservation *ctx) +static struct ceph_cap *get_cap(struct ceph_mds_client *mdsc, + struct ceph_cap_reservation *ctx) { struct ceph_cap *cap = NULL; @@ -247,71 +235,74 @@ static struct ceph_cap *get_cap(struct ceph_cap_reservation *ctx) if (!ctx) { cap = kmem_cache_alloc(ceph_cap_cachep, GFP_NOFS); if (cap) { - caps_use_count++; - caps_total_count++; + mdsc->caps_use_count++; + mdsc->caps_total_count++; } return cap; } - spin_lock(&caps_list_lock); + spin_lock(&mdsc->caps_list_lock); dout("get_cap ctx=%p (%d) %d = %d used + %d resv + %d avail\n", - ctx, ctx->count, caps_total_count, caps_use_count, - caps_reserve_count, caps_avail_count); + ctx, ctx->count, mdsc->caps_total_count, mdsc->caps_use_count, + mdsc->caps_reserve_count, mdsc->caps_avail_count); BUG_ON(!ctx->count); - BUG_ON(ctx->count > caps_reserve_count); - BUG_ON(list_empty(&caps_list)); + BUG_ON(ctx->count > mdsc->caps_reserve_count); + BUG_ON(list_empty(&mdsc->caps_list)); ctx->count--; - caps_reserve_count--; - caps_use_count++; + mdsc->caps_reserve_count--; + mdsc->caps_use_count++; - cap = list_first_entry(&caps_list, struct ceph_cap, caps_item); + cap = list_first_entry(&mdsc->caps_list, struct ceph_cap, caps_item); list_del(&cap->caps_item); - BUG_ON(caps_total_count != caps_use_count + caps_reserve_count + - caps_avail_count); - spin_unlock(&caps_list_lock); + BUG_ON(mdsc->caps_total_count != mdsc->caps_use_count + + mdsc->caps_reserve_count + mdsc->caps_avail_count); + spin_unlock(&mdsc->caps_list_lock); return cap; } -void ceph_put_cap(struct ceph_cap *cap) +void ceph_put_cap(struct ceph_mds_client *mdsc, struct ceph_cap *cap) { - spin_lock(&caps_list_lock); + spin_lock(&mdsc->caps_list_lock); dout("put_cap %p %d = %d used + %d resv + %d avail\n", - cap, caps_total_count, caps_use_count, - caps_reserve_count, caps_avail_count); - caps_use_count--; + cap, mdsc->caps_total_count, mdsc->caps_use_count, + mdsc->caps_reserve_count, mdsc->caps_avail_count); + mdsc->caps_use_count--; /* * Keep some preallocated caps around (ceph_min_count), to * avoid lots of free/alloc churn. */ - if (caps_avail_count >= caps_reserve_count + caps_min_count) { - caps_total_count--; + if (mdsc->caps_avail_count >= mdsc->caps_reserve_count + + mdsc->caps_min_count) { + mdsc->caps_total_count--; kmem_cache_free(ceph_cap_cachep, cap); } else { - caps_avail_count++; - list_add(&cap->caps_item, &caps_list); + mdsc->caps_avail_count++; + list_add(&cap->caps_item, &mdsc->caps_list); } - BUG_ON(caps_total_count != caps_use_count + caps_reserve_count + - caps_avail_count); - spin_unlock(&caps_list_lock); + BUG_ON(mdsc->caps_total_count != mdsc->caps_use_count + + mdsc->caps_reserve_count + mdsc->caps_avail_count); + spin_unlock(&mdsc->caps_list_lock); } void ceph_reservation_status(struct ceph_client *client, int *total, int *avail, int *used, int *reserved, int *min) { + struct ceph_mds_client *mdsc = &client->mdsc; + if (total) - *total = caps_total_count; + *total = mdsc->caps_total_count; if (avail) - *avail = caps_avail_count; + *avail = mdsc->caps_avail_count; if (used) - *used = caps_use_count; + *used = mdsc->caps_use_count; if (reserved) - *reserved = caps_reserve_count; + *reserved = mdsc->caps_reserve_count; if (min) - *min = caps_min_count; + *min = mdsc->caps_min_count; } /* @@ -540,7 +531,7 @@ retry: new_cap = NULL; } else { spin_unlock(&inode->i_lock); - new_cap = get_cap(caps_reservation); + new_cap = get_cap(mdsc, caps_reservation); if (new_cap == NULL) return -ENOMEM; goto retry; @@ -898,7 +889,7 @@ void __ceph_remove_cap(struct ceph_cap *cap) ci->i_auth_cap = NULL; if (removed) - ceph_put_cap(cap); + ceph_put_cap(mdsc, cap); if (!__ceph_is_any_caps(ci) && ci->i_snap_realm) { struct ceph_snap_realm *realm = ci->i_snap_realm; diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 6e40db2a0014..641a8a37e7b3 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -449,7 +449,7 @@ void ceph_mdsc_release_request(struct kref *kref) kfree(req->r_path1); kfree(req->r_path2); put_request_session(req); - ceph_unreserve_caps(&req->r_caps_reservation); + ceph_unreserve_caps(req->r_mdsc, &req->r_caps_reservation); kfree(req); } @@ -512,7 +512,8 @@ static void __register_request(struct ceph_mds_client *mdsc, { req->r_tid = ++mdsc->last_tid; if (req->r_num_caps) - ceph_reserve_caps(&req->r_caps_reservation, req->r_num_caps); + ceph_reserve_caps(mdsc, &req->r_caps_reservation, + req->r_num_caps); dout("__register_request %p tid %lld\n", req, req->r_tid); ceph_mdsc_get_request(req); __insert_request(mdsc, req); @@ -764,7 +765,7 @@ static int iterate_session_caps(struct ceph_mds_session *session, last_inode = NULL; } if (old_cap) { - ceph_put_cap(old_cap); + ceph_put_cap(session->s_mdsc, old_cap); old_cap = NULL; } @@ -793,7 +794,7 @@ out: if (last_inode) iput(last_inode); if (old_cap) - ceph_put_cap(old_cap); + ceph_put_cap(session->s_mdsc, old_cap); return ret; } @@ -1251,6 +1252,7 @@ ceph_mdsc_create_request(struct ceph_mds_client *mdsc, int op, int mode) return ERR_PTR(-ENOMEM); mutex_init(&req->r_fill_mutex); + req->r_mdsc = mdsc; req->r_started = jiffies; req->r_resend_mds = -1; INIT_LIST_HEAD(&req->r_unsafe_dir_item); @@ -1986,7 +1988,7 @@ static void handle_reply(struct ceph_mds_session *session, struct ceph_msg *msg) if (err == 0) { if (result == 0 && rinfo->dir_nr) ceph_readdir_prepopulate(req, req->r_session); - ceph_unreserve_caps(&req->r_caps_reservation); + ceph_unreserve_caps(mdsc, &req->r_caps_reservation); } mutex_unlock(&req->r_fill_mutex); @@ -2767,6 +2769,9 @@ int ceph_mdsc_init(struct ceph_mds_client *mdsc, struct ceph_client *client) spin_lock_init(&mdsc->dentry_lru_lock); INIT_LIST_HEAD(&mdsc->dentry_lru); + ceph_caps_init(mdsc); + ceph_adjust_min_caps(mdsc, client->min_caps); + return 0; } @@ -2962,6 +2967,7 @@ void ceph_mdsc_stop(struct ceph_mds_client *mdsc) if (mdsc->mdsmap) ceph_mdsmap_destroy(mdsc->mdsmap); kfree(mdsc->sessions); + ceph_caps_finalize(mdsc); } diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h index e389902db131..8f2126321f2d 100644 --- a/fs/ceph/mds_client.h +++ b/fs/ceph/mds_client.h @@ -151,6 +151,7 @@ typedef void (*ceph_mds_request_callback_t) (struct ceph_mds_client *mdsc, struct ceph_mds_request { u64 r_tid; /* transaction id */ struct rb_node r_node; + struct ceph_mds_client *r_mdsc; int r_op; /* mds op code */ int r_mds; @@ -267,6 +268,27 @@ struct ceph_mds_client { spinlock_t cap_dirty_lock; /* protects above items */ wait_queue_head_t cap_flushing_wq; + /* + * Cap reservations + * + * Maintain a global pool of preallocated struct ceph_caps, referenced + * by struct ceph_caps_reservations. This ensures that we preallocate + * memory needed to successfully process an MDS response. (If an MDS + * sends us cap information and we fail to process it, we will have + * problems due to the client and MDS being out of sync.) + * + * Reservations are 'owned' by a ceph_cap_reservation context. + */ + spinlock_t caps_list_lock; + struct list_head caps_list; /* unused (reserved or + unreserved) */ + int caps_total_count; /* total caps allocated */ + int caps_use_count; /* in use */ + int caps_reserve_count; /* unused, reserved */ + int caps_avail_count; /* unused, unreserved */ + int caps_min_count; /* keep at least this many + (unreserved) */ + #ifdef CONFIG_DEBUG_FS struct dentry *debugfs_file; #endif diff --git a/fs/ceph/super.c b/fs/ceph/super.c index fa87f51e38e1..1a0bb4863a5d 100644 --- a/fs/ceph/super.c +++ b/fs/ceph/super.c @@ -630,7 +630,6 @@ static struct ceph_client *ceph_create_client(struct ceph_mount_args *args) /* caps */ client->min_caps = args->max_readdir; - ceph_adjust_min_caps(client->min_caps); /* subsystems */ err = ceph_monc_init(&client->monc, client); @@ -680,8 +679,6 @@ static void ceph_destroy_client(struct ceph_client *client) ceph_monc_stop(&client->monc); - ceph_adjust_min_caps(-client->min_caps); - ceph_debugfs_client_cleanup(client); destroy_workqueue(client->wb_wq); destroy_workqueue(client->pg_inv_wq); @@ -1043,8 +1040,6 @@ static int __init init_ceph(void) if (ret) goto out_msgr; - ceph_caps_init(); - ret = register_filesystem(&ceph_fs_type); if (ret) goto out_icache; @@ -1069,7 +1064,6 @@ static void __exit exit_ceph(void) { dout("exit_ceph\n"); unregister_filesystem(&ceph_fs_type); - ceph_caps_finalize(); destroy_caches(); ceph_msgr_exit(); ceph_debugfs_cleanup(); diff --git a/fs/ceph/super.h b/fs/ceph/super.h index 10a4a406e887..44d10cb0aeca 100644 --- a/fs/ceph/super.h +++ b/fs/ceph/super.h @@ -560,11 +560,13 @@ static inline int __ceph_caps_wanted(struct ceph_inode_info *ci) /* what the mds thinks we want */ extern int __ceph_caps_mds_wanted(struct ceph_inode_info *ci); -extern void ceph_caps_init(void); -extern void ceph_caps_finalize(void); -extern void ceph_adjust_min_caps(int delta); -extern int ceph_reserve_caps(struct ceph_cap_reservation *ctx, int need); -extern int ceph_unreserve_caps(struct ceph_cap_reservation *ctx); +extern void ceph_caps_init(struct ceph_mds_client *mdsc); +extern void ceph_caps_finalize(struct ceph_mds_client *mdsc); +extern void ceph_adjust_min_caps(struct ceph_mds_client *mdsc, int delta); +extern int ceph_reserve_caps(struct ceph_mds_client *mdsc, + struct ceph_cap_reservation *ctx, int need); +extern int ceph_unreserve_caps(struct ceph_mds_client *mdsc, + struct ceph_cap_reservation *ctx); extern void ceph_reservation_status(struct ceph_client *client, int *total, int *avail, int *used, int *reserved, int *min); @@ -806,7 +808,8 @@ static inline void ceph_remove_cap(struct ceph_cap *cap) __ceph_remove_cap(cap); spin_unlock(&inode->i_lock); } -extern void ceph_put_cap(struct ceph_cap *cap); +extern void ceph_put_cap(struct ceph_mds_client *mdsc, + struct ceph_cap *cap); extern void ceph_queue_caps_release(struct inode *inode); extern int ceph_write_inode(struct inode *inode, struct writeback_control *wbc); -- cgit v1.2.3 From 796d6955a51ce6768d0e033f27a2f8f5be6cb39a Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Thu, 10 Jun 2010 15:50:10 -0700 Subject: ceph: only set num_pages in calc_layout Setting it elsewhere is unnecessary and more fragile. Signed-off-by: Sage Weil --- fs/ceph/osd_client.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'fs') diff --git a/fs/ceph/osd_client.c b/fs/ceph/osd_client.c index e38522347898..707d2dbd8776 100644 --- a/fs/ceph/osd_client.c +++ b/fs/ceph/osd_client.c @@ -1276,8 +1276,6 @@ int ceph_osdc_readpages(struct ceph_osd_client *osdc, /* it may be a short read due to an object boundary */ req->r_pages = pages; - num_pages = calc_pages_for(off, *plen); - req->r_num_pages = num_pages; dout("readpages final extent is %llu~%llu (%d pages)\n", off, *plen, req->r_num_pages); @@ -1319,7 +1317,6 @@ int ceph_osdc_writepages(struct ceph_osd_client *osdc, struct ceph_vino vino, /* it may be a short write due to an object boundary */ req->r_pages = pages; - req->r_num_pages = calc_pages_for(off, len); dout("writepages %llu~%llu (%d pages)\n", off, len, req->r_num_pages); -- cgit v1.2.3 From ed0552a1a21d2f2692b84c366ce04ad17377780c Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Mon, 21 Jun 2010 13:38:25 -0700 Subject: ceph: introduce helper to connect to mds export targets There are a few cases where we need to open sessions with a given mds's potential export targets. Signed-off-by: Sage Weil --- fs/ceph/mds_client.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) (limited to 'fs') diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 641a8a37e7b3..462602ec7fb1 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -704,6 +704,43 @@ static int __open_session(struct ceph_mds_client *mdsc, return 0; } +/* + * open sessions for any export targets for the given mds + * + * called under mdsc->mutex + */ +static void __open_export_target_sessions(struct ceph_mds_client *mdsc, + struct ceph_mds_session *session) +{ + struct ceph_mds_info *mi; + struct ceph_mds_session *ts; + int i, mds = session->s_mds; + int target; + + if (mds >= mdsc->mdsmap->m_max_mds) + return; + mi = &mdsc->mdsmap->m_info[mds]; + dout("open_export_target_sessions for mds%d (%d targets)\n", + session->s_mds, mi->num_export_targets); + + for (i = 0; i < mi->num_export_targets; i++) { + target = mi->export_targets[i]; + ts = __ceph_lookup_mds_session(mdsc, target); + if (!ts) { + ts = register_session(mdsc, target); + if (IS_ERR(ts)) + return; + } + if (session->s_state == CEPH_MDS_SESSION_NEW || + session->s_state == CEPH_MDS_SESSION_CLOSING) + __open_session(mdsc, session); + else + dout(" mds%d target mds%d %p is %s\n", session->s_mds, + i, ts, session_state_name(ts->s_state)); + ceph_put_mds_session(ts); + } +} + /* * session caps */ -- cgit v1.2.3 From cb170a22153730eb9c82b6c85ead2001dba6c41a Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Mon, 21 Jun 2010 13:38:35 -0700 Subject: ceph: connect to export targets if mds is laggy If an MDS we are talking to may have failed, we need to open sessions to its potential export targets to ensure that any in-progress migration that may have involved some of our caps is properly handled. Signed-off-by: Sage Weil --- fs/ceph/mds_client.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'fs') diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 462602ec7fb1..552b934c9cd0 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -2470,6 +2470,21 @@ static void check_new_map(struct ceph_mds_client *mdsc, wake_up_session_caps(s, 1); } } + + for (i = 0; i < newmap->m_max_mds && i < mdsc->max_sessions; i++) { + s = mdsc->sessions[i]; + if (!s) + continue; + if (!ceph_mdsmap_is_laggy(newmap, i)) + continue; + if (s->s_state == CEPH_MDS_SESSION_OPEN || + s->s_state == CEPH_MDS_SESSION_HUNG || + s->s_state == CEPH_MDS_SESSION_CLOSING) { + dout(" connecting to export targets of laggy mds%d\n", + i); + __open_export_target_sessions(mdsc, s); + } + } } -- cgit v1.2.3 From 154f42c2c3c3b66a7a63dad5648e8a9860a32af9 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Mon, 21 Jun 2010 13:45:04 -0700 Subject: ceph: connect to export targets on cap export When we get a cap EXPORT message, make sure we are connected to all export targets to ensure we can handle the matching IMPORT. Signed-off-by: Sage Weil --- fs/ceph/caps.c | 14 ++++++++++++-- fs/ceph/mds_client.c | 8 ++++++++ fs/ceph/mds_client.h | 3 +++ 3 files changed, 23 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index 47068b10baf8..52befa65fbf7 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -2573,7 +2573,8 @@ static void handle_cap_trunc(struct inode *inode, * caller holds s_mutex */ static void handle_cap_export(struct inode *inode, struct ceph_mds_caps *ex, - struct ceph_mds_session *session) + struct ceph_mds_session *session, + int *open_target_sessions) { struct ceph_inode_info *ci = ceph_inode(inode); int mds = session->s_mds; @@ -2605,6 +2606,12 @@ static void handle_cap_export(struct inode *inode, struct ceph_mds_caps *ex, ci->i_cap_exporting_mds = mds; ci->i_cap_exporting_mseq = mseq; ci->i_cap_exporting_issued = cap->issued; + + /* + * make sure we have open sessions with all possible + * export targets, so that we get the matching IMPORT + */ + *open_target_sessions = 1; } __ceph_remove_cap(cap); } @@ -2680,6 +2687,7 @@ void ceph_handle_caps(struct ceph_mds_session *session, u64 size, max_size; u64 tid; void *snaptrace; + int open_target_sessions = 0; dout("handle_caps from mds%d\n", mds); @@ -2731,7 +2739,7 @@ void ceph_handle_caps(struct ceph_mds_session *session, goto done; case CEPH_CAP_OP_EXPORT: - handle_cap_export(inode, h, session); + handle_cap_export(inode, h, session, &open_target_sessions); goto done; case CEPH_CAP_OP_IMPORT: @@ -2778,6 +2786,8 @@ done: done_unlocked: if (inode) iput(inode); + if (open_target_sessions) + ceph_mdsc_open_export_target_sessions(mdsc, session); return; bad: diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 552b934c9cd0..a546e0ddb8e3 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -741,6 +741,14 @@ static void __open_export_target_sessions(struct ceph_mds_client *mdsc, } } +void ceph_mdsc_open_export_target_sessions(struct ceph_mds_client *mdsc, + struct ceph_mds_session *session) +{ + mutex_lock(&mdsc->mutex); + __open_export_target_sessions(mdsc, session); + mutex_unlock(&mdsc->mutex); +} + /* * session caps */ diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h index 8f2126321f2d..c86be30e8707 100644 --- a/fs/ceph/mds_client.h +++ b/fs/ceph/mds_client.h @@ -364,4 +364,7 @@ extern void ceph_mdsc_lease_send_msg(struct ceph_mds_session *session, extern void ceph_mdsc_handle_map(struct ceph_mds_client *mdsc, struct ceph_msg *msg); +extern void ceph_mdsc_open_export_target_sessions(struct ceph_mds_client *mdsc, + struct ceph_mds_session *session); + #endif -- cgit v1.2.3 From 2bc50259fa0aa1868f8b2ba1d374406cb3c57f72 Mon Sep 17 00:00:00 2001 From: Greg Farnum Date: Wed, 30 Jun 2010 12:44:34 -0700 Subject: ceph: add ceph_get_cap_for_mds function. Signed-off-by: Greg Farnum Signed-off-by: Sage Weil --- fs/ceph/caps.c | 10 ++++++++++ fs/ceph/super.h | 2 ++ 2 files changed, 12 insertions(+) (limited to 'fs') diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index 52befa65fbf7..e3b848d7698d 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -327,6 +327,16 @@ static struct ceph_cap *__get_cap_for_mds(struct ceph_inode_info *ci, int mds) return NULL; } +struct ceph_cap *ceph_get_cap_for_mds(struct ceph_inode_info *ci, int mds) +{ + struct ceph_cap *cap; + + spin_lock(&ci->vfs_inode.i_lock); + cap = __get_cap_for_mds(ci, mds); + spin_unlock(&ci->vfs_inode.i_lock); + return cap; +} + /* * Return id of any MDS with a cap, preferably FILE_WR|BUFFER|EXCL, else -1. */ diff --git a/fs/ceph/super.h b/fs/ceph/super.h index 44d10cb0aeca..8ceb62380d3f 100644 --- a/fs/ceph/super.h +++ b/fs/ceph/super.h @@ -816,6 +816,8 @@ extern int ceph_write_inode(struct inode *inode, struct writeback_control *wbc); extern int ceph_fsync(struct file *file, int datasync); extern void ceph_kick_flushing_caps(struct ceph_mds_client *mdsc, struct ceph_mds_session *session); +extern struct ceph_cap *ceph_get_cap_for_mds(struct ceph_inode_info *ci, + int mds); extern int ceph_get_cap_mds(struct inode *inode); extern void ceph_get_cap_refs(struct ceph_inode_info *ci, int caps); extern void ceph_put_cap_refs(struct ceph_inode_info *ci, int had); -- cgit v1.2.3 From e55b71f802fd448a79275ba7b263fe1a8639be5f Mon Sep 17 00:00:00 2001 From: Greg Farnum Date: Tue, 22 Jun 2010 15:58:01 -0700 Subject: ceph: handle ESTALE properly; on receipt send to authority if it wasn't Signed-off-by: Greg Farnum Signed-off-by: Sage Weil --- fs/ceph/mds_client.c | 41 ++++++++++++++++++++++++++++++++++------- fs/ceph/mds_client.h | 2 +- 2 files changed, 35 insertions(+), 8 deletions(-) (limited to 'fs') diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index a546e0ddb8e3..34d215ff4c82 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -1628,6 +1628,15 @@ static int __prepare_send_request(struct ceph_mds_client *mdsc, req->r_mds = mds; req->r_attempts++; + if (req->r_inode) { + struct ceph_cap *cap = + ceph_get_cap_for_mds(ceph_inode(req->r_inode), mds); + + if (cap) + req->r_sent_on_mseq = cap->mseq; + else + req->r_sent_on_mseq = -1; + } dout("prepare_send_request %p tid %lld %s (attempt %d)\n", req, req->r_tid, ceph_mds_op_name(req->r_op), req->r_attempts); @@ -1962,21 +1971,39 @@ static void handle_reply(struct ceph_mds_session *session, struct ceph_msg *msg) result = le32_to_cpu(head->result); /* - * Tolerate 2 consecutive ESTALEs from the same mds. - * FIXME: we should be looking at the cap migrate_seq. + * Handle an ESTALE + * if we're not talking to the authority, send to them + * if the authority has changed while we weren't looking, + * send to new authority + * Otherwise we just have to return an ESTALE */ if (result == -ESTALE) { - req->r_direct_mode = USE_AUTH_MDS; - req->r_num_stale++; - if (req->r_num_stale <= 2) { + dout("got ESTALE on request %llu", req->r_tid); + if (!req->r_inode) ; //do nothing; not an authority problem + else if (req->r_direct_mode != USE_AUTH_MDS) { + dout("not using auth, setting for that now"); + req->r_direct_mode = USE_AUTH_MDS; __do_request(mdsc, req); mutex_unlock(&mdsc->mutex); goto out; + } else { + struct ceph_inode_info *ci = ceph_inode(req->r_inode); + struct ceph_cap *cap = + ceph_get_cap_for_mds(ci, req->r_mds);; + + dout("already using auth"); + if ((!cap || cap != ci->i_auth_cap) || + (cap->mseq != req->r_sent_on_mseq)) { + dout("but cap changed, so resending"); + __do_request(mdsc, req); + mutex_unlock(&mdsc->mutex); + goto out; + } } - } else { - req->r_num_stale = 0; + dout("have to return ESTALE on request %llu", req->r_tid); } + if (head->safe) { req->r_got_safe = true; __unregister_request(mdsc, req); diff --git a/fs/ceph/mds_client.h b/fs/ceph/mds_client.h index c86be30e8707..ab7e89f5e344 100644 --- a/fs/ceph/mds_client.h +++ b/fs/ceph/mds_client.h @@ -208,8 +208,8 @@ struct ceph_mds_request { int r_attempts; /* resend attempts */ int r_num_fwd; /* number of forward attempts */ - int r_num_stale; int r_resend_mds; /* mds to resend to next, if any*/ + u32 r_sent_on_mseq; /* cap mseq request was sent at*/ struct kref r_kref; struct list_head r_wait; -- cgit v1.2.3 From e0f9f9ee8f6cb60fe49e32e1df790a698ce0840c Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Wed, 30 Jun 2010 12:45:29 -0700 Subject: ceph: remove unused 'monport' mount option Signed-off-by: Sage Weil --- fs/ceph/super.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'fs') diff --git a/fs/ceph/super.c b/fs/ceph/super.c index 1a0bb4863a5d..c1ea38e5aebd 100644 --- a/fs/ceph/super.c +++ b/fs/ceph/super.c @@ -324,7 +324,6 @@ const char *ceph_msg_type_name(int type) enum { Opt_fsidmajor, Opt_fsidminor, - Opt_monport, Opt_wsize, Opt_rsize, Opt_osdtimeout, @@ -357,7 +356,6 @@ enum { static match_table_t arg_tokens = { {Opt_fsidmajor, "fsidmajor=%ld"}, {Opt_fsidminor, "fsidminor=%ld"}, - {Opt_monport, "monport=%d"}, {Opt_wsize, "wsize=%d"}, {Opt_rsize, "rsize=%d"}, {Opt_osdtimeout, "osdtimeout=%d"}, -- cgit v1.2.3 From c309f0ab26ca37663f368918553d02e90356c89d Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Wed, 30 Jun 2010 21:34:01 -0700 Subject: ceph: clean up fsid mount option Specify the fsid mount option in hex, not via the major/minor u64 hackery we had before. Signed-off-by: Sage Weil --- fs/ceph/super.c | 52 +++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 39 insertions(+), 13 deletions(-) (limited to 'fs') diff --git a/fs/ceph/super.c b/fs/ceph/super.c index c1ea38e5aebd..3100c909cbb1 100644 --- a/fs/ceph/super.c +++ b/fs/ceph/super.c @@ -2,6 +2,7 @@ #include "ceph_debug.h" #include +#include #include #include #include @@ -150,9 +151,7 @@ static int ceph_show_options(struct seq_file *m, struct vfsmount *mnt) struct ceph_mount_args *args = client->mount_args; if (args->flags & CEPH_OPT_FSID) - seq_printf(m, ",fsidmajor=%llu,fsidminor%llu", - le64_to_cpu(*(__le64 *)&args->fsid.fsid[0]), - le64_to_cpu(*(__le64 *)&args->fsid.fsid[8])); + seq_printf(m, ",fsid=" FSID_FORMAT, PR_FSID(&args->fsid)); if (args->flags & CEPH_OPT_NOSHARE) seq_puts(m, ",noshare"); if (args->flags & CEPH_OPT_DIRSTAT) @@ -322,8 +321,6 @@ const char *ceph_msg_type_name(int type) * mount options */ enum { - Opt_fsidmajor, - Opt_fsidminor, Opt_wsize, Opt_rsize, Opt_osdtimeout, @@ -338,6 +335,7 @@ enum { Opt_congestion_kb, Opt_last_int, /* int args above */ + Opt_fsid, Opt_snapdirname, Opt_name, Opt_secret, @@ -354,8 +352,6 @@ enum { }; static match_table_t arg_tokens = { - {Opt_fsidmajor, "fsidmajor=%ld"}, - {Opt_fsidminor, "fsidminor=%ld"}, {Opt_wsize, "wsize=%d"}, {Opt_rsize, "rsize=%d"}, {Opt_osdtimeout, "osdtimeout=%d"}, @@ -369,6 +365,7 @@ static match_table_t arg_tokens = { {Opt_readdir_max_bytes, "readdir_max_bytes=%d"}, {Opt_congestion_kb, "write_congestion_kb=%d"}, /* int args above */ + {Opt_fsid, "fsid=%s"}, {Opt_snapdirname, "snapdirname=%s"}, {Opt_name, "name=%s"}, {Opt_secret, "secret=%s"}, @@ -384,6 +381,36 @@ static match_table_t arg_tokens = { {-1, NULL} }; +static int parse_fsid(const char *str, struct ceph_fsid *fsid) +{ + int i = 0; + char tmp[3]; + int err = -EINVAL; + int d; + + dout("parse_fsid '%s'\n", str); + tmp[2] = 0; + while (*str && i < 16) { + if (ispunct(*str)) { + str++; + continue; + } + if (!isxdigit(str[0]) || !isxdigit(str[1])) + break; + tmp[0] = str[0]; + tmp[1] = str[1]; + if (sscanf(tmp, "%x", &d) < 1) + break; + fsid->fsid[i] = d & 0xff; + i++; + str += 2; + } + + if (i == 16) + err = 0; + dout("parse_fsid ret %d got fsid " FSID_FORMAT, err, PR_FSID(fsid)); + return err; +} static struct ceph_mount_args *parse_mount_args(int flags, char *options, const char *dev_name, @@ -467,12 +494,6 @@ static struct ceph_mount_args *parse_mount_args(int flags, char *options, dout("got token %d\n", token); } switch (token) { - case Opt_fsidmajor: - *(__le64 *)&args->fsid.fsid[0] = cpu_to_le64(intval); - break; - case Opt_fsidminor: - *(__le64 *)&args->fsid.fsid[8] = cpu_to_le64(intval); - break; case Opt_ip: err = ceph_parse_ips(argstr[0].from, argstr[0].to, @@ -483,6 +504,11 @@ static struct ceph_mount_args *parse_mount_args(int flags, char *options, args->flags |= CEPH_OPT_MYIP; break; + case Opt_fsid: + err = parse_fsid(argstr[0].from, &args->fsid); + if (err == 0) + args->flags |= CEPH_OPT_FSID; + break; case Opt_snapdirname: kfree(args->snapdir_name); args->snapdir_name = kstrndup(argstr[0].from, -- cgit v1.2.3 From 6a2593823ababdada2636398ac190931517b51cf Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Wed, 7 Jul 2010 09:06:08 -0700 Subject: ceph: specify supported features in super.h Specify the supported/required feature bits in super.h client code instead of using the definitions from the shared kernel/userspace headers (which will go away shortly). Signed-off-by: Sage Weil --- fs/ceph/messenger.c | 6 +++--- fs/ceph/super.h | 6 ++++++ 2 files changed, 9 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/ceph/messenger.c b/fs/ceph/messenger.c index 71263a2a78d7..79b0995dc825 100644 --- a/fs/ceph/messenger.c +++ b/fs/ceph/messenger.c @@ -647,7 +647,7 @@ static void prepare_write_connect(struct ceph_messenger *msgr, dout("prepare_write_connect %p cseq=%d gseq=%d proto=%d\n", con, con->connect_seq, global_seq, proto); - con->out_connect.features = cpu_to_le64(CEPH_FEATURE_SUPPORTED_CLIENT); + con->out_connect.features = cpu_to_le64(CEPH_FEATURE_SUPPORTED); con->out_connect.host_type = cpu_to_le32(CEPH_ENTITY_TYPE_CLIENT); con->out_connect.connect_seq = cpu_to_le32(con->connect_seq); con->out_connect.global_seq = cpu_to_le32(global_seq); @@ -1123,8 +1123,8 @@ static void fail_protocol(struct ceph_connection *con) static int process_connect(struct ceph_connection *con) { - u64 sup_feat = CEPH_FEATURE_SUPPORTED_CLIENT; - u64 req_feat = CEPH_FEATURE_REQUIRED_CLIENT; + u64 sup_feat = CEPH_FEATURE_SUPPORTED; + u64 req_feat = CEPH_FEATURE_REQUIRED; u64 server_feat = le64_to_cpu(con->in_reply.features); dout("process_connect on %p tag %d\n", con, (int)con->in_tag); diff --git a/fs/ceph/super.h b/fs/ceph/super.h index 8ceb62380d3f..af62081a9535 100644 --- a/fs/ceph/super.h +++ b/fs/ceph/super.h @@ -30,6 +30,12 @@ #define CEPH_BLOCK_SHIFT 20 /* 1 MB */ #define CEPH_BLOCK (1 << CEPH_BLOCK_SHIFT) +/* + * Supported features + */ +#define CEPH_FEATURE_SUPPORTED CEPH_FEATURE_NOSRCADDR +#define CEPH_FEATURE_REQUIRED CEPH_FEATURE_NOSRCADDR + /* * mount options */ -- cgit v1.2.3 From 9688f19a188e5ef6ac8a4b6a5b17273028d1090e Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Fri, 30 Jul 2010 10:18:04 -0700 Subject: ceph: strip misleading/obsolete version, feature info Signed-off-by: Sage Weil --- fs/ceph/ceph_fs.h | 30 ++++-------------------------- 1 file changed, 4 insertions(+), 26 deletions(-) (limited to 'fs') diff --git a/fs/ceph/ceph_fs.h b/fs/ceph/ceph_fs.h index c2e4c61a99a3..86fadeb9fd42 100644 --- a/fs/ceph/ceph_fs.h +++ b/fs/ceph/ceph_fs.h @@ -15,20 +15,6 @@ #include "msgr.h" #include "rados.h" -/* - * Ceph release version - */ -#define CEPH_VERSION_MAJOR 0 -#define CEPH_VERSION_MINOR 20 -#define CEPH_VERSION_PATCH 0 - -#define _CEPH_STRINGIFY(x) #x -#define CEPH_STRINGIFY(x) _CEPH_STRINGIFY(x) -#define CEPH_MAKE_VERSION(x, y, z) CEPH_STRINGIFY(x) "." CEPH_STRINGIFY(y) \ - "." CEPH_STRINGIFY(z) -#define CEPH_VERSION CEPH_MAKE_VERSION(CEPH_VERSION_MAJOR, \ - CEPH_VERSION_MINOR, CEPH_VERSION_PATCH) - /* * subprotocol versions. when specific messages types or high-level * protocols change, bump the affected components. we keep rev @@ -53,18 +39,10 @@ /* * feature bits */ -#define CEPH_FEATURE_UID 1 -#define CEPH_FEATURE_NOSRCADDR 2 -#define CEPH_FEATURE_FLOCK 4 - -#define CEPH_FEATURE_SUPPORTED_MON CEPH_FEATURE_UID|CEPH_FEATURE_NOSRCADDR -#define CEPH_FEATURE_REQUIRED_MON CEPH_FEATURE_UID -#define CEPH_FEATURE_SUPPORTED_MDS CEPH_FEATURE_UID|CEPH_FEATURE_NOSRCADDR|CEPH_FEATURE_FLOCK -#define CEPH_FEATURE_REQUIRED_MDS CEPH_FEATURE_UID -#define CEPH_FEATURE_SUPPORTED_OSD CEPH_FEATURE_UID|CEPH_FEATURE_NOSRCADDR -#define CEPH_FEATURE_REQUIRED_OSD CEPH_FEATURE_UID -#define CEPH_FEATURE_SUPPORTED_CLIENT CEPH_FEATURE_NOSRCADDR -#define CEPH_FEATURE_REQUIRED_CLIENT CEPH_FEATURE_NOSRCADDR +#define CEPH_FEATURE_UID (1<<0) +#define CEPH_FEATURE_NOSRCADDR (1<<1) +#define CEPH_FEATURE_MONCLOCKCHECK (1<<2) +#define CEPH_FEATURE_FLOCK (1<<3) /* -- cgit v1.2.3 From 5cd068c200795537fd78c423254389c8a07e2d20 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Wed, 7 Jul 2010 08:38:17 -0700 Subject: ceph: clean up header guards Signed-off-by: Sage Weil --- fs/ceph/ceph_frag.h | 4 ++-- fs/ceph/ceph_fs.h | 4 ++-- fs/ceph/ceph_hash.h | 4 ++-- fs/ceph/crush/crush.h | 4 ++-- fs/ceph/crush/hash.h | 4 ++-- fs/ceph/crush/mapper.h | 4 ++-- fs/ceph/msgr.h | 4 ++-- fs/ceph/rados.h | 4 ++-- 8 files changed, 16 insertions(+), 16 deletions(-) (limited to 'fs') diff --git a/fs/ceph/ceph_frag.h b/fs/ceph/ceph_frag.h index 793f50cb7c22..5babb8e95352 100644 --- a/fs/ceph/ceph_frag.h +++ b/fs/ceph/ceph_frag.h @@ -1,5 +1,5 @@ -#ifndef _FS_CEPH_FRAG_H -#define _FS_CEPH_FRAG_H +#ifndef FS_CEPH_FRAG_H +#define FS_CEPH_FRAG_H /* * "Frags" are a way to describe a subset of a 32-bit number space, diff --git a/fs/ceph/ceph_fs.h b/fs/ceph/ceph_fs.h index 86fadeb9fd42..663ff9021111 100644 --- a/fs/ceph/ceph_fs.h +++ b/fs/ceph/ceph_fs.h @@ -9,8 +9,8 @@ * LGPL2 */ -#ifndef _FS_CEPH_CEPH_FS_H -#define _FS_CEPH_CEPH_FS_H +#ifndef CEPH_FS_H +#define CEPH_FS_H #include "msgr.h" #include "rados.h" diff --git a/fs/ceph/ceph_hash.h b/fs/ceph/ceph_hash.h index 5ac470c433c9..d099c3f90236 100644 --- a/fs/ceph/ceph_hash.h +++ b/fs/ceph/ceph_hash.h @@ -1,5 +1,5 @@ -#ifndef _FS_CEPH_HASH_H -#define _FS_CEPH_HASH_H +#ifndef FS_CEPH_HASH_H +#define FS_CEPH_HASH_H #define CEPH_STR_HASH_LINUX 0x1 /* linux dcache hash */ #define CEPH_STR_HASH_RJENKINS 0x2 /* robert jenkins' */ diff --git a/fs/ceph/crush/crush.h b/fs/ceph/crush/crush.h index dcd7e7523700..97e435b191f4 100644 --- a/fs/ceph/crush/crush.h +++ b/fs/ceph/crush/crush.h @@ -1,5 +1,5 @@ -#ifndef _CRUSH_CRUSH_H -#define _CRUSH_CRUSH_H +#ifndef CEPH_CRUSH_CRUSH_H +#define CEPH_CRUSH_CRUSH_H #include diff --git a/fs/ceph/crush/hash.h b/fs/ceph/crush/hash.h index ff48e110e4bb..91e884230d5d 100644 --- a/fs/ceph/crush/hash.h +++ b/fs/ceph/crush/hash.h @@ -1,5 +1,5 @@ -#ifndef _CRUSH_HASH_H -#define _CRUSH_HASH_H +#ifndef CEPH_CRUSH_HASH_H +#define CEPH_CRUSH_HASH_H #define CRUSH_HASH_RJENKINS1 0 diff --git a/fs/ceph/crush/mapper.h b/fs/ceph/crush/mapper.h index 98e90046fd9f..c46b99c18bb0 100644 --- a/fs/ceph/crush/mapper.h +++ b/fs/ceph/crush/mapper.h @@ -1,5 +1,5 @@ -#ifndef _CRUSH_MAPPER_H -#define _CRUSH_MAPPER_H +#ifndef CEPH_CRUSH_MAPPER_H +#define CEPH_CRUSH_MAPPER_H /* * CRUSH functions for find rules and then mapping an input to an diff --git a/fs/ceph/msgr.h b/fs/ceph/msgr.h index 892a0298dfdf..680d3d648cac 100644 --- a/fs/ceph/msgr.h +++ b/fs/ceph/msgr.h @@ -1,5 +1,5 @@ -#ifndef __MSGR_H -#define __MSGR_H +#ifndef CEPH_MSGR_H +#define CEPH_MSGR_H /* * Data types for message passing layer used by Ceph. diff --git a/fs/ceph/rados.h b/fs/ceph/rados.h index 8fcc023056c7..20b5b45fa575 100644 --- a/fs/ceph/rados.h +++ b/fs/ceph/rados.h @@ -1,5 +1,5 @@ -#ifndef __RADOS_H -#define __RADOS_H +#ifndef CEPH_RADOS_H +#define CEPH_RADOS_H /* * Data types for the Ceph distributed object storage layer RADOS -- cgit v1.2.3 From f0b18d9f22ea4e50955945661b7e165a47705249 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Wed, 7 Jul 2010 08:39:03 -0700 Subject: ceph: sync header defs with server code Define ROLLBACK op, IFLOCK inode lock (for advisory file locking). Signed-off-by: Sage Weil --- fs/ceph/ceph_fs.h | 1 + fs/ceph/ceph_strings.c | 1 + fs/ceph/rados.h | 9 +++++++++ 3 files changed, 11 insertions(+) (limited to 'fs') diff --git a/fs/ceph/ceph_fs.h b/fs/ceph/ceph_fs.h index 663ff9021111..bf7dea673237 100644 --- a/fs/ceph/ceph_fs.h +++ b/fs/ceph/ceph_fs.h @@ -253,6 +253,7 @@ extern const char *ceph_mds_state_name(int s); #define CEPH_LOCK_IDFT 512 /* dir frag tree */ #define CEPH_LOCK_INEST 1024 /* mds internal */ #define CEPH_LOCK_IXATTR 2048 +#define CEPH_LOCK_IFLOCK 4096 /* advisory file locks */ #define CEPH_LOCK_INO 8192 /* immutable inode bits; not a lock */ /* client_session ops */ diff --git a/fs/ceph/ceph_strings.c b/fs/ceph/ceph_strings.c index 7503aee828ce..0f943a0a3b5e 100644 --- a/fs/ceph/ceph_strings.c +++ b/fs/ceph/ceph_strings.c @@ -28,6 +28,7 @@ const char *ceph_osd_op_name(int op) case CEPH_OSD_OP_TRUNCATE: return "truncate"; case CEPH_OSD_OP_ZERO: return "zero"; case CEPH_OSD_OP_WRITEFULL: return "writefull"; + case CEPH_OSD_OP_ROLLBACK: return "rollback"; case CEPH_OSD_OP_APPEND: return "append"; case CEPH_OSD_OP_STARTSYNC: return "startsync"; diff --git a/fs/ceph/rados.h b/fs/ceph/rados.h index 20b5b45fa575..6d5247f2e81b 100644 --- a/fs/ceph/rados.h +++ b/fs/ceph/rados.h @@ -203,6 +203,7 @@ enum { CEPH_OSD_OP_TMAPGET = CEPH_OSD_OP_MODE_RD | CEPH_OSD_OP_TYPE_DATA | 12, CEPH_OSD_OP_CREATE = CEPH_OSD_OP_MODE_WR | CEPH_OSD_OP_TYPE_DATA | 13, + CEPH_OSD_OP_ROLLBACK= CEPH_OSD_OP_MODE_WR | CEPH_OSD_OP_TYPE_DATA | 14, /** attrs **/ /* read */ @@ -272,6 +273,10 @@ static inline int ceph_osd_op_mode_modify(int op) return (op & CEPH_OSD_OP_MODE) == CEPH_OSD_OP_MODE_WR; } +/* + * note that the following tmap stuff is also defined in the ceph librados.h + * any modification here needs to be updated there + */ #define CEPH_OSD_TMAP_HDR 'h' #define CEPH_OSD_TMAP_SET 's' #define CEPH_OSD_TMAP_RM 'r' @@ -297,6 +302,7 @@ enum { CEPH_OSD_FLAG_PARALLELEXEC = 512, /* execute op in parallel */ CEPH_OSD_FLAG_PGOP = 1024, /* pg op, no object */ CEPH_OSD_FLAG_EXEC = 2048, /* op may exec */ + CEPH_OSD_FLAG_EXEC_PUBLIC = 4096, /* op may exec (public) */ }; enum { @@ -350,6 +356,9 @@ struct ceph_osd_op { struct { __le64 cookie, count; } __attribute__ ((packed)) pgls; + struct { + __le64 snapid; + } __attribute__ ((packed)) snap; }; __le32 payload_len; } __attribute__ ((packed)); -- cgit v1.2.3 From a8b763a9b34561fea8e616c1439a71913ff2c1bd Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Thu, 8 Jul 2010 13:00:18 -0700 Subject: ceph: use %pU to print uuid (fsid) Signed-off-by: Sage Weil --- fs/ceph/debugfs.c | 4 ++-- fs/ceph/super.c | 12 ++++++------ fs/ceph/super.h | 7 ------- 3 files changed, 8 insertions(+), 15 deletions(-) (limited to 'fs') diff --git a/fs/ceph/debugfs.c b/fs/ceph/debugfs.c index f2f5332ddbba..968dc5a86c3e 100644 --- a/fs/ceph/debugfs.c +++ b/fs/ceph/debugfs.c @@ -361,8 +361,8 @@ int ceph_debugfs_client_init(struct ceph_client *client) int ret = 0; char name[80]; - snprintf(name, sizeof(name), FSID_FORMAT ".client%lld", - PR_FSID(&client->fsid), client->monc.auth->global_id); + snprintf(name, sizeof(name), "%pU.client%lld", &client->fsid, + client->monc.auth->global_id); client->debugfs_dir = debugfs_create_dir(name, ceph_debugfs_dir); if (!client->debugfs_dir) diff --git a/fs/ceph/super.c b/fs/ceph/super.c index 3100c909cbb1..d80699a4dc26 100644 --- a/fs/ceph/super.c +++ b/fs/ceph/super.c @@ -151,7 +151,7 @@ static int ceph_show_options(struct seq_file *m, struct vfsmount *mnt) struct ceph_mount_args *args = client->mount_args; if (args->flags & CEPH_OPT_FSID) - seq_printf(m, ",fsid=" FSID_FORMAT, PR_FSID(&args->fsid)); + seq_printf(m, ",fsid=%pU", &args->fsid); if (args->flags & CEPH_OPT_NOSHARE) seq_puts(m, ",noshare"); if (args->flags & CEPH_OPT_DIRSTAT) @@ -408,7 +408,7 @@ static int parse_fsid(const char *str, struct ceph_fsid *fsid) if (i == 16) err = 0; - dout("parse_fsid ret %d got fsid " FSID_FORMAT, err, PR_FSID(fsid)); + dout("parse_fsid ret %d got fsid %pU", err, fsid); return err; } @@ -727,13 +727,13 @@ int ceph_check_fsid(struct ceph_client *client, struct ceph_fsid *fsid) { if (client->have_fsid) { if (ceph_fsid_compare(&client->fsid, fsid)) { - pr_err("bad fsid, had " FSID_FORMAT " got " FSID_FORMAT, - PR_FSID(&client->fsid), PR_FSID(fsid)); + pr_err("bad fsid, had %pU got %pU", + &client->fsid, fsid); return -1; } } else { - pr_info("client%lld fsid " FSID_FORMAT "\n", - client->monc.auth->global_id, PR_FSID(fsid)); + pr_info("client%lld fsid %pU\n", client->monc.auth->global_id, + fsid); memcpy(&client->fsid, fsid, sizeof(*fsid)); ceph_debugfs_client_init(client); client->have_fsid = true; diff --git a/fs/ceph/super.h b/fs/ceph/super.h index af62081a9535..281e458db8b3 100644 --- a/fs/ceph/super.h +++ b/fs/ceph/super.h @@ -746,13 +746,6 @@ extern struct kmem_cache *ceph_file_cachep; extern const char *ceph_msg_type_name(int type); extern int ceph_check_fsid(struct ceph_client *client, struct ceph_fsid *fsid); -#define FSID_FORMAT "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-" \ - "%02x%02x%02x%02x%02x%02x" -#define PR_FSID(f) (f)->fsid[0], (f)->fsid[1], (f)->fsid[2], (f)->fsid[3], \ - (f)->fsid[4], (f)->fsid[5], (f)->fsid[6], (f)->fsid[7], \ - (f)->fsid[8], (f)->fsid[9], (f)->fsid[10], (f)->fsid[11], \ - (f)->fsid[12], (f)->fsid[13], (f)->fsid[14], (f)->fsid[15] - /* inode.c */ extern const struct inode_operations ceph_file_iops; -- cgit v1.2.3 From effcb9ed43d16db27ae5837d93879e067e902151 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Fri, 9 Jul 2010 11:00:08 -0700 Subject: ceph: print useful error message when crush rule not found Include the crush_ruleset in the error message. Signed-off-by: Sage Weil --- fs/ceph/osdmap.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/ceph/osdmap.c b/fs/ceph/osdmap.c index 46b391d8e86c..1d5f58cc2d93 100644 --- a/fs/ceph/osdmap.c +++ b/fs/ceph/osdmap.c @@ -1026,8 +1026,9 @@ static int *calc_pg_raw(struct ceph_osdmap *osdmap, struct ceph_pg pgid, ruleno = crush_find_rule(osdmap->crush, pool->v.crush_ruleset, pool->v.type, pool->v.size); if (ruleno < 0) { - pr_err("no crush rule pool %d type %d size %d\n", - poolid, pool->v.type, pool->v.size); + pr_err("no crush rule pool %d ruleset %d type %d size %d\n", + poolid, pool->v.crush_ruleset, pool->v.type, + pool->v.size); return NULL; } -- cgit v1.2.3 From b8cd07e78eaa49857e882f4199309f86aeb80bbd Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Fri, 16 Jul 2010 12:00:02 -0700 Subject: ceph: warn on missing snap realm Well, this Shouldn't Happen, so it would be helpful to know the caller when it does. Signed-off-by: Sage Weil --- fs/ceph/caps.c | 1 + 1 file changed, 1 insertion(+) (limited to 'fs') diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index e3b848d7698d..30acc7b046ee 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -586,6 +586,7 @@ retry: } else { pr_err("ceph_add_cap: couldn't find snap realm %llx\n", realmino); + WARN_ON(!realm); } } -- cgit v1.2.3 From 2d9c98ae97c18e8b1c363af6a2e51d5d9e8c5e04 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Fri, 30 Jul 2010 09:38:13 -0700 Subject: ceph: make ->sync_fs not wait if wait==0 The ->sync_fs() super op only needs to wait if wait is true. Otherwise, just get some dirty cap writeback started. Signed-off-by: Sage Weil --- fs/ceph/super.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/ceph/super.c b/fs/ceph/super.c index d80699a4dc26..7f751f2850ba 100644 --- a/fs/ceph/super.c +++ b/fs/ceph/super.c @@ -102,12 +102,21 @@ static int ceph_statfs(struct dentry *dentry, struct kstatfs *buf) } -static int ceph_syncfs(struct super_block *sb, int wait) +static int ceph_sync_fs(struct super_block *sb, int wait) { - dout("sync_fs %d\n", wait); + struct ceph_client *client = ceph_sb_to_client(sb); + + if (!wait) { + dout("sync_fs (non-blocking)\n"); + ceph_flush_dirty_caps(&client->mdsc); + dout("sync_fs (non-blocking) done\n"); + return 0; + } + + dout("sync_fs (blocking)\n"); ceph_osdc_sync(&ceph_sb_to_client(sb)->osdc); ceph_mdsc_sync(&ceph_sb_to_client(sb)->mdsc); - dout("sync_fs %d done\n", wait); + dout("sync_fs (blocking) done\n"); return 0; } @@ -278,7 +287,7 @@ static const struct super_operations ceph_super_ops = { .alloc_inode = ceph_alloc_inode, .destroy_inode = ceph_destroy_inode, .write_inode = ceph_write_inode, - .sync_fs = ceph_syncfs, + .sync_fs = ceph_sync_fs, .put_super = ceph_put_super, .show_options = ceph_show_options, .statfs = ceph_statfs, -- cgit v1.2.3 From 8b67f04ab9de5d8f3a71aef72bf02c995a506db5 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Sun, 1 Aug 2010 23:14:20 -0400 Subject: ext4: Add mount options in superblock Allow mount options to be stored in the superblock. Also add default mount option bits for nobarrier, block_validity, discard, and nodelalloc. Signed-off-by: "Theodore Ts'o" --- fs/ext4/ext4.h | 9 +++++++-- fs/ext4/super.c | 29 +++++++++++++++++++++++------ 2 files changed, 30 insertions(+), 8 deletions(-) (limited to 'fs') diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 9ca3637eca5f..ed14e1db0832 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -1025,8 +1025,9 @@ struct ext4_super_block { __le32 s_last_error_line; /* line number where error happened */ __le64 s_last_error_block; /* block involved of last error */ __u8 s_last_error_func[32]; /* function where the error happened */ -#define EXT4_S_ERR_END offsetof(struct ext4_super_block, s_reserved) - __le32 s_reserved[128]; /* Padding to the end of the block */ +#define EXT4_S_ERR_END offsetof(struct ext4_super_block, s_mount_opts) + __u8 s_mount_opts[64]; + __le32 s_reserved[112]; /* Padding to the end of the block */ }; #define EXT4_S_ERR_LEN (EXT4_S_ERR_END - EXT4_S_ERR_START) @@ -1341,6 +1342,10 @@ EXT4_INODE_BIT_FNS(state, state_flags) #define EXT4_DEFM_JMODE_DATA 0x0020 #define EXT4_DEFM_JMODE_ORDERED 0x0040 #define EXT4_DEFM_JMODE_WBACK 0x0060 +#define EXT4_DEFM_NOBARRIER 0x0100 +#define EXT4_DEFM_BLOCK_VALIDITY 0x0200 +#define EXT4_DEFM_DISCARD 0x0400 +#define EXT4_DEFM_NODELALLOC 0x0800 /* * Default journal batch times diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 3e3f6484c223..3fd65eb66ccd 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -1003,10 +1003,10 @@ static int ext4_show_options(struct seq_file *seq, struct vfsmount *vfs) seq_puts(seq, ",journal_checksum"); if (test_opt(sb, I_VERSION)) seq_puts(seq, ",i_version"); - if (!test_opt(sb, DELALLOC)) + if (!test_opt(sb, DELALLOC) && + !(def_mount_opts & EXT4_DEFM_NODELALLOC)) seq_puts(seq, ",nodelalloc"); - if (sbi->s_stripe) seq_printf(seq, ",stripe=%lu", sbi->s_stripe); /* @@ -1030,7 +1030,7 @@ static int ext4_show_options(struct seq_file *seq, struct vfsmount *vfs) if (test_opt(sb, NO_AUTO_DA_ALLOC)) seq_puts(seq, ",noauto_da_alloc"); - if (test_opt(sb, DISCARD)) + if (test_opt(sb, DISCARD) && !(def_mount_opts & EXT4_DEFM_DISCARD)) seq_puts(seq, ",discard"); if (test_opt(sb, NOLOAD)) @@ -1039,6 +1039,10 @@ static int ext4_show_options(struct seq_file *seq, struct vfsmount *vfs) if (test_opt(sb, DIOREAD_NOLOCK)) seq_puts(seq, ",dioread_nolock"); + if (test_opt(sb, BLOCK_VALIDITY) && + !(def_mount_opts & EXT4_DEFM_BLOCK_VALIDITY)) + seq_puts(seq, ",block_validity"); + ext4_show_quota_options(seq, sb); return 0; @@ -2655,6 +2659,10 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) set_opt(sbi->s_mount_opt, ERRORS_CONT); else set_opt(sbi->s_mount_opt, ERRORS_RO); + if (def_mount_opts & EXT4_DEFM_BLOCK_VALIDITY) + set_opt(sbi->s_mount_opt, BLOCK_VALIDITY); + if (def_mount_opts & EXT4_DEFM_DISCARD) + set_opt(sbi->s_mount_opt, DISCARD); sbi->s_resuid = le16_to_cpu(es->s_def_resuid); sbi->s_resgid = le16_to_cpu(es->s_def_resgid); @@ -2662,15 +2670,23 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) sbi->s_min_batch_time = EXT4_DEF_MIN_BATCH_TIME; sbi->s_max_batch_time = EXT4_DEF_MAX_BATCH_TIME; - set_opt(sbi->s_mount_opt, BARRIER); + if ((def_mount_opts & EXT4_DEFM_NOBARRIER) == 0) + set_opt(sbi->s_mount_opt, BARRIER); /* * enable delayed allocation by default * Use -o nodelalloc to turn it off */ - if (!IS_EXT3_SB(sb)) + if (!IS_EXT3_SB(sb) && + ((def_mount_opts & EXT4_DEFM_NODELALLOC) == 0)) set_opt(sbi->s_mount_opt, DELALLOC); + if (!parse_options((char *) sbi->s_es->s_mount_opts, sb, + &journal_devnum, &journal_ioprio, NULL, 0)) { + ext4_msg(sb, KERN_WARNING, + "failed to parse options in superblock: %s", + sbi->s_es->s_mount_opts); + } if (!parse_options((char *) data, sb, &journal_devnum, &journal_ioprio, NULL, 0)) goto failed_mount; @@ -3141,7 +3157,8 @@ no_journal: descr = "out journal"; ext4_msg(sb, KERN_INFO, "mounted filesystem with%s. " - "Opts: %s", descr, orig_data); + "Opts: %s%s%s", descr, sbi->s_es->s_mount_opts, + *sbi->s_es->s_mount_opts ? "; " : "", orig_data); init_timer(&sbi->s_err_report); sbi->s_err_report.function = print_daily_error_info; -- cgit v1.2.3 From ea0d3ab239fba48d6e998b19c28d78f765963007 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Wed, 2 Jun 2010 13:24:43 +0900 Subject: LSM: Remove unused arguments from security_path_truncate(). When commit be6d3e56a6b9b3a4ee44a0685e39e595073c6f0d "introduce new LSM hooks where vfsmount is available." was proposed, regarding security_path_truncate(), only "struct file *" argument (which AppArmor wanted to use) was removed. But length and time_attrs arguments are not used by TOMOYO nor AppArmor. Thus, let's remove these arguments. Signed-off-by: Tetsuo Handa Acked-by: Nick Piggin Signed-off-by: James Morris --- fs/namei.c | 3 +-- fs/open.c | 5 ++--- include/linux/security.h | 11 +++-------- security/capability.c | 3 +-- security/security.c | 5 ++--- security/tomoyo/tomoyo.c | 3 +-- 6 files changed, 10 insertions(+), 20 deletions(-) (limited to 'fs') diff --git a/fs/namei.c b/fs/namei.c index 868d0cb9d473..fe34c2b879f4 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1484,8 +1484,7 @@ static int handle_truncate(struct path *path) */ error = locks_verify_locked(inode); if (!error) - error = security_path_truncate(path, 0, - ATTR_MTIME|ATTR_CTIME|ATTR_OPEN); + error = security_path_truncate(path); if (!error) { error = do_truncate(path->dentry, 0, ATTR_MTIME|ATTR_CTIME|ATTR_OPEN, diff --git a/fs/open.c b/fs/open.c index 5463266db9e6..a54ed85209c1 100644 --- a/fs/open.c +++ b/fs/open.c @@ -110,7 +110,7 @@ static long do_sys_truncate(const char __user *pathname, loff_t length) error = locks_verify_truncate(inode, NULL, length); if (!error) - error = security_path_truncate(&path, length, 0); + error = security_path_truncate(&path); if (!error) error = do_truncate(path.dentry, length, 0, NULL); @@ -165,8 +165,7 @@ static long do_sys_ftruncate(unsigned int fd, loff_t length, int small) error = locks_verify_truncate(inode, file, length); if (!error) - error = security_path_truncate(&file->f_path, length, - ATTR_MTIME|ATTR_CTIME); + error = security_path_truncate(&file->f_path); if (!error) error = do_truncate(dentry, length, ATTR_MTIME|ATTR_CTIME, file); out_putf: diff --git a/include/linux/security.h b/include/linux/security.h index 0c8819170463..723a93df756a 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -470,8 +470,6 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts) * @path_truncate: * Check permission before truncating a file. * @path contains the path structure for the file. - * @length is the new length of the file. - * @time_attrs is the flags passed to do_truncate(). * Return 0 if permission is granted. * @inode_getattr: * Check permission before obtaining file attributes. @@ -1412,8 +1410,7 @@ struct security_operations { int (*path_rmdir) (struct path *dir, struct dentry *dentry); int (*path_mknod) (struct path *dir, struct dentry *dentry, int mode, unsigned int dev); - int (*path_truncate) (struct path *path, loff_t length, - unsigned int time_attrs); + int (*path_truncate) (struct path *path); int (*path_symlink) (struct path *dir, struct dentry *dentry, const char *old_name); int (*path_link) (struct dentry *old_dentry, struct path *new_dir, @@ -2806,8 +2803,7 @@ int security_path_mkdir(struct path *dir, struct dentry *dentry, int mode); int security_path_rmdir(struct path *dir, struct dentry *dentry); int security_path_mknod(struct path *dir, struct dentry *dentry, int mode, unsigned int dev); -int security_path_truncate(struct path *path, loff_t length, - unsigned int time_attrs); +int security_path_truncate(struct path *path); int security_path_symlink(struct path *dir, struct dentry *dentry, const char *old_name); int security_path_link(struct dentry *old_dentry, struct path *new_dir, @@ -2841,8 +2837,7 @@ static inline int security_path_mknod(struct path *dir, struct dentry *dentry, return 0; } -static inline int security_path_truncate(struct path *path, loff_t length, - unsigned int time_attrs) +static inline int security_path_truncate(struct path *path) { return 0; } diff --git a/security/capability.c b/security/capability.c index 8168e3ecd5bf..4aeb699da1b3 100644 --- a/security/capability.c +++ b/security/capability.c @@ -268,8 +268,7 @@ static int cap_path_rename(struct path *old_path, struct dentry *old_dentry, return 0; } -static int cap_path_truncate(struct path *path, loff_t length, - unsigned int time_attrs) +static int cap_path_truncate(struct path *path) { return 0; } diff --git a/security/security.c b/security/security.c index 351942a4ca0e..e8c87b8601b4 100644 --- a/security/security.c +++ b/security/security.c @@ -417,12 +417,11 @@ int security_path_rename(struct path *old_dir, struct dentry *old_dentry, new_dentry); } -int security_path_truncate(struct path *path, loff_t length, - unsigned int time_attrs) +int security_path_truncate(struct path *path) { if (unlikely(IS_PRIVATE(path->dentry->d_inode))) return 0; - return security_ops->path_truncate(path, length, time_attrs); + return security_ops->path_truncate(path); } int security_path_chmod(struct dentry *dentry, struct vfsmount *mnt, diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c index 57d442e7339b..7be732cadd47 100644 --- a/security/tomoyo/tomoyo.c +++ b/security/tomoyo/tomoyo.c @@ -93,8 +93,7 @@ static int tomoyo_bprm_check_security(struct linux_binprm *bprm) return tomoyo_check_open_permission(domain, &bprm->file->f_path, O_RDONLY); } -static int tomoyo_path_truncate(struct path *path, loff_t length, - unsigned int time_attrs) +static int tomoyo_path_truncate(struct path *path) { return tomoyo_path_perm(TOMOYO_TYPE_TRUNCATE, path); } -- cgit v1.2.3 From 9cfcac810e8993fa7a5bfd24b1a21f1dbbb03a7b Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Fri, 23 Jul 2010 11:43:51 -0400 Subject: vfs: re-introduce MAY_CHDIR Currently MAY_ACCESS means that filesystems must check the permissions right then and not rely on cached results or the results of future operations on the object. This can be because of a call to sys_access() or because of a call to chdir() which needs to check search without relying on any future operations inside that dir. I plan to use MAY_ACCESS for other purposes in the security system, so I split the MAY_ACCESS and the MAY_CHDIR cases. Signed-off-by: Eric Paris Acked-by: Stephen D. Smalley Signed-off-by: James Morris --- fs/fuse/dir.c | 2 +- fs/nfs/dir.c | 2 +- fs/open.c | 6 +++--- include/linux/fs.h | 1 + 4 files changed, 6 insertions(+), 5 deletions(-) (limited to 'fs') diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 3cdc5f78a406..431be0795b6b 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -1016,7 +1016,7 @@ static int fuse_permission(struct inode *inode, int mask) exist. So if permissions are revoked this won't be noticed immediately, only after the attribute timeout has expired */ - } else if (mask & MAY_ACCESS) { + } else if (mask & (MAY_ACCESS | MAY_CHDIR)) { err = fuse_access(inode, mask); } else if ((mask & MAY_EXEC) && S_ISREG(inode->i_mode)) { if (!(inode->i_mode & S_IXUGO)) { diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index e60416d3f818..832e9e239324 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -1953,7 +1953,7 @@ int nfs_permission(struct inode *inode, int mask) if ((mask & (MAY_READ | MAY_WRITE | MAY_EXEC)) == 0) goto out; /* Is this sys_access() ? */ - if (mask & MAY_ACCESS) + if (mask & (MAY_ACCESS | MAY_CHDIR)) goto force_lookup; switch (inode->i_mode & S_IFMT) { diff --git a/fs/open.c b/fs/open.c index a54ed85209c1..0d1fa3dc0efb 100644 --- a/fs/open.c +++ b/fs/open.c @@ -366,7 +366,7 @@ SYSCALL_DEFINE1(chdir, const char __user *, filename) if (error) goto out; - error = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_ACCESS); + error = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_CHDIR); if (error) goto dput_and_out; @@ -395,7 +395,7 @@ SYSCALL_DEFINE1(fchdir, unsigned int, fd) if (!S_ISDIR(inode->i_mode)) goto out_putf; - error = inode_permission(inode, MAY_EXEC | MAY_ACCESS); + error = inode_permission(inode, MAY_EXEC | MAY_CHDIR); if (!error) set_fs_pwd(current->fs, &file->f_path); out_putf: @@ -413,7 +413,7 @@ SYSCALL_DEFINE1(chroot, const char __user *, filename) if (error) goto out; - error = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_ACCESS); + error = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_CHDIR); if (error) goto dput_and_out; diff --git a/include/linux/fs.h b/include/linux/fs.h index 68ca1b0491af..7d94b72f0346 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -53,6 +53,7 @@ struct inodes_stat_t { #define MAY_APPEND 8 #define MAY_ACCESS 16 #define MAY_OPEN 32 +#define MAY_CHDIR 64 /* * flags in file.f_mode. Note that FMODE_READ and FMODE_WRITE must correspond -- cgit v1.2.3 From d09ca73979460b96d5d4684d588b188be9a1f57d Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Fri, 23 Jul 2010 11:43:57 -0400 Subject: security: make LSMs explicitly mask off permissions SELinux needs to pass the MAY_ACCESS flag so it can handle auditting correctly. Presently the masking of MAY_* flags is done in the VFS. In order to allow LSMs to decide what flags they care about and what flags they don't just pass them all and the each LSM mask off what they don't need. This patch should contain no functional changes to either the VFS or any LSM. Signed-off-by: Eric Paris Acked-by: Stephen D. Smalley Signed-off-by: James Morris --- fs/namei.c | 3 +-- security/selinux/hooks.c | 2 ++ security/smack/smack_lsm.c | 2 ++ 3 files changed, 5 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/namei.c b/fs/namei.c index fe34c2b879f4..42d2d28fb827 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -282,8 +282,7 @@ int inode_permission(struct inode *inode, int mask) if (retval) return retval; - return security_inode_permission(inode, - mask & (MAY_READ|MAY_WRITE|MAY_EXEC|MAY_APPEND)); + return security_inode_permission(inode, mask); } /** diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 85338f0c0481..0c98846f188d 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -2645,6 +2645,8 @@ static int selinux_inode_permission(struct inode *inode, int mask) { const struct cred *cred = current_cred(); + mask &= (MAY_READ|MAY_WRITE|MAY_EXEC|MAY_APPEND); + if (!mask) { /* No permission to check. Existence test. */ return 0; diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 07abc9ce72f2..9192ba366a4c 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -598,6 +598,8 @@ static int smack_inode_rename(struct inode *old_inode, static int smack_inode_permission(struct inode *inode, int mask) { struct smk_audit_info ad; + + mask &= (MAY_READ|MAY_WRITE|MAY_EXEC|MAY_APPEND); /* * No permission to check. Existence test. Yup, it's there. */ -- cgit v1.2.3 From 0809f6ec18bbce54c996f5c36f4b9d371075c98b Mon Sep 17 00:00:00 2001 From: Steven Whitehouse Date: Mon, 2 Aug 2010 10:15:17 +0100 Subject: GFS2: Fix recovery stuck bug (try #2) This is a clean up of the code which deals with LM_FLAG_NOEXP which aims to remove any possible race conditions by using gl_spin to cover the gap between testing for the LM_FLAG_NOEXP and the GL_FROZEN flag. Signed-off-by: Steven Whitehouse --- fs/gfs2/glock.c | 46 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 39 insertions(+), 7 deletions(-) (limited to 'fs') diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c index 2b3d8f8a8393..9adf8f924e08 100644 --- a/fs/gfs2/glock.c +++ b/fs/gfs2/glock.c @@ -1063,6 +1063,9 @@ int gfs2_glock_nq(struct gfs2_holder *gh) spin_lock(&gl->gl_spin); add_to_queue(gh); + if ((LM_FLAG_NOEXP & gh->gh_flags) && + test_and_clear_bit(GLF_FROZEN, &gl->gl_flags)) + set_bit(GLF_REPLY_PENDING, &gl->gl_flags); run_queue(gl, 1); spin_unlock(&gl->gl_spin); @@ -1319,6 +1322,36 @@ void gfs2_glock_cb(struct gfs2_glock *gl, unsigned int state) gfs2_glock_put(gl); } +/** + * gfs2_should_freeze - Figure out if glock should be frozen + * @gl: The glock in question + * + * Glocks are not frozen if (a) the result of the dlm operation is + * an error, (b) the locking operation was an unlock operation or + * (c) if there is a "noexp" flagged request anywhere in the queue + * + * Returns: 1 if freezing should occur, 0 otherwise + */ + +static int gfs2_should_freeze(const struct gfs2_glock *gl) +{ + const struct gfs2_holder *gh; + + if (gl->gl_reply & ~LM_OUT_ST_MASK) + return 0; + if (gl->gl_target == LM_ST_UNLOCKED) + return 0; + + list_for_each_entry(gh, &gl->gl_holders, gh_list) { + if (test_bit(HIF_HOLDER, &gh->gh_iflags)) + continue; + if (LM_FLAG_NOEXP & gh->gh_flags) + return 0; + } + + return 1; +} + /** * gfs2_glock_complete - Callback used by locking * @gl: Pointer to the glock @@ -1329,18 +1362,17 @@ void gfs2_glock_cb(struct gfs2_glock *gl, unsigned int state) void gfs2_glock_complete(struct gfs2_glock *gl, int ret) { struct lm_lockstruct *ls = &gl->gl_sbd->sd_lockstruct; + gl->gl_reply = ret; + if (unlikely(test_bit(DFL_BLOCK_LOCKS, &ls->ls_flags))) { - struct gfs2_holder *gh; spin_lock(&gl->gl_spin); - gh = find_first_waiter(gl); - if ((!(gh && (gh->gh_flags & LM_FLAG_NOEXP)) && - (gl->gl_target != LM_ST_UNLOCKED)) || - ((ret & ~LM_OUT_ST_MASK) != 0)) + if (gfs2_should_freeze(gl)) { set_bit(GLF_FROZEN, &gl->gl_flags); - spin_unlock(&gl->gl_spin); - if (test_bit(GLF_FROZEN, &gl->gl_flags)) + spin_unlock(&gl->gl_spin); return; + } + spin_unlock(&gl->gl_spin); } set_bit(GLF_REPLY_PENDING, &gl->gl_flags); gfs2_glock_hold(gl); -- cgit v1.2.3 From abd2e44dca2c5d65e047224c6ba4b4c8059f97f8 Mon Sep 17 00:00:00 2001 From: Suresh Jayaraman Date: Tue, 22 Jun 2010 20:52:50 +0530 Subject: cifs: guard cifsglob.h against multiple inclusion Add conditional compile macros to guard the header file against multiple inclusion. Signed-off-by: Suresh Jayaraman Acked-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/cifsglob.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'fs') diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index a88479ceaad5..6b2c39d809fb 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -16,6 +16,9 @@ * the GNU Lesser General Public License for more details. * */ +#ifndef _CIFS_GLOB_H +#define _CIFS_GLOB_H + #include #include #include @@ -733,3 +736,5 @@ GLOBAL_EXTERN unsigned int cifs_min_small; /* min size of small buf pool */ GLOBAL_EXTERN unsigned int cifs_max_pending; /* MAX requests at once to server*/ extern const struct slow_work_ops cifs_oplock_break_ops; + +#endif /* _CIFS_GLOB_H */ -- cgit v1.2.3 From e4317ceca2bdf4bf91112a21e60f73b4c5a1a5da Mon Sep 17 00:00:00 2001 From: Suresh Jayaraman Date: Tue, 6 Jul 2010 18:00:10 +0530 Subject: cifs: remove an potentially confusing, obsolete comment The recent commit 6ca9f3bae8b1854794dfa63cdd3b88b7dfe24c13 modified the code so that filp is full instantiated whenever the file is created and passed back. The below comment is no longer true, remove it. Cc: Jeff Layton Signed-off-by: Suresh Jayaraman Signed-off-by: Steve French --- fs/cifs/dir.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'fs') diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index e7ae78b66fa1..a7de5e9fff11 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -130,12 +130,6 @@ cifs_bp_rename_retry: return full_path; } -/* - * When called with struct file pointer set to NULL, there is no way we could - * update file->private_data, but getting it stuck on openFileList provides a - * way to access it from cifs_fill_filedata and thereby set file->private_data - * from cifs_open. - */ struct cifsFileInfo * cifs_new_fileinfo(struct inode *newinode, __u16 fileHandle, struct file *file, struct vfsmount *mnt, unsigned int oflags) -- cgit v1.2.3 From c6332e237fb2ee54bc9c614291a006e4801e0f66 Mon Sep 17 00:00:00 2001 From: Suresh Jayaraman Date: Tue, 6 Jul 2010 17:59:46 +0530 Subject: cifs: remove unused ip_address field in struct TCP_Server_Info The ip_address field is not used and seems redundant as there is union addr already and I don't see any future use as well. Signed-off-by: Suresh Jayaraman Signed-off-by: Steve French --- fs/cifs/cifsglob.h | 1 - 1 file changed, 1 deletion(-) (limited to 'fs') diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 6b2c39d809fb..6113427651c4 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -145,7 +145,6 @@ struct TCP_Server_Info { struct list_head pending_mid_q; void *Server_NlsInfo; /* BB - placeholder for future NLS info */ unsigned short server_codepage; /* codepage for the server */ - unsigned long ip_address; /* IP addr for the server if known */ enum protocolEnum protocolType; char versionMajor; char versionMinor; -- cgit v1.2.3 From 3feb41cff8264e32a4d23ed829c3ed5369035f51 Mon Sep 17 00:00:00 2001 From: Suresh Jayaraman Date: Mon, 5 Jul 2010 18:11:33 +0530 Subject: cifs: add kernel config option for CIFS Client caching support Add a kernel config option to enable local caching for CIFS. Signed-off-by: Suresh Jayaraman Signed-off-by: Steve French --- fs/cifs/Kconfig | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'fs') diff --git a/fs/cifs/Kconfig b/fs/cifs/Kconfig index 80f352596807..5739fd7f88b4 100644 --- a/fs/cifs/Kconfig +++ b/fs/cifs/Kconfig @@ -131,6 +131,15 @@ config CIFS_DFS_UPCALL IP addresses) which is needed for implicit mounts of DFS junction points. If unsure, say N. +config CIFS_FSCACHE + bool "Provide CIFS client caching support (EXPERIMENTAL)" + depends on EXPERIMENTAL + depends on CIFS=m && FSCACHE || CIFS=y && FSCACHE=y + help + Makes CIFS FS-Cache capable. Say Y here if you want your CIFS data + to be cached locally on disk through the general filesystem cache + manager. If unsure, say N. + config CIFS_EXPERIMENTAL bool "CIFS Experimental Features (EXPERIMENTAL)" depends on CIFS && EXPERIMENTAL -- cgit v1.2.3 From c21dfb699f35b6b5508fb808bb0ca211a865f2c9 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Mon, 12 Jul 2010 13:50:14 -0700 Subject: fs/cifs: Remove unnecessary casts of private_data Signed-off-by: Joe Perches Signed-off-by: Steve French --- fs/cifs/file.c | 24 ++++++++++-------------- fs/cifs/inode.c | 4 ++-- fs/cifs/ioctl.c | 3 +-- 3 files changed, 13 insertions(+), 18 deletions(-) (limited to 'fs') diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 409e4f523e61..b5fb2a0607b0 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -427,7 +427,7 @@ static int cifs_reopen_file(struct file *file, bool can_flush) __u16 netfid; if (file->private_data) - pCifsFile = (struct cifsFileInfo *)file->private_data; + pCifsFile = file->private_data; else return -EBADF; @@ -565,8 +565,7 @@ int cifs_close(struct inode *inode, struct file *file) int xid, timeout; struct cifs_sb_info *cifs_sb; struct cifsTconInfo *pTcon; - struct cifsFileInfo *pSMBFile = - (struct cifsFileInfo *)file->private_data; + struct cifsFileInfo *pSMBFile = file->private_data; xid = GetXid(); @@ -641,8 +640,7 @@ int cifs_closedir(struct inode *inode, struct file *file) { int rc = 0; int xid; - struct cifsFileInfo *pCFileStruct = - (struct cifsFileInfo *)file->private_data; + struct cifsFileInfo *pCFileStruct = file->private_data; char *ptmp; cFYI(1, "Closedir inode = 0x%p", inode); @@ -863,8 +861,7 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock) length, pfLock, posix_lock_type, wait_flag); } else { - struct cifsFileInfo *fid = - (struct cifsFileInfo *)file->private_data; + struct cifsFileInfo *fid = file->private_data; if (numLock) { rc = CIFSSMBLock(xid, tcon, netfid, length, @@ -965,7 +962,7 @@ ssize_t cifs_user_write(struct file *file, const char __user *write_data, if (file->private_data == NULL) return -EBADF; - open_file = (struct cifsFileInfo *) file->private_data; + open_file = file->private_data; rc = generic_write_checks(file, poffset, &write_size, 0); if (rc) @@ -1067,7 +1064,7 @@ static ssize_t cifs_write(struct file *file, const char *write_data, if (file->private_data == NULL) return -EBADF; - open_file = (struct cifsFileInfo *)file->private_data; + open_file = file->private_data; xid = GetXid(); @@ -1651,8 +1648,7 @@ int cifs_fsync(struct file *file, int datasync) int xid; int rc = 0; struct cifsTconInfo *tcon; - struct cifsFileInfo *smbfile = - (struct cifsFileInfo *)file->private_data; + struct cifsFileInfo *smbfile = file->private_data; struct inode *inode = file->f_path.dentry->d_inode; xid = GetXid(); @@ -1756,7 +1752,7 @@ ssize_t cifs_user_read(struct file *file, char __user *read_data, FreeXid(xid); return rc; } - open_file = (struct cifsFileInfo *)file->private_data; + open_file = file->private_data; if ((file->f_flags & O_ACCMODE) == O_WRONLY) cFYI(1, "attempting read on write only file instance"); @@ -1837,7 +1833,7 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size, FreeXid(xid); return rc; } - open_file = (struct cifsFileInfo *)file->private_data; + open_file = file->private_data; if ((file->f_flags & O_ACCMODE) == O_WRONLY) cFYI(1, "attempting read on write only file instance"); @@ -1968,7 +1964,7 @@ static int cifs_readpages(struct file *file, struct address_space *mapping, FreeXid(xid); return rc; } - open_file = (struct cifsFileInfo *)file->private_data; + open_file = file->private_data; cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); pTcon = cifs_sb->tcon; diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 6f0683c68952..fe9b2f5fb492 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -288,7 +288,7 @@ int cifs_get_file_info_unix(struct file *filp) struct inode *inode = filp->f_path.dentry->d_inode; struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); struct cifsTconInfo *tcon = cifs_sb->tcon; - struct cifsFileInfo *cfile = (struct cifsFileInfo *) filp->private_data; + struct cifsFileInfo *cfile = filp->private_data; xid = GetXid(); rc = CIFSSMBUnixQFileInfo(xid, tcon, cfile->netfid, &find_data); @@ -515,7 +515,7 @@ int cifs_get_file_info(struct file *filp) struct inode *inode = filp->f_path.dentry->d_inode; struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); struct cifsTconInfo *tcon = cifs_sb->tcon; - struct cifsFileInfo *cfile = (struct cifsFileInfo *) filp->private_data; + struct cifsFileInfo *cfile = filp->private_data; xid = GetXid(); rc = CIFSSMBQFileInfo(xid, tcon, cfile->netfid, &find_data); diff --git a/fs/cifs/ioctl.c b/fs/cifs/ioctl.c index 505926f1ee6b..9d38a71c8e14 100644 --- a/fs/cifs/ioctl.c +++ b/fs/cifs/ioctl.c @@ -41,8 +41,7 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg) __u64 ExtAttrMask = 0; __u64 caps; struct cifsTconInfo *tcon; - struct cifsFileInfo *pSMBFile = - (struct cifsFileInfo *)filep->private_data; + struct cifsFileInfo *pSMBFile = filep->private_data; #endif /* CONFIG_CIFS_POSIX */ xid = GetXid(); -- cgit v1.2.3 From f579cf3cfd1e19ae5aab6929679d0c04bf1a6284 Mon Sep 17 00:00:00 2001 From: Suresh Jayaraman Date: Mon, 5 Jul 2010 18:11:50 +0530 Subject: cifs: register CIFS for caching Define CIFS for FS-Cache and register for caching. Upon registration the top-level index object cookie will be stuck to the netfs definition by FS-Cache. Signed-off-by: Suresh Jayaraman Signed-off-by: Steve French --- fs/cifs/Makefile | 2 ++ fs/cifs/cache.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ fs/cifs/cifsfs.c | 8 ++++++++ fs/cifs/fscache.h | 39 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 95 insertions(+) create mode 100644 fs/cifs/cache.c create mode 100644 fs/cifs/fscache.h (limited to 'fs') diff --git a/fs/cifs/Makefile b/fs/cifs/Makefile index 9948c0030e86..e2de709a6e5d 100644 --- a/fs/cifs/Makefile +++ b/fs/cifs/Makefile @@ -11,3 +11,5 @@ cifs-y := cifsfs.o cifssmb.o cifs_debug.o connect.o dir.o file.o inode.o \ cifs-$(CONFIG_CIFS_UPCALL) += cifs_spnego.o cifs-$(CONFIG_CIFS_DFS_UPCALL) += dns_resolve.o cifs_dfs_ref.o + +cifs-$(CONFIG_CIFS_FSCACHE) += cache.o diff --git a/fs/cifs/cache.c b/fs/cifs/cache.c new file mode 100644 index 000000000000..1cb5ffb017f1 --- /dev/null +++ b/fs/cifs/cache.c @@ -0,0 +1,46 @@ +/* + * fs/cifs/cache.c - CIFS filesystem cache index structure definitions + * + * Copyright (c) 2010 Novell, Inc. + * Authors(s): Suresh Jayaraman (sjayaraman@suse.de> + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "fscache.h" + +/* + * CIFS filesystem definition for FS-Cache + */ +struct fscache_netfs cifs_fscache_netfs = { + .name = "cifs", + .version = 0, +}; + +/* + * Register CIFS for caching with FS-Cache + */ +int cifs_fscache_register(void) +{ + return fscache_register_netfs(&cifs_fscache_netfs); +} + +/* + * Unregister CIFS for caching + */ +void cifs_fscache_unregister(void) +{ + fscache_unregister_netfs(&cifs_fscache_netfs); +} + diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 2cb1a70214d7..24d7f4ab3b65 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -47,6 +47,7 @@ #include #include "dns_resolve.h" #include "cifs_spnego.h" +#include "fscache.h" #define CIFS_MAGIC_NUMBER 0xFF534D42 /* the first four bytes of SMB PDUs */ int cifsFYI = 0; @@ -902,6 +903,10 @@ init_cifs(void) cFYI(1, "cifs_max_pending set to max of 256"); } + rc = cifs_fscache_register(); + if (rc) + goto out; + rc = cifs_init_inodecache(); if (rc) goto out_clean_proc; @@ -951,6 +956,8 @@ init_cifs(void) cifs_destroy_inodecache(); out_clean_proc: cifs_proc_clean(); + cifs_fscache_unregister(); + out: return rc; } @@ -959,6 +966,7 @@ exit_cifs(void) { cFYI(DBG2, "exit_cifs"); cifs_proc_clean(); + cifs_fscache_unregister(); #ifdef CONFIG_CIFS_DFS_UPCALL cifs_dfs_release_automount_timer(); cifs_exit_dns_resolver(); diff --git a/fs/cifs/fscache.h b/fs/cifs/fscache.h new file mode 100644 index 000000000000..14dae1975d78 --- /dev/null +++ b/fs/cifs/fscache.h @@ -0,0 +1,39 @@ +/* + * fs/cifs/fscache.h - CIFS filesystem cache interface definitions + * + * Copyright (c) 2010 Novell, Inc. + * Authors(s): Suresh Jayaraman (sjayaraman@suse.de> + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef _CIFS_FSCACHE_H +#define _CIFS_FSCACHE_H + +#include + +#ifdef CONFIG_CIFS_FSCACHE + +extern struct fscache_netfs cifs_fscache_netfs; + +extern int cifs_fscache_register(void); +extern void cifs_fscache_unregister(void); + +#else /* CONFIG_CIFS_FSCACHE */ +static inline int cifs_fscache_register(void) { return 0; } +static inline void cifs_fscache_unregister(void) {} + +#endif /* CONFIG_CIFS_FSCACHE */ + +#endif /* _CIFS_FSCACHE_H */ -- cgit v1.2.3 From 488f1d2d6cc9d665c9f09e4b54f77052732e3058 Mon Sep 17 00:00:00 2001 From: Suresh Jayaraman Date: Mon, 5 Jul 2010 18:12:15 +0530 Subject: cifs: define server-level cache index objects and register them Define server-level cache index objects (as managed by TCP_ServerInfo structs) and register then with FS-Cache. Each server object is created in the CIFS top-level index object and is itself an index into which superblock-level objects are inserted. The server objects are now keyed by {IPaddress,family,port} tuple. Signed-off-by: Suresh Jayaraman Signed-off-by: Steve French --- fs/cifs/Makefile | 2 +- fs/cifs/cache.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ fs/cifs/cifsglob.h | 3 +++ fs/cifs/connect.c | 5 +++++ fs/cifs/fscache.c | 41 ++++++++++++++++++++++++++++++++++++ fs/cifs/fscache.h | 14 ++++++++++++ 6 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 fs/cifs/fscache.c (limited to 'fs') diff --git a/fs/cifs/Makefile b/fs/cifs/Makefile index e2de709a6e5d..adefa60a9bdc 100644 --- a/fs/cifs/Makefile +++ b/fs/cifs/Makefile @@ -12,4 +12,4 @@ cifs-$(CONFIG_CIFS_UPCALL) += cifs_spnego.o cifs-$(CONFIG_CIFS_DFS_UPCALL) += dns_resolve.o cifs_dfs_ref.o -cifs-$(CONFIG_CIFS_FSCACHE) += cache.o +cifs-$(CONFIG_CIFS_FSCACHE) += fscache.o cache.o diff --git a/fs/cifs/cache.c b/fs/cifs/cache.c index 1cb5ffb017f1..f46468fb6a90 100644 --- a/fs/cifs/cache.c +++ b/fs/cifs/cache.c @@ -19,6 +19,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "fscache.h" +#include "cifs_debug.h" /* * CIFS filesystem definition for FS-Cache @@ -44,3 +45,64 @@ void cifs_fscache_unregister(void) fscache_unregister_netfs(&cifs_fscache_netfs); } +/* + * Key layout of CIFS server cache index object + */ +struct cifs_server_key { + uint16_t family; /* address family */ + uint16_t port; /* IP port */ + union { + struct in_addr ipv4_addr; + struct in6_addr ipv6_addr; + } addr[0]; +}; + +/* + * Server object keyed by {IPaddress,port,family} tuple + */ +static uint16_t cifs_server_get_key(const void *cookie_netfs_data, + void *buffer, uint16_t maxbuf) +{ + const struct TCP_Server_Info *server = cookie_netfs_data; + const struct sockaddr *sa = (struct sockaddr *) &server->addr.sockAddr; + struct cifs_server_key *key = buffer; + uint16_t key_len = sizeof(struct cifs_server_key); + + memset(key, 0, key_len); + + /* + * Should not be a problem as sin_family/sin6_family overlays + * sa_family field + */ + switch (sa->sa_family) { + case AF_INET: + key->family = server->addr.sockAddr.sin_family; + key->port = server->addr.sockAddr.sin_port; + key->addr[0].ipv4_addr = server->addr.sockAddr.sin_addr; + key_len += sizeof(key->addr[0].ipv4_addr); + break; + + case AF_INET6: + key->family = server->addr.sockAddr6.sin6_family; + key->port = server->addr.sockAddr6.sin6_port; + key->addr[0].ipv6_addr = server->addr.sockAddr6.sin6_addr; + key_len += sizeof(key->addr[0].ipv6_addr); + break; + + default: + cERROR(1, "CIFS: Unknown network family '%d'", sa->sa_family); + key_len = 0; + break; + } + + return key_len; +} + +/* + * Server object for FS-Cache + */ +const struct fscache_cookie_def cifs_fscache_server_index_def = { + .name = "CIFS.server", + .type = FSCACHE_COOKIE_TYPE_INDEX, + .get_key = cifs_server_get_key, +}; diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 6113427651c4..06b48998db94 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -192,6 +192,9 @@ struct TCP_Server_Info { bool sec_mskerberos; /* supports legacy MS Kerberos */ bool sec_kerberosu2u; /* supports U2U Kerberos */ bool sec_ntlmssp; /* supports NTLMSSP */ +#ifdef CONFIG_CIFS_FSCACHE + struct fscache_cookie *fscache; /* client index cache cookie */ +#endif }; /* diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 2208f06e4c45..90354e39e565 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -48,6 +48,7 @@ #include "nterr.h" #include "rfc1002pdu.h" #include "cn_cifs.h" +#include "fscache.h" #define CIFS_PORT 445 #define RFC1001_PORT 139 @@ -1460,6 +1461,8 @@ cifs_put_tcp_session(struct TCP_Server_Info *server) server->tcpStatus = CifsExiting; spin_unlock(&GlobalMid_Lock); + cifs_fscache_release_client_cookie(server); + task = xchg(&server->tsk, NULL); if (task) force_sig(SIGKILL, task); @@ -1577,6 +1580,8 @@ cifs_get_tcp_session(struct smb_vol *volume_info) list_add(&tcp_ses->tcp_ses_list, &cifs_tcp_ses_list); write_unlock(&cifs_tcp_ses_lock); + cifs_fscache_get_client_cookie(tcp_ses); + return tcp_ses; out_err: diff --git a/fs/cifs/fscache.c b/fs/cifs/fscache.c new file mode 100644 index 000000000000..ea97b76f8aa6 --- /dev/null +++ b/fs/cifs/fscache.c @@ -0,0 +1,41 @@ +/* + * fs/cifs/fscache.c - CIFS filesystem cache interface + * + * Copyright (c) 2010 Novell, Inc. + * Author(s): Suresh Jayaraman (sjayaraman@suse.de> + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "fscache.h" +#include "cifsglob.h" +#include "cifs_debug.h" + +void cifs_fscache_get_client_cookie(struct TCP_Server_Info *server) +{ + server->fscache = + fscache_acquire_cookie(cifs_fscache_netfs.primary_index, + &cifs_fscache_server_index_def, server); + cFYI(1, "CIFS: get client cookie (0x%p/0x%p)", server, + server->fscache); +} + +void cifs_fscache_release_client_cookie(struct TCP_Server_Info *server) +{ + cFYI(1, "CIFS: release client cookie (0x%p/0x%p)", server, + server->fscache); + fscache_relinquish_cookie(server->fscache, 0); + server->fscache = NULL; +} + diff --git a/fs/cifs/fscache.h b/fs/cifs/fscache.h index 14dae1975d78..8972306c58a5 100644 --- a/fs/cifs/fscache.h +++ b/fs/cifs/fscache.h @@ -23,17 +23,31 @@ #include +#include "cifsglob.h" + #ifdef CONFIG_CIFS_FSCACHE extern struct fscache_netfs cifs_fscache_netfs; +extern const struct fscache_cookie_def cifs_fscache_server_index_def; extern int cifs_fscache_register(void); extern void cifs_fscache_unregister(void); +/* + * fscache.c + */ +extern void cifs_fscache_get_client_cookie(struct TCP_Server_Info *); +extern void cifs_fscache_release_client_cookie(struct TCP_Server_Info *); + #else /* CONFIG_CIFS_FSCACHE */ static inline int cifs_fscache_register(void) { return 0; } static inline void cifs_fscache_unregister(void) {} +static inline void +cifs_fscache_get_client_cookie(struct TCP_Server_Info *server) {} +static inline void +cifs_fscache_get_client_cookie(struct TCP_Server_Info *server); {} + #endif /* CONFIG_CIFS_FSCACHE */ #endif /* _CIFS_FSCACHE_H */ -- cgit v1.2.3 From 50d971602a6c4bf1abe1f3873686f431d7539dfe Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 6 Jul 2010 20:43:01 -0400 Subject: cifs: set the port in sockaddr in a more clearly defined fashion This patch should replace the patch I sent a couple of weeks ago to set the port in cifs_convert_address. Currently we set this in cifs_find_tcp_session, but that's more of a side effect than anything. Add a new function called cifs_fill_sockaddr. Have it call cifs_convert_address and then set the port. This also allows us to skip passing in the port as a separate parm to cifs_find_tcp_session. Also, change cifs_convert_address take a struct sockaddr * rather than void * to make it clearer how this function should be called. Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/cifsproto.h | 4 +++- fs/cifs/connect.c | 12 +++++------- fs/cifs/dns_resolve.c | 2 +- fs/cifs/netmisc.c | 23 ++++++++++++++++++++++- 4 files changed, 31 insertions(+), 10 deletions(-) (limited to 'fs') diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index fb6318b81509..2eaebbd31132 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -86,7 +86,9 @@ extern unsigned int smbCalcSize(struct smb_hdr *ptr); extern unsigned int smbCalcSize_LE(struct smb_hdr *ptr); extern int decode_negTokenInit(unsigned char *security_blob, int length, struct TCP_Server_Info *server); -extern int cifs_convert_address(char *src, void *dst); +extern int cifs_convert_address(struct sockaddr *dst, char *src); +extern int cifs_fill_sockaddr(struct sockaddr *dst, char *src, + unsigned short int port); extern int map_smb_to_linux_error(struct smb_hdr *smb, int logErr); extern void header_assemble(struct smb_hdr *, char /* command */ , const struct cifsTconInfo *, int /* length of diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 90354e39e565..eca86256709b 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -1382,7 +1382,7 @@ cifs_parse_mount_options(char *options, const char *devname, } static struct TCP_Server_Info * -cifs_find_tcp_session(struct sockaddr_storage *addr, unsigned short int port) +cifs_find_tcp_session(struct sockaddr_storage *addr) { struct list_head *tmp; struct TCP_Server_Info *server; @@ -1406,7 +1406,6 @@ cifs_find_tcp_session(struct sockaddr_storage *addr, unsigned short int port) case AF_INET: if (addr4->sin_addr.s_addr == server->addr.sockAddr.sin_addr.s_addr) { - addr4->sin_port = htons(port); /* user overrode default port? */ if (addr4->sin_port) { if (addr4->sin_port != @@ -1422,7 +1421,6 @@ cifs_find_tcp_session(struct sockaddr_storage *addr, unsigned short int port) &server->addr.sockAddr6.sin6_addr) && (addr6->sin6_scope_id == server->addr.sockAddr6.sin6_scope_id)) { - addr6->sin6_port = htons(port); /* user overrode default port? */ if (addr6->sin6_port) { if (addr6->sin6_port != @@ -1482,7 +1480,9 @@ cifs_get_tcp_session(struct smb_vol *volume_info) cFYI(1, "UNC: %s ip: %s", volume_info->UNC, volume_info->UNCip); if (volume_info->UNCip && volume_info->UNC) { - rc = cifs_convert_address(volume_info->UNCip, &addr); + rc = cifs_fill_sockaddr((struct sockaddr *)&addr, + volume_info->UNCip, + volume_info->port); if (!rc) { /* we failed translating address */ rc = -EINVAL; @@ -1502,7 +1502,7 @@ cifs_get_tcp_session(struct smb_vol *volume_info) } /* see if we already have a matching tcp_ses */ - tcp_ses = cifs_find_tcp_session(&addr, volume_info->port); + tcp_ses = cifs_find_tcp_session(&addr); if (tcp_ses) return tcp_ses; @@ -1546,12 +1546,10 @@ cifs_get_tcp_session(struct smb_vol *volume_info) cFYI(1, "attempting ipv6 connect"); /* BB should we allow ipv6 on port 139? */ /* other OS never observed in Wild doing 139 with v6 */ - sin_server6->sin6_port = htons(volume_info->port); memcpy(&tcp_ses->addr.sockAddr6, sin_server6, sizeof(struct sockaddr_in6)); rc = ipv6_connect(tcp_ses); } else { - sin_server->sin_port = htons(volume_info->port); memcpy(&tcp_ses->addr.sockAddr, sin_server, sizeof(struct sockaddr_in)); rc = ipv4_connect(tcp_ses); diff --git a/fs/cifs/dns_resolve.c b/fs/cifs/dns_resolve.c index 853a968e82d7..3ad7f4300c45 100644 --- a/fs/cifs/dns_resolve.c +++ b/fs/cifs/dns_resolve.c @@ -44,7 +44,7 @@ is_ip(char *name) { struct sockaddr_storage ss; - return cifs_convert_address(name, &ss); + return cifs_convert_address((struct sockaddr *)&ss, name); } static int diff --git a/fs/cifs/netmisc.c b/fs/cifs/netmisc.c index d35d52889cb5..3489468d070b 100644 --- a/fs/cifs/netmisc.c +++ b/fs/cifs/netmisc.c @@ -164,7 +164,7 @@ cifs_inet_pton(const int address_family, const char *cp, void *dst) * Returns 0 on failure. */ int -cifs_convert_address(char *src, void *dst) +cifs_convert_address(struct sockaddr *dst, char *src) { int rc; char *pct, *endp; @@ -201,6 +201,27 @@ cifs_convert_address(char *src, void *dst) return rc; } +int +cifs_fill_sockaddr(struct sockaddr *dst, char *src, + const unsigned short int port) +{ + if (!cifs_convert_address(dst, src)) + return 0; + + switch (dst->sa_family) { + case AF_INET: + ((struct sockaddr_in *)dst)->sin_port = htons(port); + break; + case AF_INET6: + ((struct sockaddr_in6 *)dst)->sin6_port = htons(port); + break; + default: + return 0; + } + + return 1; +} + /***************************************************************************** convert a NT status code to a dos class/code *****************************************************************************/ -- cgit v1.2.3 From 4515148ef72bfda4ce3c8754149711d9972867ce Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 6 Jul 2010 20:43:02 -0400 Subject: cifs: move address comparison into separate function Move the address comparator out of cifs_find_tcp_session and into a separate function for cleanliness. Also change the argument to that function to a "struct sockaddr" pointer. Passing pointers to sockaddr_storage is a little odd since that struct is generally for declaring static storage. Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/connect.c | 73 +++++++++++++++++++++++++++---------------------------- 1 file changed, 36 insertions(+), 37 deletions(-) (limited to 'fs') diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index eca86256709b..65e760b9428f 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -1381,18 +1381,44 @@ cifs_parse_mount_options(char *options, const char *devname, return 0; } +static bool +match_address(struct TCP_Server_Info *server, struct sockaddr *addr) +{ + struct sockaddr_in *addr4 = (struct sockaddr_in *)addr; + struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr; + + switch (addr->sa_family) { + case AF_INET: + if (addr4->sin_addr.s_addr != + server->addr.sockAddr.sin_addr.s_addr) + return false; + if (addr4->sin_port && + addr4->sin_port != server->addr.sockAddr.sin_port) + return false; + break; + case AF_INET6: + if (!ipv6_addr_equal(&addr6->sin6_addr, + &server->addr.sockAddr6.sin6_addr)) + return false; + if (addr6->sin6_scope_id != + server->addr.sockAddr6.sin6_scope_id) + return false; + if (addr6->sin6_port && + addr6->sin6_port != server->addr.sockAddr6.sin6_port) + return false; + break; + } + + return true; +} + static struct TCP_Server_Info * -cifs_find_tcp_session(struct sockaddr_storage *addr) +cifs_find_tcp_session(struct sockaddr *addr) { - struct list_head *tmp; struct TCP_Server_Info *server; - struct sockaddr_in *addr4 = (struct sockaddr_in *) addr; - struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *) addr; write_lock(&cifs_tcp_ses_lock); - list_for_each(tmp, &cifs_tcp_ses_list) { - server = list_entry(tmp, struct TCP_Server_Info, - tcp_ses_list); + list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) { /* * the demux thread can exit on its own while still in CifsNew * so don't accept any sockets in that state. Since the @@ -1402,35 +1428,8 @@ cifs_find_tcp_session(struct sockaddr_storage *addr) if (server->tcpStatus == CifsNew) continue; - switch (addr->ss_family) { - case AF_INET: - if (addr4->sin_addr.s_addr == - server->addr.sockAddr.sin_addr.s_addr) { - /* user overrode default port? */ - if (addr4->sin_port) { - if (addr4->sin_port != - server->addr.sockAddr.sin_port) - continue; - } - break; - } else - continue; - - case AF_INET6: - if (ipv6_addr_equal(&addr6->sin6_addr, - &server->addr.sockAddr6.sin6_addr) && - (addr6->sin6_scope_id == - server->addr.sockAddr6.sin6_scope_id)) { - /* user overrode default port? */ - if (addr6->sin6_port) { - if (addr6->sin6_port != - server->addr.sockAddr6.sin6_port) - continue; - } - break; - } else - continue; - } + if (!match_address(server, addr)) + continue; ++server->srv_count; write_unlock(&cifs_tcp_ses_lock); @@ -1502,7 +1501,7 @@ cifs_get_tcp_session(struct smb_vol *volume_info) } /* see if we already have a matching tcp_ses */ - tcp_ses = cifs_find_tcp_session(&addr); + tcp_ses = cifs_find_tcp_session((struct sockaddr *)&addr); if (tcp_ses) return tcp_ses; -- cgit v1.2.3 From daf5b0b6f3f6d7b15c2600426cc6c60a0e155218 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 6 Jul 2010 20:43:02 -0400 Subject: cifs: match secType when searching for existing tcp session The secType is a per-tcp session entity, but the current routine doesn't verify that it is acceptible when attempting to match an existing TCP session. Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/cifsglob.h | 3 +-- fs/cifs/connect.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 54 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 06b48998db94..8fb1d10b8742 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -83,8 +83,7 @@ enum statusEnum { }; enum securityEnum { - PLAINTXT = 0, /* Legacy with Plaintext passwords */ - LANMAN, /* Legacy LANMAN auth */ + LANMAN = 0, /* Legacy LANMAN auth */ NTLM, /* Legacy NTLM012 auth with NTLM hash */ NTLMv2, /* Legacy NTLM auth with NTLMv2 hash */ RawNTLMSSP, /* NTLMSSP without SPNEGO, NTLMv2 hash */ diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 65e760b9428f..b24e4cea4e3c 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -1412,8 +1412,56 @@ match_address(struct TCP_Server_Info *server, struct sockaddr *addr) return true; } +static bool +match_security(struct TCP_Server_Info *server, struct smb_vol *vol) +{ + unsigned int secFlags; + + if (vol->secFlg & (~(CIFSSEC_MUST_SIGN | CIFSSEC_MUST_SEAL))) + secFlags = vol->secFlg; + else + secFlags = global_secflags | vol->secFlg; + + switch (server->secType) { + case LANMAN: + if (!(secFlags & (CIFSSEC_MAY_LANMAN|CIFSSEC_MAY_PLNTXT))) + return false; + break; + case NTLMv2: + if (!(secFlags & CIFSSEC_MAY_NTLMV2)) + return false; + break; + case NTLM: + if (!(secFlags & CIFSSEC_MAY_NTLM)) + return false; + break; + case Kerberos: + if (!(secFlags & CIFSSEC_MAY_KRB5)) + return false; + break; + case RawNTLMSSP: + if (!(secFlags & CIFSSEC_MAY_NTLMSSP)) + return false; + break; + default: + /* shouldn't happen */ + return false; + } + + /* now check if signing mode is acceptible */ + if ((secFlags & CIFSSEC_MAY_SIGN) == 0 && + (server->secMode & SECMODE_SIGN_REQUIRED)) + return false; + else if (((secFlags & CIFSSEC_MUST_SIGN) == CIFSSEC_MUST_SIGN) && + (server->secMode & + (SECMODE_SIGN_ENABLED|SECMODE_SIGN_REQUIRED)) == 0) + return false; + + return true; +} + static struct TCP_Server_Info * -cifs_find_tcp_session(struct sockaddr *addr) +cifs_find_tcp_session(struct sockaddr *addr, struct smb_vol *vol) { struct TCP_Server_Info *server; @@ -1431,6 +1479,9 @@ cifs_find_tcp_session(struct sockaddr *addr) if (!match_address(server, addr)) continue; + if (!match_security(server, vol)) + continue; + ++server->srv_count; write_unlock(&cifs_tcp_ses_lock); cFYI(1, "Existing tcp session with server found"); @@ -1501,7 +1552,7 @@ cifs_get_tcp_session(struct smb_vol *volume_info) } /* see if we already have a matching tcp_ses */ - tcp_ses = cifs_find_tcp_session((struct sockaddr *)&addr); + tcp_ses = cifs_find_tcp_session((struct sockaddr *)&addr, volume_info); if (tcp_ses) return tcp_ses; -- cgit v1.2.3 From 4ff67b720c02c36e54d55b88c2931879b7db1cd2 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 6 Jul 2010 20:43:02 -0400 Subject: cifs: clean up cifs_find_smb_ses (try #2) This patch replaces the earlier patch by the same name. The only difference is that MAX_PASSWORD_SIZE has been increased to attempt to match the limits that windows enforces. Do a better job of matching sessions by authtype. Matching by username for a Kerberos session is incorrect, and anonymous sessions need special handling. Also, in the case where we do match by username, we also need to match by password. That ensures that someone else doesn't "borrow" an existing session without needing to know the password. Finally, passwords can be longer than 16 bytes. Bump MAX_PASSWORD_SIZE to 512 to match the size that the userspace mount helper allows. Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/cifsglob.h | 2 +- fs/cifs/connect.c | 26 ++++++++++++++++++-------- 2 files changed, 19 insertions(+), 9 deletions(-) (limited to 'fs') diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 8fb1d10b8742..7b91cb4f0da4 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -37,7 +37,7 @@ #define MAX_SHARE_SIZE 64 /* used to be 20, this should still be enough */ #define MAX_USERNAME_SIZE 32 /* 32 is to allow for 15 char names + null termination then *2 for unicode versions */ -#define MAX_PASSWORD_SIZE 16 +#define MAX_PASSWORD_SIZE 512 /* max for windows seems to be 256 wide chars */ #define CIFS_MIN_RCV_POOL 4 diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index b24e4cea4e3c..b2063ce113ec 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -1644,17 +1644,27 @@ out_err: } static struct cifsSesInfo * -cifs_find_smb_ses(struct TCP_Server_Info *server, char *username) +cifs_find_smb_ses(struct TCP_Server_Info *server, struct smb_vol *vol) { - struct list_head *tmp; struct cifsSesInfo *ses; write_lock(&cifs_tcp_ses_lock); - list_for_each(tmp, &server->smb_ses_list) { - ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list); - if (strncmp(ses->userName, username, MAX_USERNAME_SIZE)) - continue; - + list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { + switch (server->secType) { + case Kerberos: + if (vol->linux_uid != ses->linux_uid) + continue; + break; + default: + /* anything else takes username/password */ + if (strncmp(ses->userName, vol->username, + MAX_USERNAME_SIZE)) + continue; + if (strlen(vol->username) != 0 && + strncmp(ses->password, vol->password, + MAX_PASSWORD_SIZE)) + continue; + } ++ses->ses_count; write_unlock(&cifs_tcp_ses_lock); return ses; @@ -1696,7 +1706,7 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb_vol *volume_info) xid = GetXid(); - ses = cifs_find_smb_ses(server, volume_info->username); + ses = cifs_find_smb_ses(server, volume_info); if (ses) { cFYI(1, "Existing smb sess found (status=%d)", ses->status); -- cgit v1.2.3 From 8913007e67106597fed4b9dd3787e8dca6915a83 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 6 Jul 2010 20:43:08 -0400 Subject: cifs: remove unused cifsUidInfo struct Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/cifsglob.h | 16 ---------------- 1 file changed, 16 deletions(-) (limited to 'fs') diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 7b91cb4f0da4..793c8e3a0e53 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -196,19 +196,6 @@ struct TCP_Server_Info { #endif }; -/* - * The following is our shortcut to user information. We surface the uid, - * and name. We always get the password on the fly in case it - * has changed. We also hang a list of sessions owned by this user off here. - */ -struct cifsUidInfo { - struct list_head userList; - struct list_head sessionList; /* SMB sessions for this user */ - uid_t linux_uid; - char user[MAX_USERNAME_SIZE + 1]; /* ascii name of user */ - /* BB may need ptr or callback for PAM or WinBind info */ -}; - /* * Session structure. One of these for each uid session with a particular host */ @@ -216,9 +203,6 @@ struct cifsSesInfo { struct list_head smb_ses_list; struct list_head tcon_list; struct mutex session_mutex; -#if 0 - struct cifsUidInfo *uidInfo; /* pointer to user info */ -#endif struct TCP_Server_Info *server; /* pointer to server info */ int ses_count; /* reference counter */ enum statusEnum status; -- cgit v1.2.3 From d03382ce9a89dbe27cba25130f0b90c0d631d5c5 Mon Sep 17 00:00:00 2001 From: Suresh Jayaraman Date: Mon, 5 Jul 2010 18:12:27 +0530 Subject: cifs: define superblock-level cache index objects and register them Define superblock-level cache index objects (managed by cifsTconInfo structs). Each superblock object is created in a server-level index object and in itself an index into which inode-level objects are inserted. The superblock object is keyed by sharename. The UniqueId/IndexNumber is used to validate that the exported share is the same since we accessed it last time. Signed-off-by: Suresh Jayaraman Signed-off-by: Steve French --- fs/cifs/cache.c | 109 +++++++++++++++++++++++++++++++++++++++++++++++++++++ fs/cifs/cifsglob.h | 4 ++ fs/cifs/connect.c | 3 ++ fs/cifs/fscache.c | 17 +++++++++ fs/cifs/fscache.h | 6 +++ fs/cifs/inode.c | 3 ++ 6 files changed, 142 insertions(+) (limited to 'fs') diff --git a/fs/cifs/cache.c b/fs/cifs/cache.c index f46468fb6a90..15532abdac14 100644 --- a/fs/cifs/cache.c +++ b/fs/cifs/cache.c @@ -106,3 +106,112 @@ const struct fscache_cookie_def cifs_fscache_server_index_def = { .type = FSCACHE_COOKIE_TYPE_INDEX, .get_key = cifs_server_get_key, }; + +/* + * Auxiliary data attached to CIFS superblock within the cache + */ +struct cifs_fscache_super_auxdata { + u64 resource_id; /* unique server resource id */ +}; + +static char *extract_sharename(const char *treename) +{ + const char *src; + char *delim, *dst; + int len; + + /* skip double chars at the beginning */ + src = treename + 2; + + /* share name is always preceded by '\\' now */ + delim = strchr(src, '\\'); + if (!delim) + return ERR_PTR(-EINVAL); + delim++; + len = strlen(delim); + + /* caller has to free the memory */ + dst = kstrndup(delim, len, GFP_KERNEL); + if (!dst) + return ERR_PTR(-ENOMEM); + + return dst; +} + +/* + * Superblock object currently keyed by share name + */ +static uint16_t cifs_super_get_key(const void *cookie_netfs_data, void *buffer, + uint16_t maxbuf) +{ + const struct cifsTconInfo *tcon = cookie_netfs_data; + char *sharename; + uint16_t len; + + sharename = extract_sharename(tcon->treeName); + if (IS_ERR(sharename)) { + cFYI(1, "CIFS: couldn't extract sharename\n"); + sharename = NULL; + return 0; + } + + len = strlen(sharename); + if (len > maxbuf) + return 0; + + memcpy(buffer, sharename, len); + + kfree(sharename); + + return len; +} + +static uint16_t +cifs_fscache_super_get_aux(const void *cookie_netfs_data, void *buffer, + uint16_t maxbuf) +{ + struct cifs_fscache_super_auxdata auxdata; + const struct cifsTconInfo *tcon = cookie_netfs_data; + + memset(&auxdata, 0, sizeof(auxdata)); + auxdata.resource_id = tcon->resource_id; + + if (maxbuf > sizeof(auxdata)) + maxbuf = sizeof(auxdata); + + memcpy(buffer, &auxdata, maxbuf); + + return maxbuf; +} + +static enum +fscache_checkaux cifs_fscache_super_check_aux(void *cookie_netfs_data, + const void *data, + uint16_t datalen) +{ + struct cifs_fscache_super_auxdata auxdata; + const struct cifsTconInfo *tcon = cookie_netfs_data; + + if (datalen != sizeof(auxdata)) + return FSCACHE_CHECKAUX_OBSOLETE; + + memset(&auxdata, 0, sizeof(auxdata)); + auxdata.resource_id = tcon->resource_id; + + if (memcmp(data, &auxdata, datalen) != 0) + return FSCACHE_CHECKAUX_OBSOLETE; + + return FSCACHE_CHECKAUX_OKAY; +} + +/* + * Superblock object for FS-Cache + */ +const struct fscache_cookie_def cifs_fscache_super_index_def = { + .name = "CIFS.super", + .type = FSCACHE_COOKIE_TYPE_INDEX, + .get_key = cifs_super_get_key, + .get_aux = cifs_fscache_super_get_aux, + .check_aux = cifs_fscache_super_check_aux, +}; + diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 793c8e3a0e53..a3e403e7e163 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -299,6 +299,10 @@ struct cifsTconInfo { bool local_lease:1; /* check leases (only) on local system not remote */ bool broken_posix_open; /* e.g. Samba server versions < 3.3.2, 3.2.9 */ bool need_reconnect:1; /* connection reset, tid now invalid */ +#ifdef CONFIG_CIFS_FSCACHE + u64 resource_id; /* server resource id */ + struct fscache_cookie *fscache; /* cookie for share */ +#endif /* BB add field for back pointer to sb struct(s)? */ }; diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index b2063ce113ec..6e1fe3a7f27d 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -1837,6 +1837,7 @@ cifs_put_tcon(struct cifsTconInfo *tcon) _FreeXid(xid); tconInfoFree(tcon); + cifs_fscache_release_super_cookie(tcon); cifs_put_smb_ses(ses); } @@ -1906,6 +1907,8 @@ cifs_get_tcon(struct cifsSesInfo *ses, struct smb_vol *volume_info) list_add(&tcon->tcon_list, &ses->tcon_list); write_unlock(&cifs_tcp_ses_lock); + cifs_fscache_get_super_cookie(tcon); + return tcon; out_fail: diff --git a/fs/cifs/fscache.c b/fs/cifs/fscache.c index ea97b76f8aa6..eba9beb94a4c 100644 --- a/fs/cifs/fscache.c +++ b/fs/cifs/fscache.c @@ -39,3 +39,20 @@ void cifs_fscache_release_client_cookie(struct TCP_Server_Info *server) server->fscache = NULL; } +void cifs_fscache_get_super_cookie(struct cifsTconInfo *tcon) +{ + struct TCP_Server_Info *server = tcon->ses->server; + + tcon->fscache = + fscache_acquire_cookie(server->fscache, + &cifs_fscache_super_index_def, tcon); + cFYI(1, "CIFS: get superblock cookie (0x%p/0x%p)", + server->fscache, tcon->fscache); +} + +void cifs_fscache_release_super_cookie(struct cifsTconInfo *tcon) +{ + cFYI(1, "CIFS: releasing superblock cookie (0x%p)", tcon->fscache); + fscache_relinquish_cookie(tcon->fscache, 0); + tcon->fscache = NULL; +} diff --git a/fs/cifs/fscache.h b/fs/cifs/fscache.h index 8972306c58a5..d2ba628b72cd 100644 --- a/fs/cifs/fscache.h +++ b/fs/cifs/fscache.h @@ -29,6 +29,7 @@ extern struct fscache_netfs cifs_fscache_netfs; extern const struct fscache_cookie_def cifs_fscache_server_index_def; +extern const struct fscache_cookie_def cifs_fscache_super_index_def; extern int cifs_fscache_register(void); extern void cifs_fscache_unregister(void); @@ -38,6 +39,8 @@ extern void cifs_fscache_unregister(void); */ extern void cifs_fscache_get_client_cookie(struct TCP_Server_Info *); extern void cifs_fscache_release_client_cookie(struct TCP_Server_Info *); +extern void cifs_fscache_get_super_cookie(struct cifsTconInfo *); +extern void cifs_fscache_release_super_cookie(struct cifsTconInfo *); #else /* CONFIG_CIFS_FSCACHE */ static inline int cifs_fscache_register(void) { return 0; } @@ -47,6 +50,9 @@ static inline void cifs_fscache_get_client_cookie(struct TCP_Server_Info *server) {} static inline void cifs_fscache_get_client_cookie(struct TCP_Server_Info *server); {} +static inline void cifs_fscache_get_super_cookie(struct cifsTconInfo *tcon) {} +static inline void +cifs_fscache_release_super_cookie(struct cifsTconInfo *tcon) {} #endif /* CONFIG_CIFS_FSCACHE */ diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index fe9b2f5fb492..f884cb51622a 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -807,6 +807,9 @@ struct inode *cifs_root_iget(struct super_block *sb, unsigned long ino) if (!inode) return ERR_PTR(-ENOMEM); + /* populate tcon->resource_id */ + cifs_sb->tcon->resource_id = CIFS_I(inode)->uniqueid; + if (rc && cifs_sb->tcon->ipc) { cFYI(1, "ipc connection - fake read inode"); inode->i_mode |= S_IFDIR; -- cgit v1.2.3 From 9451a9a52f91a4c171cfaca2f6d7a2ce91867b8d Mon Sep 17 00:00:00 2001 From: Suresh Jayaraman Date: Mon, 5 Jul 2010 18:12:45 +0530 Subject: cifs: define inode-level cache object and register them Define inode-level data storage objects (managed by cifsInodeInfo structs). Each inode-level object is created in a super-block level object and is itself a data storage object in to which pages from the inode are stored. The inode object is keyed by UniqueId. The coherency data being used is LastWriteTime, LastChangeTime and end of file reported by the server. Signed-off-by: Suresh Jayaraman Signed-off-by: Steve French --- fs/cifs/cache.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ fs/cifs/cifsfs.c | 7 +++++ fs/cifs/cifsglob.h | 3 ++ fs/cifs/file.c | 6 ++++ fs/cifs/fscache.c | 68 ++++++++++++++++++++++++++++++++++++++++++++ fs/cifs/fscache.h | 12 ++++++++ fs/cifs/inode.c | 4 +++ 7 files changed, 183 insertions(+) (limited to 'fs') diff --git a/fs/cifs/cache.c b/fs/cifs/cache.c index 15532abdac14..b2649cfd3a04 100644 --- a/fs/cifs/cache.c +++ b/fs/cifs/cache.c @@ -215,3 +215,86 @@ const struct fscache_cookie_def cifs_fscache_super_index_def = { .check_aux = cifs_fscache_super_check_aux, }; +/* + * Auxiliary data attached to CIFS inode within the cache + */ +struct cifs_fscache_inode_auxdata { + struct timespec last_write_time; + struct timespec last_change_time; + u64 eof; +}; + +static uint16_t cifs_fscache_inode_get_key(const void *cookie_netfs_data, + void *buffer, uint16_t maxbuf) +{ + const struct cifsInodeInfo *cifsi = cookie_netfs_data; + uint16_t keylen; + + /* use the UniqueId as the key */ + keylen = sizeof(cifsi->uniqueid); + if (keylen > maxbuf) + keylen = 0; + else + memcpy(buffer, &cifsi->uniqueid, keylen); + + return keylen; +} + +static void +cifs_fscache_inode_get_attr(const void *cookie_netfs_data, uint64_t *size) +{ + const struct cifsInodeInfo *cifsi = cookie_netfs_data; + + *size = cifsi->vfs_inode.i_size; +} + +static uint16_t +cifs_fscache_inode_get_aux(const void *cookie_netfs_data, void *buffer, + uint16_t maxbuf) +{ + struct cifs_fscache_inode_auxdata auxdata; + const struct cifsInodeInfo *cifsi = cookie_netfs_data; + + memset(&auxdata, 0, sizeof(auxdata)); + auxdata.eof = cifsi->server_eof; + auxdata.last_write_time = cifsi->vfs_inode.i_mtime; + auxdata.last_change_time = cifsi->vfs_inode.i_ctime; + + if (maxbuf > sizeof(auxdata)) + maxbuf = sizeof(auxdata); + + memcpy(buffer, &auxdata, maxbuf); + + return maxbuf; +} + +static enum +fscache_checkaux cifs_fscache_inode_check_aux(void *cookie_netfs_data, + const void *data, + uint16_t datalen) +{ + struct cifs_fscache_inode_auxdata auxdata; + struct cifsInodeInfo *cifsi = cookie_netfs_data; + + if (datalen != sizeof(auxdata)) + return FSCACHE_CHECKAUX_OBSOLETE; + + memset(&auxdata, 0, sizeof(auxdata)); + auxdata.eof = cifsi->server_eof; + auxdata.last_write_time = cifsi->vfs_inode.i_mtime; + auxdata.last_change_time = cifsi->vfs_inode.i_ctime; + + if (memcmp(data, &auxdata, datalen) != 0) + return FSCACHE_CHECKAUX_OBSOLETE; + + return FSCACHE_CHECKAUX_OKAY; +} + +const struct fscache_cookie_def cifs_fscache_inode_object_def = { + .name = "CIFS.uniqueid", + .type = FSCACHE_COOKIE_TYPE_DATAFILE, + .get_key = cifs_fscache_inode_get_key, + .get_attr = cifs_fscache_inode_get_attr, + .get_aux = cifs_fscache_inode_get_aux, + .check_aux = cifs_fscache_inode_check_aux, +}; diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 24d7f4ab3b65..8a2cf129e535 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -329,6 +329,12 @@ cifs_destroy_inode(struct inode *inode) kmem_cache_free(cifs_inode_cachep, CIFS_I(inode)); } +static void +cifs_clear_inode(struct inode *inode) +{ + cifs_fscache_release_inode_cookie(inode); +} + static void cifs_show_address(struct seq_file *s, struct TCP_Server_Info *server) { @@ -490,6 +496,7 @@ static const struct super_operations cifs_super_ops = { .alloc_inode = cifs_alloc_inode, .destroy_inode = cifs_destroy_inode, .drop_inode = cifs_drop_inode, + .clear_inode = cifs_clear_inode, /* .delete_inode = cifs_delete_inode, */ /* Do not need above function unless later we add lazy close of inodes or unless the kernel forgets to call us with the same number of releases (closes) diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index a3e403e7e163..9b7cf9aa3a00 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -390,6 +390,9 @@ struct cifsInodeInfo { bool invalid_mapping:1; /* pagecache is invalid */ u64 server_eof; /* current file size on server */ u64 uniqueid; /* server inode number */ +#ifdef CONFIG_CIFS_FSCACHE + struct fscache_cookie *fscache; +#endif struct inode vfs_inode; }; diff --git a/fs/cifs/file.c b/fs/cifs/file.c index b5fb2a0607b0..d302d941f9ac 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -40,6 +40,7 @@ #include "cifs_unicode.h" #include "cifs_debug.h" #include "cifs_fs_sb.h" +#include "fscache.h" static inline int cifs_convert_flags(unsigned int flags) { @@ -282,6 +283,9 @@ int cifs_open(struct inode *inode, struct file *file) CIFSSMBClose(xid, tcon, netfid); rc = -ENOMEM; } + + cifs_fscache_set_inode_cookie(inode, file); + goto out; } else if ((rc == -EINVAL) || (rc == -EOPNOTSUPP)) { if (tcon->ses->serverNOS) @@ -373,6 +377,8 @@ int cifs_open(struct inode *inode, struct file *file) goto out; } + cifs_fscache_set_inode_cookie(inode, file); + if (oplock & CIFS_CREATE_ACTION) { /* time to set mode which we can not set earlier due to problems creating new read-only files */ diff --git a/fs/cifs/fscache.c b/fs/cifs/fscache.c index eba9beb94a4c..6c8d96758ddb 100644 --- a/fs/cifs/fscache.c +++ b/fs/cifs/fscache.c @@ -21,6 +21,7 @@ #include "fscache.h" #include "cifsglob.h" #include "cifs_debug.h" +#include "cifs_fs_sb.h" void cifs_fscache_get_client_cookie(struct TCP_Server_Info *server) { @@ -56,3 +57,70 @@ void cifs_fscache_release_super_cookie(struct cifsTconInfo *tcon) fscache_relinquish_cookie(tcon->fscache, 0); tcon->fscache = NULL; } + +static void cifs_fscache_enable_inode_cookie(struct inode *inode) +{ + struct cifsInodeInfo *cifsi = CIFS_I(inode); + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + + if (cifsi->fscache) + return; + + cifsi->fscache = fscache_acquire_cookie(cifs_sb->tcon->fscache, + &cifs_fscache_inode_object_def, + cifsi); + cFYI(1, "CIFS: got FH cookie (0x%p/0x%p)", + cifs_sb->tcon->fscache, cifsi->fscache); +} + +void cifs_fscache_release_inode_cookie(struct inode *inode) +{ + struct cifsInodeInfo *cifsi = CIFS_I(inode); + + if (cifsi->fscache) { + cFYI(1, "CIFS releasing inode cookie (0x%p)", + cifsi->fscache); + fscache_relinquish_cookie(cifsi->fscache, 0); + cifsi->fscache = NULL; + } +} + +static void cifs_fscache_disable_inode_cookie(struct inode *inode) +{ + struct cifsInodeInfo *cifsi = CIFS_I(inode); + + if (cifsi->fscache) { + cFYI(1, "CIFS disabling inode cookie (0x%p)", + cifsi->fscache); + fscache_relinquish_cookie(cifsi->fscache, 1); + cifsi->fscache = NULL; + } +} + +void cifs_fscache_set_inode_cookie(struct inode *inode, struct file *filp) +{ + if ((filp->f_flags & O_ACCMODE) != O_RDONLY) + cifs_fscache_disable_inode_cookie(inode); + else { + cifs_fscache_enable_inode_cookie(inode); + cFYI(1, "CIFS: fscache inode cookie set"); + } +} + +void cifs_fscache_reset_inode_cookie(struct inode *inode) +{ + struct cifsInodeInfo *cifsi = CIFS_I(inode); + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct fscache_cookie *old = cifsi->fscache; + + if (cifsi->fscache) { + /* retire the current fscache cache and get a new one */ + fscache_relinquish_cookie(cifsi->fscache, 1); + + cifsi->fscache = fscache_acquire_cookie(cifs_sb->tcon->fscache, + &cifs_fscache_inode_object_def, + cifsi); + cFYI(1, "CIFS: new cookie 0x%p oldcookie 0x%p", + cifsi->fscache, old); + } +} diff --git a/fs/cifs/fscache.h b/fs/cifs/fscache.h index d2ba628b72cd..1008f4050835 100644 --- a/fs/cifs/fscache.h +++ b/fs/cifs/fscache.h @@ -30,6 +30,8 @@ extern struct fscache_netfs cifs_fscache_netfs; extern const struct fscache_cookie_def cifs_fscache_server_index_def; extern const struct fscache_cookie_def cifs_fscache_super_index_def; +extern const struct fscache_cookie_def cifs_fscache_inode_object_def; + extern int cifs_fscache_register(void); extern void cifs_fscache_unregister(void); @@ -42,6 +44,10 @@ extern void cifs_fscache_release_client_cookie(struct TCP_Server_Info *); extern void cifs_fscache_get_super_cookie(struct cifsTconInfo *); extern void cifs_fscache_release_super_cookie(struct cifsTconInfo *); +extern void cifs_fscache_release_inode_cookie(struct inode *); +extern void cifs_fscache_set_inode_cookie(struct inode *, struct file *); +extern void cifs_fscache_reset_inode_cookie(struct inode *); + #else /* CONFIG_CIFS_FSCACHE */ static inline int cifs_fscache_register(void) { return 0; } static inline void cifs_fscache_unregister(void) {} @@ -54,6 +60,12 @@ static inline void cifs_fscache_get_super_cookie(struct cifsTconInfo *tcon) {} static inline void cifs_fscache_release_super_cookie(struct cifsTconInfo *tcon) {} +static inline void cifs_fscache_release_inode_cookie(struct inode *inode) {} +static inline void cifs_fscache_set_inode_cookie(struct inode *inode, + struct file *filp) {} +static inline void cifs_fscache_reset_inode_cookie(struct inode *inode) {} + + #endif /* CONFIG_CIFS_FSCACHE */ #endif /* _CIFS_FSCACHE_H */ diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index f884cb51622a..5a68b92a0f9a 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -29,6 +29,7 @@ #include "cifsproto.h" #include "cifs_debug.h" #include "cifs_fs_sb.h" +#include "fscache.h" static void cifs_set_ops(struct inode *inode, const bool is_dfs_referral) @@ -776,6 +777,8 @@ retry_iget5_locked: inode->i_flags |= S_NOATIME | S_NOCMTIME; if (inode->i_state & I_NEW) { inode->i_ino = hash; + /* initialize per-inode cache cookie pointer */ + CIFS_I(inode)->fscache = NULL; unlock_new_inode(inode); } } @@ -1571,6 +1574,7 @@ cifs_invalidate_mapping(struct inode *inode) cifs_i->write_behind_rc = rc; } invalidate_remote_inode(inode); + cifs_fscache_reset_inode_cookie(inode); } int cifs_revalidate_file(struct file *filp) -- cgit v1.2.3 From 85f2d6b44d7e83bdeab87df910127c6f296866cf Mon Sep 17 00:00:00 2001 From: Suresh Jayaraman Date: Mon, 5 Jul 2010 18:13:00 +0530 Subject: cifs: FS-Cache page management Takes care of invalidation and release of FS-Cache marked pages and also invalidation of the FsCache page flag when the inode is removed. Signed-off-by: Suresh Jayaraman Acked-by: David Howells Signed-off-by: Steve French --- fs/cifs/cache.c | 31 +++++++++++++++++++++++++++++++ fs/cifs/file.c | 20 ++++++++++++++++++++ fs/cifs/fscache.c | 26 ++++++++++++++++++++++++++ fs/cifs/fscache.h | 16 ++++++++++++++++ 4 files changed, 93 insertions(+) (limited to 'fs') diff --git a/fs/cifs/cache.c b/fs/cifs/cache.c index b2649cfd3a04..224d7bbd1fcc 100644 --- a/fs/cifs/cache.c +++ b/fs/cifs/cache.c @@ -290,6 +290,36 @@ fscache_checkaux cifs_fscache_inode_check_aux(void *cookie_netfs_data, return FSCACHE_CHECKAUX_OKAY; } +static void cifs_fscache_inode_now_uncached(void *cookie_netfs_data) +{ + struct cifsInodeInfo *cifsi = cookie_netfs_data; + struct pagevec pvec; + pgoff_t first; + int loop, nr_pages; + + pagevec_init(&pvec, 0); + first = 0; + + cFYI(1, "cifs inode 0x%p now uncached", cifsi); + + for (;;) { + nr_pages = pagevec_lookup(&pvec, + cifsi->vfs_inode.i_mapping, first, + PAGEVEC_SIZE - pagevec_count(&pvec)); + if (!nr_pages) + break; + + for (loop = 0; loop < nr_pages; loop++) + ClearPageFsCache(pvec.pages[loop]); + + first = pvec.pages[nr_pages - 1]->index + 1; + + pvec.nr = nr_pages; + pagevec_release(&pvec); + cond_resched(); + } +} + const struct fscache_cookie_def cifs_fscache_inode_object_def = { .name = "CIFS.uniqueid", .type = FSCACHE_COOKIE_TYPE_DATAFILE, @@ -297,4 +327,5 @@ const struct fscache_cookie_def cifs_fscache_inode_object_def = { .get_attr = cifs_fscache_inode_get_attr, .get_aux = cifs_fscache_inode_get_aux, .check_aux = cifs_fscache_inode_check_aux, + .now_uncached = cifs_fscache_inode_now_uncached, }; diff --git a/fs/cifs/file.c b/fs/cifs/file.c index d302d941f9ac..f677ede766d1 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -2267,6 +2267,22 @@ out: return rc; } +static int cifs_release_page(struct page *page, gfp_t gfp) +{ + if (PagePrivate(page)) + return 0; + + return cifs_fscache_release_page(page, gfp); +} + +static void cifs_invalidate_page(struct page *page, unsigned long offset) +{ + struct cifsInodeInfo *cifsi = CIFS_I(page->mapping->host); + + if (offset == 0) + cifs_fscache_invalidate_page(page, &cifsi->vfs_inode); +} + static void cifs_oplock_break(struct slow_work *work) { @@ -2340,6 +2356,8 @@ const struct address_space_operations cifs_addr_ops = { .write_begin = cifs_write_begin, .write_end = cifs_write_end, .set_page_dirty = __set_page_dirty_nobuffers, + .releasepage = cifs_release_page, + .invalidatepage = cifs_invalidate_page, /* .sync_page = cifs_sync_page, */ /* .direct_IO = */ }; @@ -2356,6 +2374,8 @@ const struct address_space_operations cifs_addr_ops_smallbuf = { .write_begin = cifs_write_begin, .write_end = cifs_write_end, .set_page_dirty = __set_page_dirty_nobuffers, + .releasepage = cifs_release_page, + .invalidatepage = cifs_invalidate_page, /* .sync_page = cifs_sync_page, */ /* .direct_IO = */ }; diff --git a/fs/cifs/fscache.c b/fs/cifs/fscache.c index 6c8d96758ddb..5dd935280049 100644 --- a/fs/cifs/fscache.c +++ b/fs/cifs/fscache.c @@ -124,3 +124,29 @@ void cifs_fscache_reset_inode_cookie(struct inode *inode) cifsi->fscache, old); } } + +int cifs_fscache_release_page(struct page *page, gfp_t gfp) +{ + if (PageFsCache(page)) { + struct inode *inode = page->mapping->host; + struct cifsInodeInfo *cifsi = CIFS_I(inode); + + cFYI(1, "CIFS: fscache release page (0x%p/0x%p)", + page, cifsi->fscache); + if (!fscache_maybe_release_page(cifsi->fscache, page, gfp)) + return 0; + } + + return 1; +} + +void __cifs_fscache_invalidate_page(struct page *page, struct inode *inode) +{ + struct cifsInodeInfo *cifsi = CIFS_I(inode); + struct fscache_cookie *cookie = cifsi->fscache; + + cFYI(1, "CIFS: fscache invalidatepage (0x%p/0x%p)", page, cookie); + fscache_wait_on_page_write(cookie, page); + fscache_uncache_page(cookie, page); +} + diff --git a/fs/cifs/fscache.h b/fs/cifs/fscache.h index 1008f4050835..5e18a21eee9d 100644 --- a/fs/cifs/fscache.h +++ b/fs/cifs/fscache.h @@ -48,6 +48,16 @@ extern void cifs_fscache_release_inode_cookie(struct inode *); extern void cifs_fscache_set_inode_cookie(struct inode *, struct file *); extern void cifs_fscache_reset_inode_cookie(struct inode *); +extern void __cifs_fscache_invalidate_page(struct page *, struct inode *); +extern int cifs_fscache_release_page(struct page *page, gfp_t gfp); + +static inline void cifs_fscache_invalidate_page(struct page *page, + struct inode *inode) +{ + if (PageFsCache(page)) + __cifs_fscache_invalidate_page(page, inode); +} + #else /* CONFIG_CIFS_FSCACHE */ static inline int cifs_fscache_register(void) { return 0; } static inline void cifs_fscache_unregister(void) {} @@ -64,7 +74,13 @@ static inline void cifs_fscache_release_inode_cookie(struct inode *inode) {} static inline void cifs_fscache_set_inode_cookie(struct inode *inode, struct file *filp) {} static inline void cifs_fscache_reset_inode_cookie(struct inode *inode) {} +static inline void cifs_fscache_release_page(struct page *page, gfp_t gfp) +{ + return 1; /* May release page */ +} +static inline int cifs_fscache_invalidate_page(struct page *page, + struct inode *) {} #endif /* CONFIG_CIFS_FSCACHE */ -- cgit v1.2.3 From 9dc06558c223bbc08290917ac44c25963bc09e43 Mon Sep 17 00:00:00 2001 From: Suresh Jayaraman Date: Mon, 5 Jul 2010 18:13:11 +0530 Subject: cifs: store pages into local cache Store pages from an CIFS inode into the data storage object associated with that inode. Signed-off-by: Suresh Jayaraman Signed-off-by: Steve French --- fs/cifs/file.c | 7 +++++++ fs/cifs/fscache.c | 11 +++++++++++ fs/cifs/fscache.h | 11 +++++++++++ 3 files changed, 29 insertions(+) (limited to 'fs') diff --git a/fs/cifs/file.c b/fs/cifs/file.c index f677ede766d1..ff726c86b290 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -1944,6 +1944,9 @@ static void cifs_copy_cache_pages(struct address_space *mapping, SetPageUptodate(page); unlock_page(page); data += PAGE_CACHE_SIZE; + + /* add page to FS-Cache */ + cifs_readpage_to_fscache(mapping->host, page); } return; } @@ -2113,6 +2116,10 @@ static int cifs_readpage_worker(struct file *file, struct page *page, flush_dcache_page(page); SetPageUptodate(page); + + /* send this page to the cache */ + cifs_readpage_to_fscache(file->f_path.dentry->d_inode, page); + rc = 0; io_error: diff --git a/fs/cifs/fscache.c b/fs/cifs/fscache.c index 5dd935280049..3b1636704c85 100644 --- a/fs/cifs/fscache.c +++ b/fs/cifs/fscache.c @@ -140,6 +140,17 @@ int cifs_fscache_release_page(struct page *page, gfp_t gfp) return 1; } +void __cifs_readpage_to_fscache(struct inode *inode, struct page *page) +{ + int ret; + + cFYI(1, "CIFS: readpage_to_fscache(fsc: %p, p: %p, i: %p", + CIFS_I(inode)->fscache, page, inode); + ret = fscache_write_page(CIFS_I(inode)->fscache, page, GFP_KERNEL); + if (ret != 0) + fscache_uncache_page(CIFS_I(inode)->fscache, page); +} + void __cifs_fscache_invalidate_page(struct page *page, struct inode *inode) { struct cifsInodeInfo *cifsi = CIFS_I(inode); diff --git a/fs/cifs/fscache.h b/fs/cifs/fscache.h index 5e18a21eee9d..1a00d70bca97 100644 --- a/fs/cifs/fscache.h +++ b/fs/cifs/fscache.h @@ -51,6 +51,8 @@ extern void cifs_fscache_reset_inode_cookie(struct inode *); extern void __cifs_fscache_invalidate_page(struct page *, struct inode *); extern int cifs_fscache_release_page(struct page *page, gfp_t gfp); +extern void __cifs_readpage_to_fscache(struct inode *, struct page *); + static inline void cifs_fscache_invalidate_page(struct page *page, struct inode *inode) { @@ -58,6 +60,13 @@ static inline void cifs_fscache_invalidate_page(struct page *page, __cifs_fscache_invalidate_page(page, inode); } +static inline void cifs_readpage_to_fscache(struct inode *inode, + struct page *page) +{ + if (PageFsCache(page)) + __cifs_readpage_to_fscache(inode, page); +} + #else /* CONFIG_CIFS_FSCACHE */ static inline int cifs_fscache_register(void) { return 0; } static inline void cifs_fscache_unregister(void) {} @@ -81,6 +90,8 @@ static inline void cifs_fscache_release_page(struct page *page, gfp_t gfp) static inline int cifs_fscache_invalidate_page(struct page *page, struct inode *) {} +static inline void cifs_readpage_to_fscache(struct inode *inode, + struct page *page) {} #endif /* CONFIG_CIFS_FSCACHE */ -- cgit v1.2.3 From 56698236e1294848c63d4768673865ae5a9c69e0 Mon Sep 17 00:00:00 2001 From: Suresh Jayaraman Date: Mon, 5 Jul 2010 18:13:25 +0530 Subject: cifs: read pages from FS-Cache Read pages from a FS-Cache data storage object into a CIFS inode. Signed-off-by: Suresh Jayaraman Acked-by: David Howells Signed-off-by: Steve French --- fs/cifs/file.c | 17 +++++++++++++ fs/cifs/fscache.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ fs/cifs/fscache.h | 40 +++++++++++++++++++++++++++++- 3 files changed, 129 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/cifs/file.c b/fs/cifs/file.c index ff726c86b290..fa04a00d126d 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -1977,6 +1977,15 @@ static int cifs_readpages(struct file *file, struct address_space *mapping, cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); pTcon = cifs_sb->tcon; + /* + * Reads as many pages as possible from fscache. Returns -ENOBUFS + * immediately if the cookie is negative + */ + rc = cifs_readpages_from_fscache(mapping->host, mapping, page_list, + &num_pages); + if (rc == 0) + goto read_complete; + cFYI(DBG2, "rpages: num pages %d", num_pages); for (i = 0; i < num_pages; ) { unsigned contig_pages; @@ -2087,6 +2096,7 @@ static int cifs_readpages(struct file *file, struct address_space *mapping, smb_read_data = NULL; } +read_complete: FreeXid(xid); return rc; } @@ -2097,6 +2107,11 @@ static int cifs_readpage_worker(struct file *file, struct page *page, char *read_data; int rc; + /* Is the page cached? */ + rc = cifs_readpage_from_fscache(file->f_path.dentry->d_inode, page); + if (rc == 0) + goto read_complete; + page_cache_get(page); read_data = kmap(page); /* for reads over a certain size could initiate async read ahead */ @@ -2125,6 +2140,8 @@ static int cifs_readpage_worker(struct file *file, struct page *page, io_error: kunmap(page); page_cache_release(page); + +read_complete: return rc; } diff --git a/fs/cifs/fscache.c b/fs/cifs/fscache.c index 3b1636704c85..9f3f5c4be161 100644 --- a/fs/cifs/fscache.c +++ b/fs/cifs/fscache.c @@ -140,6 +140,79 @@ int cifs_fscache_release_page(struct page *page, gfp_t gfp) return 1; } +static void cifs_readpage_from_fscache_complete(struct page *page, void *ctx, + int error) +{ + cFYI(1, "CFS: readpage_from_fscache_complete (0x%p/%d)", + page, error); + if (!error) + SetPageUptodate(page); + unlock_page(page); +} + +/* + * Retrieve a page from FS-Cache + */ +int __cifs_readpage_from_fscache(struct inode *inode, struct page *page) +{ + int ret; + + cFYI(1, "CIFS: readpage_from_fscache(fsc:%p, p:%p, i:0x%p", + CIFS_I(inode)->fscache, page, inode); + ret = fscache_read_or_alloc_page(CIFS_I(inode)->fscache, page, + cifs_readpage_from_fscache_complete, + NULL, + GFP_KERNEL); + switch (ret) { + + case 0: /* page found in fscache, read submitted */ + cFYI(1, "CIFS: readpage_from_fscache: submitted"); + return ret; + case -ENOBUFS: /* page won't be cached */ + case -ENODATA: /* page not in cache */ + cFYI(1, "CIFS: readpage_from_fscache %d", ret); + return 1; + + default: + cERROR(1, "unknown error ret = %d", ret); + } + return ret; +} + +/* + * Retrieve a set of pages from FS-Cache + */ +int __cifs_readpages_from_fscache(struct inode *inode, + struct address_space *mapping, + struct list_head *pages, + unsigned *nr_pages) +{ + int ret; + + cFYI(1, "CIFS: __cifs_readpages_from_fscache (0x%p/%u/0x%p)", + CIFS_I(inode)->fscache, *nr_pages, inode); + ret = fscache_read_or_alloc_pages(CIFS_I(inode)->fscache, mapping, + pages, nr_pages, + cifs_readpage_from_fscache_complete, + NULL, + mapping_gfp_mask(mapping)); + switch (ret) { + case 0: /* read submitted to the cache for all pages */ + cFYI(1, "CIFS: readpages_from_fscache: submitted"); + return ret; + + case -ENOBUFS: /* some pages are not cached and can't be */ + case -ENODATA: /* some pages are not cached */ + cFYI(1, "CIFS: readpages_from_fscache: no page"); + return 1; + + default: + cFYI(1, "unknown error ret = %d", ret); + } + + return ret; +} + void __cifs_readpage_to_fscache(struct inode *inode, struct page *page) { int ret; diff --git a/fs/cifs/fscache.h b/fs/cifs/fscache.h index 1a00d70bca97..79164c66797e 100644 --- a/fs/cifs/fscache.h +++ b/fs/cifs/fscache.h @@ -32,7 +32,6 @@ extern const struct fscache_cookie_def cifs_fscache_server_index_def; extern const struct fscache_cookie_def cifs_fscache_super_index_def; extern const struct fscache_cookie_def cifs_fscache_inode_object_def; - extern int cifs_fscache_register(void); extern void cifs_fscache_unregister(void); @@ -50,6 +49,11 @@ extern void cifs_fscache_reset_inode_cookie(struct inode *); extern void __cifs_fscache_invalidate_page(struct page *, struct inode *); extern int cifs_fscache_release_page(struct page *page, gfp_t gfp); +extern int __cifs_readpage_from_fscache(struct inode *, struct page *); +extern int __cifs_readpages_from_fscache(struct inode *, + struct address_space *, + struct list_head *, + unsigned *); extern void __cifs_readpage_to_fscache(struct inode *, struct page *); @@ -60,6 +64,26 @@ static inline void cifs_fscache_invalidate_page(struct page *page, __cifs_fscache_invalidate_page(page, inode); } +static inline int cifs_readpage_from_fscache(struct inode *inode, + struct page *page) +{ + if (CIFS_I(inode)->fscache) + return __cifs_readpage_from_fscache(inode, page); + + return -ENOBUFS; +} + +static inline int cifs_readpages_from_fscache(struct inode *inode, + struct address_space *mapping, + struct list_head *pages, + unsigned *nr_pages) +{ + if (CIFS_I(inode)->fscache) + return __cifs_readpages_from_fscache(inode, mapping, pages, + nr_pages); + return -ENOBUFS; +} + static inline void cifs_readpage_to_fscache(struct inode *inode, struct page *page) { @@ -90,6 +114,20 @@ static inline void cifs_fscache_release_page(struct page *page, gfp_t gfp) static inline int cifs_fscache_invalidate_page(struct page *page, struct inode *) {} +static inline int +cifs_readpage_from_fscache(struct inode *inode, struct page *page) +{ + return -ENOBUFS; +} + +static inline int cifs_readpages_from_fscache(struct inode *inode, + struct address_space *mapping, + struct list_head *pages, + unsigned *nr_pages) +{ + return -ENOBUFS; +} + static inline void cifs_readpage_to_fscache(struct inode *inode, struct page *page) {} -- cgit v1.2.3 From fa1df75d4debde6d843e616df656f50a92958737 Mon Sep 17 00:00:00 2001 From: Suresh Jayaraman Date: Mon, 5 Jul 2010 18:13:36 +0530 Subject: cifs: add mount option to enable local caching Add a mount option 'fsc' to enable local caching on CIFS. I considered adding a separate debug bit for caching, but it appears that debugging would be relatively easier with the normal CIFS_INFO level. As the cifs-utils (userspace) changes are not done yet, this patch enables 'fsc' by default to enable testing. Signed-off-by: Suresh Jayaraman Acked-by: David Howells Signed-off-by: Steve French --- fs/cifs/cifs_fs_sb.h | 1 + fs/cifs/connect.c | 8 ++++++++ 2 files changed, 9 insertions(+) (limited to 'fs') diff --git a/fs/cifs/cifs_fs_sb.h b/fs/cifs/cifs_fs_sb.h index 246a167cb913..9e771450c3b8 100644 --- a/fs/cifs/cifs_fs_sb.h +++ b/fs/cifs/cifs_fs_sb.h @@ -35,6 +35,7 @@ #define CIFS_MOUNT_DYNPERM 0x1000 /* allow in-memory only mode setting */ #define CIFS_MOUNT_NOPOSIXBRL 0x2000 /* mandatory not posix byte range lock */ #define CIFS_MOUNT_NOSSYNC 0x4000 /* don't do slow SMBflush on every sync*/ +#define CIFS_MOUNT_FSCACHE 0x8000 /* local caching enabled */ struct cifs_sb_info { struct cifsTconInfo *tcon; /* primary mount */ diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 6e1fe3a7f27d..399b60129b74 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -98,6 +98,7 @@ struct smb_vol { bool noblocksnd:1; bool noautotune:1; bool nostrictsync:1; /* do not force expensive SMBflush on every sync */ + bool fsc:1; /* enable fscache */ unsigned int rsize; unsigned int wsize; bool sockopt_tcp_nodelay:1; @@ -843,6 +844,9 @@ cifs_parse_mount_options(char *options, const char *devname, /* default to using server inode numbers where available */ vol->server_ino = 1; + /* XXX: default to fsc for testing until mount.cifs pieces are done */ + vol->fsc = 1; + if (!options) return 1; @@ -1332,6 +1336,8 @@ cifs_parse_mount_options(char *options, const char *devname, printk(KERN_WARNING "CIFS: Mount option noac not " "supported. Instead set " "/proc/fs/cifs/LookupCacheEnabled to 0\n"); + } else if (strnicmp(data, "fsc", 3) == 0) { + vol->fsc = true; } else printk(KERN_WARNING "CIFS: Unknown mount option %s\n", data); @@ -2463,6 +2469,8 @@ static void setup_cifs_sb(struct smb_vol *pvolume_info, cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_OVERR_GID; if (pvolume_info->dynperm) cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DYNPERM; + if (pvolume_info->fsc) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_FSCACHE; if (pvolume_info->direct_io) { cFYI(1, "mounting share using direct i/o"); cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DIRECT_IO; -- cgit v1.2.3 From c5e04a3e4975d11d997528caf5d4880902fa90d8 Mon Sep 17 00:00:00 2001 From: Steve French Date: Fri, 16 Jul 2010 04:18:36 +0000 Subject: [CIFS] Fix build break when CONFIG_CIFS_FSCACHE disabled CC: Suresh Jayaraman Signed-off-by: Steve French --- fs/cifs/cifsfs.h | 2 +- fs/cifs/fscache.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h index a7eb65c84b1c..d82f5fb4761e 100644 --- a/fs/cifs/cifsfs.h +++ b/fs/cifs/cifsfs.h @@ -114,5 +114,5 @@ extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg); extern const struct export_operations cifs_export_ops; #endif /* EXPERIMENTAL */ -#define CIFS_VERSION "1.64" +#define CIFS_VERSION "1.65" #endif /* _CIFSFS_H */ diff --git a/fs/cifs/fscache.h b/fs/cifs/fscache.h index 79164c66797e..7d4ff41894bb 100644 --- a/fs/cifs/fscache.h +++ b/fs/cifs/fscache.h @@ -98,7 +98,7 @@ static inline void cifs_fscache_unregister(void) {} static inline void cifs_fscache_get_client_cookie(struct TCP_Server_Info *server) {} static inline void -cifs_fscache_get_client_cookie(struct TCP_Server_Info *server); {} +cifs_fscache_release_client_cookie(struct TCP_Server_Info *server) {} static inline void cifs_fscache_get_super_cookie(struct cifsTconInfo *tcon) {} static inline void cifs_fscache_release_super_cookie(struct cifsTconInfo *tcon) {} -- cgit v1.2.3 From d0e6f44e6cc3c7059e8717c452f0999aba507a38 Mon Sep 17 00:00:00 2001 From: Steve French Date: Fri, 16 Jul 2010 04:24:54 +0000 Subject: [CIFS] Missing line from previous commit CC: Suresh Jayaraman Signed-off-by: Steve French --- fs/cifs/fscache.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/cifs/fscache.h b/fs/cifs/fscache.h index 7d4ff41894bb..31b88ec2341e 100644 --- a/fs/cifs/fscache.h +++ b/fs/cifs/fscache.h @@ -107,13 +107,13 @@ static inline void cifs_fscache_release_inode_cookie(struct inode *inode) {} static inline void cifs_fscache_set_inode_cookie(struct inode *inode, struct file *filp) {} static inline void cifs_fscache_reset_inode_cookie(struct inode *inode) {} -static inline void cifs_fscache_release_page(struct page *page, gfp_t gfp) +static inline int cifs_fscache_release_page(struct page *page, gfp_t gfp) { return 1; /* May release page */ } -static inline int cifs_fscache_invalidate_page(struct page *page, - struct inode *) {} +static inline void cifs_fscache_invalidate_page(struct page *page, + struct inode *inode) {} static inline int cifs_readpage_from_fscache(struct inode *inode, struct page *page) { -- cgit v1.2.3 From 0ccd48025fe64cf01782ba3c7037654d25bd1950 Mon Sep 17 00:00:00 2001 From: Steve French Date: Fri, 16 Jul 2010 04:31:02 +0000 Subject: [CIFS] Missing ifdef Signed-off-by: Steve French --- fs/cifs/inode.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'fs') diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 5a68b92a0f9a..2d9cd2f269eb 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -777,8 +777,10 @@ retry_iget5_locked: inode->i_flags |= S_NOATIME | S_NOCMTIME; if (inode->i_state & I_NEW) { inode->i_ino = hash; +#ifdef CONFIG_CIFS_FSCACHE /* initialize per-inode cache cookie pointer */ CIFS_I(inode)->fscache = NULL; +#endif unlock_new_inode(inode); } } @@ -810,8 +812,10 @@ struct inode *cifs_root_iget(struct super_block *sb, unsigned long ino) if (!inode) return ERR_PTR(-ENOMEM); +#ifdef CONFIG_CIFS_FSCACHE /* populate tcon->resource_id */ cifs_sb->tcon->resource_id = CIFS_I(inode)->uniqueid; +#endif if (rc && cifs_sb->tcon->ipc) { cFYI(1, "ipc connection - fake read inode"); -- cgit v1.2.3 From f55fdcca6bf1c17e86a270a8c0d81c6677c61222 Mon Sep 17 00:00:00 2001 From: Kulikov Vasiliy Date: Fri, 16 Jul 2010 20:15:25 +0400 Subject: fs: cifs: check kmalloc() result If kmalloc() fails exit with -ENOMEM. Signed-off-by: Kulikov Vasiliy Acked-by: Dave Kleikamp Acked-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/readdir.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'fs') diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c index daf1753af674..d5e591fab475 100644 --- a/fs/cifs/readdir.c +++ b/fs/cifs/readdir.c @@ -847,6 +847,11 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir) end_of_smb = cifsFile->srch_inf.ntwrk_buf_start + max_len; tmp_buf = kmalloc(UNICODE_NAME_MAX, GFP_KERNEL); + if (tmp_buf == NULL) { + rc = -ENOMEM; + break; + } + for (i = 0; (i < num_to_fill) && (rc == 0); i++) { if (current_entry == NULL) { /* evaluate whether this case is an error */ -- cgit v1.2.3 From 3e4b3e1f68c10510ec8d3076cffc5729b88f8de6 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Mon, 19 Jul 2010 18:00:17 -0400 Subject: cifs: add separate cred_uid field to sesInfo Right now, there's no clear separation between the uid that owns the credentials used to do the mount and the overriding owner of the files on that mount. Add a separate cred_uid field that is set to the real uid of the mount user. Unlike the linux_uid, the uid= option does not override this parameter. The parm is sent to cifs.upcall, which can then preferentially use the creduid= parm instead of the uid= parm for finding credentials. This is not the only way to solve this. We could try to do all of this in kernel instead by having a module parameter that affects what gets passed in the uid= field of the upcall. That said, we have a lot more flexibility to change things in userspace so I think it probably makes sense to do it this way. Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/cifs_spnego.c | 3 +++ fs/cifs/cifsglob.h | 3 ++- fs/cifs/connect.c | 7 +++++-- 3 files changed, 10 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/cifs/cifs_spnego.c b/fs/cifs/cifs_spnego.c index 379bd7d9c05f..6effccff85a5 100644 --- a/fs/cifs/cifs_spnego.c +++ b/fs/cifs/cifs_spnego.c @@ -143,6 +143,9 @@ cifs_get_spnego_key(struct cifsSesInfo *sesInfo) dp = description + strlen(description); sprintf(dp, ";uid=0x%x", sesInfo->linux_uid); + dp = description + strlen(description); + sprintf(dp, ";creduid=0x%x", sesInfo->cred_uid); + dp = description + strlen(description); sprintf(dp, ";user=%s", sesInfo->userName); diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 9b7cf9aa3a00..59906146ad36 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -214,7 +214,8 @@ struct cifsSesInfo { char *serverNOS; /* name of network operating system of server */ char *serverDomain; /* security realm of server */ int Suid; /* remote smb uid */ - uid_t linux_uid; /* local Linux uid */ + uid_t linux_uid; /* overriding owner of files on the mount */ + uid_t cred_uid; /* owner of credentials */ int capabilities; char serverName[SERVER_NAME_LEN_WITH_NULL * 2]; /* BB make bigger for TCP names - will ipv6 and sctp addresses fit? */ diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 399b60129b74..52a7646cc7af 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -67,6 +67,7 @@ struct smb_vol { char *iocharset; /* local code page for mapping to and from Unicode */ char source_rfc1001_name[16]; /* netbios name of client */ char target_rfc1001_name[16]; /* netbios name of server for Win9x/ME */ + uid_t cred_uid; uid_t linux_uid; gid_t linux_gid; mode_t file_mode; @@ -832,7 +833,8 @@ cifs_parse_mount_options(char *options, const char *devname, /* null target name indicates to use *SMBSERVR default called name if we end up sending RFC1001 session initialize */ vol->target_rfc1001_name[0] = 0; - vol->linux_uid = current_uid(); /* use current_euid() instead? */ + vol->cred_uid = current_uid(); + vol->linux_uid = current_uid(); vol->linux_gid = current_gid(); /* default to only allowing write access to owner of the mount */ @@ -1658,7 +1660,7 @@ cifs_find_smb_ses(struct TCP_Server_Info *server, struct smb_vol *vol) list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { switch (server->secType) { case Kerberos: - if (vol->linux_uid != ses->linux_uid) + if (vol->cred_uid != ses->cred_uid) continue; break; default: @@ -1775,6 +1777,7 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb_vol *volume_info) if (ses->domainName) strcpy(ses->domainName, volume_info->domainname); } + ses->cred_uid = volume_info->cred_uid; ses->linux_uid = volume_info->linux_uid; ses->overrideSecFlg = volume_info->secFlg; -- cgit v1.2.3 From 9f841593ff65d2f801c7f80c4ed0955d30103f50 Mon Sep 17 00:00:00 2001 From: Steve French Date: Fri, 23 Jul 2010 20:37:53 +0000 Subject: [CIFS] relinquish fscache cookie before freeing CIFSTconInfo Doh, fix a use after free bug. Signed-off-by: Suresh Jayaraman Reviewed-and-Tested-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/connect.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 52a7646cc7af..d91a6085d55c 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -1845,8 +1845,8 @@ cifs_put_tcon(struct cifsTconInfo *tcon) CIFSSMBTDis(xid, tcon); _FreeXid(xid); - tconInfoFree(tcon); cifs_fscache_release_super_cookie(tcon); + tconInfoFree(tcon); cifs_put_smb_ses(ses); } -- cgit v1.2.3 From f30b9c11847cb6bf1f7aa65b5c436800621a07dd Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Mon, 19 Jul 2010 18:00:17 -0400 Subject: cifs: don't allow cifs_iget to match inodes of the wrong type If the type is different from what we think it should be, then don't match the existing inode. Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/inode.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'fs') diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 2d9cd2f269eb..a15b3a9bbff4 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -724,9 +724,14 @@ cifs_find_inode(struct inode *inode, void *opaque) { struct cifs_fattr *fattr = (struct cifs_fattr *) opaque; + /* don't match inode with different uniqueid */ if (CIFS_I(inode)->uniqueid != fattr->cf_uniqueid) return 0; + /* don't match inode of different type */ + if ((inode->i_mode & S_IFMT) != (fattr->cf_mode & S_IFMT)) + return 0; + /* * uh oh -- it's a directory. We can't use it since hardlinked dirs are * verboten. Disable serverino and return it as if it were found, the -- cgit v1.2.3 From 3572d2857f61f720082740cc17e2d99b45e7af7f Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Mon, 26 Jul 2010 10:29:57 -0400 Subject: cifs: map NT_STATUS_ERROR_WRITE_PROTECTED to -EROFS Seems like a more sensible mapping than -EIO. Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/netmisc.c | 1 + fs/cifs/smberr.h | 1 + 2 files changed, 2 insertions(+) (limited to 'fs') diff --git a/fs/cifs/netmisc.c b/fs/cifs/netmisc.c index 3489468d070b..c6721ee26dbc 100644 --- a/fs/cifs/netmisc.c +++ b/fs/cifs/netmisc.c @@ -61,6 +61,7 @@ static const struct smb_to_posix_error mapping_table_ERRDOS[] = { {ERRremcd, -EACCES}, {ERRdiffdevice, -EXDEV}, {ERRnofiles, -ENOENT}, + {ERRwriteprot, -EROFS}, {ERRbadshare, -ETXTBSY}, {ERRlock, -EACCES}, {ERRunsup, -EINVAL}, diff --git a/fs/cifs/smberr.h b/fs/cifs/smberr.h index c5084d27db7c..7f16cb825fe5 100644 --- a/fs/cifs/smberr.h +++ b/fs/cifs/smberr.h @@ -76,6 +76,7 @@ #define ERRnofiles 18 /* A File Search command can find no more files matching the specified criteria. */ +#define ERRwriteprot 19 /* media is write protected */ #define ERRgeneral 31 #define ERRbadshare 32 /* The sharing mode specified for an Open conflicts with existing FIDs on -- cgit v1.2.3 From f636a34802e3913415410c6e595df2bf84851cff Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Mon, 26 Jul 2010 10:29:58 -0400 Subject: cifs: ignore the "mand", "nomand" and "_netdev" mount options These are all handled by the userspace mount programs, but older versions of mount.cifs also handed them off to the kernel. Ignore them. Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/connect.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'fs') diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index d91a6085d55c..85a994c6433d 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -1264,6 +1264,12 @@ cifs_parse_mount_options(char *options, const char *devname, } else if ((strnicmp(data, "nocase", 6) == 0) || (strnicmp(data, "ignorecase", 10) == 0)) { vol->nocase = 1; + } else if (strnicmp(data, "mand", 4) == 0) { + /* ignore */ + } else if (strnicmp(data, "nomand", 6) == 0) { + /* ignore */ + } else if (strnicmp(data, "_netdev", 7) == 0) { + /* ignore */ } else if (strnicmp(data, "brl", 3) == 0) { vol->nobrl = 0; } else if ((strnicmp(data, "nobrl", 5) == 0) || -- cgit v1.2.3 From f67909cf80051e8510194a51f88c4de323b92071 Mon Sep 17 00:00:00 2001 From: Steve French Date: Mon, 26 Jul 2010 18:20:16 +0000 Subject: [CIFS] remove redundant path walking in dfs_do_refmount Reviewed-by: Dave Howells Signed-off-by: Igor Mammedov Signed-off-by: Steve French --- fs/cifs/cifs_dfs_ref.c | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) (limited to 'fs') diff --git a/fs/cifs/cifs_dfs_ref.c b/fs/cifs/cifs_dfs_ref.c index ac19a6f3dae0..dc1ed50ea06e 100644 --- a/fs/cifs/cifs_dfs_ref.c +++ b/fs/cifs/cifs_dfs_ref.c @@ -230,28 +230,22 @@ compose_mount_options_err: goto compose_mount_options_out; } - -static struct vfsmount *cifs_dfs_do_refmount(const struct vfsmount *mnt_parent, - struct dentry *dentry, const struct dfs_info3_param *ref) +/** + * cifs_dfs_do_refmount - mounts specified path using provided refferal + * @cifs_sb: parent/root superblock + * @fullpath: full path in UNC format + * @ref: server's referral + */ +static struct vfsmount *cifs_dfs_do_refmount(struct cifs_sb_info *cifs_sb, + const char *fullpath, const struct dfs_info3_param *ref) { - struct cifs_sb_info *cifs_sb; struct vfsmount *mnt; char *mountdata; char *devname = NULL; - char *fullpath; - - cifs_sb = CIFS_SB(dentry->d_inode->i_sb); - /* - * this function gives us a path with a double backslash prefix. We - * require a single backslash for DFS. - */ - fullpath = build_path_from_dentry(dentry); - if (!fullpath) - return ERR_PTR(-ENOMEM); + /* strip first '\' from fullpath */ mountdata = cifs_compose_mount_options(cifs_sb->mountdata, fullpath + 1, ref, &devname); - kfree(fullpath); if (IS_ERR(mountdata)) return (struct vfsmount *)mountdata; @@ -357,8 +351,8 @@ cifs_dfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd) rc = -EINVAL; goto out_err; } - mnt = cifs_dfs_do_refmount(nd->path.mnt, - nd->path.dentry, referrals + i); + mnt = cifs_dfs_do_refmount(cifs_sb, + full_path, referrals + i); cFYI(1, "%s: cifs_dfs_do_refmount:%s , mnt:%p", __func__, referrals[i].node_name, mnt); -- cgit v1.2.3 From cb76d5e25008b76fb8e348c861d32659430ac3fa Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Mon, 26 Jul 2010 14:25:08 -0400 Subject: cifs: fsc should not default to "on" I'm not sure why this was merged with this flag hardcoded on, but it seems quite dangerous. Turn it off. Also, mount.cifs hands unrecognized options off to the kernel so there should be no need for changes there in order to support this. Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/connect.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'fs') diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 85a994c6433d..2a43a0aca965 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -846,9 +846,6 @@ cifs_parse_mount_options(char *options, const char *devname, /* default to using server inode numbers where available */ vol->server_ino = 1; - /* XXX: default to fsc for testing until mount.cifs pieces are done */ - vol->fsc = 1; - if (!options) return 1; -- cgit v1.2.3 From a51dca9cd3bb4ec5a05bfb6feabf024a5c808a37 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Mon, 2 Aug 2010 08:43:25 -0400 Subject: jbd2: Use atomic variables to avoid taking t_handle_lock in jbd2_journal_stop By using an atomic_t for t_updates and t_outstanding credits, this should allow us to not need to take transaction t_handle_lock in jbd2_journal_stop(). Signed-off-by: "Theodore Ts'o" --- fs/jbd2/checkpoint.c | 2 +- fs/jbd2/commit.c | 13 +++++----- fs/jbd2/transaction.c | 69 +++++++++++++++++++++++++++++---------------------- include/linux/jbd2.h | 8 +++--- 4 files changed, 51 insertions(+), 41 deletions(-) (limited to 'fs') diff --git a/fs/jbd2/checkpoint.c b/fs/jbd2/checkpoint.c index 076d1cc44f95..f8cdc02520f9 100644 --- a/fs/jbd2/checkpoint.c +++ b/fs/jbd2/checkpoint.c @@ -775,7 +775,7 @@ void __jbd2_journal_drop_transaction(journal_t *journal, transaction_t *transact J_ASSERT(transaction->t_log_list == NULL); J_ASSERT(transaction->t_checkpoint_list == NULL); J_ASSERT(transaction->t_checkpoint_io_list == NULL); - J_ASSERT(transaction->t_updates == 0); + J_ASSERT(atomic_read(&transaction->t_updates) == 0); J_ASSERT(journal->j_committing_transaction != transaction); J_ASSERT(journal->j_running_transaction != transaction); diff --git a/fs/jbd2/commit.c b/fs/jbd2/commit.c index af056810acb6..fbd2c564e916 100644 --- a/fs/jbd2/commit.c +++ b/fs/jbd2/commit.c @@ -417,12 +417,12 @@ void jbd2_journal_commit_transaction(journal_t *journal) stats.run.rs_locked); spin_lock(&commit_transaction->t_handle_lock); - while (commit_transaction->t_updates) { + while (atomic_read(&commit_transaction->t_updates)) { DEFINE_WAIT(wait); prepare_to_wait(&journal->j_wait_updates, &wait, TASK_UNINTERRUPTIBLE); - if (commit_transaction->t_updates) { + if (atomic_read(&commit_transaction->t_updates)) { spin_unlock(&commit_transaction->t_handle_lock); spin_unlock(&journal->j_state_lock); schedule(); @@ -433,7 +433,7 @@ void jbd2_journal_commit_transaction(journal_t *journal) } spin_unlock(&commit_transaction->t_handle_lock); - J_ASSERT (commit_transaction->t_outstanding_credits <= + J_ASSERT (atomic_read(&commit_transaction->t_outstanding_credits) <= journal->j_max_transaction_buffers); /* @@ -527,11 +527,12 @@ void jbd2_journal_commit_transaction(journal_t *journal) stats.run.rs_logging = jiffies; stats.run.rs_flushing = jbd2_time_diff(stats.run.rs_flushing, stats.run.rs_logging); - stats.run.rs_blocks = commit_transaction->t_outstanding_credits; + stats.run.rs_blocks = + atomic_read(&commit_transaction->t_outstanding_credits); stats.run.rs_blocks_logged = 0; J_ASSERT(commit_transaction->t_nr_buffers <= - commit_transaction->t_outstanding_credits); + atomic_read(&commit_transaction->t_outstanding_credits)); err = 0; descriptor = NULL; @@ -616,7 +617,7 @@ void jbd2_journal_commit_transaction(journal_t *journal) * the free space in the log, but this counter is changed * by jbd2_journal_next_log_block() also. */ - commit_transaction->t_outstanding_credits--; + atomic_dec(&commit_transaction->t_outstanding_credits); /* Bump b_count to prevent truncate from stumbling over the shadowed buffer! @@@ This can go if we ever get diff --git a/fs/jbd2/transaction.c b/fs/jbd2/transaction.c index 001e95fb0fe1..9c64c7ec48d4 100644 --- a/fs/jbd2/transaction.c +++ b/fs/jbd2/transaction.c @@ -55,6 +55,8 @@ jbd2_get_transaction(journal_t *journal, transaction_t *transaction) transaction->t_tid = journal->j_transaction_sequence++; transaction->t_expires = jiffies + journal->j_commit_interval; spin_lock_init(&transaction->t_handle_lock); + atomic_set(&transaction->t_updates, 0); + atomic_set(&transaction->t_outstanding_credits, 0); INIT_LIST_HEAD(&transaction->t_inode_list); INIT_LIST_HEAD(&transaction->t_private_list); @@ -177,7 +179,7 @@ repeat_locked: * checkpoint to free some more log space. */ spin_lock(&transaction->t_handle_lock); - needed = transaction->t_outstanding_credits + nblocks; + needed = atomic_read(&transaction->t_outstanding_credits) + nblocks; if (needed > journal->j_max_transaction_buffers) { /* @@ -240,11 +242,12 @@ repeat_locked: } handle->h_transaction = transaction; - transaction->t_outstanding_credits += nblocks; - transaction->t_updates++; + atomic_add(nblocks, &transaction->t_outstanding_credits); + atomic_inc(&transaction->t_updates); transaction->t_handle_count++; jbd_debug(4, "Handle %p given %d credits (total %d, free %d)\n", - handle, nblocks, transaction->t_outstanding_credits, + handle, nblocks, + atomic_read(&transaction->t_outstanding_credits), __jbd2_log_space_left(journal)); spin_unlock(&transaction->t_handle_lock); spin_unlock(&journal->j_state_lock); @@ -369,7 +372,7 @@ int jbd2_journal_extend(handle_t *handle, int nblocks) } spin_lock(&transaction->t_handle_lock); - wanted = transaction->t_outstanding_credits + nblocks; + wanted = atomic_read(&transaction->t_outstanding_credits) + nblocks; if (wanted > journal->j_max_transaction_buffers) { jbd_debug(3, "denied handle %p %d blocks: " @@ -384,7 +387,7 @@ int jbd2_journal_extend(handle_t *handle, int nblocks) } handle->h_buffer_credits += nblocks; - transaction->t_outstanding_credits += nblocks; + atomic_add(nblocks, &transaction->t_outstanding_credits); result = 0; jbd_debug(3, "extended handle %p by %d\n", handle, nblocks); @@ -426,15 +429,14 @@ int jbd2__journal_restart(handle_t *handle, int nblocks, int gfp_mask) * First unlink the handle from its current transaction, and start the * commit on that. */ - J_ASSERT(transaction->t_updates > 0); + J_ASSERT(atomic_read(&transaction->t_updates) > 0); J_ASSERT(journal_current_handle() == handle); spin_lock(&journal->j_state_lock); spin_lock(&transaction->t_handle_lock); - transaction->t_outstanding_credits -= handle->h_buffer_credits; - transaction->t_updates--; - - if (!transaction->t_updates) + atomic_sub(handle->h_buffer_credits, + &transaction->t_outstanding_credits); + if (atomic_dec_and_test(&transaction->t_updates)) wake_up(&journal->j_wait_updates); spin_unlock(&transaction->t_handle_lock); @@ -481,7 +483,7 @@ void jbd2_journal_lock_updates(journal_t *journal) break; spin_lock(&transaction->t_handle_lock); - if (!transaction->t_updates) { + if (!atomic_read(&transaction->t_updates)) { spin_unlock(&transaction->t_handle_lock); break; } @@ -1258,7 +1260,8 @@ int jbd2_journal_stop(handle_t *handle) { transaction_t *transaction = handle->h_transaction; journal_t *journal = transaction->t_journal; - int err; + int err, wait_for_commit = 0; + tid_t tid; pid_t pid; J_ASSERT(journal_current_handle() == handle); @@ -1266,7 +1269,7 @@ int jbd2_journal_stop(handle_t *handle) if (is_handle_aborted(handle)) err = -EIO; else { - J_ASSERT(transaction->t_updates > 0); + J_ASSERT(atomic_read(&transaction->t_updates) > 0); err = 0; } @@ -1334,14 +1337,8 @@ int jbd2_journal_stop(handle_t *handle) if (handle->h_sync) transaction->t_synchronous_commit = 1; current->journal_info = NULL; - spin_lock(&transaction->t_handle_lock); - transaction->t_outstanding_credits -= handle->h_buffer_credits; - transaction->t_updates--; - if (!transaction->t_updates) { - wake_up(&journal->j_wait_updates); - if (journal->j_barrier_count) - wake_up(&journal->j_wait_transaction_locked); - } + atomic_sub(handle->h_buffer_credits, + &transaction->t_outstanding_credits); /* * If the handle is marked SYNC, we need to set another commit @@ -1350,15 +1347,13 @@ int jbd2_journal_stop(handle_t *handle) * transaction is too old now. */ if (handle->h_sync || - transaction->t_outstanding_credits > - journal->j_max_transaction_buffers || - time_after_eq(jiffies, transaction->t_expires)) { + (atomic_read(&transaction->t_outstanding_credits) > + journal->j_max_transaction_buffers) || + time_after_eq(jiffies, transaction->t_expires)) { /* Do this even for aborted journals: an abort still * completes the commit thread, it just doesn't write * anything to disk. */ - tid_t tid = transaction->t_tid; - spin_unlock(&transaction->t_handle_lock); jbd_debug(2, "transaction too old, requesting commit for " "handle %p\n", handle); /* This is non-blocking */ @@ -1369,11 +1364,25 @@ int jbd2_journal_stop(handle_t *handle) * to wait for the commit to complete. */ if (handle->h_sync && !(current->flags & PF_MEMALLOC)) - err = jbd2_log_wait_commit(journal, tid); - } else { - spin_unlock(&transaction->t_handle_lock); + wait_for_commit = 1; } + /* + * Once we drop t_updates, if it goes to zero the transaction + * could start commiting on us and eventually disappear. So + * once we do this, we must not dereference transaction + * pointer again. + */ + tid = transaction->t_tid; + if (atomic_dec_and_test(&transaction->t_updates)) { + wake_up(&journal->j_wait_updates); + if (journal->j_barrier_count) + wake_up(&journal->j_wait_transaction_locked); + } + + if (wait_for_commit) + err = jbd2_log_wait_commit(journal, tid); + lock_map_release(&handle->h_lockdep_map); jbd2_free_handle(handle); diff --git a/include/linux/jbd2.h b/include/linux/jbd2.h index 5a72bc75b273..a72ce21de0e1 100644 --- a/include/linux/jbd2.h +++ b/include/linux/jbd2.h @@ -601,13 +601,13 @@ struct transaction_s * Number of outstanding updates running on this transaction * [t_handle_lock] */ - int t_updates; + atomic_t t_updates; /* * Number of buffers reserved for use by all handles in this transaction * handle but not yet modified. [t_handle_lock] */ - int t_outstanding_credits; + atomic_t t_outstanding_credits; /* * Forward and backward links for the circular list of all transactions @@ -1258,8 +1258,8 @@ static inline int jbd_space_needed(journal_t *journal) { int nblocks = journal->j_max_transaction_buffers; if (journal->j_committing_transaction) - nblocks += journal->j_committing_transaction-> - t_outstanding_credits; + nblocks += atomic_read(&journal->j_committing_transaction-> + t_outstanding_credits); return nblocks; } -- cgit v1.2.3 From de67445f0e6009fd1e338eda023857b18b16e647 Mon Sep 17 00:00:00 2001 From: Huang Weiyi Date: Sun, 23 May 2010 01:13:09 +0800 Subject: udf: remove duplicated #include Remove duplicated #include('s) in fs/udf/file.c Signed-off-by: Huang Weiyi Signed-off-by: Jan Kara --- fs/udf/file.c | 1 - 1 file changed, 1 deletion(-) (limited to 'fs') diff --git a/fs/udf/file.c b/fs/udf/file.c index 94e06d6bddbd..6e450e01a1bb 100644 --- a/fs/udf/file.c +++ b/fs/udf/file.c @@ -36,7 +36,6 @@ #include #include #include -#include #include "udf_i.h" #include "udf_sb.h" -- cgit v1.2.3 From 581b7e9fc00789782cb3bdca58db49f6f0f66608 Mon Sep 17 00:00:00 2001 From: "Justin P. Mattock" Date: Wed, 7 Jul 2010 21:42:13 -0700 Subject: udf: super.c Fix warning: variable 'sbi' set but not used This fixes this warning when building the kernel: CC fs/udf/super.o fs/udf/super.c: In function 'udf_load_sequence': fs/udf/super.c:1582:22: warning: variable 'sbi' set but not used Please have a look, when you have time and let me know. Signed-off-by: Justin P. Mattock Signed-off-by: Jan Kara --- fs/udf/super.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'fs') diff --git a/fs/udf/super.c b/fs/udf/super.c index 612d1e2e285a..12bb651e5400 100644 --- a/fs/udf/super.c +++ b/fs/udf/super.c @@ -1579,9 +1579,7 @@ static int udf_load_sequence(struct super_block *sb, struct buffer_head *bh, { struct anchorVolDescPtr *anchor; long main_s, main_e, reserve_s, reserve_e; - struct udf_sb_info *sbi; - sbi = UDF_SB(sb); anchor = (struct anchorVolDescPtr *)bh->b_data; /* Locate the main sequence */ -- cgit v1.2.3 From 73a7e693f9da464b0df07643af3f8ffc04dcc0b5 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Mon, 2 Aug 2010 11:00:55 -0700 Subject: ceph: fix decoding of pool snap info The pool info contains a vector for snap_info_t, not snap ids. This fixes the broken decoding, which would declare teh update corrupt when a pool snapshot was created. Signed-off-by: Sage Weil --- fs/ceph/osdmap.c | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/ceph/osdmap.c b/fs/ceph/osdmap.c index 1d5f58cc2d93..e94c6fb2e2a4 100644 --- a/fs/ceph/osdmap.c +++ b/fs/ceph/osdmap.c @@ -424,12 +424,30 @@ static void __remove_pg_pool(struct rb_root *root, struct ceph_pg_pool_info *pi) kfree(pi); } -static void __decode_pool(void **p, struct ceph_pg_pool_info *pi) +static int __decode_pool(void **p, void *end, struct ceph_pg_pool_info *pi) { + unsigned n, m; + ceph_decode_copy(p, &pi->v, sizeof(pi->v)); calc_pg_masks(pi); - *p += le32_to_cpu(pi->v.num_snaps) * sizeof(u64); + + /* num_snaps * snap_info_t */ + n = le32_to_cpu(pi->v.num_snaps); + while (n--) { + ceph_decode_need(p, end, sizeof(u64) + 1 + sizeof(u64) + + sizeof(struct ceph_timespec), bad); + *p += sizeof(u64) + /* key */ + 1 + sizeof(u64) + /* u8, snapid */ + sizeof(struct ceph_timespec); + m = ceph_decode_32(p); /* snap name */ + *p += m; + } + *p += le32_to_cpu(pi->v.num_removed_snap_intervals) * sizeof(u64) * 2; + return 0; + +bad: + return -EINVAL; } static int __decode_pool_names(void **p, void *end, struct ceph_osdmap *map) @@ -571,7 +589,9 @@ struct ceph_osdmap *osdmap_decode(void **p, void *end) kfree(pi); goto bad; } - __decode_pool(p, pi); + err = __decode_pool(p, end, pi); + if (err < 0) + goto bad; __insert_pg_pool(&map->pg_pools, pi); } @@ -760,7 +780,9 @@ struct ceph_osdmap *osdmap_apply_incremental(void **p, void *end, pi->id = pool; __insert_pg_pool(&map->pg_pools, pi); } - __decode_pool(p, pi); + err = __decode_pool(p, end, pi); + if (err < 0) + goto bad; } if (version >= 5 && __decode_pool_names(p, end, map) < 0) goto bad; -- cgit v1.2.3 From 97e8442b0971ea6be9a495b3d03402985cfe5d6a Mon Sep 17 00:00:00 2001 From: "M. Mohan Kumar" Date: Fri, 4 Jun 2010 11:59:07 +0000 Subject: 9p: Make use of iounit for read/write Change the v9fs_file_readn function to limit the maximum transfer size based on the iounit or msize. Also remove the redundant check for limiting the transfer size in v9fs_file_write. This check is done by p9_client_write. Signed-off-by: M. Mohan Kumar Signed-off-by: Eric Van Hensbergen --- fs/9p/vfs_file.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'fs') diff --git a/fs/9p/vfs_file.c b/fs/9p/vfs_file.c index 2bedc6c94fc2..2d686ec322a0 100644 --- a/fs/9p/vfs_file.c +++ b/fs/9p/vfs_file.c @@ -139,7 +139,7 @@ ssize_t v9fs_file_readn(struct file *filp, char *data, char __user *udata, u32 count, u64 offset) { - int n, total; + int n, total, size; struct p9_fid *fid = filp->private_data; P9_DPRINTK(P9_DEBUG_VFS, "fid %d offset %llu count %d\n", fid->fid, @@ -147,6 +147,7 @@ v9fs_file_readn(struct file *filp, char *data, char __user *udata, u32 count, n = 0; total = 0; + size = fid->iounit ? fid->iounit : fid->clnt->msize - P9_IOHDRSZ; do { n = p9_client_read(fid, data, udata, offset, count); if (n <= 0) @@ -160,7 +161,7 @@ v9fs_file_readn(struct file *filp, char *data, char __user *udata, u32 count, offset += n; count -= n; total += n; - } while (count > 0 && n == (fid->clnt->msize - P9_IOHDRSZ)); + } while (count > 0 && n == size); if (n < 0) total = n; @@ -183,11 +184,13 @@ v9fs_file_read(struct file *filp, char __user *udata, size_t count, { int ret; struct p9_fid *fid; + size_t size; P9_DPRINTK(P9_DEBUG_VFS, "count %zu offset %lld\n", count, *offset); fid = filp->private_data; - if (count > (fid->clnt->msize - P9_IOHDRSZ)) + size = fid->iounit ? fid->iounit : fid->clnt->msize - P9_IOHDRSZ; + if (count > size) ret = v9fs_file_readn(filp, NULL, udata, count, *offset); else ret = p9_client_read(fid, NULL, udata, *offset, count); @@ -224,9 +227,7 @@ v9fs_file_write(struct file *filp, const char __user * data, fid = filp->private_data; clnt = fid->clnt; - rsize = fid->iounit; - if (!rsize || rsize > clnt->msize-P9_IOHDRSZ) - rsize = clnt->msize - P9_IOHDRSZ; + rsize = fid->iounit ? fid->iounit : clnt->msize - P9_IOHDRSZ; do { if (count < rsize) -- cgit v1.2.3 From 7751bdb3a095ad32dd4fcff3443cf8dd4cb1e748 Mon Sep 17 00:00:00 2001 From: Sripathi Kodi Date: Fri, 4 Jun 2010 13:41:26 +0000 Subject: 9p: readdir implementation for 9p2000.L This patch implements the kernel part of readdir() implementation for 9p2000.L Change from V3: Instead of inode, server now sends qids for each dirent SYNOPSIS size[4] Treaddir tag[2] fid[4] offset[8] count[4] size[4] Rreaddir tag[2] count[4] data[count] DESCRIPTION The readdir request asks the server to read the directory specified by 'fid' at an offset specified by 'offset' and return as many dirent structures as possible that fit into count bytes. Each dirent structure is laid out as follows. qid.type[1] the type of the file (directory, etc.), represented as a bit vector corresponding to the high 8 bits of the file's mode word. qid.vers[4] version number for given path qid.path[8] the file server's unique identification for the file offset[8] offset into the next dirent. type[1] type of this directory entry. name[256] name of this directory entry. This patch adds v9fs_dir_readdir_dotl() as the readdir() call for 9p2000.L. This function sends P9_TREADDIR command to the server. In response the server sends a buffer filled with dirent structures. This is different from the existing v9fs_dir_readdir() call which receives stat structures from the server. This results in significant speedup of readdir() on large directories. For example, doing 'ls >/dev/null' on a directory with 10000 files on my laptop takes 1.088 seconds with the existing code, but only takes 0.339 seconds with the new readdir. Signed-off-by: Sripathi Kodi Reviewed-by: Aneesh Kumar K.V Signed-off-by: Eric Van Hensbergen --- fs/9p/vfs_dir.c | 134 ++++++++++++++++++++++++++++++++++++++++++------ include/net/9p/9p.h | 17 ++++++ include/net/9p/client.h | 18 +++++++ net/9p/client.c | 47 +++++++++++++++++ net/9p/protocol.c | 27 ++++++++++ 5 files changed, 227 insertions(+), 16 deletions(-) (limited to 'fs') diff --git a/fs/9p/vfs_dir.c b/fs/9p/vfs_dir.c index 36d961f342af..16c8a2a98c1b 100644 --- a/fs/9p/vfs_dir.c +++ b/fs/9p/vfs_dir.c @@ -87,29 +87,19 @@ static void p9stat_init(struct p9_wstat *stbuf) } /** - * v9fs_dir_readdir - read a directory + * v9fs_alloc_rdir_buf - Allocate buffer used for read and readdir * @filp: opened file structure - * @dirent: directory structure ??? - * @filldir: function to populate directory structure ??? + * @buflen: Length in bytes of buffer to allocate * */ -static int v9fs_dir_readdir(struct file *filp, void *dirent, filldir_t filldir) +static int v9fs_alloc_rdir_buf(struct file *filp, int buflen) { - int over; - struct p9_wstat st; - int err = 0; - struct p9_fid *fid; - int buflen; - int reclen = 0; struct p9_rdir *rdir; + struct p9_fid *fid; + int err = 0; - P9_DPRINTK(P9_DEBUG_VFS, "name %s\n", filp->f_path.dentry->d_name.name); fid = filp->private_data; - - buflen = fid->clnt->msize - P9_IOHDRSZ; - - /* allocate rdir on demand */ if (!fid->rdir) { rdir = kmalloc(sizeof(struct p9_rdir) + buflen, GFP_KERNEL); @@ -128,6 +118,36 @@ static int v9fs_dir_readdir(struct file *filp, void *dirent, filldir_t filldir) spin_unlock(&filp->f_dentry->d_lock); kfree(rdir); } +exit: + return err; +} + +/** + * v9fs_dir_readdir - read a directory + * @filp: opened file structure + * @dirent: directory structure ??? + * @filldir: function to populate directory structure ??? + * + */ + +static int v9fs_dir_readdir(struct file *filp, void *dirent, filldir_t filldir) +{ + int over; + struct p9_wstat st; + int err = 0; + struct p9_fid *fid; + int buflen; + int reclen = 0; + struct p9_rdir *rdir; + + P9_DPRINTK(P9_DEBUG_VFS, "name %s\n", filp->f_path.dentry->d_name.name); + fid = filp->private_data; + + buflen = fid->clnt->msize - P9_IOHDRSZ; + + err = v9fs_alloc_rdir_buf(filp, buflen); + if (err) + goto exit; rdir = (struct p9_rdir *) fid->rdir; err = mutex_lock_interruptible(&rdir->mutex); @@ -176,6 +196,88 @@ exit: return err; } +/** + * v9fs_dir_readdir_dotl - read a directory + * @filp: opened file structure + * @dirent: buffer to fill dirent structures + * @filldir: function to populate dirent structures + * + */ +static int v9fs_dir_readdir_dotl(struct file *filp, void *dirent, + filldir_t filldir) +{ + int over; + int err = 0; + struct p9_fid *fid; + int buflen; + struct p9_rdir *rdir; + struct p9_dirent curdirent; + u64 oldoffset = 0; + + P9_DPRINTK(P9_DEBUG_VFS, "name %s\n", filp->f_path.dentry->d_name.name); + fid = filp->private_data; + + buflen = fid->clnt->msize - P9_READDIRHDRSZ; + + err = v9fs_alloc_rdir_buf(filp, buflen); + if (err) + goto exit; + rdir = (struct p9_rdir *) fid->rdir; + + err = mutex_lock_interruptible(&rdir->mutex); + if (err) + return err; + + while (err == 0) { + if (rdir->tail == rdir->head) { + err = p9_client_readdir(fid, rdir->buf, buflen, + filp->f_pos); + if (err <= 0) + goto unlock_and_exit; + + rdir->head = 0; + rdir->tail = err; + } + + while (rdir->head < rdir->tail) { + + err = p9dirent_read(rdir->buf + rdir->head, + buflen - rdir->head, &curdirent, + fid->clnt->proto_version); + if (err < 0) { + P9_DPRINTK(P9_DEBUG_VFS, "returned %d\n", err); + err = -EIO; + goto unlock_and_exit; + } + + /* d_off in dirent structure tracks the offset into + * the next dirent in the dir. However, filldir() + * expects offset into the current dirent. Hence + * while calling filldir send the offset from the + * previous dirent structure. + */ + over = filldir(dirent, curdirent.d_name, + strlen(curdirent.d_name), + oldoffset, v9fs_qid2ino(&curdirent.qid), + curdirent.d_type); + oldoffset = curdirent.d_off; + + if (over) { + err = 0; + goto unlock_and_exit; + } + + filp->f_pos = curdirent.d_off; + rdir->head += err; + } + } + +unlock_and_exit: + mutex_unlock(&rdir->mutex); +exit: + return err; +} + /** * v9fs_dir_release - close a directory @@ -207,7 +309,7 @@ const struct file_operations v9fs_dir_operations = { const struct file_operations v9fs_dir_operations_dotl = { .read = generic_read_dir, .llseek = generic_file_llseek, - .readdir = v9fs_dir_readdir, + .readdir = v9fs_dir_readdir_dotl, .open = v9fs_file_open, .release = v9fs_dir_release, }; diff --git a/include/net/9p/9p.h b/include/net/9p/9p.h index 156c26bb8bd7..f1b0b310265d 100644 --- a/include/net/9p/9p.h +++ b/include/net/9p/9p.h @@ -133,6 +133,8 @@ enum p9_msg_t { P9_RSTATFS, P9_TRENAME = 20, P9_RRENAME, + P9_TREADDIR = 40, + P9_RREADDIR, P9_TVERSION = 100, P9_RVERSION, P9_TAUTH = 102, @@ -275,6 +277,9 @@ enum p9_qid_t { /* ample room for Twrite/Rread header */ #define P9_IOHDRSZ 24 +/* Room for readdir header */ +#define P9_READDIRHDRSZ 24 + /** * struct p9_str - length prefixed string type * @len: length of the string @@ -485,6 +490,18 @@ struct p9_rwrite { u32 count; }; +struct p9_treaddir { + u32 fid; + u64 offset; + u32 count; +}; + +struct p9_rreaddir { + u32 count; + u8 *data; +}; + + struct p9_tclunk { u32 fid; }; diff --git a/include/net/9p/client.h b/include/net/9p/client.h index 7dd3ed85c782..2ec93685e6db 100644 --- a/include/net/9p/client.h +++ b/include/net/9p/client.h @@ -195,6 +195,21 @@ struct p9_fid { struct list_head dlist; /* list of all fids attached to a dentry */ }; +/** + * struct p9_dirent - directory entry structure + * @qid: The p9 server qid for this dirent + * @d_off: offset to the next dirent + * @d_type: type of file + * @d_name: file name + */ + +struct p9_dirent { + struct p9_qid qid; + u64 d_off; + unsigned char d_type; + char d_name[256]; +}; + int p9_client_statfs(struct p9_fid *fid, struct p9_rstatfs *sb); int p9_client_rename(struct p9_fid *fid, struct p9_fid *newdirfid, char *name); int p9_client_version(struct p9_client *); @@ -217,6 +232,9 @@ int p9_client_read(struct p9_fid *fid, char *data, char __user *udata, u64 offset, u32 count); int p9_client_write(struct p9_fid *fid, char *data, const char __user *udata, u64 offset, u32 count); +int p9_client_readdir(struct p9_fid *fid, char *data, u32 count, u64 offset); +int p9dirent_read(char *buf, int len, struct p9_dirent *dirent, + int proto_version); struct p9_wstat *p9_client_stat(struct p9_fid *fid); int p9_client_wstat(struct p9_fid *fid, struct p9_wstat *wst); diff --git a/net/9p/client.c b/net/9p/client.c index 37c8da07a80b..a80357483a47 100644 --- a/net/9p/client.c +++ b/net/9p/client.c @@ -1432,3 +1432,50 @@ error: } EXPORT_SYMBOL(p9_client_rename); +int p9_client_readdir(struct p9_fid *fid, char *data, u32 count, u64 offset) +{ + int err, rsize, total; + struct p9_client *clnt; + struct p9_req_t *req; + char *dataptr; + + P9_DPRINTK(P9_DEBUG_9P, ">>> TREADDIR fid %d offset %llu count %d\n", + fid->fid, (long long unsigned) offset, count); + + err = 0; + clnt = fid->clnt; + total = 0; + + rsize = fid->iounit; + if (!rsize || rsize > clnt->msize-P9_READDIRHDRSZ) + rsize = clnt->msize - P9_READDIRHDRSZ; + + if (count < rsize) + rsize = count; + + req = p9_client_rpc(clnt, P9_TREADDIR, "dqd", fid->fid, offset, rsize); + if (IS_ERR(req)) { + err = PTR_ERR(req); + goto error; + } + + err = p9pdu_readf(req->rc, clnt->proto_version, "D", &count, &dataptr); + if (err) { + p9pdu_dump(1, req->rc); + goto free_and_error; + } + + P9_DPRINTK(P9_DEBUG_9P, "<<< RREADDIR count %d\n", count); + + if (data) + memmove(data, dataptr, count); + + p9_free_req(clnt, req); + return count; + +free_and_error: + p9_free_req(clnt, req); +error: + return err; +} +EXPORT_SYMBOL(p9_client_readdir); diff --git a/net/9p/protocol.c b/net/9p/protocol.c index 149f82160130..b645c8263538 100644 --- a/net/9p/protocol.c +++ b/net/9p/protocol.c @@ -580,3 +580,30 @@ void p9pdu_reset(struct p9_fcall *pdu) pdu->offset = 0; pdu->size = 0; } + +int p9dirent_read(char *buf, int len, struct p9_dirent *dirent, + int proto_version) +{ + struct p9_fcall fake_pdu; + int ret; + char *nameptr; + + fake_pdu.size = len; + fake_pdu.capacity = len; + fake_pdu.sdata = buf; + fake_pdu.offset = 0; + + ret = p9pdu_readf(&fake_pdu, proto_version, "Qqbs", &dirent->qid, + &dirent->d_off, &dirent->d_type, &nameptr); + if (ret) { + P9_DPRINTK(P9_DEBUG_9P, "<<< p9dirent_read failed: %d\n", ret); + p9pdu_dump(1, &fake_pdu); + goto out; + } + + strcpy(dirent->d_name, nameptr); + +out: + return fake_pdu.offset; +} +EXPORT_SYMBOL(p9dirent_read); -- cgit v1.2.3 From 9ffaf63e34821ea60b2e1c8593f968d73728f82b Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Tue, 1 Jun 2010 09:26:18 +0000 Subject: fs/9p: Pass the correct user credentials during attach We need to make sure we pass the right uid value during attach. dotl is similar to dotu in this regard. Without this mapped security model on dotl doesn't work Signed-off-by: Aneesh Kumar K.V Signed-off-by: Eric Van Hensbergen --- fs/9p/fid.c | 3 ++- fs/9p/v9fs.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/9p/fid.c b/fs/9p/fid.c index 7317b39b2815..5d6cfcbf73e7 100644 --- a/fs/9p/fid.c +++ b/fs/9p/fid.c @@ -152,7 +152,8 @@ struct p9_fid *v9fs_fid_lookup(struct dentry *dentry) if (access == V9FS_ACCESS_SINGLE) return ERR_PTR(-EPERM); - if (v9fs_proto_dotu(v9ses)) + if (v9fs_proto_dotu(v9ses) || + v9fs_proto_dotl(v9ses)) uname = NULL; else uname = v9ses->uname; diff --git a/fs/9p/v9fs.c b/fs/9p/v9fs.c index f8b86e92cd66..3c492011221c 100644 --- a/fs/9p/v9fs.c +++ b/fs/9p/v9fs.c @@ -278,7 +278,7 @@ struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses, v9ses->maxdata = v9ses->clnt->msize - P9_IOHDRSZ; /* for legacy mode, fall back to V9FS_ACCESS_ANY */ - if (!v9fs_proto_dotu(v9ses) && + if (!(v9fs_proto_dotu(v9ses) || v9fs_proto_dotl(v9ses)) && ((v9ses->flags&V9FS_ACCESS_MASK) == V9FS_ACCESS_USER)) { v9ses->flags &= ~V9FS_ACCESS_MASK; -- cgit v1.2.3 From f085312204f384a0277a66c3c48ba8f9edcd58f2 Mon Sep 17 00:00:00 2001 From: Sripathi Kodi Date: Mon, 12 Jul 2010 20:07:23 +0530 Subject: 9p: getattr client implementation for 9P2000.L protocol. SYNOPSIS size[4] Tgetattr tag[2] fid[4] request_mask[8] size[4] Rgetattr tag[2] lstat[n] DESCRIPTION The getattr transaction inquires about the file identified by fid. request_mask is a bit mask that specifies which fields of the stat structure is the client interested in. The reply will contain a machine-independent directory entry, laid out as follows: st_result_mask[8] Bit mask that indicates which fields in the stat structure have been populated by the server qid.type[1] the type of the file (directory, etc.), represented as a bit vector corresponding to the high 8 bits of the file's mode word. qid.vers[4] version number for given path qid.path[8] the file server's unique identification for the file st_mode[4] Permission and flags st_uid[4] User id of owner st_gid[4] Group ID of owner st_nlink[8] Number of hard links st_rdev[8] Device ID (if special file) st_size[8] Size, in bytes st_blksize[8] Block size for file system IO st_blocks[8] Number of file system blocks allocated st_atime_sec[8] Time of last access, seconds st_atime_nsec[8] Time of last access, nanoseconds st_mtime_sec[8] Time of last modification, seconds st_mtime_nsec[8] Time of last modification, nanoseconds st_ctime_sec[8] Time of last status change, seconds st_ctime_nsec[8] Time of last status change, nanoseconds st_btime_sec[8] Time of creation (birth) of file, seconds st_btime_nsec[8] Time of creation (birth) of file, nanoseconds st_gen[8] Inode generation st_data_version[8] Data version number request_mask and result_mask bit masks contain the following bits #define P9_STATS_MODE 0x00000001ULL #define P9_STATS_NLINK 0x00000002ULL #define P9_STATS_UID 0x00000004ULL #define P9_STATS_GID 0x00000008ULL #define P9_STATS_RDEV 0x00000010ULL #define P9_STATS_ATIME 0x00000020ULL #define P9_STATS_MTIME 0x00000040ULL #define P9_STATS_CTIME 0x00000080ULL #define P9_STATS_INO 0x00000100ULL #define P9_STATS_SIZE 0x00000200ULL #define P9_STATS_BLOCKS 0x00000400ULL #define P9_STATS_BTIME 0x00000800ULL #define P9_STATS_GEN 0x00001000ULL #define P9_STATS_DATA_VERSION 0x00002000ULL #define P9_STATS_BASIC 0x000007ffULL #define P9_STATS_ALL 0x00003fffULL This patch implements the client side of getattr implementation for 9P2000.L. It introduces a new structure p9_stat_dotl for getting Linux stat information along with QID. The data layout is similar to stat structure in Linux user space with the following major differences: inode (st_ino) is not part of data. Instead qid is. device (st_dev) is not part of data because this doesn't make sense on the client. All time variables are 64 bit wide on the wire. The kernel seems to use 32 bit variables for these variables. However, some of the architectures have used 64 bit variables and glibc exposes 64 bit variables to user space on some architectures. Hence to be on the safer side we have made these 64 bit in the protocol. Refer to the comments in include/asm-generic/stat.h There are some additional fields: st_btime_sec, st_btime_nsec, st_gen, st_data_version apart from the bitmask, st_result_mask. The bit mask is filled by the server to indicate which stat fields have been populated by the server. Currently there is no clean way for the server to obtain these additional fields, so it sends back just the basic fields. Signed-off-by: Sripathi Kodi Signed-off-by: Eric Van Hensbegren --- fs/9p/v9fs_vfs.h | 1 + fs/9p/vfs_inode.c | 177 +++++++++++++++++++++++++++++++++++++++++++----- fs/9p/vfs_super.c | 43 +++++++----- include/net/9p/9p.h | 44 ++++++++++++ include/net/9p/client.h | 3 + net/9p/client.c | 59 ++++++++++++++++ net/9p/protocol.c | 28 ++++++++ 7 files changed, 321 insertions(+), 34 deletions(-) (limited to 'fs') diff --git a/fs/9p/v9fs_vfs.h b/fs/9p/v9fs_vfs.h index 32ef4009d030..f47c6bbb01b3 100644 --- a/fs/9p/v9fs_vfs.h +++ b/fs/9p/v9fs_vfs.h @@ -55,6 +55,7 @@ struct inode *v9fs_get_inode(struct super_block *sb, int mode); void v9fs_clear_inode(struct inode *inode); ino_t v9fs_qid2ino(struct p9_qid *qid); void v9fs_stat2inode(struct p9_wstat *, struct inode *, struct super_block *); +void v9fs_stat2inode_dotl(struct p9_stat_dotl *, struct inode *); int v9fs_dir_release(struct inode *inode, struct file *filp); int v9fs_file_open(struct inode *inode, struct file *file); void v9fs_inode2stat(struct inode *inode, struct p9_wstat *stat); diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index 4331b3b5ee1c..afcb8d889382 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -396,23 +396,14 @@ void v9fs_clear_inode(struct inode *inode) #endif } -/** - * v9fs_inode_from_fid - populate an inode by issuing a attribute request - * @v9ses: session information - * @fid: fid to issue attribute request for - * @sb: superblock on which to create inode - * - */ - static struct inode * -v9fs_inode_from_fid(struct v9fs_session_info *v9ses, struct p9_fid *fid, +v9fs_inode(struct v9fs_session_info *v9ses, struct p9_fid *fid, struct super_block *sb) { int err, umode; - struct inode *ret; + struct inode *ret = NULL; struct p9_wstat *st; - ret = NULL; st = p9_client_stat(fid); if (IS_ERR(st)) return ERR_CAST(st); @@ -433,15 +424,62 @@ v9fs_inode_from_fid(struct v9fs_session_info *v9ses, struct p9_fid *fid, #endif p9stat_free(st); kfree(st); - return ret; - error: p9stat_free(st); kfree(st); return ERR_PTR(err); } +static struct inode * +v9fs_inode_dotl(struct v9fs_session_info *v9ses, struct p9_fid *fid, + struct super_block *sb) +{ + struct inode *ret = NULL; + int err; + struct p9_stat_dotl *st; + + st = p9_client_getattr_dotl(fid, P9_STATS_BASIC); + if (IS_ERR(st)) + return ERR_CAST(st); + + ret = v9fs_get_inode(sb, st->st_mode); + if (IS_ERR(ret)) { + err = PTR_ERR(ret); + goto error; + } + + v9fs_stat2inode_dotl(st, ret); + ret->i_ino = v9fs_qid2ino(&st->qid); +#ifdef CONFIG_9P_FSCACHE + v9fs_vcookie_set_qid(ret, &st->qid); + v9fs_cache_inode_get_cookie(ret); +#endif + kfree(st); + return ret; +error: + kfree(st); + return ERR_PTR(err); +} + +/** + * v9fs_inode_from_fid - Helper routine to populate an inode by + * issuing a attribute request + * @v9ses: session information + * @fid: fid to issue attribute request for + * @sb: superblock on which to create inode + * + */ +static inline struct inode * +v9fs_inode_from_fid(struct v9fs_session_info *v9ses, struct p9_fid *fid, + struct super_block *sb) +{ + if (v9fs_proto_dotl(v9ses)) + return v9fs_inode_dotl(v9ses, fid, sb); + else + return v9fs_inode(v9ses, fid, sb); +} + /** * v9fs_remove - helper function to remove files and directories * @dir: directory inode that is being deleted @@ -853,6 +891,42 @@ v9fs_vfs_getattr(struct vfsmount *mnt, struct dentry *dentry, return 0; } +static int +v9fs_vfs_getattr_dotl(struct vfsmount *mnt, struct dentry *dentry, + struct kstat *stat) +{ + int err; + struct v9fs_session_info *v9ses; + struct p9_fid *fid; + struct p9_stat_dotl *st; + + P9_DPRINTK(P9_DEBUG_VFS, "dentry: %p\n", dentry); + err = -EPERM; + v9ses = v9fs_inode2v9ses(dentry->d_inode); + if (v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE) + return simple_getattr(mnt, dentry, stat); + + fid = v9fs_fid_lookup(dentry); + if (IS_ERR(fid)) + return PTR_ERR(fid); + + /* Ask for all the fields in stat structure. Server will return + * whatever it supports + */ + + st = p9_client_getattr_dotl(fid, P9_STATS_ALL); + if (IS_ERR(st)) + return PTR_ERR(st); + + v9fs_stat2inode_dotl(st, dentry->d_inode); + generic_fillattr(dentry->d_inode, stat); + /* Change block size to what the server returned */ + stat->blksize = st->st_blksize; + + kfree(st); + return 0; +} + /** * v9fs_vfs_setattr - set file metadata * @dentry: file whose metadata to set @@ -979,6 +1053,77 @@ v9fs_stat2inode(struct p9_wstat *stat, struct inode *inode, inode->i_blocks = (i_size_read(inode) + 512 - 1) >> 9; } +/** + * v9fs_stat2inode_dotl - populate an inode structure with stat info + * @stat: stat structure + * @inode: inode to populate + * @sb: superblock of filesystem + * + */ + +void +v9fs_stat2inode_dotl(struct p9_stat_dotl *stat, struct inode *inode) +{ + + if ((stat->st_result_mask & P9_STATS_BASIC) == P9_STATS_BASIC) { + inode->i_atime.tv_sec = stat->st_atime_sec; + inode->i_atime.tv_nsec = stat->st_atime_nsec; + inode->i_mtime.tv_sec = stat->st_mtime_sec; + inode->i_mtime.tv_nsec = stat->st_mtime_nsec; + inode->i_ctime.tv_sec = stat->st_ctime_sec; + inode->i_ctime.tv_nsec = stat->st_ctime_nsec; + inode->i_uid = stat->st_uid; + inode->i_gid = stat->st_gid; + inode->i_nlink = stat->st_nlink; + inode->i_mode = stat->st_mode; + inode->i_rdev = new_decode_dev(stat->st_rdev); + + if ((S_ISBLK(inode->i_mode)) || (S_ISCHR(inode->i_mode))) + init_special_inode(inode, inode->i_mode, inode->i_rdev); + + i_size_write(inode, stat->st_size); + inode->i_blocks = stat->st_blocks; + } else { + if (stat->st_result_mask & P9_STATS_ATIME) { + inode->i_atime.tv_sec = stat->st_atime_sec; + inode->i_atime.tv_nsec = stat->st_atime_nsec; + } + if (stat->st_result_mask & P9_STATS_MTIME) { + inode->i_mtime.tv_sec = stat->st_mtime_sec; + inode->i_mtime.tv_nsec = stat->st_mtime_nsec; + } + if (stat->st_result_mask & P9_STATS_CTIME) { + inode->i_ctime.tv_sec = stat->st_ctime_sec; + inode->i_ctime.tv_nsec = stat->st_ctime_nsec; + } + if (stat->st_result_mask & P9_STATS_UID) + inode->i_uid = stat->st_uid; + if (stat->st_result_mask & P9_STATS_GID) + inode->i_gid = stat->st_gid; + if (stat->st_result_mask & P9_STATS_NLINK) + inode->i_nlink = stat->st_nlink; + if (stat->st_result_mask & P9_STATS_MODE) { + inode->i_mode = stat->st_mode; + if ((S_ISBLK(inode->i_mode)) || + (S_ISCHR(inode->i_mode))) + init_special_inode(inode, inode->i_mode, + inode->i_rdev); + } + if (stat->st_result_mask & P9_STATS_RDEV) + inode->i_rdev = new_decode_dev(stat->st_rdev); + if (stat->st_result_mask & P9_STATS_SIZE) + i_size_write(inode, stat->st_size); + if (stat->st_result_mask & P9_STATS_BLOCKS) + inode->i_blocks = stat->st_blocks; + } + if (stat->st_result_mask & P9_STATS_GEN) + inode->i_generation = stat->st_gen; + + /* Currently we don't support P9_STATS_BTIME and P9_STATS_DATA_VERSION + * because the inode structure does not have fields for them. + */ +} + /** * v9fs_qid2ino - convert qid into inode number * @qid: qid to hash @@ -1254,7 +1399,7 @@ static const struct inode_operations v9fs_dir_inode_operations_dotl = { .rmdir = v9fs_vfs_rmdir, .mknod = v9fs_vfs_mknod, .rename = v9fs_vfs_rename, - .getattr = v9fs_vfs_getattr, + .getattr = v9fs_vfs_getattr_dotl, .setattr = v9fs_vfs_setattr, }; @@ -1276,7 +1421,7 @@ static const struct inode_operations v9fs_file_inode_operations = { }; static const struct inode_operations v9fs_file_inode_operations_dotl = { - .getattr = v9fs_vfs_getattr, + .getattr = v9fs_vfs_getattr_dotl, .setattr = v9fs_vfs_setattr, }; @@ -1292,6 +1437,6 @@ static const struct inode_operations v9fs_symlink_inode_operations_dotl = { .readlink = generic_readlink, .follow_link = v9fs_vfs_follow_link, .put_link = v9fs_vfs_put_link, - .getattr = v9fs_vfs_getattr, + .getattr = v9fs_vfs_getattr_dotl, .setattr = v9fs_vfs_setattr, }; diff --git a/fs/9p/vfs_super.c b/fs/9p/vfs_super.c index be74d020436e..3623f692b448 100644 --- a/fs/9p/vfs_super.c +++ b/fs/9p/vfs_super.c @@ -107,7 +107,6 @@ static int v9fs_get_sb(struct file_system_type *fs_type, int flags, struct inode *inode = NULL; struct dentry *root = NULL; struct v9fs_session_info *v9ses = NULL; - struct p9_wstat *st = NULL; int mode = S_IRWXUGO | S_ISVTX; struct p9_fid *fid; int retval = 0; @@ -124,16 +123,10 @@ static int v9fs_get_sb(struct file_system_type *fs_type, int flags, goto close_session; } - st = p9_client_stat(fid); - if (IS_ERR(st)) { - retval = PTR_ERR(st); - goto clunk_fid; - } - sb = sget(fs_type, NULL, v9fs_set_super, v9ses); if (IS_ERR(sb)) { retval = PTR_ERR(sb); - goto free_stat; + goto clunk_fid; } v9fs_fill_super(sb, v9ses, flags, data); @@ -151,22 +144,38 @@ static int v9fs_get_sb(struct file_system_type *fs_type, int flags, } sb->s_root = root; - root->d_inode->i_ino = v9fs_qid2ino(&st->qid); - v9fs_stat2inode(st, root->d_inode, sb); + if (v9fs_proto_dotl(v9ses)) { + struct p9_stat_dotl *st = NULL; + st = p9_client_getattr_dotl(fid, P9_STATS_BASIC); + if (IS_ERR(st)) { + retval = PTR_ERR(st); + goto clunk_fid; + } + + v9fs_stat2inode_dotl(st, root->d_inode); + kfree(st); + } else { + struct p9_wstat *st = NULL; + st = p9_client_stat(fid); + if (IS_ERR(st)) { + retval = PTR_ERR(st); + goto clunk_fid; + } + + root->d_inode->i_ino = v9fs_qid2ino(&st->qid); + v9fs_stat2inode(st, root->d_inode, sb); + + p9stat_free(st); + kfree(st); + } v9fs_fid_add(root, fid); - p9stat_free(st); - kfree(st); P9_DPRINTK(P9_DEBUG_VFS, " simple set mount, return 0\n"); simple_set_mnt(mnt, sb); return 0; -free_stat: - p9stat_free(st); - kfree(st); - clunk_fid: p9_client_clunk(fid); @@ -176,8 +185,6 @@ close_session: return retval; release_sb: - p9stat_free(st); - kfree(st); deactivate_locked_super(sb); return retval; } diff --git a/include/net/9p/9p.h b/include/net/9p/9p.h index f1b0b310265d..ab12e1c9cc7e 100644 --- a/include/net/9p/9p.h +++ b/include/net/9p/9p.h @@ -133,6 +133,8 @@ enum p9_msg_t { P9_RSTATFS, P9_TRENAME = 20, P9_RRENAME, + P9_TGETATTR = 24, + P9_RGETATTR, P9_TREADDIR = 40, P9_RREADDIR, P9_TVERSION = 100, @@ -362,6 +364,48 @@ struct p9_wstat { u32 n_muid; /* 9p2000.u extensions */ }; +struct p9_stat_dotl { + u64 st_result_mask; + struct p9_qid qid; + u32 st_mode; + u32 st_uid; + u32 st_gid; + u64 st_nlink; + u64 st_rdev; + u64 st_size; + u64 st_blksize; + u64 st_blocks; + u64 st_atime_sec; + u64 st_atime_nsec; + u64 st_mtime_sec; + u64 st_mtime_nsec; + u64 st_ctime_sec; + u64 st_ctime_nsec; + u64 st_btime_sec; + u64 st_btime_nsec; + u64 st_gen; + u64 st_data_version; +}; + +#define P9_STATS_MODE 0x00000001ULL +#define P9_STATS_NLINK 0x00000002ULL +#define P9_STATS_UID 0x00000004ULL +#define P9_STATS_GID 0x00000008ULL +#define P9_STATS_RDEV 0x00000010ULL +#define P9_STATS_ATIME 0x00000020ULL +#define P9_STATS_MTIME 0x00000040ULL +#define P9_STATS_CTIME 0x00000080ULL +#define P9_STATS_INO 0x00000100ULL +#define P9_STATS_SIZE 0x00000200ULL +#define P9_STATS_BLOCKS 0x00000400ULL + +#define P9_STATS_BTIME 0x00000800ULL +#define P9_STATS_GEN 0x00001000ULL +#define P9_STATS_DATA_VERSION 0x00002000ULL + +#define P9_STATS_BASIC 0x000007ffULL /* Mask for fields up to BLOCKS */ +#define P9_STATS_ALL 0x00003fffULL /* Mask for All fields above */ + /* Structures for Protocol Operations */ struct p9_tstatfs { u32 fid; diff --git a/include/net/9p/client.h b/include/net/9p/client.h index 2ec93685e6db..6462eec435bc 100644 --- a/include/net/9p/client.h +++ b/include/net/9p/client.h @@ -238,6 +238,9 @@ int p9dirent_read(char *buf, int len, struct p9_dirent *dirent, struct p9_wstat *p9_client_stat(struct p9_fid *fid); int p9_client_wstat(struct p9_fid *fid, struct p9_wstat *wst); +struct p9_stat_dotl *p9_client_getattr_dotl(struct p9_fid *fid, + u64 request_mask); + struct p9_req_t *p9_tag_lookup(struct p9_client *, u16); void p9_client_cb(struct p9_client *c, struct p9_req_t *req); diff --git a/net/9p/client.c b/net/9p/client.c index 4ff068e98f76..5e97118da3bf 100644 --- a/net/9p/client.c +++ b/net/9p/client.c @@ -1303,6 +1303,65 @@ error: } EXPORT_SYMBOL(p9_client_stat); +struct p9_stat_dotl *p9_client_getattr_dotl(struct p9_fid *fid, + u64 request_mask) +{ + int err; + struct p9_client *clnt; + struct p9_stat_dotl *ret = kmalloc(sizeof(struct p9_stat_dotl), + GFP_KERNEL); + struct p9_req_t *req; + + P9_DPRINTK(P9_DEBUG_9P, ">>> TGETATTR fid %d, request_mask %lld\n", + fid->fid, request_mask); + + if (!ret) + return ERR_PTR(-ENOMEM); + + err = 0; + clnt = fid->clnt; + + req = p9_client_rpc(clnt, P9_TGETATTR, "dq", fid->fid, request_mask); + if (IS_ERR(req)) { + err = PTR_ERR(req); + goto error; + } + + err = p9pdu_readf(req->rc, clnt->proto_version, "A", ret); + if (err) { + p9pdu_dump(1, req->rc); + p9_free_req(clnt, req); + goto error; + } + + P9_DPRINTK(P9_DEBUG_9P, + "<<< RGETATTR st_result_mask=%lld\n" + "<<< qid=%x.%llx.%x\n" + "<<< st_mode=%8.8x st_nlink=%llu\n" + "<<< st_uid=%d st_gid=%d\n" + "<<< st_rdev=%llx st_size=%llx st_blksize=%llu st_blocks=%llu\n" + "<<< st_atime_sec=%lld st_atime_nsec=%lld\n" + "<<< st_mtime_sec=%lld st_mtime_nsec=%lld\n" + "<<< st_ctime_sec=%lld st_ctime_nsec=%lld\n" + "<<< st_btime_sec=%lld st_btime_nsec=%lld\n" + "<<< st_gen=%lld st_data_version=%lld", + ret->st_result_mask, ret->qid.type, ret->qid.path, + ret->qid.version, ret->st_mode, ret->st_nlink, ret->st_uid, + ret->st_gid, ret->st_rdev, ret->st_size, ret->st_blksize, + ret->st_blocks, ret->st_atime_sec, ret->st_atime_nsec, + ret->st_mtime_sec, ret->st_mtime_nsec, ret->st_ctime_sec, + ret->st_ctime_nsec, ret->st_btime_sec, ret->st_btime_nsec, + ret->st_gen, ret->st_data_version); + + p9_free_req(clnt, req); + return ret; + +error: + kfree(ret); + return ERR_PTR(err); +} +EXPORT_SYMBOL(p9_client_getattr_dotl); + static int p9_client_statsize(struct p9_wstat *wst, int proto_version) { int ret; diff --git a/net/9p/protocol.c b/net/9p/protocol.c index b645c8263538..3e4f77695891 100644 --- a/net/9p/protocol.c +++ b/net/9p/protocol.c @@ -141,6 +141,7 @@ pdu_write_u(struct p9_fcall *pdu, const char __user *udata, size_t size) D - data blob (int32_t size followed by void *, results are not freed) T - array of strings (int16_t count, followed by strings) R - array of qids (int16_t count, followed by qids) + A - stat for 9p2000.L (p9_stat_dotl) ? - if optional = 1, continue parsing */ @@ -340,6 +341,33 @@ p9pdu_vreadf(struct p9_fcall *pdu, int proto_version, const char *fmt, } } break; + case 'A': { + struct p9_stat_dotl *stbuf = + va_arg(ap, struct p9_stat_dotl *); + + memset(stbuf, 0, sizeof(struct p9_stat_dotl)); + errcode = + p9pdu_readf(pdu, proto_version, + "qQdddqqqqqqqqqqqqqqq", + &stbuf->st_result_mask, + &stbuf->qid, + &stbuf->st_mode, + &stbuf->st_uid, &stbuf->st_gid, + &stbuf->st_nlink, + &stbuf->st_rdev, &stbuf->st_size, + &stbuf->st_blksize, &stbuf->st_blocks, + &stbuf->st_atime_sec, + &stbuf->st_atime_nsec, + &stbuf->st_mtime_sec, + &stbuf->st_mtime_nsec, + &stbuf->st_ctime_sec, + &stbuf->st_ctime_nsec, + &stbuf->st_btime_sec, + &stbuf->st_btime_nsec, + &stbuf->st_gen, + &stbuf->st_data_version); + } + break; case '?': if ((proto_version != p9_proto_2000u) && (proto_version != p9_proto_2000L)) -- cgit v1.2.3 From 87d7845aa0b157a62448dd3e339856f28befe1f4 Mon Sep 17 00:00:00 2001 From: Sripathi Kodi Date: Fri, 18 Jun 2010 11:50:10 +0530 Subject: 9p: Implement client side of setattr for 9P2000.L protocol. SYNOPSIS size[4] Tsetattr tag[2] attr[n] size[4] Rsetattr tag[2] DESCRIPTION The setattr command changes some of the file status information. attr resembles the iattr structure used in Linux kernel. It specifies which status parameter is to be changed and to what value. It is laid out as follows: valid[4] specifies which status information is to be changed. Possible values are: ATTR_MODE (1 << 0) ATTR_UID (1 << 1) ATTR_GID (1 << 2) ATTR_SIZE (1 << 3) ATTR_ATIME (1 << 4) ATTR_MTIME (1 << 5) ATTR_ATIME_SET (1 << 7) ATTR_MTIME_SET (1 << 8) The last two bits represent whether the time information is being sent by the client's user space. In the absense of these bits the server always uses server's time. mode[4] File permission bits uid[4] Owner id of file gid[4] Group id of the file size[8] File size atime_sec[8] Time of last file access, seconds atime_nsec[8] Time of last file access, nanoseconds mtime_sec[8] Time of last file modification, seconds mtime_nsec[8] Time of last file modification, nanoseconds Explanation of the patches: -------------------------- *) The kernel just copies relevent contents of iattr structure to p9_iattr_dotl structure and passes it down to the client. The only check it has is calling inode_change_ok() *) The p9_iattr_dotl structure does not have ctime and ia_file parameters because I don't think these are needed in our case. The client user space can request updating just ctime by calling chown(fd, -1, -1). This is handled on server side without a need for putting ctime on the wire. *) The server currently supports changing mode, time, ownership and size of the file. *) 9P RFC says "Either all the changes in wstat request happen, or none of them does: if the request succeeds, all changes were made; if it fails, none were." I have not done anything to implement this specifically because I don't see a reason. Signed-off-by: Sripathi Kodi Signed-off-by: Venkateswararao Jujjuri Signed-off-by: Eric Van Hensbergen --- fs/9p/vfs_inode.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++--- include/net/9p/9p.h | 28 ++++++++++++++++++++++++++++ include/net/9p/client.h | 1 + net/9p/client.c | 30 ++++++++++++++++++++++++++++++ net/9p/protocol.c | 17 +++++++++++++++++ 5 files changed, 122 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index afcb8d889382..a90324f4546a 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -976,6 +976,49 @@ static int v9fs_vfs_setattr(struct dentry *dentry, struct iattr *iattr) return retval; } +/** + * v9fs_vfs_setattr_dotl - set file metadata + * @dentry: file whose metadata to set + * @iattr: metadata assignment structure + * + */ + +static int v9fs_vfs_setattr_dotl(struct dentry *dentry, struct iattr *iattr) +{ + int retval; + struct v9fs_session_info *v9ses; + struct p9_fid *fid; + struct p9_iattr_dotl p9attr; + + P9_DPRINTK(P9_DEBUG_VFS, "\n"); + + retval = inode_change_ok(dentry->d_inode, iattr); + if (retval) + return retval; + + p9attr.valid = iattr->ia_valid; + p9attr.mode = iattr->ia_mode; + p9attr.uid = iattr->ia_uid; + p9attr.gid = iattr->ia_gid; + p9attr.size = iattr->ia_size; + p9attr.atime_sec = iattr->ia_atime.tv_sec; + p9attr.atime_nsec = iattr->ia_atime.tv_nsec; + p9attr.mtime_sec = iattr->ia_mtime.tv_sec; + p9attr.mtime_nsec = iattr->ia_mtime.tv_nsec; + + retval = -EPERM; + v9ses = v9fs_inode2v9ses(dentry->d_inode); + fid = v9fs_fid_lookup(dentry); + if (IS_ERR(fid)) + return PTR_ERR(fid); + + retval = p9_client_setattr(fid, &p9attr); + if (retval >= 0) + retval = inode_setattr(dentry->d_inode, iattr); + + return retval; +} + /** * v9fs_stat2inode - populate an inode structure with mistat info * @stat: Plan 9 metadata (mistat) structure @@ -1400,7 +1443,7 @@ static const struct inode_operations v9fs_dir_inode_operations_dotl = { .mknod = v9fs_vfs_mknod, .rename = v9fs_vfs_rename, .getattr = v9fs_vfs_getattr_dotl, - .setattr = v9fs_vfs_setattr, + .setattr = v9fs_vfs_setattr_dotl, }; static const struct inode_operations v9fs_dir_inode_operations = { @@ -1422,7 +1465,7 @@ static const struct inode_operations v9fs_file_inode_operations = { static const struct inode_operations v9fs_file_inode_operations_dotl = { .getattr = v9fs_vfs_getattr_dotl, - .setattr = v9fs_vfs_setattr, + .setattr = v9fs_vfs_setattr_dotl, }; static const struct inode_operations v9fs_symlink_inode_operations = { @@ -1438,5 +1481,5 @@ static const struct inode_operations v9fs_symlink_inode_operations_dotl = { .follow_link = v9fs_vfs_follow_link, .put_link = v9fs_vfs_put_link, .getattr = v9fs_vfs_getattr_dotl, - .setattr = v9fs_vfs_setattr, + .setattr = v9fs_vfs_setattr_dotl, }; diff --git a/include/net/9p/9p.h b/include/net/9p/9p.h index ab12e1c9cc7e..7f64d72f6c61 100644 --- a/include/net/9p/9p.h +++ b/include/net/9p/9p.h @@ -135,6 +135,8 @@ enum p9_msg_t { P9_RRENAME, P9_TGETATTR = 24, P9_RGETATTR, + P9_TSETATTR = 26, + P9_RSETATTR, P9_TREADDIR = 40, P9_RREADDIR, P9_TVERSION = 100, @@ -406,6 +408,32 @@ struct p9_stat_dotl { #define P9_STATS_BASIC 0x000007ffULL /* Mask for fields up to BLOCKS */ #define P9_STATS_ALL 0x00003fffULL /* Mask for All fields above */ +/** + * struct p9_iattr_dotl - P9 inode attribute for setattr + * @valid: bitfield specifying which fields are valid + * same as in struct iattr + * @mode: File permission bits + * @uid: user id of owner + * @gid: group id + * @size: File size + * @atime_sec: Last access time, seconds + * @atime_nsec: Last access time, nanoseconds + * @mtime_sec: Last modification time, seconds + * @mtime_nsec: Last modification time, nanoseconds + */ + +struct p9_iattr_dotl { + u32 valid; + u32 mode; + u32 uid; + u32 gid; + u64 size; + u64 atime_sec; + u64 atime_nsec; + u64 mtime_sec; + u64 mtime_nsec; +}; + /* Structures for Protocol Operations */ struct p9_tstatfs { u32 fid; diff --git a/include/net/9p/client.h b/include/net/9p/client.h index 6462eec435bc..afdc385152f6 100644 --- a/include/net/9p/client.h +++ b/include/net/9p/client.h @@ -237,6 +237,7 @@ int p9dirent_read(char *buf, int len, struct p9_dirent *dirent, int proto_version); struct p9_wstat *p9_client_stat(struct p9_fid *fid); int p9_client_wstat(struct p9_fid *fid, struct p9_wstat *wst); +int p9_client_setattr(struct p9_fid *fid, struct p9_iattr_dotl *attr); struct p9_stat_dotl *p9_client_getattr_dotl(struct p9_fid *fid, u64 request_mask); diff --git a/net/9p/client.c b/net/9p/client.c index 5e97118da3bf..b2f70ec889c2 100644 --- a/net/9p/client.c +++ b/net/9p/client.c @@ -1426,6 +1426,36 @@ error: } EXPORT_SYMBOL(p9_client_wstat); +int p9_client_setattr(struct p9_fid *fid, struct p9_iattr_dotl *p9attr) +{ + int err; + struct p9_req_t *req; + struct p9_client *clnt; + + err = 0; + clnt = fid->clnt; + P9_DPRINTK(P9_DEBUG_9P, ">>> TSETATTR fid %d\n", fid->fid); + P9_DPRINTK(P9_DEBUG_9P, + " valid=%x mode=%x uid=%d gid=%d size=%lld\n" + " atime_sec=%lld atime_nsec=%lld\n" + " mtime_sec=%lld mtime_nsec=%lld\n", + p9attr->valid, p9attr->mode, p9attr->uid, p9attr->gid, + p9attr->size, p9attr->atime_sec, p9attr->atime_nsec, + p9attr->mtime_sec, p9attr->mtime_nsec); + + req = p9_client_rpc(clnt, P9_TSETATTR, "dI", fid->fid, p9attr); + + if (IS_ERR(req)) { + err = PTR_ERR(req); + goto error; + } + P9_DPRINTK(P9_DEBUG_9P, "<<< RSETATTR fid %d\n", fid->fid); + p9_free_req(clnt, req); +error: + return err; +} +EXPORT_SYMBOL(p9_client_setattr); + int p9_client_statfs(struct p9_fid *fid, struct p9_rstatfs *sb) { int err; diff --git a/net/9p/protocol.c b/net/9p/protocol.c index 3e4f77695891..3acd3afb20c8 100644 --- a/net/9p/protocol.c +++ b/net/9p/protocol.c @@ -516,6 +516,23 @@ p9pdu_vwritef(struct p9_fcall *pdu, int proto_version, const char *fmt, } } break; + case 'I':{ + struct p9_iattr_dotl *p9attr = va_arg(ap, + struct p9_iattr_dotl *); + + errcode = p9pdu_writef(pdu, proto_version, + "ddddqqqqq", + p9attr->valid, + p9attr->mode, + p9attr->uid, + p9attr->gid, + p9attr->size, + p9attr->atime_sec, + p9attr->atime_nsec, + p9attr->mtime_sec, + p9attr->mtime_nsec); + } + break; case '?': if ((proto_version != p9_proto_2000u) && (proto_version != p9_proto_2000L)) -- cgit v1.2.3 From 09d34ee5f93b2e53b64ffba27bc18731e31154e1 Mon Sep 17 00:00:00 2001 From: Eric Van Hensbergen Date: Mon, 2 Aug 2010 14:28:09 -0500 Subject: 9p: Define and implement TLINK for 9P2000.L This patch adds a helper function to get the dentry from inode and uses it in creating a Hardlink SYNOPSIS size[4] Tlink tag[2] dfid[4] oldfid[4] newpath[s] size[4] Rlink tag[2] DESCRIPTION Create a link 'newpath' in directory pointed by dfid linking to oldfid path. [sripathik@in.ibm.com : p9_client_link should not free req structure if p9_client_rpc has returned an error.] Signed-off-by: Venkateswararao Jujjuri Signed-off-by: Eric Van Hensbergen --- fs/9p/vfs_inode.c | 107 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 106 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index a90324f4546a..e6ece237241f 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -235,6 +235,41 @@ void v9fs_destroy_inode(struct inode *inode) } #endif +/** + * v9fs_get_fsgid_for_create - Helper function to get the gid for creating a + * new file system object. This checks the S_ISGID to determine the owning + * group of the new file system object. + */ + +static gid_t v9fs_get_fsgid_for_create(struct inode *dir_inode) +{ + BUG_ON(dir_inode == NULL); + + if (dir_inode->i_mode & S_ISGID) { + /* set_gid bit is set.*/ + return dir_inode->i_gid; + } + return current_fsgid(); +} + +/** + * v9fs_dentry_from_dir_inode - helper function to get the dentry from + * dir inode. + * + */ + +struct dentry *v9fs_dentry_from_dir_inode(struct inode *inode) +{ + struct dentry *dentry; + + spin_lock(&dcache_lock); + /* Directory should have only one entry. */ + BUG_ON(S_ISDIR(inode->i_mode) && !list_is_singular(&inode->i_dentry)); + dentry = list_entry(inode->i_dentry.next, struct dentry, d_alias); + spin_unlock(&dcache_lock); + return dentry; +} + /** * v9fs_get_inode - helper function to setup an inode * @sb: superblock @@ -1373,6 +1408,76 @@ clunk_fid: return retval; } +/** + * v9fs_vfs_link_dotl - create a hardlink for dotl + * @old_dentry: dentry for file to link to + * @dir: inode destination for new link + * @dentry: dentry for link + * + */ + +static int +v9fs_vfs_link_dotl(struct dentry *old_dentry, struct inode *dir, + struct dentry *dentry) +{ + int err; + struct p9_fid *dfid, *oldfid; + char *name; + struct v9fs_session_info *v9ses; + struct dentry *dir_dentry; + + P9_DPRINTK(P9_DEBUG_VFS, "dir ino: %lu, old_name: %s, new_name: %s\n", + dir->i_ino, old_dentry->d_name.name, + dentry->d_name.name); + + v9ses = v9fs_inode2v9ses(dir); + dir_dentry = v9fs_dentry_from_dir_inode(dir); + dfid = v9fs_fid_lookup(dir_dentry); + if (IS_ERR(dfid)) + return PTR_ERR(dfid); + + oldfid = v9fs_fid_lookup(old_dentry); + if (IS_ERR(oldfid)) + return PTR_ERR(oldfid); + + name = (char *) dentry->d_name.name; + + err = p9_client_link(dfid, oldfid, (char *)dentry->d_name.name); + + if (err < 0) { + P9_DPRINTK(P9_DEBUG_VFS, "p9_client_link failed %d\n", err); + return err; + } + + if (v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE) { + /* Get the latest stat info from server. */ + struct p9_fid *fid; + struct p9_stat_dotl *st; + + fid = v9fs_fid_lookup(old_dentry); + if (IS_ERR(fid)) + return PTR_ERR(fid); + + st = p9_client_getattr_dotl(fid, P9_STATS_BASIC); + if (IS_ERR(st)) + return PTR_ERR(st); + + v9fs_stat2inode_dotl(st, old_dentry->d_inode); + + kfree(st); + } else { + /* Caching disabled. No need to get upto date stat info. + * This dentry will be released immediately. So, just i_count++ + */ + atomic_inc(&old_dentry->d_inode->i_count); + } + + dentry->d_op = old_dentry->d_op; + d_instantiate(dentry, old_dentry->d_inode); + + return err; +} + /** * v9fs_vfs_mknod - create a special file * @dir: inode destination for new link @@ -1422,7 +1527,7 @@ static const struct inode_operations v9fs_dir_inode_operations_dotu = { .create = v9fs_vfs_create, .lookup = v9fs_vfs_lookup, .symlink = v9fs_vfs_symlink, - .link = v9fs_vfs_link, + .link = v9fs_vfs_link_dotl, .unlink = v9fs_vfs_unlink, .mkdir = v9fs_vfs_mkdir, .rmdir = v9fs_vfs_rmdir, -- cgit v1.2.3 From 50cc42ff3d7bc48a436c5a0413459ca7841b505f Mon Sep 17 00:00:00 2001 From: "Venkateswararao Jujjuri (JV)" Date: Wed, 9 Jun 2010 15:59:31 -0700 Subject: 9p: Define and implement TSYMLINK for 9P2000.L Create a symbolic link SYNOPSIS size[4] Tsymlink tag[2] fid[4] name[s] symtgt[s] gid[4] size[4] Rsymlink tag[2] qid[13] DESCRIPTION Create a symbolic link named 'name' pointing to 'symtgt'. gid represents the effective group id of the caller. The permissions of a symbolic link are irrelevant hence it is omitted from the protocol. Signed-off-by: Venkateswararao Jujjuri Reviewed-by: Sripathi Kodi Signed-off-by: Eric Van Hensbergen --- fs/9p/vfs_inode.c | 101 ++++++++++++++++++++++++++++++++++++++++++++++-- include/net/9p/9p.h | 4 ++ include/net/9p/client.h | 2 + net/9p/client.c | 34 ++++++++++++++++ 4 files changed, 137 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index e6ece237241f..a7319364544b 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -1245,7 +1245,7 @@ static int v9fs_readlink(struct dentry *dentry, char *buffer, int buflen) if (IS_ERR(fid)) return PTR_ERR(fid); - if (!v9fs_proto_dotu(v9ses)) + if (!v9fs_proto_dotu(v9ses) && !v9fs_proto_dotl(v9ses)) return -EBADF; st = p9_client_stat(fid); @@ -1350,6 +1350,99 @@ static int v9fs_vfs_mkspecial(struct inode *dir, struct dentry *dentry, return 0; } +/** + * v9fs_vfs_symlink_dotl - helper function to create symlinks + * @dir: directory inode containing symlink + * @dentry: dentry for symlink + * @symname: symlink data + * + * See Also: 9P2000.L RFC for more information + * + */ + +static int +v9fs_vfs_symlink_dotl(struct inode *dir, struct dentry *dentry, + const char *symname) +{ + struct v9fs_session_info *v9ses; + struct p9_fid *dfid; + struct p9_fid *fid = NULL; + struct inode *inode; + struct p9_qid qid; + char *name; + int err; + gid_t gid; + + name = (char *) dentry->d_name.name; + P9_DPRINTK(P9_DEBUG_VFS, "v9fs_vfs_symlink_dotl : %lu,%s,%s\n", + dir->i_ino, name, symname); + v9ses = v9fs_inode2v9ses(dir); + + dfid = v9fs_fid_lookup(dentry->d_parent); + if (IS_ERR(dfid)) { + err = PTR_ERR(dfid); + P9_DPRINTK(P9_DEBUG_VFS, "fid lookup failed %d\n", err); + return err; + } + + gid = v9fs_get_fsgid_for_create(dir); + + if (gid < 0) { + P9_DPRINTK(P9_DEBUG_VFS, "v9fs_get_egid failed %d\n", gid); + goto error; + } + + /* Server doesn't alter fid on TSYMLINK. Hence no need to clone it. */ + err = p9_client_symlink(dfid, name, (char *)symname, gid, &qid); + + if (err < 0) { + P9_DPRINTK(P9_DEBUG_VFS, "p9_client_symlink failed %d\n", err); + goto error; + } + + if (v9ses->cache) { + /* Now walk from the parent so we can get an unopened fid. */ + fid = p9_client_walk(dfid, 1, &name, 1); + if (IS_ERR(fid)) { + err = PTR_ERR(fid); + P9_DPRINTK(P9_DEBUG_VFS, "p9_client_walk failed %d\n", + err); + fid = NULL; + goto error; + } + + /* instantiate inode and assign the unopened fid to dentry */ + inode = v9fs_inode_from_fid(v9ses, fid, dir->i_sb); + if (IS_ERR(inode)) { + err = PTR_ERR(inode); + P9_DPRINTK(P9_DEBUG_VFS, "inode creation failed %d\n", + err); + goto error; + } + dentry->d_op = &v9fs_cached_dentry_operations; + d_instantiate(dentry, inode); + err = v9fs_fid_add(dentry, fid); + if (err < 0) + goto error; + fid = NULL; + } else { + /* Not in cached mode. No need to populate inode with stat */ + inode = v9fs_get_inode(dir->i_sb, S_IFLNK); + if (IS_ERR(inode)) { + err = PTR_ERR(inode); + goto error; + } + dentry->d_op = &v9fs_dentry_operations; + d_instantiate(dentry, inode); + } + +error: + if (fid) + p9_client_clunk(fid); + + return err; +} + /** * v9fs_vfs_symlink - helper function to create symlinks * @dir: directory inode containing symlink @@ -1527,7 +1620,7 @@ static const struct inode_operations v9fs_dir_inode_operations_dotu = { .create = v9fs_vfs_create, .lookup = v9fs_vfs_lookup, .symlink = v9fs_vfs_symlink, - .link = v9fs_vfs_link_dotl, + .link = v9fs_vfs_link, .unlink = v9fs_vfs_unlink, .mkdir = v9fs_vfs_mkdir, .rmdir = v9fs_vfs_rmdir, @@ -1540,8 +1633,8 @@ static const struct inode_operations v9fs_dir_inode_operations_dotu = { static const struct inode_operations v9fs_dir_inode_operations_dotl = { .create = v9fs_vfs_create, .lookup = v9fs_vfs_lookup, - .symlink = v9fs_vfs_symlink, - .link = v9fs_vfs_link, + .link = v9fs_vfs_link_dotl, + .symlink = v9fs_vfs_symlink_dotl, .unlink = v9fs_vfs_unlink, .mkdir = v9fs_vfs_mkdir, .rmdir = v9fs_vfs_rmdir, diff --git a/include/net/9p/9p.h b/include/net/9p/9p.h index 5985c0f83db3..44a6883d7144 100644 --- a/include/net/9p/9p.h +++ b/include/net/9p/9p.h @@ -88,6 +88,8 @@ do { \ * enum p9_msg_t - 9P message types * @P9_TSTATFS: file system status request * @P9_RSTATFS: file system status response + * @P9_TSYMLINK: make symlink request + * @P9_RSYMLINK: make symlink response * @P9_TRENAME: rename request * @P9_RRENAME: rename response * @P9_TVERSION: version handshake request @@ -131,6 +133,8 @@ do { \ enum p9_msg_t { P9_TSTATFS = 8, P9_RSTATFS, + P9_TSYMLINK = 16, + P9_RSYMLINK, P9_TRENAME = 20, P9_RRENAME, P9_TGETATTR = 24, diff --git a/include/net/9p/client.h b/include/net/9p/client.h index e36f11650e99..2e039730920e 100644 --- a/include/net/9p/client.h +++ b/include/net/9p/client.h @@ -227,6 +227,8 @@ int p9_client_open(struct p9_fid *fid, int mode); int p9_client_fcreate(struct p9_fid *fid, char *name, u32 perm, int mode, char *extension); int p9_client_link(struct p9_fid *fid, struct p9_fid *oldfid, char *newname); +int p9_client_symlink(struct p9_fid *fid, char *name, char *symname, gid_t gid, + struct p9_qid *qid); int p9_client_clunk(struct p9_fid *fid); int p9_client_remove(struct p9_fid *fid); int p9_client_read(struct p9_fid *fid, char *data, char __user *udata, diff --git a/net/9p/client.c b/net/9p/client.c index ad1c4489ab4d..e37e64cb9394 100644 --- a/net/9p/client.c +++ b/net/9p/client.c @@ -1095,6 +1095,40 @@ error: } EXPORT_SYMBOL(p9_client_fcreate); +int p9_client_symlink(struct p9_fid *dfid, char *name, char *symtgt, gid_t gid, + struct p9_qid *qid) +{ + int err = 0; + struct p9_client *clnt; + struct p9_req_t *req; + + P9_DPRINTK(P9_DEBUG_9P, ">>> TSYMLINK dfid %d name %s symtgt %s\n", + dfid->fid, name, symtgt); + clnt = dfid->clnt; + + req = p9_client_rpc(clnt, P9_TSYMLINK, "dssd", dfid->fid, name, symtgt, + gid); + if (IS_ERR(req)) { + err = PTR_ERR(req); + goto error; + } + + err = p9pdu_readf(req->rc, clnt->proto_version, "Q", qid); + if (err) { + p9pdu_dump(1, req->rc); + goto free_and_error; + } + + P9_DPRINTK(P9_DEBUG_9P, "<<< RSYMLINK qid %x.%llx.%x\n", + qid->type, (unsigned long long)qid->path, qid->version); + +free_and_error: + p9_free_req(clnt, req); +error: + return err; +} +EXPORT_SYMBOL(p9_client_symlink); + int p9_client_link(struct p9_fid *dfid, struct p9_fid *oldfid, char *newname) { struct p9_client *clnt; -- cgit v1.2.3 From 4b43516ab19b748b48322937fd9307af17541c4d Mon Sep 17 00:00:00 2001 From: "M. Mohan Kumar" Date: Wed, 16 Jun 2010 14:27:01 +0530 Subject: 9p: Implement TMKNOD Synopsis size[4] Tmknod tag[2] fid[4] name[s] mode[4] major[4] minor[4] gid[4] size[4] Rmknod tag[2] qid[13] Description mknod asks the file server to create a device node with given major and minor number, mode and gid. The qid for the new device node is returned with the mknod reply message. [sripathik@in.ibm.com: Fix error handling code] Signed-off-by: M. Mohan Kumar Signed-off-by: Venkateswararao Jujjuri Signed-off-by: Eric Van Hensbergen --- fs/9p/vfs_inode.c | 106 ++++++++++++++++++++++++++++++++++++++++++++++-- include/net/9p/9p.h | 4 ++ include/net/9p/client.h | 2 + net/9p/client.c | 31 ++++++++++++++ 4 files changed, 140 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index a7319364544b..4d9f45ec6126 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -302,7 +302,13 @@ struct inode *v9fs_get_inode(struct super_block *sb, int mode) case S_IFBLK: case S_IFCHR: case S_IFSOCK: - if (!v9fs_proto_dotu(v9ses)) { + if (v9fs_proto_dotl(v9ses)) { + inode->i_op = &v9fs_file_inode_operations_dotl; + inode->i_fop = &v9fs_file_operations_dotl; + } else if (v9fs_proto_dotu(v9ses)) { + inode->i_op = &v9fs_file_inode_operations; + inode->i_fop = &v9fs_file_operations; + } else { P9_DPRINTK(P9_DEBUG_ERROR, "special files without extended mode\n"); err = -EINVAL; @@ -1616,6 +1622,100 @@ v9fs_vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev) return retval; } +/** + * v9fs_vfs_mknod_dotl - create a special file + * @dir: inode destination for new link + * @dentry: dentry for file + * @mode: mode for creation + * @rdev: device associated with special file + * + */ +static int +v9fs_vfs_mknod_dotl(struct inode *dir, struct dentry *dentry, int mode, + dev_t rdev) +{ + int err; + char *name; + struct v9fs_session_info *v9ses; + struct p9_fid *fid = NULL, *dfid = NULL; + struct inode *inode; + gid_t gid; + struct p9_qid qid; + struct dentry *dir_entry; + + P9_DPRINTK(P9_DEBUG_VFS, + " %lu,%s mode: %x MAJOR: %u MINOR: %u\n", dir->i_ino, + dentry->d_name.name, mode, MAJOR(rdev), MINOR(rdev)); + + if (!new_valid_dev(rdev)) + return -EINVAL; + + v9ses = v9fs_inode2v9ses(dir); + dir_dentry = v9fs_dentry_from_dir_inode(dir); + dfid = v9fs_fid_lookup(dir_entry); + if (IS_ERR(dfid)) { + err = PTR_ERR(dfid); + P9_DPRINTK(P9_DEBUG_VFS, "fid lookup failed %d\n", err); + dfid = NULL; + goto error; + } + + gid = v9fs_get_fsgid_for_create(dir); + if (gid < 0) { + P9_DPRINTK(P9_DEBUG_VFS, "v9fs_get_fsgid_for_create failed\n"); + goto error; + } + + name = (char *) dentry->d_name.name; + + err = p9_client_mknod_dotl(dfid, name, mode, rdev, gid, &qid); + if (err < 0) + goto error; + + /* instantiate inode and assign the unopened fid to the dentry */ + if (v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE) { + fid = p9_client_walk(dfid, 1, &name, 1); + if (IS_ERR(fid)) { + err = PTR_ERR(fid); + P9_DPRINTK(P9_DEBUG_VFS, "p9_client_walk failed %d\n", + err); + fid = NULL; + goto error; + } + + inode = v9fs_inode_from_fid(v9ses, fid, dir->i_sb); + if (IS_ERR(inode)) { + err = PTR_ERR(inode); + P9_DPRINTK(P9_DEBUG_VFS, "inode creation failed %d\n", + err); + goto error; + } + dentry->d_op = &v9fs_cached_dentry_operations; + d_instantiate(dentry, inode); + err = v9fs_fid_add(dentry, fid); + if (err < 0) + goto error; + fid = NULL; + } else { + /* + * Not in cached mode. No need to populate inode with stat. + * socket syscall returns a fd, so we need instantiate + */ + inode = v9fs_get_inode(dir->i_sb, mode); + if (IS_ERR(inode)) { + err = PTR_ERR(inode); + goto error; + } + dentry->d_op = &v9fs_dentry_operations; + d_instantiate(dentry, inode); + } + +error: + if (fid) + p9_client_clunk(fid); + return err; +} + static const struct inode_operations v9fs_dir_inode_operations_dotu = { .create = v9fs_vfs_create, .lookup = v9fs_vfs_lookup, @@ -1624,7 +1724,7 @@ static const struct inode_operations v9fs_dir_inode_operations_dotu = { .unlink = v9fs_vfs_unlink, .mkdir = v9fs_vfs_mkdir, .rmdir = v9fs_vfs_rmdir, - .mknod = v9fs_vfs_mknod, + .mknod = v9fs_vfs_mknod_dotl, .rename = v9fs_vfs_rename, .getattr = v9fs_vfs_getattr, .setattr = v9fs_vfs_setattr, @@ -1638,7 +1738,7 @@ static const struct inode_operations v9fs_dir_inode_operations_dotl = { .unlink = v9fs_vfs_unlink, .mkdir = v9fs_vfs_mkdir, .rmdir = v9fs_vfs_rmdir, - .mknod = v9fs_vfs_mknod, + .mknod = v9fs_vfs_mknod_dotl, .rename = v9fs_vfs_rename, .getattr = v9fs_vfs_getattr_dotl, .setattr = v9fs_vfs_setattr_dotl, diff --git a/include/net/9p/9p.h b/include/net/9p/9p.h index 44a6883d7144..ff32091d8063 100644 --- a/include/net/9p/9p.h +++ b/include/net/9p/9p.h @@ -90,6 +90,8 @@ do { \ * @P9_RSTATFS: file system status response * @P9_TSYMLINK: make symlink request * @P9_RSYMLINK: make symlink response + * @P9_TMKNOD: create a special file object request + * @P9_RMKNOD: create a special file object response * @P9_TRENAME: rename request * @P9_RRENAME: rename response * @P9_TVERSION: version handshake request @@ -135,6 +137,8 @@ enum p9_msg_t { P9_RSTATFS, P9_TSYMLINK = 16, P9_RSYMLINK, + P9_TMKNOD = 18, + P9_RMKNOD, P9_TRENAME = 20, P9_RRENAME, P9_TGETATTR = 24, diff --git a/include/net/9p/client.h b/include/net/9p/client.h index 2e039730920e..6e70358c71d9 100644 --- a/include/net/9p/client.h +++ b/include/net/9p/client.h @@ -245,6 +245,8 @@ int p9_client_setattr(struct p9_fid *fid, struct p9_iattr_dotl *attr); struct p9_stat_dotl *p9_client_getattr_dotl(struct p9_fid *fid, u64 request_mask); +int p9_client_mknod_dotl(struct p9_fid *oldfid, char *name, int mode, + dev_t rdev, gid_t gid, struct p9_qid *); struct p9_req_t *p9_tag_lookup(struct p9_client *, u16); void p9_client_cb(struct p9_client *c, struct p9_req_t *req); diff --git a/net/9p/client.c b/net/9p/client.c index e37e64cb9394..cdfbd6740796 100644 --- a/net/9p/client.c +++ b/net/9p/client.c @@ -1622,3 +1622,34 @@ error: return err; } EXPORT_SYMBOL(p9_client_readdir); + +int p9_client_mknod_dotl(struct p9_fid *fid, char *name, int mode, + dev_t rdev, gid_t gid, struct p9_qid *qid) +{ + int err; + struct p9_client *clnt; + struct p9_req_t *req; + + err = 0; + clnt = fid->clnt; + P9_DPRINTK(P9_DEBUG_9P, ">>> TMKNOD fid %d name %s mode %d major %d " + "minor %d\n", fid->fid, name, mode, MAJOR(rdev), MINOR(rdev)); + req = p9_client_rpc(clnt, P9_TMKNOD, "dsdddd", fid->fid, name, mode, + MAJOR(rdev), MINOR(rdev), gid); + if (IS_ERR(req)) + return PTR_ERR(req); + + err = p9pdu_readf(req->rc, clnt->proto_version, "Q", qid); + if (err) { + p9pdu_dump(1, req->rc); + goto error; + } + P9_DPRINTK(P9_DEBUG_9P, "<<< RMKNOD qid %x.%llx.%x\n", qid->type, + (unsigned long long)qid->path, qid->version); + +error: + p9_free_req(clnt, req); + return err; + +} +EXPORT_SYMBOL(p9_client_mknod_dotl); -- cgit v1.2.3 From 01a622bd7409bb7af38e784cff814e5e723f7951 Mon Sep 17 00:00:00 2001 From: "M. Mohan Kumar" Date: Wed, 16 Jun 2010 14:27:22 +0530 Subject: 9p: Implement TMKDIR Implement TMKDIR as part of 2000.L Work Synopsis size[4] Tmkdir tag[2] fid[4] name[s] mode[4] gid[4] size[4] Rmkdir tag[2] qid[13] Description mkdir asks the file server to create a directory with given name, mode and gid. The qid for the new directory is returned with the mkdir reply message. Note: 72 is selected as the opcode for TMKDIR from the reserved list. Signed-off-by: M. Mohan Kumar Signed-off-by: Venkateswararao Jujjuri Signed-off-by: Eric Van Hensbergen --- fs/9p/vfs_inode.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++-- include/net/9p/9p.h | 4 +++ include/net/9p/client.h | 2 ++ net/9p/client.c | 31 ++++++++++++++++++ 4 files changed, 117 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index 4d9f45ec6126..39dc79567322 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -731,6 +731,83 @@ static int v9fs_vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) return err; } + +/** + * v9fs_vfs_mkdir_dotl - VFS mkdir hook to create a directory + * @dir: inode that is being unlinked + * @dentry: dentry that is being unlinked + * @mode: mode for new directory + * + */ + +static int v9fs_vfs_mkdir_dotl(struct inode *dir, struct dentry *dentry, + int mode) +{ + int err; + struct v9fs_session_info *v9ses; + struct p9_fid *fid = NULL, *dfid = NULL; + gid_t gid; + char *name; + struct inode *inode; + struct p9_qid qid; + struct dentry *dir_dentry; + + P9_DPRINTK(P9_DEBUG_VFS, "name %s\n", dentry->d_name.name); + err = 0; + v9ses = v9fs_inode2v9ses(dir); + + mode |= S_IFDIR; + dir_dentry = v9fs_dentry_from_dir_inode(dir); + dfid = v9fs_fid_lookup(dir_dentry); + if (IS_ERR(dfid)) { + err = PTR_ERR(dfid); + P9_DPRINTK(P9_DEBUG_VFS, "fid lookup failed %d\n", err); + dfid = NULL; + goto error; + } + + gid = v9fs_get_fsgid_for_create(dir); + if (gid < 0) { + P9_DPRINTK(P9_DEBUG_VFS, "v9fs_get_fsgid_for_create failed\n"); + goto error; + } + + name = (char *) dentry->d_name.name; + err = p9_client_mkdir_dotl(dfid, name, mode, gid, &qid); + if (err < 0) + goto error; + + /* instantiate inode and assign the unopened fid to the dentry */ + if (v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE) { + fid = p9_client_walk(dfid, 1, &name, 1); + if (IS_ERR(fid)) { + err = PTR_ERR(fid); + P9_DPRINTK(P9_DEBUG_VFS, "p9_client_walk failed %d\n", + err); + fid = NULL; + goto error; + } + + inode = v9fs_inode_from_fid(v9ses, fid, dir->i_sb); + if (IS_ERR(inode)) { + err = PTR_ERR(inode); + P9_DPRINTK(P9_DEBUG_VFS, "inode creation failed %d\n", + err); + goto error; + } + dentry->d_op = &v9fs_cached_dentry_operations; + d_instantiate(dentry, inode); + err = v9fs_fid_add(dentry, fid); + if (err < 0) + goto error; + fid = NULL; + } +error: + if (fid) + p9_client_clunk(fid); + return err; +} + /** * v9fs_vfs_lookup - VFS lookup hook to "walk" to a new inode * @dir: inode that is being walked from @@ -1641,7 +1718,7 @@ v9fs_vfs_mknod_dotl(struct inode *dir, struct dentry *dentry, int mode, struct inode *inode; gid_t gid; struct p9_qid qid; - struct dentry *dir_entry; + struct dentry *dir_dentry; P9_DPRINTK(P9_DEBUG_VFS, " %lu,%s mode: %x MAJOR: %u MINOR: %u\n", dir->i_ino, @@ -1652,7 +1729,7 @@ v9fs_vfs_mknod_dotl(struct inode *dir, struct dentry *dentry, int mode, v9ses = v9fs_inode2v9ses(dir); dir_dentry = v9fs_dentry_from_dir_inode(dir); - dfid = v9fs_fid_lookup(dir_entry); + dfid = v9fs_fid_lookup(dir_dentry); if (IS_ERR(dfid)) { err = PTR_ERR(dfid); P9_DPRINTK(P9_DEBUG_VFS, "fid lookup failed %d\n", err); @@ -1736,7 +1813,7 @@ static const struct inode_operations v9fs_dir_inode_operations_dotl = { .link = v9fs_vfs_link_dotl, .symlink = v9fs_vfs_symlink_dotl, .unlink = v9fs_vfs_unlink, - .mkdir = v9fs_vfs_mkdir, + .mkdir = v9fs_vfs_mkdir_dotl, .rmdir = v9fs_vfs_rmdir, .mknod = v9fs_vfs_mknod_dotl, .rename = v9fs_vfs_rename, diff --git a/include/net/9p/9p.h b/include/net/9p/9p.h index ff32091d8063..091b471d8f05 100644 --- a/include/net/9p/9p.h +++ b/include/net/9p/9p.h @@ -94,6 +94,8 @@ do { \ * @P9_RMKNOD: create a special file object response * @P9_TRENAME: rename request * @P9_RRENAME: rename response + * @P9_TMKDIR: create a directory request + * @P9_RMKDIR: create a directory response * @P9_TVERSION: version handshake request * @P9_RVERSION: version handshake response * @P9_TAUTH: request to establish authentication channel @@ -149,6 +151,8 @@ enum p9_msg_t { P9_RREADDIR, P9_TLINK = 70, P9_RLINK, + P9_TMKDIR = 72, + P9_RMKDIR, P9_TVERSION = 100, P9_RVERSION, P9_TAUTH = 102, diff --git a/include/net/9p/client.h b/include/net/9p/client.h index 6e70358c71d9..55d913a9b797 100644 --- a/include/net/9p/client.h +++ b/include/net/9p/client.h @@ -247,6 +247,8 @@ struct p9_stat_dotl *p9_client_getattr_dotl(struct p9_fid *fid, int p9_client_mknod_dotl(struct p9_fid *oldfid, char *name, int mode, dev_t rdev, gid_t gid, struct p9_qid *); +int p9_client_mkdir_dotl(struct p9_fid *fid, char *name, int mode, + gid_t gid, struct p9_qid *); struct p9_req_t *p9_tag_lookup(struct p9_client *, u16); void p9_client_cb(struct p9_client *c, struct p9_req_t *req); diff --git a/net/9p/client.c b/net/9p/client.c index cdfbd6740796..a3bdd341f2ac 100644 --- a/net/9p/client.c +++ b/net/9p/client.c @@ -1653,3 +1653,34 @@ error: } EXPORT_SYMBOL(p9_client_mknod_dotl); + +int p9_client_mkdir_dotl(struct p9_fid *fid, char *name, int mode, + gid_t gid, struct p9_qid *qid) +{ + int err; + struct p9_client *clnt; + struct p9_req_t *req; + + err = 0; + clnt = fid->clnt; + P9_DPRINTK(P9_DEBUG_9P, ">>> TMKDIR fid %d name %s mode %d gid %d\n", + fid->fid, name, mode, gid); + req = p9_client_rpc(clnt, P9_TMKDIR, "dsdd", fid->fid, name, mode, + gid); + if (IS_ERR(req)) + return PTR_ERR(req); + + err = p9pdu_readf(req->rc, clnt->proto_version, "Q", qid); + if (err) { + p9pdu_dump(1, req->rc); + goto error; + } + P9_DPRINTK(P9_DEBUG_9P, "<<< RMKDIR qid %x.%llx.%x\n", qid->type, + (unsigned long long)qid->path, qid->version); + +error: + p9_free_req(clnt, req); + return err; + +} +EXPORT_SYMBOL(p9_client_mkdir_dotl); -- cgit v1.2.3 From 5643135a28464e7c19d8d23a9e0804697a62c84b Mon Sep 17 00:00:00 2001 From: "Venkateswararao Jujjuri (JV)" Date: Thu, 17 Jun 2010 18:27:46 -0700 Subject: fs/9p: This patch implements TLCREATE for 9p2000.L protocol. SYNOPSIS size[4] Tlcreate tag[2] fid[4] name[s] flags[4] mode[4] gid[4] size[4] Rlcreate tag[2] qid[13] iounit[4] DESCRIPTION The Tlreate request asks the file server to create a new regular file with the name supplied, in the directory (dir) represented by fid. The mode argument specifies the permissions to use. New file is created with the uid if the fid and with supplied gid. The flags argument represent Linux access mode flags with which the caller is requesting to open the file with. Protocol allows all the Linux access modes but it is upto the server to allow/disallow any of these acess modes. If the server doesn't support any of the access mode, it is expected to return error. Signed-off-by: Venkateswararao Jujjuri Signed-off-by: Eric Van Hensbergen --- fs/9p/vfs_inode.c | 114 +++++++++++++++++++++++++++++++++++++++++++++++- include/net/9p/9p.h | 4 ++ include/net/9p/client.h | 2 + net/9p/client.c | 44 +++++++++++++++++++ 4 files changed, 163 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index 39dc79567322..2ac245902a4f 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -641,6 +641,118 @@ error: return ERR_PTR(err); } +/** + * v9fs_vfs_create_dotl - VFS hook to create files for 9P2000.L protocol. + * @dir: directory inode that is being created + * @dentry: dentry that is being deleted + * @mode: create permissions + * @nd: path information + * + */ + +static int +v9fs_vfs_create_dotl(struct inode *dir, struct dentry *dentry, int mode, + struct nameidata *nd) +{ + int err = 0; + char *name = NULL; + gid_t gid; + int flags; + struct v9fs_session_info *v9ses; + struct p9_fid *fid = NULL; + struct p9_fid *dfid, *ofid; + struct file *filp; + struct p9_qid qid; + struct inode *inode; + + v9ses = v9fs_inode2v9ses(dir); + if (nd && nd->flags & LOOKUP_OPEN) + flags = nd->intent.open.flags - 1; + else + flags = O_RDWR; + + name = (char *) dentry->d_name.name; + P9_DPRINTK(P9_DEBUG_VFS, "v9fs_vfs_create_dotl: name:%s flags:0x%x " + "mode:0x%x\n", name, flags, mode); + + dfid = v9fs_fid_lookup(dentry->d_parent); + if (IS_ERR(dfid)) { + err = PTR_ERR(dfid); + P9_DPRINTK(P9_DEBUG_VFS, "fid lookup failed %d\n", err); + return err; + } + + /* clone a fid to use for creation */ + ofid = p9_client_walk(dfid, 0, NULL, 1); + if (IS_ERR(ofid)) { + err = PTR_ERR(ofid); + P9_DPRINTK(P9_DEBUG_VFS, "p9_client_walk failed %d\n", err); + return err; + } + + gid = v9fs_get_fsgid_for_create(dir); + err = p9_client_create_dotl(ofid, name, flags, mode, gid, &qid); + if (err < 0) { + P9_DPRINTK(P9_DEBUG_VFS, + "p9_client_open_dotl failed in creat %d\n", + err); + goto error; + } + + /* No need to populate the inode if we are not opening the file AND + * not in cached mode. + */ + if (!v9ses->cache && !(nd && nd->flags & LOOKUP_OPEN)) { + /* Not in cached mode. No need to populate inode with stat */ + dentry->d_op = &v9fs_dentry_operations; + p9_client_clunk(ofid); + d_instantiate(dentry, NULL); + return 0; + } + + /* Now walk from the parent so we can get an unopened fid. */ + fid = p9_client_walk(dfid, 1, &name, 1); + if (IS_ERR(fid)) { + err = PTR_ERR(fid); + P9_DPRINTK(P9_DEBUG_VFS, "p9_client_walk failed %d\n", err); + fid = NULL; + goto error; + } + + /* instantiate inode and assign the unopened fid to dentry */ + inode = v9fs_inode_from_fid(v9ses, fid, dir->i_sb); + if (IS_ERR(inode)) { + err = PTR_ERR(inode); + P9_DPRINTK(P9_DEBUG_VFS, "inode creation failed %d\n", err); + goto error; + } + dentry->d_op = &v9fs_cached_dentry_operations; + d_instantiate(dentry, inode); + err = v9fs_fid_add(dentry, fid); + if (err < 0) + goto error; + + /* if we are opening a file, assign the open fid to the file */ + if (nd && nd->flags & LOOKUP_OPEN) { + filp = lookup_instantiate_filp(nd, dentry, v9fs_open_created); + if (IS_ERR(filp)) { + p9_client_clunk(ofid); + return PTR_ERR(filp); + } + filp->private_data = ofid; + } else + p9_client_clunk(ofid); + + return 0; + +error: + if (ofid) + p9_client_clunk(ofid); + if (fid) + p9_client_clunk(fid); + return err; +} + /** * v9fs_vfs_create - VFS hook to create files * @dir: directory inode that is being created @@ -1808,7 +1920,7 @@ static const struct inode_operations v9fs_dir_inode_operations_dotu = { }; static const struct inode_operations v9fs_dir_inode_operations_dotl = { - .create = v9fs_vfs_create, + .create = v9fs_vfs_create_dotl, .lookup = v9fs_vfs_lookup, .link = v9fs_vfs_link_dotl, .symlink = v9fs_vfs_symlink_dotl, diff --git a/include/net/9p/9p.h b/include/net/9p/9p.h index 091b471d8f05..06d111d61038 100644 --- a/include/net/9p/9p.h +++ b/include/net/9p/9p.h @@ -92,6 +92,8 @@ do { \ * @P9_RSYMLINK: make symlink response * @P9_TMKNOD: create a special file object request * @P9_RMKNOD: create a special file object response + * @P9_TLCREATE: prepare a handle for I/O on an new file for 9P2000.L + * @P9_RLCREATE: response with file access information for 9P2000.L * @P9_TRENAME: rename request * @P9_RRENAME: rename response * @P9_TMKDIR: create a directory request @@ -137,6 +139,8 @@ do { \ enum p9_msg_t { P9_TSTATFS = 8, P9_RSTATFS, + P9_TLCREATE = 14, + P9_RLCREATE, P9_TSYMLINK = 16, P9_RSYMLINK, P9_TMKNOD = 18, diff --git a/include/net/9p/client.h b/include/net/9p/client.h index 55d913a9b797..d755c0ed6750 100644 --- a/include/net/9p/client.h +++ b/include/net/9p/client.h @@ -229,6 +229,8 @@ int p9_client_fcreate(struct p9_fid *fid, char *name, u32 perm, int mode, int p9_client_link(struct p9_fid *fid, struct p9_fid *oldfid, char *newname); int p9_client_symlink(struct p9_fid *fid, char *name, char *symname, gid_t gid, struct p9_qid *qid); +int p9_client_create_dotl(struct p9_fid *ofid, char *name, u32 flags, u32 mode, + gid_t gid, struct p9_qid *qid); int p9_client_clunk(struct p9_fid *fid); int p9_client_remove(struct p9_fid *fid); int p9_client_read(struct p9_fid *fid, char *data, char __user *udata, diff --git a/net/9p/client.c b/net/9p/client.c index a3bdd341f2ac..e580409b1052 100644 --- a/net/9p/client.c +++ b/net/9p/client.c @@ -1050,6 +1050,50 @@ error: } EXPORT_SYMBOL(p9_client_open); +int p9_client_create_dotl(struct p9_fid *ofid, char *name, u32 flags, u32 mode, + gid_t gid, struct p9_qid *qid) +{ + int err = 0; + struct p9_client *clnt; + struct p9_req_t *req; + int iounit; + + P9_DPRINTK(P9_DEBUG_9P, + ">>> TLCREATE fid %d name %s flags %d mode %d gid %d\n", + ofid->fid, name, flags, mode, gid); + clnt = ofid->clnt; + + if (ofid->mode != -1) + return -EINVAL; + + req = p9_client_rpc(clnt, P9_TLCREATE, "dsddd", ofid->fid, name, flags, + mode, gid); + if (IS_ERR(req)) { + err = PTR_ERR(req); + goto error; + } + + err = p9pdu_readf(req->rc, clnt->proto_version, "Qd", qid, &iounit); + if (err) { + p9pdu_dump(1, req->rc); + goto free_and_error; + } + + P9_DPRINTK(P9_DEBUG_9P, "<<< RLCREATE qid %x.%llx.%x iounit %x\n", + qid->type, + (unsigned long long)qid->path, + qid->version, iounit); + + ofid->mode = mode; + ofid->iounit = iounit; + +free_and_error: + p9_free_req(clnt, req); +error: + return err; +} +EXPORT_SYMBOL(p9_client_create_dotl); + int p9_client_fcreate(struct p9_fid *fid, char *name, u32 perm, int mode, char *extension) { -- cgit v1.2.3 From ef56547efa3c88609069e2a91f46e25c31dd536e Mon Sep 17 00:00:00 2001 From: "M. Mohan Kumar" Date: Tue, 22 Jun 2010 19:47:50 +0530 Subject: 9p: Implement LOPEN Implement 9p2000.L version of open(LOPEN) interface in 9p client. For LOPEN, no need to convert the flags to and from 9p mode to VFS mode. Synopsis: size[4] Tlopen tag[2] fid[4] mode[4] size[4] Rlopen tag[2] qid[13] iounit[4] [Fix mode bit format - jvrao@linux.vnet.ibm.com] Signed-off-by: M. Mohan Kumar Signed-off-by: Venkateswararao Jujjuri Signed-off-by: Eric Van Hensbegren --- fs/9p/vfs_file.c | 13 +++++++++---- include/net/9p/9p.h | 2 ++ net/9p/client.c | 17 ++++++++++------- 3 files changed, 21 insertions(+), 11 deletions(-) (limited to 'fs') diff --git a/fs/9p/vfs_file.c b/fs/9p/vfs_file.c index 2d686ec322a0..e97c92bd6f16 100644 --- a/fs/9p/vfs_file.c +++ b/fs/9p/vfs_file.c @@ -59,9 +59,13 @@ int v9fs_file_open(struct inode *inode, struct file *file) struct p9_fid *fid; int omode; - P9_DPRINTK(P9_DEBUG_VFS, "inode: %p file: %p \n", inode, file); + P9_DPRINTK(P9_DEBUG_VFS, "inode: %p file: %p\n", inode, file); v9ses = v9fs_inode2v9ses(inode); - omode = v9fs_uflags2omode(file->f_flags, v9fs_proto_dotu(v9ses)); + if (v9fs_proto_dotl(v9ses)) + omode = file->f_flags; + else + omode = v9fs_uflags2omode(file->f_flags, + v9fs_proto_dotu(v9ses)); fid = file->private_data; if (!fid) { fid = v9fs_fid_clone(file->f_path.dentry); @@ -73,11 +77,12 @@ int v9fs_file_open(struct inode *inode, struct file *file) p9_client_clunk(fid); return err; } - if (omode & P9_OTRUNC) { + if (file->f_flags & O_TRUNC) { i_size_write(inode, 0); inode->i_blocks = 0; } - if ((file->f_flags & O_APPEND) && (!v9fs_proto_dotu(v9ses))) + if ((file->f_flags & O_APPEND) && + (!v9fs_proto_dotu(v9ses) && !v9fs_proto_dotl(v9ses))) generic_file_llseek(file, 0, SEEK_END); } diff --git a/include/net/9p/9p.h b/include/net/9p/9p.h index 06d111d61038..cf580a40e299 100644 --- a/include/net/9p/9p.h +++ b/include/net/9p/9p.h @@ -139,6 +139,8 @@ do { \ enum p9_msg_t { P9_TSTATFS = 8, P9_RSTATFS, + P9_TLOPEN = 12, + P9_RLOPEN, P9_TLCREATE = 14, P9_RLCREATE, P9_TSYMLINK = 16, diff --git a/net/9p/client.c b/net/9p/client.c index e580409b1052..c458e042d384 100644 --- a/net/9p/client.c +++ b/net/9p/client.c @@ -1016,14 +1016,18 @@ int p9_client_open(struct p9_fid *fid, int mode) struct p9_qid qid; int iounit; - P9_DPRINTK(P9_DEBUG_9P, ">>> TOPEN fid %d mode %d\n", fid->fid, mode); - err = 0; clnt = fid->clnt; + P9_DPRINTK(P9_DEBUG_9P, ">>> %s fid %d mode %d\n", + p9_is_proto_dotl(clnt) ? "TLOPEN" : "TOPEN", fid->fid, mode); + err = 0; if (fid->mode != -1) return -EINVAL; - req = p9_client_rpc(clnt, P9_TOPEN, "db", fid->fid, mode); + if (p9_is_proto_dotl(clnt)) + req = p9_client_rpc(clnt, P9_TLOPEN, "dd", fid->fid, mode); + else + req = p9_client_rpc(clnt, P9_TOPEN, "db", fid->fid, mode); if (IS_ERR(req)) { err = PTR_ERR(req); goto error; @@ -1035,10 +1039,9 @@ int p9_client_open(struct p9_fid *fid, int mode) goto free_and_error; } - P9_DPRINTK(P9_DEBUG_9P, "<<< ROPEN qid %x.%llx.%x iounit %x\n", - qid.type, - (unsigned long long)qid.path, - qid.version, iounit); + P9_DPRINTK(P9_DEBUG_9P, "<<< %s qid %x.%llx.%x iounit %x\n", + p9_is_proto_dotl(clnt) ? "RLOPEN" : "ROPEN", qid.type, + (unsigned long long)qid.path, qid.version, iounit); fid->mode = mode; fid->iounit = iounit; -- cgit v1.2.3 From ebf46264a004818fe5b23f0ac18ac7336897d807 Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Mon, 31 May 2010 13:22:56 +0530 Subject: fs/9p: Add support user. xattr Signed-off-by: Aneesh Kumar K.V Signed-off-by: Venkateswararao Jujjuri Signed-off-by: Eric Van Hensbergen --- fs/9p/Makefile | 4 +- fs/9p/vfs_inode.c | 15 +++++ fs/9p/vfs_super.c | 6 +- fs/9p/xattr.c | 160 +++++++++++++++++++++++++++++++++++++++++++++++++++++ fs/9p/xattr.h | 27 +++++++++ fs/9p/xattr_user.c | 80 +++++++++++++++++++++++++++ 6 files changed, 289 insertions(+), 3 deletions(-) create mode 100644 fs/9p/xattr.c create mode 100644 fs/9p/xattr.h create mode 100644 fs/9p/xattr_user.c (limited to 'fs') diff --git a/fs/9p/Makefile b/fs/9p/Makefile index 1a940ec7af61..91fba025fcbe 100644 --- a/fs/9p/Makefile +++ b/fs/9p/Makefile @@ -8,6 +8,8 @@ obj-$(CONFIG_9P_FS) := 9p.o vfs_dir.o \ vfs_dentry.o \ v9fs.o \ - fid.o + fid.o \ + xattr.o \ + xattr_user.o 9p-$(CONFIG_9P_FSCACHE) += cache.o diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index 2ac245902a4f..39352ef954dc 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -42,6 +43,7 @@ #include "v9fs_vfs.h" #include "fid.h" #include "cache.h" +#include "xattr.h" static const struct inode_operations v9fs_dir_inode_operations; static const struct inode_operations v9fs_dir_inode_operations_dotu; @@ -1931,6 +1933,11 @@ static const struct inode_operations v9fs_dir_inode_operations_dotl = { .rename = v9fs_vfs_rename, .getattr = v9fs_vfs_getattr_dotl, .setattr = v9fs_vfs_setattr_dotl, + .setxattr = generic_setxattr, + .getxattr = generic_getxattr, + .removexattr = generic_removexattr, + .listxattr = v9fs_listxattr, + }; static const struct inode_operations v9fs_dir_inode_operations = { @@ -1953,6 +1960,10 @@ static const struct inode_operations v9fs_file_inode_operations = { static const struct inode_operations v9fs_file_inode_operations_dotl = { .getattr = v9fs_vfs_getattr_dotl, .setattr = v9fs_vfs_setattr_dotl, + .setxattr = generic_setxattr, + .getxattr = generic_getxattr, + .removexattr = generic_removexattr, + .listxattr = v9fs_listxattr, }; static const struct inode_operations v9fs_symlink_inode_operations = { @@ -1969,4 +1980,8 @@ static const struct inode_operations v9fs_symlink_inode_operations_dotl = { .put_link = v9fs_vfs_put_link, .getattr = v9fs_vfs_getattr_dotl, .setattr = v9fs_vfs_setattr_dotl, + .setxattr = generic_setxattr, + .getxattr = generic_getxattr, + .removexattr = generic_removexattr, + .listxattr = v9fs_listxattr, }; diff --git a/fs/9p/vfs_super.c b/fs/9p/vfs_super.c index 3623f692b448..0a2e2030c844 100644 --- a/fs/9p/vfs_super.c +++ b/fs/9p/vfs_super.c @@ -45,6 +45,7 @@ #include "v9fs.h" #include "v9fs_vfs.h" #include "fid.h" +#include "xattr.h" static const struct super_operations v9fs_super_ops, v9fs_super_ops_dotl; @@ -77,9 +78,10 @@ v9fs_fill_super(struct super_block *sb, struct v9fs_session_info *v9ses, sb->s_blocksize_bits = fls(v9ses->maxdata - 1); sb->s_blocksize = 1 << sb->s_blocksize_bits; sb->s_magic = V9FS_MAGIC; - if (v9fs_proto_dotl(v9ses)) + if (v9fs_proto_dotl(v9ses)) { sb->s_op = &v9fs_super_ops_dotl; - else + sb->s_xattr = v9fs_xattr_handlers; + } else sb->s_op = &v9fs_super_ops; sb->s_bdi = &v9ses->bdi; diff --git a/fs/9p/xattr.c b/fs/9p/xattr.c new file mode 100644 index 000000000000..c434424be869 --- /dev/null +++ b/fs/9p/xattr.c @@ -0,0 +1,160 @@ +/* + * Copyright IBM Corporation, 2010 + * Author Aneesh Kumar K.V + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2.1 of the GNU Lesser General Public License + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + */ + +#include +#include +#include +#include +#include + +#include "fid.h" +#include "xattr.h" + +/* + * v9fs_xattr_get() + * + * Copy an extended attribute into the buffer + * provided, or compute the buffer size required. + * Buffer is NULL to compute the size of the buffer required. + * + * Returns a negative error number on failure, or the number of bytes + * used / required on success. + */ +ssize_t v9fs_xattr_get(struct dentry *dentry, const char *name, + void *buffer, size_t buffer_size) +{ + ssize_t retval; + int msize, read_count; + u64 offset = 0, attr_size; + struct p9_fid *fid, *attr_fid; + + P9_DPRINTK(P9_DEBUG_VFS, "%s: name = %s value_len = %zu\n", + __func__, name, buffer_size); + + fid = v9fs_fid_lookup(dentry); + if (IS_ERR(fid)) + return PTR_ERR(fid); + + attr_fid = p9_client_xattrwalk(fid, name, &attr_size); + if (IS_ERR(attr_fid)) { + retval = PTR_ERR(attr_fid); + P9_DPRINTK(P9_DEBUG_VFS, + "p9_client_attrwalk failed %zd\n", retval); + attr_fid = NULL; + goto error; + } + if (!buffer_size) { + /* request to get the attr_size */ + retval = attr_size; + goto error; + } + if (attr_size > buffer_size) { + retval = -ERANGE; + goto error; + } + msize = attr_fid->clnt->msize; + while (attr_size) { + if (attr_size > (msize - P9_IOHDRSZ)) + read_count = msize - P9_IOHDRSZ; + else + read_count = attr_size; + read_count = p9_client_read(attr_fid, ((char *)buffer)+offset, + 0, offset, read_count); + if (read_count < 0) { + /* error in xattr read */ + retval = read_count; + goto error; + } + offset += read_count; + attr_size -= read_count; + } + /* Total read xattr bytes */ + retval = offset; +error: + if (attr_fid) + p9_client_clunk(attr_fid); + return retval; + +} + +/* + * v9fs_xattr_set() + * + * Create, replace or remove an extended attribute for this inode. Buffer + * is NULL to remove an existing extended attribute, and non-NULL to + * either replace an existing extended attribute, or create a new extended + * attribute. The flags XATTR_REPLACE and XATTR_CREATE + * specify that an extended attribute must exist and must not exist + * previous to the call, respectively. + * + * Returns 0, or a negative error number on failure. + */ +int v9fs_xattr_set(struct dentry *dentry, const char *name, + const void *value, size_t value_len, int flags) +{ + u64 offset = 0; + int retval, msize, write_count; + struct p9_fid *fid = NULL; + + P9_DPRINTK(P9_DEBUG_VFS, "%s: name = %s value_len = %zu flags = %d\n", + __func__, name, value_len, flags); + + fid = v9fs_fid_clone(dentry); + if (IS_ERR(fid)) { + retval = PTR_ERR(fid); + fid = NULL; + goto error; + } + /* + * On success fid points to xattr + */ + retval = p9_client_xattrcreate(fid, name, value_len, flags); + if (retval < 0) { + P9_DPRINTK(P9_DEBUG_VFS, + "p9_client_xattrcreate failed %d\n", retval); + goto error; + } + msize = fid->clnt->msize;; + while (value_len) { + if (value_len > (msize - P9_IOHDRSZ)) + write_count = msize - P9_IOHDRSZ; + else + write_count = value_len; + write_count = p9_client_write(fid, ((char *)value)+offset, + 0, offset, write_count); + if (write_count < 0) { + /* error in xattr write */ + retval = write_count; + goto error; + } + offset += write_count; + value_len -= write_count; + } + /* Total read xattr bytes */ + retval = offset; +error: + if (fid) + retval = p9_client_clunk(fid); + return retval; +} + +ssize_t v9fs_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size) +{ + return v9fs_xattr_get(dentry, NULL, buffer, buffer_size); +} + +const struct xattr_handler *v9fs_xattr_handlers[] = { + &v9fs_xattr_user_handler, + NULL +}; diff --git a/fs/9p/xattr.h b/fs/9p/xattr.h new file mode 100644 index 000000000000..9ddf672ae5c4 --- /dev/null +++ b/fs/9p/xattr.h @@ -0,0 +1,27 @@ +/* + * Copyright IBM Corporation, 2010 + * Author Aneesh Kumar K.V + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2.1 of the GNU Lesser General Public License + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + */ +#ifndef FS_9P_XATTR_H +#define FS_9P_XATTR_H + +#include + +extern const struct xattr_handler *v9fs_xattr_handlers[]; +extern struct xattr_handler v9fs_xattr_user_handler; + +extern ssize_t v9fs_xattr_get(struct dentry *, const char *, + void *, size_t); +extern int v9fs_xattr_set(struct dentry *, const char *, + const void *, size_t, int); +extern ssize_t v9fs_listxattr(struct dentry *, char *, size_t); +#endif /* FS_9P_XATTR_H */ diff --git a/fs/9p/xattr_user.c b/fs/9p/xattr_user.c new file mode 100644 index 000000000000..d0b701b72080 --- /dev/null +++ b/fs/9p/xattr_user.c @@ -0,0 +1,80 @@ +/* + * Copyright IBM Corporation, 2010 + * Author Aneesh Kumar K.V + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2.1 of the GNU Lesser General Public License + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + */ + + +#include +#include +#include +#include +#include "xattr.h" + +static int v9fs_xattr_user_get(struct dentry *dentry, const char *name, + void *buffer, size_t size, int type) +{ + int retval; + char *full_name; + size_t name_len; + size_t prefix_len = XATTR_USER_PREFIX_LEN; + + if (name == NULL) + return -EINVAL; + + if (strcmp(name, "") == 0) + return -EINVAL; + + name_len = strlen(name); + full_name = kmalloc(prefix_len + name_len + 1 , GFP_KERNEL); + if (!full_name) + return -ENOMEM; + memcpy(full_name, XATTR_USER_PREFIX, prefix_len); + memcpy(full_name+prefix_len, name, name_len); + full_name[prefix_len + name_len] = '\0'; + + retval = v9fs_xattr_get(dentry, full_name, buffer, size); + kfree(full_name); + return retval; +} + +static int v9fs_xattr_user_set(struct dentry *dentry, const char *name, + const void *value, size_t size, int flags, int type) +{ + int retval; + char *full_name; + size_t name_len; + size_t prefix_len = XATTR_USER_PREFIX_LEN; + + if (name == NULL) + return -EINVAL; + + if (strcmp(name, "") == 0) + return -EINVAL; + + name_len = strlen(name); + full_name = kmalloc(prefix_len + name_len + 1 , GFP_KERNEL); + if (!full_name) + return -ENOMEM; + memcpy(full_name, XATTR_USER_PREFIX, prefix_len); + memcpy(full_name + prefix_len, name, name_len); + full_name[prefix_len + name_len] = '\0'; + + retval = v9fs_xattr_set(dentry, full_name, value, size, flags); + kfree(full_name); + return retval; +} + +struct xattr_handler v9fs_xattr_user_handler = { + .prefix = XATTR_USER_PREFIX, + .get = v9fs_xattr_user_get, + .set = v9fs_xattr_user_set, +}; -- cgit v1.2.3 From a534c8d15b1f1d0f861fc2bb9e0529bd8486ec3f Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Wed, 30 Jun 2010 19:18:50 +0530 Subject: fs/9p: Prevent parallel rename when doing fid_lookup During fid lookup we need to make sure that the dentry->d_parent doesn't change so that we can safely walk the parent dentries. To ensure that we need to prevent cross directory rename during fid_lookup. Add a per superblock rename_sem rw_semaphore to prevent parallel fid lookup and rename. Signed-off-by: Aneesh Kumar K.V Signed-off-by: Venkateswararao Jujjuri Signed-off-by: Eric Van Hensbergen --- fs/9p/fid.c | 114 +++++++++++++++++++++++++++++++++++++----------------- fs/9p/v9fs.c | 1 + fs/9p/v9fs.h | 1 + fs/9p/vfs_inode.c | 13 +++++-- fs/9p/vfs_super.c | 1 + 5 files changed, 91 insertions(+), 39 deletions(-) (limited to 'fs') diff --git a/fs/9p/fid.c b/fs/9p/fid.c index 5d6cfcbf73e7..358563689064 100644 --- a/fs/9p/fid.c +++ b/fs/9p/fid.c @@ -97,6 +97,34 @@ static struct p9_fid *v9fs_fid_find(struct dentry *dentry, u32 uid, int any) return ret; } +/* + * We need to hold v9ses->rename_sem as long as we hold references + * to returned path array. Array element contain pointers to + * dentry names. + */ +static int build_path_from_dentry(struct v9fs_session_info *v9ses, + struct dentry *dentry, char ***names) +{ + int n = 0, i; + char **wnames; + struct dentry *ds; + + for (ds = dentry; !IS_ROOT(ds); ds = ds->d_parent) + n++; + + wnames = kmalloc(sizeof(char *) * n, GFP_KERNEL); + if (!wnames) + goto err_out; + + for (ds = dentry, i = (n-1); i >= 0; i--, ds = ds->d_parent) + wnames[i] = (char *)ds->d_name.name; + + *names = wnames; + return n; +err_out: + return -ENOMEM; +} + /** * v9fs_fid_lookup - lookup for a fid, try to walk if not found * @dentry: dentry to look for fid in @@ -112,7 +140,7 @@ struct p9_fid *v9fs_fid_lookup(struct dentry *dentry) int i, n, l, clone, any, access; u32 uid; struct p9_fid *fid, *old_fid = NULL; - struct dentry *d, *ds; + struct dentry *ds; struct v9fs_session_info *v9ses; char **wnames, *uname; @@ -139,50 +167,62 @@ struct p9_fid *v9fs_fid_lookup(struct dentry *dentry) fid = v9fs_fid_find(dentry, uid, any); if (fid) return fid; - + /* + * we don't have a matching fid. To do a TWALK we need + * parent fid. We need to prevent rename when we want to + * look at the parent. + */ + down_read(&v9ses->rename_sem); ds = dentry->d_parent; fid = v9fs_fid_find(ds, uid, any); - if (!fid) { /* walk from the root */ - n = 0; - for (ds = dentry; !IS_ROOT(ds); ds = ds->d_parent) - n++; - - fid = v9fs_fid_find(ds, uid, any); - if (!fid) { /* the user is not attached to the fs yet */ - if (access == V9FS_ACCESS_SINGLE) - return ERR_PTR(-EPERM); - - if (v9fs_proto_dotu(v9ses) || - v9fs_proto_dotl(v9ses)) - uname = NULL; - else - uname = v9ses->uname; + if (fid) { + /* Found the parent fid do a lookup with that */ + fid = p9_client_walk(fid, 1, (char **)&dentry->d_name.name, 1); + goto fid_out; + } + up_read(&v9ses->rename_sem); - fid = p9_client_attach(v9ses->clnt, NULL, uname, uid, - v9ses->aname); + /* start from the root and try to do a lookup */ + fid = v9fs_fid_find(dentry->d_sb->s_root, uid, any); + if (!fid) { + /* the user is not attached to the fs yet */ + if (access == V9FS_ACCESS_SINGLE) + return ERR_PTR(-EPERM); - if (IS_ERR(fid)) - return fid; + if (v9fs_proto_dotu(v9ses) || v9fs_proto_dotl(v9ses)) + uname = NULL; + else + uname = v9ses->uname; - v9fs_fid_add(ds, fid); - } - } else /* walk from the parent */ - n = 1; + fid = p9_client_attach(v9ses->clnt, NULL, uname, uid, + v9ses->aname); + if (IS_ERR(fid)) + return fid; - if (ds == dentry) + v9fs_fid_add(dentry->d_sb->s_root, fid); + } + /* If we are root ourself just return that */ + if (dentry->d_sb->s_root == dentry) return fid; - - wnames = kmalloc(sizeof(char *) * n, GFP_KERNEL); - if (!wnames) - return ERR_PTR(-ENOMEM); - - for (d = dentry, i = (n-1); i >= 0; i--, d = d->d_parent) - wnames[i] = (char *) d->d_name.name; - + /* + * Do a multipath walk with attached root. + * When walking parent we need to make sure we + * don't have a parallel rename happening + */ + down_read(&v9ses->rename_sem); + n = build_path_from_dentry(v9ses, dentry, &wnames); + if (n < 0) { + fid = ERR_PTR(n); + goto err_out; + } clone = 1; i = 0; while (i < n) { l = min(n - i, P9_MAXWELEM); + /* + * We need to hold rename lock when doing a multipath + * walk to ensure none of the patch component change + */ fid = p9_client_walk(fid, l, &wnames[i], clone); if (IS_ERR(fid)) { if (old_fid) { @@ -194,15 +234,17 @@ struct p9_fid *v9fs_fid_lookup(struct dentry *dentry) p9_client_clunk(old_fid); } kfree(wnames); - return fid; + goto err_out; } old_fid = fid; i += l; clone = 0; } - kfree(wnames); +fid_out: v9fs_fid_add(dentry, fid); +err_out: + up_read(&v9ses->rename_sem); return fid; } diff --git a/fs/9p/v9fs.c b/fs/9p/v9fs.c index 3c492011221c..38dc0e067599 100644 --- a/fs/9p/v9fs.c +++ b/fs/9p/v9fs.c @@ -237,6 +237,7 @@ struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses, __putname(v9ses->uname); return ERR_PTR(-ENOMEM); } + init_rwsem(&v9ses->rename_sem); rc = bdi_setup_and_register(&v9ses->bdi, "9p", BDI_CAP_MAP_COPY); if (rc) { diff --git a/fs/9p/v9fs.h b/fs/9p/v9fs.h index bec4d0bcb458..4c963c9fc41f 100644 --- a/fs/9p/v9fs.h +++ b/fs/9p/v9fs.h @@ -104,6 +104,7 @@ struct v9fs_session_info { struct p9_client *clnt; /* 9p client */ struct list_head slist; /* list of sessions registered with v9fs */ struct backing_dev_info bdi; + struct rw_semaphore rename_sem; }; struct p9_fid *v9fs_session_init(struct v9fs_session_info *, const char *, diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index 39352ef954dc..75c261fdc7b4 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -948,6 +948,7 @@ static struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry, sb = dir->i_sb; v9ses = v9fs_inode2v9ses(dir); + /* We can walk d_parent because we hold the dir->i_mutex */ dfid = v9fs_fid_lookup(dentry->d_parent); if (IS_ERR(dfid)) return ERR_CAST(dfid); @@ -1055,27 +1056,33 @@ v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry, goto clunk_olddir; } + down_write(&v9ses->rename_sem); if (v9fs_proto_dotl(v9ses)) { retval = p9_client_rename(oldfid, newdirfid, (char *) new_dentry->d_name.name); if (retval != -ENOSYS) goto clunk_newdir; } + if (old_dentry->d_parent != new_dentry->d_parent) { + /* + * 9P .u can only handle file rename in the same directory + */ - /* 9P can only handle file rename in the same directory */ - if (memcmp(&olddirfid->qid, &newdirfid->qid, sizeof(newdirfid->qid))) { P9_DPRINTK(P9_DEBUG_ERROR, "old dir and new dir are different\n"); retval = -EXDEV; goto clunk_newdir; } - v9fs_blank_wstat(&wstat); wstat.muid = v9ses->uname; wstat.name = (char *) new_dentry->d_name.name; retval = p9_client_wstat(oldfid, &wstat); clunk_newdir: + if (!retval) + /* successful rename */ + d_move(old_dentry, new_dentry); + up_write(&v9ses->rename_sem); p9_client_clunk(newdirfid); clunk_olddir: diff --git a/fs/9p/vfs_super.c b/fs/9p/vfs_super.c index 0a2e2030c844..4b9ede0b41b7 100644 --- a/fs/9p/vfs_super.c +++ b/fs/9p/vfs_super.c @@ -287,4 +287,5 @@ struct file_system_type v9fs_fs_type = { .get_sb = v9fs_get_sb, .kill_sb = v9fs_kill_super, .owner = THIS_MODULE, + .fs_flags = FS_RENAME_DOES_D_MOVE, }; -- cgit v1.2.3 From ea1375333ef58298ba4d3c638f3cba982c76504d Mon Sep 17 00:00:00 2001 From: Eric Van Hensbergen Date: Tue, 27 Jul 2010 14:49:43 -0500 Subject: fs/9p: remove sparse warning in vfs_inode make v9fs_dentry_from_dir_inode static Signed-off-by: Eric Van Hensbergen --- fs/9p/vfs_inode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index 75c261fdc7b4..6e94f3247cec 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -260,7 +260,7 @@ static gid_t v9fs_get_fsgid_for_create(struct inode *dir_inode) * */ -struct dentry *v9fs_dentry_from_dir_inode(struct inode *inode) +static struct dentry *v9fs_dentry_from_dir_inode(struct inode *inode) { struct dentry *dentry; -- cgit v1.2.3 From 327aec03ac4c7bbf5e41ff03ac3a84c424589f27 Mon Sep 17 00:00:00 2001 From: Eric Van Hensbergen Date: Mon, 2 Aug 2010 11:36:18 -0500 Subject: 9p: fix sparse warnings in new xattr code fixes: CHECK fs/9p/xattr.c fs/9p/xattr.c:73:6: warning: Using plain integer as NULL pointer fs/9p/xattr.c:135:6: warning: Using plain integer as NULL pointer Signed-off-by: Eric Van Hensbergen --- fs/9p/xattr.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/9p/xattr.c b/fs/9p/xattr.c index c434424be869..f88e5c2dc873 100644 --- a/fs/9p/xattr.c +++ b/fs/9p/xattr.c @@ -70,7 +70,7 @@ ssize_t v9fs_xattr_get(struct dentry *dentry, const char *name, else read_count = attr_size; read_count = p9_client_read(attr_fid, ((char *)buffer)+offset, - 0, offset, read_count); + NULL, offset, read_count); if (read_count < 0) { /* error in xattr read */ retval = read_count; @@ -132,7 +132,7 @@ int v9fs_xattr_set(struct dentry *dentry, const char *name, else write_count = value_len; write_count = p9_client_write(fid, ((char *)value)+offset, - 0, offset, write_count); + NULL, offset, write_count); if (write_count < 0) { /* error in xattr write */ retval = write_count; -- cgit v1.2.3 From cbbfe499055f49c09210e04d9f88c2f483052384 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Mon, 2 Aug 2010 15:48:23 -0700 Subject: ceph: move AES iv definition to shared header Signed-off-by: Sage Weil --- fs/ceph/ceph_fs.h | 2 ++ fs/ceph/crypto.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/ceph/ceph_fs.h b/fs/ceph/ceph_fs.h index bf7dea673237..5bf4ec2cf9eb 100644 --- a/fs/ceph/ceph_fs.h +++ b/fs/ceph/ceph_fs.h @@ -74,6 +74,8 @@ int ceph_file_layout_is_valid(const struct ceph_file_layout *layout); #define CEPH_CRYPTO_NONE 0x0 #define CEPH_CRYPTO_AES 0x1 +#define CEPH_AES_IV "cephsageyudagreg" + /* security/authentication protocols */ #define CEPH_AUTH_UNKNOWN 0x0 #define CEPH_AUTH_NONE 0x1 diff --git a/fs/ceph/crypto.c b/fs/ceph/crypto.c index c9318278f419..a3e627f63293 100644 --- a/fs/ceph/crypto.c +++ b/fs/ceph/crypto.c @@ -75,7 +75,7 @@ static struct crypto_blkcipher *ceph_crypto_alloc_cipher(void) return crypto_alloc_blkcipher("cbc(aes)", 0, CRYPTO_ALG_ASYNC); } -static const u8 *aes_iv = "cephsageyudagreg"; +static const u8 *aes_iv = (u8 *)CEPH_AES_IV; static int ceph_aes_encrypt(const void *key, int key_len, void *dst, size_t *dst_len, -- cgit v1.2.3 From ce1fbc8dd657a4bbcf26c683c9d07c88db83fd86 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Mon, 2 Aug 2010 15:09:39 -0700 Subject: ceph: support v2 client_caps encoding Add support for v2 encoding of MClientCaps, which includes a flock blob. Signed-off-by: Sage Weil --- fs/ceph/caps.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index 30acc7b046ee..51546d54b841 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -2698,6 +2698,9 @@ void ceph_handle_caps(struct ceph_mds_session *session, u64 size, max_size; u64 tid; void *snaptrace; + size_t snaptrace_len; + void *flock; + u32 flock_len; int open_target_sessions = 0; dout("handle_caps from mds%d\n", mds); @@ -2707,7 +2710,6 @@ void ceph_handle_caps(struct ceph_mds_session *session, if (msg->front.iov_len < sizeof(*h)) goto bad; h = msg->front.iov_base; - snaptrace = h + 1; op = le32_to_cpu(h->op); vino.ino = le64_to_cpu(h->ino); vino.snap = CEPH_NOSNAP; @@ -2717,6 +2719,21 @@ void ceph_handle_caps(struct ceph_mds_session *session, size = le64_to_cpu(h->size); max_size = le64_to_cpu(h->max_size); + snaptrace = h + 1; + snaptrace_len = le32_to_cpu(h->snap_trace_len); + + if (le16_to_cpu(msg->hdr.version) >= 2) { + void *p, *end; + + p = snaptrace + snaptrace_len; + end = msg->front.iov_base + msg->front.iov_len; + ceph_decode_32_safe(&p, end, flock_len, bad); + flock = p; + } else { + flock = NULL; + flock_len = 0; + } + mutex_lock(&session->s_mutex); session->s_seq++; dout(" mds%d seq %lld cap seq %u\n", session->s_mds, session->s_seq, @@ -2755,7 +2772,7 @@ void ceph_handle_caps(struct ceph_mds_session *session, case CEPH_CAP_OP_IMPORT: handle_cap_import(mdsc, inode, h, session, - snaptrace, le32_to_cpu(h->snap_trace_len)); + snaptrace, snaptrace_len); ceph_check_caps(ceph_inode(inode), CHECK_CAPS_NODELAY, session); goto done_unlocked; -- cgit v1.2.3 From 20cb34ae9e4b008a8789a48d52f5aa279dc400b6 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Wed, 12 May 2010 15:21:32 -0700 Subject: ceph: support v2 reconnect encoding Encode either old or v2 encoding of client_reconnect message, depending on whether the peer has the FLOCK feature bit. Signed-off-by: Sage Weil --- fs/ceph/ceph_fs.h | 11 ++++++++++- fs/ceph/mds_client.c | 52 ++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 50 insertions(+), 13 deletions(-) (limited to 'fs') diff --git a/fs/ceph/ceph_fs.h b/fs/ceph/ceph_fs.h index 5bf4ec2cf9eb..bb17a18cc190 100644 --- a/fs/ceph/ceph_fs.h +++ b/fs/ceph/ceph_fs.h @@ -632,6 +632,16 @@ struct ceph_mds_lease { /* client reconnect */ struct ceph_mds_cap_reconnect { + __le64 cap_id; + __le32 wanted; + __le32 issued; + __le64 snaprealm; + __le64 pathbase; /* base ino for our path to this ino */ + __le32 flock_len; /* size of flock state blob, if any */ +} __attribute__ ((packed)); +/* followed by flock blob */ + +struct ceph_mds_cap_reconnect_v1 { __le64 cap_id; __le32 wanted; __le32 issued; @@ -640,7 +650,6 @@ struct ceph_mds_cap_reconnect { __le64 snaprealm; __le64 pathbase; /* base ino for our path to this ino */ } __attribute__ ((packed)); -/* followed by encoded string */ struct ceph_mds_snaprealm_reconnect { __le64 ino; /* snap realm base */ diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 34d215ff4c82..615f720a819d 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -37,6 +37,11 @@ * are no longer valid. */ +struct ceph_reconnect_state { + struct ceph_pagelist *pagelist; + bool flock; +}; + static void __wake_requests(struct ceph_mds_client *mdsc, struct list_head *head); @@ -2268,9 +2273,14 @@ static void replay_unsafe_requests(struct ceph_mds_client *mdsc, static int encode_caps_cb(struct inode *inode, struct ceph_cap *cap, void *arg) { - struct ceph_mds_cap_reconnect rec; + union { + struct ceph_mds_cap_reconnect v2; + struct ceph_mds_cap_reconnect_v1 v1; + } rec; + size_t reclen; struct ceph_inode_info *ci; - struct ceph_pagelist *pagelist = arg; + struct ceph_reconnect_state *recon_state = arg; + struct ceph_pagelist *pagelist = recon_state->pagelist; char *path; int pathlen, err; u64 pathbase; @@ -2303,17 +2313,29 @@ static int encode_caps_cb(struct inode *inode, struct ceph_cap *cap, spin_lock(&inode->i_lock); cap->seq = 0; /* reset cap seq */ cap->issue_seq = 0; /* and issue_seq */ - rec.cap_id = cpu_to_le64(cap->cap_id); - rec.pathbase = cpu_to_le64(pathbase); - rec.wanted = cpu_to_le32(__ceph_caps_wanted(ci)); - rec.issued = cpu_to_le32(cap->issued); - rec.size = cpu_to_le64(inode->i_size); - ceph_encode_timespec(&rec.mtime, &inode->i_mtime); - ceph_encode_timespec(&rec.atime, &inode->i_atime); - rec.snaprealm = cpu_to_le64(ci->i_snap_realm->ino); + + if (recon_state->flock) { + rec.v2.cap_id = cpu_to_le64(cap->cap_id); + rec.v2.wanted = cpu_to_le32(__ceph_caps_wanted(ci)); + rec.v2.issued = cpu_to_le32(cap->issued); + rec.v2.snaprealm = cpu_to_le64(ci->i_snap_realm->ino); + rec.v2.pathbase = cpu_to_le64(pathbase); + rec.v2.flock_len = 0; + reclen = sizeof(rec.v2); + } else { + rec.v1.cap_id = cpu_to_le64(cap->cap_id); + rec.v1.wanted = cpu_to_le32(__ceph_caps_wanted(ci)); + rec.v1.issued = cpu_to_le32(cap->issued); + rec.v1.size = cpu_to_le64(inode->i_size); + ceph_encode_timespec(&rec.v1.mtime, &inode->i_mtime); + ceph_encode_timespec(&rec.v1.atime, &inode->i_atime); + rec.v1.snaprealm = cpu_to_le64(ci->i_snap_realm->ino); + rec.v1.pathbase = cpu_to_le64(pathbase); + reclen = sizeof(rec.v1); + } spin_unlock(&inode->i_lock); - err = ceph_pagelist_append(pagelist, &rec, sizeof(rec)); + err = ceph_pagelist_append(pagelist, &rec, reclen); out: kfree(path); @@ -2342,6 +2364,7 @@ static void send_mds_reconnect(struct ceph_mds_client *mdsc, int mds = session->s_mds; int err = -ENOMEM; struct ceph_pagelist *pagelist; + struct ceph_reconnect_state recon_state; pr_info("mds%d reconnect start\n", mds); @@ -2376,7 +2399,10 @@ static void send_mds_reconnect(struct ceph_mds_client *mdsc, err = ceph_pagelist_encode_32(pagelist, session->s_nr_caps); if (err) goto fail; - err = iterate_session_caps(session, encode_caps_cb, pagelist); + + recon_state.pagelist = pagelist; + recon_state.flock = session->s_con.peer_features & CEPH_FEATURE_FLOCK; + err = iterate_session_caps(session, encode_caps_cb, &recon_state); if (err < 0) goto fail; @@ -2401,6 +2427,8 @@ static void send_mds_reconnect(struct ceph_mds_client *mdsc, } reply->pagelist = pagelist; + if (recon_state.flock) + reply->hdr.version = cpu_to_le16(2); reply->hdr.data_len = cpu_to_le32(pagelist->length); reply->nr_pages = calc_pages_for(0, pagelist->length); ceph_con_send(&session->s_con, reply); -- cgit v1.2.3 From c6f3fdc592d61847da0e2172e352dbcb53c83d39 Mon Sep 17 00:00:00 2001 From: Greg Farnum Date: Mon, 2 Aug 2010 14:55:24 -0700 Subject: ceph: add CEPH_FEATURE_FLOCK to the supported feature bits This informs the server that we will accept v2 client_caps format and v2 client_reconnect format messages. Signed-off-by: Greg Farnum Signed-off-by: Sage Weil --- fs/ceph/super.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/ceph/super.h b/fs/ceph/super.h index 281e458db8b3..83f7cc5fab10 100644 --- a/fs/ceph/super.h +++ b/fs/ceph/super.h @@ -33,7 +33,7 @@ /* * Supported features */ -#define CEPH_FEATURE_SUPPORTED CEPH_FEATURE_NOSRCADDR +#define CEPH_FEATURE_SUPPORTED CEPH_FEATURE_NOSRCADDR | CEPH_FEATURE_FLOCK #define CEPH_FEATURE_REQUIRED CEPH_FEATURE_NOSRCADDR /* -- cgit v1.2.3 From fbaad9797a761c2d5ff6e755bbb4c046207a1ca2 Mon Sep 17 00:00:00 2001 From: Greg Farnum Date: Mon, 2 Aug 2010 15:30:08 -0700 Subject: ceph: define on-wire types, constants for file locking support Define the MDS operations and data types for doing file advisory locking with the MDS. Signed-off-by: Greg Farnum Signed-off-by: Sage Weil --- fs/ceph/ceph_fs.h | 36 ++++++++++++++++++++++++++++++++++-- fs/ceph/ceph_strings.c | 2 ++ 2 files changed, 36 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/ceph/ceph_fs.h b/fs/ceph/ceph_fs.h index bb17a18cc190..d5619ac86711 100644 --- a/fs/ceph/ceph_fs.h +++ b/fs/ceph/ceph_fs.h @@ -297,6 +297,8 @@ enum { CEPH_MDS_OP_RMXATTR = 0x01106, CEPH_MDS_OP_SETLAYOUT = 0x01107, CEPH_MDS_OP_SETATTR = 0x01108, + CEPH_MDS_OP_SETFILELOCK= 0x01109, + CEPH_MDS_OP_GETFILELOCK= 0x00110, CEPH_MDS_OP_MKNOD = 0x01201, CEPH_MDS_OP_LINK = 0x01202, @@ -367,6 +369,15 @@ union ceph_mds_request_args { struct { struct ceph_file_layout layout; } __attribute__ ((packed)) setlayout; + struct { + __u8 rule; /* currently fcntl or flock */ + __u8 type; /* shared, exclusive, remove*/ + __le64 pid; /* process id requesting the lock */ + __le64 pid_namespace; + __le64 start; /* initial location to lock */ + __le64 length; /* num bytes to lock from start */ + __u8 wait; /* will caller wait for lock to become available? */ + } __attribute__ ((packed)) filelock_change; } __attribute__ ((packed)); #define CEPH_MDS_FLAG_REPLAY 1 /* this is a replayed op */ @@ -461,6 +472,23 @@ struct ceph_mds_reply_dirfrag { __le32 dist[]; } __attribute__ ((packed)); +#define CEPH_LOCK_FCNTL 1 +#define CEPH_LOCK_FLOCK 2 + +#define CEPH_LOCK_SHARED 1 +#define CEPH_LOCK_EXCL 2 +#define CEPH_LOCK_UNLOCK 4 + +struct ceph_filelock { + __le64 start;/* file offset to start lock at */ + __le64 length; /* num bytes to lock; 0 for all following start */ + __le64 client; /* which client holds the lock */ + __le64 pid; /* process id holding the lock on the client */ + __le64 pid_namespace; + __u8 type; /* shared lock, exclusive lock, or unlock */ +} __attribute__ ((packed)); + + /* file access modes */ #define CEPH_FILE_MODE_PIN 0 #define CEPH_FILE_MODE_RD 1 @@ -489,9 +517,10 @@ int ceph_flags_to_mode(int flags); #define CEPH_CAP_SAUTH 2 #define CEPH_CAP_SLINK 4 #define CEPH_CAP_SXATTR 6 -#define CEPH_CAP_SFILE 8 /* goes at the end (uses >2 cap bits) */ +#define CEPH_CAP_SFILE 8 +#define CEPH_CAP_SFLOCK 20 -#define CEPH_CAP_BITS 16 +#define CEPH_CAP_BITS 22 /* composed values */ #define CEPH_CAP_AUTH_SHARED (CEPH_CAP_GSHARED << CEPH_CAP_SAUTH) @@ -509,6 +538,9 @@ int ceph_flags_to_mode(int flags); #define CEPH_CAP_FILE_BUFFER (CEPH_CAP_GBUFFER << CEPH_CAP_SFILE) #define CEPH_CAP_FILE_WREXTEND (CEPH_CAP_GWREXTEND << CEPH_CAP_SFILE) #define CEPH_CAP_FILE_LAZYIO (CEPH_CAP_GLAZYIO << CEPH_CAP_SFILE) +#define CEPH_CAP_FLOCK_SHARED (CEPH_CAP_GSHARED << CEPH_CAP_SFLOCK) +#define CEPH_CAP_FLOCK_EXCL (CEPH_CAP_GEXCL << CEPH_CAP_SFLOCK) + /* cap masks (for getattr) */ #define CEPH_STAT_CAP_INODE CEPH_CAP_PIN diff --git a/fs/ceph/ceph_strings.c b/fs/ceph/ceph_strings.c index 0f943a0a3b5e..c6179d3a26a2 100644 --- a/fs/ceph/ceph_strings.c +++ b/fs/ceph/ceph_strings.c @@ -130,6 +130,8 @@ const char *ceph_mds_op_name(int op) case CEPH_MDS_OP_LSSNAP: return "lssnap"; case CEPH_MDS_OP_MKSNAP: return "mksnap"; case CEPH_MDS_OP_RMSNAP: return "rmsnap"; + case CEPH_MDS_OP_SETFILELOCK: return "setfilelock"; + case CEPH_MDS_OP_GETFILELOCK: return "getfilelock"; } return "???"; } -- cgit v1.2.3 From 40819f6fb227c1832935b775ac22aef10aa6f6dd Mon Sep 17 00:00:00 2001 From: Greg Farnum Date: Mon, 2 Aug 2010 15:34:23 -0700 Subject: ceph: add flock/fcntl lock support Implement flock inode operation to support advisory file locking. All lock/unlock operations are synchronous with the MDS. Lock state is sent when reconnecting to a recovering MDS to restore the shared lock state. Signed-off-by: Greg Farnum Signed-off-by: Sage Weil --- fs/ceph/Makefile | 2 +- fs/ceph/file.c | 2 + fs/ceph/locks.c | 256 +++++++++++++++++++++++++++++++++++++++++++++++++++ fs/ceph/mds_client.c | 18 +++- fs/ceph/super.h | 8 ++ 5 files changed, 284 insertions(+), 2 deletions(-) create mode 100644 fs/ceph/locks.c (limited to 'fs') diff --git a/fs/ceph/Makefile b/fs/ceph/Makefile index 6a660e610be8..278e1172600d 100644 --- a/fs/ceph/Makefile +++ b/fs/ceph/Makefile @@ -6,7 +6,7 @@ ifneq ($(KERNELRELEASE),) obj-$(CONFIG_CEPH_FS) += ceph.o -ceph-objs := super.o inode.o dir.o file.o addr.o ioctl.o \ +ceph-objs := super.o inode.o dir.o file.o locks.o addr.o ioctl.o \ export.o caps.o snap.o xattr.o \ messenger.o msgpool.o buffer.o pagelist.o \ mds_client.o mdsmap.o \ diff --git a/fs/ceph/file.c b/fs/ceph/file.c index 5fe50ffa2deb..e850e63f9563 100644 --- a/fs/ceph/file.c +++ b/fs/ceph/file.c @@ -938,6 +938,8 @@ const struct file_operations ceph_file_fops = { .aio_write = ceph_aio_write, .mmap = ceph_mmap, .fsync = ceph_fsync, + .lock = ceph_lock, + .flock = ceph_flock, .splice_read = generic_file_splice_read, .splice_write = generic_file_splice_write, .unlocked_ioctl = ceph_ioctl, diff --git a/fs/ceph/locks.c b/fs/ceph/locks.c new file mode 100644 index 000000000000..ae85af06454f --- /dev/null +++ b/fs/ceph/locks.c @@ -0,0 +1,256 @@ +#include "ceph_debug.h" + +#include +#include + +#include "super.h" +#include "mds_client.h" +#include "pagelist.h" + +/** + * Implement fcntl and flock locking functions. + */ +static int ceph_lock_message(u8 lock_type, u16 operation, struct file *file, + u64 pid, u64 pid_ns, + int cmd, u64 start, u64 length, u8 wait) +{ + struct inode *inode = file->f_dentry->d_inode; + struct ceph_mds_client *mdsc = + &ceph_sb_to_client(inode->i_sb)->mdsc; + struct ceph_mds_request *req; + int err; + + req = ceph_mdsc_create_request(mdsc, operation, USE_AUTH_MDS); + if (IS_ERR(req)) + return PTR_ERR(req); + req->r_inode = igrab(inode); + + dout("ceph_lock_message: rule: %d, op: %d, pid: %llu, start: %llu, " + "length: %llu, wait: %d, type`: %d", (int)lock_type, + (int)operation, pid, start, length, wait, cmd); + + req->r_args.filelock_change.rule = lock_type; + req->r_args.filelock_change.type = cmd; + req->r_args.filelock_change.pid = cpu_to_le64(pid); + /* This should be adjusted, but I'm not sure if + namespaces actually get id numbers*/ + req->r_args.filelock_change.pid_namespace = + cpu_to_le64((u64)pid_ns); + req->r_args.filelock_change.start = cpu_to_le64(start); + req->r_args.filelock_change.length = cpu_to_le64(length); + req->r_args.filelock_change.wait = wait; + + err = ceph_mdsc_do_request(mdsc, inode, req); + ceph_mdsc_put_request(req); + dout("ceph_lock_message: rule: %d, op: %d, pid: %llu, start: %llu, " + "length: %llu, wait: %d, type`: %d err code %d", (int)lock_type, + (int)operation, pid, start, length, wait, cmd, err); + return err; +} + +/** + * Attempt to set an fcntl lock. + * For now, this just goes away to the server. Later it may be more awesome. + */ +int ceph_lock(struct file *file, int cmd, struct file_lock *fl) +{ + u64 length; + u8 lock_cmd; + int err; + u8 wait = 0; + u16 op = CEPH_MDS_OP_SETFILELOCK; + + fl->fl_nspid = get_pid(task_tgid(current)); + dout("ceph_lock, fl_pid:%d", fl->fl_pid); + + /* set wait bit as appropriate, then make command as Ceph expects it*/ + if (F_SETLKW == cmd) + wait = 1; + if (F_GETLK == cmd) + op = CEPH_MDS_OP_GETFILELOCK; + + if (F_RDLCK == fl->fl_type) + lock_cmd = CEPH_LOCK_SHARED; + else if (F_WRLCK == fl->fl_type) + lock_cmd = CEPH_LOCK_EXCL; + else + lock_cmd = CEPH_LOCK_UNLOCK; + + if (LLONG_MAX == fl->fl_end) + length = 0; + else + length = fl->fl_end - fl->fl_start + 1; + + err = ceph_lock_message(CEPH_LOCK_FCNTL, op, file, + (u64)fl->fl_pid, (u64)fl->fl_nspid, + lock_cmd, fl->fl_start, + length, wait); + if (!err) { + dout("mds locked, locking locally"); + err = posix_lock_file(file, fl, NULL); + if (err && (CEPH_MDS_OP_SETFILELOCK == op)) { + /* undo! This should only happen if the kernel detects + * local deadlock. */ + ceph_lock_message(CEPH_LOCK_FCNTL, op, file, + (u64)fl->fl_pid, (u64)fl->fl_nspid, + CEPH_LOCK_UNLOCK, fl->fl_start, + length, 0); + dout("got %d on posix_lock_file, undid lock", err); + } + } else { + dout("mds returned error code %d", err); + } + return err; +} + +int ceph_flock(struct file *file, int cmd, struct file_lock *fl) +{ + u64 length; + u8 lock_cmd; + int err; + u8 wait = 1; + + fl->fl_nspid = get_pid(task_tgid(current)); + dout("ceph_flock, fl_pid:%d", fl->fl_pid); + + /* set wait bit, then clear it out of cmd*/ + if (cmd & LOCK_NB) + wait = 0; + cmd = cmd & (LOCK_SH | LOCK_EX | LOCK_UN); + /* set command sequence that Ceph wants to see: + shared lock, exclusive lock, or unlock */ + if (LOCK_SH == cmd) + lock_cmd = CEPH_LOCK_SHARED; + else if (LOCK_EX == cmd) + lock_cmd = CEPH_LOCK_EXCL; + else + lock_cmd = CEPH_LOCK_UNLOCK; + /* mds requires start and length rather than start and end */ + if (LLONG_MAX == fl->fl_end) + length = 0; + else + length = fl->fl_end - fl->fl_start + 1; + + err = ceph_lock_message(CEPH_LOCK_FLOCK, CEPH_MDS_OP_SETFILELOCK, + file, (u64)fl->fl_pid, (u64)fl->fl_nspid, + lock_cmd, fl->fl_start, + length, wait); + if (!err) { + err = flock_lock_file_wait(file, fl); + if (err) { + ceph_lock_message(CEPH_LOCK_FLOCK, + CEPH_MDS_OP_SETFILELOCK, + file, (u64)fl->fl_pid, + (u64)fl->fl_nspid, + CEPH_LOCK_UNLOCK, fl->fl_start, + length, 0); + dout("got %d on flock_lock_file_wait, undid lock", err); + } + } else { + dout("mds error code %d", err); + } + return err; +} + +/** + * Must be called with BKL already held. Fills in the passed + * counter variables, so you can prepare pagelist metadata before calling + * ceph_encode_locks. + */ +void ceph_count_locks(struct inode *inode, int *fcntl_count, int *flock_count) +{ + struct file_lock *lock; + + *fcntl_count = 0; + *flock_count = 0; + + for (lock = inode->i_flock; lock != NULL; lock = lock->fl_next) { + if (lock->fl_flags & FL_POSIX) + ++(*fcntl_count); + else if (lock->fl_flags & FL_FLOCK) + ++(*flock_count); + } + dout("counted %d flock locks and %d fcntl locks", + *flock_count, *fcntl_count); +} + +/** + * Encode the flock and fcntl locks for the given inode into the pagelist. + * Format is: #fcntl locks, sequential fcntl locks, #flock locks, + * sequential flock locks. + * Must be called with BLK already held, and the lock numbers should have + * been gathered under the same lock holding window. + */ +int ceph_encode_locks(struct inode *inode, struct ceph_pagelist *pagelist, + int num_fcntl_locks, int num_flock_locks) +{ + struct file_lock *lock; + struct ceph_filelock cephlock; + int err = 0; + + dout("encoding %d flock and %d fcntl locks", num_flock_locks, + num_fcntl_locks); + err = ceph_pagelist_append(pagelist, &num_fcntl_locks, sizeof(u32)); + if (err) + goto fail; + for (lock = inode->i_flock; lock != NULL; lock = lock->fl_next) { + if (lock->fl_flags & FL_POSIX) { + err = lock_to_ceph_filelock(lock, &cephlock); + if (err) + goto fail; + err = ceph_pagelist_append(pagelist, &cephlock, + sizeof(struct ceph_filelock)); + } + if (err) + goto fail; + } + + err = ceph_pagelist_append(pagelist, &num_flock_locks, sizeof(u32)); + if (err) + goto fail; + for (lock = inode->i_flock; lock != NULL; lock = lock->fl_next) { + if (lock->fl_flags & FL_FLOCK) { + err = lock_to_ceph_filelock(lock, &cephlock); + if (err) + goto fail; + err = ceph_pagelist_append(pagelist, &cephlock, + sizeof(struct ceph_filelock)); + } + if (err) + goto fail; + } +fail: + return err; +} + +/* + * Given a pointer to a lock, convert it to a ceph filelock + */ +int lock_to_ceph_filelock(struct file_lock *lock, + struct ceph_filelock *cephlock) +{ + int err = 0; + + cephlock->start = cpu_to_le64(lock->fl_start); + cephlock->length = cpu_to_le64(lock->fl_end - lock->fl_start + 1); + cephlock->client = cpu_to_le64(0); + cephlock->pid = cpu_to_le64(lock->fl_pid); + cephlock->pid_namespace = cpu_to_le64((u64)lock->fl_nspid); + + switch (lock->fl_type) { + case F_RDLCK: + cephlock->type = CEPH_LOCK_SHARED; + break; + case F_WRLCK: + cephlock->type = CEPH_LOCK_EXCL; + break; + case F_UNLCK: + cephlock->type = CEPH_LOCK_UNLOCK; + break; + default: + dout("Have unknown lock type %d", lock->fl_type); + err = -EINVAL; + } + + return err; +} diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 615f720a819d..9f0833e1631a 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -3,6 +3,7 @@ #include #include #include +#include #include "mds_client.h" #include "mon_client.h" @@ -2335,7 +2336,22 @@ static int encode_caps_cb(struct inode *inode, struct ceph_cap *cap, } spin_unlock(&inode->i_lock); - err = ceph_pagelist_append(pagelist, &rec, reclen); + if (recon_state->flock) { + int num_fcntl_locks, num_flock_locks; + + lock_kernel(); + ceph_count_locks(inode, &num_fcntl_locks, &num_flock_locks); + rec.v2.flock_len = (2*sizeof(u32) + + (num_fcntl_locks+num_flock_locks) * + sizeof(struct ceph_filelock)); + + err = ceph_pagelist_append(pagelist, &rec, reclen); + if (!err) + err = ceph_encode_locks(inode, pagelist, + num_fcntl_locks, + num_flock_locks); + unlock_kernel(); + } out: kfree(path); diff --git a/fs/ceph/super.h b/fs/ceph/super.h index 83f7cc5fab10..95adce441cc0 100644 --- a/fs/ceph/super.h +++ b/fs/ceph/super.h @@ -892,6 +892,14 @@ extern void ceph_debugfs_cleanup(void); extern int ceph_debugfs_client_init(struct ceph_client *client); extern void ceph_debugfs_client_cleanup(struct ceph_client *client); +/* locks.c */ +extern int ceph_lock(struct file *file, int cmd, struct file_lock *fl); +extern int ceph_flock(struct file *file, int cmd, struct file_lock *fl); +extern void ceph_count_locks(struct inode *inode, int *p_num, int *f_num); +extern int ceph_encode_locks(struct inode *i, struct ceph_pagelist *p, + int p_locks, int f_locks); +extern int lock_to_ceph_filelock(struct file_lock *fl, struct ceph_filelock *c); + static inline struct inode *get_dentry_parent_inode(struct dentry *dentry) { if (dentry && dentry->d_parent) -- cgit v1.2.3 From c18de72fb3c72fdc5ca883910761af3f14d90d76 Mon Sep 17 00:00:00 2001 From: Matthieu CASTET Date: Mon, 2 Aug 2010 11:36:06 +0200 Subject: UBIFS: fix a memory leak on error path. In 'mount_ubifs()', in case of 'ubifs_leb_unmap()' falure, free allocated resources. Signed-off-by: Matthieu CASTET Signed-off-by: Artem Bityutskiy --- fs/ubifs/super.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c index 010eea009036..5fc5a0988970 100644 --- a/fs/ubifs/super.c +++ b/fs/ubifs/super.c @@ -1320,7 +1320,7 @@ static int mount_ubifs(struct ubifs_info *c) */ err = ubifs_leb_unmap(c, c->gc_lnum); if (err) - return err; + goto out_orphans; } err = dbg_check_lprops(c); -- cgit v1.2.3 From 213c99ee0cf17ff0fbffb6fb540bd29615cd19d5 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Tue, 3 Aug 2010 10:25:11 -0700 Subject: ceph: whitespace cleanup Signed-off-by: Sage Weil --- fs/ceph/addr.c | 14 +++++++++----- fs/ceph/debugfs.c | 17 +++++++++-------- fs/ceph/file.c | 2 +- fs/ceph/mds_client.c | 5 +++-- fs/ceph/messenger.c | 9 +++++---- fs/ceph/osd_client.c | 6 +++--- fs/ceph/osdmap.c | 2 +- 7 files changed, 31 insertions(+), 24 deletions(-) (limited to 'fs') diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c index e00797eed29c..5598a0d02295 100644 --- a/fs/ceph/addr.c +++ b/fs/ceph/addr.c @@ -309,7 +309,8 @@ static int ceph_readpages(struct file *file, struct address_space *mapping, zero_user_segment(page, s, PAGE_CACHE_SIZE); } - if (add_to_page_cache_lru(page, mapping, page->index, GFP_NOFS)) { + if (add_to_page_cache_lru(page, mapping, page->index, + GFP_NOFS)) { page_cache_release(page); dout("readpages %p add_to_page_cache failed %p\n", inode, page); @@ -797,9 +798,12 @@ get_more_pages: dout("%p will write page %p idx %lu\n", inode, page, page->index); - writeback_stat = atomic_long_inc_return(&client->writeback_count); - if (writeback_stat > CONGESTION_ON_THRESH(client->mount_args->congestion_kb)) { - set_bdi_congested(&client->backing_dev_info, BLK_RW_ASYNC); + writeback_stat = + atomic_long_inc_return(&client->writeback_count); + if (writeback_stat > CONGESTION_ON_THRESH( + client->mount_args->congestion_kb)) { + set_bdi_congested(&client->backing_dev_info, + BLK_RW_ASYNC); } set_page_writeback(page); @@ -1036,7 +1040,7 @@ static int ceph_write_begin(struct file *file, struct address_space *mapping, *pagep = page; dout("write_begin file %p inode %p page %p %d~%d\n", file, - inode, page, (int)pos, (int)len); + inode, page, (int)pos, (int)len); r = ceph_update_writeable_page(file, pos, len, page); } while (r == -EAGAIN); diff --git a/fs/ceph/debugfs.c b/fs/ceph/debugfs.c index 968dc5a86c3e..360c4f22718d 100644 --- a/fs/ceph/debugfs.c +++ b/fs/ceph/debugfs.c @@ -291,7 +291,7 @@ static int dentry_lru_show(struct seq_file *s, void *ptr) return 0; } -#define DEFINE_SHOW_FUNC(name) \ +#define DEFINE_SHOW_FUNC(name) \ static int name##_open(struct inode *inode, struct file *file) \ { \ struct seq_file *sf; \ @@ -432,11 +432,12 @@ int ceph_debugfs_client_init(struct ceph_client *client) if (!client->debugfs_caps) goto out; - client->debugfs_congestion_kb = debugfs_create_file("writeback_congestion_kb", - 0600, - client->debugfs_dir, - client, - &congestion_kb_fops); + client->debugfs_congestion_kb = + debugfs_create_file("writeback_congestion_kb", + 0600, + client->debugfs_dir, + client, + &congestion_kb_fops); if (!client->debugfs_congestion_kb) goto out; @@ -466,7 +467,7 @@ void ceph_debugfs_client_cleanup(struct ceph_client *client) debugfs_remove(client->debugfs_dir); } -#else // CONFIG_DEBUG_FS +#else /* CONFIG_DEBUG_FS */ int __init ceph_debugfs_init(void) { @@ -486,4 +487,4 @@ void ceph_debugfs_client_cleanup(struct ceph_client *client) { } -#endif // CONFIG_DEBUG_FS +#endif /* CONFIG_DEBUG_FS */ diff --git a/fs/ceph/file.c b/fs/ceph/file.c index e850e63f9563..8c044a4f0457 100644 --- a/fs/ceph/file.c +++ b/fs/ceph/file.c @@ -665,7 +665,7 @@ more: * throw out any page cache pages in this range. this * may block. */ - truncate_inode_pages_range(inode->i_mapping, pos, + truncate_inode_pages_range(inode->i_mapping, pos, (pos+len) | (PAGE_CACHE_SIZE-1)); } else { pages = ceph_alloc_page_vector(num_pages, GFP_NOFS); diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c index 9f0833e1631a..a75ddbf9fe37 100644 --- a/fs/ceph/mds_client.c +++ b/fs/ceph/mds_client.c @@ -1985,8 +1985,9 @@ static void handle_reply(struct ceph_mds_session *session, struct ceph_msg *msg) */ if (result == -ESTALE) { dout("got ESTALE on request %llu", req->r_tid); - if (!req->r_inode) ; //do nothing; not an authority problem - else if (req->r_direct_mode != USE_AUTH_MDS) { + if (!req->r_inode) { + /* do nothing; not an authority problem */ + } else if (req->r_direct_mode != USE_AUTH_MDS) { dout("not using auth, setting for that now"); req->r_direct_mode = USE_AUTH_MDS; __do_request(mdsc, req); diff --git a/fs/ceph/messenger.c b/fs/ceph/messenger.c index 79b0995dc825..2502d76fcec1 100644 --- a/fs/ceph/messenger.c +++ b/fs/ceph/messenger.c @@ -1302,8 +1302,8 @@ static void process_ack(struct ceph_connection *con) static int read_partial_message_section(struct ceph_connection *con, - struct kvec *section, unsigned int sec_len, - u32 *crc) + struct kvec *section, + unsigned int sec_len, u32 *crc) { int left; int ret; @@ -1434,7 +1434,8 @@ static int read_partial_message(struct ceph_connection *con) /* middle */ if (m->middle) { - ret = read_partial_message_section(con, &m->middle->vec, middle_len, + ret = read_partial_message_section(con, &m->middle->vec, + middle_len, &con->in_middle_crc); if (ret <= 0) return ret; @@ -1920,7 +1921,7 @@ out: /* * in case we faulted due to authentication, invalidate our * current tickets so that we can get new ones. - */ + */ if (con->auth_retry && con->ops->invalidate_authorizer) { dout("calling invalidate_authorizer()\n"); con->ops->invalidate_authorizer(con); diff --git a/fs/ceph/osd_client.c b/fs/ceph/osd_client.c index 707d2dbd8776..bed6391e52c7 100644 --- a/fs/ceph/osd_client.c +++ b/fs/ceph/osd_client.c @@ -1473,8 +1473,8 @@ static void put_osd_con(struct ceph_connection *con) * authentication */ static int get_authorizer(struct ceph_connection *con, - void **buf, int *len, int *proto, - void **reply_buf, int *reply_len, int force_new) + void **buf, int *len, int *proto, + void **reply_buf, int *reply_len, int force_new) { struct ceph_osd *o = con->private; struct ceph_osd_client *osdc = o->o_osdc; @@ -1494,7 +1494,7 @@ static int get_authorizer(struct ceph_connection *con, &o->o_authorizer_reply_buf, &o->o_authorizer_reply_buf_len); if (ret) - return ret; + return ret; } *proto = ac->protocol; diff --git a/fs/ceph/osdmap.c b/fs/ceph/osdmap.c index e94c6fb2e2a4..e31f118f1392 100644 --- a/fs/ceph/osdmap.c +++ b/fs/ceph/osdmap.c @@ -855,7 +855,7 @@ struct ceph_osdmap *osdmap_apply_incremental(void **p, void *end, node)->pgid, pgid) <= 0) { struct ceph_pg_mapping *cur = rb_entry(rbp, struct ceph_pg_mapping, node); - + rbp = rb_next(rbp); dout(" removed pg_temp %llx\n", *(u64 *)&cur->pgid); rb_erase(&cur->node, &map->pg_temp); -- cgit v1.2.3 From 52dfb8ac0ef41168c1a10590b7259a5ab1cd2ab7 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Tue, 3 Aug 2010 10:25:30 -0700 Subject: ceph: constify dentry_operations This makes checkpatch happy. Signed-off-by: Sage Weil --- fs/ceph/dir.c | 8 ++++---- fs/ceph/super.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'fs') diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c index 0ec6e67533ef..67bbb41d5526 100644 --- a/fs/ceph/dir.c +++ b/fs/ceph/dir.c @@ -27,7 +27,7 @@ const struct inode_operations ceph_dir_iops; const struct file_operations ceph_dir_fops; -struct dentry_operations ceph_dentry_ops; +const struct dentry_operations ceph_dentry_ops; /* * Initialize ceph dentry state. @@ -1241,16 +1241,16 @@ const struct inode_operations ceph_dir_iops = { .create = ceph_create, }; -struct dentry_operations ceph_dentry_ops = { +const struct dentry_operations ceph_dentry_ops = { .d_revalidate = ceph_d_revalidate, .d_release = ceph_dentry_release, }; -struct dentry_operations ceph_snapdir_dentry_ops = { +const struct dentry_operations ceph_snapdir_dentry_ops = { .d_revalidate = ceph_snapdir_d_revalidate, .d_release = ceph_dentry_release, }; -struct dentry_operations ceph_snap_dentry_ops = { +const struct dentry_operations ceph_snap_dentry_ops = { .d_release = ceph_dentry_release, }; diff --git a/fs/ceph/super.h b/fs/ceph/super.h index 95adce441cc0..2482d696f0de 100644 --- a/fs/ceph/super.h +++ b/fs/ceph/super.h @@ -861,7 +861,7 @@ extern void ceph_release_page_vector(struct page **pages, int num_pages); /* dir.c */ extern const struct file_operations ceph_dir_fops; extern const struct inode_operations ceph_dir_iops; -extern struct dentry_operations ceph_dentry_ops, ceph_snap_dentry_ops, +extern const struct dentry_operations ceph_dentry_ops, ceph_snap_dentry_ops, ceph_snapdir_dentry_ops; extern int ceph_handle_notrace_create(struct inode *dir, struct dentry *dentry); -- cgit v1.2.3 From e9d177443134bc4ac1c1393af69e2a8704bcac09 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Mon, 2 Aug 2010 16:23:49 -0700 Subject: ceph: do not ignore osd_idle_ttl mount option Actually apply the mount option to the mount_args struct. Signed-off-by: Sage Weil --- fs/ceph/super.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'fs') diff --git a/fs/ceph/super.c b/fs/ceph/super.c index 7f751f2850ba..9922628532b2 100644 --- a/fs/ceph/super.c +++ b/fs/ceph/super.c @@ -548,6 +548,9 @@ static struct ceph_mount_args *parse_mount_args(int flags, char *options, case Opt_osdkeepalivetimeout: args->osd_keepalive_timeout = intval; break; + case Opt_osd_idle_ttl: + args->osd_idle_ttl = intval; + break; case Opt_mount_timeout: args->mount_timeout = intval; break; -- cgit v1.2.3 From a931da6ac9331a6c80dd91c199105806f2336188 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Tue, 3 Aug 2010 21:35:12 -0400 Subject: jbd2: Change j_state_lock to be a rwlock_t Lockstat reports have shown that j_state_lock is a major source of lock contention, especially on systems with more than 4 CPU cores. So change it to be a read/write spinlock. Signed-off-by: "Theodore Ts'o" --- fs/ext4/inode.c | 4 +-- fs/ext4/super.c | 4 +-- fs/jbd2/checkpoint.c | 16 ++++----- fs/jbd2/commit.c | 26 +++++++------- fs/jbd2/journal.c | 94 +++++++++++++++++++++++++-------------------------- fs/jbd2/transaction.c | 74 +++++++++++++++++++++------------------- fs/ocfs2/journal.c | 4 +-- include/linux/jbd2.h | 2 +- 8 files changed, 114 insertions(+), 110 deletions(-) (limited to 'fs') diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 533b607f9cb5..ab2247d642c6 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -5066,7 +5066,7 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino) transaction_t *transaction; tid_t tid; - spin_lock(&journal->j_state_lock); + read_lock(&journal->j_state_lock); if (journal->j_running_transaction) transaction = journal->j_running_transaction; else @@ -5075,7 +5075,7 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino) tid = transaction->t_tid; else tid = journal->j_commit_sequence; - spin_unlock(&journal->j_state_lock); + read_unlock(&journal->j_state_lock); ei->i_sync_tid = tid; ei->i_datasync_tid = tid; } diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 3fd65eb66ccd..81cb3fc1218e 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -3232,7 +3232,7 @@ static void ext4_init_journal_params(struct super_block *sb, journal_t *journal) journal->j_min_batch_time = sbi->s_min_batch_time; journal->j_max_batch_time = sbi->s_max_batch_time; - spin_lock(&journal->j_state_lock); + write_lock(&journal->j_state_lock); if (test_opt(sb, BARRIER)) journal->j_flags |= JBD2_BARRIER; else @@ -3241,7 +3241,7 @@ static void ext4_init_journal_params(struct super_block *sb, journal_t *journal) journal->j_flags |= JBD2_ABORT_ON_SYNCDATA_ERR; else journal->j_flags &= ~JBD2_ABORT_ON_SYNCDATA_ERR; - spin_unlock(&journal->j_state_lock); + write_unlock(&journal->j_state_lock); } static journal_t *ext4_get_journal(struct super_block *sb, diff --git a/fs/jbd2/checkpoint.c b/fs/jbd2/checkpoint.c index f8cdc02520f9..1c23a0f4e8a3 100644 --- a/fs/jbd2/checkpoint.c +++ b/fs/jbd2/checkpoint.c @@ -118,13 +118,13 @@ static int __try_to_free_cp_buf(struct journal_head *jh) void __jbd2_log_wait_for_space(journal_t *journal) { int nblocks, space_left; - assert_spin_locked(&journal->j_state_lock); + /* assert_spin_locked(&journal->j_state_lock); */ nblocks = jbd_space_needed(journal); while (__jbd2_log_space_left(journal) < nblocks) { if (journal->j_flags & JBD2_ABORT) return; - spin_unlock(&journal->j_state_lock); + write_unlock(&journal->j_state_lock); mutex_lock(&journal->j_checkpoint_mutex); /* @@ -138,7 +138,7 @@ void __jbd2_log_wait_for_space(journal_t *journal) * filesystem, so abort the journal and leave a stack * trace for forensic evidence. */ - spin_lock(&journal->j_state_lock); + write_lock(&journal->j_state_lock); spin_lock(&journal->j_list_lock); nblocks = jbd_space_needed(journal); space_left = __jbd2_log_space_left(journal); @@ -149,7 +149,7 @@ void __jbd2_log_wait_for_space(journal_t *journal) if (journal->j_committing_transaction) tid = journal->j_committing_transaction->t_tid; spin_unlock(&journal->j_list_lock); - spin_unlock(&journal->j_state_lock); + write_unlock(&journal->j_state_lock); if (chkpt) { jbd2_log_do_checkpoint(journal); } else if (jbd2_cleanup_journal_tail(journal) == 0) { @@ -167,7 +167,7 @@ void __jbd2_log_wait_for_space(journal_t *journal) WARN_ON(1); jbd2_journal_abort(journal, 0); } - spin_lock(&journal->j_state_lock); + write_lock(&journal->j_state_lock); } else { spin_unlock(&journal->j_list_lock); } @@ -474,7 +474,7 @@ int jbd2_cleanup_journal_tail(journal_t *journal) * next transaction ID we will write, and where it will * start. */ - spin_lock(&journal->j_state_lock); + write_lock(&journal->j_state_lock); spin_lock(&journal->j_list_lock); transaction = journal->j_checkpoint_transactions; if (transaction) { @@ -496,7 +496,7 @@ int jbd2_cleanup_journal_tail(journal_t *journal) /* If the oldest pinned transaction is at the tail of the log already then there's not much we can do right now. */ if (journal->j_tail_sequence == first_tid) { - spin_unlock(&journal->j_state_lock); + write_unlock(&journal->j_state_lock); return 1; } @@ -516,7 +516,7 @@ int jbd2_cleanup_journal_tail(journal_t *journal) journal->j_free += freed; journal->j_tail_sequence = first_tid; journal->j_tail = blocknr; - spin_unlock(&journal->j_state_lock); + write_unlock(&journal->j_state_lock); /* * If there is an external journal, we need to make sure that diff --git a/fs/jbd2/commit.c b/fs/jbd2/commit.c index fbd2c564e916..67bb0a2f35e5 100644 --- a/fs/jbd2/commit.c +++ b/fs/jbd2/commit.c @@ -152,9 +152,9 @@ static int journal_submit_commit_record(journal_t *journal, printk(KERN_WARNING "JBD2: Disabling barriers on %s, " "not supported by device\n", journal->j_devname); - spin_lock(&journal->j_state_lock); + write_lock(&journal->j_state_lock); journal->j_flags &= ~JBD2_BARRIER; - spin_unlock(&journal->j_state_lock); + write_unlock(&journal->j_state_lock); /* And try again, without the barrier */ lock_buffer(bh); @@ -182,9 +182,9 @@ retry: printk(KERN_WARNING "JBD2: %s: disabling barries on %s - not supported " "by device\n", __func__, journal->j_devname); - spin_lock(&journal->j_state_lock); + write_lock(&journal->j_state_lock); journal->j_flags &= ~JBD2_BARRIER; - spin_unlock(&journal->j_state_lock); + write_unlock(&journal->j_state_lock); lock_buffer(bh); clear_buffer_dirty(bh); @@ -400,7 +400,7 @@ void jbd2_journal_commit_transaction(journal_t *journal) jbd_debug(1, "JBD: starting commit of transaction %d\n", commit_transaction->t_tid); - spin_lock(&journal->j_state_lock); + write_lock(&journal->j_state_lock); commit_transaction->t_state = T_LOCKED; /* @@ -424,9 +424,9 @@ void jbd2_journal_commit_transaction(journal_t *journal) TASK_UNINTERRUPTIBLE); if (atomic_read(&commit_transaction->t_updates)) { spin_unlock(&commit_transaction->t_handle_lock); - spin_unlock(&journal->j_state_lock); + write_unlock(&journal->j_state_lock); schedule(); - spin_lock(&journal->j_state_lock); + write_lock(&journal->j_state_lock); spin_lock(&commit_transaction->t_handle_lock); } finish_wait(&journal->j_wait_updates, &wait); @@ -497,7 +497,7 @@ void jbd2_journal_commit_transaction(journal_t *journal) start_time = ktime_get(); commit_transaction->t_log_start = journal->j_head; wake_up(&journal->j_wait_transaction_locked); - spin_unlock(&journal->j_state_lock); + write_unlock(&journal->j_state_lock); jbd_debug (3, "JBD: commit phase 2\n"); @@ -519,9 +519,9 @@ void jbd2_journal_commit_transaction(journal_t *journal) * transaction! Now comes the tricky part: we need to write out * metadata. Loop over the transaction's entire buffer list: */ - spin_lock(&journal->j_state_lock); + write_lock(&journal->j_state_lock); commit_transaction->t_state = T_COMMIT; - spin_unlock(&journal->j_state_lock); + write_unlock(&journal->j_state_lock); trace_jbd2_commit_logging(journal, commit_transaction); stats.run.rs_logging = jiffies; @@ -978,7 +978,7 @@ restart_loop: * __jbd2_journal_drop_transaction(). Otherwise we could race with * other checkpointing code processing the transaction... */ - spin_lock(&journal->j_state_lock); + write_lock(&journal->j_state_lock); spin_lock(&journal->j_list_lock); /* * Now recheck if some buffers did not get attached to the transaction @@ -986,7 +986,7 @@ restart_loop: */ if (commit_transaction->t_forget) { spin_unlock(&journal->j_list_lock); - spin_unlock(&journal->j_state_lock); + write_unlock(&journal->j_state_lock); goto restart_loop; } @@ -1038,7 +1038,7 @@ restart_loop: journal->j_average_commit_time*3) / 4; else journal->j_average_commit_time = commit_time; - spin_unlock(&journal->j_state_lock); + write_unlock(&journal->j_state_lock); if (commit_transaction->t_checkpoint_list == NULL && commit_transaction->t_checkpoint_io_list == NULL) { diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c index a79d3345b55a..e7bf0fd9cec7 100644 --- a/fs/jbd2/journal.c +++ b/fs/jbd2/journal.c @@ -142,7 +142,7 @@ static int kjournald2(void *arg) /* * And now, wait forever for commit wakeup events. */ - spin_lock(&journal->j_state_lock); + write_lock(&journal->j_state_lock); loop: if (journal->j_flags & JBD2_UNMOUNT) @@ -153,10 +153,10 @@ loop: if (journal->j_commit_sequence != journal->j_commit_request) { jbd_debug(1, "OK, requests differ\n"); - spin_unlock(&journal->j_state_lock); + write_unlock(&journal->j_state_lock); del_timer_sync(&journal->j_commit_timer); jbd2_journal_commit_transaction(journal); - spin_lock(&journal->j_state_lock); + write_lock(&journal->j_state_lock); goto loop; } @@ -168,9 +168,9 @@ loop: * be already stopped. */ jbd_debug(1, "Now suspending kjournald2\n"); - spin_unlock(&journal->j_state_lock); + write_unlock(&journal->j_state_lock); refrigerator(); - spin_lock(&journal->j_state_lock); + write_lock(&journal->j_state_lock); } else { /* * We assume on resume that commits are already there, @@ -190,9 +190,9 @@ loop: if (journal->j_flags & JBD2_UNMOUNT) should_sleep = 0; if (should_sleep) { - spin_unlock(&journal->j_state_lock); + write_unlock(&journal->j_state_lock); schedule(); - spin_lock(&journal->j_state_lock); + write_lock(&journal->j_state_lock); } finish_wait(&journal->j_wait_commit, &wait); } @@ -210,7 +210,7 @@ loop: goto loop; end_loop: - spin_unlock(&journal->j_state_lock); + write_unlock(&journal->j_state_lock); del_timer_sync(&journal->j_commit_timer); journal->j_task = NULL; wake_up(&journal->j_wait_done_commit); @@ -233,16 +233,16 @@ static int jbd2_journal_start_thread(journal_t *journal) static void journal_kill_thread(journal_t *journal) { - spin_lock(&journal->j_state_lock); + write_lock(&journal->j_state_lock); journal->j_flags |= JBD2_UNMOUNT; while (journal->j_task) { wake_up(&journal->j_wait_commit); - spin_unlock(&journal->j_state_lock); + write_unlock(&journal->j_state_lock); wait_event(journal->j_wait_done_commit, journal->j_task == NULL); - spin_lock(&journal->j_state_lock); + write_lock(&journal->j_state_lock); } - spin_unlock(&journal->j_state_lock); + write_unlock(&journal->j_state_lock); } /* @@ -452,7 +452,7 @@ int __jbd2_log_space_left(journal_t *journal) { int left = journal->j_free; - assert_spin_locked(&journal->j_state_lock); + /* assert_spin_locked(&journal->j_state_lock); */ /* * Be pessimistic here about the number of those free blocks which @@ -497,9 +497,9 @@ int jbd2_log_start_commit(journal_t *journal, tid_t tid) { int ret; - spin_lock(&journal->j_state_lock); + write_lock(&journal->j_state_lock); ret = __jbd2_log_start_commit(journal, tid); - spin_unlock(&journal->j_state_lock); + write_unlock(&journal->j_state_lock); return ret; } @@ -518,7 +518,7 @@ int jbd2_journal_force_commit_nested(journal_t *journal) transaction_t *transaction = NULL; tid_t tid; - spin_lock(&journal->j_state_lock); + read_lock(&journal->j_state_lock); if (journal->j_running_transaction && !current->journal_info) { transaction = journal->j_running_transaction; __jbd2_log_start_commit(journal, transaction->t_tid); @@ -526,12 +526,12 @@ int jbd2_journal_force_commit_nested(journal_t *journal) transaction = journal->j_committing_transaction; if (!transaction) { - spin_unlock(&journal->j_state_lock); + read_unlock(&journal->j_state_lock); return 0; /* Nothing to retry */ } tid = transaction->t_tid; - spin_unlock(&journal->j_state_lock); + read_unlock(&journal->j_state_lock); jbd2_log_wait_commit(journal, tid); return 1; } @@ -545,7 +545,7 @@ int jbd2_journal_start_commit(journal_t *journal, tid_t *ptid) { int ret = 0; - spin_lock(&journal->j_state_lock); + write_lock(&journal->j_state_lock); if (journal->j_running_transaction) { tid_t tid = journal->j_running_transaction->t_tid; @@ -564,7 +564,7 @@ int jbd2_journal_start_commit(journal_t *journal, tid_t *ptid) *ptid = journal->j_committing_transaction->t_tid; ret = 1; } - spin_unlock(&journal->j_state_lock); + write_unlock(&journal->j_state_lock); return ret; } @@ -576,26 +576,24 @@ int jbd2_log_wait_commit(journal_t *journal, tid_t tid) { int err = 0; + read_lock(&journal->j_state_lock); #ifdef CONFIG_JBD2_DEBUG - spin_lock(&journal->j_state_lock); if (!tid_geq(journal->j_commit_request, tid)) { printk(KERN_EMERG "%s: error: j_commit_request=%d, tid=%d\n", __func__, journal->j_commit_request, tid); } - spin_unlock(&journal->j_state_lock); #endif - spin_lock(&journal->j_state_lock); while (tid_gt(tid, journal->j_commit_sequence)) { jbd_debug(1, "JBD: want %d, j_commit_sequence=%d\n", tid, journal->j_commit_sequence); wake_up(&journal->j_wait_commit); - spin_unlock(&journal->j_state_lock); + read_unlock(&journal->j_state_lock); wait_event(journal->j_wait_done_commit, !tid_gt(tid, journal->j_commit_sequence)); - spin_lock(&journal->j_state_lock); + read_lock(&journal->j_state_lock); } - spin_unlock(&journal->j_state_lock); + read_unlock(&journal->j_state_lock); if (unlikely(is_journal_aborted(journal))) { printk(KERN_EMERG "journal commit I/O error\n"); @@ -612,7 +610,7 @@ int jbd2_journal_next_log_block(journal_t *journal, unsigned long long *retp) { unsigned long blocknr; - spin_lock(&journal->j_state_lock); + write_lock(&journal->j_state_lock); J_ASSERT(journal->j_free > 1); blocknr = journal->j_head; @@ -620,7 +618,7 @@ int jbd2_journal_next_log_block(journal_t *journal, unsigned long long *retp) journal->j_free--; if (journal->j_head == journal->j_last) journal->j_head = journal->j_first; - spin_unlock(&journal->j_state_lock); + write_unlock(&journal->j_state_lock); return jbd2_journal_bmap(journal, blocknr, retp); } @@ -840,7 +838,7 @@ static journal_t * journal_init_common (void) mutex_init(&journal->j_checkpoint_mutex); spin_lock_init(&journal->j_revoke_lock); spin_lock_init(&journal->j_list_lock); - spin_lock_init(&journal->j_state_lock); + rwlock_init(&journal->j_state_lock); journal->j_commit_interval = (HZ * JBD2_DEFAULT_MAX_COMMIT_AGE); journal->j_min_batch_time = 0; @@ -1106,14 +1104,14 @@ void jbd2_journal_update_superblock(journal_t *journal, int wait) set_buffer_uptodate(bh); } - spin_lock(&journal->j_state_lock); + read_lock(&journal->j_state_lock); jbd_debug(1,"JBD: updating superblock (start %ld, seq %d, errno %d)\n", journal->j_tail, journal->j_tail_sequence, journal->j_errno); sb->s_sequence = cpu_to_be32(journal->j_tail_sequence); sb->s_start = cpu_to_be32(journal->j_tail); sb->s_errno = cpu_to_be32(journal->j_errno); - spin_unlock(&journal->j_state_lock); + read_unlock(&journal->j_state_lock); BUFFER_TRACE(bh, "marking dirty"); mark_buffer_dirty(bh); @@ -1134,12 +1132,12 @@ out: * any future commit will have to be careful to update the * superblock again to re-record the true start of the log. */ - spin_lock(&journal->j_state_lock); + write_lock(&journal->j_state_lock); if (sb->s_start) journal->j_flags &= ~JBD2_FLUSHED; else journal->j_flags |= JBD2_FLUSHED; - spin_unlock(&journal->j_state_lock); + write_unlock(&journal->j_state_lock); } /* @@ -1551,7 +1549,7 @@ int jbd2_journal_flush(journal_t *journal) transaction_t *transaction = NULL; unsigned long old_tail; - spin_lock(&journal->j_state_lock); + write_lock(&journal->j_state_lock); /* Force everything buffered to the log... */ if (journal->j_running_transaction) { @@ -1564,10 +1562,10 @@ int jbd2_journal_flush(journal_t *journal) if (transaction) { tid_t tid = transaction->t_tid; - spin_unlock(&journal->j_state_lock); + write_unlock(&journal->j_state_lock); jbd2_log_wait_commit(journal, tid); } else { - spin_unlock(&journal->j_state_lock); + write_unlock(&journal->j_state_lock); } /* ...and flush everything in the log out to disk. */ @@ -1591,12 +1589,12 @@ int jbd2_journal_flush(journal_t *journal) * the magic code for a fully-recovered superblock. Any future * commits of data to the journal will restore the current * s_start value. */ - spin_lock(&journal->j_state_lock); + write_lock(&journal->j_state_lock); old_tail = journal->j_tail; journal->j_tail = 0; - spin_unlock(&journal->j_state_lock); + write_unlock(&journal->j_state_lock); jbd2_journal_update_superblock(journal, 1); - spin_lock(&journal->j_state_lock); + write_lock(&journal->j_state_lock); journal->j_tail = old_tail; J_ASSERT(!journal->j_running_transaction); @@ -1604,7 +1602,7 @@ int jbd2_journal_flush(journal_t *journal) J_ASSERT(!journal->j_checkpoint_transactions); J_ASSERT(journal->j_head == journal->j_tail); J_ASSERT(journal->j_tail_sequence == journal->j_transaction_sequence); - spin_unlock(&journal->j_state_lock); + write_unlock(&journal->j_state_lock); return 0; } @@ -1668,12 +1666,12 @@ void __jbd2_journal_abort_hard(journal_t *journal) printk(KERN_ERR "Aborting journal on device %s.\n", journal->j_devname); - spin_lock(&journal->j_state_lock); + write_lock(&journal->j_state_lock); journal->j_flags |= JBD2_ABORT; transaction = journal->j_running_transaction; if (transaction) __jbd2_log_start_commit(journal, transaction->t_tid); - spin_unlock(&journal->j_state_lock); + write_unlock(&journal->j_state_lock); } /* Soft abort: record the abort error status in the journal superblock, @@ -1758,12 +1756,12 @@ int jbd2_journal_errno(journal_t *journal) { int err; - spin_lock(&journal->j_state_lock); + read_lock(&journal->j_state_lock); if (journal->j_flags & JBD2_ABORT) err = -EROFS; else err = journal->j_errno; - spin_unlock(&journal->j_state_lock); + read_unlock(&journal->j_state_lock); return err; } @@ -1778,12 +1776,12 @@ int jbd2_journal_clear_err(journal_t *journal) { int err = 0; - spin_lock(&journal->j_state_lock); + write_lock(&journal->j_state_lock); if (journal->j_flags & JBD2_ABORT) err = -EROFS; else journal->j_errno = 0; - spin_unlock(&journal->j_state_lock); + write_unlock(&journal->j_state_lock); return err; } @@ -1796,10 +1794,10 @@ int jbd2_journal_clear_err(journal_t *journal) */ void jbd2_journal_ack_err(journal_t *journal) { - spin_lock(&journal->j_state_lock); + write_lock(&journal->j_state_lock); if (journal->j_errno) journal->j_flags |= JBD2_ACK_ERR; - spin_unlock(&journal->j_state_lock); + write_unlock(&journal->j_state_lock); } int jbd2_journal_blocks_per_page(struct inode *inode) diff --git a/fs/jbd2/transaction.c b/fs/jbd2/transaction.c index 9c64c7ec48d4..663065142b42 100644 --- a/fs/jbd2/transaction.c +++ b/fs/jbd2/transaction.c @@ -124,36 +124,38 @@ alloc_transaction: jbd_debug(3, "New handle %p going live.\n", handle); -repeat: - /* * We need to hold j_state_lock until t_updates has been incremented, * for proper journal barrier handling */ - spin_lock(&journal->j_state_lock); -repeat_locked: +repeat: + read_lock(&journal->j_state_lock); if (is_journal_aborted(journal) || (journal->j_errno != 0 && !(journal->j_flags & JBD2_ACK_ERR))) { - spin_unlock(&journal->j_state_lock); + read_unlock(&journal->j_state_lock); kfree(new_transaction); return -EROFS; } /* Wait on the journal's transaction barrier if necessary */ if (journal->j_barrier_count) { - spin_unlock(&journal->j_state_lock); + read_unlock(&journal->j_state_lock); wait_event(journal->j_wait_transaction_locked, journal->j_barrier_count == 0); goto repeat; } if (!journal->j_running_transaction) { - if (!new_transaction) { - spin_unlock(&journal->j_state_lock); + read_unlock(&journal->j_state_lock); + if (!new_transaction) goto alloc_transaction; + write_lock(&journal->j_state_lock); + if (!journal->j_running_transaction) { + jbd2_get_transaction(journal, new_transaction); + new_transaction = NULL; } - jbd2_get_transaction(journal, new_transaction); - new_transaction = NULL; + write_unlock(&journal->j_state_lock); + goto repeat; } transaction = journal->j_running_transaction; @@ -167,7 +169,7 @@ repeat_locked: prepare_to_wait(&journal->j_wait_transaction_locked, &wait, TASK_UNINTERRUPTIBLE); - spin_unlock(&journal->j_state_lock); + read_unlock(&journal->j_state_lock); schedule(); finish_wait(&journal->j_wait_transaction_locked, &wait); goto repeat; @@ -194,7 +196,7 @@ repeat_locked: prepare_to_wait(&journal->j_wait_transaction_locked, &wait, TASK_UNINTERRUPTIBLE); __jbd2_log_start_commit(journal, transaction->t_tid); - spin_unlock(&journal->j_state_lock); + read_unlock(&journal->j_state_lock); schedule(); finish_wait(&journal->j_wait_transaction_locked, &wait); goto repeat; @@ -228,8 +230,12 @@ repeat_locked: if (__jbd2_log_space_left(journal) < jbd_space_needed(journal)) { jbd_debug(2, "Handle %p waiting for checkpoint...\n", handle); spin_unlock(&transaction->t_handle_lock); - __jbd2_log_wait_for_space(journal); - goto repeat_locked; + read_unlock(&journal->j_state_lock); + write_lock(&journal->j_state_lock); + if (__jbd2_log_space_left(journal) < jbd_space_needed(journal)) + __jbd2_log_wait_for_space(journal); + write_unlock(&journal->j_state_lock); + goto repeat; } /* OK, account for the buffers that this operation expects to @@ -250,7 +256,7 @@ repeat_locked: atomic_read(&transaction->t_outstanding_credits), __jbd2_log_space_left(journal)); spin_unlock(&transaction->t_handle_lock); - spin_unlock(&journal->j_state_lock); + read_unlock(&journal->j_state_lock); lock_map_acquire(&handle->h_lockdep_map); kfree(new_transaction); @@ -362,7 +368,7 @@ int jbd2_journal_extend(handle_t *handle, int nblocks) result = 1; - spin_lock(&journal->j_state_lock); + read_lock(&journal->j_state_lock); /* Don't extend a locked-down transaction! */ if (handle->h_transaction->t_state != T_RUNNING) { @@ -394,7 +400,7 @@ int jbd2_journal_extend(handle_t *handle, int nblocks) unlock: spin_unlock(&transaction->t_handle_lock); error_out: - spin_unlock(&journal->j_state_lock); + read_unlock(&journal->j_state_lock); out: return result; } @@ -432,7 +438,7 @@ int jbd2__journal_restart(handle_t *handle, int nblocks, int gfp_mask) J_ASSERT(atomic_read(&transaction->t_updates) > 0); J_ASSERT(journal_current_handle() == handle); - spin_lock(&journal->j_state_lock); + read_lock(&journal->j_state_lock); spin_lock(&transaction->t_handle_lock); atomic_sub(handle->h_buffer_credits, &transaction->t_outstanding_credits); @@ -442,7 +448,7 @@ int jbd2__journal_restart(handle_t *handle, int nblocks, int gfp_mask) jbd_debug(2, "restarting handle %p\n", handle); __jbd2_log_start_commit(journal, transaction->t_tid); - spin_unlock(&journal->j_state_lock); + read_unlock(&journal->j_state_lock); lock_map_release(&handle->h_lockdep_map); handle->h_buffer_credits = nblocks; @@ -472,7 +478,7 @@ void jbd2_journal_lock_updates(journal_t *journal) { DEFINE_WAIT(wait); - spin_lock(&journal->j_state_lock); + write_lock(&journal->j_state_lock); ++journal->j_barrier_count; /* Wait until there are no running updates */ @@ -490,12 +496,12 @@ void jbd2_journal_lock_updates(journal_t *journal) prepare_to_wait(&journal->j_wait_updates, &wait, TASK_UNINTERRUPTIBLE); spin_unlock(&transaction->t_handle_lock); - spin_unlock(&journal->j_state_lock); + write_unlock(&journal->j_state_lock); schedule(); finish_wait(&journal->j_wait_updates, &wait); - spin_lock(&journal->j_state_lock); + write_lock(&journal->j_state_lock); } - spin_unlock(&journal->j_state_lock); + write_unlock(&journal->j_state_lock); /* * We have now established a barrier against other normal updates, but @@ -519,9 +525,9 @@ void jbd2_journal_unlock_updates (journal_t *journal) J_ASSERT(journal->j_barrier_count != 0); mutex_unlock(&journal->j_barrier); - spin_lock(&journal->j_state_lock); + write_lock(&journal->j_state_lock); --journal->j_barrier_count; - spin_unlock(&journal->j_state_lock); + write_unlock(&journal->j_state_lock); wake_up(&journal->j_wait_transaction_locked); } @@ -1314,9 +1320,9 @@ int jbd2_journal_stop(handle_t *handle) journal->j_last_sync_writer = pid; - spin_lock(&journal->j_state_lock); + read_lock(&journal->j_state_lock); commit_time = journal->j_average_commit_time; - spin_unlock(&journal->j_state_lock); + read_unlock(&journal->j_state_lock); trans_time = ktime_to_ns(ktime_sub(ktime_get(), transaction->t_start_time)); @@ -1748,7 +1754,7 @@ static int journal_unmap_buffer(journal_t *journal, struct buffer_head *bh) goto zap_buffer_unlocked; /* OK, we have data buffer in journaled mode */ - spin_lock(&journal->j_state_lock); + write_lock(&journal->j_state_lock); jbd_lock_bh_state(bh); spin_lock(&journal->j_list_lock); @@ -1801,7 +1807,7 @@ static int journal_unmap_buffer(journal_t *journal, struct buffer_head *bh) jbd2_journal_put_journal_head(jh); spin_unlock(&journal->j_list_lock); jbd_unlock_bh_state(bh); - spin_unlock(&journal->j_state_lock); + write_unlock(&journal->j_state_lock); return ret; } else { /* There is no currently-running transaction. So the @@ -1815,7 +1821,7 @@ static int journal_unmap_buffer(journal_t *journal, struct buffer_head *bh) jbd2_journal_put_journal_head(jh); spin_unlock(&journal->j_list_lock); jbd_unlock_bh_state(bh); - spin_unlock(&journal->j_state_lock); + write_unlock(&journal->j_state_lock); return ret; } else { /* The orphan record's transaction has @@ -1839,7 +1845,7 @@ static int journal_unmap_buffer(journal_t *journal, struct buffer_head *bh) jbd2_journal_put_journal_head(jh); spin_unlock(&journal->j_list_lock); jbd_unlock_bh_state(bh); - spin_unlock(&journal->j_state_lock); + write_unlock(&journal->j_state_lock); return 0; } else { /* Good, the buffer belongs to the running transaction. @@ -1858,7 +1864,7 @@ zap_buffer: zap_buffer_no_jh: spin_unlock(&journal->j_list_lock); jbd_unlock_bh_state(bh); - spin_unlock(&journal->j_state_lock); + write_unlock(&journal->j_state_lock); zap_buffer_unlocked: clear_buffer_dirty(bh); J_ASSERT_BH(bh, !buffer_jbddirty(bh)); @@ -2165,9 +2171,9 @@ int jbd2_journal_begin_ordered_truncate(journal_t *journal, /* Locks are here just to force reading of recent values, it is * enough that the transaction was not committing before we started * a transaction adding the inode to orphan list */ - spin_lock(&journal->j_state_lock); + read_lock(&journal->j_state_lock); commit_trans = journal->j_committing_transaction; - spin_unlock(&journal->j_state_lock); + read_unlock(&journal->j_state_lock); spin_lock(&journal->j_list_lock); inode_trans = jinode->i_transaction; spin_unlock(&journal->j_list_lock); diff --git a/fs/ocfs2/journal.c b/fs/ocfs2/journal.c index 47878cf16418..9c1b92ebeb94 100644 --- a/fs/ocfs2/journal.c +++ b/fs/ocfs2/journal.c @@ -760,13 +760,13 @@ void ocfs2_set_journal_params(struct ocfs2_super *osb) if (osb->osb_commit_interval) commit_interval = osb->osb_commit_interval; - spin_lock(&journal->j_state_lock); + write_lock(&journal->j_state_lock); journal->j_commit_interval = commit_interval; if (osb->s_mount_opt & OCFS2_MOUNT_BARRIER) journal->j_flags |= JBD2_BARRIER; else journal->j_flags &= ~JBD2_BARRIER; - spin_unlock(&journal->j_state_lock); + write_unlock(&journal->j_state_lock); } int ocfs2_journal_init(struct ocfs2_journal *journal, int *dirty) diff --git a/include/linux/jbd2.h b/include/linux/jbd2.h index a72ce21de0e1..15d5743ccfbb 100644 --- a/include/linux/jbd2.h +++ b/include/linux/jbd2.h @@ -764,7 +764,7 @@ struct journal_s /* * Protect the various scalars in the journal */ - spinlock_t j_state_lock; + rwlock_t j_state_lock; /* * Number of processes waiting to create a barrier lock [j_state_lock] -- cgit v1.2.3 From 8dd420466c7bfc459fa04680bd5690bfc41a4553 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Tue, 3 Aug 2010 21:38:29 -0400 Subject: jbd2: Remove t_handle_lock from start_this_handle() This should remove the last exclusive lock from start_this_handle(), so that we should now be able to start multiple transactions at the same time on large SMP systems. Signed-off-by: "Theodore Ts'o" --- fs/jbd2/commit.c | 3 ++- fs/jbd2/transaction.c | 33 ++++++++++++++++++++++----------- include/linux/jbd2.h | 2 +- 3 files changed, 25 insertions(+), 13 deletions(-) (limited to 'fs') diff --git a/fs/jbd2/commit.c b/fs/jbd2/commit.c index 67bb0a2f35e5..f52e5e8049f1 100644 --- a/fs/jbd2/commit.c +++ b/fs/jbd2/commit.c @@ -1004,7 +1004,8 @@ restart_loop: * File the transaction statistics */ stats.ts_tid = commit_transaction->t_tid; - stats.run.rs_handle_count = commit_transaction->t_handle_count; + stats.run.rs_handle_count = + atomic_read(&commit_transaction->t_handle_count); trace_jbd2_run_stats(journal->j_fs_dev->bd_dev, commit_transaction->t_tid, &stats.run); diff --git a/fs/jbd2/transaction.c b/fs/jbd2/transaction.c index 663065142b42..0752bcda535f 100644 --- a/fs/jbd2/transaction.c +++ b/fs/jbd2/transaction.c @@ -57,6 +57,7 @@ jbd2_get_transaction(journal_t *journal, transaction_t *transaction) spin_lock_init(&transaction->t_handle_lock); atomic_set(&transaction->t_updates, 0); atomic_set(&transaction->t_outstanding_credits, 0); + atomic_set(&transaction->t_handle_count, 0); INIT_LIST_HEAD(&transaction->t_inode_list); INIT_LIST_HEAD(&transaction->t_private_list); @@ -180,8 +181,8 @@ repeat: * buffers requested by this operation, we need to stall pending a log * checkpoint to free some more log space. */ - spin_lock(&transaction->t_handle_lock); - needed = atomic_read(&transaction->t_outstanding_credits) + nblocks; + needed = atomic_add_return(nblocks, + &transaction->t_outstanding_credits); if (needed > journal->j_max_transaction_buffers) { /* @@ -192,7 +193,7 @@ repeat: DEFINE_WAIT(wait); jbd_debug(2, "Handle %p starting new commit...\n", handle); - spin_unlock(&transaction->t_handle_lock); + atomic_sub(nblocks, &transaction->t_outstanding_credits); prepare_to_wait(&journal->j_wait_transaction_locked, &wait, TASK_UNINTERRUPTIBLE); __jbd2_log_start_commit(journal, transaction->t_tid); @@ -229,7 +230,7 @@ repeat: */ if (__jbd2_log_space_left(journal) < jbd_space_needed(journal)) { jbd_debug(2, "Handle %p waiting for checkpoint...\n", handle); - spin_unlock(&transaction->t_handle_lock); + atomic_sub(nblocks, &transaction->t_outstanding_credits); read_unlock(&journal->j_state_lock); write_lock(&journal->j_state_lock); if (__jbd2_log_space_left(journal) < jbd_space_needed(journal)) @@ -239,23 +240,33 @@ repeat: } /* OK, account for the buffers that this operation expects to - * use and add the handle to the running transaction. */ - - if (time_after(transaction->t_start, ts)) { + * use and add the handle to the running transaction. + * + * In order for t_max_wait to be reliable, it must be + * protected by a lock. But doing so will mean that + * start_this_handle() can not be run in parallel on SMP + * systems, which limits our scalability. So we only enable + * it when debugging is enabled. We may want to use a + * separate flag, eventually, so we can enable this + * independently of debugging. + */ +#ifdef CONFIG_JBD2_DEBUG + if (jbd2_journal_enable_debug && + time_after(transaction->t_start, ts)) { ts = jbd2_time_diff(ts, transaction->t_start); + spin_lock(&transaction->t_handle_lock); if (ts > transaction->t_max_wait) transaction->t_max_wait = ts; + spin_unlock(&transaction->t_handle_lock); } - +#endif handle->h_transaction = transaction; - atomic_add(nblocks, &transaction->t_outstanding_credits); atomic_inc(&transaction->t_updates); - transaction->t_handle_count++; + atomic_inc(&transaction->t_handle_count); jbd_debug(4, "Handle %p given %d credits (total %d, free %d)\n", handle, nblocks, atomic_read(&transaction->t_outstanding_credits), __jbd2_log_space_left(journal)); - spin_unlock(&transaction->t_handle_lock); read_unlock(&journal->j_state_lock); lock_map_acquire(&handle->h_lockdep_map); diff --git a/include/linux/jbd2.h b/include/linux/jbd2.h index 15d5743ccfbb..01743b5446ff 100644 --- a/include/linux/jbd2.h +++ b/include/linux/jbd2.h @@ -629,7 +629,7 @@ struct transaction_s /* * How many handles used this transaction? [t_handle_lock] */ - int t_handle_count; + atomic_t t_handle_count; /* * This transaction is being forced and some process is -- cgit v1.2.3 From af7fa16506bf9b6323e862a61e14c20555152bb3 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 31 Jul 2010 14:29:06 -0400 Subject: NFS: Fix up the fsync code Christoph points out that the VFS will always flush out data before calling nfs_fsync(), so we can dispense with a full call to nfs_wb_all(), and replace that with a simpler call to nfs_commit_inode(). Signed-off-by: Trond Myklebust --- fs/nfs/file.c | 51 +++++++++++++++++++++------------------------------ 1 file changed, 21 insertions(+), 30 deletions(-) (limited to 'fs') diff --git a/fs/nfs/file.c b/fs/nfs/file.c index 36a5e74f51b4..f36581cd4767 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -201,38 +201,12 @@ static loff_t nfs_file_llseek(struct file *filp, loff_t offset, int origin) return loff; } -/* - * Helper for nfs_file_flush() and nfs_file_fsync() - * - * Notice that it clears the NFS_CONTEXT_ERROR_WRITE before synching to - * disk, but it retrieves and clears ctx->error after synching, despite - * the two being set at the same time in nfs_context_set_write_error(). - * This is because the former is used to notify the _next_ call to - * nfs_file_write() that a write error occured, and hence cause it to - * fall back to doing a synchronous write. - */ -static int nfs_do_fsync(struct nfs_open_context *ctx, struct inode *inode) -{ - int have_error, status; - int ret = 0; - - have_error = test_and_clear_bit(NFS_CONTEXT_ERROR_WRITE, &ctx->flags); - status = nfs_wb_all(inode); - have_error |= test_bit(NFS_CONTEXT_ERROR_WRITE, &ctx->flags); - if (have_error) - ret = xchg(&ctx->error, 0); - if (!ret) - ret = status; - return ret; -} - /* * Flush all dirty pages, and check for write errors. */ static int nfs_file_flush(struct file *file, fl_owner_t id) { - struct nfs_open_context *ctx = nfs_file_open_context(file); struct dentry *dentry = file->f_path.dentry; struct inode *inode = dentry->d_inode; @@ -245,7 +219,7 @@ nfs_file_flush(struct file *file, fl_owner_t id) return 0; /* Flush writes to the server and return any errors */ - return nfs_do_fsync(ctx, inode); + return vfs_fsync(file, 0); } static ssize_t @@ -320,6 +294,13 @@ nfs_file_mmap(struct file * file, struct vm_area_struct * vma) * Flush any dirty pages for this process, and check for write errors. * The return status from this call provides a reliable indication of * whether any write errors occurred for this process. + * + * Notice that it clears the NFS_CONTEXT_ERROR_WRITE before synching to + * disk, but it retrieves and clears ctx->error after synching, despite + * the two being set at the same time in nfs_context_set_write_error(). + * This is because the former is used to notify the _next_ call to + * nfs_file_write() that a write error occured, and hence cause it to + * fall back to doing a synchronous write. */ static int nfs_file_fsync(struct file *file, int datasync) @@ -327,13 +308,23 @@ nfs_file_fsync(struct file *file, int datasync) struct dentry *dentry = file->f_path.dentry; struct nfs_open_context *ctx = nfs_file_open_context(file); struct inode *inode = dentry->d_inode; + int have_error, status; + int ret = 0; + dprintk("NFS: fsync file(%s/%s) datasync %d\n", dentry->d_parent->d_name.name, dentry->d_name.name, datasync); nfs_inc_stats(inode, NFSIOS_VFSFSYNC); - return nfs_do_fsync(ctx, inode); + have_error = test_and_clear_bit(NFS_CONTEXT_ERROR_WRITE, &ctx->flags); + status = nfs_commit_inode(inode, FLUSH_SYNC); + have_error |= test_bit(NFS_CONTEXT_ERROR_WRITE, &ctx->flags); + if (have_error) + ret = xchg(&ctx->error, 0); + if (!ret) + ret = status; + return ret; } /* @@ -639,7 +630,7 @@ static ssize_t nfs_file_write(struct kiocb *iocb, const struct iovec *iov, /* Return error values for O_DSYNC and IS_SYNC() */ if (result >= 0 && nfs_need_sync_write(iocb->ki_filp, inode)) { - int err = nfs_do_fsync(nfs_file_open_context(iocb->ki_filp), inode); + int err = vfs_fsync(iocb->ki_filp, 0); if (err < 0) result = err; } @@ -675,7 +666,7 @@ static ssize_t nfs_file_splice_write(struct pipe_inode_info *pipe, written = ret; if (ret >= 0 && nfs_need_sync_write(filp, inode)) { - int err = nfs_do_fsync(nfs_file_open_context(filp), inode); + int err = vfs_fsync(filp, 0); if (err < 0) ret = err; } -- cgit v1.2.3 From 1b924e5f878e3cd62a20bd8dbf3b911a40185a99 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 31 Jul 2010 14:29:06 -0400 Subject: NFS: Clean up the callers of nfs_wb_all() There is no need to flush out writes before calling nfs_wb_all(). Signed-off-by: Trond Myklebust --- fs/nfs/delegation.c | 10 +--------- fs/nfs/inode.c | 4 +--- 2 files changed, 2 insertions(+), 12 deletions(-) (limited to 'fs') diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index f34f4ac52b81..b9c3c43cea1d 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c @@ -268,14 +268,6 @@ out: return status; } -/* Sync all data to disk upon delegation return */ -static void nfs_msync_inode(struct inode *inode) -{ - filemap_fdatawrite(inode->i_mapping); - nfs_wb_all(inode); - filemap_fdatawait(inode->i_mapping); -} - /* * Basic procedure for returning a delegation to the server */ @@ -367,7 +359,7 @@ int nfs_inode_return_delegation(struct inode *inode) delegation = nfs_detach_delegation_locked(nfsi, NULL, clp); spin_unlock(&clp->cl_lock); if (delegation != NULL) { - nfs_msync_inode(inode); + nfs_wb_all(inode); err = __nfs_inode_return_delegation(inode, delegation, 1); } } diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index ec7a8f96a2c2..581d8f081e68 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -413,10 +413,8 @@ nfs_setattr(struct dentry *dentry, struct iattr *attr) return 0; /* Write all dirty data */ - if (S_ISREG(inode->i_mode)) { - filemap_write_and_wait(inode->i_mapping); + if (S_ISREG(inode->i_mode)) nfs_wb_all(inode); - } fattr = nfs_alloc_fattr(); if (fattr == NULL) -- cgit v1.2.3 From 0a8ebba943dd89bdd57c5dab5a66932f690847d9 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 31 Jul 2010 14:29:06 -0400 Subject: NFS: nfs_rename() should not have to flush out writebacks We don't really support nfs servers that invalidate the file handle after a rename, so precautions such as flushing out dirty data before renaming the file are superfluous. Signed-off-by: Trond Myklebust --- fs/nfs/dir.c | 9 --------- 1 file changed, 9 deletions(-) (limited to 'fs') diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 782b431ef91c..067a051397ba 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -1652,16 +1652,7 @@ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry, } } - /* - * ... prune child dentries and writebacks if needed. - */ - if (atomic_read(&old_dentry->d_count) > 1) { - if (S_ISREG(old_inode->i_mode)) - nfs_wb_all(old_inode); - shrink_dcache_parent(old_dentry); - } nfs_inode_return_delegation(old_inode); - if (new_inode != NULL) nfs_inode_return_delegation(new_inode); -- cgit v1.2.3 From 14516c3a30e256e8d4e7a9af271c8df644ac3222 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 31 Jul 2010 14:29:06 -0400 Subject: NFSv4.1: Handle NFS4ERR_DELAY on SEQUENCE correctly In RFC5661, an NFS4ERR_DELAY error on a SEQUENCE operation has the special meaning that the server is not finished processing the request. In this case we want to just retry the request without touching the slot. Also fix a bug whereby we would fail to update the sequence id if the server returned any error other than NFS_OK/NFS4ERR_DELAY. Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 92 +++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 62 insertions(+), 30 deletions(-) (limited to 'fs') diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index d6413b48b057..cab0eb915145 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -389,11 +389,12 @@ static void nfs41_sequence_free_slot(struct nfs4_sequence_res *res) res->sr_slotid = NFS4_MAX_SLOT_TABLE; } -static void nfs41_sequence_done(struct nfs4_sequence_res *res) +static int nfs41_sequence_done(struct rpc_task *task, struct nfs4_sequence_res *res) { unsigned long timestamp; struct nfs4_slot_table *tbl; struct nfs4_slot *slot; + struct nfs_client *clp; /* * sr_status remains 1 if an RPC level error occurred. The server @@ -408,14 +409,16 @@ static void nfs41_sequence_done(struct nfs4_sequence_res *res) if (res->sr_slotid == NFS4_MAX_SLOT_TABLE) goto out; + tbl = &res->sr_session->fc_slot_table; + slot = tbl->slots + res->sr_slotid; + /* Check the SEQUENCE operation status */ - if (res->sr_status == 0) { - struct nfs_client *clp = res->sr_session->clp; - tbl = &res->sr_session->fc_slot_table; - slot = tbl->slots + res->sr_slotid; + switch (res->sr_status) { + case 0: /* Update the slot's sequence and clientid lease timer */ ++slot->seq_nr; timestamp = res->sr_renewal_time; + clp = res->sr_session->clp; spin_lock(&clp->cl_lock); if (time_before(clp->cl_last_renewal, timestamp)) clp->cl_last_renewal = timestamp; @@ -423,18 +426,39 @@ static void nfs41_sequence_done(struct nfs4_sequence_res *res) /* Check sequence flags */ if (atomic_read(&clp->cl_count) > 1) nfs41_handle_sequence_flag_errors(clp, res->sr_status_flags); + break; + case -NFS4ERR_DELAY: + /* The server detected a resend of the RPC call and + * returned NFS4ERR_DELAY as per Section 2.10.6.2 + * of RFC5661. + */ + dprintk("%s: slot=%d seq=%d: Operation in progress\n", + __func__, res->sr_slotid, slot->seq_nr); + goto out_retry; + default: + /* Just update the slot sequence no. */ + ++slot->seq_nr; } out: /* The session may be reset by one of the error handlers. */ dprintk("%s: Error %d free the slot \n", __func__, res->sr_status); nfs41_sequence_free_slot(res); + return 1; +out_retry: + rpc_restart_call(task); + /* FIXME: rpc_restart_call() should be made to return success/fail */ + if (task->tk_action == NULL) + goto out; + rpc_delay(task, NFS4_POLL_RETRY_MAX); + return 0; } -static void nfs4_sequence_done(const struct nfs_server *server, - struct nfs4_sequence_res *res, int rpc_status) +static int nfs4_sequence_done(struct rpc_task *task, + struct nfs4_sequence_res *res) { - if (res->sr_session != NULL) - nfs41_sequence_done(res); + if (res->sr_session == NULL) + return 1; + return nfs41_sequence_done(task, res); } /* @@ -592,7 +616,7 @@ static void nfs41_call_sync_done(struct rpc_task *task, void *calldata) { struct nfs41_call_sync_data *data = calldata; - nfs41_sequence_done(data->seq_res); + nfs41_sequence_done(task, data->seq_res); } struct rpc_call_ops nfs41_call_sync_ops = { @@ -650,9 +674,10 @@ int _nfs4_call_sync_session(struct nfs_server *server, } #else -static void nfs4_sequence_done(const struct nfs_server *server, - struct nfs4_sequence_res *res, int rpc_status) +static int nfs4_sequence_done(struct rpc_task *task, + struct nfs4_sequence_res *res) { + return 1; } #endif /* CONFIG_NFS_V4_1 */ @@ -1379,8 +1404,8 @@ static void nfs4_open_done(struct rpc_task *task, void *calldata) data->rpc_status = task->tk_status; - nfs4_sequence_done(data->o_arg.server, &data->o_res.seq_res, - task->tk_status); + if (!nfs4_sequence_done(task, &data->o_res.seq_res)) + return; if (RPC_ASSASSINATED(task)) return; @@ -1832,7 +1857,8 @@ static void nfs4_close_done(struct rpc_task *task, void *data) struct nfs4_state *state = calldata->state; struct nfs_server *server = NFS_SERVER(calldata->inode); - nfs4_sequence_done(server, &calldata->res.seq_res, task->tk_status); + if (!nfs4_sequence_done(task, &calldata->res.seq_res)) + return; if (RPC_ASSASSINATED(task)) return; /* hmm. we are done with the inode, and in the process of freeing @@ -2642,7 +2668,8 @@ static int nfs4_proc_unlink_done(struct rpc_task *task, struct inode *dir) { struct nfs_removeres *res = task->tk_msg.rpc_resp; - nfs4_sequence_done(res->server, &res->seq_res, task->tk_status); + if (!nfs4_sequence_done(task, &res->seq_res)) + return 0; if (nfs4_async_handle_error(task, res->server, NULL) == -EAGAIN) return 0; update_changeattr(dir, &res->cinfo); @@ -3087,7 +3114,8 @@ static int nfs4_read_done(struct rpc_task *task, struct nfs_read_data *data) dprintk("--> %s\n", __func__); - nfs4_sequence_done(server, &data->res.seq_res, task->tk_status); + if (!nfs4_sequence_done(task, &data->res.seq_res)) + return -EAGAIN; if (nfs4_async_handle_error(task, server, data->args.context->state) == -EAGAIN) { nfs_restart_rpc(task, server->nfs_client); @@ -3110,8 +3138,8 @@ static int nfs4_write_done(struct rpc_task *task, struct nfs_write_data *data) { struct inode *inode = data->inode; - nfs4_sequence_done(NFS_SERVER(inode), &data->res.seq_res, - task->tk_status); + if (!nfs4_sequence_done(task, &data->res.seq_res)) + return -EAGAIN; if (nfs4_async_handle_error(task, NFS_SERVER(inode), data->args.context->state) == -EAGAIN) { nfs_restart_rpc(task, NFS_SERVER(inode)->nfs_client); @@ -3139,8 +3167,9 @@ static int nfs4_commit_done(struct rpc_task *task, struct nfs_write_data *data) { struct inode *inode = data->inode; - nfs4_sequence_done(NFS_SERVER(inode), &data->res.seq_res, - task->tk_status); + if (!nfs4_sequence_done(task, &data->res.seq_res)) + return -EAGAIN; + if (nfs4_async_handle_error(task, NFS_SERVER(inode), NULL) == -EAGAIN) { nfs_restart_rpc(task, NFS_SERVER(inode)->nfs_client); return -EAGAIN; @@ -3630,8 +3659,8 @@ static void nfs4_delegreturn_done(struct rpc_task *task, void *calldata) { struct nfs4_delegreturndata *data = calldata; - nfs4_sequence_done(data->res.server, &data->res.seq_res, - task->tk_status); + if (!nfs4_sequence_done(task, &data->res.seq_res)) + return; switch (task->tk_status) { case -NFS4ERR_STALE_STATEID: @@ -3881,8 +3910,8 @@ static void nfs4_locku_done(struct rpc_task *task, void *data) { struct nfs4_unlockdata *calldata = data; - nfs4_sequence_done(calldata->server, &calldata->res.seq_res, - task->tk_status); + if (!nfs4_sequence_done(task, &calldata->res.seq_res)) + return; if (RPC_ASSASSINATED(task)) return; switch (task->tk_status) { @@ -4091,8 +4120,8 @@ static void nfs4_lock_done(struct rpc_task *task, void *calldata) dprintk("%s: begin!\n", __func__); - nfs4_sequence_done(data->server, &data->res.seq_res, - task->tk_status); + if (!nfs4_sequence_done(task, &data->res.seq_res)) + return; data->rpc_status = task->tk_status; if (RPC_ASSASSINATED(task)) @@ -4629,7 +4658,8 @@ static void nfs4_get_lease_time_done(struct rpc_task *task, void *calldata) (struct nfs4_get_lease_time_data *)calldata; dprintk("--> %s\n", __func__); - nfs41_sequence_done(&data->res->lr_seq_res); + if (!nfs41_sequence_done(task, &data->res->lr_seq_res)) + return; switch (task->tk_status) { case -NFS4ERR_DELAY: case -NFS4ERR_GRACE: @@ -5111,7 +5141,8 @@ static void nfs41_sequence_call_done(struct rpc_task *task, void *data) struct nfs4_sequence_data *calldata = data; struct nfs_client *clp = calldata->clp; - nfs41_sequence_done(task->tk_msg.rpc_resp); + if (!nfs41_sequence_done(task, task->tk_msg.rpc_resp)) + return; if (task->tk_status < 0) { dprintk("%s ERROR %d\n", __func__, task->tk_status); @@ -5255,7 +5286,8 @@ static void nfs4_reclaim_complete_done(struct rpc_task *task, void *data) struct nfs4_sequence_res *res = &calldata->res.seq_res; dprintk("--> %s\n", __func__); - nfs41_sequence_done(res); + if (!nfs41_sequence_done(task, res)) + return; if (nfs41_reclaim_complete_handle_errors(task, clp) == -EAGAIN) { rpc_restart_call_prepare(task); -- cgit v1.2.3 From 452e93523d9433f83670e7b42cbe75319c208762 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 31 Jul 2010 14:29:06 -0400 Subject: NFSv4: Clean up the process of renewing the NFSv4 lease Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) (limited to 'fs') diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index cab0eb915145..d2d20fbeb086 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -303,15 +303,19 @@ do_state_recovery: } -static void renew_lease(const struct nfs_server *server, unsigned long timestamp) +static void do_renew_lease(struct nfs_client *clp, unsigned long timestamp) { - struct nfs_client *clp = server->nfs_client; spin_lock(&clp->cl_lock); if (time_before(clp->cl_last_renewal,timestamp)) clp->cl_last_renewal = timestamp; spin_unlock(&clp->cl_lock); } +static void renew_lease(const struct nfs_server *server, unsigned long timestamp) +{ + do_renew_lease(server->nfs_client, timestamp); +} + #if defined(CONFIG_NFS_V4_1) /* @@ -419,10 +423,7 @@ static int nfs41_sequence_done(struct rpc_task *task, struct nfs4_sequence_res * ++slot->seq_nr; timestamp = res->sr_renewal_time; clp = res->sr_session->clp; - spin_lock(&clp->cl_lock); - if (time_before(clp->cl_last_renewal, timestamp)) - clp->cl_last_renewal = timestamp; - spin_unlock(&clp->cl_lock); + do_renew_lease(clp, timestamp); /* Check sequence flags */ if (atomic_read(&clp->cl_count) > 1) nfs41_handle_sequence_flag_errors(clp, res->sr_status_flags); @@ -3219,10 +3220,7 @@ static void nfs4_renew_done(struct rpc_task *task, void *calldata) nfs4_schedule_state_recovery(clp); return; } - spin_lock(&clp->cl_lock); - if (time_before(clp->cl_last_renewal,timestamp)) - clp->cl_last_renewal = timestamp; - spin_unlock(&clp->cl_lock); + do_renew_lease(clp, timestamp); } static const struct rpc_call_ops nfs4_renew_ops = { @@ -3263,10 +3261,7 @@ int nfs4_proc_renew(struct nfs_client *clp, struct rpc_cred *cred) status = rpc_call_sync(clp->cl_rpcclient, &msg, 0); if (status < 0) return status; - spin_lock(&clp->cl_lock); - if (time_before(clp->cl_last_renewal,now)) - clp->cl_last_renewal = now; - spin_unlock(&clp->cl_lock); + do_renew_lease(clp, now); return 0; } -- cgit v1.2.3 From a6f03393ec86fd25523c79497a9a773bda170d1d Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 31 Jul 2010 14:29:07 -0400 Subject: NFSv4: Get rid of the bogus RPC_ASSASSINATED(task) checks There is no real reason to have RPC_ASSASSINATED() checks in the NFS code. As far as it is concerned, this is just an RPC error... Signed-off-by: Trond Myklebust --- fs/nfs/nfs4proc.c | 10 ---------- 1 file changed, 10 deletions(-) (limited to 'fs') diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index d2d20fbeb086..101bf403f507 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -1275,8 +1275,6 @@ static void nfs4_open_confirm_done(struct rpc_task *task, void *calldata) struct nfs4_opendata *data = calldata; data->rpc_status = task->tk_status; - if (RPC_ASSASSINATED(task)) - return; if (data->rpc_status == 0) { memcpy(data->o_res.stateid.data, data->c_res.stateid.data, sizeof(data->o_res.stateid.data)); @@ -1408,8 +1406,6 @@ static void nfs4_open_done(struct rpc_task *task, void *calldata) if (!nfs4_sequence_done(task, &data->o_res.seq_res)) return; - if (RPC_ASSASSINATED(task)) - return; if (task->tk_status == 0) { switch (data->o_res.f_attr->mode & S_IFMT) { case S_IFREG: @@ -1860,8 +1856,6 @@ static void nfs4_close_done(struct rpc_task *task, void *data) if (!nfs4_sequence_done(task, &calldata->res.seq_res)) return; - if (RPC_ASSASSINATED(task)) - return; /* hmm. we are done with the inode, and in the process of freeing * the state_owner. we keep this around to process errors */ @@ -3907,8 +3901,6 @@ static void nfs4_locku_done(struct rpc_task *task, void *data) if (!nfs4_sequence_done(task, &calldata->res.seq_res)) return; - if (RPC_ASSASSINATED(task)) - return; switch (task->tk_status) { case 0: memcpy(calldata->lsp->ls_stateid.data, @@ -4119,8 +4111,6 @@ static void nfs4_lock_done(struct rpc_task *task, void *calldata) return; data->rpc_status = task->tk_status; - if (RPC_ASSASSINATED(task)) - goto out; if (data->arg.new_lock_owner != 0) { if (data->rpc_status == 0) nfs_confirm_seqid(&data->lsp->ls_seqid, 0); -- cgit v1.2.3 From d05dd4e98f0dd30ee933e05ac9363614c47df83a Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 31 Jul 2010 14:29:07 -0400 Subject: NFS: Fix the NFS users of rpc_restart_call() Fix up those functions that depend on knowing whether or not rpc_restart_call is successful or not. Signed-off-by: Trond Myklebust --- fs/nfs/internal.h | 7 +++---- fs/nfs/nfs4proc.c | 4 +--- 2 files changed, 4 insertions(+), 7 deletions(-) (limited to 'fs') diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index d8bd619e386c..699725fee34f 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -369,10 +369,9 @@ unsigned int nfs_page_array_len(unsigned int base, size_t len) * Helper for restarting RPC calls in the possible presence of NFSv4.1 * sessions. */ -static inline void nfs_restart_rpc(struct rpc_task *task, const struct nfs_client *clp) +static inline int nfs_restart_rpc(struct rpc_task *task, const struct nfs_client *clp) { if (nfs4_has_session(clp)) - rpc_restart_call_prepare(task); - else - rpc_restart_call(task); + return rpc_restart_call_prepare(task); + return rpc_restart_call(task); } diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 101bf403f507..7ffbb98ddec3 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -446,9 +446,7 @@ out: nfs41_sequence_free_slot(res); return 1; out_retry: - rpc_restart_call(task); - /* FIXME: rpc_restart_call() should be made to return success/fail */ - if (task->tk_action == NULL) + if (!rpc_restart_call(task)) goto out; rpc_delay(task, NFS4_POLL_RETRY_MAX); return 0; -- cgit v1.2.3 From 85dc7878c6c2277de2eda2c4d1b11ea5c5b1068a Mon Sep 17 00:00:00 2001 From: Boaz Harrosh Date: Mon, 31 May 2010 18:55:43 +0300 Subject: exofs: Remove superfluous dependency on buffer_head and writeback exofs_releasepage && exofs_invalidatepage are never called. Leave the WARN_ONs but remove any code. Remove the cleanup other stale #includes. Signed-off-by: Boaz Harrosh --- fs/exofs/inode.c | 9 ++------- fs/exofs/super.c | 1 - 2 files changed, 2 insertions(+), 8 deletions(-) (limited to 'fs') diff --git a/fs/exofs/inode.c b/fs/exofs/inode.c index 4bb6ef822e46..fbf9f34554e0 100644 --- a/fs/exofs/inode.c +++ b/fs/exofs/inode.c @@ -32,9 +32,6 @@ */ #include -#include -#include -#include #include "exofs.h" @@ -759,15 +756,13 @@ static int exofs_releasepage(struct page *page, gfp_t gfp) { EXOFS_DBGMSG("page 0x%lx\n", page->index); WARN_ON(1); - return try_to_free_buffers(page); + return 0; } static void exofs_invalidatepage(struct page *page, unsigned long offset) { - EXOFS_DBGMSG("page_has_buffers=>%d\n", page_has_buffers(page)); + EXOFS_DBGMSG("page 0x%lx offset 0x%lx\n", page->index, offset); WARN_ON(1); - - block_invalidatepage(page, offset); } const struct address_space_operations exofs_aops = { diff --git a/fs/exofs/super.c b/fs/exofs/super.c index 03149b9a5178..50cb1745e29c 100644 --- a/fs/exofs/super.c +++ b/fs/exofs/super.c @@ -31,7 +31,6 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include #include #include #include -- cgit v1.2.3 From b2848349296f3428850eb34c3a52d586f48d4b04 Mon Sep 17 00:00:00 2001 From: Boaz Harrosh Date: Mon, 31 May 2010 18:02:39 +0300 Subject: exofs: exofs_file_fsync and exofs_file_flush correctness As per Christoph advise: no need to call filemap_write_and_wait(). In exofs all metadata is at the inode so just writing the inode is all is needed. ->fsync implies this must be done synchronously. But now exofs_file_fsync can not be used by exofs_file_flush. vfs_fsync() should do that job correctly. FIXME: remove the sb_sync and fix that sb_update better. Signed-off-by: Boaz Harrosh --- fs/exofs/file.c | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) (limited to 'fs') diff --git a/fs/exofs/file.c b/fs/exofs/file.c index fef6899be397..aa1fd1a372cf 100644 --- a/fs/exofs/file.c +++ b/fs/exofs/file.c @@ -30,9 +30,6 @@ * along with exofs; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include - #include "exofs.h" static int exofs_release_file(struct inode *inode, struct file *filp) @@ -40,19 +37,27 @@ static int exofs_release_file(struct inode *inode, struct file *filp) return 0; } +/* exofs_file_fsync - flush the inode to disk + * + * Note, in exofs all metadata is written as part of inode, regardless. + * The writeout is synchronous + */ static int exofs_file_fsync(struct file *filp, int datasync) { int ret; - struct address_space *mapping = filp->f_mapping; - struct inode *inode = mapping->host; + struct inode *inode = filp->f_mapping->host; + struct writeback_control wbc = { + .sync_mode = WB_SYNC_ALL, + .nr_to_write = 0, /* metadata-only; caller takes care of data */ + }; struct super_block *sb; - ret = filemap_write_and_wait(mapping); - if (ret) - return ret; + if (!(inode->i_state & I_DIRTY)) + return 0; + if (datasync && !(inode->i_state & I_DIRTY_DATASYNC)) + return 0; - /* sync the inode attributes */ - ret = write_inode_now(inode, 1); + ret = sync_inode(inode, &wbc); /* This is a good place to write the sb */ /* TODO: Sechedule an sb-sync on create */ @@ -65,9 +70,9 @@ static int exofs_file_fsync(struct file *filp, int datasync) static int exofs_flush(struct file *file, fl_owner_t id) { - exofs_file_fsync(file, 1); + int ret = vfs_fsync(file, 0); /* TODO: Flush the OSD target */ - return 0; + return ret; } const struct file_operations exofs_file_operations = { -- cgit v1.2.3 From 6e31609b1dcd595d7e4676ce62323532b29e8999 Mon Sep 17 00:00:00 2001 From: Boaz Harrosh Date: Thu, 29 Jul 2010 17:08:13 +0300 Subject: exofs: Remove useless optimization We used to compact all used devices in an IO to the beginning of the device array in an io_state. And keep a last device used so in later loops we don't iterate on all device slots. This does not prevent us from checking if slots are empty since in reads we only read from a single mirror and jump to the next mirror-set. This optimization is marginal, and needlessly complicates the code. Specially when we will later want to support raid/456 with same abstract code. So remove the distinction between "dev" and "comp". Only "dev" is used both as the device used and as the index (component) in the device array. [Note that now the io_state->dev member is redundant but I keep it because I might want to optimize by only IOing a single group, though keeping a group_width*mirrors devices in io_state, we now keep num-devices in each io_state] Signed-off-by: Boaz Harrosh --- fs/exofs/ios.c | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) (limited to 'fs') diff --git a/fs/exofs/ios.c b/fs/exofs/ios.c index 4337cad7777b..5bb47373c7e0 100644 --- a/fs/exofs/ios.c +++ b/fs/exofs/ios.c @@ -392,20 +392,19 @@ static int _add_stripe_unit(struct exofs_io_state *ios, unsigned *cur_pg, } static int _prepare_one_group(struct exofs_io_state *ios, u64 length, - struct _striping_info *si, unsigned first_comp) + struct _striping_info *si) { unsigned stripe_unit = ios->layout->stripe_unit; unsigned mirrors_p1 = ios->layout->mirrors_p1; unsigned devs_in_group = ios->layout->group_width * mirrors_p1; unsigned dev = si->dev; unsigned first_dev = dev - (dev % devs_in_group); - unsigned comp = first_comp + (dev - first_dev); unsigned max_comp = ios->numdevs ? ios->numdevs - mirrors_p1 : 0; unsigned cur_pg = ios->pages_consumed; int ret = 0; while (length) { - struct exofs_per_dev_state *per_dev = &ios->per_dev[comp]; + struct exofs_per_dev_state *per_dev = &ios->per_dev[dev]; unsigned cur_len, page_off = 0; if (!per_dev->length) { @@ -424,11 +423,8 @@ static int _prepare_one_group(struct exofs_io_state *ios, u64 length, cur_len = stripe_unit; } - if (max_comp < comp) - max_comp = comp; - - dev += mirrors_p1; - dev = (dev % devs_in_group) + first_dev; + if (max_comp < dev) + max_comp = dev; } else { cur_len = stripe_unit; } @@ -440,8 +436,8 @@ static int _prepare_one_group(struct exofs_io_state *ios, u64 length, if (unlikely(ret)) goto out; - comp += mirrors_p1; - comp = (comp % devs_in_group) + first_comp; + dev += mirrors_p1; + dev = (dev % devs_in_group) + first_dev; length -= cur_len; } @@ -457,7 +453,6 @@ static int _prepare_for_striping(struct exofs_io_state *ios) struct _striping_info si; unsigned devs_in_group = ios->layout->group_width * ios->layout->mirrors_p1; - unsigned first_comp = 0; int ret = 0; _calc_stripe_info(ios, ios->offset, &si); @@ -482,7 +477,7 @@ static int _prepare_for_striping(struct exofs_io_state *ios) if (length < si.group_length) si.group_length = length; - ret = _prepare_one_group(ios, si.group_length, &si, first_comp); + ret = _prepare_one_group(ios, si.group_length, &si); if (unlikely(ret)) goto out; @@ -496,9 +491,6 @@ static int _prepare_for_striping(struct exofs_io_state *ios) si.dev = (si.dev - (si.dev % devs_in_group)) + devs_in_group; si.dev %= ios->layout->s_numdevs; - - first_comp += devs_in_group; - first_comp %= ios->layout->s_numdevs; } out: -- cgit v1.2.3 From 5002dd18c5940ce63b917d84f2b852c3b96009bb Mon Sep 17 00:00:00 2001 From: Boaz Harrosh Date: Mon, 2 Aug 2010 20:06:46 +0300 Subject: exofs: Fix groups code when num_devices is not divisible by group_width There is a bug when num_devices is not divisible by group_width * mirrors. We would not return to the proper device and offset when looping on to the next group. The fix makes code simpler actually. Signed-off-by: Boaz Harrosh --- fs/exofs/ios.c | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) (limited to 'fs') diff --git a/fs/exofs/ios.c b/fs/exofs/ios.c index 5bb47373c7e0..5a1960500c8a 100644 --- a/fs/exofs/ios.c +++ b/fs/exofs/ios.c @@ -305,8 +305,6 @@ int exofs_check_io(struct exofs_io_state *ios, u64 *resid) struct _striping_info { u64 obj_offset; u64 group_length; - u64 total_group_length; - u64 Major; unsigned dev; unsigned unit_off; }; @@ -343,8 +341,6 @@ static void _calc_stripe_info(struct exofs_io_state *ios, u64 file_offset, (M * group_depth * stripe_unit); si->group_length = T - H; - si->total_group_length = T; - si->Major = M; } static int _add_stripe_unit(struct exofs_io_state *ios, unsigned *cur_pg, @@ -450,17 +446,15 @@ out: static int _prepare_for_striping(struct exofs_io_state *ios) { u64 length = ios->length; + u64 offset = ios->offset; struct _striping_info si; - unsigned devs_in_group = ios->layout->group_width * - ios->layout->mirrors_p1; int ret = 0; - _calc_stripe_info(ios, ios->offset, &si); - if (!ios->pages) { if (ios->kern_buff) { struct exofs_per_dev_state *per_dev = &ios->per_dev[0]; + _calc_stripe_info(ios, ios->offset, &si); per_dev->offset = si.obj_offset; per_dev->dev = si.dev; @@ -474,6 +468,8 @@ static int _prepare_for_striping(struct exofs_io_state *ios) } while (length) { + _calc_stripe_info(ios, offset, &si); + if (length < si.group_length) si.group_length = length; @@ -481,16 +477,8 @@ static int _prepare_for_striping(struct exofs_io_state *ios) if (unlikely(ret)) goto out; + offset += si.group_length; length -= si.group_length; - - si.group_length = si.total_group_length; - si.unit_off = 0; - ++si.Major; - si.obj_offset = si.Major * ios->layout->stripe_unit * - ios->layout->group_depth; - - si.dev = (si.dev - (si.dev % devs_in_group)) + devs_in_group; - si.dev %= ios->layout->s_numdevs; } out: -- cgit v1.2.3 From a17c2153d2e271b0cbacae9bed83b0eaa41db7e1 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Sat, 31 Jul 2010 14:29:08 -0400 Subject: SUNRPC: Move the bound cred to struct rpc_rqst This will allow us to save the original generic cred in rpc_message, so that if we migrate from one server to another, we can generate a new bound cred without having to punt back to the NFS layer. Signed-off-by: Trond Myklebust --- fs/nfs/nfs2xdr.c | 7 ++-- fs/nfs/nfs3xdr.c | 8 ++-- fs/nfs/nfs4xdr.c | 2 +- include/linux/sunrpc/auth.h | 2 - include/linux/sunrpc/xprt.h | 1 + net/sunrpc/auth.c | 43 ++++++++++---------- net/sunrpc/auth_gss/auth_gss.c | 22 +++++----- net/sunrpc/auth_null.c | 2 +- net/sunrpc/auth_unix.c | 6 +-- net/sunrpc/clnt.c | 91 +++++++++++++++++++++--------------------- net/sunrpc/sched.c | 2 +- net/sunrpc/xprt.c | 2 + 12 files changed, 92 insertions(+), 96 deletions(-) (limited to 'fs') diff --git a/fs/nfs/nfs2xdr.c b/fs/nfs/nfs2xdr.c index 81cf14257916..db8846a0e82e 100644 --- a/fs/nfs/nfs2xdr.c +++ b/fs/nfs/nfs2xdr.c @@ -233,7 +233,7 @@ nfs_xdr_removeargs(struct rpc_rqst *req, __be32 *p, const struct nfs_removeargs static int nfs_xdr_readargs(struct rpc_rqst *req, __be32 *p, struct nfs_readargs *args) { - struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth; + struct rpc_auth *auth = req->rq_cred->cr_auth; unsigned int replen; u32 offset = (u32)args->offset; u32 count = args->count; @@ -393,8 +393,7 @@ nfs_xdr_symlinkargs(struct rpc_rqst *req, __be32 *p, struct nfs_symlinkargs *arg static int nfs_xdr_readdirargs(struct rpc_rqst *req, __be32 *p, struct nfs_readdirargs *args) { - struct rpc_task *task = req->rq_task; - struct rpc_auth *auth = task->tk_msg.rpc_cred->cr_auth; + struct rpc_auth *auth = req->rq_cred->cr_auth; unsigned int replen; u32 count = args->count; @@ -575,7 +574,7 @@ nfs_xdr_diropres(struct rpc_rqst *req, __be32 *p, struct nfs_diropok *res) static int nfs_xdr_readlinkargs(struct rpc_rqst *req, __be32 *p, struct nfs_readlinkargs *args) { - struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth; + struct rpc_auth *auth = req->rq_cred->cr_auth; unsigned int replen; p = xdr_encode_fhandle(p, args->fh); diff --git a/fs/nfs/nfs3xdr.c b/fs/nfs/nfs3xdr.c index 75dcfc7da365..9769704f8ce6 100644 --- a/fs/nfs/nfs3xdr.c +++ b/fs/nfs/nfs3xdr.c @@ -330,7 +330,7 @@ nfs3_xdr_accessargs(struct rpc_rqst *req, __be32 *p, struct nfs3_accessargs *arg static int nfs3_xdr_readargs(struct rpc_rqst *req, __be32 *p, struct nfs_readargs *args) { - struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth; + struct rpc_auth *auth = req->rq_cred->cr_auth; unsigned int replen; u32 count = args->count; @@ -471,7 +471,7 @@ nfs3_xdr_linkargs(struct rpc_rqst *req, __be32 *p, struct nfs3_linkargs *args) static int nfs3_xdr_readdirargs(struct rpc_rqst *req, __be32 *p, struct nfs3_readdirargs *args) { - struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth; + struct rpc_auth *auth = req->rq_cred->cr_auth; unsigned int replen; u32 count = args->count; @@ -675,7 +675,7 @@ static int nfs3_xdr_getaclargs(struct rpc_rqst *req, __be32 *p, struct nfs3_getaclargs *args) { - struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth; + struct rpc_auth *auth = req->rq_cred->cr_auth; unsigned int replen; p = xdr_encode_fhandle(p, args->fh); @@ -802,7 +802,7 @@ nfs3_xdr_accessres(struct rpc_rqst *req, __be32 *p, struct nfs3_accessres *res) static int nfs3_xdr_readlinkargs(struct rpc_rqst *req, __be32 *p, struct nfs3_readlinkargs *args) { - struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth; + struct rpc_auth *auth = req->rq_cred->cr_auth; unsigned int replen; p = xdr_encode_fhandle(p, args->fh); diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 257c1811feb4..08ef91291132 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -758,7 +758,7 @@ static void encode_compound_hdr(struct xdr_stream *xdr, struct compound_hdr *hdr) { __be32 *p; - struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth; + struct rpc_auth *auth = req->rq_cred->cr_auth; /* initialize running count of expected bytes in reply. * NOTE: the replied tag SHOULD be the same is the one sent, diff --git a/include/linux/sunrpc/auth.h b/include/linux/sunrpc/auth.h index 90e4c3827ac0..5bbc447175dc 100644 --- a/include/linux/sunrpc/auth.h +++ b/include/linux/sunrpc/auth.h @@ -135,10 +135,8 @@ void rpcauth_release(struct rpc_auth *); struct rpc_cred * rpcauth_lookup_credcache(struct rpc_auth *, struct auth_cred *, int); void rpcauth_init_cred(struct rpc_cred *, const struct auth_cred *, struct rpc_auth *, const struct rpc_credops *); struct rpc_cred * rpcauth_lookupcred(struct rpc_auth *, int); -int rpcauth_bindcred(struct rpc_task *, struct rpc_cred *, int); struct rpc_cred * rpcauth_generic_bind_cred(struct rpc_task *, struct rpc_cred *, int); void put_rpccred(struct rpc_cred *); -void rpcauth_unbindcred(struct rpc_task *); __be32 * rpcauth_marshcred(struct rpc_task *, __be32 *); __be32 * rpcauth_checkverf(struct rpc_task *, __be32 *); int rpcauth_wrap_req(struct rpc_task *task, kxdrproc_t encode, void *rqstp, __be32 *data, void *obj); diff --git a/include/linux/sunrpc/xprt.h b/include/linux/sunrpc/xprt.h index b51470302399..ff5a77b28c50 100644 --- a/include/linux/sunrpc/xprt.h +++ b/include/linux/sunrpc/xprt.h @@ -64,6 +64,7 @@ struct rpc_rqst { * This is the private part */ struct rpc_task * rq_task; /* RPC task data */ + struct rpc_cred * rq_cred; /* Bound cred */ __be32 rq_xid; /* request XID */ int rq_cong; /* has incremented xprt->cong */ u32 rq_seqno; /* gss seq no. used on req. */ diff --git a/net/sunrpc/auth.c b/net/sunrpc/auth.c index d8968faf5ccf..95721426296d 100644 --- a/net/sunrpc/auth.c +++ b/net/sunrpc/auth.c @@ -477,9 +477,10 @@ rpcauth_bind_new_cred(struct rpc_task *task, int lookupflags) return rpcauth_lookupcred(auth, lookupflags); } -int +static int rpcauth_bindcred(struct rpc_task *task, struct rpc_cred *cred, int flags) { + struct rpc_rqst *req = task->tk_rqstp; struct rpc_cred *new; int lookupflags = 0; @@ -493,9 +494,9 @@ rpcauth_bindcred(struct rpc_task *task, struct rpc_cred *cred, int flags) new = rpcauth_bind_new_cred(task, lookupflags); if (IS_ERR(new)) return PTR_ERR(new); - if (task->tk_msg.rpc_cred != NULL) - put_rpccred(task->tk_msg.rpc_cred); - task->tk_msg.rpc_cred = new; + if (req->rq_cred != NULL) + put_rpccred(req->rq_cred); + req->rq_cred = new; return 0; } @@ -535,22 +536,10 @@ out_nodestroy: } EXPORT_SYMBOL_GPL(put_rpccred); -void -rpcauth_unbindcred(struct rpc_task *task) -{ - struct rpc_cred *cred = task->tk_msg.rpc_cred; - - dprintk("RPC: %5u releasing %s cred %p\n", - task->tk_pid, cred->cr_auth->au_ops->au_name, cred); - - put_rpccred(cred); - task->tk_msg.rpc_cred = NULL; -} - __be32 * rpcauth_marshcred(struct rpc_task *task, __be32 *p) { - struct rpc_cred *cred = task->tk_msg.rpc_cred; + struct rpc_cred *cred = task->tk_rqstp->rq_cred; dprintk("RPC: %5u marshaling %s cred %p\n", task->tk_pid, cred->cr_auth->au_ops->au_name, cred); @@ -561,7 +550,7 @@ rpcauth_marshcred(struct rpc_task *task, __be32 *p) __be32 * rpcauth_checkverf(struct rpc_task *task, __be32 *p) { - struct rpc_cred *cred = task->tk_msg.rpc_cred; + struct rpc_cred *cred = task->tk_rqstp->rq_cred; dprintk("RPC: %5u validating %s cred %p\n", task->tk_pid, cred->cr_auth->au_ops->au_name, cred); @@ -573,7 +562,7 @@ int rpcauth_wrap_req(struct rpc_task *task, kxdrproc_t encode, void *rqstp, __be32 *data, void *obj) { - struct rpc_cred *cred = task->tk_msg.rpc_cred; + struct rpc_cred *cred = task->tk_rqstp->rq_cred; dprintk("RPC: %5u using %s cred %p to wrap rpc data\n", task->tk_pid, cred->cr_ops->cr_name, cred); @@ -587,7 +576,7 @@ int rpcauth_unwrap_resp(struct rpc_task *task, kxdrproc_t decode, void *rqstp, __be32 *data, void *obj) { - struct rpc_cred *cred = task->tk_msg.rpc_cred; + struct rpc_cred *cred = task->tk_rqstp->rq_cred; dprintk("RPC: %5u using %s cred %p to unwrap rpc data\n", task->tk_pid, cred->cr_ops->cr_name, cred); @@ -601,13 +590,21 @@ rpcauth_unwrap_resp(struct rpc_task *task, kxdrproc_t decode, void *rqstp, int rpcauth_refreshcred(struct rpc_task *task) { - struct rpc_cred *cred = task->tk_msg.rpc_cred; + struct rpc_cred *cred = task->tk_rqstp->rq_cred; int err; + cred = task->tk_rqstp->rq_cred; + if (cred == NULL) { + err = rpcauth_bindcred(task, task->tk_msg.rpc_cred, task->tk_flags); + if (err < 0) + goto out; + cred = task->tk_rqstp->rq_cred; + }; dprintk("RPC: %5u refreshing %s cred %p\n", task->tk_pid, cred->cr_auth->au_ops->au_name, cred); err = cred->cr_ops->crrefresh(task); +out: if (err < 0) task->tk_status = err; return err; @@ -616,7 +613,7 @@ rpcauth_refreshcred(struct rpc_task *task) void rpcauth_invalcred(struct rpc_task *task) { - struct rpc_cred *cred = task->tk_msg.rpc_cred; + struct rpc_cred *cred = task->tk_rqstp->rq_cred; dprintk("RPC: %5u invalidating %s cred %p\n", task->tk_pid, cred->cr_auth->au_ops->au_name, cred); @@ -627,7 +624,7 @@ rpcauth_invalcred(struct rpc_task *task) int rpcauth_uptodatecred(struct rpc_task *task) { - struct rpc_cred *cred = task->tk_msg.rpc_cred; + struct rpc_cred *cred = task->tk_rqstp->rq_cred; return cred == NULL || test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) != 0; diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index 8da2a0e68574..096e1260bc67 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -373,7 +373,7 @@ gss_handle_downcall_result(struct gss_cred *gss_cred, struct gss_upcall_msg *gss static void gss_upcall_callback(struct rpc_task *task) { - struct gss_cred *gss_cred = container_of(task->tk_msg.rpc_cred, + struct gss_cred *gss_cred = container_of(task->tk_rqstp->rq_cred, struct gss_cred, gc_base); struct gss_upcall_msg *gss_msg = gss_cred->gc_upcall; struct inode *inode = &gss_msg->inode->vfs_inode; @@ -502,7 +502,7 @@ static void warn_gssd(void) static inline int gss_refresh_upcall(struct rpc_task *task) { - struct rpc_cred *cred = task->tk_msg.rpc_cred; + struct rpc_cred *cred = task->tk_rqstp->rq_cred; struct gss_auth *gss_auth = container_of(cred->cr_auth, struct gss_auth, rpc_auth); struct gss_cred *gss_cred = container_of(cred, @@ -1064,12 +1064,12 @@ out: static __be32 * gss_marshal(struct rpc_task *task, __be32 *p) { - struct rpc_cred *cred = task->tk_msg.rpc_cred; + struct rpc_rqst *req = task->tk_rqstp; + struct rpc_cred *cred = req->rq_cred; struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base); struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred); __be32 *cred_len; - struct rpc_rqst *req = task->tk_rqstp; u32 maj_stat = 0; struct xdr_netobj mic; struct kvec iov; @@ -1119,7 +1119,7 @@ out_put_ctx: static int gss_renew_cred(struct rpc_task *task) { - struct rpc_cred *oldcred = task->tk_msg.rpc_cred; + struct rpc_cred *oldcred = task->tk_rqstp->rq_cred; struct gss_cred *gss_cred = container_of(oldcred, struct gss_cred, gc_base); @@ -1133,7 +1133,7 @@ static int gss_renew_cred(struct rpc_task *task) new = gss_lookup_cred(auth, &acred, RPCAUTH_LOOKUP_NEW); if (IS_ERR(new)) return PTR_ERR(new); - task->tk_msg.rpc_cred = new; + task->tk_rqstp->rq_cred = new; put_rpccred(oldcred); return 0; } @@ -1161,7 +1161,7 @@ static int gss_cred_is_negative_entry(struct rpc_cred *cred) static int gss_refresh(struct rpc_task *task) { - struct rpc_cred *cred = task->tk_msg.rpc_cred; + struct rpc_cred *cred = task->tk_rqstp->rq_cred; int ret = 0; if (gss_cred_is_negative_entry(cred)) @@ -1172,7 +1172,7 @@ gss_refresh(struct rpc_task *task) ret = gss_renew_cred(task); if (ret < 0) goto out; - cred = task->tk_msg.rpc_cred; + cred = task->tk_rqstp->rq_cred; } if (test_bit(RPCAUTH_CRED_NEW, &cred->cr_flags)) @@ -1191,7 +1191,7 @@ gss_refresh_null(struct rpc_task *task) static __be32 * gss_validate(struct rpc_task *task, __be32 *p) { - struct rpc_cred *cred = task->tk_msg.rpc_cred; + struct rpc_cred *cred = task->tk_rqstp->rq_cred; struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred); __be32 seq; struct kvec iov; @@ -1400,7 +1400,7 @@ static int gss_wrap_req(struct rpc_task *task, kxdrproc_t encode, void *rqstp, __be32 *p, void *obj) { - struct rpc_cred *cred = task->tk_msg.rpc_cred; + struct rpc_cred *cred = task->tk_rqstp->rq_cred; struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base); struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred); @@ -1503,7 +1503,7 @@ static int gss_unwrap_resp(struct rpc_task *task, kxdrproc_t decode, void *rqstp, __be32 *p, void *obj) { - struct rpc_cred *cred = task->tk_msg.rpc_cred; + struct rpc_cred *cred = task->tk_rqstp->rq_cred; struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base); struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred); diff --git a/net/sunrpc/auth_null.c b/net/sunrpc/auth_null.c index 1db618f56ecb..a5c36c01707b 100644 --- a/net/sunrpc/auth_null.c +++ b/net/sunrpc/auth_null.c @@ -75,7 +75,7 @@ nul_marshal(struct rpc_task *task, __be32 *p) static int nul_refresh(struct rpc_task *task) { - set_bit(RPCAUTH_CRED_UPTODATE, &task->tk_msg.rpc_cred->cr_flags); + set_bit(RPCAUTH_CRED_UPTODATE, &task->tk_rqstp->rq_cred->cr_flags); return 0; } diff --git a/net/sunrpc/auth_unix.c b/net/sunrpc/auth_unix.c index d5e37dbf207b..4cb70dc6e7ad 100644 --- a/net/sunrpc/auth_unix.c +++ b/net/sunrpc/auth_unix.c @@ -140,7 +140,7 @@ static __be32 * unx_marshal(struct rpc_task *task, __be32 *p) { struct rpc_clnt *clnt = task->tk_client; - struct unx_cred *cred = container_of(task->tk_msg.rpc_cred, struct unx_cred, uc_base); + struct unx_cred *cred = container_of(task->tk_rqstp->rq_cred, struct unx_cred, uc_base); __be32 *base, *hold; int i; @@ -173,7 +173,7 @@ unx_marshal(struct rpc_task *task, __be32 *p) static int unx_refresh(struct rpc_task *task) { - set_bit(RPCAUTH_CRED_UPTODATE, &task->tk_msg.rpc_cred->cr_flags); + set_bit(RPCAUTH_CRED_UPTODATE, &task->tk_rqstp->rq_cred->cr_flags); return 0; } @@ -196,7 +196,7 @@ unx_validate(struct rpc_task *task, __be32 *p) printk("RPC: giant verf size: %u\n", size); return NULL; } - task->tk_msg.rpc_cred->cr_auth->au_rslack = (size >> 2) + 2; + task->tk_rqstp->rq_cred->cr_auth->au_rslack = (size >> 2) + 2; p += (size >> 2); return p; diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index f34b5e3823c0..2388d83b68ff 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -605,8 +605,8 @@ rpc_task_set_rpc_message(struct rpc_task *task, const struct rpc_message *msg) task->tk_msg.rpc_proc = msg->rpc_proc; task->tk_msg.rpc_argp = msg->rpc_argp; task->tk_msg.rpc_resp = msg->rpc_resp; - /* Bind the user cred */ - task->tk_status = rpcauth_bindcred(task, msg->rpc_cred, task->tk_flags); + if (msg->rpc_cred != NULL) + task->tk_msg.rpc_cred = get_rpccred(msg->rpc_cred); } } @@ -909,11 +909,6 @@ call_reserve(struct rpc_task *task) { dprint_status(task); - if (!rpcauth_uptodatecred(task)) { - task->tk_action = call_refresh; - return; - } - task->tk_status = 0; task->tk_action = call_reserveresult; xprt_reserve(task); @@ -977,7 +972,7 @@ call_reserveresult(struct rpc_task *task) static void call_allocate(struct rpc_task *task) { - unsigned int slack = task->tk_msg.rpc_cred->cr_auth->au_cslack; + unsigned int slack = task->tk_client->cl_auth->au_cslack; struct rpc_rqst *req = task->tk_rqstp; struct rpc_xprt *xprt = task->tk_xprt; struct rpc_procinfo *proc = task->tk_msg.rpc_proc; @@ -985,7 +980,7 @@ call_allocate(struct rpc_task *task) dprint_status(task); task->tk_status = 0; - task->tk_action = call_bind; + task->tk_action = call_refresh; if (req->rq_buffer) return; @@ -1022,6 +1017,47 @@ call_allocate(struct rpc_task *task) rpc_exit(task, -ERESTARTSYS); } +/* + * 2a. Bind and/or refresh the credentials + */ +static void +call_refresh(struct rpc_task *task) +{ + dprint_status(task); + + task->tk_action = call_refreshresult; + task->tk_status = 0; + task->tk_client->cl_stats->rpcauthrefresh++; + rpcauth_refreshcred(task); +} + +/* + * 2b. Process the results of a credential refresh + */ +static void +call_refreshresult(struct rpc_task *task) +{ + int status = task->tk_status; + + dprint_status(task); + + task->tk_status = 0; + task->tk_action = call_bind; + if (status >= 0 && rpcauth_uptodatecred(task)) + return; + switch (status) { + case -EACCES: + rpc_exit(task, -EACCES); + return; + case -ENOMEM: + rpc_exit(task, -ENOMEM); + return; + case -ETIMEDOUT: + rpc_delay(task, 3*HZ); + } + task->tk_action = call_refresh; +} + static inline int rpc_task_need_encode(struct rpc_task *task) { @@ -1557,43 +1593,6 @@ out_retry: } } -/* - * 8. Refresh the credentials if rejected by the server - */ -static void -call_refresh(struct rpc_task *task) -{ - dprint_status(task); - - task->tk_action = call_refreshresult; - task->tk_status = 0; - task->tk_client->cl_stats->rpcauthrefresh++; - rpcauth_refreshcred(task); -} - -/* - * 8a. Process the results of a credential refresh - */ -static void -call_refreshresult(struct rpc_task *task) -{ - int status = task->tk_status; - - dprint_status(task); - - task->tk_status = 0; - task->tk_action = call_reserve; - if (status >= 0 && rpcauth_uptodatecred(task)) - return; - if (status == -EACCES) { - rpc_exit(task, -EACCES); - return; - } - task->tk_action = call_refresh; - if (status != -ETIMEDOUT) - rpc_delay(task, 3*HZ); -} - static __be32 * rpc_encode_header(struct rpc_task *task) { diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index a42296db2ecd..f6db6131fb2e 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -864,7 +864,7 @@ void rpc_put_task(struct rpc_task *task) if (task->tk_rqstp) xprt_release(task); if (task->tk_msg.rpc_cred) - rpcauth_unbindcred(task); + put_rpccred(task->tk_msg.rpc_cred); rpc_task_release_client(task); if (task->tk_workqueue != NULL) { INIT_WORK(&task->u.tk_work, rpc_async_release); diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index dcd0132396ba..70297836a191 100644 --- a/net/sunrpc/xprt.c +++ b/net/sunrpc/xprt.c @@ -1032,6 +1032,8 @@ void xprt_release(struct rpc_task *task) spin_unlock_bh(&xprt->transport_lock); if (req->rq_buffer) xprt->ops->buf_free(req->rq_buffer); + if (req->rq_cred != NULL) + put_rpccred(req->rq_cred); task->tk_rqstp = NULL; if (req->rq_release_snd_buf) req->rq_release_snd_buf(req); -- cgit v1.2.3 From 73b2c7165b76b20eb1290e7efebc33cfd21db1ca Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 30 Jul 2010 21:02:47 +0200 Subject: fix comment typo "choosed" -> "chosen" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Uwe Kleine-König Signed-off-by: Jiri Kosina --- fs/ext4/mballoc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c index 12b3bc026a68..0e83dfd351d5 100644 --- a/fs/ext4/mballoc.c +++ b/fs/ext4/mballoc.c @@ -2704,7 +2704,7 @@ void exit_ext4_mballoc(void) /* - * Check quota and mark choosed space (ac->ac_b_ex) non-free in bitmaps + * Check quota and mark chosen space (ac->ac_b_ex) non-free in bitmaps * Returns 0 if success or error code */ static noinline_for_stack int -- cgit v1.2.3 From e75aa85892b2ee78c79edac720868cbef16e62eb Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 4 Aug 2010 17:59:39 +0200 Subject: block_dev: always serialize exclusive open attempts bd_prepare_to_claim() incorrectly allowed multiple attempts for exclusive open to progress in parallel if the attempting holders are identical. This triggered BUG_ON() as reported in the following bug. https://bugzilla.kernel.org/show_bug.cgi?id=16393 __bd_abort_claiming() is used to finish claiming blocks and doesn't work if multiple openers are inside a claiming block. Allowing multiple parallel open attempts to continue doesn't gain anything as those are serialized down in the call chain anyway. Fix it by always allowing only single open attempt in a claiming block. This problem can easily be reproduced by adding a delay after bd_prepare_to_claim() and attempting to mount two partitions of a disk. stable: only applicable to v2.6.35 Signed-off-by: Tejun Heo Reported-by: Markus Trippelsdorf Cc: stable@kernel.org Signed-off-by: Linus Torvalds --- fs/block_dev.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/block_dev.c b/fs/block_dev.c index 99d6af811747..b3171fb0dc9a 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -681,8 +681,8 @@ retry: if (!bd_may_claim(bdev, whole, holder)) return -EBUSY; - /* if someone else is claiming, wait for it to finish */ - if (whole->bd_claiming && whole->bd_claiming != holder) { + /* if claiming is already in progress, wait for it to finish */ + if (whole->bd_claiming) { wait_queue_head_t *wq = bit_waitqueue(&whole->bd_claiming, 0); DEFINE_WAIT(wait); -- cgit v1.2.3 From 79cb8ced7eef53856b5a877db0544acf52e00c80 Mon Sep 17 00:00:00 2001 From: Chan Jeong Date: Thu, 5 Aug 2010 02:29:59 +0100 Subject: Squashfs: Add LZO compression support Signed-off-by: Chan Jeong Signed-off-by: Phillip Lougher --- fs/squashfs/Kconfig | 5 ++ fs/squashfs/Makefile | 2 +- fs/squashfs/decompressor.c | 6 ++ fs/squashfs/lzo_wrapper.c | 134 +++++++++++++++++++++++++++++++++++++++++++++ fs/squashfs/squashfs.h | 3 + 5 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 fs/squashfs/lzo_wrapper.c (limited to 'fs') diff --git a/fs/squashfs/Kconfig b/fs/squashfs/Kconfig index 3e9ee95ef37d..3da01108c44e 100644 --- a/fs/squashfs/Kconfig +++ b/fs/squashfs/Kconfig @@ -37,6 +37,11 @@ config SQUASHFS_XATTR If unsure, say N. +config SQUASHFS_LZO + bool "Include support for LZO compressed file systems" + depends on SQUASHFS + select LZO_DECOMPRESS + config SQUASHFS_EMBEDDED bool "Additional option for memory-constrained systems" diff --git a/fs/squashfs/Makefile b/fs/squashfs/Makefile index 6fa6b9deecd3..7672bac8d328 100644 --- a/fs/squashfs/Makefile +++ b/fs/squashfs/Makefile @@ -6,4 +6,4 @@ obj-$(CONFIG_SQUASHFS) += squashfs.o squashfs-y += block.o cache.o dir.o export.o file.o fragment.o id.o inode.o squashfs-y += namei.o super.o symlink.o zlib_wrapper.o decompressor.o squashfs-$(CONFIG_SQUASHFS_XATTR) += xattr.o xattr_id.o - +squashfs-$(CONFIG_SQUASHFS_LZO) += lzo_wrapper.o diff --git a/fs/squashfs/decompressor.c b/fs/squashfs/decompressor.c index 157478da6ac9..24af9ce9722f 100644 --- a/fs/squashfs/decompressor.c +++ b/fs/squashfs/decompressor.c @@ -40,9 +40,11 @@ static const struct squashfs_decompressor squashfs_lzma_unsupported_comp_ops = { NULL, NULL, NULL, LZMA_COMPRESSION, "lzma", 0 }; +#ifndef CONFIG_SQUASHFS_LZO static const struct squashfs_decompressor squashfs_lzo_unsupported_comp_ops = { NULL, NULL, NULL, LZO_COMPRESSION, "lzo", 0 }; +#endif static const struct squashfs_decompressor squashfs_unknown_comp_ops = { NULL, NULL, NULL, 0, "unknown", 0 @@ -51,7 +53,11 @@ static const struct squashfs_decompressor squashfs_unknown_comp_ops = { static const struct squashfs_decompressor *decompressor[] = { &squashfs_zlib_comp_ops, &squashfs_lzma_unsupported_comp_ops, +#ifdef CONFIG_SQUASHFS_LZO + &squashfs_lzo_comp_ops, +#else &squashfs_lzo_unsupported_comp_ops, +#endif &squashfs_unknown_comp_ops }; diff --git a/fs/squashfs/lzo_wrapper.c b/fs/squashfs/lzo_wrapper.c new file mode 100644 index 000000000000..e1f86ded0ee5 --- /dev/null +++ b/fs/squashfs/lzo_wrapper.c @@ -0,0 +1,134 @@ +/* + * Squashfs - a compressed read only filesystem for Linux + * + * Copyright (c) 2010 LG Electronics + * Chan Jeong + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * lzo_wrapper.c + */ + +#include +#include +#include +#include +#include + +#include "squashfs_fs.h" +#include "squashfs_fs_sb.h" +#include "squashfs_fs_i.h" +#include "squashfs.h" +#include "decompressor.h" + +struct squashfs_lzo { + void *input; + void *output; +}; + +static void *lzo_init(struct squashfs_sb_info *msblk) +{ + struct squashfs_lzo *stream = kzalloc(sizeof(*stream), GFP_KERNEL); + if (stream == NULL) + goto failed; + stream->input = vmalloc(msblk->block_size); + if (stream->input == NULL) + goto failed; + stream->output = vmalloc(msblk->block_size); + if (stream->output == NULL) + goto failed2; + + return stream; + +failed2: + vfree(stream->input); +failed: + ERROR("Failed to allocate lzo workspace\n"); + kfree(stream); + return NULL; +} + + +static void lzo_free(void *strm) +{ + struct squashfs_lzo *stream = strm; + + if (stream) { + vfree(stream->input); + vfree(stream->output); + } + kfree(stream); +} + + +static int lzo_uncompress(struct squashfs_sb_info *msblk, void **buffer, + struct buffer_head **bh, int b, int offset, int length, int srclength, + int pages) +{ + struct squashfs_lzo *stream = msblk->stream; + void *buff = stream->input; + int avail, i, bytes = length, res; + size_t out_len = msblk->block_size; + + mutex_lock(&msblk->read_data_mutex); + + for (i = 0; i < b; i++) { + wait_on_buffer(bh[i]); + if (!buffer_uptodate(bh[i])) + goto block_release; + + avail = min(bytes, msblk->devblksize - offset); + memcpy(buff, bh[i]->b_data + offset, avail); + buff += avail; + bytes -= avail; + offset = 0; + put_bh(bh[i]); + } + + res = lzo1x_decompress_safe(stream->input, (size_t)length, + stream->output, &out_len); + if (res != LZO_E_OK) + goto failed; + + res = bytes = (int)out_len; + for (i = 0, buff = stream->output; bytes && i < pages; i++) { + avail = min_t(int, bytes, PAGE_CACHE_SIZE); + memcpy(buffer[i], buff, avail); + buff += avail; + bytes -= avail; + } + + mutex_unlock(&msblk->read_data_mutex); + return res; + +block_release: + for (; i < b; i++) + put_bh(bh[i]); + +failed: + mutex_unlock(&msblk->read_data_mutex); + + ERROR("lzo decompression failed, data probably corrupt\n"); + return -EIO; +} + +const struct squashfs_decompressor squashfs_lzo_comp_ops = { + .init = lzo_init, + .free = lzo_free, + .decompress = lzo_uncompress, + .id = LZO_COMPRESSION, + .name = "lzo", + .supported = 1 +}; diff --git a/fs/squashfs/squashfs.h b/fs/squashfs/squashfs.h index 733a17c42945..3a95ea68b00f 100644 --- a/fs/squashfs/squashfs.h +++ b/fs/squashfs/squashfs.h @@ -104,3 +104,6 @@ extern const struct xattr_handler *squashfs_xattr_handlers[]; /* zlib_wrapper.c */ extern const struct squashfs_decompressor squashfs_zlib_comp_ops; + +/* lzo_wrappers.c */ +extern const struct squashfs_decompressor squashfs_lzo_comp_ops; -- cgit v1.2.3 From f3065f60ddfd4b5e34a412851d91d0cf27cdbf7e Mon Sep 17 00:00:00 2001 From: Phillip Lougher Date: Thu, 5 Aug 2010 04:51:50 +0100 Subject: Squashfs: fix block size use in LZO decompressor Sizing the buffer using block size alone is incorrect leading to a potential buffer over-run on 4K block size file systems (because the metadata block size is always 8K). Srclength is set to the maximum expected size of the decompressed block and it is block_size or 8K depending on whether a data or metadata block is being decompressed. Signed-off-by: Phillip Lougher --- fs/squashfs/lzo_wrapper.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/squashfs/lzo_wrapper.c b/fs/squashfs/lzo_wrapper.c index e1f86ded0ee5..5d87789bf1c1 100644 --- a/fs/squashfs/lzo_wrapper.c +++ b/fs/squashfs/lzo_wrapper.c @@ -40,13 +40,15 @@ struct squashfs_lzo { static void *lzo_init(struct squashfs_sb_info *msblk) { + int block_size = max_t(int, msblk->block_size, SQUASHFS_METADATA_SIZE); + struct squashfs_lzo *stream = kzalloc(sizeof(*stream), GFP_KERNEL); if (stream == NULL) goto failed; - stream->input = vmalloc(msblk->block_size); + stream->input = vmalloc(block_size); if (stream->input == NULL) goto failed; - stream->output = vmalloc(msblk->block_size); + stream->output = vmalloc(block_size); if (stream->output == NULL) goto failed2; @@ -80,7 +82,7 @@ static int lzo_uncompress(struct squashfs_sb_info *msblk, void **buffer, struct squashfs_lzo *stream = msblk->stream; void *buff = stream->input; int avail, i, bytes = length, res; - size_t out_len = msblk->block_size; + size_t out_len = srclength; mutex_lock(&msblk->read_data_mutex); -- cgit v1.2.3 From 0cfc9255a1efb0467de2162950197750570ecec0 Mon Sep 17 00:00:00 2001 From: Eric Sandeen Date: Thu, 5 Aug 2010 01:46:37 -0400 Subject: ext4: re-inline ext4_rec_len_(to|from)_disk functions commit 3d0518f4, "ext4: New rec_len encoding for very large blocksizes" made several changes to this path, but from a perf perspective, un-inlining ext4_rec_len_from_disk() seems most significant. This function is called from ext4_check_dir_entry(), which on a file-creation workload is called extremely often. I tested this with bonnie: # bonnie++ -u root -s 0 -f -x 200 -d /mnt/test -n 32 (this does 200 iterations) and got this for the file creations: ext4 stock: Average = 21206.8 files/s ext4 inlined: Average = 22346.7 files/s (+5%) Signed-off-by: Eric Sandeen Signed-off-by: "Theodore Ts'o" --- fs/ext4/ext4.h | 39 +++++++++++++++++++++++++++++++++++++-- fs/ext4/namei.c | 24 ------------------------ 2 files changed, 37 insertions(+), 26 deletions(-) (limited to 'fs') diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index ed14e1db0832..e03841d9f30b 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -1411,6 +1411,43 @@ struct ext4_dir_entry_2 { ~EXT4_DIR_ROUND) #define EXT4_MAX_REC_LEN ((1<<16)-1) +/* + * If we ever get support for fs block sizes > page_size, we'll need + * to remove the #if statements in the next two functions... + */ +static inline unsigned int +ext4_rec_len_from_disk(__le16 dlen, unsigned blocksize) +{ + unsigned len = le16_to_cpu(dlen); + +#if (PAGE_CACHE_SIZE >= 65536) + if (len == EXT4_MAX_REC_LEN || len == 0) + return blocksize; + return (len & 65532) | ((len & 3) << 16); +#else + return len; +#endif +} + +static inline __le16 ext4_rec_len_to_disk(unsigned len, unsigned blocksize) +{ + if ((len > blocksize) || (blocksize > (1 << 18)) || (len & 3)) + BUG(); +#if (PAGE_CACHE_SIZE >= 65536) + if (len < 65536) + return cpu_to_le16(len); + if (len == blocksize) { + if (blocksize == 65536) + return cpu_to_le16(EXT4_MAX_REC_LEN); + else + return cpu_to_le16(0); + } + return cpu_to_le16((len & 65532) | ((len >> 16) & 3)); +#else + return cpu_to_le16(len); +#endif +} + /* * Hash Tree Directory indexing * (c) Daniel Phillips, 2001 @@ -1636,8 +1673,6 @@ extern long ext4_compat_ioctl(struct file *, unsigned int, unsigned long); extern int ext4_ext_migrate(struct inode *); /* namei.c */ -extern unsigned int ext4_rec_len_from_disk(__le16 dlen, unsigned blocksize); -extern __le16 ext4_rec_len_to_disk(unsigned len, unsigned blocksize); extern int ext4_orphan_add(handle_t *, struct inode *); extern int ext4_orphan_del(handle_t *, struct inode *); extern int ext4_htree_fill_tree(struct file *dir_file, __u32 start_hash, diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index ea8b59d96213..314c0d3b3fa9 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -179,30 +179,6 @@ static struct buffer_head * ext4_dx_find_entry(struct inode *dir, static int ext4_dx_add_entry(handle_t *handle, struct dentry *dentry, struct inode *inode); -unsigned int ext4_rec_len_from_disk(__le16 dlen, unsigned blocksize) -{ - unsigned len = le16_to_cpu(dlen); - - if (len == EXT4_MAX_REC_LEN || len == 0) - return blocksize; - return (len & 65532) | ((len & 3) << 16); -} - -__le16 ext4_rec_len_to_disk(unsigned len, unsigned blocksize) -{ - if ((len > blocksize) || (blocksize > (1 << 18)) || (len & 3)) - BUG(); - if (len < 65536) - return cpu_to_le16(len); - if (len == blocksize) { - if (blocksize == 65536) - return cpu_to_le16(EXT4_MAX_REC_LEN); - else - return cpu_to_le16(0); - } - return cpu_to_le16((len & 65532) | ((len >> 16) & 3)); -} - /* * p is at least 6 bytes before the end of page */ -- cgit v1.2.3 From 95c99904f614f90797fc589c40c2050b8c1c42cb Mon Sep 17 00:00:00 2001 From: Suresh Jayaraman Date: Fri, 30 Jul 2010 18:01:17 +0530 Subject: cifs: update README Update the README file to reflect that now DebugData shows all the features enabled. Signed-off-by: Suresh Jayaraman Cc: Jeff Layton -- fs/cifs/README | 5 +++-- 1 files changed, 3 insertions(+), 2 deletions(-) Signed-off-by: Steve French --- fs/cifs/README | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/cifs/README b/fs/cifs/README index a727b7cb075f..a7081eeeb85d 100644 --- a/fs/cifs/README +++ b/fs/cifs/README @@ -568,8 +568,9 @@ module can be displayed via modinfo. Misc /proc/fs/cifs Flags and Debug Info ======================================= Informational pseudo-files: -DebugData Displays information about active CIFS sessions - and shares, as well as the cifs.ko version. +DebugData Displays information about active CIFS sessions and + shares, features enabled as well as the cifs.ko + version. Stats Lists summary resource usage information as well as per share statistics, if CONFIG_CIFS_STATS in enabled in the kernel configuration. -- cgit v1.2.3 From f579903ef3e392251dc7e93cb521ddb622fbf8e0 Mon Sep 17 00:00:00 2001 From: Suresh Jayaraman Date: Fri, 30 Jul 2010 18:25:56 +0530 Subject: cifs: show features compiled in as part of DebugData Fixed the nit pointed out by Jeff. From: Suresh Jayaraman Subject: [PATCH 1/2] cifs: show features compiled in as part of DebugData This patch adds the features that are compiled in to the CIFS debugging data as shown below: $cat /proc/fs/cifs/DebugData Display Internal CIFS Data Structures for Debugging --------------------------------------------------- CIFS Version 1.64 Features: dfs fscache posix spnego xattr Active VFS Requests: 0 ... This patch provides a definitive way to tell what features are currently enabled in the running kernel. This could also help debugging. Signed-off-by: Suresh Jayaraman Cc: Jeff Layton Signed-off-by: Steve French --- fs/cifs/cifs_debug.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'fs') diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c index 4fce6e61b34e..eb1ba493489f 100644 --- a/fs/cifs/cifs_debug.c +++ b/fs/cifs/cifs_debug.c @@ -119,6 +119,31 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v) "Display Internal CIFS Data Structures for Debugging\n" "---------------------------------------------------\n"); seq_printf(m, "CIFS Version %s\n", CIFS_VERSION); + seq_printf(m, "Features: "); +#ifdef CONFIG_CIFS_DFS_UPCALL + seq_printf(m, "dfs"); + seq_putc(m, ' '); +#endif +#ifdef CONFIG_CIFS_FSCACHE + seq_printf(m, "fscache"); + seq_putc(m, ' '); +#endif +#ifdef CONFIG_CIFS_WEAK_PW_HASH + seq_printf(m, "lanman"); + seq_putc(m, ' '); +#endif +#ifdef CONFIG_CIFS_POSIX + seq_printf(m, "posix"); + seq_putc(m, ' '); +#endif +#ifdef CONFIG_CIFS_UPCALL + seq_printf(m, "spnego"); + seq_putc(m, ' '); +#endif +#ifdef CONFIG_CIFS_XATTR + seq_printf(m, "xattr"); +#endif + seq_putc(m, '\n'); seq_printf(m, "Active VFS Requests: %d\n", GlobalTotalActiveXid); seq_printf(m, "Servers:"); -- cgit v1.2.3 From 67b7626a0512d12e34b38ff45e32c693cf9c79a1 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 22 Jul 2010 18:33:01 +0100 Subject: CIFS: Make cifs_convert_address() take a const src pointer and a length Make cifs_convert_address() take a const src pointer and a length so that all the strlen() calls in their can be cut out and to make it unnecessary to modify the src string. Also return the data length from dns_resolve_server_name_to_ip() so that a strlen() can be cut out of cifs_compose_mount_options() too. Acked-by: Jeff Layton Signed-off-by: David Howells Signed-off-by: Steve French --- fs/cifs/cifs_dfs_ref.c | 5 ++--- fs/cifs/cifsproto.h | 4 ++-- fs/cifs/connect.c | 1 + fs/cifs/dns_resolve.c | 20 +++++++++----------- fs/cifs/netmisc.c | 45 ++++++++++++++++++++++++--------------------- 5 files changed, 38 insertions(+), 37 deletions(-) (limited to 'fs') diff --git a/fs/cifs/cifs_dfs_ref.c b/fs/cifs/cifs_dfs_ref.c index dc1ed50ea06e..d6ced7aa23cf 100644 --- a/fs/cifs/cifs_dfs_ref.c +++ b/fs/cifs/cifs_dfs_ref.c @@ -141,7 +141,7 @@ char *cifs_compose_mount_options(const char *sb_mountdata, } rc = dns_resolve_server_name_to_ip(*devname, &srvIP); - if (rc != 0) { + if (rc < 0) { cERROR(1, "%s: Failed to resolve server part of %s to IP: %d", __func__, *devname, rc); goto compose_mount_options_err; @@ -150,8 +150,7 @@ char *cifs_compose_mount_options(const char *sb_mountdata, * assuming that we have 'unc=' and 'ip=' in * the original sb_mountdata */ - md_len = strlen(sb_mountdata) + strlen(srvIP) + - strlen(ref->node_name) + 12; + md_len = strlen(sb_mountdata) + rc + strlen(ref->node_name) + 12; mountdata = kzalloc(md_len+1, GFP_KERNEL); if (mountdata == NULL) { rc = -ENOMEM; diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 2eaebbd31132..1f5450814087 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -86,8 +86,8 @@ extern unsigned int smbCalcSize(struct smb_hdr *ptr); extern unsigned int smbCalcSize_LE(struct smb_hdr *ptr); extern int decode_negTokenInit(unsigned char *security_blob, int length, struct TCP_Server_Info *server); -extern int cifs_convert_address(struct sockaddr *dst, char *src); -extern int cifs_fill_sockaddr(struct sockaddr *dst, char *src, +extern int cifs_convert_address(struct sockaddr *dst, const char *src, int len); +extern int cifs_fill_sockaddr(struct sockaddr *dst, const char *src, int len, unsigned short int port); extern int map_smb_to_linux_error(struct smb_hdr *smb, int logErr); extern void header_assemble(struct smb_hdr *, char /* command */ , diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 2a43a0aca965..95c2ea67edfb 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -1543,6 +1543,7 @@ cifs_get_tcp_session(struct smb_vol *volume_info) if (volume_info->UNCip && volume_info->UNC) { rc = cifs_fill_sockaddr((struct sockaddr *)&addr, volume_info->UNCip, + strlen(volume_info->UNCip), volume_info->port); if (!rc) { /* we failed translating address */ diff --git a/fs/cifs/dns_resolve.c b/fs/cifs/dns_resolve.c index 3ad7f4300c45..aa967e7917f8 100644 --- a/fs/cifs/dns_resolve.c +++ b/fs/cifs/dns_resolve.c @@ -40,11 +40,11 @@ static const struct cred *dns_resolver_cache; * 0 - name is not IP */ static int -is_ip(char *name) +is_ip(const char *name, int len) { struct sockaddr_storage ss; - return cifs_convert_address((struct sockaddr *)&ss, name); + return cifs_convert_address((struct sockaddr *)&ss, name, len); } static int @@ -54,6 +54,10 @@ dns_resolver_instantiate(struct key *key, const void *data, int rc = 0; char *ip; + /* make sure this looks like an address */ + if (!is_ip(data, datalen)) + return -EINVAL; + ip = kmalloc(datalen + 1, GFP_KERNEL); if (!ip) return -ENOMEM; @@ -61,12 +65,6 @@ dns_resolver_instantiate(struct key *key, const void *data, memcpy(ip, data, datalen); ip[datalen] = '\0'; - /* make sure this looks like an address */ - if (!is_ip(ip)) { - kfree(ip); - return -EINVAL; - } - key->type_data.x[0] = datalen; key->payload.data = ip; @@ -93,7 +91,7 @@ struct key_type key_type_dns_resolver = { * unc - server UNC * output: * *ip_addr - pointer to server ip, caller responcible for freeing it. - * return 0 on success + * return the length of the returned string on success */ int dns_resolve_server_name_to_ip(const char *unc, char **ip_addr) @@ -131,7 +129,7 @@ dns_resolve_server_name_to_ip(const char *unc, char **ip_addr) memcpy(name, unc+2, len); name[len] = 0; - if (is_ip(name)) { + if (is_ip(name, len)) { cFYI(1, "%s: it is IP, skipping dns upcall: %s", __func__, name); data = name; @@ -164,7 +162,7 @@ skip_upcall: name, *ip_addr ); - rc = 0; + rc = len; } else { rc = -ENOMEM; } diff --git a/fs/cifs/netmisc.c b/fs/cifs/netmisc.c index c6721ee26dbc..f97851119e6c 100644 --- a/fs/cifs/netmisc.c +++ b/fs/cifs/netmisc.c @@ -140,17 +140,18 @@ static const struct smb_to_posix_error mapping_table_ERRHRD[] = { * Returns 0 on failure. */ static int -cifs_inet_pton(const int address_family, const char *cp, void *dst) +cifs_inet_pton(const int address_family, const char *cp, int len, void *dst) { int ret = 0; /* calculate length by finding first slash or NULL */ if (address_family == AF_INET) - ret = in4_pton(cp, -1 /* len */, dst, '\\', NULL); + ret = in4_pton(cp, len, dst, '\\', NULL); else if (address_family == AF_INET6) - ret = in6_pton(cp, -1 /* len */, dst , '\\', NULL); + ret = in6_pton(cp, len, dst , '\\', NULL); - cFYI(DBG2, "address conversion returned %d for %s", ret, cp); + cFYI(DBG2, "address conversion returned %d for %*.*s", + ret, len, len, cp); if (ret > 0) ret = 1; return ret; @@ -165,37 +166,39 @@ cifs_inet_pton(const int address_family, const char *cp, void *dst) * Returns 0 on failure. */ int -cifs_convert_address(struct sockaddr *dst, char *src) +cifs_convert_address(struct sockaddr *dst, const char *src, int len) { - int rc; - char *pct, *endp; + int rc, alen, slen; + const char *pct; + char *endp, scope_id[13]; struct sockaddr_in *s4 = (struct sockaddr_in *) dst; struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) dst; /* IPv4 address */ - if (cifs_inet_pton(AF_INET, src, &s4->sin_addr.s_addr)) { + if (cifs_inet_pton(AF_INET, src, len, &s4->sin_addr.s_addr)) { s4->sin_family = AF_INET; return 1; } - /* temporarily terminate string */ - pct = strchr(src, '%'); - if (pct) - *pct = '\0'; - - rc = cifs_inet_pton(AF_INET6, src, &s6->sin6_addr.s6_addr); - - /* repair temp termination (if any) and make pct point to scopeid */ - if (pct) - *pct++ = '%'; + /* attempt to exclude the scope ID from the address part */ + pct = memchr(src, '%', len); + alen = pct ? pct - src : len; + rc = cifs_inet_pton(AF_INET6, src, alen, &s6->sin6_addr.s6_addr); if (!rc) return rc; s6->sin6_family = AF_INET6; if (pct) { + /* grab the scope ID */ + slen = len - (alen + 1); + if (slen <= 0 || slen > 12) + return 0; + memcpy(scope_id, pct + 1, slen); + scope_id[slen] = '\0'; + s6->sin6_scope_id = (u32) simple_strtoul(pct, &endp, 0); - if (!*pct || *endp) + if (endp != scope_id + slen) return 0; } @@ -203,10 +206,10 @@ cifs_convert_address(struct sockaddr *dst, char *src) } int -cifs_fill_sockaddr(struct sockaddr *dst, char *src, +cifs_fill_sockaddr(struct sockaddr *dst, const char *src, int len, const unsigned short int port) { - if (!cifs_convert_address(dst, src)) + if (!cifs_convert_address(dst, src, len)) return 0; switch (dst->sa_family) { -- cgit v1.2.3 From 5acfec2502cf60a91dc1959e476b588ecb3a1b8a Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Mon, 2 Aug 2010 17:43:54 -0400 Subject: cifs: reduce false positives with inode aliasing serverino autodisable It turns out that not all directory inodes with dentries on the i_dentry list are unusable here. We only consider them unusable if they are still hashed or if they have a root dentry attached. Full disclosure -- this check is inherently racy. There's nothing that stops someone from slapping a new dentry onto this inode just after this check, or hashing an existing one that's already attached. So, this is really a "best effort" thing to work around misbehaving servers. Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/inode.c | 43 +++++++++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 12 deletions(-) (limited to 'fs') diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index a15b3a9bbff4..dc4c47ab9588 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -732,15 +732,9 @@ cifs_find_inode(struct inode *inode, void *opaque) if ((inode->i_mode & S_IFMT) != (fattr->cf_mode & S_IFMT)) return 0; - /* - * uh oh -- it's a directory. We can't use it since hardlinked dirs are - * verboten. Disable serverino and return it as if it were found, the - * caller can discard it, generate a uniqueid and retry the find - */ - if (S_ISDIR(inode->i_mode) && !list_empty(&inode->i_dentry)) { + /* if it's not a directory or has no dentries, then flag it */ + if (S_ISDIR(inode->i_mode) && !list_empty(&inode->i_dentry)) fattr->cf_flags |= CIFS_FATTR_INO_COLLISION; - cifs_autodisable_serverino(CIFS_SB(inode->i_sb)); - } return 1; } @@ -754,6 +748,27 @@ cifs_init_inode(struct inode *inode, void *opaque) return 0; } +/* + * walk dentry list for an inode and report whether it has aliases that + * are hashed. We use this to determine if a directory inode can actually + * be used. + */ +static bool +inode_has_hashed_dentries(struct inode *inode) +{ + struct dentry *dentry; + + spin_lock(&dcache_lock); + list_for_each_entry(dentry, &inode->i_dentry, d_alias) { + if (!d_unhashed(dentry) || IS_ROOT(dentry)) { + spin_unlock(&dcache_lock); + return true; + } + } + spin_unlock(&dcache_lock); + return false; +} + /* Given fattrs, get a corresponding inode */ struct inode * cifs_iget(struct super_block *sb, struct cifs_fattr *fattr) @@ -769,12 +784,16 @@ retry_iget5_locked: inode = iget5_locked(sb, hash, cifs_find_inode, cifs_init_inode, fattr); if (inode) { - /* was there a problematic inode number collision? */ + /* was there a potentially problematic inode collision? */ if (fattr->cf_flags & CIFS_FATTR_INO_COLLISION) { - iput(inode); - fattr->cf_uniqueid = iunique(sb, ROOT_I); fattr->cf_flags &= ~CIFS_FATTR_INO_COLLISION; - goto retry_iget5_locked; + + if (inode_has_hashed_dentries(inode)) { + cifs_autodisable_serverino(CIFS_SB(sb)); + iput(inode); + fattr->cf_uniqueid = iunique(sb, ROOT_I); + goto retry_iget5_locked; + } } cifs_fattr_to_inode(inode, fattr); -- cgit v1.2.3 From ba5dadbf4e7b531bd7ccecffb4d3935c80a3372e Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Tue, 3 Aug 2010 10:19:50 -0400 Subject: cifs: account for new creduid=0x%x parameter in spnego upcall string The commit that added the creduid=0x%x parameter failed to increase the buffer allocation to account for it. Reported-by: J. Bruce Fields Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/cifs_spnego.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'fs') diff --git a/fs/cifs/cifs_spnego.c b/fs/cifs/cifs_spnego.c index 6effccff85a5..87044906cd1f 100644 --- a/fs/cifs/cifs_spnego.c +++ b/fs/cifs/cifs_spnego.c @@ -84,6 +84,9 @@ struct key_type cifs_spnego_key_type = { /* strlen of ";uid=0x" */ #define UID_KEY_LEN 7 +/* strlen of ";creduid=0x" */ +#define CREDUID_KEY_LEN 11 + /* strlen of ";user=" */ #define USER_KEY_LEN 6 @@ -107,6 +110,7 @@ cifs_get_spnego_key(struct cifsSesInfo *sesInfo) IP_KEY_LEN + INET6_ADDRSTRLEN + MAX_MECH_STR_LEN + UID_KEY_LEN + (sizeof(uid_t) * 2) + + CREDUID_KEY_LEN + (sizeof(uid_t) * 2) + USER_KEY_LEN + strlen(sesInfo->userName) + PID_KEY_LEN + (sizeof(pid_t) * 2) + 1; -- cgit v1.2.3 From 1a4240f4764ac78adbf4b0ebb49b3bd8c72ffa11 Mon Sep 17 00:00:00 2001 From: Wang Lei Date: Wed, 4 Aug 2010 15:16:33 +0100 Subject: DNS: Separate out CIFS DNS Resolver code Separate out the DNS resolver key type from the CIFS filesystem into its own module so that it can be made available for general use, including the AFS filesystem module. This facility makes it possible for the kernel to upcall to userspace to have it issue DNS requests, package up the replies and present them to the kernel in a useful form. The kernel is then able to cache the DNS replies as keys can be retained in keyrings. Resolver keys are of type "dns_resolver" and have a case-insensitive description that is of the form "[:]". The optional indicates the particular DNS lookup and packaging that's required. The is the query to be made. If isn't given, a basic hostname to IP address lookup is made, and the result is stored in the key in the form of a printable string consisting of a comma-separated list of IPv4 and IPv6 addresses. This key type is supported by userspace helpers driven from /sbin/request-key and configured through /etc/request-key.conf. The cifs.upcall utility is invoked for UNC path server name to IP address resolution. The CIFS functionality is encapsulated by the dns_resolve_unc_to_ip() function, which is used to resolve a UNC path to an IP address for CIFS filesystem. This part remains in the CIFS module for now. See the added Documentation/networking/dns_resolver.txt for more information. Signed-off-by: Wang Lei Signed-off-by: David Howells Acked-by: Jeff Layton Signed-off-by: Steve French --- Documentation/networking/dns_resolver.txt | 146 +++++++++++++++++++ fs/cifs/Kconfig | 17 +-- fs/cifs/cifsfs.c | 13 +- fs/cifs/dns_resolve.c | 229 ++++++------------------------ fs/cifs/dns_resolve.h | 2 - include/keys/dns_resolver-type.h | 23 +++ include/linux/dns_resolver.h | 34 +++++ net/Kconfig | 1 + net/Makefile | 1 + net/dns_resolver/Kconfig | 27 ++++ net/dns_resolver/Makefile | 7 + net/dns_resolver/dns_key.c | 210 +++++++++++++++++++++++++++ net/dns_resolver/dns_query.c | 159 +++++++++++++++++++++ net/dns_resolver/internal.h | 44 ++++++ 14 files changed, 708 insertions(+), 205 deletions(-) create mode 100644 Documentation/networking/dns_resolver.txt create mode 100644 include/keys/dns_resolver-type.h create mode 100644 include/linux/dns_resolver.h create mode 100644 net/dns_resolver/Kconfig create mode 100644 net/dns_resolver/Makefile create mode 100644 net/dns_resolver/dns_key.c create mode 100644 net/dns_resolver/dns_query.c create mode 100644 net/dns_resolver/internal.h (limited to 'fs') diff --git a/Documentation/networking/dns_resolver.txt b/Documentation/networking/dns_resolver.txt new file mode 100644 index 000000000000..d8e0ce1d38c7 --- /dev/null +++ b/Documentation/networking/dns_resolver.txt @@ -0,0 +1,146 @@ + =================== + DNS Resolver Module + =================== + +Contents: + + - Overview. + - Compilation. + - Setting up. + - Usage. + - Mechanism. + - Debugging. + + +======== +OVERVIEW +======== + +The DNS resolver module provides a way for kernel services to make DNS queries +by way of requesting a key of key type dns_resolver. These queries are +upcalled to userspace through /sbin/request-key. + +These routines must be supported by userspace tools dns.upcall, cifs.upcall and +request-key. It is under development and does not yet provide the full feature +set. The features it does support include: + + (*) Implements the dns_resolver key_type to contact userspace. + +It does not yet support the following AFS features: + + (*) Dns query support for AFSDB resource record. + +This code is extracted from the CIFS filesystem. + + +=========== +COMPILATION +=========== + +The module should be enabled by turning on the kernel configuration options: + + CONFIG_DNS_RESOLVER - tristate "DNS Resolver support" + + +========== +SETTING UP +========== + +To set up this facility, the /etc/request-key.conf file must be altered so that +/sbin/request-key can appropriately direct the upcalls. For example, to handle +basic dname to IPv4/IPv6 address resolution, the following line should be +added: + + #OP TYPE DESC CO-INFO PROGRAM ARG1 ARG2 ARG3 ... + #====== ============ ======= ======= ========================== + create dns_resolver * * /usr/sbin/cifs.upcall %k + +To direct a query for query type 'foo', a line of the following should be added +before the more general line given above as the first match is the one taken. + + create dns_resolver foo:* * /usr/sbin/dns.foo %k + + + +===== +USAGE +===== + +To make use of this facility, one of the following functions that are +implemented in the module can be called after doing: + + #include + + (1) int dns_query(const char *type, const char *name, size_t namelen, + const char *options, char **_result, time_t *_expiry); + + This is the basic access function. It looks for a cached DNS query and if + it doesn't find it, it upcalls to userspace to make a new DNS query, which + may then be cached. The key description is constructed as a string of the + form: + + [:] + + where optionally specifies the particular upcall program to invoke, + and thus the type of query to do, and specifies the string to be + looked up. The default query type is a straight hostname to IP address + set lookup. + + The name parameter is not required to be a NUL-terminated string, and its + length should be given by the namelen argument. + + The options parameter may be NULL or it may be a set of options + appropriate to the query type. + + The return value is a string appropriate to the query type. For instance, + for the default query type it is just a list of comma-separated IPv4 and + IPv6 addresses. The caller must free the result. + + The length of the result string is returned on success, and a -ve error + code is returned otherwise. -EKEYREJECTED will be returned if the DNS + lookup failed. + + If _expiry is non-NULL, the expiry time (TTL) of the result will be + returned also. + + +========= +MECHANISM +========= + +The dnsresolver module registers a key type called "dns_resolver". Keys of +this type are used to transport and cache DNS lookup results from userspace. + +When dns_query() is invoked, it calls request_key() to search the local +keyrings for a cached DNS result. If that fails to find one, it upcalls to +userspace to get a new result. + +Upcalls to userspace are made through the request_key() upcall vector, and are +directed by means of configuration lines in /etc/request-key.conf that tell +/sbin/request-key what program to run to instantiate the key. + +The upcall handler program is responsible for querying the DNS, processing the +result into a form suitable for passing to the keyctl_instantiate_key() +routine. This then passes the data to dns_resolver_instantiate() which strips +off and processes any options included in the data, and then attaches the +remainder of the string to the key as its payload. + +The upcall handler program should set the expiry time on the key to that of the +lowest TTL of all the records it has extracted a result from. This means that +the key will be discarded and recreated when the data it holds has expired. + +dns_query() returns a copy of the value attached to the key, or an error if +that is indicated instead. + +See for further information about +request-key function. + + +========= +DEBUGGING +========= + +Debugging messages can be turned on dynamically by writing a 1 into the +following file: + + /sys/module/dnsresolver/parameters/debug diff --git a/fs/cifs/Kconfig b/fs/cifs/Kconfig index 5739fd7f88b4..57f0aa9f141f 100644 --- a/fs/cifs/Kconfig +++ b/fs/cifs/Kconfig @@ -71,14 +71,14 @@ config CIFS_WEAK_PW_HASH If unsure, say N. config CIFS_UPCALL - bool "Kerberos/SPNEGO advanced session setup" - depends on CIFS && KEYS - help - Enables an upcall mechanism for CIFS which accesses - userspace helper utilities to provide SPNEGO packaged (RFC 4178) - Kerberos tickets which are needed to mount to certain secure servers - (for which more secure Kerberos authentication is required). If - unsure, say N. + bool "Kerberos/SPNEGO advanced session setup" + depends on CIFS && KEYS + select DNS_RESOLVER + help + Enables an upcall mechanism for CIFS which accesses userspace helper + utilities to provide SPNEGO packaged (RFC 4178) Kerberos tickets + which are needed to mount to certain secure servers (for which more + secure Kerberos authentication is required). If unsure, say N. config CIFS_XATTR bool "CIFS extended attributes" @@ -122,6 +122,7 @@ config CIFS_DEBUG2 config CIFS_DFS_UPCALL bool "DFS feature support" depends on CIFS && KEYS + select DNS_RESOLVER help Distributed File System (DFS) support is used to access shares transparently in an enterprise name space, even if the share diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 8a2cf129e535..2a0c892959f4 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -45,7 +45,6 @@ #include "cifs_fs_sb.h" #include #include -#include "dns_resolve.h" #include "cifs_spnego.h" #include "fscache.h" #define CIFS_MAGIC_NUMBER 0xFF534D42 /* the first four bytes of SMB PDUs */ @@ -933,23 +932,14 @@ init_cifs(void) rc = register_key_type(&cifs_spnego_key_type); if (rc) goto out_unregister_filesystem; -#endif -#ifdef CONFIG_CIFS_DFS_UPCALL - rc = cifs_init_dns_resolver(); - if (rc) - goto out_unregister_key_type; #endif rc = slow_work_register_user(THIS_MODULE); if (rc) - goto out_unregister_resolver_key; + goto out_unregister_key_type; return 0; - out_unregister_resolver_key: -#ifdef CONFIG_CIFS_DFS_UPCALL - cifs_exit_dns_resolver(); out_unregister_key_type: -#endif #ifdef CONFIG_CIFS_UPCALL unregister_key_type(&cifs_spnego_key_type); out_unregister_filesystem: @@ -976,7 +966,6 @@ exit_cifs(void) cifs_fscache_unregister(); #ifdef CONFIG_CIFS_DFS_UPCALL cifs_dfs_release_automount_timer(); - cifs_exit_dns_resolver(); #endif #ifdef CONFIG_CIFS_UPCALL unregister_key_type(&cifs_spnego_key_type); diff --git a/fs/cifs/dns_resolve.c b/fs/cifs/dns_resolve.c index aa967e7917f8..0eb87026cad3 100644 --- a/fs/cifs/dns_resolve.c +++ b/fs/cifs/dns_resolve.c @@ -4,6 +4,8 @@ * Copyright (c) 2007 Igor Mammedov * Author(s): Igor Mammedov (niallain@gmail.com) * Steve French (sfrench@us.ibm.com) + * Wang Lei (wang840925@gmail.com) + * David Howells (dhowells@redhat.com) * * Contains the CIFS DFS upcall routines used for hostname to * IP address translation. @@ -24,212 +26,73 @@ */ #include -#include -#include -#include +#include #include "dns_resolve.h" #include "cifsglob.h" #include "cifsproto.h" #include "cifs_debug.h" -static const struct cred *dns_resolver_cache; - -/* Checks if supplied name is IP address - * returns: - * 1 - name is IP - * 0 - name is not IP - */ -static int -is_ip(const char *name, int len) -{ - struct sockaddr_storage ss; - - return cifs_convert_address((struct sockaddr *)&ss, name, len); -} - -static int -dns_resolver_instantiate(struct key *key, const void *data, - size_t datalen) -{ - int rc = 0; - char *ip; - - /* make sure this looks like an address */ - if (!is_ip(data, datalen)) - return -EINVAL; - - ip = kmalloc(datalen + 1, GFP_KERNEL); - if (!ip) - return -ENOMEM; - - memcpy(ip, data, datalen); - ip[datalen] = '\0'; - - key->type_data.x[0] = datalen; - key->payload.data = ip; - - return rc; -} - -static void -dns_resolver_destroy(struct key *key) -{ - kfree(key->payload.data); -} - -struct key_type key_type_dns_resolver = { - .name = "dns_resolver", - .def_datalen = sizeof(struct in_addr), - .describe = user_describe, - .instantiate = dns_resolver_instantiate, - .destroy = dns_resolver_destroy, - .match = user_match, -}; - -/* Resolves server name to ip address. - * input: - * unc - server UNC - * output: - * *ip_addr - pointer to server ip, caller responcible for freeing it. - * return the length of the returned string on success +/** + * dns_resolve_server_name_to_ip - Resolve UNC server name to ip address. + * @unc: UNC path specifying the server + * @ip_addr: Where to return the IP address. + * + * The IP address will be returned in string form, and the caller is + * responsible for freeing it. + * + * Returns length of result on success, -ve on error. */ int dns_resolve_server_name_to_ip(const char *unc, char **ip_addr) { - const struct cred *saved_cred; - int rc = -EAGAIN; - struct key *rkey = ERR_PTR(-EAGAIN); + struct sockaddr_storage ss; + const char *hostname, *sep; char *name; - char *data = NULL; - int len; + int len, rc; if (!ip_addr || !unc) return -EINVAL; - /* search for server name delimiter */ len = strlen(unc); if (len < 3) { cFYI(1, "%s: unc is too short: %s", __func__, unc); return -EINVAL; } - len -= 2; - name = memchr(unc+2, '\\', len); - if (!name) { - cFYI(1, "%s: probably server name is whole unc: %s", - __func__, unc); - } else { - len = (name - unc) - 2/* leading // */; - } - - name = kmalloc(len+1, GFP_KERNEL); - if (!name) { - rc = -ENOMEM; - return rc; - } - memcpy(name, unc+2, len); - name[len] = 0; - - if (is_ip(name, len)) { - cFYI(1, "%s: it is IP, skipping dns upcall: %s", - __func__, name); - data = name; - goto skip_upcall; - } - saved_cred = override_creds(dns_resolver_cache); - rkey = request_key(&key_type_dns_resolver, name, ""); - revert_creds(saved_cred); - if (!IS_ERR(rkey)) { - if (!(rkey->perm & KEY_USR_VIEW)) { - down_read(&rkey->sem); - rkey->perm |= KEY_USR_VIEW; - up_read(&rkey->sem); - } - len = rkey->type_data.x[0]; - data = rkey->payload.data; - } else { - cERROR(1, "%s: unable to resolve: %s", __func__, name); - goto out; - } - -skip_upcall: - if (data) { - *ip_addr = kmalloc(len + 1, GFP_KERNEL); - if (*ip_addr) { - memcpy(*ip_addr, data, len + 1); - if (!IS_ERR(rkey)) - cFYI(1, "%s: resolved: %s to %s", __func__, - name, - *ip_addr - ); - rc = len; - } else { - rc = -ENOMEM; - } - if (!IS_ERR(rkey)) - key_put(rkey); - } + /* Discount leading slashes for cifs */ + len -= 2; + hostname = unc + 2; -out: - kfree(name); + /* Search for server name delimiter */ + sep = memchr(hostname, '\\', len); + if (sep) + len = sep - unc; + else + cFYI(1, "%s: probably server name is whole unc: %s", + __func__, unc); + + /* Try to interpret hostname as an IPv4 or IPv6 address */ + rc = cifs_convert_address((struct sockaddr *)&ss, hostname, len); + if (rc > 0) + goto name_is_IP_address; + + /* Perform the upcall */ + rc = dns_query(NULL, hostname, len, NULL, ip_addr, NULL); + if (rc < 0) + cERROR(1, "%s: unable to resolve: %*.*s", + __func__, len, len, hostname); + else + cFYI(1, "%s: resolved: %*.*s to %s", + __func__, len, len, hostname, *ip_addr); return rc; -} -int __init cifs_init_dns_resolver(void) -{ - struct cred *cred; - struct key *keyring; - int ret; - - printk(KERN_NOTICE "Registering the %s key type\n", - key_type_dns_resolver.name); - - /* create an override credential set with a special thread keyring in - * which DNS requests are cached - * - * this is used to prevent malicious redirections from being installed - * with add_key(). - */ - cred = prepare_kernel_cred(NULL); - if (!cred) +name_is_IP_address: + name = kmalloc(len + 1, GFP_KERNEL); + if (!name) return -ENOMEM; - - keyring = key_alloc(&key_type_keyring, ".dns_resolver", 0, 0, cred, - (KEY_POS_ALL & ~KEY_POS_SETATTR) | - KEY_USR_VIEW | KEY_USR_READ, - KEY_ALLOC_NOT_IN_QUOTA); - if (IS_ERR(keyring)) { - ret = PTR_ERR(keyring); - goto failed_put_cred; - } - - ret = key_instantiate_and_link(keyring, NULL, 0, NULL, NULL); - if (ret < 0) - goto failed_put_key; - - ret = register_key_type(&key_type_dns_resolver); - if (ret < 0) - goto failed_put_key; - - /* instruct request_key() to use this special keyring as a cache for - * the results it looks up */ - cred->thread_keyring = keyring; - cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING; - dns_resolver_cache = cred; + memcpy(name, hostname, len); + name[len] = 0; + cFYI(1, "%s: unc is IP, skipping dns upcall: %s", __func__, name); + *ip_addr = name; return 0; - -failed_put_key: - key_put(keyring); -failed_put_cred: - put_cred(cred); - return ret; -} - -void cifs_exit_dns_resolver(void) -{ - key_revoke(dns_resolver_cache->thread_keyring); - unregister_key_type(&key_type_dns_resolver); - put_cred(dns_resolver_cache); - printk(KERN_NOTICE "Unregistered %s key type\n", - key_type_dns_resolver.name); } diff --git a/fs/cifs/dns_resolve.h b/fs/cifs/dns_resolve.h index 5d7f291df162..d3f5d27f4d06 100644 --- a/fs/cifs/dns_resolve.h +++ b/fs/cifs/dns_resolve.h @@ -24,8 +24,6 @@ #define _DNS_RESOLVE_H #ifdef __KERNEL__ -extern int __init cifs_init_dns_resolver(void); -extern void cifs_exit_dns_resolver(void); extern int dns_resolve_server_name_to_ip(const char *unc, char **ip_addr); #endif /* KERNEL */ diff --git a/include/keys/dns_resolver-type.h b/include/keys/dns_resolver-type.h new file mode 100644 index 000000000000..9284a19393aa --- /dev/null +++ b/include/keys/dns_resolver-type.h @@ -0,0 +1,23 @@ +/* DNS resolver key type + * + * Copyright (C) 2010 Wang Lei. All Rights Reserved. + * Written by Wang Lei (wang840925@gmail.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _KEYS_DNS_RESOLVER_TYPE_H +#define _KEYS_DNS_RESOLVER_TYPE_H + +#include + +extern struct key_type key_type_dns_resolver; + +extern int request_dns_resolver_key(const char *description, + const char *callout_info, + char **data); + +#endif /* _KEYS_DNS_RESOLVER_TYPE_H */ diff --git a/include/linux/dns_resolver.h b/include/linux/dns_resolver.h new file mode 100644 index 000000000000..cc92268af89a --- /dev/null +++ b/include/linux/dns_resolver.h @@ -0,0 +1,34 @@ +/* + * DNS Resolver upcall management for CIFS DFS and AFS + * Handles host name to IP address resolution and DNS query for AFSDB RR. + * + * Copyright (c) International Business Machines Corp., 2008 + * Author(s): Steve French (sfrench@us.ibm.com) + * Wang Lei (wang840925@gmail.com) + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _LINUX_DNS_RESOLVER_H +#define _LINUX_DNS_RESOLVER_H + +#ifdef __KERNEL__ + +extern int dns_query(const char *type, const char *name, size_t namelen, + const char *options, char **_result, time_t *_expiry); + +#endif /* KERNEL */ + +#endif /* _LINUX_DNS_RESOLVER_H */ diff --git a/net/Kconfig b/net/Kconfig index e24fa0873f32..e330594d3709 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -213,6 +213,7 @@ source "net/phonet/Kconfig" source "net/ieee802154/Kconfig" source "net/sched/Kconfig" source "net/dcb/Kconfig" +source "net/dns_resolver/Kconfig" config RPS boolean diff --git a/net/Makefile b/net/Makefile index 41d420070a38..ea60fbce9b1b 100644 --- a/net/Makefile +++ b/net/Makefile @@ -67,3 +67,4 @@ ifeq ($(CONFIG_NET),y) obj-$(CONFIG_SYSCTL) += sysctl_net.o endif obj-$(CONFIG_WIMAX) += wimax/ +obj-$(CONFIG_DNS_RESOLVER) += dns_resolver/ diff --git a/net/dns_resolver/Kconfig b/net/dns_resolver/Kconfig new file mode 100644 index 000000000000..2ec47cb5d0d6 --- /dev/null +++ b/net/dns_resolver/Kconfig @@ -0,0 +1,27 @@ +# +# Configuration for DNS Resolver +# +config DNS_RESOLVER + tristate "DNS Resolver support" + depends on NET && KEYS + help + Saying Y here will include support for the DNS Resolver key type + which can be used to make upcalls to perform DNS lookups in + userspace. + + DNS Resolver is used to query DNS server for information. Examples + being resolving a UNC hostname element to an IP address for CIFS or + performing a DNS query for AFSDB records so that AFS can locate a + cell's volume location database servers. + + DNS Resolver is used by the CIFS and AFS modules, and would support + samba4 later. DNS Resolver is supported by the userspace upcall + helper "/sbin/dns.resolver" via /etc/request-key.conf. + + See for further + information. + + To compile this as a module, choose M here: the module will be called + dnsresolver. + + If unsure, say N. diff --git a/net/dns_resolver/Makefile b/net/dns_resolver/Makefile new file mode 100644 index 000000000000..c0ef4e71dc49 --- /dev/null +++ b/net/dns_resolver/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for the Linux DNS Resolver. +# + +obj-$(CONFIG_DNS_RESOLVER) += dns_resolver.o + +dns_resolver-objs := dns_key.o dns_query.o diff --git a/net/dns_resolver/dns_key.c b/net/dns_resolver/dns_key.c new file mode 100644 index 000000000000..1b1b411adcf1 --- /dev/null +++ b/net/dns_resolver/dns_key.c @@ -0,0 +1,210 @@ +/* Key type used to cache DNS lookups made by the kernel + * + * See Documentation/networking/dns_resolver.txt + * + * Copyright (c) 2007 Igor Mammedov + * Author(s): Igor Mammedov (niallain@gmail.com) + * Steve French (sfrench@us.ibm.com) + * Wang Lei (wang840925@gmail.com) + * David Howells (dhowells@redhat.com) + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "internal.h" + +MODULE_DESCRIPTION("DNS Resolver"); +MODULE_AUTHOR("Wang Lei"); +MODULE_LICENSE("GPL"); + +unsigned dns_resolver_debug; +module_param_named(debug, dns_resolver_debug, uint, S_IWUSR | S_IRUGO); +MODULE_PARM_DESC(debug, "DNS Resolver debugging mask"); + +const struct cred *dns_resolver_cache; + +/* + * Instantiate a user defined key for dns_resolver. + * + * The data must be a NUL-terminated string, with the NUL char accounted in + * datalen. + * + * If the data contains a '#' characters, then we take the clause after each + * one to be an option of the form 'key=value'. The actual data of interest is + * the string leading up to the first '#'. For instance: + * + * "ip1,ip2,...#foo=bar" + */ +static int +dns_resolver_instantiate(struct key *key, const void *_data, size_t datalen) +{ + struct user_key_payload *upayload; + int ret; + size_t result_len = 0; + const char *data = _data, *opt; + + kenter("%%%d,%s,'%s',%zu", + key->serial, key->description, data, datalen); + + if (datalen <= 1 || !data || data[datalen - 1] != '\0') + return -EINVAL; + datalen--; + + /* deal with any options embedded in the data */ + opt = memchr(data, '#', datalen); + if (!opt) { + kdebug("no options currently supported"); + return -EINVAL; + } + + result_len = datalen; + ret = key_payload_reserve(key, result_len); + if (ret < 0) + return -EINVAL; + + upayload = kmalloc(sizeof(*upayload) + result_len + 1, GFP_KERNEL); + if (!upayload) { + kleave(" = -ENOMEM"); + return -ENOMEM; + } + + upayload->datalen = result_len; + memcpy(upayload->data, data, result_len); + upayload->data[result_len] = '\0'; + rcu_assign_pointer(key->payload.data, upayload); + + kleave(" = 0"); + return 0; +} + +/* + * The description is of the form "[:]" + * + * The domain name may be a simple name or an absolute domain name (which + * should end with a period). The domain name is case-independent. + */ +static int +dns_resolver_match(const struct key *key, const void *description) +{ + int slen, dlen, ret = 0; + const char *src = key->description, *dsp = description; + + kenter("%s,%s", src, dsp); + + if (!src || !dsp) + goto no_match; + + if (strcasecmp(src, dsp) == 0) + goto matched; + + slen = strlen(src); + dlen = strlen(dsp); + if (slen <= 0 || dlen <= 0) + goto no_match; + if (src[slen - 1] == '.') + slen--; + if (dsp[dlen - 1] == '.') + dlen--; + if (slen != dlen || strncasecmp(src, dsp, slen) != 0) + goto no_match; + +matched: + ret = 1; +no_match: + kleave(" = %d", ret); + return ret; +} + +struct key_type key_type_dns_resolver = { + .name = "dns_resolver", + .instantiate = dns_resolver_instantiate, + .match = dns_resolver_match, + .revoke = user_revoke, + .destroy = user_destroy, + .describe = user_describe, + .read = user_read, +}; + +static int __init init_dns_resolver(void) +{ + struct cred *cred; + struct key *keyring; + int ret; + + printk(KERN_NOTICE "Registering the %s key type\n", + key_type_dns_resolver.name); + + /* create an override credential set with a special thread keyring in + * which DNS requests are cached + * + * this is used to prevent malicious redirections from being installed + * with add_key(). + */ + cred = prepare_kernel_cred(NULL); + if (!cred) + return -ENOMEM; + + keyring = key_alloc(&key_type_keyring, ".dns_resolver", 0, 0, cred, + (KEY_POS_ALL & ~KEY_POS_SETATTR) | + KEY_USR_VIEW | KEY_USR_READ, + KEY_ALLOC_NOT_IN_QUOTA); + if (IS_ERR(keyring)) { + ret = PTR_ERR(keyring); + goto failed_put_cred; + } + + ret = key_instantiate_and_link(keyring, NULL, 0, NULL, NULL); + if (ret < 0) + goto failed_put_key; + + ret = register_key_type(&key_type_dns_resolver); + if (ret < 0) + goto failed_put_key; + + /* instruct request_key() to use this special keyring as a cache for + * the results it looks up */ + cred->thread_keyring = keyring; + cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING; + dns_resolver_cache = cred; + + kdebug("DNS resolver keyring: %d\n", key_serial(keyring)); + return 0; + +failed_put_key: + key_put(keyring); +failed_put_cred: + put_cred(cred); + return ret; +} + +static void __exit exit_dns_resolver(void) +{ + key_revoke(dns_resolver_cache->thread_keyring); + unregister_key_type(&key_type_dns_resolver); + put_cred(dns_resolver_cache); + printk(KERN_NOTICE "Unregistered %s key type\n", + key_type_dns_resolver.name); +} + +module_init(init_dns_resolver) +module_exit(exit_dns_resolver) +MODULE_LICENSE("GPL"); diff --git a/net/dns_resolver/dns_query.c b/net/dns_resolver/dns_query.c new file mode 100644 index 000000000000..6c0cf31ea00d --- /dev/null +++ b/net/dns_resolver/dns_query.c @@ -0,0 +1,159 @@ +/* Upcall routine, designed to work as a key type and working through + * /sbin/request-key to contact userspace when handling DNS queries. + * + * See Documentation/networking/dns_resolver.txt + * + * Copyright (c) 2007 Igor Mammedov + * Author(s): Igor Mammedov (niallain@gmail.com) + * Steve French (sfrench@us.ibm.com) + * Wang Lei (wang840925@gmail.com) + * David Howells (dhowells@redhat.com) + * + * The upcall wrapper used to make an arbitrary DNS query. + * + * This function requires the appropriate userspace tool dns.upcall to be + * installed and something like the following lines should be added to the + * /etc/request-key.conf file: + * + * create dns_resolver * * /sbin/dns.upcall %k + * + * For example to use this module to query AFSDB RR: + * + * create dns_resolver afsdb:* * /sbin/dns.afsdb %k + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include + +#include "internal.h" + +/* + * dns_query - Query the DNS + * @type: Query type (or NULL for straight host->IP lookup) + * @name: Name to look up + * @namelen: Length of name + * @options: Request options (or NULL if no options) + * @_result: Where to place the returned data. + * @_expiry: Where to store the result expiry time (or NULL) + * + * The data will be returned in the pointer at *result, and the caller is + * responsible for freeing it. + * + * The description should be of the form "[:]", and + * the options need to be appropriate for the query type requested. If no + * query_type is given, then the query is a straight hostname to IP address + * lookup. + * + * The DNS resolution lookup is performed by upcalling to userspace by way of + * requesting a key of type dns_resolver. + * + * Returns the size of the result on success, -ve error code otherwise. + */ +int dns_query(const char *type, const char *name, size_t namelen, + const char *options, char **_result, time_t *_expiry) +{ + struct key *rkey; + struct user_key_payload *upayload; + const struct cred *saved_cred; + size_t typelen, desclen; + char *desc, *cp; + int ret, len; + + kenter("%s,%*.*s,%zu,%s", + type, (int)namelen, (int)namelen, name, namelen, options); + + if (!name || namelen == 0 || !_result) + return -EINVAL; + + /* construct the query key description as "[:]" */ + typelen = 0; + desclen = 0; + if (type) { + typelen = strlen(type); + if (typelen < 1) + return -EINVAL; + desclen += typelen + 1; + } + + if (!namelen) + namelen = strlen(name); + if (namelen < 3) + return -EINVAL; + desclen += namelen + 1; + + desc = kmalloc(desclen, GFP_KERNEL); + if (!desc) + return -ENOMEM; + + cp = desc; + if (type) { + memcpy(cp, type, typelen); + cp += typelen; + *cp++ = ':'; + } + memcpy(cp, name, namelen); + cp += namelen; + *cp = '\0'; + + if (!options) + options = ""; + kdebug("call request_key(,%s,%s)", desc, options); + + /* make the upcall, using special credentials to prevent the use of + * add_key() to preinstall malicious redirections + */ + saved_cred = override_creds(dns_resolver_cache); + rkey = request_key(&key_type_dns_resolver, desc, options); + revert_creds(saved_cred); + kfree(desc); + if (IS_ERR(rkey)) { + ret = PTR_ERR(rkey); + goto out; + } + + down_read(&rkey->sem); + rkey->perm |= KEY_USR_VIEW; + + ret = key_validate(rkey); + if (ret < 0) + goto put; + + upayload = rcu_dereference_protected(rkey->payload.data, + lockdep_is_held(&rkey->sem)); + len = upayload->datalen; + + ret = -ENOMEM; + *_result = kmalloc(len + 1, GFP_KERNEL); + if (!*_result) + goto put; + + memcpy(*_result, upayload->data, len + 1); + if (_expiry) + *_expiry = rkey->expiry; + + ret = len; +put: + up_read(&rkey->sem); + key_put(rkey); +out: + kleave(" = %d", ret); + return ret; +} +EXPORT_SYMBOL(dns_query); diff --git a/net/dns_resolver/internal.h b/net/dns_resolver/internal.h new file mode 100644 index 000000000000..189ca9e9b785 --- /dev/null +++ b/net/dns_resolver/internal.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2010 Wang Lei + * Author(s): Wang Lei (wang840925@gmail.com). All Rights Reserved. + * + * Internal DNS Rsolver stuff + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include + +/* + * dns_key.c + */ +extern const struct cred *dns_resolver_cache; + +/* + * debug tracing + */ +extern unsigned dns_resolver_debug; + +#define kdebug(FMT, ...) \ +do { \ + if (unlikely(dns_resolver_debug)) \ + printk(KERN_DEBUG "[%-6.6s] "FMT"\n", \ + current->comm, ##__VA_ARGS__); \ +} while (0) + +#define kenter(FMT, ...) kdebug("==> %s("FMT")", __func__, ##__VA_ARGS__) +#define kleave(FMT, ...) kdebug("<== %s()"FMT"", __func__, ##__VA_ARGS__) -- cgit v1.2.3 From 07567a5509327bcbf2c867286eb1524447c9b954 Mon Sep 17 00:00:00 2001 From: Wang Lei Date: Wed, 4 Aug 2010 15:16:38 +0100 Subject: DNS: Make AFS go to the DNS for AFSDB records for unknown cells Add DNS query support for AFS so that it can get the IP addresses of Volume Location servers from the DNS using an AFSDB record. This requires userspace support. /etc/request-key.conf must be configured to invoke a helper for dns_resolver type keys with a subtype of "afsdb:" in the description. Signed-off-by: Wang Lei Signed-off-by: David Howells Signed-off-by: Steve French --- fs/afs/Kconfig | 1 + fs/afs/cell.c | 40 ++++++++++++++++++++++++++++++---------- 2 files changed, 31 insertions(+), 10 deletions(-) (limited to 'fs') diff --git a/fs/afs/Kconfig b/fs/afs/Kconfig index 5c4e61d3c772..8f975f25b486 100644 --- a/fs/afs/Kconfig +++ b/fs/afs/Kconfig @@ -2,6 +2,7 @@ config AFS_FS tristate "Andrew File System support (AFS) (EXPERIMENTAL)" depends on INET && EXPERIMENTAL select AF_RXRPC + select DNS_RESOLVER help If you say Y here, you will get an experimental Andrew File System driver. It currently only supports unsecured read-only AFS access. diff --git a/fs/afs/cell.c b/fs/afs/cell.c index e19c13f059ed..ffea35c63879 100644 --- a/fs/afs/cell.c +++ b/fs/afs/cell.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include "internal.h" @@ -36,6 +37,8 @@ static struct afs_cell *afs_cell_alloc(const char *name, char *vllist) struct key *key; size_t namelen; char keyname[4 + AFS_MAXCELLNAME + 1], *cp, *dp, *next; + char *dvllist = NULL, *_vllist = NULL; + char delimiter = ':'; int ret; _enter("%s,%s", name, vllist); @@ -43,8 +46,10 @@ static struct afs_cell *afs_cell_alloc(const char *name, char *vllist) BUG_ON(!name); /* TODO: want to look up "this cell" in the cache */ namelen = strlen(name); - if (namelen > AFS_MAXCELLNAME) + if (namelen > AFS_MAXCELLNAME) { + _leave(" = -ENAMETOOLONG"); return ERR_PTR(-ENAMETOOLONG); + } /* allocate and initialise a cell record */ cell = kzalloc(sizeof(struct afs_cell) + namelen + 1, GFP_KERNEL); @@ -64,15 +69,31 @@ static struct afs_cell *afs_cell_alloc(const char *name, char *vllist) INIT_LIST_HEAD(&cell->vl_list); spin_lock_init(&cell->vl_lock); + /* if the ip address is invalid, try dns query */ + if (!vllist || strlen(vllist) < 7) { + ret = dns_query("afsdb", name, namelen, "ipv4", &dvllist, NULL); + if (ret < 0) { + _leave(" = %d", ret); + return ERR_PTR(ret); + } + _vllist = dvllist; + + /* change the delimiter for user-space reply */ + delimiter = ','; + + } else { + _vllist = vllist; + } + /* fill in the VL server list from the rest of the string */ do { unsigned a, b, c, d; - next = strchr(vllist, ':'); + next = strchr(_vllist, delimiter); if (next) *next++ = 0; - if (sscanf(vllist, "%u.%u.%u.%u", &a, &b, &c, &d) != 4) + if (sscanf(_vllist, "%u.%u.%u.%u", &a, &b, &c, &d) != 4) goto bad_address; if (a > 255 || b > 255 || c > 255 || d > 255) @@ -81,7 +102,7 @@ static struct afs_cell *afs_cell_alloc(const char *name, char *vllist) cell->vl_addrs[cell->vl_naddrs++].s_addr = htonl((a << 24) | (b << 16) | (c << 8) | d); - } while (cell->vl_naddrs < AFS_CELL_MAX_ADDRS && (vllist = next)); + } while (cell->vl_naddrs < AFS_CELL_MAX_ADDRS && (_vllist = next)); /* create a key to represent an anonymous user */ memcpy(keyname, "afs@", 4); @@ -110,6 +131,7 @@ bad_address: ret = -EINVAL; error: key_put(cell->anonymous_key); + kfree(dvllist); kfree(cell); _leave(" = %d", ret); return ERR_PTR(ret); @@ -201,14 +223,12 @@ int afs_cell_init(char *rootcell) } cp = strchr(rootcell, ':'); - if (!cp) { - printk(KERN_ERR "kAFS: no VL server IP addresses specified\n"); - _leave(" = -EINVAL"); - return -EINVAL; - } + if (!cp) + _debug("kAFS: no VL server IP addresses specified"); + else + *cp++ = 0; /* allocate a cell record for the root cell */ - *cp++ = 0; new_root = afs_cell_create(rootcell, cp); if (IS_ERR(new_root)) { _leave(" = %ld", PTR_ERR(new_root)); -- cgit v1.2.3 From 56d35a4cd13e7bc5eca5b2dba5a41794afb17e11 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Thu, 5 Aug 2010 14:41:42 -0400 Subject: ext4: Fix dirtying of journalled buffers in data=journal mode In data=journal mode, we still use block_write_begin() to prepare page for writing. This function can occasionally mark buffer dirty which violates journalling assumptions - when a buffer is part of a transaction, it should be dirty and a buffer can be already part of a forget list of some transaction when block_write_begin() gets called. This violation of journalling assumptions then results in "JBD: Spotted dirty metadata buffer..." warnings. In fact, temporary dirtying the buffer while the page is still locked does not really cause problems to the journalling because we won't write the buffer until the page gets unlocked. So we just have to make sure to clear dirty bits before unlocking the page. Signed-off-by: Jan Kara --- fs/ext4/inode.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index ab2247d642c6..a0ab3754d0d6 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -1527,9 +1527,25 @@ static int walk_page_buffers(handle_t *handle, static int do_journal_get_write_access(handle_t *handle, struct buffer_head *bh) { + int dirty = buffer_dirty(bh); + int ret; + if (!buffer_mapped(bh) || buffer_freed(bh)) return 0; - return ext4_journal_get_write_access(handle, bh); + /* + * __block_prepare_write() could have dirtied some buffers. Clean + * the dirty bit as jbd2_journal_get_write_access() could complain + * otherwise about fs integrity issues. Setting of the dirty bit + * by __block_prepare_write() isn't a real problem here as we clear + * the bit before releasing a page lock and thus writeback cannot + * ever write the buffer. + */ + if (dirty) + clear_buffer_dirty(bh); + ret = ext4_journal_get_write_access(handle, bh); + if (!ret && dirty) + ret = ext4_handle_dirty_metadata(handle, NULL, bh); + return ret; } /* -- cgit v1.2.3 From a4d935bd97b9ccf40aa0ec59646612df8ed46640 Mon Sep 17 00:00:00 2001 From: Changli Gao Date: Mon, 26 Jul 2010 17:19:19 +0800 Subject: dlm: use genl_register_family_with_ops() Signed-off-by: Changli Gao Signed-off-by: David Teigland --- fs/dlm/netlink.c | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) (limited to 'fs') diff --git a/fs/dlm/netlink.c b/fs/dlm/netlink.c index 2c6ad518100d..ef17e0169da1 100644 --- a/fs/dlm/netlink.c +++ b/fs/dlm/netlink.c @@ -81,24 +81,11 @@ static struct genl_ops dlm_nl_ops = { int __init dlm_netlink_init(void) { - int rv; - - rv = genl_register_family(&family); - if (rv) - return rv; - - rv = genl_register_ops(&family, &dlm_nl_ops); - if (rv < 0) - goto err; - return 0; - err: - genl_unregister_family(&family); - return rv; + return genl_register_family_with_ops(&family, &dlm_nl_ops, 1); } void dlm_netlink_exit(void) { - genl_unregister_ops(&family, &dlm_nl_ops); genl_unregister_family(&family); } -- cgit v1.2.3 From f70cb33b9c270f4f1a7f28327e7d35dbf1a6fc40 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Tue, 3 Aug 2010 23:34:16 +0200 Subject: fs/dlm: Drop unnecessary null test hlist_for_each_entry binds its first argument to a non-null value, and thus any null test on the value of that argument is superfluous. The semantic patch that makes this change is as follows: (http://coccinelle.lip6.fr/) // @@ iterator I; expression x,E,E1,E2; statement S,S1,S2; @@ I(x,...) { <... - (x != NULL) && E ...> } // Signed-off-by: Julia Lawall Signed-off-by: David Teigland --- fs/dlm/lowcomms.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/dlm/lowcomms.c b/fs/dlm/lowcomms.c index c0d35c620526..37a34c2c622a 100644 --- a/fs/dlm/lowcomms.c +++ b/fs/dlm/lowcomms.c @@ -248,7 +248,7 @@ static struct connection *assoc2con(int assoc_id) for (i = 0 ; i < CONN_HASH_SIZE; i++) { hlist_for_each_entry(con, h, &connection_hash[i], list) { - if (con && con->sctp_assoc == assoc_id) { + if (con->sctp_assoc == assoc_id) { mutex_unlock(&connections_lock); return con; } -- cgit v1.2.3 From 5f11e6a44059f728dddd8d0dbe5b4368ea93575b Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Thu, 5 Aug 2010 12:38:26 +0200 Subject: ext3: Fix dirtying of journalled buffers in data=journal mode In data=journal mode, we still use block_write_begin() to prepare page for writing. This function can occasionally mark buffer dirty which violates journalling assumptions - when a buffer is part of a transaction, it should be dirty and a buffer can be already part of a forget list of some transaction when block_write_begin() gets called. This violation of journalling assumptions then results in "JBD: Spotted dirty metadata buffer..." warnings. In fact, temporary dirtying the buffer while the page is still locked does not really cause problems to the journalling because we won't write the buffer until the page gets unlocked. So we just have to make sure to clear dirty bits before unlocking the page. Reviewed-by: "Theodore Ts'o" Signed-off-by: Jan Kara --- fs/ext3/inode.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c index 436e5bbccbc2..001eb0e2d48e 100644 --- a/fs/ext3/inode.c +++ b/fs/ext3/inode.c @@ -1149,9 +1149,25 @@ static int walk_page_buffers( handle_t *handle, static int do_journal_get_write_access(handle_t *handle, struct buffer_head *bh) { + int dirty = buffer_dirty(bh); + int ret; + if (!buffer_mapped(bh) || buffer_freed(bh)) return 0; - return ext3_journal_get_write_access(handle, bh); + /* + * __block_prepare_write() could have dirtied some buffers. Clean + * the dirty bit as jbd2_journal_get_write_access() could complain + * otherwise about fs integrity issues. Setting of the dirty bit + * by __block_prepare_write() isn't a real problem here as we clear + * the bit before releasing a page lock and thus writeback cannot + * ever write the buffer. + */ + if (dirty) + clear_buffer_dirty(bh); + ret = ext3_journal_get_write_access(handle, bh); + if (!ret && dirty) + ret = ext3_journal_dirty_metadata(handle, bh); + return ret; } /* -- cgit v1.2.3 From 642b5123ac5ec40a28575e930a3e2ff595473e9d Mon Sep 17 00:00:00 2001 From: Satoru Takeuchi Date: Thu, 5 Aug 2010 11:23:11 -0700 Subject: aio: fix wrong subsystem comments - sys_io_destroy(): acutually return -EINVAL if the context pointed to is invalidIndex: linux-2.6.33-rc4/fs/aio.c - sys_io_getevents(): An argument specifying timeout is not `when', but `timeout'. - sys_io_getevents(): Should describe what is returned if this syscall succeeds. Signed-off-by: Satoru Takeuchi Signed-off-by: Randy Dunlap Reviewed-by: Jeff Moyer Signed-off-by: Linus Torvalds --- fs/aio.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) (limited to 'fs') diff --git a/fs/aio.c b/fs/aio.c index 1ccf25cef1f0..3006b5bc33d6 100644 --- a/fs/aio.c +++ b/fs/aio.c @@ -1277,7 +1277,7 @@ out: /* sys_io_destroy: * Destroy the aio_context specified. May cancel any outstanding * AIOs and block on completion. Will fail with -ENOSYS if not - * implemented. May fail with -EFAULT if the context pointed to + * implemented. May fail with -EINVAL if the context pointed to * is invalid. */ SYSCALL_DEFINE1(io_destroy, aio_context_t, ctx) @@ -1795,15 +1795,16 @@ SYSCALL_DEFINE3(io_cancel, aio_context_t, ctx_id, struct iocb __user *, iocb, /* io_getevents: * Attempts to read at least min_nr events and up to nr events from - * the completion queue for the aio_context specified by ctx_id. May - * fail with -EINVAL if ctx_id is invalid, if min_nr is out of range, - * if nr is out of range, if when is out of range. May fail with - * -EFAULT if any of the memory specified to is invalid. May return - * 0 or < min_nr if no events are available and the timeout specified - * by when has elapsed, where when == NULL specifies an infinite - * timeout. Note that the timeout pointed to by when is relative and - * will be updated if not NULL and the operation blocks. Will fail - * with -ENOSYS if not implemented. + * the completion queue for the aio_context specified by ctx_id. If + * it succeeds, the number of read events is returned. May fail with + * -EINVAL if ctx_id is invalid, if min_nr is out of range, if nr is + * out of range, if timeout is out of range. May fail with -EFAULT + * if any of the memory specified is invalid. May return 0 or + * < min_nr if the timeout specified by timeout has elapsed + * before sufficient events are available, where timeout == NULL + * specifies an infinite timeout. Note that the timeout pointed to by + * timeout is relative and will be updated if not NULL and the + * operation blocks. Will fail with -ENOSYS if not implemented. */ SYSCALL_DEFINE5(io_getevents, aio_context_t, ctx_id, long, min_nr, -- cgit v1.2.3 From 6c7a120ac6c62316ab1fc78dfc0a7b13f3bfcbff Mon Sep 17 00:00:00 2001 From: Aditya Kali Date: Thu, 5 Aug 2010 16:22:24 -0400 Subject: ext4: Adding error check after calling ext4_mb_regular_allocator() If the bitmap block on disk is bad, ext4_mb_load_buddy() returns an error. This error is returned to the caller, ext4_mb_regular_allocator() and then to ext4_mb_new_blocks(). But ext4_mb_new_blocks() did not check for the return value of ext4_mb_regular_allocator() and would repeatedly try to load the bitmap block. The fix simply catches the return value and exits out of the 'repeat' loop after cleanup. We also take the opportunity to clean up the error handling in ext4_mb_new_blocks(). Google-Bug-Id: 2853530 Signed-off-by: Aditya Kali Signed-off-by: "Theodore Ts'o" --- fs/ext4/mballoc.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) (limited to 'fs') diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c index 3da28281bc54..1f360f07cb40 100644 --- a/fs/ext4/mballoc.c +++ b/fs/ext4/mballoc.c @@ -4253,7 +4253,7 @@ static int ext4_mb_discard_preallocations(struct super_block *sb, int needed) * to usual allocation */ ext4_fsblk_t ext4_mb_new_blocks(handle_t *handle, - struct ext4_allocation_request *ar, int *errp) + struct ext4_allocation_request *ar, int *errp) { int freed; struct ext4_allocation_context *ac = NULL; @@ -4297,7 +4297,7 @@ ext4_fsblk_t ext4_mb_new_blocks(handle_t *handle, inquota = ar->len; if (ar->len == 0) { *errp = -EDQUOT; - goto out3; + goto out; } } @@ -4305,13 +4305,13 @@ ext4_fsblk_t ext4_mb_new_blocks(handle_t *handle, if (!ac) { ar->len = 0; *errp = -ENOMEM; - goto out1; + goto out; } *errp = ext4_mb_initialize_context(ac, ar); if (*errp) { ar->len = 0; - goto out2; + goto out; } ac->ac_op = EXT4_MB_HISTORY_PREALLOC; @@ -4320,7 +4320,9 @@ ext4_fsblk_t ext4_mb_new_blocks(handle_t *handle, ext4_mb_normalize_request(ac, ar); repeat: /* allocate space in core */ - ext4_mb_regular_allocator(ac); + *errp = ext4_mb_regular_allocator(ac); + if (*errp) + goto errout; /* as we've just preallocated more space than * user requested orinally, we store allocated @@ -4331,7 +4333,7 @@ repeat: } if (likely(ac->ac_status == AC_STATUS_FOUND)) { *errp = ext4_mb_mark_diskspace_used(ac, handle, reserv_blks); - if (*errp == -EAGAIN) { + if (*errp == -EAGAIN) { /* * drop the reference that we took * in ext4_mb_use_best_found @@ -4342,12 +4344,10 @@ repeat: ac->ac_b_ex.fe_len = 0; ac->ac_status = AC_STATUS_CONTINUE; goto repeat; - } else if (*errp) { + } else if (*errp) + errout: ext4_discard_allocated_blocks(ac); - ac->ac_b_ex.fe_len = 0; - ar->len = 0; - ext4_mb_show_ac(ac); - } else { + else { block = ext4_grp_offs_to_block(sb, &ac->ac_b_ex); ar->len = ac->ac_b_ex.fe_len; } @@ -4356,19 +4356,19 @@ repeat: if (freed) goto repeat; *errp = -ENOSPC; + } + + if (*errp) { ac->ac_b_ex.fe_len = 0; ar->len = 0; ext4_mb_show_ac(ac); } - ext4_mb_release_context(ac); - -out2: - kmem_cache_free(ext4_ac_cachep, ac); -out1: +out: + if (ac) + kmem_cache_free(ext4_ac_cachep, ac); if (inquota && ar->len < inquota) dquot_free_block(ar->inode, inquota - ar->len); -out3: if (!ar->len) { if (!EXT4_I(ar->inode)->i_delalloc_reserved_flag) /* release all the reserved blocks if non delalloc */ -- cgit v1.2.3 From 49c19400f60bbe362202d7e7b3e68cc66040d0fa Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 2 Jul 2010 16:54:05 +0200 Subject: sysfs: sysfs_chmod_file's attr can be const sysfs_chmod_file doesn't change the attribute it operates on, so this attribute can be marked const. Signed-off-by: Jean Delvare Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/file.c | 3 ++- include/linux/sysfs.h | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index 1beaa739d0a6..1b27b5688f62 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -593,7 +593,8 @@ EXPORT_SYMBOL_GPL(sysfs_add_file_to_group); * @mode: file permissions. * */ -int sysfs_chmod_file(struct kobject *kobj, struct attribute *attr, mode_t mode) +int sysfs_chmod_file(struct kobject *kobj, const struct attribute *attr, + mode_t mode) { struct sysfs_dirent *sd; struct iattr newattrs; diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index f2694eb4dd3d..8bf06b64487c 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -136,8 +136,8 @@ int __must_check sysfs_create_file(struct kobject *kobj, const struct attribute *attr); int __must_check sysfs_create_files(struct kobject *kobj, const struct attribute **attr); -int __must_check sysfs_chmod_file(struct kobject *kobj, struct attribute *attr, - mode_t mode); +int __must_check sysfs_chmod_file(struct kobject *kobj, + const struct attribute *attr, mode_t mode); void sysfs_remove_file(struct kobject *kobj, const struct attribute *attr); void sysfs_remove_files(struct kobject *kobj, const struct attribute **attr); @@ -225,7 +225,7 @@ static inline int sysfs_create_files(struct kobject *kobj, } static inline int sysfs_chmod_file(struct kobject *kobj, - struct attribute *attr, mode_t mode) + const struct attribute *attr, mode_t mode) { return 0; } -- cgit v1.2.3 From 0eb6cd49f6e3ec523787d09cf08d3179be270db4 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Thu, 5 Aug 2010 13:53:18 -0700 Subject: ceph: only queue async writeback on cap revocation if there is dirty data Normally, if the Fb cap bit is being revoked, we queue an async writeback. If there is no dirty data but we still hold the cap, this leaves the client sitting around doing nothing until the cap timeouts expire and the cap is released on its own (as it would have been without the revocation). Instead, only queue writeback if the bit is actually used (i.e., we have dirty data). If not, we can reply to the revocation immediately. Signed-off-by: Sage Weil --- fs/ceph/caps.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c index 51546d54b841..7bf182b03973 100644 --- a/fs/ceph/caps.c +++ b/fs/ceph/caps.c @@ -2384,7 +2384,7 @@ static void handle_cap_grant(struct inode *inode, struct ceph_mds_caps *grant, ceph_cap_string(cap->issued), ceph_cap_string(newcaps), ceph_cap_string(revoking)); - if (revoking & CEPH_CAP_FILE_BUFFER) + if (revoking & used & CEPH_CAP_FILE_BUFFER) writeback = 1; /* initiate writeback; will delay ack */ else if (revoking == CEPH_CAP_FILE_CACHE && (newcaps & CEPH_CAP_FILE_LAZYIO) == 0 && -- cgit v1.2.3 From 4b676d2dbed3dadc6ef913d58f85360547fa071e Mon Sep 17 00:00:00 2001 From: Phillip Lougher Date: Thu, 5 Aug 2010 23:42:54 +0100 Subject: Squashfs: update Kconfig and documentation for LZO Update compression types supported and add some help text for the LZO Kconfig option. Also add missing "default n" line and make some trivial whitespace cleanups too. Signed-off-by: Phillip Lougher --- Documentation/filesystems/squashfs.txt | 2 +- fs/squashfs/Kconfig | 18 ++++++++++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) (limited to 'fs') diff --git a/Documentation/filesystems/squashfs.txt b/Documentation/filesystems/squashfs.txt index 203f7202cc9e..66699afd66ca 100644 --- a/Documentation/filesystems/squashfs.txt +++ b/Documentation/filesystems/squashfs.txt @@ -2,7 +2,7 @@ SQUASHFS 4.0 FILESYSTEM ======================= Squashfs is a compressed read-only filesystem for Linux. -It uses zlib compression to compress files, inodes and directories. +It uses zlib/lzo compression to compress files, inodes and directories. Inodes in the system are very small and all blocks are packed to minimise data overhead. Block sizes greater than 4K are supported up to a maximum of 1Mbytes (default block size 128K). diff --git a/fs/squashfs/Kconfig b/fs/squashfs/Kconfig index 3da01108c44e..e5f63da64d04 100644 --- a/fs/squashfs/Kconfig +++ b/fs/squashfs/Kconfig @@ -5,13 +5,13 @@ config SQUASHFS help Saying Y here includes support for SquashFS 4.0 (a Compressed Read-Only File System). Squashfs is a highly compressed read-only - filesystem for Linux. It uses zlib compression to compress both + filesystem for Linux. It uses zlib/lzo compression to compress both files, inodes and directories. Inodes in the system are very small and all blocks are packed to minimise data overhead. Block sizes greater than 4K are supported up to a maximum of 1 Mbytes (default block size 128K). SquashFS 4.0 supports 64 bit filesystems and files (larger than 4GB), full uid/gid information, hard links and - timestamps. + timestamps. Squashfs is intended for general read-only filesystem use, for archival use (i.e. in cases where a .tar.gz file may be used), and in @@ -40,11 +40,21 @@ config SQUASHFS_XATTR config SQUASHFS_LZO bool "Include support for LZO compressed file systems" depends on SQUASHFS + default n select LZO_DECOMPRESS + help + Saying Y here includes support for reading Squashfs file systems + compressed with LZO compresssion. LZO compression is mainly + aimed at embedded systems with slower CPUs where the overheads + of zlib are too high. -config SQUASHFS_EMBEDDED + LZO is not the standard compression used in Squashfs and so most + file systems will be readable without selecting this option. - bool "Additional option for memory-constrained systems" + If unsure, say N. + +config SQUASHFS_EMBEDDED + bool "Additional option for memory-constrained systems" depends on SQUASHFS default n help -- cgit v1.2.3 From 4f86b8fd48cb9b9a5b45aa0249e44c9d4fd7d796 Mon Sep 17 00:00:00 2001 From: Phillip Lougher Date: Thu, 5 Aug 2010 23:52:53 +0100 Subject: Squashfs: fix filename typo Signed-off-by: Phillip Lougher --- fs/squashfs/squashfs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/squashfs/squashfs.h b/fs/squashfs/squashfs.h index 3a95ea68b00f..5d45569d5f72 100644 --- a/fs/squashfs/squashfs.h +++ b/fs/squashfs/squashfs.h @@ -105,5 +105,5 @@ extern const struct xattr_handler *squashfs_xattr_handlers[]; /* zlib_wrapper.c */ extern const struct squashfs_decompressor squashfs_zlib_comp_ops; -/* lzo_wrappers.c */ +/* lzo_wrapper.c */ extern const struct squashfs_decompressor squashfs_lzo_comp_ops; -- cgit v1.2.3 From 31d1d48e199e99077fb30f6fb9a793be7bec756f Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 6 Aug 2010 16:34:43 +0100 Subject: Fix init ordering of /dev/console vs callers of modprobe Make /dev/console get initialised before any initialisation routine that invokes modprobe because if modprobe fails, it's going to want to open /dev/console, presumably to write an error message to. The problem with that is that if the /dev/console driver is not yet initialised, the chardev handler will call request_module() to invoke modprobe, which will fail, because we never compile /dev/console as a module. This will lead to a modprobe loop, showing the following in the kernel log: request_module: runaway loop modprobe char-major-5-1 request_module: runaway loop modprobe char-major-5-1 request_module: runaway loop modprobe char-major-5-1 request_module: runaway loop modprobe char-major-5-1 request_module: runaway loop modprobe char-major-5-1 This can happen, for example, when the built in md5 module can't find the built in cryptomgr module (because the latter fails to initialise). The md5 module comes before the call to tty_init(), presumably because 'crypto' comes before 'drivers' alphabetically. Fix this by calling tty_init() from chrdev_init(). Signed-off-by: David Howells Signed-off-by: Linus Torvalds --- drivers/char/mem.c | 2 +- drivers/char/tty_io.c | 4 ++-- fs/char_dev.c | 1 + include/linux/tty.h | 3 +++ 4 files changed, 7 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/drivers/char/mem.c b/drivers/char/mem.c index f54dab8acdcd..a398ecdbd758 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -916,7 +916,7 @@ static int __init chr_dev_init(void) NULL, devlist[minor].name); } - return 0; + return tty_init(); } fs_initcall(chr_dev_init); diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index d71f0fc34b46..507441ac6edb 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -3128,7 +3128,7 @@ static struct cdev tty_cdev, console_cdev; * Ok, now we can initialize the rest of the tty devices and can count * on memory allocations, interrupts etc.. */ -static int __init tty_init(void) +int __init tty_init(void) { cdev_init(&tty_cdev, &tty_fops); if (cdev_add(&tty_cdev, MKDEV(TTYAUX_MAJOR, 0), 1) || @@ -3149,4 +3149,4 @@ static int __init tty_init(void) #endif return 0; } -module_init(tty_init); + diff --git a/fs/char_dev.c b/fs/char_dev.c index d6db933df2b2..f80a4f25123c 100644 --- a/fs/char_dev.c +++ b/fs/char_dev.c @@ -20,6 +20,7 @@ #include #include #include +#include #include "internal.h" diff --git a/include/linux/tty.h b/include/linux/tty.h index 931078b73226..7802a243ee13 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -552,6 +552,9 @@ static inline void tty_audit_push_task(struct task_struct *tsk, } #endif +/* tty_io.c */ +extern int __init tty_init(void); + /* tty_ioctl.c */ extern int n_tty_ioctl_helper(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg); -- cgit v1.2.3 From 761fe93cdfa29071879d882c92e966ae692c0048 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Wed, 4 Aug 2010 09:29:52 -0400 Subject: NFS: Fix the locking in nfs4_callback_getattr The delegation is protected by RCU now, so we need to replace the nfsi->rwsem protection with an rcu protected section. Reported-by: Fred Isaman Signed-off-by: Trond Myklebust --- fs/nfs/callback_proc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/nfs/callback_proc.c b/fs/nfs/callback_proc.c index 7445dd0ae3f3..930d10fecdaf 100644 --- a/fs/nfs/callback_proc.c +++ b/fs/nfs/callback_proc.c @@ -37,8 +37,8 @@ __be32 nfs4_callback_getattr(struct cb_getattrargs *args, struct cb_getattrres * if (inode == NULL) goto out_putclient; nfsi = NFS_I(inode); - down_read(&nfsi->rwsem); - delegation = nfsi->delegation; + rcu_read_lock(); + delegation = rcu_dereference(nfsi->delegation); if (delegation == NULL || (delegation->type & FMODE_WRITE) == 0) goto out_iput; res->size = i_size_read(inode); @@ -53,7 +53,7 @@ __be32 nfs4_callback_getattr(struct cb_getattrargs *args, struct cb_getattrres * args->bitmap[1]; res->status = 0; out_iput: - up_read(&nfsi->rwsem); + rcu_read_unlock(); iput(inode); out_putclient: nfs_put_client(clp); -- cgit v1.2.3 From d5eff1a3412f6d75bf28f423c5015ece8055407a Mon Sep 17 00:00:00 2001 From: Bryan Schumaker Date: Tue, 3 Aug 2010 13:04:00 -0400 Subject: NFS: Fix /proc/mount for legacy binary interface Add a flag so we know if we mounted the NFS server using the legacy binary interface. If we used the legacy interface, then we should not show the mountd options. Signed-off-by: Bryan Schumaker Signed-off-by: Trond Myklebust --- fs/nfs/super.c | 4 ++++ include/linux/nfs_mount.h | 1 + 2 files changed, 5 insertions(+) (limited to 'fs') diff --git a/fs/nfs/super.c b/fs/nfs/super.c index f9df16de4a56..f1ae39f6cb02 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -546,6 +546,9 @@ static void nfs_show_mountd_options(struct seq_file *m, struct nfs_server *nfss, { struct sockaddr *sap = (struct sockaddr *)&nfss->mountd_address; + if (nfss->flags & NFS_MOUNT_LEGACY_INTERFACE) + return; + switch (sap->sa_family) { case AF_INET: { struct sockaddr_in *sin = (struct sockaddr_in *)sap; @@ -1780,6 +1783,7 @@ static int nfs_validate_mount_data(void *options, * can deal with. */ args->flags = data->flags & NFS_MOUNT_FLAGMASK; + args->flags |= NFS_MOUNT_LEGACY_INTERFACE; args->rsize = data->rsize; args->wsize = data->wsize; args->timeo = data->timeo; diff --git a/include/linux/nfs_mount.h b/include/linux/nfs_mount.h index 4499016e6d0d..5d59ae861aa6 100644 --- a/include/linux/nfs_mount.h +++ b/include/linux/nfs_mount.h @@ -69,5 +69,6 @@ struct nfs_mount_data { #define NFS_MOUNT_LOOKUP_CACHE_NONEG 0x10000 #define NFS_MOUNT_LOOKUP_CACHE_NONE 0x20000 #define NFS_MOUNT_NORESVPORT 0x40000 +#define NFS_MOUNT_LEGACY_INTERFACE 0x80000 #endif -- cgit v1.2.3 From b3edc2bc19b4856c705f4aea3f5472970b99f386 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Wed, 4 Aug 2010 14:38:01 -0400 Subject: NFS: NFS_V4 is no longer an EXPERIMENTAL feature Signed-off-by: Trond Myklebust --- fs/nfs/Kconfig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/nfs/Kconfig b/fs/nfs/Kconfig index a43d07e7b924..d55f3f5371b2 100644 --- a/fs/nfs/Kconfig +++ b/fs/nfs/Kconfig @@ -61,8 +61,8 @@ config NFS_V3_ACL If unsure, say N. config NFS_V4 - bool "NFS client support for NFS version 4 (EXPERIMENTAL)" - depends on NFS_FS && EXPERIMENTAL + bool "NFS client support for NFS version 4" + depends on NFS_FS select RPCSEC_GSS_KRB5 help This option enables support for version 4 of the NFS protocol @@ -72,7 +72,7 @@ config NFS_V4 space programs which can be found in the Linux nfs-utils package, available from http://linux-nfs.org/. - If unsure, say N. + If unsure, say Y. config NFS_V4_1 bool "NFS client support for NFSv4.1 (DEVELOPER ONLY)" -- cgit v1.2.3 From 3dce9a5c3a39a664e372886ea86c42ae7ae33dfc Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Wed, 4 Aug 2010 14:39:16 -0400 Subject: NFS: NFSv4.1 is no longer a "developer only" feature Mark it as 'experimental' instead, since in practice, NFSv4.1 should now be relatively stable. Signed-off-by: Trond Myklebust --- fs/nfs/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/nfs/Kconfig b/fs/nfs/Kconfig index d55f3f5371b2..cc1bb33b59b8 100644 --- a/fs/nfs/Kconfig +++ b/fs/nfs/Kconfig @@ -75,13 +75,13 @@ config NFS_V4 If unsure, say Y. config NFS_V4_1 - bool "NFS client support for NFSv4.1 (DEVELOPER ONLY)" + bool "NFS client support for NFSv4.1 (EXPERIMENTAL)" depends on NFS_V4 && EXPERIMENTAL help This option enables support for minor version 1 of the NFSv4 protocol (draft-ietf-nfsv4-minorversion1) in the kernel's NFS client. - Unless you're an NFS developer, say N. + If unsure, say N. config ROOT_NFS bool "Root file system on NFS" -- cgit v1.2.3 From 41f2df62894bfcd3bf868af916b32b90aa7168dc Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 17 Jun 2010 08:54:16 +0200 Subject: block: BARRIER request should imply SYNC A barrier request should by defintion have priority in get_request and let the queue be unplugged immediately as it's blocking all forward progress due to the queue draining. Most filesystems already get this implicitly by the way how submit_bh treats the buffer_ordered flag, and gfs2 sets it explicitly. But btrfs and XFS are still forgetting to set the flag, as is blkdev_issue_flush and some places in DM/MD. For XFS on metadata heavy workloads this gives a consistent speedup in the 2-3% range. Signed-off-by: Christoph Hellwig Signed-off-by: Jens Axboe --- fs/gfs2/log.c | 2 +- include/linux/fs.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/gfs2/log.c b/fs/gfs2/log.c index 6a857e24f947..efc3539ac5a1 100644 --- a/fs/gfs2/log.c +++ b/fs/gfs2/log.c @@ -595,7 +595,7 @@ static void log_write_header(struct gfs2_sbd *sdp, u32 flags, int pull) if (test_bit(SDF_NOBARRIERS, &sdp->sd_flags)) goto skip_barrier; get_bh(bh); - submit_bh(WRITE_SYNC | (1 << BIO_RW_BARRIER) | (1 << BIO_RW_META), bh); + submit_bh(WRITE_BARRIER | (1 << BIO_RW_META), bh); wait_on_buffer(bh); if (buffer_eopnotsupp(bh)) { clear_buffer_eopnotsupp(bh); diff --git a/include/linux/fs.h b/include/linux/fs.h index 68ca1b0491af..598878831497 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -136,7 +136,7 @@ struct inodes_stat_t { * SWRITE_SYNC * SWRITE_SYNC_PLUG Like WRITE_SYNC/WRITE_SYNC_PLUG, but locks the buffer. * See SWRITE. - * WRITE_BARRIER Like WRITE, but tells the block layer that all + * WRITE_BARRIER Like WRITE_SYNC, but tells the block layer that all * previously submitted writes must be safely on storage * before this one is started. Also guarantees that when * this write is complete, it itself is also safely on @@ -159,7 +159,7 @@ struct inodes_stat_t { #define SWRITE_SYNC_PLUG \ (SWRITE | (1 << BIO_RW_SYNCIO) | (1 << BIO_RW_NOIDLE)) #define SWRITE_SYNC (SWRITE_SYNC_PLUG | (1 << BIO_RW_UNPLUG)) -#define WRITE_BARRIER (WRITE | (1 << BIO_RW_BARRIER)) +#define WRITE_BARRIER (WRITE_SYNC | (1 << BIO_RW_BARRIER)) /* * These aren't really reads or writes, they pass down information about -- cgit v1.2.3 From 7b6d91daee5cac6402186ff224c3af39d79f4a0e Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sat, 7 Aug 2010 18:20:39 +0200 Subject: block: unify flags for struct bio and struct request Remove the current bio flags and reuse the request flags for the bio, too. This allows to more easily trace the type of I/O from the filesystem down to the block driver. There were two flags in the bio that were missing in the requests: BIO_RW_UNPLUG and BIO_RW_AHEAD. Also I've renamed two request flags that had a superflous RW in them. Note that the flags are in bio.h despite having the REQ_ name - as blkdev.h includes bio.h that is the only way to go for now. Signed-off-by: Christoph Hellwig Signed-off-by: Jens Axboe --- block/blk-barrier.c | 2 +- block/blk-core.c | 37 +++-------- block/blk-map.c | 2 +- block/blk-merge.c | 2 +- block/cfq-iosched.c | 14 ++--- block/elevator.c | 3 +- drivers/ata/libata-scsi.c | 2 +- drivers/block/aoe/aoeblk.c | 2 +- drivers/block/brd.c | 2 +- drivers/block/drbd/drbd_actlog.c | 8 +-- drivers/block/drbd/drbd_main.c | 6 +- drivers/block/drbd/drbd_receiver.c | 22 +++---- drivers/block/drbd/drbd_req.c | 2 +- drivers/block/loop.c | 2 +- drivers/block/pktcdvd.c | 2 +- drivers/block/umem.c | 2 +- drivers/ide/ide-cd_ioctl.c | 2 +- drivers/ide/ide-floppy.c | 2 +- drivers/md/dm-io.c | 12 ++-- drivers/md/dm-kcopyd.c | 2 +- drivers/md/dm-raid1.c | 2 +- drivers/md/dm-stripe.c | 2 +- drivers/md/dm.c | 14 ++--- drivers/md/linear.c | 2 +- drivers/md/md.c | 10 +-- drivers/md/md.h | 4 +- drivers/md/multipath.c | 8 +-- drivers/md/raid0.c | 2 +- drivers/md/raid1.c | 22 +++---- drivers/md/raid10.c | 12 ++-- drivers/md/raid5.c | 2 +- drivers/scsi/osd/osd_initiator.c | 8 +-- fs/bio.c | 5 +- fs/btrfs/disk-io.c | 8 +-- fs/btrfs/inode.c | 6 +- fs/btrfs/volumes.c | 18 +++--- fs/exofs/ios.c | 2 +- fs/gfs2/log.c | 4 +- fs/gfs2/meta_io.c | 8 +-- fs/gfs2/ops_fstype.c | 2 +- fs/nilfs2/segbuf.c | 2 +- include/linux/bio.h | 125 +++++++++++++++++++++++-------------- include/linux/blkdev.h | 66 +------------------- include/linux/fs.h | 38 +++++------ kernel/power/block_io.c | 2 +- kernel/trace/blktrace.c | 27 ++++---- mm/page_io.c | 2 +- 47 files changed, 242 insertions(+), 289 deletions(-) (limited to 'fs') diff --git a/block/blk-barrier.c b/block/blk-barrier.c index 74e404393172..7c6f4a714687 100644 --- a/block/blk-barrier.c +++ b/block/blk-barrier.c @@ -203,7 +203,7 @@ static inline bool start_ordered(struct request_queue *q, struct request **rqp) /* initialize proxy request and queue it */ blk_rq_init(q, rq); if (bio_data_dir(q->orig_bar_rq->bio) == WRITE) - rq->cmd_flags |= REQ_RW; + rq->cmd_flags |= REQ_WRITE; if (q->ordered & QUEUE_ORDERED_DO_FUA) rq->cmd_flags |= REQ_FUA; init_request_from_bio(rq, q->orig_bar_rq->bio); diff --git a/block/blk-core.c b/block/blk-core.c index dca43a31e725..66c3cfe94d0a 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -1140,25 +1140,9 @@ void init_request_from_bio(struct request *req, struct bio *bio) req->cpu = bio->bi_comp_cpu; req->cmd_type = REQ_TYPE_FS; - /* - * Inherit FAILFAST from bio (for read-ahead, and explicit - * FAILFAST). FAILFAST flags are identical for req and bio. - */ - if (bio_rw_flagged(bio, BIO_RW_AHEAD)) + req->cmd_flags |= bio->bi_rw & REQ_COMMON_MASK; + if (bio->bi_rw & REQ_RAHEAD) req->cmd_flags |= REQ_FAILFAST_MASK; - else - req->cmd_flags |= bio->bi_rw & REQ_FAILFAST_MASK; - - if (bio_rw_flagged(bio, BIO_RW_DISCARD)) - req->cmd_flags |= REQ_DISCARD; - if (bio_rw_flagged(bio, BIO_RW_BARRIER)) - req->cmd_flags |= REQ_HARDBARRIER; - if (bio_rw_flagged(bio, BIO_RW_SYNCIO)) - req->cmd_flags |= REQ_RW_SYNC; - if (bio_rw_flagged(bio, BIO_RW_META)) - req->cmd_flags |= REQ_RW_META; - if (bio_rw_flagged(bio, BIO_RW_NOIDLE)) - req->cmd_flags |= REQ_NOIDLE; req->errors = 0; req->__sector = bio->bi_sector; @@ -1181,12 +1165,12 @@ static int __make_request(struct request_queue *q, struct bio *bio) int el_ret; unsigned int bytes = bio->bi_size; const unsigned short prio = bio_prio(bio); - const bool sync = bio_rw_flagged(bio, BIO_RW_SYNCIO); - const bool unplug = bio_rw_flagged(bio, BIO_RW_UNPLUG); + const bool sync = (bio->bi_rw & REQ_SYNC); + const bool unplug = (bio->bi_rw & REQ_UNPLUG); const unsigned int ff = bio->bi_rw & REQ_FAILFAST_MASK; int rw_flags; - if (bio_rw_flagged(bio, BIO_RW_BARRIER) && + if ((bio->bi_rw & REQ_HARDBARRIER) && (q->next_ordered == QUEUE_ORDERED_NONE)) { bio_endio(bio, -EOPNOTSUPP); return 0; @@ -1200,7 +1184,7 @@ static int __make_request(struct request_queue *q, struct bio *bio) spin_lock_irq(q->queue_lock); - if (unlikely(bio_rw_flagged(bio, BIO_RW_BARRIER)) || elv_queue_empty(q)) + if (unlikely((bio->bi_rw & REQ_HARDBARRIER)) || elv_queue_empty(q)) goto get_rq; el_ret = elv_merge(q, &req, bio); @@ -1275,7 +1259,7 @@ get_rq: */ rw_flags = bio_data_dir(bio); if (sync) - rw_flags |= REQ_RW_SYNC; + rw_flags |= REQ_SYNC; /* * Grab a free request. This is might sleep but can not fail. @@ -1464,7 +1448,7 @@ static inline void __generic_make_request(struct bio *bio) goto end_io; } - if (unlikely(!bio_rw_flagged(bio, BIO_RW_DISCARD) && + if (unlikely(!(bio->bi_rw & REQ_DISCARD) && nr_sectors > queue_max_hw_sectors(q))) { printk(KERN_ERR "bio too big device %s (%u > %u)\n", bdevname(bio->bi_bdev, b), @@ -1497,8 +1481,7 @@ static inline void __generic_make_request(struct bio *bio) if (bio_check_eod(bio, nr_sectors)) goto end_io; - if (bio_rw_flagged(bio, BIO_RW_DISCARD) && - !blk_queue_discard(q)) { + if ((bio->bi_rw & REQ_DISCARD) && !blk_queue_discard(q)) { err = -EOPNOTSUPP; goto end_io; } @@ -2365,7 +2348,7 @@ void blk_rq_bio_prep(struct request_queue *q, struct request *rq, struct bio *bio) { /* Bit 0 (R/W) is identical in rq->cmd_flags and bio->bi_rw */ - rq->cmd_flags |= bio->bi_rw & REQ_RW; + rq->cmd_flags |= bio->bi_rw & REQ_WRITE; if (bio_has_data(bio)) { rq->nr_phys_segments = bio_phys_segments(q, bio); diff --git a/block/blk-map.c b/block/blk-map.c index 9083cf0180cc..c65d7593f7f1 100644 --- a/block/blk-map.c +++ b/block/blk-map.c @@ -307,7 +307,7 @@ int blk_rq_map_kern(struct request_queue *q, struct request *rq, void *kbuf, return PTR_ERR(bio); if (rq_data_dir(rq) == WRITE) - bio->bi_rw |= (1 << BIO_RW); + bio->bi_rw |= (1 << REQ_WRITE); if (do_copy) rq->cmd_flags |= REQ_COPY_USER; diff --git a/block/blk-merge.c b/block/blk-merge.c index 87e4fb7d0e98..4852475521ea 100644 --- a/block/blk-merge.c +++ b/block/blk-merge.c @@ -180,7 +180,7 @@ new_segment: } if (q->dma_drain_size && q->dma_drain_needed(rq)) { - if (rq->cmd_flags & REQ_RW) + if (rq->cmd_flags & REQ_WRITE) memset(q->dma_drain_buffer, 0, q->dma_drain_size); sg->page_link &= ~0x02; diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index d4edeb8fceb8..eb4086f7dfef 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -458,7 +458,7 @@ static inline struct cfq_data *cic_to_cfqd(struct cfq_io_context *cic) */ static inline bool cfq_bio_sync(struct bio *bio) { - return bio_data_dir(bio) == READ || bio_rw_flagged(bio, BIO_RW_SYNCIO); + return bio_data_dir(bio) == READ || (bio->bi_rw & REQ_SYNC); } /* @@ -646,10 +646,10 @@ cfq_choose_req(struct cfq_data *cfqd, struct request *rq1, struct request *rq2, return rq1; else if (rq_is_sync(rq2) && !rq_is_sync(rq1)) return rq2; - if ((rq1->cmd_flags & REQ_RW_META) && !(rq2->cmd_flags & REQ_RW_META)) + if ((rq1->cmd_flags & REQ_META) && !(rq2->cmd_flags & REQ_META)) return rq1; - else if ((rq2->cmd_flags & REQ_RW_META) && - !(rq1->cmd_flags & REQ_RW_META)) + else if ((rq2->cmd_flags & REQ_META) && + !(rq1->cmd_flags & REQ_META)) return rq2; s1 = blk_rq_pos(rq1); @@ -1485,7 +1485,7 @@ static void cfq_remove_request(struct request *rq) cfqq->cfqd->rq_queued--; cfq_blkiocg_update_io_remove_stats(&(RQ_CFQG(rq))->blkg, rq_data_dir(rq), rq_is_sync(rq)); - if (rq->cmd_flags & REQ_RW_META) { + if (rq->cmd_flags & REQ_META) { WARN_ON(!cfqq->meta_pending); cfqq->meta_pending--; } @@ -3177,7 +3177,7 @@ cfq_should_preempt(struct cfq_data *cfqd, struct cfq_queue *new_cfqq, * So both queues are sync. Let the new request get disk time if * it's a metadata request and the current queue is doing regular IO. */ - if ((rq->cmd_flags & REQ_RW_META) && !cfqq->meta_pending) + if ((rq->cmd_flags & REQ_META) && !cfqq->meta_pending) return true; /* @@ -3231,7 +3231,7 @@ cfq_rq_enqueued(struct cfq_data *cfqd, struct cfq_queue *cfqq, struct cfq_io_context *cic = RQ_CIC(rq); cfqd->rq_queued++; - if (rq->cmd_flags & REQ_RW_META) + if (rq->cmd_flags & REQ_META) cfqq->meta_pending++; cfq_update_io_thinktime(cfqd, cic); diff --git a/block/elevator.c b/block/elevator.c index aa99b59c03d6..816a7c8d6394 100644 --- a/block/elevator.c +++ b/block/elevator.c @@ -79,8 +79,7 @@ int elv_rq_merge_ok(struct request *rq, struct bio *bio) /* * Don't merge file system requests and discard requests */ - if (bio_rw_flagged(bio, BIO_RW_DISCARD) != - bio_rw_flagged(rq->bio, BIO_RW_DISCARD)) + if ((bio->bi_rw & REQ_DISCARD) != (rq->bio->bi_rw & REQ_DISCARD)) return 0; /* diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index a5c08b082edb..0a8cd3484791 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -1114,7 +1114,7 @@ static int atapi_drain_needed(struct request *rq) if (likely(rq->cmd_type != REQ_TYPE_BLOCK_PC)) return 0; - if (!blk_rq_bytes(rq) || (rq->cmd_flags & REQ_RW)) + if (!blk_rq_bytes(rq) || (rq->cmd_flags & REQ_WRITE)) return 0; return atapi_cmd_type(rq->cmd[0]) == ATAPI_MISC; diff --git a/drivers/block/aoe/aoeblk.c b/drivers/block/aoe/aoeblk.c index 035cefe4045a..65deffde60ac 100644 --- a/drivers/block/aoe/aoeblk.c +++ b/drivers/block/aoe/aoeblk.c @@ -173,7 +173,7 @@ aoeblk_make_request(struct request_queue *q, struct bio *bio) BUG(); bio_endio(bio, -ENXIO); return 0; - } else if (bio_rw_flagged(bio, BIO_RW_BARRIER)) { + } else if (bio->bi_rw & REQ_HARDBARRIER) { bio_endio(bio, -EOPNOTSUPP); return 0; } else if (bio->bi_io_vec == NULL) { diff --git a/drivers/block/brd.c b/drivers/block/brd.c index f1bf79d9bc0a..1b218c6b6820 100644 --- a/drivers/block/brd.c +++ b/drivers/block/brd.c @@ -340,7 +340,7 @@ static int brd_make_request(struct request_queue *q, struct bio *bio) get_capacity(bdev->bd_disk)) goto out; - if (unlikely(bio_rw_flagged(bio, BIO_RW_DISCARD))) { + if (unlikely(bio->bi_rw & REQ_DISCARD)) { err = 0; discard_from_brd(brd, sector, bio->bi_size); goto out; diff --git a/drivers/block/drbd/drbd_actlog.c b/drivers/block/drbd/drbd_actlog.c index df018990c422..9400845d602e 100644 --- a/drivers/block/drbd/drbd_actlog.c +++ b/drivers/block/drbd/drbd_actlog.c @@ -79,8 +79,8 @@ static int _drbd_md_sync_page_io(struct drbd_conf *mdev, md_io.error = 0; if ((rw & WRITE) && !test_bit(MD_NO_BARRIER, &mdev->flags)) - rw |= (1 << BIO_RW_BARRIER); - rw |= ((1<bi_rw & REQ_HARDBARRIER) && !ok)) { /* Try again with no barrier */ dev_warn(DEV, "Barriers not supported on meta data device - disabling\n"); set_bit(MD_NO_BARRIER, &mdev->flags); - rw &= ~(1 << BIO_RW_BARRIER); + rw &= ~REQ_HARDBARRIER; bio_put(bio); goto retry; } diff --git a/drivers/block/drbd/drbd_main.c b/drivers/block/drbd/drbd_main.c index 7258c95e895e..e2ab13d99d69 100644 --- a/drivers/block/drbd/drbd_main.c +++ b/drivers/block/drbd/drbd_main.c @@ -2425,15 +2425,15 @@ int drbd_send_dblock(struct drbd_conf *mdev, struct drbd_request *req) /* NOTE: no need to check if barriers supported here as we would * not pass the test in make_request_common in that case */ - if (bio_rw_flagged(req->master_bio, BIO_RW_BARRIER)) { + if (req->master_bio->bi_rw & REQ_HARDBARRIER) { dev_err(DEV, "ASSERT FAILED would have set DP_HARDBARRIER\n"); /* dp_flags |= DP_HARDBARRIER; */ } - if (bio_rw_flagged(req->master_bio, BIO_RW_SYNCIO)) + if (req->master_bio->bi_rw & REQ_SYNC) dp_flags |= DP_RW_SYNC; /* for now handle SYNCIO and UNPLUG * as if they still were one and the same flag */ - if (bio_rw_flagged(req->master_bio, BIO_RW_UNPLUG)) + if (req->master_bio->bi_rw & REQ_UNPLUG) dp_flags |= DP_RW_SYNC; if (mdev->state.conn >= C_SYNC_SOURCE && mdev->state.conn <= C_PAUSED_SYNC_T) diff --git a/drivers/block/drbd/drbd_receiver.c b/drivers/block/drbd/drbd_receiver.c index dff48701b84d..cba1deb7b271 100644 --- a/drivers/block/drbd/drbd_receiver.c +++ b/drivers/block/drbd/drbd_receiver.c @@ -1180,7 +1180,7 @@ next_bio: bio->bi_sector = sector; bio->bi_bdev = mdev->ldev->backing_bdev; /* we special case some flags in the multi-bio case, see below - * (BIO_RW_UNPLUG, BIO_RW_BARRIER) */ + * (REQ_UNPLUG, REQ_HARDBARRIER) */ bio->bi_rw = rw; bio->bi_private = e; bio->bi_end_io = drbd_endio_sec; @@ -1209,16 +1209,16 @@ next_bio: bios = bios->bi_next; bio->bi_next = NULL; - /* strip off BIO_RW_UNPLUG unless it is the last bio */ + /* strip off REQ_UNPLUG unless it is the last bio */ if (bios) - bio->bi_rw &= ~(1<bi_rw &= ~REQ_UNPLUG; drbd_generic_make_request(mdev, fault_type, bio); - /* strip off BIO_RW_BARRIER, + /* strip off REQ_HARDBARRIER, * unless it is the first or last bio */ if (bios && bios->bi_next) - bios->bi_rw &= ~(1<bi_rw &= ~REQ_HARDBARRIER; } while (bios); maybe_kick_lo(mdev); return 0; @@ -1233,7 +1233,7 @@ fail: } /** - * w_e_reissue() - Worker callback; Resubmit a bio, without BIO_RW_BARRIER set + * w_e_reissue() - Worker callback; Resubmit a bio, without REQ_HARDBARRIER set * @mdev: DRBD device. * @w: work object. * @cancel: The connection will be closed anyways (unused in this callback) @@ -1245,7 +1245,7 @@ int w_e_reissue(struct drbd_conf *mdev, struct drbd_work *w, int cancel) __relea (and DE_BARRIER_IN_NEXT_EPOCH_ISSUED in the previous Epoch) so that we can finish that epoch in drbd_may_finish_epoch(). That is necessary if we already have a long chain of Epochs, before - we realize that BIO_RW_BARRIER is actually not supported */ + we realize that REQ_HARDBARRIER is actually not supported */ /* As long as the -ENOTSUPP on the barrier is reported immediately that will never trigger. If it is reported late, we will just @@ -1824,14 +1824,14 @@ static int receive_Data(struct drbd_conf *mdev, struct p_header *h) epoch = list_entry(e->epoch->list.prev, struct drbd_epoch, list); if (epoch == e->epoch) { set_bit(DE_CONTAINS_A_BARRIER, &e->epoch->flags); - rw |= (1<flags |= EE_IS_BARRIER; } else { if (atomic_read(&epoch->epoch_size) > 1 || !test_bit(DE_CONTAINS_A_BARRIER, &epoch->flags)) { set_bit(DE_BARRIER_IN_NEXT_EPOCH_ISSUED, &epoch->flags); set_bit(DE_CONTAINS_A_BARRIER, &e->epoch->flags); - rw |= (1<flags |= EE_IS_BARRIER; } } @@ -1841,10 +1841,10 @@ static int receive_Data(struct drbd_conf *mdev, struct p_header *h) dp_flags = be32_to_cpu(p->dp_flags); if (dp_flags & DP_HARDBARRIER) { dev_err(DEV, "ASSERT FAILED would have submitted barrier request\n"); - /* rw |= (1<flags |= EE_MAY_SET_IN_SYNC; diff --git a/drivers/block/drbd/drbd_req.c b/drivers/block/drbd/drbd_req.c index 654f1ef5cbb0..f761d98a4e90 100644 --- a/drivers/block/drbd/drbd_req.c +++ b/drivers/block/drbd/drbd_req.c @@ -997,7 +997,7 @@ int drbd_make_request_26(struct request_queue *q, struct bio *bio) * because of those XXX, this is not yet enabled, * i.e. in drbd_init_set_defaults we set the NO_BARRIER_SUPP bit. */ - if (unlikely(bio_rw_flagged(bio, BIO_RW_BARRIER) && test_bit(NO_BARRIER_SUPP, &mdev->flags))) { + if (unlikely(bio->bi_rw & REQ_HARDBARRIER) && test_bit(NO_BARRIER_SUPP, &mdev->flags)) { /* dev_warn(DEV, "Rejecting barrier request as underlying device does not support\n"); */ bio_endio(bio, -EOPNOTSUPP); return 0; diff --git a/drivers/block/loop.c b/drivers/block/loop.c index 6120922f459f..fedfdb7d3cdf 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -476,7 +476,7 @@ static int do_bio_filebacked(struct loop_device *lo, struct bio *bio) pos = ((loff_t) bio->bi_sector << 9) + lo->lo_offset; if (bio_rw(bio) == WRITE) { - bool barrier = bio_rw_flagged(bio, BIO_RW_BARRIER); + bool barrier = (bio->bi_rw & REQ_HARDBARRIER); struct file *file = lo->lo_backing_file; if (barrier) { diff --git a/drivers/block/pktcdvd.c b/drivers/block/pktcdvd.c index 8a549db2aa78..9f3e4454274b 100644 --- a/drivers/block/pktcdvd.c +++ b/drivers/block/pktcdvd.c @@ -1221,7 +1221,7 @@ static int pkt_start_recovery(struct packet_data *pkt) pkt->bio->bi_flags = 1 << BIO_UPTODATE; pkt->bio->bi_idx = 0; - BUG_ON(pkt->bio->bi_rw != (1 << BIO_RW)); + BUG_ON(pkt->bio->bi_rw != REQ_WRITE); BUG_ON(pkt->bio->bi_vcnt != pkt->frames); BUG_ON(pkt->bio->bi_size != pkt->frames * CD_FRAMESIZE); BUG_ON(pkt->bio->bi_end_io != pkt_end_io_packet_write); diff --git a/drivers/block/umem.c b/drivers/block/umem.c index 2f9470ff8f7c..8be57151f5d6 100644 --- a/drivers/block/umem.c +++ b/drivers/block/umem.c @@ -478,7 +478,7 @@ static void process_page(unsigned long data) le32_to_cpu(desc->local_addr)>>9, le32_to_cpu(desc->transfer_size)); dump_dmastat(card, control); - } else if (test_bit(BIO_RW, &bio->bi_rw) && + } else if ((bio->bi_rw & REQ_WRITE) && le32_to_cpu(desc->local_addr) >> 9 == card->init_size) { card->init_size += le32_to_cpu(desc->transfer_size) >> 9; diff --git a/drivers/ide/ide-cd_ioctl.c b/drivers/ide/ide-cd_ioctl.c index 02712bf045c1..766b3deeb23c 100644 --- a/drivers/ide/ide-cd_ioctl.c +++ b/drivers/ide/ide-cd_ioctl.c @@ -454,7 +454,7 @@ int ide_cdrom_packet(struct cdrom_device_info *cdi, touch it at all. */ if (cgc->data_direction == CGC_DATA_WRITE) - flags |= REQ_RW; + flags |= REQ_WRITE; if (cgc->sense) memset(cgc->sense, 0, sizeof(struct request_sense)); diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c index c7d0737bb18a..5406b6ea3ad1 100644 --- a/drivers/ide/ide-floppy.c +++ b/drivers/ide/ide-floppy.c @@ -207,7 +207,7 @@ static void idefloppy_create_rw_cmd(ide_drive_t *drive, memcpy(rq->cmd, pc->c, 12); pc->rq = rq; - if (rq->cmd_flags & REQ_RW) + if (rq->cmd_flags & REQ_WRITE) pc->flags |= PC_FLAG_WRITING; pc->flags |= PC_FLAG_DMA_OK; diff --git a/drivers/md/dm-io.c b/drivers/md/dm-io.c index 10f457ca6af2..0590c75b0ab6 100644 --- a/drivers/md/dm-io.c +++ b/drivers/md/dm-io.c @@ -356,7 +356,7 @@ static void dispatch_io(int rw, unsigned int num_regions, BUG_ON(num_regions > DM_IO_MAX_REGIONS); if (sync) - rw |= (1 << BIO_RW_SYNCIO) | (1 << BIO_RW_UNPLUG); + rw |= REQ_SYNC | REQ_UNPLUG; /* * For multiple regions we need to be careful to rewind @@ -364,7 +364,7 @@ static void dispatch_io(int rw, unsigned int num_regions, */ for (i = 0; i < num_regions; i++) { *dp = old_pages; - if (where[i].count || (rw & (1 << BIO_RW_BARRIER))) + if (where[i].count || (rw & REQ_HARDBARRIER)) do_region(rw, i, where + i, dp, io); } @@ -412,8 +412,8 @@ retry: } set_current_state(TASK_RUNNING); - if (io->eopnotsupp_bits && (rw & (1 << BIO_RW_BARRIER))) { - rw &= ~(1 << BIO_RW_BARRIER); + if (io->eopnotsupp_bits && (rw & REQ_HARDBARRIER)) { + rw &= ~REQ_HARDBARRIER; goto retry; } @@ -479,8 +479,8 @@ static int dp_init(struct dm_io_request *io_req, struct dpages *dp) * New collapsed (a)synchronous interface. * * If the IO is asynchronous (i.e. it has notify.fn), you must either unplug - * the queue with blk_unplug() some time later or set the BIO_RW_SYNC bit in - * io_req->bi_rw. If you fail to do one of these, the IO will be submitted to + * the queue with blk_unplug() some time later or set REQ_SYNC in +io_req->bi_rw. If you fail to do one of these, the IO will be submitted to * the disk after q->unplug_delay, which defaults to 3ms in blk-settings.c. */ int dm_io(struct dm_io_request *io_req, unsigned num_regions, diff --git a/drivers/md/dm-kcopyd.c b/drivers/md/dm-kcopyd.c index addf83475040..d8587bac5682 100644 --- a/drivers/md/dm-kcopyd.c +++ b/drivers/md/dm-kcopyd.c @@ -345,7 +345,7 @@ static int run_io_job(struct kcopyd_job *job) { int r; struct dm_io_request io_req = { - .bi_rw = job->rw | (1 << BIO_RW_SYNCIO) | (1 << BIO_RW_UNPLUG), + .bi_rw = job->rw | REQ_SYNC | REQ_UNPLUG, .mem.type = DM_IO_PAGE_LIST, .mem.ptr.pl = job->pages, .mem.offset = job->offset, diff --git a/drivers/md/dm-raid1.c b/drivers/md/dm-raid1.c index ddda531723dc..74136262d654 100644 --- a/drivers/md/dm-raid1.c +++ b/drivers/md/dm-raid1.c @@ -1211,7 +1211,7 @@ static int mirror_end_io(struct dm_target *ti, struct bio *bio, if (error == -EOPNOTSUPP) goto out; - if ((error == -EWOULDBLOCK) && bio_rw_flagged(bio, BIO_RW_AHEAD)) + if ((error == -EWOULDBLOCK) && (bio->bi_rw & REQ_RAHEAD)) goto out; if (unlikely(error)) { diff --git a/drivers/md/dm-stripe.c b/drivers/md/dm-stripe.c index e610725db766..d6e28d732b4d 100644 --- a/drivers/md/dm-stripe.c +++ b/drivers/md/dm-stripe.c @@ -284,7 +284,7 @@ static int stripe_end_io(struct dm_target *ti, struct bio *bio, if (!error) return 0; /* I/O complete */ - if ((error == -EWOULDBLOCK) && bio_rw_flagged(bio, BIO_RW_AHEAD)) + if ((error == -EWOULDBLOCK) && (bio->bi_rw & REQ_RAHEAD)) return error; if (error == -EOPNOTSUPP) diff --git a/drivers/md/dm.c b/drivers/md/dm.c index 1e0e6dd51501..d6f77baeafd6 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -614,7 +614,7 @@ static void dec_pending(struct dm_io *io, int error) */ spin_lock_irqsave(&md->deferred_lock, flags); if (__noflush_suspending(md)) { - if (!bio_rw_flagged(io->bio, BIO_RW_BARRIER)) + if (!(io->bio->bi_rw & REQ_HARDBARRIER)) bio_list_add_head(&md->deferred, io->bio); } else @@ -626,7 +626,7 @@ static void dec_pending(struct dm_io *io, int error) io_error = io->error; bio = io->bio; - if (bio_rw_flagged(bio, BIO_RW_BARRIER)) { + if (bio->bi_rw & REQ_HARDBARRIER) { /* * There can be just one barrier request so we use * a per-device variable for error reporting. @@ -1106,7 +1106,7 @@ static struct bio *split_bvec(struct bio *bio, sector_t sector, clone->bi_sector = sector; clone->bi_bdev = bio->bi_bdev; - clone->bi_rw = bio->bi_rw & ~(1 << BIO_RW_BARRIER); + clone->bi_rw = bio->bi_rw & ~REQ_HARDBARRIER; clone->bi_vcnt = 1; clone->bi_size = to_bytes(len); clone->bi_io_vec->bv_offset = offset; @@ -1133,7 +1133,7 @@ static struct bio *clone_bio(struct bio *bio, sector_t sector, clone = bio_alloc_bioset(GFP_NOIO, bio->bi_max_vecs, bs); __bio_clone(clone, bio); - clone->bi_rw &= ~(1 << BIO_RW_BARRIER); + clone->bi_rw &= ~REQ_HARDBARRIER; clone->bi_destructor = dm_bio_destructor; clone->bi_sector = sector; clone->bi_idx = idx; @@ -1301,7 +1301,7 @@ static void __split_and_process_bio(struct mapped_device *md, struct bio *bio) ci.map = dm_get_live_table(md); if (unlikely(!ci.map)) { - if (!bio_rw_flagged(bio, BIO_RW_BARRIER)) + if (!(bio->bi_rw & REQ_HARDBARRIER)) bio_io_error(bio); else if (!md->barrier_error) @@ -1414,7 +1414,7 @@ static int _dm_request(struct request_queue *q, struct bio *bio) * we have to queue this io for later. */ if (unlikely(test_bit(DMF_QUEUE_IO_TO_THREAD, &md->flags)) || - unlikely(bio_rw_flagged(bio, BIO_RW_BARRIER))) { + unlikely(bio->bi_rw & REQ_HARDBARRIER)) { up_read(&md->io_lock); if (unlikely(test_bit(DMF_BLOCK_IO_FOR_SUSPEND, &md->flags)) && @@ -2296,7 +2296,7 @@ static void dm_wq_work(struct work_struct *work) if (dm_request_based(md)) generic_make_request(c); else { - if (bio_rw_flagged(c, BIO_RW_BARRIER)) + if (c->bi_rw & REQ_HARDBARRIER) process_barrier(md, c); else __split_and_process_bio(md, c); diff --git a/drivers/md/linear.c b/drivers/md/linear.c index 7e0e057db9a7..ba19060bcf3f 100644 --- a/drivers/md/linear.c +++ b/drivers/md/linear.c @@ -294,7 +294,7 @@ static int linear_make_request (mddev_t *mddev, struct bio *bio) dev_info_t *tmp_dev; sector_t start_sector; - if (unlikely(bio_rw_flagged(bio, BIO_RW_BARRIER))) { + if (unlikely(bio->bi_rw & REQ_HARDBARRIER)) { md_barrier_request(mddev, bio); return 0; } diff --git a/drivers/md/md.c b/drivers/md/md.c index cb20d0b0555a..1893af678779 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -353,7 +353,7 @@ static void md_submit_barrier(struct work_struct *ws) /* an empty barrier - all done */ bio_endio(bio, 0); else { - bio->bi_rw &= ~(1<bi_rw &= ~REQ_HARDBARRIER; if (mddev->pers->make_request(mddev, bio)) generic_make_request(bio); mddev->barrier = POST_REQUEST_BARRIER; @@ -675,11 +675,11 @@ void md_super_write(mddev_t *mddev, mdk_rdev_t *rdev, * if zero is reached. * If an error occurred, call md_error * - * As we might need to resubmit the request if BIO_RW_BARRIER + * As we might need to resubmit the request if REQ_HARDBARRIER * causes ENOTSUPP, we allocate a spare bio... */ struct bio *bio = bio_alloc(GFP_NOIO, 1); - int rw = (1<bi_bdev = rdev->bdev; bio->bi_sector = sector; @@ -691,7 +691,7 @@ void md_super_write(mddev_t *mddev, mdk_rdev_t *rdev, atomic_inc(&mddev->pending_writes); if (!test_bit(BarriersNotsupp, &rdev->flags)) { struct bio *rbio; - rw |= (1<bi_private = bio; rbio->bi_end_io = super_written_barrier; @@ -736,7 +736,7 @@ int sync_page_io(struct block_device *bdev, sector_t sector, int size, struct completion event; int ret; - rw |= (1 << BIO_RW_SYNCIO) | (1 << BIO_RW_UNPLUG); + rw |= REQ_SYNC | REQ_UNPLUG; bio->bi_bdev = bdev; bio->bi_sector = sector; diff --git a/drivers/md/md.h b/drivers/md/md.h index 10597bfec000..fc56e0f21c80 100644 --- a/drivers/md/md.h +++ b/drivers/md/md.h @@ -67,7 +67,7 @@ struct mdk_rdev_s #define Faulty 1 /* device is known to have a fault */ #define In_sync 2 /* device is in_sync with rest of array */ #define WriteMostly 4 /* Avoid reading if at all possible */ -#define BarriersNotsupp 5 /* BIO_RW_BARRIER is not supported */ +#define BarriersNotsupp 5 /* REQ_HARDBARRIER is not supported */ #define AllReserved 6 /* If whole device is reserved for * one array */ #define AutoDetected 7 /* added by auto-detect */ @@ -254,7 +254,7 @@ struct mddev_s * fails. Only supported */ struct bio *biolist; /* bios that need to be retried - * because BIO_RW_BARRIER is not supported + * because REQ_HARDBARRIER is not supported */ atomic_t recovery_active; /* blocks scheduled, but not written */ diff --git a/drivers/md/multipath.c b/drivers/md/multipath.c index 410fb60699ac..0307d217e7a4 100644 --- a/drivers/md/multipath.c +++ b/drivers/md/multipath.c @@ -91,7 +91,7 @@ static void multipath_end_request(struct bio *bio, int error) if (uptodate) multipath_end_bh_io(mp_bh, 0); - else if (!bio_rw_flagged(bio, BIO_RW_AHEAD)) { + else if (!(bio->bi_rw & REQ_RAHEAD)) { /* * oops, IO error: */ @@ -142,7 +142,7 @@ static int multipath_make_request(mddev_t *mddev, struct bio * bio) struct multipath_bh * mp_bh; struct multipath_info *multipath; - if (unlikely(bio_rw_flagged(bio, BIO_RW_BARRIER))) { + if (unlikely(bio->bi_rw & REQ_HARDBARRIER)) { md_barrier_request(mddev, bio); return 0; } @@ -163,7 +163,7 @@ static int multipath_make_request(mddev_t *mddev, struct bio * bio) mp_bh->bio = *bio; mp_bh->bio.bi_sector += multipath->rdev->data_offset; mp_bh->bio.bi_bdev = multipath->rdev->bdev; - mp_bh->bio.bi_rw |= (1 << BIO_RW_FAILFAST_TRANSPORT); + mp_bh->bio.bi_rw |= REQ_FAILFAST_TRANSPORT; mp_bh->bio.bi_end_io = multipath_end_request; mp_bh->bio.bi_private = mp_bh; generic_make_request(&mp_bh->bio); @@ -398,7 +398,7 @@ static void multipathd (mddev_t *mddev) *bio = *(mp_bh->master_bio); bio->bi_sector += conf->multipaths[mp_bh->path].rdev->data_offset; bio->bi_bdev = conf->multipaths[mp_bh->path].rdev->bdev; - bio->bi_rw |= (1 << BIO_RW_FAILFAST_TRANSPORT); + bio->bi_rw |= REQ_FAILFAST_TRANSPORT; bio->bi_end_io = multipath_end_request; bio->bi_private = mp_bh; generic_make_request(bio); diff --git a/drivers/md/raid0.c b/drivers/md/raid0.c index 563abed5a2cb..6f7af46d623c 100644 --- a/drivers/md/raid0.c +++ b/drivers/md/raid0.c @@ -483,7 +483,7 @@ static int raid0_make_request(mddev_t *mddev, struct bio *bio) struct strip_zone *zone; mdk_rdev_t *tmp_dev; - if (unlikely(bio_rw_flagged(bio, BIO_RW_BARRIER))) { + if (unlikely(bio->bi_rw & REQ_HARDBARRIER)) { md_barrier_request(mddev, bio); return 0; } diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index a948da8012de..73cc74ffc26b 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -787,7 +787,7 @@ static int make_request(mddev_t *mddev, struct bio * bio) struct bio_list bl; struct page **behind_pages = NULL; const int rw = bio_data_dir(bio); - const bool do_sync = bio_rw_flagged(bio, BIO_RW_SYNCIO); + const bool do_sync = (bio->bi_rw & REQ_SYNC); bool do_barriers; mdk_rdev_t *blocked_rdev; @@ -822,7 +822,7 @@ static int make_request(mddev_t *mddev, struct bio * bio) finish_wait(&conf->wait_barrier, &w); } if (unlikely(!mddev->barriers_work && - bio_rw_flagged(bio, BIO_RW_BARRIER))) { + (bio->bi_rw & REQ_HARDBARRIER))) { if (rw == WRITE) md_write_end(mddev); bio_endio(bio, -EOPNOTSUPP); @@ -877,7 +877,7 @@ static int make_request(mddev_t *mddev, struct bio * bio) read_bio->bi_sector = r1_bio->sector + mirror->rdev->data_offset; read_bio->bi_bdev = mirror->rdev->bdev; read_bio->bi_end_io = raid1_end_read_request; - read_bio->bi_rw = READ | (do_sync << BIO_RW_SYNCIO); + read_bio->bi_rw = READ | do_sync; read_bio->bi_private = r1_bio; generic_make_request(read_bio); @@ -959,7 +959,7 @@ static int make_request(mddev_t *mddev, struct bio * bio) atomic_set(&r1_bio->remaining, 0); atomic_set(&r1_bio->behind_remaining, 0); - do_barriers = bio_rw_flagged(bio, BIO_RW_BARRIER); + do_barriers = bio->bi_rw & REQ_HARDBARRIER; if (do_barriers) set_bit(R1BIO_Barrier, &r1_bio->state); @@ -975,8 +975,7 @@ static int make_request(mddev_t *mddev, struct bio * bio) mbio->bi_sector = r1_bio->sector + conf->mirrors[i].rdev->data_offset; mbio->bi_bdev = conf->mirrors[i].rdev->bdev; mbio->bi_end_io = raid1_end_write_request; - mbio->bi_rw = WRITE | (do_barriers << BIO_RW_BARRIER) | - (do_sync << BIO_RW_SYNCIO); + mbio->bi_rw = WRITE | do_barriers | do_sync; mbio->bi_private = r1_bio; if (behind_pages) { @@ -1633,7 +1632,7 @@ static void raid1d(mddev_t *mddev) sync_request_write(mddev, r1_bio); unplug = 1; } else if (test_bit(R1BIO_BarrierRetry, &r1_bio->state)) { - /* some requests in the r1bio were BIO_RW_BARRIER + /* some requests in the r1bio were REQ_HARDBARRIER * requests which failed with -EOPNOTSUPP. Hohumm.. * Better resubmit without the barrier. * We know which devices to resubmit for, because @@ -1641,7 +1640,7 @@ static void raid1d(mddev_t *mddev) * We already have a nr_pending reference on these rdevs. */ int i; - const bool do_sync = bio_rw_flagged(r1_bio->master_bio, BIO_RW_SYNCIO); + const bool do_sync = (r1_bio->master_bio->bi_rw & REQ_SYNC); clear_bit(R1BIO_BarrierRetry, &r1_bio->state); clear_bit(R1BIO_Barrier, &r1_bio->state); for (i=0; i < conf->raid_disks; i++) @@ -1662,8 +1661,7 @@ static void raid1d(mddev_t *mddev) conf->mirrors[i].rdev->data_offset; bio->bi_bdev = conf->mirrors[i].rdev->bdev; bio->bi_end_io = raid1_end_write_request; - bio->bi_rw = WRITE | - (do_sync << BIO_RW_SYNCIO); + bio->bi_rw = WRITE | do_sync; bio->bi_private = r1_bio; r1_bio->bios[i] = bio; generic_make_request(bio); @@ -1698,7 +1696,7 @@ static void raid1d(mddev_t *mddev) (unsigned long long)r1_bio->sector); raid_end_bio_io(r1_bio); } else { - const bool do_sync = bio_rw_flagged(r1_bio->master_bio, BIO_RW_SYNCIO); + const bool do_sync = r1_bio->master_bio->bi_rw & REQ_SYNC; r1_bio->bios[r1_bio->read_disk] = mddev->ro ? IO_BLOCKED : NULL; r1_bio->read_disk = disk; @@ -1715,7 +1713,7 @@ static void raid1d(mddev_t *mddev) bio->bi_sector = r1_bio->sector + rdev->data_offset; bio->bi_bdev = rdev->bdev; bio->bi_end_io = raid1_end_read_request; - bio->bi_rw = READ | (do_sync << BIO_RW_SYNCIO); + bio->bi_rw = READ | do_sync; bio->bi_private = r1_bio; unplug = 1; generic_make_request(bio); diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index 42e64e4e5e25..62ecb6650fd0 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -799,12 +799,12 @@ static int make_request(mddev_t *mddev, struct bio * bio) int i; int chunk_sects = conf->chunk_mask + 1; const int rw = bio_data_dir(bio); - const bool do_sync = bio_rw_flagged(bio, BIO_RW_SYNCIO); + const bool do_sync = (bio->bi_rw & REQ_SYNC); struct bio_list bl; unsigned long flags; mdk_rdev_t *blocked_rdev; - if (unlikely(bio_rw_flagged(bio, BIO_RW_BARRIER))) { + if (unlikely(bio->bi_rw & REQ_HARDBARRIER)) { md_barrier_request(mddev, bio); return 0; } @@ -879,7 +879,7 @@ static int make_request(mddev_t *mddev, struct bio * bio) mirror->rdev->data_offset; read_bio->bi_bdev = mirror->rdev->bdev; read_bio->bi_end_io = raid10_end_read_request; - read_bio->bi_rw = READ | (do_sync << BIO_RW_SYNCIO); + read_bio->bi_rw = READ | do_sync; read_bio->bi_private = r10_bio; generic_make_request(read_bio); @@ -947,7 +947,7 @@ static int make_request(mddev_t *mddev, struct bio * bio) conf->mirrors[d].rdev->data_offset; mbio->bi_bdev = conf->mirrors[d].rdev->bdev; mbio->bi_end_io = raid10_end_write_request; - mbio->bi_rw = WRITE | (do_sync << BIO_RW_SYNCIO); + mbio->bi_rw = WRITE | do_sync; mbio->bi_private = r10_bio; atomic_inc(&r10_bio->remaining); @@ -1716,7 +1716,7 @@ static void raid10d(mddev_t *mddev) raid_end_bio_io(r10_bio); bio_put(bio); } else { - const bool do_sync = bio_rw_flagged(r10_bio->master_bio, BIO_RW_SYNCIO); + const bool do_sync = (r10_bio->master_bio->bi_rw & REQ_SYNC); bio_put(bio); rdev = conf->mirrors[mirror].rdev; if (printk_ratelimit()) @@ -1730,7 +1730,7 @@ static void raid10d(mddev_t *mddev) bio->bi_sector = r10_bio->devs[r10_bio->read_slot].addr + rdev->data_offset; bio->bi_bdev = rdev->bdev; - bio->bi_rw = READ | (do_sync << BIO_RW_SYNCIO); + bio->bi_rw = READ | do_sync; bio->bi_private = r10_bio; bio->bi_end_io = raid10_end_read_request; unplug = 1; diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 96c690279fc6..20ac2f14376a 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -3958,7 +3958,7 @@ static int make_request(mddev_t *mddev, struct bio * bi) const int rw = bio_data_dir(bi); int remaining; - if (unlikely(bio_rw_flagged(bi, BIO_RW_BARRIER))) { + if (unlikely(bi->bi_rw & REQ_HARDBARRIER)) { /* Drain all pending writes. We only really need * to ensure they have been submitted, but this is * easier. diff --git a/drivers/scsi/osd/osd_initiator.c b/drivers/scsi/osd/osd_initiator.c index ee4b6914667f..fda4de3440c4 100644 --- a/drivers/scsi/osd/osd_initiator.c +++ b/drivers/scsi/osd/osd_initiator.c @@ -716,7 +716,7 @@ static int _osd_req_list_objects(struct osd_request *or, return PTR_ERR(bio); } - bio->bi_rw &= ~(1 << BIO_RW); + bio->bi_rw &= ~REQ_WRITE; or->in.bio = bio; or->in.total_bytes = bio->bi_size; return 0; @@ -814,7 +814,7 @@ void osd_req_write(struct osd_request *or, { _osd_req_encode_common(or, OSD_ACT_WRITE, obj, offset, len); WARN_ON(or->out.bio || or->out.total_bytes); - WARN_ON(0 == bio_rw_flagged(bio, BIO_RW)); + WARN_ON(0 == (bio->bi_rw & REQ_WRITE)); or->out.bio = bio; or->out.total_bytes = len; } @@ -829,7 +829,7 @@ int osd_req_write_kern(struct osd_request *or, if (IS_ERR(bio)) return PTR_ERR(bio); - bio->bi_rw |= (1 << BIO_RW); /* FIXME: bio_set_dir() */ + bio->bi_rw |= REQ_WRITE; /* FIXME: bio_set_dir() */ osd_req_write(or, obj, offset, bio, len); return 0; } @@ -865,7 +865,7 @@ void osd_req_read(struct osd_request *or, { _osd_req_encode_common(or, OSD_ACT_READ, obj, offset, len); WARN_ON(or->in.bio || or->in.total_bytes); - WARN_ON(1 == bio_rw_flagged(bio, BIO_RW)); + WARN_ON(1 == (bio->bi_rw & REQ_WRITE)); or->in.bio = bio; or->in.total_bytes = len; } diff --git a/fs/bio.c b/fs/bio.c index e7bf6ca64dcf..8abb2dfb2e7c 100644 --- a/fs/bio.c +++ b/fs/bio.c @@ -843,7 +843,8 @@ struct bio *bio_copy_user_iov(struct request_queue *q, if (!bio) goto out_bmd; - bio->bi_rw |= (!write_to_vm << BIO_RW); + if (!write_to_vm) + bio->bi_rw |= REQ_WRITE; ret = 0; @@ -1024,7 +1025,7 @@ static struct bio *__bio_map_user_iov(struct request_queue *q, * set data direction, and check if mapped pages need bouncing */ if (!write_to_vm) - bio->bi_rw |= (1 << BIO_RW); + bio->bi_rw |= REQ_WRITE; bio->bi_bdev = bdev; bio->bi_flags |= (1 << BIO_USER_MAPPED); diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 34f7c375567e..64f10082f048 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -480,7 +480,7 @@ static void end_workqueue_bio(struct bio *bio, int err) end_io_wq->work.func = end_workqueue_fn; end_io_wq->work.flags = 0; - if (bio->bi_rw & (1 << BIO_RW)) { + if (bio->bi_rw & REQ_WRITE) { if (end_io_wq->metadata) btrfs_queue_worker(&fs_info->endio_meta_write_workers, &end_io_wq->work); @@ -604,7 +604,7 @@ int btrfs_wq_submit_bio(struct btrfs_fs_info *fs_info, struct inode *inode, atomic_inc(&fs_info->nr_async_submits); - if (rw & (1 << BIO_RW_SYNCIO)) + if (rw & REQ_SYNC) btrfs_set_work_high_prio(&async->work); btrfs_queue_worker(&fs_info->workers, &async->work); @@ -668,7 +668,7 @@ static int btree_submit_bio_hook(struct inode *inode, int rw, struct bio *bio, bio, 1); BUG_ON(ret); - if (!(rw & (1 << BIO_RW))) { + if (!(rw & REQ_WRITE)) { /* * called for a read, do the setup so that checksum validation * can happen in the async kernel threads @@ -1427,7 +1427,7 @@ static void end_workqueue_fn(struct btrfs_work *work) * ram and up to date before trying to verify things. For * blocksize <= pagesize, it is basically a noop */ - if (!(bio->bi_rw & (1 << BIO_RW)) && end_io_wq->metadata && + if (!(bio->bi_rw & REQ_WRITE) && end_io_wq->metadata && !bio_ready_for_csum(bio)) { btrfs_queue_worker(&fs_info->endio_meta_workers, &end_io_wq->work); diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 1bff92ad4744..e975d7180a88 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -1429,7 +1429,7 @@ static int btrfs_submit_bio_hook(struct inode *inode, int rw, struct bio *bio, ret = btrfs_bio_wq_end_io(root->fs_info, bio, 0); BUG_ON(ret); - if (!(rw & (1 << BIO_RW))) { + if (!(rw & REQ_WRITE)) { if (bio_flags & EXTENT_BIO_COMPRESSED) { return btrfs_submit_compressed_read(inode, bio, mirror_num, bio_flags); @@ -1841,7 +1841,7 @@ static int btrfs_io_failed_hook(struct bio *failed_bio, bio->bi_size = 0; bio_add_page(bio, page, failrec->len, start - page_offset(page)); - if (failed_bio->bi_rw & (1 << BIO_RW)) + if (failed_bio->bi_rw & REQ_WRITE) rw = WRITE; else rw = READ; @@ -5642,7 +5642,7 @@ static void btrfs_submit_direct(int rw, struct bio *bio, struct inode *inode, struct bio_vec *bvec = bio->bi_io_vec; u64 start; int skip_sum; - int write = rw & (1 << BIO_RW); + int write = rw & REQ_WRITE; int ret = 0; skip_sum = BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM; diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index d6e3af8be95b..dd318ff280b2 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -258,7 +258,7 @@ loop_lock: BUG_ON(atomic_read(&cur->bi_cnt) == 0); - if (bio_rw_flagged(cur, BIO_RW_SYNCIO)) + if (cur->bi_rw & REQ_SYNC) num_sync_run++; submit_bio(cur->bi_rw, cur); @@ -2651,7 +2651,7 @@ static int __btrfs_map_block(struct btrfs_mapping_tree *map_tree, int rw, int max_errors = 0; struct btrfs_multi_bio *multi = NULL; - if (multi_ret && !(rw & (1 << BIO_RW))) + if (multi_ret && !(rw & REQ_WRITE)) stripes_allocated = 1; again: if (multi_ret) { @@ -2687,7 +2687,7 @@ again: mirror_num = 0; /* if our multi bio struct is too small, back off and try again */ - if (rw & (1 << BIO_RW)) { + if (rw & REQ_WRITE) { if (map->type & (BTRFS_BLOCK_GROUP_RAID1 | BTRFS_BLOCK_GROUP_DUP)) { stripes_required = map->num_stripes; @@ -2697,7 +2697,7 @@ again: max_errors = 1; } } - if (multi_ret && (rw & (1 << BIO_RW)) && + if (multi_ret && (rw & REQ_WRITE) && stripes_allocated < stripes_required) { stripes_allocated = map->num_stripes; free_extent_map(em); @@ -2733,7 +2733,7 @@ again: num_stripes = 1; stripe_index = 0; if (map->type & BTRFS_BLOCK_GROUP_RAID1) { - if (unplug_page || (rw & (1 << BIO_RW))) + if (unplug_page || (rw & REQ_WRITE)) num_stripes = map->num_stripes; else if (mirror_num) stripe_index = mirror_num - 1; @@ -2744,7 +2744,7 @@ again: } } else if (map->type & BTRFS_BLOCK_GROUP_DUP) { - if (rw & (1 << BIO_RW)) + if (rw & REQ_WRITE) num_stripes = map->num_stripes; else if (mirror_num) stripe_index = mirror_num - 1; @@ -2755,7 +2755,7 @@ again: stripe_index = do_div(stripe_nr, factor); stripe_index *= map->sub_stripes; - if (unplug_page || (rw & (1 << BIO_RW))) + if (unplug_page || (rw & REQ_WRITE)) num_stripes = map->sub_stripes; else if (mirror_num) stripe_index += mirror_num - 1; @@ -2945,7 +2945,7 @@ static noinline int schedule_bio(struct btrfs_root *root, struct btrfs_pending_bios *pending_bios; /* don't bother with additional async steps for reads, right now */ - if (!(rw & (1 << BIO_RW))) { + if (!(rw & REQ_WRITE)) { bio_get(bio); submit_bio(rw, bio); bio_put(bio); @@ -2964,7 +2964,7 @@ static noinline int schedule_bio(struct btrfs_root *root, bio->bi_rw |= rw; spin_lock(&device->io_lock); - if (bio_rw_flagged(bio, BIO_RW_SYNCIO)) + if (bio->bi_rw & REQ_SYNC) pending_bios = &device->pending_sync_bios; else pending_bios = &device->pending_bios; diff --git a/fs/exofs/ios.c b/fs/exofs/ios.c index 4337cad7777b..e2732203fa93 100644 --- a/fs/exofs/ios.c +++ b/fs/exofs/ios.c @@ -599,7 +599,7 @@ static int _sbi_write_mirror(struct exofs_io_state *ios, int cur_comp) } else { bio = master_dev->bio; /* FIXME: bio_set_dir() */ - bio->bi_rw |= (1 << BIO_RW); + bio->bi_rw |= REQ_WRITE; } osd_req_write(or, &ios->obj, per_dev->offset, bio, diff --git a/fs/gfs2/log.c b/fs/gfs2/log.c index efc3539ac5a1..cde1248a6225 100644 --- a/fs/gfs2/log.c +++ b/fs/gfs2/log.c @@ -595,7 +595,7 @@ static void log_write_header(struct gfs2_sbd *sdp, u32 flags, int pull) if (test_bit(SDF_NOBARRIERS, &sdp->sd_flags)) goto skip_barrier; get_bh(bh); - submit_bh(WRITE_BARRIER | (1 << BIO_RW_META), bh); + submit_bh(WRITE_BARRIER | REQ_META, bh); wait_on_buffer(bh); if (buffer_eopnotsupp(bh)) { clear_buffer_eopnotsupp(bh); @@ -605,7 +605,7 @@ static void log_write_header(struct gfs2_sbd *sdp, u32 flags, int pull) lock_buffer(bh); skip_barrier: get_bh(bh); - submit_bh(WRITE_SYNC | (1 << BIO_RW_META), bh); + submit_bh(WRITE_SYNC | REQ_META, bh); wait_on_buffer(bh); } if (!buffer_uptodate(bh)) diff --git a/fs/gfs2/meta_io.c b/fs/gfs2/meta_io.c index 18176d0b75d7..f3b071f921aa 100644 --- a/fs/gfs2/meta_io.c +++ b/fs/gfs2/meta_io.c @@ -36,8 +36,8 @@ static int gfs2_aspace_writepage(struct page *page, struct writeback_control *wb { struct buffer_head *bh, *head; int nr_underway = 0; - int write_op = (1 << BIO_RW_META) | ((wbc->sync_mode == WB_SYNC_ALL ? - WRITE_SYNC_PLUG : WRITE)); + int write_op = REQ_META | + (wbc->sync_mode == WB_SYNC_ALL ? WRITE_SYNC_PLUG : WRITE); BUG_ON(!PageLocked(page)); BUG_ON(!page_has_buffers(page)); @@ -225,7 +225,7 @@ int gfs2_meta_read(struct gfs2_glock *gl, u64 blkno, int flags, } bh->b_end_io = end_buffer_read_sync; get_bh(bh); - submit_bh(READ_SYNC | (1 << BIO_RW_META), bh); + submit_bh(READ_SYNC | REQ_META, bh); if (!(flags & DIO_WAIT)) return 0; @@ -432,7 +432,7 @@ struct buffer_head *gfs2_meta_ra(struct gfs2_glock *gl, u64 dblock, u32 extlen) if (buffer_uptodate(first_bh)) goto out; if (!buffer_locked(first_bh)) - ll_rw_block(READ_SYNC | (1 << BIO_RW_META), 1, &first_bh); + ll_rw_block(READ_SYNC | REQ_META, 1, &first_bh); dblock++; extlen--; diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c index 3593b3a7290e..fd4f8946abf5 100644 --- a/fs/gfs2/ops_fstype.c +++ b/fs/gfs2/ops_fstype.c @@ -275,7 +275,7 @@ static int gfs2_read_super(struct gfs2_sbd *sdp, sector_t sector) bio->bi_end_io = end_bio_io_page; bio->bi_private = page; - submit_bio(READ_SYNC | (1 << BIO_RW_META), bio); + submit_bio(READ_SYNC | REQ_META, bio); wait_on_page_locked(page); bio_put(bio); if (!PageUptodate(page)) { diff --git a/fs/nilfs2/segbuf.c b/fs/nilfs2/segbuf.c index 2e6a2723b8fa..4588fb9e93df 100644 --- a/fs/nilfs2/segbuf.c +++ b/fs/nilfs2/segbuf.c @@ -508,7 +508,7 @@ static int nilfs_segbuf_write(struct nilfs_segment_buffer *segbuf, * Last BIO is always sent through the following * submission. */ - rw |= (1 << BIO_RW_SYNCIO) | (1 << BIO_RW_UNPLUG); + rw |= REQ_SYNC | REQ_UNPLUG; res = nilfs_segbuf_submit_bio(segbuf, &wi, rw); } diff --git a/include/linux/bio.h b/include/linux/bio.h index 7fc5606e6ea5..4d379c8250ae 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -138,55 +138,83 @@ struct bio { #define BIO_POOL_IDX(bio) ((bio)->bi_flags >> BIO_POOL_OFFSET) /* - * bio bi_rw flags - * - * bit 0 -- data direction - * If not set, bio is a read from device. If set, it's a write to device. - * bit 1 -- fail fast device errors - * bit 2 -- fail fast transport errors - * bit 3 -- fail fast driver errors - * bit 4 -- rw-ahead when set - * bit 5 -- barrier - * Insert a serialization point in the IO queue, forcing previously - * submitted IO to be completed before this one is issued. - * bit 6 -- synchronous I/O hint. - * bit 7 -- Unplug the device immediately after submitting this bio. - * bit 8 -- metadata request - * Used for tracing to differentiate metadata and data IO. May also - * get some preferential treatment in the IO scheduler - * bit 9 -- discard sectors - * Informs the lower level device that this range of sectors is no longer - * used by the file system and may thus be freed by the device. Used - * for flash based storage. - * Don't want driver retries for any fast fail whatever the reason. - * bit 10 -- Tell the IO scheduler not to wait for more requests after this - one has been submitted, even if it is a SYNC request. + * Request flags. For use in the cmd_flags field of struct request, and in + * bi_rw of struct bio. Note that some flags are only valid in either one. */ -enum bio_rw_flags { - BIO_RW, - BIO_RW_FAILFAST_DEV, - BIO_RW_FAILFAST_TRANSPORT, - BIO_RW_FAILFAST_DRIVER, - /* above flags must match REQ_* */ - BIO_RW_AHEAD, - BIO_RW_BARRIER, - BIO_RW_SYNCIO, - BIO_RW_UNPLUG, - BIO_RW_META, - BIO_RW_DISCARD, - BIO_RW_NOIDLE, +enum rq_flag_bits { + /* common flags */ + __REQ_WRITE, /* not set, read. set, write */ + __REQ_FAILFAST_DEV, /* no driver retries of device errors */ + __REQ_FAILFAST_TRANSPORT, /* no driver retries of transport errors */ + __REQ_FAILFAST_DRIVER, /* no driver retries of driver errors */ + + __REQ_HARDBARRIER, /* may not be passed by drive either */ + __REQ_SYNC, /* request is sync (sync write or read) */ + __REQ_META, /* metadata io request */ + __REQ_DISCARD, /* request to discard sectors */ + __REQ_NOIDLE, /* don't anticipate more IO after this one */ + + /* bio only flags */ + __REQ_UNPLUG, /* unplug the immediately after submission */ + __REQ_RAHEAD, /* read ahead, can fail anytime */ + + /* request only flags */ + __REQ_SORTED, /* elevator knows about this request */ + __REQ_SOFTBARRIER, /* may not be passed by ioscheduler */ + __REQ_FUA, /* forced unit access */ + __REQ_NOMERGE, /* don't touch this for merging */ + __REQ_STARTED, /* drive already may have started this one */ + __REQ_DONTPREP, /* don't call prep for this one */ + __REQ_QUEUED, /* uses queueing */ + __REQ_ELVPRIV, /* elevator private data attached */ + __REQ_FAILED, /* set if the request failed */ + __REQ_QUIET, /* don't worry about errors */ + __REQ_PREEMPT, /* set for "ide_preempt" requests */ + __REQ_ORDERED_COLOR, /* is before or after barrier */ + __REQ_ALLOCED, /* request came from our alloc pool */ + __REQ_COPY_USER, /* contains copies of user pages */ + __REQ_INTEGRITY, /* integrity metadata has been remapped */ + __REQ_IO_STAT, /* account I/O stat */ + __REQ_MIXED_MERGE, /* merge of different types, fail separately */ + __REQ_NR_BITS, /* stops here */ }; -/* - * First four bits must match between bio->bi_rw and rq->cmd_flags, make - * that explicit here. - */ -#define BIO_RW_RQ_MASK 0xf - -static inline bool bio_rw_flagged(struct bio *bio, enum bio_rw_flags flag) -{ - return (bio->bi_rw & (1 << flag)) != 0; -} +#define REQ_WRITE (1 << __REQ_WRITE) +#define REQ_FAILFAST_DEV (1 << __REQ_FAILFAST_DEV) +#define REQ_FAILFAST_TRANSPORT (1 << __REQ_FAILFAST_TRANSPORT) +#define REQ_FAILFAST_DRIVER (1 << __REQ_FAILFAST_DRIVER) +#define REQ_HARDBARRIER (1 << __REQ_HARDBARRIER) +#define REQ_SYNC (1 << __REQ_SYNC) +#define REQ_META (1 << __REQ_META) +#define REQ_DISCARD (1 << __REQ_DISCARD) +#define REQ_NOIDLE (1 << __REQ_NOIDLE) + +#define REQ_FAILFAST_MASK \ + (REQ_FAILFAST_DEV | REQ_FAILFAST_TRANSPORT | REQ_FAILFAST_DRIVER) +#define REQ_COMMON_MASK \ + (REQ_WRITE | REQ_FAILFAST_MASK | REQ_HARDBARRIER | REQ_SYNC | \ + REQ_META| REQ_DISCARD | REQ_NOIDLE) + +#define REQ_UNPLUG (1 << __REQ_UNPLUG) +#define REQ_RAHEAD (1 << __REQ_RAHEAD) + +#define REQ_SORTED (1 << __REQ_SORTED) +#define REQ_SOFTBARRIER (1 << __REQ_SOFTBARRIER) +#define REQ_FUA (1 << __REQ_FUA) +#define REQ_NOMERGE (1 << __REQ_NOMERGE) +#define REQ_STARTED (1 << __REQ_STARTED) +#define REQ_DONTPREP (1 << __REQ_DONTPREP) +#define REQ_QUEUED (1 << __REQ_QUEUED) +#define REQ_ELVPRIV (1 << __REQ_ELVPRIV) +#define REQ_FAILED (1 << __REQ_FAILED) +#define REQ_QUIET (1 << __REQ_QUIET) +#define REQ_PREEMPT (1 << __REQ_PREEMPT) +#define REQ_ORDERED_COLOR (1 << __REQ_ORDERED_COLOR) +#define REQ_ALLOCED (1 << __REQ_ALLOCED) +#define REQ_COPY_USER (1 << __REQ_COPY_USER) +#define REQ_INTEGRITY (1 << __REQ_INTEGRITY) +#define REQ_IO_STAT (1 << __REQ_IO_STAT) +#define REQ_MIXED_MERGE (1 << __REQ_MIXED_MERGE) /* * upper 16 bits of bi_rw define the io priority of this bio @@ -211,7 +239,10 @@ static inline bool bio_rw_flagged(struct bio *bio, enum bio_rw_flags flag) #define bio_offset(bio) bio_iovec((bio))->bv_offset #define bio_segments(bio) ((bio)->bi_vcnt - (bio)->bi_idx) #define bio_sectors(bio) ((bio)->bi_size >> 9) -#define bio_empty_barrier(bio) (bio_rw_flagged(bio, BIO_RW_BARRIER) && !bio_has_data(bio) && !bio_rw_flagged(bio, BIO_RW_DISCARD)) +#define bio_empty_barrier(bio) \ + ((bio->bi_rw & REQ_HARDBARRIER) && \ + !bio_has_data(bio) && \ + !(bio->bi_rw & REQ_DISCARD)) static inline unsigned int bio_cur_bytes(struct bio *bio) { diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 3ecd28ef9ba4..3fc0f5908619 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -84,70 +84,6 @@ enum { REQ_LB_OP_FLUSH = 0x41, /* flush request */ }; -/* - * request type modified bits. first four bits match BIO_RW* bits, important - */ -enum rq_flag_bits { - __REQ_RW, /* not set, read. set, write */ - __REQ_FAILFAST_DEV, /* no driver retries of device errors */ - __REQ_FAILFAST_TRANSPORT, /* no driver retries of transport errors */ - __REQ_FAILFAST_DRIVER, /* no driver retries of driver errors */ - /* above flags must match BIO_RW_* */ - __REQ_DISCARD, /* request to discard sectors */ - __REQ_SORTED, /* elevator knows about this request */ - __REQ_SOFTBARRIER, /* may not be passed by ioscheduler */ - __REQ_HARDBARRIER, /* may not be passed by drive either */ - __REQ_FUA, /* forced unit access */ - __REQ_NOMERGE, /* don't touch this for merging */ - __REQ_STARTED, /* drive already may have started this one */ - __REQ_DONTPREP, /* don't call prep for this one */ - __REQ_QUEUED, /* uses queueing */ - __REQ_ELVPRIV, /* elevator private data attached */ - __REQ_FAILED, /* set if the request failed */ - __REQ_QUIET, /* don't worry about errors */ - __REQ_PREEMPT, /* set for "ide_preempt" requests */ - __REQ_ORDERED_COLOR, /* is before or after barrier */ - __REQ_RW_SYNC, /* request is sync (sync write or read) */ - __REQ_ALLOCED, /* request came from our alloc pool */ - __REQ_RW_META, /* metadata io request */ - __REQ_COPY_USER, /* contains copies of user pages */ - __REQ_INTEGRITY, /* integrity metadata has been remapped */ - __REQ_NOIDLE, /* Don't anticipate more IO after this one */ - __REQ_IO_STAT, /* account I/O stat */ - __REQ_MIXED_MERGE, /* merge of different types, fail separately */ - __REQ_NR_BITS, /* stops here */ -}; - -#define REQ_RW (1 << __REQ_RW) -#define REQ_FAILFAST_DEV (1 << __REQ_FAILFAST_DEV) -#define REQ_FAILFAST_TRANSPORT (1 << __REQ_FAILFAST_TRANSPORT) -#define REQ_FAILFAST_DRIVER (1 << __REQ_FAILFAST_DRIVER) -#define REQ_DISCARD (1 << __REQ_DISCARD) -#define REQ_SORTED (1 << __REQ_SORTED) -#define REQ_SOFTBARRIER (1 << __REQ_SOFTBARRIER) -#define REQ_HARDBARRIER (1 << __REQ_HARDBARRIER) -#define REQ_FUA (1 << __REQ_FUA) -#define REQ_NOMERGE (1 << __REQ_NOMERGE) -#define REQ_STARTED (1 << __REQ_STARTED) -#define REQ_DONTPREP (1 << __REQ_DONTPREP) -#define REQ_QUEUED (1 << __REQ_QUEUED) -#define REQ_ELVPRIV (1 << __REQ_ELVPRIV) -#define REQ_FAILED (1 << __REQ_FAILED) -#define REQ_QUIET (1 << __REQ_QUIET) -#define REQ_PREEMPT (1 << __REQ_PREEMPT) -#define REQ_ORDERED_COLOR (1 << __REQ_ORDERED_COLOR) -#define REQ_RW_SYNC (1 << __REQ_RW_SYNC) -#define REQ_ALLOCED (1 << __REQ_ALLOCED) -#define REQ_RW_META (1 << __REQ_RW_META) -#define REQ_COPY_USER (1 << __REQ_COPY_USER) -#define REQ_INTEGRITY (1 << __REQ_INTEGRITY) -#define REQ_NOIDLE (1 << __REQ_NOIDLE) -#define REQ_IO_STAT (1 << __REQ_IO_STAT) -#define REQ_MIXED_MERGE (1 << __REQ_MIXED_MERGE) - -#define REQ_FAILFAST_MASK (REQ_FAILFAST_DEV | REQ_FAILFAST_TRANSPORT | \ - REQ_FAILFAST_DRIVER) - #define BLK_MAX_CDB 16 /* @@ -631,7 +567,7 @@ enum { */ static inline bool rw_is_sync(unsigned int rw_flags) { - return !(rw_flags & REQ_RW) || (rw_flags & REQ_RW_SYNC); + return !(rw_flags & REQ_WRITE) || (rw_flags & REQ_SYNC); } static inline bool rq_is_sync(struct request *rq) diff --git a/include/linux/fs.h b/include/linux/fs.h index 598878831497..c5c92943c767 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -144,29 +144,31 @@ struct inodes_stat_t { * of this IO. * */ -#define RW_MASK 1 -#define RWA_MASK 2 -#define READ 0 -#define WRITE 1 -#define READA 2 /* read-ahead - don't block if no resources */ -#define SWRITE 3 /* for ll_rw_block() - wait for buffer lock */ -#define READ_SYNC (READ | (1 << BIO_RW_SYNCIO) | (1 << BIO_RW_UNPLUG)) -#define READ_META (READ | (1 << BIO_RW_META)) -#define WRITE_SYNC_PLUG (WRITE | (1 << BIO_RW_SYNCIO) | (1 << BIO_RW_NOIDLE)) -#define WRITE_SYNC (WRITE_SYNC_PLUG | (1 << BIO_RW_UNPLUG)) -#define WRITE_ODIRECT_PLUG (WRITE | (1 << BIO_RW_SYNCIO)) -#define WRITE_META (WRITE | (1 << BIO_RW_META)) -#define SWRITE_SYNC_PLUG \ - (SWRITE | (1 << BIO_RW_SYNCIO) | (1 << BIO_RW_NOIDLE)) -#define SWRITE_SYNC (SWRITE_SYNC_PLUG | (1 << BIO_RW_UNPLUG)) -#define WRITE_BARRIER (WRITE_SYNC | (1 << BIO_RW_BARRIER)) +#define RW_MASK 1 +#define RWA_MASK 2 + +#define READ 0 +#define WRITE 1 +#define READA 2 /* readahead - don't block if no resources */ +#define SWRITE 3 /* for ll_rw_block() - wait for buffer lock */ + +#define READ_SYNC (READ | REQ_SYNC | REQ_UNPLUG) +#define READ_META (READ | REQ_META) +#define WRITE_SYNC_PLUG (WRITE | REQ_SYNC | REQ_NOIDLE) +#define WRITE_SYNC (WRITE | REQ_SYNC | REQ_NOIDLE | REQ_UNPLUG) +#define WRITE_ODIRECT_PLUG (WRITE | REQ_SYNC) +#define WRITE_META (WRITE | REQ_META) +#define WRITE_BARRIER (WRITE | REQ_SYNC | REQ_NOIDLE | REQ_UNPLUG | \ + REQ_HARDBARRIER) +#define SWRITE_SYNC_PLUG (SWRITE | REQ_SYNC | REQ_NOIDLE) +#define SWRITE_SYNC (SWRITE | REQ_SYNC | REQ_NOIDLE | REQ_UNPLUG) /* * These aren't really reads or writes, they pass down information about * parts of device that are now unused by the file system. */ -#define DISCARD_NOBARRIER (WRITE | (1 << BIO_RW_DISCARD)) -#define DISCARD_BARRIER (DISCARD_NOBARRIER | (1 << BIO_RW_BARRIER)) +#define DISCARD_NOBARRIER (WRITE | REQ_DISCARD) +#define DISCARD_BARRIER (WRITE | REQ_DISCARD | REQ_HARDBARRIER) #define SEL_IN 1 #define SEL_OUT 2 diff --git a/kernel/power/block_io.c b/kernel/power/block_io.c index 97024fd40cd5..83bbc7c02df9 100644 --- a/kernel/power/block_io.c +++ b/kernel/power/block_io.c @@ -28,7 +28,7 @@ static int submit(int rw, struct block_device *bdev, sector_t sector, struct page *page, struct bio **bio_chain) { - const int bio_rw = rw | (1 << BIO_RW_SYNCIO) | (1 << BIO_RW_UNPLUG); + const int bio_rw = rw | REQ_SYNC | REQ_UNPLUG; struct bio *bio; bio = bio_alloc(__GFP_WAIT | __GFP_HIGH, 1); diff --git a/kernel/trace/blktrace.c b/kernel/trace/blktrace.c index 4f149944cb89..3b4a695051b6 100644 --- a/kernel/trace/blktrace.c +++ b/kernel/trace/blktrace.c @@ -169,9 +169,12 @@ static int act_log_check(struct blk_trace *bt, u32 what, sector_t sector, static const u32 ddir_act[2] = { BLK_TC_ACT(BLK_TC_READ), BLK_TC_ACT(BLK_TC_WRITE) }; +#define BLK_TC_HARDBARRIER BLK_TC_BARRIER +#define BLK_TC_RAHEAD BLK_TC_AHEAD + /* The ilog2() calls fall out because they're constant */ -#define MASK_TC_BIT(rw, __name) ((rw & (1 << BIO_RW_ ## __name)) << \ - (ilog2(BLK_TC_ ## __name) + BLK_TC_SHIFT - BIO_RW_ ## __name)) +#define MASK_TC_BIT(rw, __name) ((rw & REQ_ ## __name) << \ + (ilog2(BLK_TC_ ## __name) + BLK_TC_SHIFT - __REQ_ ## __name)) /* * The worker for the various blk_add_trace*() types. Fills out a @@ -194,9 +197,9 @@ static void __blk_add_trace(struct blk_trace *bt, sector_t sector, int bytes, return; what |= ddir_act[rw & WRITE]; - what |= MASK_TC_BIT(rw, BARRIER); - what |= MASK_TC_BIT(rw, SYNCIO); - what |= MASK_TC_BIT(rw, AHEAD); + what |= MASK_TC_BIT(rw, HARDBARRIER); + what |= MASK_TC_BIT(rw, SYNC); + what |= MASK_TC_BIT(rw, RAHEAD); what |= MASK_TC_BIT(rw, META); what |= MASK_TC_BIT(rw, DISCARD); @@ -662,7 +665,7 @@ static void blk_add_trace_rq(struct request_queue *q, struct request *rq, return; if (rq->cmd_flags & REQ_DISCARD) - rw |= (1 << BIO_RW_DISCARD); + rw |= REQ_DISCARD; if (rq->cmd_type == REQ_TYPE_BLOCK_PC) { what |= BLK_TC_ACT(BLK_TC_PC); @@ -1755,20 +1758,20 @@ void blk_fill_rwbs(char *rwbs, u32 rw, int bytes) if (rw & WRITE) rwbs[i++] = 'W'; - else if (rw & 1 << BIO_RW_DISCARD) + else if (rw & REQ_DISCARD) rwbs[i++] = 'D'; else if (bytes) rwbs[i++] = 'R'; else rwbs[i++] = 'N'; - if (rw & 1 << BIO_RW_AHEAD) + if (rw & REQ_RAHEAD) rwbs[i++] = 'A'; - if (rw & 1 << BIO_RW_BARRIER) + if (rw & REQ_HARDBARRIER) rwbs[i++] = 'B'; - if (rw & 1 << BIO_RW_SYNCIO) + if (rw & REQ_SYNC) rwbs[i++] = 'S'; - if (rw & 1 << BIO_RW_META) + if (rw & REQ_META) rwbs[i++] = 'M'; rwbs[i] = '\0'; @@ -1780,7 +1783,7 @@ void blk_fill_rwbs_rq(char *rwbs, struct request *rq) int bytes; if (rq->cmd_flags & REQ_DISCARD) - rw |= (1 << BIO_RW_DISCARD); + rw |= REQ_DISCARD; bytes = blk_rq_bytes(rq); diff --git a/mm/page_io.c b/mm/page_io.c index 31a3b962230a..2dee975bf469 100644 --- a/mm/page_io.c +++ b/mm/page_io.c @@ -106,7 +106,7 @@ int swap_writepage(struct page *page, struct writeback_control *wbc) goto out; } if (wbc->sync_mode == WB_SYNC_ALL) - rw |= (1 << BIO_RW_SYNCIO) | (1 << BIO_RW_UNPLUG); + rw |= REQ_SYNC | REQ_UNPLUG; count_vm_event(PSWPOUT); set_page_writeback(page); unlock_page(page); -- cgit v1.2.3 From c1955ce32fdb0877b7a1b22feb2669358f65be76 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sat, 19 Jun 2010 23:08:06 +0200 Subject: writeback: remove wb_list The wb_list member of struct backing_device_info always has exactly one element. Just use the direct bdi->wb pointer instead and simplify some code. Also remove bdi_task_init which is now trivial to prepare for the next patch. Signed-off-by: Christoph Hellwig Signed-off-by: Jens Axboe --- fs/fs-writeback.c | 4 +-- include/linux/backing-dev.h | 5 +-- mm/backing-dev.c | 83 ++++++++++++++++----------------------------- 3 files changed, 32 insertions(+), 60 deletions(-) (limited to 'fs') diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index d5be1693ac93..d67989b8ba44 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -73,9 +73,9 @@ static void bdi_queue_work(struct backing_dev_info *bdi, * If the default thread isn't there, make sure we add it. When * it gets created and wakes up, we'll run this work. */ - if (unlikely(list_empty_careful(&bdi->wb_list))) + if (unlikely(!bdi->wb.task)) { wake_up_process(default_backing_dev_info.wb.task); - else { + } else { struct bdi_writeback *wb = &bdi->wb; if (wb->task) diff --git a/include/linux/backing-dev.h b/include/linux/backing-dev.h index e9aec0d099df..50f146146169 100644 --- a/include/linux/backing-dev.h +++ b/include/linux/backing-dev.h @@ -45,8 +45,6 @@ enum bdi_stat_item { #define BDI_STAT_BATCH (8*(1+ilog2(nr_cpu_ids))) struct bdi_writeback { - struct list_head list; /* hangs off the bdi */ - struct backing_dev_info *bdi; /* our parent bdi */ unsigned int nr; @@ -80,8 +78,7 @@ struct backing_dev_info { unsigned int max_ratio, max_prop_frac; struct bdi_writeback wb; /* default writeback info for this bdi */ - spinlock_t wb_lock; /* protects update side of wb_list */ - struct list_head wb_list; /* the flusher threads hanging off this bdi */ + spinlock_t wb_lock; /* protects work_list */ struct list_head work_list; diff --git a/mm/backing-dev.c b/mm/backing-dev.c index 123bcef13e51..6c2a09c8922c 100644 --- a/mm/backing-dev.c +++ b/mm/backing-dev.c @@ -65,28 +65,21 @@ static void bdi_debug_init(void) static int bdi_debug_stats_show(struct seq_file *m, void *v) { struct backing_dev_info *bdi = m->private; - struct bdi_writeback *wb; + struct bdi_writeback *wb = &bdi->wb; unsigned long background_thresh; unsigned long dirty_thresh; unsigned long bdi_thresh; unsigned long nr_dirty, nr_io, nr_more_io, nr_wb; struct inode *inode; - /* - * inode lock is enough here, the bdi->wb_list is protected by - * RCU on the reader side - */ nr_wb = nr_dirty = nr_io = nr_more_io = 0; spin_lock(&inode_lock); - list_for_each_entry(wb, &bdi->wb_list, list) { - nr_wb++; - list_for_each_entry(inode, &wb->b_dirty, i_list) - nr_dirty++; - list_for_each_entry(inode, &wb->b_io, i_list) - nr_io++; - list_for_each_entry(inode, &wb->b_more_io, i_list) - nr_more_io++; - } + list_for_each_entry(inode, &wb->b_dirty, i_list) + nr_dirty++; + list_for_each_entry(inode, &wb->b_io, i_list) + nr_io++; + list_for_each_entry(inode, &wb->b_more_io, i_list) + nr_more_io++; spin_unlock(&inode_lock); get_dirty_limits(&background_thresh, &dirty_thresh, &bdi_thresh, bdi); @@ -98,19 +91,16 @@ static int bdi_debug_stats_show(struct seq_file *m, void *v) "BdiDirtyThresh: %8lu kB\n" "DirtyThresh: %8lu kB\n" "BackgroundThresh: %8lu kB\n" - "WritebackThreads: %8lu\n" "b_dirty: %8lu\n" "b_io: %8lu\n" "b_more_io: %8lu\n" "bdi_list: %8u\n" - "state: %8lx\n" - "wb_list: %8u\n", + "state: %8lx\n", (unsigned long) K(bdi_stat(bdi, BDI_WRITEBACK)), (unsigned long) K(bdi_stat(bdi, BDI_RECLAIMABLE)), K(bdi_thresh), K(dirty_thresh), - K(background_thresh), nr_wb, nr_dirty, nr_io, nr_more_io, - !list_empty(&bdi->bdi_list), bdi->state, - !list_empty(&bdi->wb_list)); + K(background_thresh), nr_dirty, nr_io, nr_more_io, + !list_empty(&bdi->bdi_list), bdi->state); #undef K return 0; @@ -270,24 +260,6 @@ static void bdi_wb_init(struct bdi_writeback *wb, struct backing_dev_info *bdi) INIT_LIST_HEAD(&wb->b_more_io); } -static void bdi_task_init(struct backing_dev_info *bdi, - struct bdi_writeback *wb) -{ - struct task_struct *tsk = current; - - spin_lock(&bdi->wb_lock); - list_add_tail_rcu(&wb->list, &bdi->wb_list); - spin_unlock(&bdi->wb_lock); - - tsk->flags |= PF_FLUSHER | PF_SWAPWRITE; - set_freezable(); - - /* - * Our parent may run at a different priority, just set us to normal - */ - set_user_nice(tsk, 0); -} - static int bdi_start_fn(void *ptr) { struct bdi_writeback *wb = ptr; @@ -301,7 +273,13 @@ static int bdi_start_fn(void *ptr) list_add_rcu(&bdi->bdi_list, &bdi_list); spin_unlock_bh(&bdi_lock); - bdi_task_init(bdi, wb); + current->flags |= PF_FLUSHER | PF_SWAPWRITE; + set_freezable(); + + /* + * Our parent may run at a different priority, just set us to normal + */ + set_user_nice(current, 0); /* * Clear pending bit and wakeup anybody waiting to tear us down @@ -312,12 +290,7 @@ static int bdi_start_fn(void *ptr) ret = bdi_writeback_task(wb); - /* - * Remove us from the list - */ - spin_lock(&bdi->wb_lock); - list_del_rcu(&wb->list); - spin_unlock(&bdi->wb_lock); + wb->task = NULL; /* * Flush any work that raced with us exiting. No new work @@ -326,7 +299,6 @@ static int bdi_start_fn(void *ptr) if (!list_empty(&bdi->work_list)) wb_do_writeback(wb, 1); - wb->task = NULL; return ret; } @@ -391,7 +363,13 @@ static int bdi_forker_task(void *ptr) { struct bdi_writeback *me = ptr; - bdi_task_init(me->bdi, me); + current->flags |= PF_FLUSHER | PF_SWAPWRITE; + set_freezable(); + + /* + * Our parent may run at a different priority, just set us to normal + */ + set_user_nice(current, 0); for (;;) { struct backing_dev_info *bdi, *tmp; @@ -598,8 +576,6 @@ EXPORT_SYMBOL(bdi_register_dev); */ static void bdi_wb_shutdown(struct backing_dev_info *bdi) { - struct bdi_writeback *wb; - if (!bdi_cap_writeback_dirty(bdi)) return; @@ -615,14 +591,14 @@ static void bdi_wb_shutdown(struct backing_dev_info *bdi) bdi_remove_from_list(bdi); /* - * Finally, kill the kernel threads. We don't need to be RCU + * Finally, kill the kernel thread. We don't need to be RCU * safe anymore, since the bdi is gone from visibility. Force * unfreeze of the thread before calling kthread_stop(), otherwise * it would never exet if it is currently stuck in the refrigerator. */ - list_for_each_entry(wb, &bdi->wb_list, list) { - thaw_process(wb->task); - kthread_stop(wb->task); + if (bdi->wb.task) { + thaw_process(bdi->wb.task); + kthread_stop(bdi->wb.task); } } @@ -667,7 +643,6 @@ int bdi_init(struct backing_dev_info *bdi) spin_lock_init(&bdi->wb_lock); INIT_RCU_HEAD(&bdi->rcu_head); INIT_LIST_HEAD(&bdi->bdi_list); - INIT_LIST_HEAD(&bdi->wb_list); INIT_LIST_HEAD(&bdi->work_list); bdi_wb_init(&bdi->wb, bdi); -- cgit v1.2.3 From 082439004b31adc146e96e5f1c574dd2b57dcd93 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sat, 19 Jun 2010 23:08:22 +0200 Subject: writeback: merge bdi_writeback_task and bdi_start_fn Move all code for the writeback thread into fs/fs-writeback.c instead of splitting it over two functions in two files. Signed-off-by: Christoph Hellwig Signed-off-by: Jens Axboe --- fs/fs-writeback.c | 35 ++++++++++++++++++++++++++++++++++- include/linux/backing-dev.h | 2 +- mm/backing-dev.c | 44 +------------------------------------------- 3 files changed, 36 insertions(+), 45 deletions(-) (limited to 'fs') diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index d67989b8ba44..c8471b3ddccf 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -775,12 +775,36 @@ long wb_do_writeback(struct bdi_writeback *wb, int force_wait) * Handle writeback of dirty data for the device backed by this bdi. Also * wakes up periodically and does kupdated style flushing. */ -int bdi_writeback_task(struct bdi_writeback *wb) +int bdi_writeback_thread(void *data) { + struct bdi_writeback *wb = data; + struct backing_dev_info *bdi = wb->bdi; unsigned long last_active = jiffies; unsigned long wait_jiffies = -1UL; long pages_written; + /* + * Add us to the active bdi_list + */ + spin_lock_bh(&bdi_lock); + list_add_rcu(&bdi->bdi_list, &bdi_list); + spin_unlock_bh(&bdi_lock); + + current->flags |= PF_FLUSHER | PF_SWAPWRITE; + set_freezable(); + + /* + * Our parent may run at a different priority, just set us to normal + */ + set_user_nice(current, 0); + + /* + * Clear pending bit and wakeup anybody waiting to tear us down + */ + clear_bit(BDI_pending, &bdi->state); + smp_mb__after_clear_bit(); + wake_up_bit(&bdi->state, BDI_pending); + while (!kthread_should_stop()) { pages_written = wb_do_writeback(wb, 0); @@ -813,9 +837,18 @@ int bdi_writeback_task(struct bdi_writeback *wb) try_to_freeze(); } + wb->task = NULL; + + /* + * Flush any work that raced with us exiting. No new work + * will be added, since this bdi isn't discoverable anymore. + */ + if (!list_empty(&bdi->work_list)) + wb_do_writeback(wb, 1); return 0; } + /* * Start writeback of `nr_pages' pages. If `nr_pages' is zero, write back * the whole world. diff --git a/include/linux/backing-dev.h b/include/linux/backing-dev.h index 50f146146169..e536f3a74e60 100644 --- a/include/linux/backing-dev.h +++ b/include/linux/backing-dev.h @@ -102,7 +102,7 @@ void bdi_unregister(struct backing_dev_info *bdi); int bdi_setup_and_register(struct backing_dev_info *, char *, unsigned int); void bdi_start_writeback(struct backing_dev_info *bdi, long nr_pages); void bdi_start_background_writeback(struct backing_dev_info *bdi); -int bdi_writeback_task(struct bdi_writeback *wb); +int bdi_writeback_thread(void *data); int bdi_has_dirty_io(struct backing_dev_info *bdi); void bdi_arm_supers_timer(void); diff --git a/mm/backing-dev.c b/mm/backing-dev.c index 6c2a09c8922c..bceac647e4d1 100644 --- a/mm/backing-dev.c +++ b/mm/backing-dev.c @@ -260,48 +260,6 @@ static void bdi_wb_init(struct bdi_writeback *wb, struct backing_dev_info *bdi) INIT_LIST_HEAD(&wb->b_more_io); } -static int bdi_start_fn(void *ptr) -{ - struct bdi_writeback *wb = ptr; - struct backing_dev_info *bdi = wb->bdi; - int ret; - - /* - * Add us to the active bdi_list - */ - spin_lock_bh(&bdi_lock); - list_add_rcu(&bdi->bdi_list, &bdi_list); - spin_unlock_bh(&bdi_lock); - - current->flags |= PF_FLUSHER | PF_SWAPWRITE; - set_freezable(); - - /* - * Our parent may run at a different priority, just set us to normal - */ - set_user_nice(current, 0); - - /* - * Clear pending bit and wakeup anybody waiting to tear us down - */ - clear_bit(BDI_pending, &bdi->state); - smp_mb__after_clear_bit(); - wake_up_bit(&bdi->state, BDI_pending); - - ret = bdi_writeback_task(wb); - - wb->task = NULL; - - /* - * Flush any work that raced with us exiting. No new work - * will be added, since this bdi isn't discoverable anymore. - */ - if (!list_empty(&bdi->work_list)) - wb_do_writeback(wb, 1); - - return ret; -} - int bdi_has_dirty_io(struct backing_dev_info *bdi) { return wb_has_dirty_io(&bdi->wb); @@ -425,7 +383,7 @@ static int bdi_forker_task(void *ptr) spin_unlock_bh(&bdi_lock); wb = &bdi->wb; - wb->task = kthread_run(bdi_start_fn, wb, "flush-%s", + wb->task = kthread_run(bdi_writeback_thread, wb, "flush-%s", dev_name(bdi->dev)); /* * If task creation fails, then readd the bdi to -- cgit v1.2.3 From 1676effca4cd2a6b32e6e8e0ecaa91522dfda6fa Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Mon, 21 Jun 2010 11:02:48 +0200 Subject: gcc-4.6: fs: fix unused but set warnings No real bugs I believe, just some dead code, and some shut up code. Signed-off-by: Andi Kleen Cc: Eric Paris Signed-off-by: Andrew Morton Signed-off-by: Jens Axboe --- fs/splice.c | 2 -- include/linux/audit.h | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/splice.c b/fs/splice.c index efdbfece9932..ec11c52d646d 100644 --- a/fs/splice.c +++ b/fs/splice.c @@ -597,7 +597,6 @@ ssize_t default_file_splice_read(struct file *in, loff_t *ppos, struct page *pages[PIPE_DEF_BUFFERS]; struct partial_page partial[PIPE_DEF_BUFFERS]; struct iovec *vec, __vec[PIPE_DEF_BUFFERS]; - pgoff_t index; ssize_t res; size_t this_len; int error; @@ -621,7 +620,6 @@ ssize_t default_file_splice_read(struct file *in, loff_t *ppos, goto shrink_ret; } - index = *ppos >> PAGE_CACHE_SHIFT; offset = *ppos & ~PAGE_CACHE_MASK; nr_pages = (len + offset + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; diff --git a/include/linux/audit.h b/include/linux/audit.h index f391d45c8aea..e24afabc548f 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -544,7 +544,7 @@ extern int audit_signals; #define audit_putname(n) do { ; } while (0) #define __audit_inode(n,d) do { ; } while (0) #define __audit_inode_child(i,p) do { ; } while (0) -#define audit_inode(n,d) do { ; } while (0) +#define audit_inode(n,d) do { (void)(d); } while (0) #define audit_inode_child(i,p) do { ; } while (0) #define audit_core_dumps(i) do { ; } while (0) #define auditsc_get_stamp(c,t,s) (0) -- cgit v1.2.3 From 455b2864686d3591b3b2f39eb46290c95f76471f Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Wed, 7 Jul 2010 13:24:06 +1000 Subject: writeback: Initial tracing support Trace queue/sched/exec parts of the writeback loop. This provides insight into when and why flusher threads are scheduled to run. e.g a sync invocation leaves traces like: sync-[...]: writeback_queue: bdi 8:0: sb_dev 8:1 nr_pages=7712 sync_mode=0 kupdate=0 range_cyclic=0 background=0 flush-8:0-[...]: writeback_exec: bdi 8:0: sb_dev 8:1 nr_pages=7712 sync_mode=0 kupdate=0 range_cyclic=0 background=0 This also lays the foundation for adding more writeback tracing to provide deeper insight into the whole writeback path. The original tracing code is from Jens Axboe, though this version is a rewrite as a result of the code being traced changing significantly. Signed-off-by: Dave Chinner Signed-off-by: Jens Axboe --- fs/fs-writeback.c | 38 +++++++++++++---- include/trace/events/writeback.h | 91 ++++++++++++++++++++++++++++++++++++++++ mm/backing-dev.c | 3 ++ 3 files changed, 124 insertions(+), 8 deletions(-) create mode 100644 include/trace/events/writeback.h (limited to 'fs') diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index c8471b3ddccf..73acab4dc2b7 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -26,15 +26,9 @@ #include #include #include +#include #include "internal.h" -#define inode_to_bdi(inode) ((inode)->i_mapping->backing_dev_info) - -/* - * We don't actually have pdflush, but this one is exported though /proc... - */ -int nr_pdflush_threads; - /* * Passed into wb_writeback(), essentially a subset of writeback_control */ @@ -50,6 +44,21 @@ struct wb_writeback_work { struct completion *done; /* set if the caller waits */ }; +/* + * Include the creation of the trace points after defining the + * wb_writeback_work structure so that the definition remains local to this + * file. + */ +#define CREATE_TRACE_POINTS +#include + +#define inode_to_bdi(inode) ((inode)->i_mapping->backing_dev_info) + +/* + * We don't actually have pdflush, but this one is exported though /proc... + */ +int nr_pdflush_threads; + /** * writeback_in_progress - determine whether there is writeback in progress * @bdi: the device's backing_dev_info structure. @@ -65,6 +74,8 @@ int writeback_in_progress(struct backing_dev_info *bdi) static void bdi_queue_work(struct backing_dev_info *bdi, struct wb_writeback_work *work) { + trace_writeback_queue(bdi, work); + spin_lock(&bdi->wb_lock); list_add_tail(&work->list, &bdi->work_list); spin_unlock(&bdi->wb_lock); @@ -74,6 +85,7 @@ static void bdi_queue_work(struct backing_dev_info *bdi, * it gets created and wakes up, we'll run this work. */ if (unlikely(!bdi->wb.task)) { + trace_writeback_nothread(bdi, work); wake_up_process(default_backing_dev_info.wb.task); } else { struct bdi_writeback *wb = &bdi->wb; @@ -95,8 +107,10 @@ __bdi_start_writeback(struct backing_dev_info *bdi, long nr_pages, */ work = kzalloc(sizeof(*work), GFP_ATOMIC); if (!work) { - if (bdi->wb.task) + if (bdi->wb.task) { + trace_writeback_nowork(bdi); wake_up_process(bdi->wb.task); + } return; } @@ -751,6 +765,8 @@ long wb_do_writeback(struct bdi_writeback *wb, int force_wait) if (force_wait) work->sync_mode = WB_SYNC_ALL; + trace_writeback_exec(bdi, work); + wrote += wb_writeback(wb, work); /* @@ -805,9 +821,13 @@ int bdi_writeback_thread(void *data) smp_mb__after_clear_bit(); wake_up_bit(&bdi->state, BDI_pending); + trace_writeback_thread_start(bdi); + while (!kthread_should_stop()) { pages_written = wb_do_writeback(wb, 0); + trace_writeback_pages_written(pages_written); + if (pages_written) last_active = jiffies; else if (wait_jiffies != -1UL) { @@ -845,6 +865,8 @@ int bdi_writeback_thread(void *data) */ if (!list_empty(&bdi->work_list)) wb_do_writeback(wb, 1); + + trace_writeback_thread_stop(bdi); return 0; } diff --git a/include/trace/events/writeback.h b/include/trace/events/writeback.h new file mode 100644 index 000000000000..562fcae10d9d --- /dev/null +++ b/include/trace/events/writeback.h @@ -0,0 +1,91 @@ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM writeback + +#if !defined(_TRACE_WRITEBACK_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_WRITEBACK_H + +#include +#include + +struct wb_writeback_work; + +DECLARE_EVENT_CLASS(writeback_work_class, + TP_PROTO(struct backing_dev_info *bdi, struct wb_writeback_work *work), + TP_ARGS(bdi, work), + TP_STRUCT__entry( + __array(char, name, 32) + __field(long, nr_pages) + __field(dev_t, sb_dev) + __field(int, sync_mode) + __field(int, for_kupdate) + __field(int, range_cyclic) + __field(int, for_background) + ), + TP_fast_assign( + strncpy(__entry->name, dev_name(bdi->dev), 32); + __entry->nr_pages = work->nr_pages; + __entry->sb_dev = work->sb ? work->sb->s_dev : 0; + __entry->sync_mode = work->sync_mode; + __entry->for_kupdate = work->for_kupdate; + __entry->range_cyclic = work->range_cyclic; + __entry->for_background = work->for_background; + ), + TP_printk("bdi %s: sb_dev %d:%d nr_pages=%ld sync_mode=%d " + "kupdate=%d range_cyclic=%d background=%d", + __entry->name, + MAJOR(__entry->sb_dev), MINOR(__entry->sb_dev), + __entry->nr_pages, + __entry->sync_mode, + __entry->for_kupdate, + __entry->range_cyclic, + __entry->for_background + ) +); +#define DEFINE_WRITEBACK_WORK_EVENT(name) \ +DEFINE_EVENT(writeback_work_class, name, \ + TP_PROTO(struct backing_dev_info *bdi, struct wb_writeback_work *work), \ + TP_ARGS(bdi, work)) +DEFINE_WRITEBACK_WORK_EVENT(writeback_nothread); +DEFINE_WRITEBACK_WORK_EVENT(writeback_queue); +DEFINE_WRITEBACK_WORK_EVENT(writeback_exec); + +TRACE_EVENT(writeback_pages_written, + TP_PROTO(long pages_written), + TP_ARGS(pages_written), + TP_STRUCT__entry( + __field(long, pages) + ), + TP_fast_assign( + __entry->pages = pages_written; + ), + TP_printk("%ld", __entry->pages) +); + +DECLARE_EVENT_CLASS(writeback_class, + TP_PROTO(struct backing_dev_info *bdi), + TP_ARGS(bdi), + TP_STRUCT__entry( + __array(char, name, 32) + ), + TP_fast_assign( + strncpy(__entry->name, dev_name(bdi->dev), 32); + ), + TP_printk("bdi %s", + __entry->name + ) +); +#define DEFINE_WRITEBACK_EVENT(name) \ +DEFINE_EVENT(writeback_class, name, \ + TP_PROTO(struct backing_dev_info *bdi), \ + TP_ARGS(bdi)) + +DEFINE_WRITEBACK_EVENT(writeback_nowork); +DEFINE_WRITEBACK_EVENT(writeback_bdi_register); +DEFINE_WRITEBACK_EVENT(writeback_bdi_unregister); +DEFINE_WRITEBACK_EVENT(writeback_thread_start); +DEFINE_WRITEBACK_EVENT(writeback_thread_stop); + +#endif /* _TRACE_WRITEBACK_H */ + +/* This part must be outside protection */ +#include diff --git a/mm/backing-dev.c b/mm/backing-dev.c index bceac647e4d1..ac78a3336181 100644 --- a/mm/backing-dev.c +++ b/mm/backing-dev.c @@ -10,6 +10,7 @@ #include #include #include +#include static atomic_long_t bdi_seq = ATOMIC_LONG_INIT(0); @@ -518,6 +519,7 @@ int bdi_register(struct backing_dev_info *bdi, struct device *parent, bdi_debug_register(bdi, dev_name(dev)); set_bit(BDI_registered, &bdi->state); + trace_writeback_bdi_register(bdi); exit: return ret; } @@ -578,6 +580,7 @@ static void bdi_prune_sb(struct backing_dev_info *bdi) void bdi_unregister(struct backing_dev_info *bdi) { if (bdi->dev) { + trace_writeback_bdi_unregister(bdi); bdi_prune_sb(bdi); if (!bdi_cap_flush_forker(bdi)) -- cgit v1.2.3 From 028c2dd184c097809986684f2f0627eea5529fea Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Wed, 7 Jul 2010 13:24:07 +1000 Subject: writeback: Add tracing to balance_dirty_pages Tracing high level background writeback events is good, but it doesn't give the entire picture. Add visibility into write throttling to catch IO dispatched by foreground throttling of processing dirtying lots of pages. Signed-off-by: Dave Chinner Signed-off-by: Jens Axboe --- fs/fs-writeback.c | 5 ++++ include/trace/events/writeback.h | 64 ++++++++++++++++++++++++++++++++++++++++ mm/page-writeback.c | 4 +++ 3 files changed, 73 insertions(+) (limited to 'fs') diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 73acab4dc2b7..bf10cbf379dd 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -656,10 +656,14 @@ static long wb_writeback(struct bdi_writeback *wb, wbc.more_io = 0; wbc.nr_to_write = MAX_WRITEBACK_PAGES; wbc.pages_skipped = 0; + + trace_wbc_writeback_start(&wbc, wb->bdi); if (work->sb) __writeback_inodes_sb(work->sb, wb, &wbc); else writeback_inodes_wb(wb, &wbc); + trace_wbc_writeback_written(&wbc, wb->bdi); + work->nr_pages -= MAX_WRITEBACK_PAGES - wbc.nr_to_write; wrote += MAX_WRITEBACK_PAGES - wbc.nr_to_write; @@ -687,6 +691,7 @@ static long wb_writeback(struct bdi_writeback *wb, if (!list_empty(&wb->b_more_io)) { inode = list_entry(wb->b_more_io.prev, struct inode, i_list); + trace_wbc_writeback_wait(&wbc, wb->bdi); inode_wait_for_writeback(inode); } spin_unlock(&inode_lock); diff --git a/include/trace/events/writeback.h b/include/trace/events/writeback.h index 562fcae10d9d..0be26acae064 100644 --- a/include/trace/events/writeback.h +++ b/include/trace/events/writeback.h @@ -85,6 +85,70 @@ DEFINE_WRITEBACK_EVENT(writeback_bdi_unregister); DEFINE_WRITEBACK_EVENT(writeback_thread_start); DEFINE_WRITEBACK_EVENT(writeback_thread_stop); +DECLARE_EVENT_CLASS(wbc_class, + TP_PROTO(struct writeback_control *wbc, struct backing_dev_info *bdi), + TP_ARGS(wbc, bdi), + TP_STRUCT__entry( + __array(char, name, 32) + __field(long, nr_to_write) + __field(long, pages_skipped) + __field(int, sync_mode) + __field(int, nonblocking) + __field(int, encountered_congestion) + __field(int, for_kupdate) + __field(int, for_background) + __field(int, for_reclaim) + __field(int, range_cyclic) + __field(int, more_io) + __field(unsigned long, older_than_this) + __field(long, range_start) + __field(long, range_end) + ), + + TP_fast_assign( + strncpy(__entry->name, dev_name(bdi->dev), 32); + __entry->nr_to_write = wbc->nr_to_write; + __entry->pages_skipped = wbc->pages_skipped; + __entry->sync_mode = wbc->sync_mode; + __entry->for_kupdate = wbc->for_kupdate; + __entry->for_background = wbc->for_background; + __entry->for_reclaim = wbc->for_reclaim; + __entry->range_cyclic = wbc->range_cyclic; + __entry->more_io = wbc->more_io; + __entry->older_than_this = wbc->older_than_this ? + *wbc->older_than_this : 0; + __entry->range_start = (long)wbc->range_start; + __entry->range_end = (long)wbc->range_end; + ), + + TP_printk("bdi %s: towrt=%ld skip=%ld mode=%d kupd=%d " + "bgrd=%d reclm=%d cyclic=%d more=%d older=0x%lx " + "start=0x%lx end=0x%lx", + __entry->name, + __entry->nr_to_write, + __entry->pages_skipped, + __entry->sync_mode, + __entry->for_kupdate, + __entry->for_background, + __entry->for_reclaim, + __entry->range_cyclic, + __entry->more_io, + __entry->older_than_this, + __entry->range_start, + __entry->range_end) +) + +#define DEFINE_WBC_EVENT(name) \ +DEFINE_EVENT(wbc_class, name, \ + TP_PROTO(struct writeback_control *wbc, struct backing_dev_info *bdi), \ + TP_ARGS(wbc, bdi)) +DEFINE_WBC_EVENT(wbc_writeback_start); +DEFINE_WBC_EVENT(wbc_writeback_written); +DEFINE_WBC_EVENT(wbc_writeback_wait); +DEFINE_WBC_EVENT(wbc_balance_dirty_start); +DEFINE_WBC_EVENT(wbc_balance_dirty_written); +DEFINE_WBC_EVENT(wbc_balance_dirty_wait); + #endif /* _TRACE_WRITEBACK_H */ /* This part must be outside protection */ diff --git a/mm/page-writeback.c b/mm/page-writeback.c index 37498ef61548..d556cd829af6 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c @@ -34,6 +34,7 @@ #include #include #include +#include /* * After a CPU has dirtied this many pages, balance_dirty_pages_ratelimited @@ -535,11 +536,13 @@ static void balance_dirty_pages(struct address_space *mapping, * threshold otherwise wait until the disk writes catch * up. */ + trace_wbc_balance_dirty_start(&wbc, bdi); if (bdi_nr_reclaimable > bdi_thresh) { writeback_inodes_wb(&bdi->wb, &wbc); pages_written += write_chunk - wbc.nr_to_write; get_dirty_limits(&background_thresh, &dirty_thresh, &bdi_thresh, bdi); + trace_wbc_balance_dirty_written(&wbc, bdi); } /* @@ -565,6 +568,7 @@ static void balance_dirty_pages(struct address_space *mapping, if (pages_written >= write_chunk) break; /* We've done our duty */ + trace_wbc_balance_dirty_wait(&wbc, bdi); __set_current_state(TASK_INTERRUPTIBLE); io_schedule_timeout(pause); -- cgit v1.2.3 From 6e9624b8caec290d28b4c6d9ec75749df6372b87 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Sat, 7 Aug 2010 18:25:34 +0200 Subject: block: push down BKL into .open and .release The open and release block_device_operations are currently called with the BKL held. In order to change that, we must first make sure that all drivers that currently rely on this have no regressions. This blindly pushes the BKL into all .open and .release operations for all block drivers to prepare for the next step. The drivers can subsequently replace the BKL with their own locks or remove it completely when it can be shown that it is not needed. The functions blkdev_get and blkdev_put are the only remaining users of the big kernel lock in the block layer, besides a few uses in the ioctl code, none of which need to serialize with blkdev_{get,put}. Most of these two functions is also under the protection of bdev->bd_mutex, including the actual calls to ->open and ->release, and the common code does not access any global data structures that need the BKL. Signed-off-by: Arnd Bergmann Acked-by: Christoph Hellwig Signed-off-by: Jens Axboe --- arch/um/drivers/ubd_kern.c | 7 ++++++- drivers/block/DAC960.c | 13 +++++++++---- drivers/block/amiflop.c | 12 ++++++++++-- drivers/block/aoe/aoeblk.c | 4 ++++ drivers/block/ataflop.c | 14 +++++++++++++- drivers/block/cciss.c | 23 ++++++++++++++++++++--- drivers/block/cpqarray.c | 22 +++++++++++++++++++--- drivers/block/drbd/drbd_main.c | 4 ++++ drivers/block/floppy.c | 5 +++++ drivers/block/loop.c | 5 +++++ drivers/block/paride/pcd.c | 10 +++++++++- drivers/block/paride/pd.c | 4 ++++ drivers/block/paride/pf.c | 20 +++++++++++++++----- drivers/block/pktcdvd.c | 5 +++++ drivers/block/swim.c | 15 ++++++++++++++- drivers/block/swim3.c | 15 ++++++++++++++- drivers/block/ub.c | 17 ++++++++++++++++- drivers/block/viodasd.c | 19 ++++++++++++++++++- drivers/block/xen-blkfront.c | 7 +++++++ drivers/block/xsysace.c | 6 ++++++ drivers/block/z2ram.c | 13 ++++++++++--- drivers/cdrom/gdrom.c | 8 +++++++- drivers/cdrom/viocd.c | 10 +++++++++- drivers/ide/ide-cd.c | 14 +++++++++----- drivers/ide/ide-gd.c | 17 ++++++++++++++++- drivers/ide/ide-tape.c | 9 ++++++++- drivers/md/dm.c | 7 +++++++ drivers/md/md.c | 6 ++++++ drivers/memstick/core/mspro_block.c | 9 ++++++++- drivers/message/i2o/i2o_block.c | 5 +++++ drivers/mmc/card/block.c | 5 +++++ drivers/mtd/mtd_blkdevs.c | 6 +++++- drivers/s390/block/dasd.c | 6 ++++++ drivers/s390/block/dcssblk.c | 5 +++++ drivers/s390/char/tape_block.c | 8 +++++++- drivers/scsi/sd.c | 5 +++++ drivers/scsi/sr.c | 7 ++++++- drivers/staging/hv/blkvsc_drv.c | 5 +++++ fs/block_dev.c | 10 ++-------- 39 files changed, 334 insertions(+), 48 deletions(-) (limited to 'fs') diff --git a/arch/um/drivers/ubd_kern.c b/arch/um/drivers/ubd_kern.c index da992a3ad6b7..1bcd208c459f 100644 --- a/arch/um/drivers/ubd_kern.c +++ b/arch/um/drivers/ubd_kern.c @@ -33,6 +33,7 @@ #include "linux/mm.h" #include "linux/slab.h" #include "linux/vmalloc.h" +#include "linux/smp_lock.h" #include "linux/blkpg.h" #include "linux/genhd.h" #include "linux/spinlock.h" @@ -1098,6 +1099,7 @@ static int ubd_open(struct block_device *bdev, fmode_t mode) struct ubd *ubd_dev = disk->private_data; int err = 0; + lock_kernel(); if(ubd_dev->count == 0){ err = ubd_open_dev(ubd_dev); if(err){ @@ -1115,7 +1117,8 @@ static int ubd_open(struct block_device *bdev, fmode_t mode) if(--ubd_dev->count == 0) ubd_close_dev(ubd_dev); err = -EROFS; }*/ - out: +out: + unlock_kernel(); return err; } @@ -1123,8 +1126,10 @@ static int ubd_release(struct gendisk *disk, fmode_t mode) { struct ubd *ubd_dev = disk->private_data; + lock_kernel(); if(--ubd_dev->count == 0) ubd_close_dev(ubd_dev); + unlock_kernel(); return 0; } diff --git a/drivers/block/DAC960.c b/drivers/block/DAC960.c index c5f22bb0a48e..4e2c367fec11 100644 --- a/drivers/block/DAC960.c +++ b/drivers/block/DAC960.c @@ -79,23 +79,28 @@ static int DAC960_open(struct block_device *bdev, fmode_t mode) struct gendisk *disk = bdev->bd_disk; DAC960_Controller_T *p = disk->queue->queuedata; int drive_nr = (long)disk->private_data; + int ret = -ENXIO; + lock_kernel(); if (p->FirmwareType == DAC960_V1_Controller) { if (p->V1.LogicalDriveInformation[drive_nr]. LogicalDriveState == DAC960_V1_LogicalDrive_Offline) - return -ENXIO; + goto out; } else { DAC960_V2_LogicalDeviceInfo_T *i = p->V2.LogicalDeviceInformation[drive_nr]; if (!i || i->LogicalDeviceState == DAC960_V2_LogicalDevice_Offline) - return -ENXIO; + goto out; } check_disk_change(bdev); if (!get_capacity(p->disks[drive_nr])) - return -ENXIO; - return 0; + goto out; + ret = 0; +out: + unlock_kernel(); + return ret; } static int DAC960_getgeo(struct block_device *bdev, struct hd_geometry *geo) diff --git a/drivers/block/amiflop.c b/drivers/block/amiflop.c index 0fa26359304c..76f114f0bba3 100644 --- a/drivers/block/amiflop.c +++ b/drivers/block/amiflop.c @@ -1555,10 +1555,13 @@ static int floppy_open(struct block_device *bdev, fmode_t mode) int old_dev; unsigned long flags; + lock_kernel(); old_dev = fd_device[drive]; - if (fd_ref[drive] && old_dev != system) + if (fd_ref[drive] && old_dev != system) { + unlock_kernel(); return -EBUSY; + } if (mode & (FMODE_READ|FMODE_WRITE)) { check_disk_change(bdev); @@ -1571,8 +1574,10 @@ static int floppy_open(struct block_device *bdev, fmode_t mode) fd_deselect (drive); rel_fdc(); - if (wrprot) + if (wrprot) { + unlock_kernel(); return -EROFS; + } } } @@ -1589,6 +1594,7 @@ static int floppy_open(struct block_device *bdev, fmode_t mode) printk(KERN_INFO "fd%d: accessing %s-disk with %s-layout\n",drive, unit[drive].type->name, data_types[system].name); + unlock_kernel(); return 0; } @@ -1597,6 +1603,7 @@ static int floppy_release(struct gendisk *disk, fmode_t mode) struct amiga_floppy_struct *p = disk->private_data; int drive = p - unit; + lock_kernel(); if (unit[drive].dirty == 1) { del_timer (flush_track_timer + drive); non_int_flush_track (drive); @@ -1610,6 +1617,7 @@ static int floppy_release(struct gendisk *disk, fmode_t mode) /* the mod_use counter is handled this way */ floppy_off (drive | 0x40000000); #endif + unlock_kernel(); return 0; } diff --git a/drivers/block/aoe/aoeblk.c b/drivers/block/aoe/aoeblk.c index 65deffde60ac..a946929735a5 100644 --- a/drivers/block/aoe/aoeblk.c +++ b/drivers/block/aoe/aoeblk.c @@ -12,6 +12,7 @@ #include #include #include +#include #include "aoe.h" static struct kmem_cache *buf_pool_cache; @@ -124,13 +125,16 @@ aoeblk_open(struct block_device *bdev, fmode_t mode) struct aoedev *d = bdev->bd_disk->private_data; ulong flags; + lock_kernel(); spin_lock_irqsave(&d->lock, flags); if (d->flags & DEVFL_UP) { d->nopen++; spin_unlock_irqrestore(&d->lock, flags); + unlock_kernel(); return 0; } spin_unlock_irqrestore(&d->lock, flags); + unlock_kernel(); return -ENODEV; } diff --git a/drivers/block/ataflop.c b/drivers/block/ataflop.c index 1bb8bfcfdbd9..aceb96476524 100644 --- a/drivers/block/ataflop.c +++ b/drivers/block/ataflop.c @@ -1850,22 +1850,34 @@ static int floppy_open(struct block_device *bdev, fmode_t mode) return 0; } +static int floppy_unlocked_open(struct block_device *bdev, fmode_t mode) +{ + int ret; + + lock_kernel(); + ret = floppy_open(bdev, mode); + unlock_kernel(); + + return ret; +} static int floppy_release(struct gendisk *disk, fmode_t mode) { struct atari_floppy_struct *p = disk->private_data; + lock_kernel(); if (p->ref < 0) p->ref = 0; else if (!p->ref--) { printk(KERN_ERR "floppy_release with fd_ref == 0"); p->ref = 0; } + unlock_kernel(); return 0; } static const struct block_device_operations floppy_fops = { .owner = THIS_MODULE, - .open = floppy_open, + .open = floppy_unlocked_open, .release = floppy_release, .ioctl = fd_ioctl, .media_changed = check_floppy_change, diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index a6c0494dd054..665a470310a9 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -178,6 +178,7 @@ static void do_cciss_request(struct request_queue *q); static irqreturn_t do_cciss_intx(int irq, void *dev_id); static irqreturn_t do_cciss_msix_intr(int irq, void *dev_id); static int cciss_open(struct block_device *bdev, fmode_t mode); +static int cciss_unlocked_open(struct block_device *bdev, fmode_t mode); static int cciss_release(struct gendisk *disk, fmode_t mode); static int do_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, unsigned long arg); @@ -237,7 +238,7 @@ static int cciss_compat_ioctl(struct block_device *, fmode_t, static const struct block_device_operations cciss_fops = { .owner = THIS_MODULE, - .open = cciss_open, + .open = cciss_unlocked_open, .release = cciss_release, .ioctl = do_ioctl, .getgeo = cciss_getgeo, @@ -1042,13 +1043,28 @@ static int cciss_open(struct block_device *bdev, fmode_t mode) return 0; } +static int cciss_unlocked_open(struct block_device *bdev, fmode_t mode) +{ + int ret; + + lock_kernel(); + ret = cciss_open(bdev, mode); + unlock_kernel(); + + return ret; +} + /* * Close. Sync first. */ static int cciss_release(struct gendisk *disk, fmode_t mode) { - ctlr_info_t *host = get_host(disk); - drive_info_struct *drv = get_drv(disk); + ctlr_info_t *host; + drive_info_struct *drv; + + lock_kernel(); + host = get_host(disk); + drv = get_drv(disk); #ifdef CCISS_DEBUG printk(KERN_DEBUG "cciss_release %s\n", disk->disk_name); @@ -1056,6 +1072,7 @@ static int cciss_release(struct gendisk *disk, fmode_t mode) drv->usage_count--; host->usage_count--; + unlock_kernel(); return 0; } diff --git a/drivers/block/cpqarray.c b/drivers/block/cpqarray.c index c459aeea3c0c..28937b661564 100644 --- a/drivers/block/cpqarray.c +++ b/drivers/block/cpqarray.c @@ -158,7 +158,7 @@ static int sendcmd( unsigned int blkcnt, unsigned int log_unit ); -static int ida_open(struct block_device *bdev, fmode_t mode); +static int ida_unlocked_open(struct block_device *bdev, fmode_t mode); static int ida_release(struct gendisk *disk, fmode_t mode); static int ida_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, unsigned long arg); static int ida_getgeo(struct block_device *bdev, struct hd_geometry *geo); @@ -196,7 +196,7 @@ static inline ctlr_info_t *get_host(struct gendisk *disk) static const struct block_device_operations ida_fops = { .owner = THIS_MODULE, - .open = ida_open, + .open = ida_unlocked_open, .release = ida_release, .ioctl = ida_ioctl, .getgeo = ida_getgeo, @@ -841,13 +841,29 @@ static int ida_open(struct block_device *bdev, fmode_t mode) return 0; } +static int ida_unlocked_open(struct block_device *bdev, fmode_t mode) +{ + int ret; + + lock_kernel(); + ret = ida_open(bdev, mode); + unlock_kernel(); + + return ret; +} + /* * Close. Sync first. */ static int ida_release(struct gendisk *disk, fmode_t mode) { - ctlr_info_t *host = get_host(disk); + ctlr_info_t *host; + + lock_kernel(); + host = get_host(disk); host->usage_count--; + unlock_kernel(); + return 0; } diff --git a/drivers/block/drbd/drbd_main.c b/drivers/block/drbd/drbd_main.c index e2ab13d99d69..d2b6764a7b1f 100644 --- a/drivers/block/drbd/drbd_main.c +++ b/drivers/block/drbd/drbd_main.c @@ -2604,6 +2604,7 @@ static int drbd_open(struct block_device *bdev, fmode_t mode) unsigned long flags; int rv = 0; + lock_kernel(); spin_lock_irqsave(&mdev->req_lock, flags); /* to have a stable mdev->state.role * and no race with updating open_cnt */ @@ -2618,6 +2619,7 @@ static int drbd_open(struct block_device *bdev, fmode_t mode) if (!rv) mdev->open_cnt++; spin_unlock_irqrestore(&mdev->req_lock, flags); + unlock_kernel(); return rv; } @@ -2625,7 +2627,9 @@ static int drbd_open(struct block_device *bdev, fmode_t mode) static int drbd_release(struct gendisk *gd, fmode_t mode) { struct drbd_conf *mdev = gd->private_data; + lock_kernel(); mdev->open_cnt--; + unlock_kernel(); return 0; } diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c index 40419b066aa9..3126d5122b2b 100644 --- a/drivers/block/floppy.c +++ b/drivers/block/floppy.c @@ -3616,6 +3616,7 @@ static int floppy_release(struct gendisk *disk, fmode_t mode) { int drive = (long)disk->private_data; + lock_kernel(); mutex_lock(&open_lock); if (UDRS->fd_ref < 0) UDRS->fd_ref = 0; @@ -3626,6 +3627,7 @@ static int floppy_release(struct gendisk *disk, fmode_t mode) if (!UDRS->fd_ref) opened_bdev[drive] = NULL; mutex_unlock(&open_lock); + unlock_kernel(); return 0; } @@ -3643,6 +3645,7 @@ static int floppy_open(struct block_device *bdev, fmode_t mode) int res = -EBUSY; char *tmp; + lock_kernel(); mutex_lock(&open_lock); old_dev = UDRS->fd_device; if (opened_bdev[drive] && opened_bdev[drive] != bdev) @@ -3719,6 +3722,7 @@ static int floppy_open(struct block_device *bdev, fmode_t mode) goto out; } mutex_unlock(&open_lock); + unlock_kernel(); return 0; out: if (UDRS->fd_ref < 0) @@ -3729,6 +3733,7 @@ out: opened_bdev[drive] = NULL; out2: mutex_unlock(&open_lock); + unlock_kernel(); return res; } diff --git a/drivers/block/loop.c b/drivers/block/loop.c index d285a5481965..f3c636d23718 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -67,6 +67,7 @@ #include #include #include +#include #include #include /* for invalidate_bdev() */ #include @@ -1408,9 +1409,11 @@ static int lo_open(struct block_device *bdev, fmode_t mode) { struct loop_device *lo = bdev->bd_disk->private_data; + lock_kernel(); mutex_lock(&lo->lo_ctl_mutex); lo->lo_refcnt++; mutex_unlock(&lo->lo_ctl_mutex); + unlock_kernel(); return 0; } @@ -1420,6 +1423,7 @@ static int lo_release(struct gendisk *disk, fmode_t mode) struct loop_device *lo = disk->private_data; int err; + lock_kernel(); mutex_lock(&lo->lo_ctl_mutex); if (--lo->lo_refcnt) @@ -1444,6 +1448,7 @@ static int lo_release(struct gendisk *disk, fmode_t mode) out: mutex_unlock(&lo->lo_ctl_mutex); out_unlocked: + lock_kernel(); return 0; } diff --git a/drivers/block/paride/pcd.c b/drivers/block/paride/pcd.c index daba7a62a663..76f8565e1e8d 100644 --- a/drivers/block/paride/pcd.c +++ b/drivers/block/paride/pcd.c @@ -225,13 +225,21 @@ static char *pcd_buf; /* buffer for request in progress */ static int pcd_block_open(struct block_device *bdev, fmode_t mode) { struct pcd_unit *cd = bdev->bd_disk->private_data; - return cdrom_open(&cd->info, bdev, mode); + int ret; + + lock_kernel(); + ret = cdrom_open(&cd->info, bdev, mode); + unlock_kernel(); + + return ret; } static int pcd_block_release(struct gendisk *disk, fmode_t mode) { struct pcd_unit *cd = disk->private_data; + lock_kernel(); cdrom_release(&cd->info, mode); + unlock_kernel(); return 0; } diff --git a/drivers/block/paride/pd.c b/drivers/block/paride/pd.c index c4d6ed9846ca..985f0d4f1d1e 100644 --- a/drivers/block/paride/pd.c +++ b/drivers/block/paride/pd.c @@ -736,12 +736,14 @@ static int pd_open(struct block_device *bdev, fmode_t mode) { struct pd_unit *disk = bdev->bd_disk->private_data; + lock_kernel(); disk->access++; if (disk->removable) { pd_special_command(disk, pd_media_check); pd_special_command(disk, pd_door_lock); } + unlock_kernel(); return 0; } @@ -783,8 +785,10 @@ static int pd_release(struct gendisk *p, fmode_t mode) { struct pd_unit *disk = p->private_data; + lock_kernel(); if (!--disk->access && disk->removable) pd_special_command(disk, pd_door_unlock); + unlock_kernel(); return 0; } diff --git a/drivers/block/paride/pf.c b/drivers/block/paride/pf.c index 38b4d566b816..4457b494882a 100644 --- a/drivers/block/paride/pf.c +++ b/drivers/block/paride/pf.c @@ -300,20 +300,26 @@ static void __init pf_init_units(void) static int pf_open(struct block_device *bdev, fmode_t mode) { struct pf_unit *pf = bdev->bd_disk->private_data; + int ret; + lock_kernel(); pf_identify(pf); + ret = -ENODEV; if (pf->media_status == PF_NM) - return -ENODEV; + goto out; + ret = -EROFS; if ((pf->media_status == PF_RO) && (mode & FMODE_WRITE)) - return -EROFS; + goto out; + ret = 0; pf->access++; if (pf->removable) pf_lock(pf, 1); - - return 0; +out: + unlock_kernel(); + return ret; } static int pf_getgeo(struct block_device *bdev, struct hd_geometry *geo) @@ -354,14 +360,18 @@ static int pf_release(struct gendisk *disk, fmode_t mode) { struct pf_unit *pf = disk->private_data; - if (pf->access <= 0) + lock_kernel(); + if (pf->access <= 0) { + unlock_kernel(); return -EINVAL; + } pf->access--; if (!pf->access && pf->removable) pf_lock(pf, 0); + unlock_kernel(); return 0; } diff --git a/drivers/block/pktcdvd.c b/drivers/block/pktcdvd.c index 40f1e31f42c4..b1cbeb59bb76 100644 --- a/drivers/block/pktcdvd.c +++ b/drivers/block/pktcdvd.c @@ -2383,6 +2383,7 @@ static int pkt_open(struct block_device *bdev, fmode_t mode) VPRINTK(DRIVER_NAME": entering open\n"); + lock_kernel(); mutex_lock(&ctl_mutex); pd = pkt_find_dev_from_minor(MINOR(bdev->bd_dev)); if (!pd) { @@ -2410,6 +2411,7 @@ static int pkt_open(struct block_device *bdev, fmode_t mode) } mutex_unlock(&ctl_mutex); + unlock_kernel(); return 0; out_dec: @@ -2417,6 +2419,7 @@ out_dec: out: VPRINTK(DRIVER_NAME": failed open (%d)\n", ret); mutex_unlock(&ctl_mutex); + unlock_kernel(); return ret; } @@ -2425,6 +2428,7 @@ static int pkt_close(struct gendisk *disk, fmode_t mode) struct pktcdvd_device *pd = disk->private_data; int ret = 0; + lock_kernel(); mutex_lock(&ctl_mutex); pd->refcnt--; BUG_ON(pd->refcnt < 0); @@ -2433,6 +2437,7 @@ static int pkt_close(struct gendisk *disk, fmode_t mode) pkt_release_dev(pd, flush); } mutex_unlock(&ctl_mutex); + unlock_kernel(); return ret; } diff --git a/drivers/block/swim.c b/drivers/block/swim.c index f04f74e3758f..2e46815876df 100644 --- a/drivers/block/swim.c +++ b/drivers/block/swim.c @@ -662,11 +662,23 @@ out: return err; } +static int floppy_unlocked_open(struct block_device *bdev, fmode_t mode) +{ + int ret; + + lock_kernel(); + ret = floppy_open(bdev, mode); + unlock_kernel(); + + return ret; +} + static int floppy_release(struct gendisk *disk, fmode_t mode) { struct floppy_state *fs = disk->private_data; struct swim __iomem *base = fs->swd->base; + lock_kernel(); if (fs->ref_count < 0) fs->ref_count = 0; else if (fs->ref_count > 0) @@ -674,6 +686,7 @@ static int floppy_release(struct gendisk *disk, fmode_t mode) if (fs->ref_count == 0) swim_motor(base, OFF); + unlock_kernel(); return 0; } @@ -754,7 +767,7 @@ static int floppy_revalidate(struct gendisk *disk) static const struct block_device_operations floppy_fops = { .owner = THIS_MODULE, - .open = floppy_open, + .open = floppy_unlocked_open, .release = floppy_release, .ioctl = floppy_ioctl, .getgeo = floppy_getgeo, diff --git a/drivers/block/swim3.c b/drivers/block/swim3.c index f3657b2a5386..cc6a3864822c 100644 --- a/drivers/block/swim3.c +++ b/drivers/block/swim3.c @@ -949,15 +949,28 @@ static int floppy_open(struct block_device *bdev, fmode_t mode) return 0; } +static int floppy_unlocked_open(struct block_device *bdev, fmode_t mode) +{ + int ret; + + lock_kernel(); + ret = floppy_open(bdev, mode); + unlock_kernel(); + + return ret; +} + static int floppy_release(struct gendisk *disk, fmode_t mode) { struct floppy_state *fs = disk->private_data; struct swim3 __iomem *sw = fs->swim3; + lock_kernel(); if (fs->ref_count > 0 && --fs->ref_count == 0) { swim3_action(fs, MOTOR_OFF); out_8(&sw->control_bic, 0xff); swim3_select(fs, RELAX); } + unlock_kernel(); return 0; } @@ -1008,7 +1021,7 @@ static int floppy_revalidate(struct gendisk *disk) } static const struct block_device_operations floppy_fops = { - .open = floppy_open, + .open = floppy_unlocked_open, .release = floppy_release, .ioctl = floppy_ioctl, .media_changed = floppy_check_change, diff --git a/drivers/block/ub.c b/drivers/block/ub.c index 102ed52d0e0f..c48e14878582 100644 --- a/drivers/block/ub.c +++ b/drivers/block/ub.c @@ -1711,6 +1711,18 @@ err_open: return rc; } +static int ub_bd_unlocked_open(struct block_device *bdev, fmode_t mode) +{ + int ret; + + lock_kernel(); + ret = ub_bd_open(bdev, mode); + unlock_kernel(); + + return ret; +} + + /* */ static int ub_bd_release(struct gendisk *disk, fmode_t mode) @@ -1718,7 +1730,10 @@ static int ub_bd_release(struct gendisk *disk, fmode_t mode) struct ub_lun *lun = disk->private_data; struct ub_dev *sc = lun->udev; + lock_kernel(); ub_put(sc); + unlock_kernel(); + return 0; } @@ -1798,7 +1813,7 @@ static int ub_bd_media_changed(struct gendisk *disk) static const struct block_device_operations ub_bd_fops = { .owner = THIS_MODULE, - .open = ub_bd_open, + .open = ub_bd_unlocked_open, .release = ub_bd_release, .ioctl = ub_bd_ioctl, .media_changed = ub_bd_media_changed, diff --git a/drivers/block/viodasd.c b/drivers/block/viodasd.c index 5663d3c284c8..f651e51a3319 100644 --- a/drivers/block/viodasd.c +++ b/drivers/block/viodasd.c @@ -41,6 +41,7 @@ #include #include #include +#include #include #include #include @@ -175,6 +176,18 @@ static int viodasd_open(struct block_device *bdev, fmode_t mode) return 0; } +static int viodasd_unlocked_open(struct block_device *bdev, fmode_t mode) +{ + int ret; + + lock_kernel(); + ret = viodasd_open(bdev, mode); + unlock_kernel(); + + return ret; +} + + /* * External release entry point. */ @@ -183,6 +196,7 @@ static int viodasd_release(struct gendisk *disk, fmode_t mode) struct viodasd_device *d = disk->private_data; HvLpEvent_Rc hvrc; + lock_kernel(); /* Send the event to OS/400. We DON'T expect a response */ hvrc = HvCallEvent_signalLpEventFast(viopath_hostLp, HvLpEvent_Type_VirtualIo, @@ -195,6 +209,9 @@ static int viodasd_release(struct gendisk *disk, fmode_t mode) 0, 0, 0); if (hvrc != 0) pr_warning("HV close call failed %d\n", (int)hvrc); + + unlock_kernel(); + return 0; } @@ -219,7 +236,7 @@ static int viodasd_getgeo(struct block_device *bdev, struct hd_geometry *geo) */ static const struct block_device_operations viodasd_fops = { .owner = THIS_MODULE, - .open = viodasd_open, + .open = viodasd_unlocked_open, .release = viodasd_release, .getgeo = viodasd_getgeo, }; diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c index 9119cd3d56a4..91374282755d 100644 --- a/drivers/block/xen-blkfront.c +++ b/drivers/block/xen-blkfront.c @@ -41,6 +41,7 @@ #include #include #include +#include #include #include @@ -1018,13 +1019,18 @@ static int blkfront_is_ready(struct xenbus_device *dev) static int blkif_open(struct block_device *bdev, fmode_t mode) { struct blkfront_info *info = bdev->bd_disk->private_data; + + lock_kernel(); info->users++; + unlock_kernel(); + return 0; } static int blkif_release(struct gendisk *disk, fmode_t mode) { struct blkfront_info *info = disk->private_data; + lock_kernel(); info->users--; if (info->users == 0) { /* Check whether we have been instructed to close. We will @@ -1036,6 +1042,7 @@ static int blkif_release(struct gendisk *disk, fmode_t mode) if (state == XenbusStateClosing && info->is_ready) blkfront_closing(dev); } + unlock_kernel(); return 0; } diff --git a/drivers/block/xsysace.c b/drivers/block/xsysace.c index ac278ac908d5..b71888b909a0 100644 --- a/drivers/block/xsysace.c +++ b/drivers/block/xsysace.c @@ -89,6 +89,7 @@ #include #include #include +#include #include #include #include @@ -901,11 +902,14 @@ static int ace_open(struct block_device *bdev, fmode_t mode) dev_dbg(ace->dev, "ace_open() users=%i\n", ace->users + 1); + lock_kernel(); spin_lock_irqsave(&ace->lock, flags); ace->users++; spin_unlock_irqrestore(&ace->lock, flags); check_disk_change(bdev); + unlock_kernel(); + return 0; } @@ -917,6 +921,7 @@ static int ace_release(struct gendisk *disk, fmode_t mode) dev_dbg(ace->dev, "ace_release() users=%i\n", ace->users - 1); + lock_kernel(); spin_lock_irqsave(&ace->lock, flags); ace->users--; if (ace->users == 0) { @@ -924,6 +929,7 @@ static int ace_release(struct gendisk *disk, fmode_t mode) ace_out(ace, ACE_CTRL, val & ~ACE_CTRL_LOCKREQ); } spin_unlock_irqrestore(&ace->lock, flags); + unlock_kernel(); return 0; } diff --git a/drivers/block/z2ram.c b/drivers/block/z2ram.c index 9114654b54d9..d75b2bb601ad 100644 --- a/drivers/block/z2ram.c +++ b/drivers/block/z2ram.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -153,6 +154,7 @@ static int z2_open(struct block_device *bdev, fmode_t mode) device = MINOR(bdev->bd_dev); + lock_kernel(); if ( current_device != -1 && current_device != device ) { rc = -EBUSY; @@ -294,20 +296,25 @@ static int z2_open(struct block_device *bdev, fmode_t mode) set_capacity(z2ram_gendisk, z2ram_size >> 9); } + unlock_kernel(); return 0; err_out_kfree: kfree(z2ram_map); err_out: + unlock_kernel(); return rc; } static int z2_release(struct gendisk *disk, fmode_t mode) { - if ( current_device == -1 ) - return 0; - + lock_kernel(); + if ( current_device == -1 ) { + unlock_kernel(); + return 0; + } + unlock_kernel(); /* * FIXME: unmap memory */ diff --git a/drivers/cdrom/gdrom.c b/drivers/cdrom/gdrom.c index 1772fd914fb9..261107d1457c 100644 --- a/drivers/cdrom/gdrom.c +++ b/drivers/cdrom/gdrom.c @@ -493,12 +493,18 @@ static struct cdrom_device_ops gdrom_ops = { static int gdrom_bdops_open(struct block_device *bdev, fmode_t mode) { - return cdrom_open(gd.cd_info, bdev, mode); + int ret; + lock_kernel(); + ret = cdrom_open(gd.cd_info, bdev, mode); + unlock_kernel(); + return ret; } static int gdrom_bdops_release(struct gendisk *disk, fmode_t mode) { + lock_kernel(); cdrom_release(gd.cd_info, mode); + unlock_kernel(); return 0; } diff --git a/drivers/cdrom/viocd.c b/drivers/cdrom/viocd.c index 16dada0627ee..56bf9f44700c 100644 --- a/drivers/cdrom/viocd.c +++ b/drivers/cdrom/viocd.c @@ -154,13 +154,21 @@ static const struct file_operations proc_viocd_operations = { static int viocd_blk_open(struct block_device *bdev, fmode_t mode) { struct disk_info *di = bdev->bd_disk->private_data; - return cdrom_open(&di->viocd_info, bdev, mode); + int ret; + + lock_kernel(); + ret = cdrom_open(&di->viocd_info, bdev, mode); + unlock_kernel(); + + return ret; } static int viocd_blk_release(struct gendisk *disk, fmode_t mode) { struct disk_info *di = disk->private_data; + lock_kernel(); cdrom_release(&di->viocd_info, mode); + unlock_kernel(); return 0; } diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index bf9f61a5c2f8..5108e9739c96 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -1591,17 +1591,19 @@ static struct ide_driver ide_cdrom_driver = { static int idecd_open(struct block_device *bdev, fmode_t mode) { - struct cdrom_info *info = ide_cd_get(bdev->bd_disk); - int rc = -ENOMEM; + struct cdrom_info *info; + int rc = -ENXIO; + lock_kernel(); + info = ide_cd_get(bdev->bd_disk); if (!info) - return -ENXIO; + goto out; rc = cdrom_open(&info->devinfo, bdev, mode); - if (rc < 0) ide_cd_put(info); - +out: + unlock_kernel(); return rc; } @@ -1609,9 +1611,11 @@ static int idecd_release(struct gendisk *disk, fmode_t mode) { struct cdrom_info *info = ide_drv_g(disk, cdrom_info); + lock_kernel(); cdrom_release(&info->devinfo, mode); ide_cd_put(info); + unlock_kernel(); return 0; } diff --git a/drivers/ide/ide-gd.c b/drivers/ide/ide-gd.c index 883f0c979c9f..137337a795a9 100644 --- a/drivers/ide/ide-gd.c +++ b/drivers/ide/ide-gd.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -237,6 +238,18 @@ out_put_idkp: return ret; } +static int ide_gd_unlocked_open(struct block_device *bdev, fmode_t mode) +{ + int ret; + + lock_kernel(); + ret = ide_gd_open(bdev, mode); + unlock_kernel(); + + return ret; +} + + static int ide_gd_release(struct gendisk *disk, fmode_t mode) { struct ide_disk_obj *idkp = ide_drv_g(disk, ide_disk_obj); @@ -244,6 +257,7 @@ static int ide_gd_release(struct gendisk *disk, fmode_t mode) ide_debug_log(IDE_DBG_FUNC, "enter"); + lock_kernel(); if (idkp->openers == 1) drive->disk_ops->flush(drive); @@ -255,6 +269,7 @@ static int ide_gd_release(struct gendisk *disk, fmode_t mode) idkp->openers--; ide_disk_put(idkp); + unlock_kernel(); return 0; } @@ -321,7 +336,7 @@ static int ide_gd_ioctl(struct block_device *bdev, fmode_t mode, static const struct block_device_operations ide_gd_ops = { .owner = THIS_MODULE, - .open = ide_gd_open, + .open = ide_gd_unlocked_open, .release = ide_gd_release, .ioctl = ide_gd_ioctl, .getgeo = ide_gd_getgeo, diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index 39b0a5c45f07..6d622cb5ac81 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -1907,7 +1907,11 @@ static const struct file_operations idetape_fops = { static int idetape_open(struct block_device *bdev, fmode_t mode) { - struct ide_tape_obj *tape = ide_tape_get(bdev->bd_disk, false, 0); + struct ide_tape_obj *tape; + + lock_kernel(); + tape = ide_tape_get(bdev->bd_disk, false, 0); + unlock_kernel(); if (!tape) return -ENXIO; @@ -1919,7 +1923,10 @@ static int idetape_release(struct gendisk *disk, fmode_t mode) { struct ide_tape_obj *tape = ide_drv_g(disk, ide_tape_obj); + lock_kernel(); ide_tape_put(tape); + unlock_kernel(); + return 0; } diff --git a/drivers/md/dm.c b/drivers/md/dm.c index d505a96845c1..a3f21dc02bd8 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -338,6 +339,7 @@ static int dm_blk_open(struct block_device *bdev, fmode_t mode) { struct mapped_device *md; + lock_kernel(); spin_lock(&_minor_lock); md = bdev->bd_disk->private_data; @@ -355,6 +357,7 @@ static int dm_blk_open(struct block_device *bdev, fmode_t mode) out: spin_unlock(&_minor_lock); + unlock_kernel(); return md ? 0 : -ENXIO; } @@ -362,8 +365,12 @@ out: static int dm_blk_close(struct gendisk *disk, fmode_t mode) { struct mapped_device *md = disk->private_data; + + lock_kernel(); atomic_dec(&md->open_count); dm_put(md); + unlock_kernel(); + return 0; } diff --git a/drivers/md/md.c b/drivers/md/md.c index 1893af678779..700c96edf9b2 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -36,6 +36,7 @@ #include #include #include +#include #include /* for invalidate_bdev */ #include #include @@ -5902,6 +5903,7 @@ static int md_open(struct block_device *bdev, fmode_t mode) mddev_t *mddev = mddev_find(bdev->bd_dev); int err; + lock_kernel(); if (mddev->gendisk != bdev->bd_disk) { /* we are racing with mddev_put which is discarding this * bd_disk. @@ -5910,6 +5912,7 @@ static int md_open(struct block_device *bdev, fmode_t mode) /* Wait until bdev->bd_disk is definitely gone */ flush_scheduled_work(); /* Then retry the open from the top */ + unlock_kernel(); return -ERESTARTSYS; } BUG_ON(mddev != bdev->bd_disk->private_data); @@ -5923,6 +5926,7 @@ static int md_open(struct block_device *bdev, fmode_t mode) check_disk_size_change(mddev->gendisk, bdev); out: + unlock_kernel(); return err; } @@ -5931,8 +5935,10 @@ static int md_release(struct gendisk *disk, fmode_t mode) mddev_t *mddev = disk->private_data; BUG_ON(!mddev); + lock_kernel(); atomic_dec(&mddev->openers); mddev_put(mddev); + unlock_kernel(); return 0; } diff --git a/drivers/memstick/core/mspro_block.c b/drivers/memstick/core/mspro_block.c index 56645408d225..eef78a068fd1 100644 --- a/drivers/memstick/core/mspro_block.c +++ b/drivers/memstick/core/mspro_block.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #define DRIVER_NAME "mspro_block" @@ -179,6 +180,7 @@ static int mspro_block_bd_open(struct block_device *bdev, fmode_t mode) struct mspro_block_data *msb = disk->private_data; int rc = -ENXIO; + lock_kernel(); mutex_lock(&mspro_block_disk_lock); if (msb && msb->card) { @@ -190,6 +192,7 @@ static int mspro_block_bd_open(struct block_device *bdev, fmode_t mode) } mutex_unlock(&mspro_block_disk_lock); + unlock_kernel(); return rc; } @@ -221,7 +224,11 @@ static int mspro_block_disk_release(struct gendisk *disk) static int mspro_block_bd_release(struct gendisk *disk, fmode_t mode) { - return mspro_block_disk_release(disk); + int ret; + lock_kernel(); + ret = mspro_block_disk_release(disk); + unlock_kernel(); + return ret; } static int mspro_block_bd_getgeo(struct block_device *bdev, diff --git a/drivers/message/i2o/i2o_block.c b/drivers/message/i2o/i2o_block.c index d1bdf8abe5db..a5bc3ee0d93e 100644 --- a/drivers/message/i2o/i2o_block.c +++ b/drivers/message/i2o/i2o_block.c @@ -53,6 +53,7 @@ #include #include #include +#include #include @@ -577,6 +578,7 @@ static int i2o_block_open(struct block_device *bdev, fmode_t mode) if (!dev->i2o_dev) return -ENODEV; + lock_kernel(); if (dev->power > 0x1f) i2o_block_device_power(dev, 0x02); @@ -585,6 +587,7 @@ static int i2o_block_open(struct block_device *bdev, fmode_t mode) i2o_block_device_lock(dev->i2o_dev, -1); osm_debug("Ready.\n"); + unlock_kernel(); return 0; }; @@ -615,6 +618,7 @@ static int i2o_block_release(struct gendisk *disk, fmode_t mode) if (!dev->i2o_dev) return 0; + lock_kernel(); i2o_block_device_flush(dev->i2o_dev); i2o_block_device_unlock(dev->i2o_dev, -1); @@ -625,6 +629,7 @@ static int i2o_block_release(struct gendisk *disk, fmode_t mode) operation = 0x24; i2o_block_device_power(dev, operation); + unlock_kernel(); return 0; } diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index cb9fbc83b090..8433cde29c8b 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -107,6 +108,7 @@ static int mmc_blk_open(struct block_device *bdev, fmode_t mode) struct mmc_blk_data *md = mmc_blk_get(bdev->bd_disk); int ret = -ENXIO; + lock_kernel(); if (md) { if (md->usage == 2) check_disk_change(bdev); @@ -117,6 +119,7 @@ static int mmc_blk_open(struct block_device *bdev, fmode_t mode) ret = -EROFS; } } + unlock_kernel(); return ret; } @@ -125,7 +128,9 @@ static int mmc_blk_release(struct gendisk *disk, fmode_t mode) { struct mmc_blk_data *md = disk->private_data; + lock_kernel(); mmc_blk_put(md); + unlock_kernel(); return 0; } diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c index 8c83b11a77d5..5ca80aee2ed0 100644 --- a/drivers/mtd/mtd_blkdevs.c +++ b/drivers/mtd/mtd_blkdevs.c @@ -165,8 +165,9 @@ static int blktrans_open(struct block_device *bdev, fmode_t mode) int ret; if (!dev) - return -ERESTARTSYS; + return -ERESTARTSYS; /* FIXME: busy loop! -arnd*/ + lock_kernel(); mutex_lock(&dev->lock); if (!dev->mtd) { @@ -183,6 +184,7 @@ static int blktrans_open(struct block_device *bdev, fmode_t mode) unlock: mutex_unlock(&dev->lock); blktrans_dev_put(dev); + unlock_kernel(); return ret; } @@ -194,6 +196,7 @@ static int blktrans_release(struct gendisk *disk, fmode_t mode) if (!dev) return ret; + lock_kernel(); mutex_lock(&dev->lock); /* Release one reference, we sure its not the last one here*/ @@ -206,6 +209,7 @@ static int blktrans_release(struct gendisk *disk, fmode_t mode) unlock: mutex_unlock(&dev->lock); blktrans_dev_put(dev); + unlock_kernel(); return ret; } diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index 17b033d0e050..1a84fae155e1 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -2235,6 +2236,7 @@ static int dasd_open(struct block_device *bdev, fmode_t mode) if (!block) return -ENODEV; + lock_kernel(); base = block->base; atomic_inc(&block->open_count); if (test_bit(DASD_FLAG_OFFLINE, &base->flags)) { @@ -2269,12 +2271,14 @@ static int dasd_open(struct block_device *bdev, fmode_t mode) goto out; } + unlock_kernel(); return 0; out: module_put(base->discipline->owner); unlock: atomic_dec(&block->open_count); + unlock_kernel(); return rc; } @@ -2282,8 +2286,10 @@ static int dasd_release(struct gendisk *disk, fmode_t mode) { struct dasd_block *block = disk->private_data; + lock_kernel(); atomic_dec(&block->open_count); module_put(block->base->discipline->owner); + unlock_kernel(); return 0; } diff --git a/drivers/s390/block/dcssblk.c b/drivers/s390/block/dcssblk.c index 9b43ae94beba..2bd72aa34c59 100644 --- a/drivers/s390/block/dcssblk.c +++ b/drivers/s390/block/dcssblk.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -775,6 +776,7 @@ dcssblk_open(struct block_device *bdev, fmode_t mode) struct dcssblk_dev_info *dev_info; int rc; + lock_kernel(); dev_info = bdev->bd_disk->private_data; if (NULL == dev_info) { rc = -ENODEV; @@ -784,6 +786,7 @@ dcssblk_open(struct block_device *bdev, fmode_t mode) bdev->bd_block_size = 4096; rc = 0; out: + unlock_kernel(); return rc; } @@ -794,6 +797,7 @@ dcssblk_release(struct gendisk *disk, fmode_t mode) struct segment_info *entry; int rc; + lock_kernel(); if (!dev_info) { rc = -ENODEV; goto out; @@ -811,6 +815,7 @@ dcssblk_release(struct gendisk *disk, fmode_t mode) up_write(&dcssblk_devices_sem); rc = 0; out: + unlock_kernel(); return rc; } diff --git a/drivers/s390/char/tape_block.c b/drivers/s390/char/tape_block.c index 097da8ce6be6..b7de02525ec9 100644 --- a/drivers/s390/char/tape_block.c +++ b/drivers/s390/char/tape_block.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -361,6 +362,7 @@ tapeblock_open(struct block_device *bdev, fmode_t mode) struct tape_device * device; int rc; + lock_kernel(); device = tape_get_device(disk->private_data); if (device->required_tapemarks) { @@ -384,12 +386,14 @@ tapeblock_open(struct block_device *bdev, fmode_t mode) * is called. */ tape_state_set(device, TS_BLKUSE); + unlock_kernel(); return 0; release: tape_release(device); put_device: tape_put_device(device); + unlock_kernel(); return rc; } @@ -403,10 +407,12 @@ static int tapeblock_release(struct gendisk *disk, fmode_t mode) { struct tape_device *device = disk->private_data; - + + lock_kernel(); tape_state_set(device, TS_IN_USE); tape_release(device); tape_put_device(device); + unlock_kernel(); return 0; } diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 633ac32b25c1..01680c7c8507 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -795,6 +795,7 @@ static int sd_open(struct block_device *bdev, fmode_t mode) SCSI_LOG_HLQUEUE(3, sd_printk(KERN_INFO, sdkp, "sd_open\n")); + lock_kernel(); sdev = sdkp->device; /* @@ -838,10 +839,12 @@ static int sd_open(struct block_device *bdev, fmode_t mode) scsi_set_medium_removal(sdev, SCSI_REMOVAL_PREVENT); } + unlock_kernel(); return 0; error_out: scsi_disk_put(sdkp); + unlock_kernel(); return retval; } @@ -863,6 +866,7 @@ static int sd_release(struct gendisk *disk, fmode_t mode) SCSI_LOG_HLQUEUE(3, sd_printk(KERN_INFO, sdkp, "sd_release\n")); + lock_kernel(); if (!--sdkp->openers && sdev->removable) { if (scsi_block_when_processing_errors(sdev)) scsi_set_medium_removal(sdev, SCSI_REMOVAL_ALLOW); @@ -873,6 +877,7 @@ static int sd_release(struct gendisk *disk, fmode_t mode) * XXX is followed by a "rmmod sd_mod"? */ scsi_disk_put(sdkp); + unlock_kernel(); return 0; } diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c index d42fa6468f41..ba9c3e0387ce 100644 --- a/drivers/scsi/sr.c +++ b/drivers/scsi/sr.c @@ -467,22 +467,27 @@ static int sr_prep_fn(struct request_queue *q, struct request *rq) static int sr_block_open(struct block_device *bdev, fmode_t mode) { - struct scsi_cd *cd = scsi_cd_get(bdev->bd_disk); + struct scsi_cd *cd; int ret = -ENXIO; + lock_kernel(); + cd = scsi_cd_get(bdev->bd_disk); if (cd) { ret = cdrom_open(&cd->cdi, bdev, mode); if (ret) scsi_cd_put(cd); } + unlock_kernel(); return ret; } static int sr_block_release(struct gendisk *disk, fmode_t mode) { struct scsi_cd *cd = scsi_cd(disk); + lock_kernel(); cdrom_release(&cd->cdi, mode); scsi_cd_put(cd); + unlock_kernel(); return 0; } diff --git a/drivers/staging/hv/blkvsc_drv.c b/drivers/staging/hv/blkvsc_drv.c index a9aff90e58e0..87a11c9293e9 100644 --- a/drivers/staging/hv/blkvsc_drv.c +++ b/drivers/staging/hv/blkvsc_drv.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -1326,6 +1327,7 @@ static int blkvsc_open(struct block_device *bdev, fmode_t mode) DPRINT_DBG(BLKVSC_DRV, "- users %d disk %s\n", blkdev->users, blkdev->gd->disk_name); + lock_kernel(); spin_lock(&blkdev->lock); if (!blkdev->users && blkdev->device_type == DVD_TYPE) { @@ -1337,6 +1339,7 @@ static int blkvsc_open(struct block_device *bdev, fmode_t mode) blkdev->users++; spin_unlock(&blkdev->lock); + unlock_kernel(); return 0; } @@ -1347,6 +1350,7 @@ static int blkvsc_release(struct gendisk *disk, fmode_t mode) DPRINT_DBG(BLKVSC_DRV, "- users %d disk %s\n", blkdev->users, blkdev->gd->disk_name); + lock_kernel(); spin_lock(&blkdev->lock); if (blkdev->users == 1) { spin_unlock(&blkdev->lock); @@ -1357,6 +1361,7 @@ static int blkvsc_release(struct gendisk *disk, fmode_t mode) blkdev->users--; spin_unlock(&blkdev->lock); + unlock_kernel(); return 0; } diff --git a/fs/block_dev.c b/fs/block_dev.c index 99d6af811747..693c2bf5d65e 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -1345,13 +1345,12 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part) return ret; } - lock_kernel(); restart: ret = -ENXIO; disk = get_gendisk(bdev->bd_dev, &partno); if (!disk) - goto out_unlock_kernel; + goto out; mutex_lock_nested(&bdev->bd_mutex, for_part); if (!bdev->bd_openers) { @@ -1431,7 +1430,6 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part) if (for_part) bdev->bd_part_count++; mutex_unlock(&bdev->bd_mutex); - unlock_kernel(); return 0; out_clear: @@ -1444,9 +1442,7 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part) bdev->bd_contains = NULL; out_unlock_bdev: mutex_unlock(&bdev->bd_mutex); - out_unlock_kernel: - unlock_kernel(); - + out: if (disk) module_put(disk->fops->owner); put_disk(disk); @@ -1515,7 +1511,6 @@ static int __blkdev_put(struct block_device *bdev, fmode_t mode, int for_part) struct block_device *victim = NULL; mutex_lock_nested(&bdev->bd_mutex, for_part); - lock_kernel(); if (for_part) bdev->bd_part_count--; @@ -1540,7 +1535,6 @@ static int __blkdev_put(struct block_device *bdev, fmode_t mode, int for_part) victim = bdev->bd_contains; bdev->bd_contains = NULL; } - unlock_kernel(); mutex_unlock(&bdev->bd_mutex); bdput(bdev); if (victim) -- cgit v1.2.3 From 6965031d331a642e31278fa1b5bd47f372ffdd5d Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Tue, 3 Aug 2010 12:48:50 +0200 Subject: splice: fix misuse of SPLICE_F_NONBLOCK SPLICE_F_NONBLOCK is clearly documented to only affect blocking on the pipe. In __generic_file_splice_read(), however, it causes an EAGAIN if the page is currently being read. This makes it impossible to write an application that only wants failure if the pipe is full. For example if the same process is handling both ends of a pipe and isn't otherwise able to determine whether a splice to the pipe will fill it or not. We could make the read non-blocking on O_NONBLOCK or some other splice flag, but for now this is the simplest fix. Signed-off-by: Miklos Szeredi CC: stable@kernel.org Signed-off-by: Jens Axboe --- fs/splice.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) (limited to 'fs') diff --git a/fs/splice.c b/fs/splice.c index ec11c52d646d..8f1dfaecc8f0 100644 --- a/fs/splice.c +++ b/fs/splice.c @@ -399,17 +399,7 @@ __generic_file_splice_read(struct file *in, loff_t *ppos, * If the page isn't uptodate, we may need to start io on it */ if (!PageUptodate(page)) { - /* - * If in nonblock mode then dont block on waiting - * for an in-flight io page - */ - if (flags & SPLICE_F_NONBLOCK) { - if (!trylock_page(page)) { - error = -EAGAIN; - break; - } - } else - lock_page(page); + lock_page(page); /* * Page was truncated, or invalidated by the -- cgit v1.2.3 From 08852b6d6c40f387f2b75e199e2ca1df68970f4c Mon Sep 17 00:00:00 2001 From: Minchan Kim Date: Tue, 3 Aug 2010 12:51:16 +0200 Subject: writeback: remove wb in get_next_work_item 83ba7b07 cleans up the writeback. So we don't use wb any more in get_next_work_item. Let's remove unnecessary argument. CC: Christoph Hellwig Signed-off-by: Minchan Kim Signed-off-by: Jens Axboe --- fs/fs-writeback.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index bf10cbf379dd..261570deb22c 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -704,7 +704,7 @@ static long wb_writeback(struct bdi_writeback *wb, * Return the next wb_writeback_work struct that hasn't been processed yet. */ static struct wb_writeback_work * -get_next_work_item(struct backing_dev_info *bdi, struct bdi_writeback *wb) +get_next_work_item(struct backing_dev_info *bdi) { struct wb_writeback_work *work = NULL; @@ -762,7 +762,7 @@ long wb_do_writeback(struct bdi_writeback *wb, int force_wait) struct wb_writeback_work *work; long wrote = 0; - while ((work = get_next_work_item(bdi, wb)) != NULL) { + while ((work = get_next_work_item(bdi)) != NULL) { /* * Override sync mode, in case we must wait for completion * because this thread is exiting now. -- cgit v1.2.3 From 4aeefdc69f7b6f3f287e6fd8d4b213953b9e92d8 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Tue, 3 Aug 2010 13:22:51 +0200 Subject: coda: fixup clash with block layer REQ_* defines CODA should not be using defines in the global name space of that nature, prefix them with CODA_. Signed-off-by: Jens Axboe --- fs/coda/psdev.c | 12 ++++++------ fs/coda/upcall.c | 12 ++++++------ include/linux/coda_psdev.h | 8 ++++---- 3 files changed, 16 insertions(+), 16 deletions(-) (limited to 'fs') diff --git a/fs/coda/psdev.c b/fs/coda/psdev.c index 66b9cf79c5ba..de89645777c7 100644 --- a/fs/coda/psdev.c +++ b/fs/coda/psdev.c @@ -177,7 +177,7 @@ static ssize_t coda_psdev_write(struct file *file, const char __user *buf, nbytes = req->uc_outSize; /* don't have more space! */ } if (copy_from_user(req->uc_data, buf, nbytes)) { - req->uc_flags |= REQ_ABORT; + req->uc_flags |= CODA_REQ_ABORT; wake_up(&req->uc_sleep); retval = -EFAULT; goto out; @@ -254,8 +254,8 @@ static ssize_t coda_psdev_read(struct file * file, char __user * buf, retval = -EFAULT; /* If request was not a signal, enqueue and don't free */ - if (!(req->uc_flags & REQ_ASYNC)) { - req->uc_flags |= REQ_READ; + if (!(req->uc_flags & CODA_REQ_ASYNC)) { + req->uc_flags |= CODA_REQ_READ; list_add_tail(&(req->uc_chain), &vcp->vc_processing); goto out; } @@ -315,19 +315,19 @@ static int coda_psdev_release(struct inode * inode, struct file * file) list_del(&req->uc_chain); /* Async requests need to be freed here */ - if (req->uc_flags & REQ_ASYNC) { + if (req->uc_flags & CODA_REQ_ASYNC) { CODA_FREE(req->uc_data, sizeof(struct coda_in_hdr)); kfree(req); continue; } - req->uc_flags |= REQ_ABORT; + req->uc_flags |= CODA_REQ_ABORT; wake_up(&req->uc_sleep); } list_for_each_entry_safe(req, tmp, &vcp->vc_processing, uc_chain) { list_del(&req->uc_chain); - req->uc_flags |= REQ_ABORT; + req->uc_flags |= CODA_REQ_ABORT; wake_up(&req->uc_sleep); } diff --git a/fs/coda/upcall.c b/fs/coda/upcall.c index f09c5ed76f6c..b8893ab6f9e6 100644 --- a/fs/coda/upcall.c +++ b/fs/coda/upcall.c @@ -604,7 +604,7 @@ static void coda_unblock_signals(sigset_t *old) (((r)->uc_opcode != CODA_CLOSE && \ (r)->uc_opcode != CODA_STORE && \ (r)->uc_opcode != CODA_RELEASE) || \ - (r)->uc_flags & REQ_READ)) + (r)->uc_flags & CODA_REQ_READ)) static inline void coda_waitfor_upcall(struct upc_req *req) { @@ -624,7 +624,7 @@ static inline void coda_waitfor_upcall(struct upc_req *req) set_current_state(TASK_UNINTERRUPTIBLE); /* got a reply */ - if (req->uc_flags & (REQ_WRITE | REQ_ABORT)) + if (req->uc_flags & (CODA_REQ_WRITE | CODA_REQ_ABORT)) break; if (blocked && time_after(jiffies, timeout) && @@ -708,7 +708,7 @@ static int coda_upcall(struct venus_comm *vcp, coda_waitfor_upcall(req); /* Op went through, interrupt or not... */ - if (req->uc_flags & REQ_WRITE) { + if (req->uc_flags & CODA_REQ_WRITE) { out = (union outputArgs *)req->uc_data; /* here we map positive Venus errors to kernel errors */ error = -out->oh.result; @@ -717,13 +717,13 @@ static int coda_upcall(struct venus_comm *vcp, } error = -EINTR; - if ((req->uc_flags & REQ_ABORT) || !signal_pending(current)) { + if ((req->uc_flags & CODA_REQ_ABORT) || !signal_pending(current)) { printk(KERN_WARNING "coda: Unexpected interruption.\n"); goto exit; } /* Interrupted before venus read it. */ - if (!(req->uc_flags & REQ_READ)) + if (!(req->uc_flags & CODA_REQ_READ)) goto exit; /* Venus saw the upcall, make sure we can send interrupt signal */ @@ -747,7 +747,7 @@ static int coda_upcall(struct venus_comm *vcp, sig_inputArgs->ih.opcode = CODA_SIGNAL; sig_inputArgs->ih.unique = req->uc_unique; - sig_req->uc_flags = REQ_ASYNC; + sig_req->uc_flags = CODA_REQ_ASYNC; sig_req->uc_opcode = sig_inputArgs->ih.opcode; sig_req->uc_unique = sig_inputArgs->ih.unique; sig_req->uc_inSize = sizeof(struct coda_in_hdr); diff --git a/include/linux/coda_psdev.h b/include/linux/coda_psdev.h index 8859e2ede9fe..284b520934a0 100644 --- a/include/linux/coda_psdev.h +++ b/include/linux/coda_psdev.h @@ -86,9 +86,9 @@ struct upc_req { wait_queue_head_t uc_sleep; /* process' wait queue */ }; -#define REQ_ASYNC 0x1 -#define REQ_READ 0x2 -#define REQ_WRITE 0x4 -#define REQ_ABORT 0x8 +#define CODA_REQ_ASYNC 0x1 +#define CODA_REQ_READ 0x2 +#define CODA_REQ_WRITE 0x4 +#define CODA_REQ_ABORT 0x8 #endif -- cgit v1.2.3 From 6f904ff0e39ea88f81eb77e8dfb4e1238492f0a8 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Sun, 25 Jul 2010 14:29:11 +0300 Subject: writeback: harmonize writeback threads naming The write-back code mixes words "thread" and "task" for the same things. This is not a big deal, but still an inconsistency. hch: a convention I tend to use and I've seen in various places is to always use _task for the storage of the task_struct pointer, and thread everywhere else. This especially helps with having foo_thread for the actual thread and foo_task for a global variable keeping the task_struct pointer This patch renames: * 'bdi_add_default_flusher_task()' -> 'bdi_add_default_flusher_thread()' * 'bdi_forker_task()' -> 'bdi_forker_thread()' because bdi threads are 'bdi_writeback_thread()', so these names are more consistent. This patch also amends commentaries and makes them refer the forker and bdi threads as "thread", not "task". Also, while on it, make 'bdi_add_default_flusher_thread()' declaration use 'static void' instead of 'void static' and make checkpatch.pl happy. Signed-off-by: Artem Bityutskiy Reviewed-by: Christoph Hellwig Signed-off-by: Jens Axboe --- fs/fs-writeback.c | 2 +- include/linux/backing-dev.h | 2 +- mm/backing-dev.c | 26 +++++++++++++------------- 3 files changed, 15 insertions(+), 15 deletions(-) (limited to 'fs') diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 261570deb22c..002be0ff2ab3 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -840,7 +840,7 @@ int bdi_writeback_thread(void *data) /* * Longest period of inactivity that we tolerate. If we - * see dirty data again later, the task will get + * see dirty data again later, the thread will get * recreated automatically. */ max_idle = max(5UL * 60 * HZ, wait_jiffies); diff --git a/include/linux/backing-dev.h b/include/linux/backing-dev.h index e536f3a74e60..f0936f5f85dd 100644 --- a/include/linux/backing-dev.h +++ b/include/linux/backing-dev.h @@ -50,7 +50,7 @@ struct bdi_writeback { unsigned long last_old_flush; /* last old data flush */ - struct task_struct *task; /* writeback task */ + struct task_struct *task; /* writeback thread */ struct list_head b_dirty; /* dirty inodes */ struct list_head b_io; /* parked for writeback */ struct list_head b_more_io; /* parked for more writeback */ diff --git a/mm/backing-dev.c b/mm/backing-dev.c index ac78a3336181..4e9ed2a8521f 100644 --- a/mm/backing-dev.c +++ b/mm/backing-dev.c @@ -50,7 +50,7 @@ static struct timer_list sync_supers_timer; static int bdi_sync_supers(void *); static void sync_supers_timer_fn(unsigned long); -static void bdi_add_default_flusher_task(struct backing_dev_info *bdi); +static void bdi_add_default_flusher_thread(struct backing_dev_info *bdi); #ifdef CONFIG_DEBUG_FS #include @@ -279,10 +279,10 @@ static void bdi_flush_io(struct backing_dev_info *bdi) } /* - * kupdated() used to do this. We cannot do it from the bdi_forker_task() + * kupdated() used to do this. We cannot do it from the bdi_forker_thread() * or we risk deadlocking on ->s_umount. The longer term solution would be * to implement sync_supers_bdi() or similar and simply do it from the - * bdi writeback tasks individually. + * bdi writeback thread individually. */ static int bdi_sync_supers(void *unused) { @@ -318,7 +318,7 @@ static void sync_supers_timer_fn(unsigned long unused) bdi_arm_supers_timer(); } -static int bdi_forker_task(void *ptr) +static int bdi_forker_thread(void *ptr) { struct bdi_writeback *me = ptr; @@ -354,7 +354,7 @@ static int bdi_forker_task(void *ptr) !bdi_has_dirty_io(bdi)) continue; - bdi_add_default_flusher_task(bdi); + bdi_add_default_flusher_thread(bdi); } set_current_state(TASK_INTERRUPTIBLE); @@ -376,7 +376,7 @@ static int bdi_forker_task(void *ptr) /* * This is our real job - check for pending entries in - * bdi_pending_list, and create the tasks that got added + * bdi_pending_list, and create the threads that got added */ bdi = list_entry(bdi_pending_list.next, struct backing_dev_info, bdi_list); @@ -387,7 +387,7 @@ static int bdi_forker_task(void *ptr) wb->task = kthread_run(bdi_writeback_thread, wb, "flush-%s", dev_name(bdi->dev)); /* - * If task creation fails, then readd the bdi to + * If thread creation fails, then readd the bdi to * the pending list and force writeout of the bdi * from this forker thread. That will free some memory * and we can try again. @@ -430,10 +430,10 @@ static void bdi_add_to_pending(struct rcu_head *head) } /* - * Add the default flusher task that gets created for any bdi + * Add the default flusher thread that gets created for any bdi * that has dirty data pending writeout */ -void static bdi_add_default_flusher_task(struct backing_dev_info *bdi) +static void bdi_add_default_flusher_thread(struct backing_dev_info *bdi) { if (!bdi_cap_writeback_dirty(bdi)) return; @@ -445,10 +445,10 @@ void static bdi_add_default_flusher_task(struct backing_dev_info *bdi) } /* - * Check with the helper whether to proceed adding a task. Will only + * Check with the helper whether to proceed adding a thread. Will only * abort if we two or more simultanous calls to - * bdi_add_default_flusher_task() occured, further additions will block - * waiting for previous additions to finish. + * bdi_add_default_flusher_thread() occured, further additions will + * block waiting for previous additions to finish. */ if (!test_and_set_bit(BDI_pending, &bdi->state)) { list_del_rcu(&bdi->bdi_list); @@ -506,7 +506,7 @@ int bdi_register(struct backing_dev_info *bdi, struct device *parent, if (bdi_cap_flush_forker(bdi)) { struct bdi_writeback *wb = &bdi->wb; - wb->task = kthread_run(bdi_forker_task, wb, "bdi-%s", + wb->task = kthread_run(bdi_forker_thread, wb, "bdi-%s", dev_name(dev)); if (IS_ERR(wb->task)) { wb->task = NULL; -- cgit v1.2.3 From 297252c81de8043ca6c36e5984c24fdb5aab9013 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Sun, 25 Jul 2010 14:29:15 +0300 Subject: writeback: do not lose wake-ups in bdi threads Currently, bdi threads ('bdi_writeback_thread()') can lose wake-ups. For example, if 'bdi_queue_work()' is executed after the bdi thread have had finished 'wb_do_writeback()' but before it called 'schedule_timeout_interruptible()'. To fix this issue, we have to check whether we have works to process after we have changed the task state to 'TASK_INTERRUPTIBLE'. This patch also clean-ups handling of the cases when 'dirty_writeback_interval' is zero or non-zero. Additionally, this patch also removes unneeded 'list_empty_careful()' call. Signed-off-by: Artem Bityutskiy Reviewed-by: Christoph Hellwig Signed-off-by: Jens Axboe --- fs/fs-writeback.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'fs') diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 002be0ff2ab3..05444eaa3f36 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -848,17 +848,18 @@ int bdi_writeback_thread(void *data) break; } - if (dirty_writeback_interval) { - wait_jiffies = msecs_to_jiffies(dirty_writeback_interval * 10); - schedule_timeout_interruptible(wait_jiffies); - } else { - set_current_state(TASK_INTERRUPTIBLE); - if (list_empty_careful(&wb->bdi->work_list) && - !kthread_should_stop()) - schedule(); + set_current_state(TASK_INTERRUPTIBLE); + if (!list_empty(&bdi->work_list)) { __set_current_state(TASK_RUNNING); + continue; } + if (dirty_writeback_interval) { + wait_jiffies = msecs_to_jiffies(dirty_writeback_interval * 10); + schedule_timeout(wait_jiffies); + } else + schedule(); + try_to_freeze(); } -- cgit v1.2.3 From 78c40cb6581a74adc48821f3de6b864a54d4c34d Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Sun, 25 Jul 2010 14:29:17 +0300 Subject: writeback: do not remove bdi from bdi_list The forker thread removes bdis from 'bdi_list' before forking the bdi thread. But this is wrong for at least 2 reasons. Reason #1: if we temporary remove a bdi from the list, we may miss works which would otherwise be given to us. Reason #2: this is racy; indeed, 'bdi_wb_shutdown()' expects that bdis are always in the 'bdi_list' (see 'bdi_remove_from_list()'), and when it races with the forker thread, it can shut down the bdi thread at the same time as the forker creates it. This patch makes sure the forker thread never removes bdis from 'bdi_list' (which was suggested by Christoph Hellwig). In order to make sure that we do not race with 'bdi_wb_shutdown()', we have to hold the 'bdi_lock' while walking the 'bdi_list' and setting the 'BDI_pending' flag. NOTE! The error path is interesting. Currently, when we fail to create a bdi thread, we move the bdi to the tail of 'bdi_list'. But if we never remove the bdi from the list, we cannot move it to the tail either, because then we can mess up the RCU readers which walk the list. And also, we'll have the race described above in "Reason #2". But I not think that adding to the tail is any important so I just do not do that. Signed-off-by: Artem Bityutskiy Reviewed-by: Christoph Hellwig Signed-off-by: Jens Axboe --- fs/fs-writeback.c | 7 ------- mm/backing-dev.c | 31 ++++++++++--------------------- 2 files changed, 10 insertions(+), 28 deletions(-) (limited to 'fs') diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 05444eaa3f36..57fbfd0ebc52 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -804,13 +804,6 @@ int bdi_writeback_thread(void *data) unsigned long wait_jiffies = -1UL; long pages_written; - /* - * Add us to the active bdi_list - */ - spin_lock_bh(&bdi_lock); - list_add_rcu(&bdi->bdi_list, &bdi_list); - spin_unlock_bh(&bdi_lock); - current->flags |= PF_FLUSHER | PF_SWAPWRITE; set_freezable(); diff --git a/mm/backing-dev.c b/mm/backing-dev.c index dbc66815a0fe..672c17bb32db 100644 --- a/mm/backing-dev.c +++ b/mm/backing-dev.c @@ -331,7 +331,7 @@ static int bdi_forker_thread(void *ptr) for (;;) { bool fork = false; struct task_struct *task; - struct backing_dev_info *bdi, *tmp; + struct backing_dev_info *bdi; /* * Temporary measure, we want to make sure we don't see @@ -347,7 +347,7 @@ static int bdi_forker_thread(void *ptr) * Check if any existing bdi's have dirty data without * a thread registered. If so, set that up. */ - list_for_each_entry_safe(bdi, tmp, &bdi_list, bdi_list) { + list_for_each_entry(bdi, &bdi_list, bdi_list) { if (!bdi_cap_writeback_dirty(bdi)) continue; if (bdi->wb.task) @@ -359,8 +359,13 @@ static int bdi_forker_thread(void *ptr) WARN(!test_bit(BDI_registered, &bdi->state), "bdi %p/%s is not registered!\n", bdi, bdi->name); - list_del_rcu(&bdi->bdi_list); fork = true; + + /* + * Set the pending bit - if someone will try to + * unregister this bdi - it'll wait on this bit. + */ + set_bit(BDI_pending, &bdi->state); break; } spin_unlock_bh(&bdi_lock); @@ -383,29 +388,13 @@ static int bdi_forker_thread(void *ptr) __set_current_state(TASK_RUNNING); - /* - * Set the pending bit - if someone will try to unregister this - * bdi - it'll wait on this bit. - */ - set_bit(BDI_pending, &bdi->state); - - /* Make sure no one uses the picked bdi */ - synchronize_rcu(); - task = kthread_run(bdi_writeback_thread, &bdi->wb, "flush-%s", dev_name(bdi->dev)); if (IS_ERR(task)) { /* - * If thread creation fails, then readd the bdi back to - * the list and force writeout of the bdi from this - * forker thread. That will free some memory and we can - * try again. Add it to the tail so we get a chance to - * flush other bdi's to free memory. + * If thread creation fails, force writeout of the bdi + * from the thread. */ - spin_lock_bh(&bdi_lock); - list_add_tail_rcu(&bdi->bdi_list, &bdi_list); - spin_unlock_bh(&bdi_lock); - bdi_flush_io(bdi); } else bdi->wb.task = task; -- cgit v1.2.3 From ecd584030da67ede1bf17955746a6ce834d9fc6b Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Sun, 25 Jul 2010 14:29:18 +0300 Subject: writeback: move last_active to bdi Currently bdi threads use local variable 'last_active' which stores last time when the bdi thread did some useful work. Move this local variable to 'struct bdi_writeback'. This is just a preparation for the further patches which will make the forker thread decide when bdi threads should be killed. Signed-off-by: Artem Bityutskiy Reviewed-by: Christoph Hellwig Signed-off-by: Jens Axboe --- fs/fs-writeback.c | 6 +++--- include/linux/backing-dev.h | 13 +++++++------ 2 files changed, 10 insertions(+), 9 deletions(-) (limited to 'fs') diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 57fbfd0ebc52..9f5cab75c157 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -800,12 +800,12 @@ int bdi_writeback_thread(void *data) { struct bdi_writeback *wb = data; struct backing_dev_info *bdi = wb->bdi; - unsigned long last_active = jiffies; unsigned long wait_jiffies = -1UL; long pages_written; current->flags |= PF_FLUSHER | PF_SWAPWRITE; set_freezable(); + wb->last_active = jiffies; /* * Our parent may run at a different priority, just set us to normal @@ -827,7 +827,7 @@ int bdi_writeback_thread(void *data) trace_writeback_pages_written(pages_written); if (pages_written) - last_active = jiffies; + wb->last_active = jiffies; else if (wait_jiffies != -1UL) { unsigned long max_idle; @@ -837,7 +837,7 @@ int bdi_writeback_thread(void *data) * recreated automatically. */ max_idle = max(5UL * 60 * HZ, wait_jiffies); - if (time_after(jiffies, max_idle + last_active)) + if (time_after(jiffies, max_idle + wb->last_active)) break; } diff --git a/include/linux/backing-dev.h b/include/linux/backing-dev.h index 95ecb2bebca8..71b6223e0a77 100644 --- a/include/linux/backing-dev.h +++ b/include/linux/backing-dev.h @@ -45,15 +45,16 @@ enum bdi_stat_item { #define BDI_STAT_BATCH (8*(1+ilog2(nr_cpu_ids))) struct bdi_writeback { - struct backing_dev_info *bdi; /* our parent bdi */ + struct backing_dev_info *bdi; /* our parent bdi */ unsigned int nr; - unsigned long last_old_flush; /* last old data flush */ + unsigned long last_old_flush; /* last old data flush */ + unsigned long last_active; /* last time bdi thread was active */ - struct task_struct *task; /* writeback thread */ - struct list_head b_dirty; /* dirty inodes */ - struct list_head b_io; /* parked for writeback */ - struct list_head b_more_io; /* parked for more writeback */ + struct task_struct *task; /* writeback thread */ + struct list_head b_dirty; /* dirty inodes */ + struct list_head b_io; /* parked for writeback */ + struct list_head b_more_io; /* parked for more writeback */ }; struct backing_dev_info { -- cgit v1.2.3 From fff5b85aa4225a7be157f208277a055822039a9e Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Sun, 25 Jul 2010 14:29:20 +0300 Subject: writeback: move bdi threads exiting logic to the forker thread Currently, bdi threads can decide to exit if there were no useful activities for 5 minutes. However, this causes nasty races: we can easily oops in the 'bdi_queue_work()' if the bdi thread decides to exit while we are waking it up. And even if we do not oops, but the bdi tread exits immediately after we wake it up, we'd lose the wake-up event and have an unnecessary delay (up to 5 secs) in the bdi work processing. This patch makes the forker thread to be the central place which not only creates bdi threads, but also kills them if they were inactive long enough. This better design-wise. Another reason why this change was done is to prepare for the further changes which will prevent the bdi threads from waking up every 5 sec and wasting power. Indeed, when the task does not wake up periodically anymore, it won't be able to exit either. This patch also moves the the 'wake_up_bit()' call from the bdi thread to the forker thread as well. So now the forker thread sets the BDI_pending bit, then forks the task or kills it, then clears the bit and wakes up the waiting process. The only process which may wain on the bit is 'bdi_wb_shutdown()'. This function was changed as well - now it first removes the bdi from the 'bdi_list', then waits on the 'BDI_pending' bit. Once it wakes up, it is guaranteed that the forker thread won't race with it, because the bdi is not visible. Note, the forker thread sets the 'BDI_pending' bit under the 'bdi->wb_lock' which is essential for proper serialization. And additionally, when we change 'bdi->wb.task', we now take the 'bdi->work_lock', to make sure that we do not lose wake-ups which we otherwise would when raced with, say, 'bdi_queue_work()'. Signed-off-by: Artem Bityutskiy Reviewed-by: Christoph Hellwig Signed-off-by: Jens Axboe --- fs/fs-writeback.c | 54 ++++++++++--------------------------------- mm/backing-dev.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 70 insertions(+), 53 deletions(-) (limited to 'fs') diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 9f5cab75c157..905f3ea38488 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -78,21 +78,17 @@ static void bdi_queue_work(struct backing_dev_info *bdi, spin_lock(&bdi->wb_lock); list_add_tail(&work->list, &bdi->work_list); - spin_unlock(&bdi->wb_lock); - - /* - * If the default thread isn't there, make sure we add it. When - * it gets created and wakes up, we'll run this work. - */ - if (unlikely(!bdi->wb.task)) { + if (bdi->wb.task) { + wake_up_process(bdi->wb.task); + } else { + /* + * The bdi thread isn't there, wake up the forker thread which + * will create and run it. + */ trace_writeback_nothread(bdi, work); wake_up_process(default_backing_dev_info.wb.task); - } else { - struct bdi_writeback *wb = &bdi->wb; - - if (wb->task) - wake_up_process(wb->task); } + spin_unlock(&bdi->wb_lock); } static void @@ -800,7 +796,6 @@ int bdi_writeback_thread(void *data) { struct bdi_writeback *wb = data; struct backing_dev_info *bdi = wb->bdi; - unsigned long wait_jiffies = -1UL; long pages_written; current->flags |= PF_FLUSHER | PF_SWAPWRITE; @@ -812,13 +807,6 @@ int bdi_writeback_thread(void *data) */ set_user_nice(current, 0); - /* - * Clear pending bit and wakeup anybody waiting to tear us down - */ - clear_bit(BDI_pending, &bdi->state); - smp_mb__after_clear_bit(); - wake_up_bit(&bdi->state, BDI_pending); - trace_writeback_thread_start(bdi); while (!kthread_should_stop()) { @@ -828,18 +816,6 @@ int bdi_writeback_thread(void *data) if (pages_written) wb->last_active = jiffies; - else if (wait_jiffies != -1UL) { - unsigned long max_idle; - - /* - * Longest period of inactivity that we tolerate. If we - * see dirty data again later, the thread will get - * recreated automatically. - */ - max_idle = max(5UL * 60 * HZ, wait_jiffies); - if (time_after(jiffies, max_idle + wb->last_active)) - break; - } set_current_state(TASK_INTERRUPTIBLE); if (!list_empty(&bdi->work_list)) { @@ -847,21 +823,15 @@ int bdi_writeback_thread(void *data) continue; } - if (dirty_writeback_interval) { - wait_jiffies = msecs_to_jiffies(dirty_writeback_interval * 10); - schedule_timeout(wait_jiffies); - } else + if (dirty_writeback_interval) + schedule_timeout(msecs_to_jiffies(dirty_writeback_interval * 10)); + else schedule(); try_to_freeze(); } - wb->task = NULL; - - /* - * Flush any work that raced with us exiting. No new work - * will be added, since this bdi isn't discoverable anymore. - */ + /* Flush any work that raced with us exiting */ if (!list_empty(&bdi->work_list)) wb_do_writeback(wb, 1); diff --git a/mm/backing-dev.c b/mm/backing-dev.c index e104e32c2ee8..9c1c199f88ce 100644 --- a/mm/backing-dev.c +++ b/mm/backing-dev.c @@ -316,6 +316,18 @@ static void sync_supers_timer_fn(unsigned long unused) bdi_arm_supers_timer(); } +/* + * Calculate the longest interval (jiffies) bdi threads are allowed to be + * inactive. + */ +static unsigned long bdi_longest_inactive(void) +{ + unsigned long interval; + + interval = msecs_to_jiffies(dirty_writeback_interval * 10); + return max(5UL * 60 * HZ, interval); +} + static int bdi_forker_thread(void *ptr) { struct bdi_writeback *me = ptr; @@ -329,11 +341,12 @@ static int bdi_forker_thread(void *ptr) set_user_nice(current, 0); for (;;) { - struct task_struct *task; + struct task_struct *task = NULL; struct backing_dev_info *bdi; enum { NO_ACTION, /* Nothing to do */ FORK_THREAD, /* Fork bdi thread */ + KILL_THREAD, /* Kill inactive bdi thread */ } action = NO_ACTION; /* @@ -346,10 +359,6 @@ static int bdi_forker_thread(void *ptr) spin_lock_bh(&bdi_lock); set_current_state(TASK_INTERRUPTIBLE); - /* - * Check if any existing bdi's have dirty data without - * a thread registered. If so, set that up. - */ list_for_each_entry(bdi, &bdi_list, bdi_list) { bool have_dirty_io; @@ -376,6 +385,25 @@ static int bdi_forker_thread(void *ptr) action = FORK_THREAD; break; } + + spin_lock(&bdi->wb_lock); + /* + * If there is no work to do and the bdi thread was + * inactive long enough - kill it. The wb_lock is taken + * to make sure no-one adds more work to this bdi and + * wakes the bdi thread up. + */ + if (bdi->wb.task && !have_dirty_io && + time_after(jiffies, bdi->wb.last_active + + bdi_longest_inactive())) { + task = bdi->wb.task; + bdi->wb.task = NULL; + spin_unlock(&bdi->wb_lock); + set_bit(BDI_pending, &bdi->state); + action = KILL_THREAD; + break; + } + spin_unlock(&bdi->wb_lock); } spin_unlock_bh(&bdi_lock); @@ -394,8 +422,20 @@ static int bdi_forker_thread(void *ptr) * the bdi from the thread. */ bdi_flush_io(bdi); - } else + } else { + /* + * The spinlock makes sure we do not lose + * wake-ups when racing with 'bdi_queue_work()'. + */ + spin_lock(&bdi->wb_lock); bdi->wb.task = task; + spin_unlock(&bdi->wb_lock); + } + break; + + case KILL_THREAD: + __set_current_state(TASK_RUNNING); + kthread_stop(task); break; case NO_ACTION: @@ -407,6 +447,13 @@ static int bdi_forker_thread(void *ptr) /* Back to the main loop */ continue; } + + /* + * Clear pending bit and wakeup anybody waiting to tear us down. + */ + clear_bit(BDI_pending, &bdi->state); + smp_mb__after_clear_bit(); + wake_up_bit(&bdi->state, BDI_pending); } return 0; @@ -490,15 +537,15 @@ static void bdi_wb_shutdown(struct backing_dev_info *bdi) return; /* - * If setup is pending, wait for that to complete first + * Make sure nobody finds us on the bdi_list anymore */ - wait_on_bit(&bdi->state, BDI_pending, bdi_sched_wait, - TASK_UNINTERRUPTIBLE); + bdi_remove_from_list(bdi); /* - * Make sure nobody finds us on the bdi_list anymore + * If setup is pending, wait for that to complete first */ - bdi_remove_from_list(bdi); + wait_on_bit(&bdi->state, BDI_pending, bdi_sched_wait, + TASK_UNINTERRUPTIBLE); /* * Finally, kill the kernel thread. We don't need to be RCU -- cgit v1.2.3 From 253c34e9b10c30d3064be654b5b78fbc1a8b1896 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Sun, 25 Jul 2010 14:29:21 +0300 Subject: writeback: prevent unnecessary bdi threads wakeups Finally, we can get rid of unnecessary wake-ups in bdi threads, which are very bad for battery-driven devices. There are two types of activities bdi threads do: 1. process bdi works from the 'bdi->work_list' 2. periodic write-back So there are 2 sources of wake-up events for bdi threads: 1. 'bdi_queue_work()' - submits bdi works 2. '__mark_inode_dirty()' - adds dirty I/O to bdi's The former already has bdi wake-up code. The latter does not, and this patch adds it. '__mark_inode_dirty()' is hot-path function, but this patch adds another 'spin_lock(&bdi->wb_lock)' there. However, it is taken only in rare cases when the bdi has no dirty inodes. So adding this spinlock should be fine and should not affect performance. This patch makes sure bdi threads and the forker thread do not wake-up if there is nothing to do. The forker thread will nevertheless wake up at least every 5 min. to check whether it has to kill a bdi thread. This can also be optimized, but is not worth it. This patch also tidies up the warning about unregistered bid, and turns it from an ugly crocodile to a simple 'WARN()' statement. Signed-off-by: Artem Bityutskiy Reviewed-by: Christoph Hellwig Signed-off-by: Jens Axboe --- fs/fs-writeback.c | 59 ++++++++++++++++++++++++++++++++++++++++++++----------- mm/backing-dev.c | 13 +++++++++--- 2 files changed, 58 insertions(+), 14 deletions(-) (limited to 'fs') diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 905f3ea38488..55f6e46e06f1 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -823,10 +823,16 @@ int bdi_writeback_thread(void *data) continue; } - if (dirty_writeback_interval) + if (wb_has_dirty_io(wb) && dirty_writeback_interval) schedule_timeout(msecs_to_jiffies(dirty_writeback_interval * 10)); - else + else { + /* + * We have nothing to do, so can go sleep without any + * timeout and save power. When a work is queued or + * something is made dirty - we will be woken up. + */ schedule(); + } try_to_freeze(); } @@ -862,6 +868,26 @@ void wakeup_flusher_threads(long nr_pages) rcu_read_unlock(); } +/* + * This function is used when the first inode for this bdi is marked dirty. It + * wakes-up the corresponding bdi thread which should then take care of the + * periodic background write-out of dirty inodes. + */ +static void wakeup_bdi_thread(struct backing_dev_info *bdi) +{ + spin_lock(&bdi->wb_lock); + if (bdi->wb.task) + wake_up_process(bdi->wb.task); + else + /* + * When bdi tasks are inactive for long time, they are killed. + * In this case we have to wake-up the forker thread which + * should create and run the bdi thread. + */ + wake_up_process(default_backing_dev_info.wb.task); + spin_unlock(&bdi->wb_lock); +} + static noinline void block_dump___mark_inode_dirty(struct inode *inode) { if (inode->i_ino || strcmp(inode->i_sb->s_id, "bdev")) { @@ -914,6 +940,8 @@ static noinline void block_dump___mark_inode_dirty(struct inode *inode) void __mark_inode_dirty(struct inode *inode, int flags) { struct super_block *sb = inode->i_sb; + struct backing_dev_info *bdi = NULL; + bool wakeup_bdi = false; /* * Don't do this for I_DIRTY_PAGES - that doesn't actually @@ -967,22 +995,31 @@ void __mark_inode_dirty(struct inode *inode, int flags) * reposition it (that would break b_dirty time-ordering). */ if (!was_dirty) { - struct bdi_writeback *wb = &inode_to_bdi(inode)->wb; - struct backing_dev_info *bdi = wb->bdi; - - if (bdi_cap_writeback_dirty(bdi) && - !test_bit(BDI_registered, &bdi->state)) { - WARN_ON(1); - printk(KERN_ERR "bdi-%s not registered\n", - bdi->name); + bdi = inode_to_bdi(inode); + + if (bdi_cap_writeback_dirty(bdi)) { + WARN(!test_bit(BDI_registered, &bdi->state), + "bdi-%s not registered\n", bdi->name); + + /* + * If this is the first dirty inode for this + * bdi, we have to wake-up the corresponding + * bdi thread to make sure background + * write-back happens later. + */ + if (!wb_has_dirty_io(&bdi->wb)) + wakeup_bdi = true; } inode->dirtied_when = jiffies; - list_move(&inode->i_list, &wb->b_dirty); + list_move(&inode->i_list, &bdi->wb.b_dirty); } } out: spin_unlock(&inode_lock); + + if (wakeup_bdi) + wakeup_bdi_thread(bdi); } EXPORT_SYMBOL(__mark_inode_dirty); diff --git a/mm/backing-dev.c b/mm/backing-dev.c index 9c1c199f88ce..a9a08d88a745 100644 --- a/mm/backing-dev.c +++ b/mm/backing-dev.c @@ -439,10 +439,17 @@ static int bdi_forker_thread(void *ptr) break; case NO_ACTION: - if (dirty_writeback_interval) - schedule_timeout(msecs_to_jiffies(dirty_writeback_interval * 10)); + if (!wb_has_dirty_io(me) || !dirty_writeback_interval) + /* + * There are no dirty data. The only thing we + * should now care about is checking for + * inactive bdi threads and killing them. Thus, + * let's sleep for longer time, save energy and + * be friendly for battery-driven devices. + */ + schedule_timeout(bdi_longest_inactive()); else - schedule(); + schedule_timeout(msecs_to_jiffies(dirty_writeback_interval * 10)); try_to_freeze(); /* Back to the main loop */ continue; -- cgit v1.2.3 From 6467716a37673e8d47b4984eb19839bdad0a8353 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Sun, 25 Jul 2010 14:29:22 +0300 Subject: writeback: optimize periodic bdi thread wakeups Whe the first inode for a bdi is marked dirty, we wake up the bdi thread which should take care of the periodic background write-out. However, the write-out will actually start only 'dirty_writeback_interval' centisecs later, so we can delay the wake-up. This change was requested by Nick Piggin who pointed out that if we delay the wake-up, we weed out 2 unnecessary contex switches, which matters because '__mark_inode_dirty()' is a hot-path function. This patch introduces a new function - 'bdi_wakeup_thread_delayed()', which sets up a timer to wake-up the bdi thread and returns. So the wake-up is delayed. We also delete the timer in bdi threads just before writing-back. And synchronously delete it when unregistering bdi. At the unregister point the bdi does not have any users, so no one can arm it again. Since now we take 'bdi->wb_lock' in the timer, which can execute in softirq context, we have to use 'spin_lock_bh()' for 'bdi->wb_lock'. This patch makes this change as well. This patch also moves the 'bdi_wb_init()' function down in the file to avoid forward-declaration of 'bdi_wakeup_thread_delayed()'. Signed-off-by: Artem Bityutskiy Signed-off-by: Jens Axboe --- fs/fs-writeback.c | 36 +++++++--------------- include/linux/backing-dev.h | 2 ++ mm/backing-dev.c | 73 +++++++++++++++++++++++++++++++++++---------- 3 files changed, 70 insertions(+), 41 deletions(-) (limited to 'fs') diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 55f6e46e06f1..bfa2df2c7ce2 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -76,7 +76,7 @@ static void bdi_queue_work(struct backing_dev_info *bdi, { trace_writeback_queue(bdi, work); - spin_lock(&bdi->wb_lock); + spin_lock_bh(&bdi->wb_lock); list_add_tail(&work->list, &bdi->work_list); if (bdi->wb.task) { wake_up_process(bdi->wb.task); @@ -88,7 +88,7 @@ static void bdi_queue_work(struct backing_dev_info *bdi, trace_writeback_nothread(bdi, work); wake_up_process(default_backing_dev_info.wb.task); } - spin_unlock(&bdi->wb_lock); + spin_unlock_bh(&bdi->wb_lock); } static void @@ -704,13 +704,13 @@ get_next_work_item(struct backing_dev_info *bdi) { struct wb_writeback_work *work = NULL; - spin_lock(&bdi->wb_lock); + spin_lock_bh(&bdi->wb_lock); if (!list_empty(&bdi->work_list)) { work = list_entry(bdi->work_list.next, struct wb_writeback_work, list); list_del_init(&work->list); } - spin_unlock(&bdi->wb_lock); + spin_unlock_bh(&bdi->wb_lock); return work; } @@ -810,6 +810,12 @@ int bdi_writeback_thread(void *data) trace_writeback_thread_start(bdi); while (!kthread_should_stop()) { + /* + * Remove own delayed wake-up timer, since we are already awake + * and we'll take care of the preriodic write-back. + */ + del_timer(&wb->wakeup_timer); + pages_written = wb_do_writeback(wb, 0); trace_writeback_pages_written(pages_written); @@ -868,26 +874,6 @@ void wakeup_flusher_threads(long nr_pages) rcu_read_unlock(); } -/* - * This function is used when the first inode for this bdi is marked dirty. It - * wakes-up the corresponding bdi thread which should then take care of the - * periodic background write-out of dirty inodes. - */ -static void wakeup_bdi_thread(struct backing_dev_info *bdi) -{ - spin_lock(&bdi->wb_lock); - if (bdi->wb.task) - wake_up_process(bdi->wb.task); - else - /* - * When bdi tasks are inactive for long time, they are killed. - * In this case we have to wake-up the forker thread which - * should create and run the bdi thread. - */ - wake_up_process(default_backing_dev_info.wb.task); - spin_unlock(&bdi->wb_lock); -} - static noinline void block_dump___mark_inode_dirty(struct inode *inode) { if (inode->i_ino || strcmp(inode->i_sb->s_id, "bdev")) { @@ -1019,7 +1005,7 @@ out: spin_unlock(&inode_lock); if (wakeup_bdi) - wakeup_bdi_thread(bdi); + bdi_wakeup_thread_delayed(bdi); } EXPORT_SYMBOL(__mark_inode_dirty); diff --git a/include/linux/backing-dev.h b/include/linux/backing-dev.h index 71b6223e0a77..7628219e5386 100644 --- a/include/linux/backing-dev.h +++ b/include/linux/backing-dev.h @@ -52,6 +52,7 @@ struct bdi_writeback { unsigned long last_active; /* last time bdi thread was active */ struct task_struct *task; /* writeback thread */ + struct timer_list wakeup_timer; /* used for delayed bdi thread wakeup */ struct list_head b_dirty; /* dirty inodes */ struct list_head b_io; /* parked for writeback */ struct list_head b_more_io; /* parked for more writeback */ @@ -105,6 +106,7 @@ void bdi_start_background_writeback(struct backing_dev_info *bdi); int bdi_writeback_thread(void *data); int bdi_has_dirty_io(struct backing_dev_info *bdi); void bdi_arm_supers_timer(void); +void bdi_wakeup_thread_delayed(struct backing_dev_info *bdi); extern spinlock_t bdi_lock; extern struct list_head bdi_list; diff --git a/mm/backing-dev.c b/mm/backing-dev.c index a9a08d88a745..cfff7225138c 100644 --- a/mm/backing-dev.c +++ b/mm/backing-dev.c @@ -248,17 +248,6 @@ static int __init default_bdi_init(void) } subsys_initcall(default_bdi_init); -static void bdi_wb_init(struct bdi_writeback *wb, struct backing_dev_info *bdi) -{ - memset(wb, 0, sizeof(*wb)); - - wb->bdi = bdi; - wb->last_old_flush = jiffies; - INIT_LIST_HEAD(&wb->b_dirty); - INIT_LIST_HEAD(&wb->b_io); - INIT_LIST_HEAD(&wb->b_more_io); -} - int bdi_has_dirty_io(struct backing_dev_info *bdi) { return wb_has_dirty_io(&bdi->wb); @@ -316,6 +305,43 @@ static void sync_supers_timer_fn(unsigned long unused) bdi_arm_supers_timer(); } +static void wakeup_timer_fn(unsigned long data) +{ + struct backing_dev_info *bdi = (struct backing_dev_info *)data; + + spin_lock_bh(&bdi->wb_lock); + if (bdi->wb.task) { + wake_up_process(bdi->wb.task); + } else { + /* + * When bdi tasks are inactive for long time, they are killed. + * In this case we have to wake-up the forker thread which + * should create and run the bdi thread. + */ + wake_up_process(default_backing_dev_info.wb.task); + } + spin_unlock_bh(&bdi->wb_lock); +} + +/* + * This function is used when the first inode for this bdi is marked dirty. It + * wakes-up the corresponding bdi thread which should then take care of the + * periodic background write-out of dirty inodes. Since the write-out would + * starts only 'dirty_writeback_interval' centisecs from now anyway, we just + * set up a timer which wakes the bdi thread up later. + * + * Note, we wouldn't bother setting up the timer, but this function is on the + * fast-path (used by '__mark_inode_dirty()'), so we save few context switches + * by delaying the wake-up. + */ +void bdi_wakeup_thread_delayed(struct backing_dev_info *bdi) +{ + unsigned long timeout; + + timeout = msecs_to_jiffies(dirty_writeback_interval * 10); + mod_timer(&bdi->wb.wakeup_timer, jiffies + timeout); +} + /* * Calculate the longest interval (jiffies) bdi threads are allowed to be * inactive. @@ -353,8 +379,10 @@ static int bdi_forker_thread(void *ptr) * Temporary measure, we want to make sure we don't see * dirty data on the default backing_dev_info */ - if (wb_has_dirty_io(me) || !list_empty(&me->bdi->work_list)) + if (wb_has_dirty_io(me) || !list_empty(&me->bdi->work_list)) { + del_timer(&me->wakeup_timer); wb_do_writeback(me, 0); + } spin_lock_bh(&bdi_lock); set_current_state(TASK_INTERRUPTIBLE); @@ -386,7 +414,7 @@ static int bdi_forker_thread(void *ptr) break; } - spin_lock(&bdi->wb_lock); + spin_lock_bh(&bdi->wb_lock); /* * If there is no work to do and the bdi thread was * inactive long enough - kill it. The wb_lock is taken @@ -403,7 +431,7 @@ static int bdi_forker_thread(void *ptr) action = KILL_THREAD; break; } - spin_unlock(&bdi->wb_lock); + spin_unlock_bh(&bdi->wb_lock); } spin_unlock_bh(&bdi_lock); @@ -427,9 +455,9 @@ static int bdi_forker_thread(void *ptr) * The spinlock makes sure we do not lose * wake-ups when racing with 'bdi_queue_work()'. */ - spin_lock(&bdi->wb_lock); + spin_lock_bh(&bdi->wb_lock); bdi->wb.task = task; - spin_unlock(&bdi->wb_lock); + spin_unlock_bh(&bdi->wb_lock); } break; @@ -586,6 +614,7 @@ void bdi_unregister(struct backing_dev_info *bdi) if (bdi->dev) { trace_writeback_bdi_unregister(bdi); bdi_prune_sb(bdi); + del_timer_sync(&bdi->wb.wakeup_timer); if (!bdi_cap_flush_forker(bdi)) bdi_wb_shutdown(bdi); @@ -596,6 +625,18 @@ void bdi_unregister(struct backing_dev_info *bdi) } EXPORT_SYMBOL(bdi_unregister); +static void bdi_wb_init(struct bdi_writeback *wb, struct backing_dev_info *bdi) +{ + memset(wb, 0, sizeof(*wb)); + + wb->bdi = bdi; + wb->last_old_flush = jiffies; + INIT_LIST_HEAD(&wb->b_dirty); + INIT_LIST_HEAD(&wb->b_io); + INIT_LIST_HEAD(&wb->b_more_io); + setup_timer(&wb->wakeup_timer, wakeup_timer_fn, (unsigned long)bdi); +} + int bdi_init(struct backing_dev_info *bdi) { int i, err; -- cgit v1.2.3 From 6eda3dd33f8a0ce58ee56a11351758643a698db4 Mon Sep 17 00:00:00 2001 From: Tiger Yang Date: Fri, 16 Jul 2010 11:21:23 +0800 Subject: ocfs2: do not overwrite error codes in ocfs2_init_acl Setting the acl while creating a new inode depends on the error codes of posix_acl_create_masq. This patch fix a issue of overwriting the error codes of it. Reported-by: Pawel Zawora Cc: [ .33, .34 ] Signed-off-by: Tiger Yang Signed-off-by: Joel Becker --- fs/ocfs2/acl.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/ocfs2/acl.c b/fs/ocfs2/acl.c index da702294d7e7..5a2177833210 100644 --- a/fs/ocfs2/acl.c +++ b/fs/ocfs2/acl.c @@ -344,7 +344,7 @@ int ocfs2_init_acl(handle_t *handle, { struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); struct posix_acl *acl = NULL; - int ret = 0; + int ret = 0, ret2; mode_t mode; if (!S_ISLNK(inode->i_mode)) { @@ -381,7 +381,12 @@ int ocfs2_init_acl(handle_t *handle, mode = inode->i_mode; ret = posix_acl_create_masq(clone, &mode); if (ret >= 0) { - ret = ocfs2_acl_set_mode(inode, di_bh, handle, mode); + ret2 = ocfs2_acl_set_mode(inode, di_bh, handle, mode); + if (ret2) { + mlog_errno(ret2); + ret = ret2; + goto cleanup; + } if (ret > 0) { ret = ocfs2_set_acl(handle, inode, di_bh, ACL_TYPE_ACCESS, -- cgit v1.2.3 From 6d98c3ccb52f692f1a60339dde7c700686a5568b Mon Sep 17 00:00:00 2001 From: Wengang Wang Date: Fri, 16 Jul 2010 23:13:33 +0800 Subject: ocfs2/dlm: fix a dead lock When we have to take both dlm->master_lock and lockres->spinlock, take them in order lockres->spinlock and then dlm->master_lock. The patch fixes a violation of the rule. We can simply move taking dlm->master_lock to where we have dropped res->spinlock since when we access res->state and free mle memory we don't need master_lock's protection. Signed-off-by: Wengang Wang Cc: stable@kernel.org Signed-off-by: Joel Becker --- fs/ocfs2/dlm/dlmmaster.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/ocfs2/dlm/dlmmaster.c b/fs/ocfs2/dlm/dlmmaster.c index 94b97fc6a88e..6d098b89d46e 100644 --- a/fs/ocfs2/dlm/dlmmaster.c +++ b/fs/ocfs2/dlm/dlmmaster.c @@ -3050,8 +3050,6 @@ int dlm_migrate_request_handler(struct o2net_msg *msg, u32 len, void *data, /* check for pre-existing lock */ spin_lock(&dlm->spinlock); res = __dlm_lookup_lockres(dlm, name, namelen, hash); - spin_lock(&dlm->master_lock); - if (res) { spin_lock(&res->spinlock); if (res->state & DLM_LOCK_RES_RECOVERING) { @@ -3069,14 +3067,15 @@ int dlm_migrate_request_handler(struct o2net_msg *msg, u32 len, void *data, spin_unlock(&res->spinlock); } + spin_lock(&dlm->master_lock); /* ignore status. only nonzero status would BUG. */ ret = dlm_add_migration_mle(dlm, res, mle, &oldmle, name, namelen, migrate->new_master, migrate->master); -unlock: spin_unlock(&dlm->master_lock); +unlock: spin_unlock(&dlm->spinlock); if (oldmle) { -- cgit v1.2.3 From 7beaf243787f85a2ef9213ccf13ab4a243283fde Mon Sep 17 00:00:00 2001 From: Srinivas Eeda Date: Mon, 19 Jul 2010 16:04:12 -0700 Subject: ocfs2 fix o2dlm dlm run purgelist (rev 3) This patch fixes two problems in dlm_run_purgelist 1. If a lockres is found to be in use, dlm_run_purgelist keeps trying to purge the same lockres instead of trying the next lockres. 2. When a lockres is found unused, dlm_run_purgelist releases lockres spinlock before setting DLM_LOCK_RES_DROPPING_REF and calls dlm_purge_lockres. spinlock is reacquired but in this window lockres can get reused. This leads to BUG. This patch modifies dlm_run_purgelist to skip lockres if it's in use and purge next lockres. It also sets DLM_LOCK_RES_DROPPING_REF before releasing the lockres spinlock protecting it from getting reused. Signed-off-by: Srinivas Eeda Acked-by: Sunil Mushran Cc: stable@kernel.org Signed-off-by: Joel Becker --- fs/ocfs2/dlm/dlmthread.c | 80 ++++++++++++++++++++---------------------------- 1 file changed, 34 insertions(+), 46 deletions(-) (limited to 'fs') diff --git a/fs/ocfs2/dlm/dlmthread.c b/fs/ocfs2/dlm/dlmthread.c index d4f73ca68fe5..dd78ca3f360f 100644 --- a/fs/ocfs2/dlm/dlmthread.c +++ b/fs/ocfs2/dlm/dlmthread.c @@ -152,45 +152,25 @@ void dlm_lockres_calc_usage(struct dlm_ctxt *dlm, spin_unlock(&dlm->spinlock); } -static int dlm_purge_lockres(struct dlm_ctxt *dlm, +static void dlm_purge_lockres(struct dlm_ctxt *dlm, struct dlm_lock_resource *res) { int master; int ret = 0; - spin_lock(&res->spinlock); - if (!__dlm_lockres_unused(res)) { - mlog(0, "%s:%.*s: tried to purge but not unused\n", - dlm->name, res->lockname.len, res->lockname.name); - __dlm_print_one_lock_resource(res); - spin_unlock(&res->spinlock); - BUG(); - } - - if (res->state & DLM_LOCK_RES_MIGRATING) { - mlog(0, "%s:%.*s: Delay dropref as this lockres is " - "being remastered\n", dlm->name, res->lockname.len, - res->lockname.name); - /* Re-add the lockres to the end of the purge list */ - if (!list_empty(&res->purge)) { - list_del_init(&res->purge); - list_add_tail(&res->purge, &dlm->purge_list); - } - spin_unlock(&res->spinlock); - return 0; - } + assert_spin_locked(&dlm->spinlock); + assert_spin_locked(&res->spinlock); master = (res->owner == dlm->node_num); - if (!master) - res->state |= DLM_LOCK_RES_DROPPING_REF; - spin_unlock(&res->spinlock); mlog(0, "purging lockres %.*s, master = %d\n", res->lockname.len, res->lockname.name, master); if (!master) { + res->state |= DLM_LOCK_RES_DROPPING_REF; /* drop spinlock... retake below */ + spin_unlock(&res->spinlock); spin_unlock(&dlm->spinlock); spin_lock(&res->spinlock); @@ -208,31 +188,35 @@ static int dlm_purge_lockres(struct dlm_ctxt *dlm, mlog(0, "%s:%.*s: dlm_deref_lockres returned %d\n", dlm->name, res->lockname.len, res->lockname.name, ret); spin_lock(&dlm->spinlock); + spin_lock(&res->spinlock); } - spin_lock(&res->spinlock); if (!list_empty(&res->purge)) { mlog(0, "removing lockres %.*s:%p from purgelist, " "master = %d\n", res->lockname.len, res->lockname.name, res, master); list_del_init(&res->purge); - spin_unlock(&res->spinlock); dlm_lockres_put(res); dlm->purge_count--; - } else - spin_unlock(&res->spinlock); + } + + if (!__dlm_lockres_unused(res)) { + mlog(ML_ERROR, "found lockres %s:%.*s: in use after deref\n", + dlm->name, res->lockname.len, res->lockname.name); + __dlm_print_one_lock_resource(res); + BUG(); + } __dlm_unhash_lockres(res); /* lockres is not in the hash now. drop the flag and wake up * any processes waiting in dlm_get_lock_resource. */ if (!master) { - spin_lock(&res->spinlock); res->state &= ~DLM_LOCK_RES_DROPPING_REF; spin_unlock(&res->spinlock); wake_up(&res->wq); - } - return 0; + } else + spin_unlock(&res->spinlock); } static void dlm_run_purge_list(struct dlm_ctxt *dlm, @@ -251,17 +235,7 @@ static void dlm_run_purge_list(struct dlm_ctxt *dlm, lockres = list_entry(dlm->purge_list.next, struct dlm_lock_resource, purge); - /* Status of the lockres *might* change so double - * check. If the lockres is unused, holding the dlm - * spinlock will prevent people from getting and more - * refs on it -- there's no need to keep the lockres - * spinlock. */ spin_lock(&lockres->spinlock); - unused = __dlm_lockres_unused(lockres); - spin_unlock(&lockres->spinlock); - - if (!unused) - continue; purge_jiffies = lockres->last_used + msecs_to_jiffies(DLM_PURGE_INTERVAL_MS); @@ -273,15 +247,29 @@ static void dlm_run_purge_list(struct dlm_ctxt *dlm, * in tail order, we can stop at the first * unpurgable resource -- anyone added after * him will have a greater last_used value */ + spin_unlock(&lockres->spinlock); break; } + /* Status of the lockres *might* change so double + * check. If the lockres is unused, holding the dlm + * spinlock will prevent people from getting and more + * refs on it. */ + unused = __dlm_lockres_unused(lockres); + if (!unused || + (lockres->state & DLM_LOCK_RES_MIGRATING)) { + mlog(0, "lockres %s:%.*s: is in use or " + "being remastered, used %d, state %d\n", + dlm->name, lockres->lockname.len, + lockres->lockname.name, !unused, lockres->state); + list_move_tail(&dlm->purge_list, &lockres->purge); + spin_unlock(&lockres->spinlock); + continue; + } + dlm_lockres_get(lockres); - /* This may drop and reacquire the dlm spinlock if it - * has to do migration. */ - if (dlm_purge_lockres(dlm, lockres)) - BUG(); + dlm_purge_lockres(dlm, lockres); dlm_lockres_put(lockres); -- cgit v1.2.3 From 8a2e70c40ff58f82dde67770e6623ca45f0cb0c8 Mon Sep 17 00:00:00 2001 From: Tao Ma Date: Thu, 22 Jul 2010 13:56:45 +0800 Subject: ocfs2: Count more refcount records in file system fragmentation. The refcount record calculation in ocfs2_calc_refcount_meta_credits is too optimistic that we can always allocate contiguous clusters and handle an already existed refcount rec as a whole. Actually because of file system fragmentation, we may have the chance to split a refcount record into 3 parts during the transaction. So consider the worst case in record calculation. Cc: stable@kernel.org Signed-off-by: Tao Ma Signed-off-by: Joel Becker --- fs/ocfs2/refcounttree.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) (limited to 'fs') diff --git a/fs/ocfs2/refcounttree.c b/fs/ocfs2/refcounttree.c index 3ac5aa733e9c..73a11ccfd4c2 100644 --- a/fs/ocfs2/refcounttree.c +++ b/fs/ocfs2/refcounttree.c @@ -2436,16 +2436,26 @@ static int ocfs2_calc_refcount_meta_credits(struct super_block *sb, len = min((u64)cpos + clusters, le64_to_cpu(rec.r_cpos) + le32_to_cpu(rec.r_clusters)) - cpos; /* - * If the refcount rec already exist, cool. We just need - * to check whether there is a split. Otherwise we just need - * to increase the refcount. - * If we will insert one, increases recs_add. - * * We record all the records which will be inserted to the * same refcount block, so that we can tell exactly whether * we need a new refcount block or not. + * + * If we will insert a new one, this is easy and only happens + * during adding refcounted flag to the extent, so we don't + * have a chance of spliting. We just need one record. + * + * If the refcount rec already exists, that would be a little + * complicated. we may have to: + * 1) split at the beginning if the start pos isn't aligned. + * we need 1 more record in this case. + * 2) split int the end if the end pos isn't aligned. + * we need 1 more record in this case. + * 3) split in the middle because of file system fragmentation. + * we need 2 more records in this case(we can't detect this + * beforehand, so always think of the worst case). */ if (rec.r_refcount) { + recs_add += 2; /* Check whether we need a split at the beginning. */ if (cpos == start_cpos && cpos != le64_to_cpu(rec.r_cpos)) -- cgit v1.2.3 From 845b6cf34150100deb5f58c8a37a372b111f2918 Mon Sep 17 00:00:00 2001 From: Jiaju Zhang Date: Wed, 28 Jul 2010 13:21:06 +0800 Subject: Fix the nested PR lock calling issue in ACL Hi, Thanks a lot for all the review and comments so far;) I'd like to send the improved (V4) version of this patch. This patch fixes a deadlock in OCFS2 ACL. We found this bug in OCFS2 and Samba integration using scenario, the symptom is several smbd processes will be hung under heavy workload. Finally we found out it is the nested PR lock calling that leads to this deadlock: node1 node2 gr PR | V PR(EX)---> BAST:OCFS2_LOCK_BLOCKED | V rq PR | V wait=1 After requesting the 2nd PR lock, the process "smbd" went into D state. It can only be woken up when the 1st PR lock's RO holder equals zero. There should be an ocfs2_inode_unlock in the calling path later on, which can decrement the RO holder. But since it has been in uninterruptible sleep, the unlock function has no chance to be called. The related stack trace is: smbd D ffff8800013d0600 0 9522 5608 0x00000000 ffff88002ca7fb18 0000000000000282 ffff88002f964500 ffff88002ca7fa98 ffff8800013d0600 ffff88002ca7fae0 ffff88002f964340 ffff88002f964340 ffff88002ca7ffd8 ffff88002ca7ffd8 ffff88002f964340 ffff88002f964340 Call Trace: [] schedule_timeout+0x175/0x210 [] wait_for_common+0xf0/0x210 [] __ocfs2_cluster_lock+0x3b9/0xa90 [ocfs2] [] ocfs2_inode_lock_full_nested+0x255/0xdb0 [ocfs2] [] ocfs2_get_acl+0x69/0x120 [ocfs2] [] ocfs2_check_acl+0x28/0x80 [ocfs2] [] acl_permission_check+0x57/0xb0 [] generic_permission+0x1d/0xc0 [] ocfs2_permission+0x10a/0x1d0 [ocfs2] [] inode_permission+0x45/0x100 [] sys_chdir+0x53/0x90 [] system_call_fastpath+0x16/0x1b [<00007f34a4ef6927>] 0x7f34a4ef6927 For details, please see: https://bugzilla.novell.com/show_bug.cgi?id=614332 and http://oss.oracle.com/bugzilla/show_bug.cgi?id=1278 Signed-off-by: Jiaju Zhang Acked-by: Mark Fasheh Cc: stable@kernel.org Signed-off-by: Joel Becker --- fs/ocfs2/acl.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/ocfs2/acl.c b/fs/ocfs2/acl.c index 5a2177833210..a76e0aa5cd3f 100644 --- a/fs/ocfs2/acl.c +++ b/fs/ocfs2/acl.c @@ -290,12 +290,30 @@ static int ocfs2_set_acl(handle_t *handle, int ocfs2_check_acl(struct inode *inode, int mask) { - struct posix_acl *acl = ocfs2_get_acl(inode, ACL_TYPE_ACCESS); + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); + struct buffer_head *di_bh = NULL; + struct posix_acl *acl; + int ret = -EAGAIN; - if (IS_ERR(acl)) + if (!(osb->s_mount_opt & OCFS2_MOUNT_POSIX_ACL)) + return ret; + + ret = ocfs2_read_inode_block(inode, &di_bh); + if (ret < 0) { + mlog_errno(ret); + return ret; + } + + acl = ocfs2_get_acl_nolock(inode, ACL_TYPE_ACCESS, di_bh); + + brelse(di_bh); + + if (IS_ERR(acl)) { + mlog_errno(PTR_ERR(acl)); return PTR_ERR(acl); + } if (acl) { - int ret = posix_acl_permission(inode, acl, mask); + ret = posix_acl_permission(inode, acl, mask); posix_acl_release(acl); return ret; } -- cgit v1.2.3 From a524812b7eaa7783d7811198921100f079034e61 Mon Sep 17 00:00:00 2001 From: Wengang Wang Date: Fri, 30 Jul 2010 16:14:44 +0800 Subject: ocfs2/dlm: avoid incorrect bit set in refmap on recovery master In the following situation, there remains an incorrect bit in refmap on the recovery master. Finally the recovery master will fail at purging the lockres due to the incorrect bit in refmap. 1) node A has no interest on lockres A any longer, so it is purging it. 2) the owner of lockres A is node B, so node A is sending de-ref message to node B. 3) at this time, node B crashed. node C becomes the recovery master. it recovers lockres A(because the master is the dead node B). 4) node A migrated lockres A to node C with a refbit there. 5) node A failed to send de-ref message to node B because it crashed. The failure is ignored. no other action is done for lockres A any more. For mormal, re-send the deref message to it to recovery master can fix it. Well, ignoring the failure of deref to the original master and not recovering the lockres to recovery master has the same effect. And the later is simpler. Signed-off-by: Wengang Wang Acked-by: Srinivas Eeda Cc: stable@kernel.org Signed-off-by: Joel Becker --- fs/ocfs2/dlm/dlmrecovery.c | 22 ++++++++++------------ fs/ocfs2/dlm/dlmthread.c | 34 +++++++++++++++++++++------------- 2 files changed, 31 insertions(+), 25 deletions(-) (limited to 'fs') diff --git a/fs/ocfs2/dlm/dlmrecovery.c b/fs/ocfs2/dlm/dlmrecovery.c index 9dfaac73b36d..aaaffbcbe916 100644 --- a/fs/ocfs2/dlm/dlmrecovery.c +++ b/fs/ocfs2/dlm/dlmrecovery.c @@ -1997,6 +1997,8 @@ void dlm_move_lockres_to_recovery_list(struct dlm_ctxt *dlm, struct list_head *queue; struct dlm_lock *lock, *next; + assert_spin_locked(&dlm->spinlock); + assert_spin_locked(&res->spinlock); res->state |= DLM_LOCK_RES_RECOVERING; if (!list_empty(&res->recovering)) { mlog(0, @@ -2326,19 +2328,15 @@ static void dlm_do_local_recovery_cleanup(struct dlm_ctxt *dlm, u8 dead_node) /* zero the lvb if necessary */ dlm_revalidate_lvb(dlm, res, dead_node); if (res->owner == dead_node) { - if (res->state & DLM_LOCK_RES_DROPPING_REF) - mlog(0, "%s:%.*s: owned by " - "dead node %u, this node was " - "dropping its ref when it died. " - "continue, dropping the flag.\n", - dlm->name, res->lockname.len, - res->lockname.name, dead_node); - - /* the wake_up for this will happen when the - * RECOVERING flag is dropped later */ - res->state &= ~DLM_LOCK_RES_DROPPING_REF; + if (res->state & DLM_LOCK_RES_DROPPING_REF) { + mlog(ML_NOTICE, "Ignore %.*s for " + "recovery as it is being freed\n", + res->lockname.len, + res->lockname.name); + } else + dlm_move_lockres_to_recovery_list(dlm, + res); - dlm_move_lockres_to_recovery_list(dlm, res); } else if (res->owner == dlm->node_num) { dlm_free_dead_locks(dlm, res, dead_node); __dlm_lockres_calc_usage(dlm, res); diff --git a/fs/ocfs2/dlm/dlmthread.c b/fs/ocfs2/dlm/dlmthread.c index dd78ca3f360f..2211acf33d9b 100644 --- a/fs/ocfs2/dlm/dlmthread.c +++ b/fs/ocfs2/dlm/dlmthread.c @@ -92,19 +92,27 @@ int __dlm_lockres_has_locks(struct dlm_lock_resource *res) * truly ready to be freed. */ int __dlm_lockres_unused(struct dlm_lock_resource *res) { - if (!__dlm_lockres_has_locks(res) && - (list_empty(&res->dirty) && !(res->state & DLM_LOCK_RES_DIRTY))) { - /* try not to scan the bitmap unless the first two - * conditions are already true */ - int bit = find_next_bit(res->refmap, O2NM_MAX_NODES, 0); - if (bit >= O2NM_MAX_NODES) { - /* since the bit for dlm->node_num is not - * set, inflight_locks better be zero */ - BUG_ON(res->inflight_locks != 0); - return 1; - } - } - return 0; + int bit; + + if (__dlm_lockres_has_locks(res)) + return 0; + + if (!list_empty(&res->dirty) || res->state & DLM_LOCK_RES_DIRTY) + return 0; + + if (res->state & DLM_LOCK_RES_RECOVERING) + return 0; + + bit = find_next_bit(res->refmap, O2NM_MAX_NODES, 0); + if (bit < O2NM_MAX_NODES) + return 0; + + /* + * since the bit for dlm->node_num is not set, inflight_locks better + * be zero + */ + BUG_ON(res->inflight_locks != 0); + return 1; } -- cgit v1.2.3 From b11f1f1ab73fd358b1b734a9427744802202ba68 Mon Sep 17 00:00:00 2001 From: Wengang Wang Date: Fri, 30 Jul 2010 23:18:00 +0800 Subject: ocfs2/dlm: remove potential deadlock -V3 When we need to take both dlm_domain_lock and dlm->spinlock, we should take them in order of: dlm_domain_lock then dlm->spinlock. There is pathes disobey this order. That is calling dlm_lockres_put() with dlm->spinlock held in dlm_run_purge_list. dlm_lockres_put() calls dlm_put() at the ref and dlm_put() locks on dlm_domain_lock. Fix: Don't grab/put the dlm when the initialising/releasing lockres. That grab is not required because we don't call dlm_unregister_domain() based on refcount. Signed-off-by: Wengang Wang Cc: stable@kernel.org Signed-off-by: Joel Becker --- fs/ocfs2/dlm/dlmmaster.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'fs') diff --git a/fs/ocfs2/dlm/dlmmaster.c b/fs/ocfs2/dlm/dlmmaster.c index 6d098b89d46e..ffb4c68dafa4 100644 --- a/fs/ocfs2/dlm/dlmmaster.c +++ b/fs/ocfs2/dlm/dlmmaster.c @@ -511,8 +511,6 @@ static void dlm_lockres_release(struct kref *kref) atomic_dec(&dlm->res_cur_count); - dlm_put(dlm); - if (!hlist_unhashed(&res->hash_node) || !list_empty(&res->granted) || !list_empty(&res->converting) || @@ -585,8 +583,6 @@ static void dlm_init_lockres(struct dlm_ctxt *dlm, res->migration_pending = 0; res->inflight_locks = 0; - /* put in dlm_lockres_release */ - dlm_grab(dlm); res->dlm = dlm; kref_init(&res->refs); -- cgit v1.2.3 From 415cf32c9cdfcc60f34d0ac17f29634e941ba7d2 Mon Sep 17 00:00:00 2001 From: Tristan Ye Date: Mon, 2 Aug 2010 10:00:26 +0800 Subject: O2net: Disallow o2net accept connection request from itself. Currently, o2net_accept_one() is allowed to accept a connection from listening node itself, such a fake connection will not be successfully established due to no handshake detected afterwards, and later end up with triggering connecting worker in a loop. We're going to fix this by treating such connection request as 'invalid', since we've got no chance of requesting connection from a node to itself in a OCFS2 cluster. The fix doesn't hurt user's scan for o2net-listener, it always gets a successful connection from userpace. Signed-off-by: Tristan Ye Acked-by: Sunil Mushran Signed-off-by: Joel Becker --- fs/ocfs2/cluster/tcp.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) (limited to 'fs') diff --git a/fs/ocfs2/cluster/tcp.c b/fs/ocfs2/cluster/tcp.c index aa75ca3f78da..1361997cf205 100644 --- a/fs/ocfs2/cluster/tcp.c +++ b/fs/ocfs2/cluster/tcp.c @@ -1759,6 +1759,7 @@ static int o2net_accept_one(struct socket *sock) struct sockaddr_in sin; struct socket *new_sock = NULL; struct o2nm_node *node = NULL; + struct o2nm_node *local_node = NULL; struct o2net_sock_container *sc = NULL; struct o2net_node *nn; @@ -1796,11 +1797,15 @@ static int o2net_accept_one(struct socket *sock) goto out; } - if (o2nm_this_node() > node->nd_num) { - mlog(ML_NOTICE, "unexpected connect attempted from a lower " - "numbered node '%s' at " "%pI4:%d with num %u\n", - node->nd_name, &sin.sin_addr.s_addr, - ntohs(sin.sin_port), node->nd_num); + if (o2nm_this_node() >= node->nd_num) { + local_node = o2nm_get_node_by_num(o2nm_this_node()); + mlog(ML_NOTICE, "unexpected connect attempt seen at node '%s' (" + "%u, %pI4:%d) from node '%s' (%u, %pI4:%d)\n", + local_node->nd_name, local_node->nd_num, + &(local_node->nd_ipv4_address), + ntohs(local_node->nd_ipv4_port), + node->nd_name, node->nd_num, &sin.sin_addr.s_addr, + ntohs(sin.sin_port)); ret = -EINVAL; goto out; } @@ -1857,6 +1862,8 @@ out: sock_release(new_sock); if (node) o2nm_node_put(node); + if (local_node) + o2nm_node_put(local_node); if (sc) sc_put(sc); return ret; -- cgit v1.2.3 From df44f9f4f9b5d362b5a2d1c8444fe7e6d4c42653 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 6 Aug 2010 17:26:48 +0100 Subject: AFS: Fix the module init error handling Fix the module init error handling. There are a bunch of goto labels for aborting the init procedure at different points and just undoing what needs undoing - they aren't all in the right places, however. This can lead to an oops like the following: BUG: unable to handle kernel NULL pointer dereference at 0000000000000020 IP: [] destroy_workqueue+0x17/0xc0 ... Modules linked in: kafs(+) dns_resolver rxkad af_rxrpc fscache Pid: 2171, comm: insmod Not tainted 2.6.35-cachefs+ #319 DG965RY/ ... Process insmod (pid: 2171, threadinfo ffff88003ca6a000, task ffff88003dcc3050) ... Call Trace: [] afs_callback_update_kill+0x10/0x12 [kafs] [] afs_init+0x190/0x1ce [kafs] [] ? afs_init+0x0/0x1ce [kafs] [] do_one_initcall+0x59/0x14e [] sys_init_module+0x9c/0x1de [] system_call_fastpath+0x16/0x1b Signed-off-by: David Howells Signed-off-by: Linus Torvalds --- fs/afs/main.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/afs/main.c b/fs/afs/main.c index 66d54d348c55..cfd1cbe25b22 100644 --- a/fs/afs/main.c +++ b/fs/afs/main.c @@ -111,6 +111,8 @@ static int __init afs_init(void) /* initialise the callback update process */ ret = afs_callback_update_init(); + if (ret < 0) + goto error_callback_update_init; /* create the RxRPC transport */ ret = afs_open_socket(); @@ -127,15 +129,16 @@ static int __init afs_init(void) error_fs: afs_close_socket(); error_open_socket: + afs_callback_update_kill(); +error_callback_update_init: + afs_vlocation_purge(); error_vl_update_init: + afs_cell_purge(); error_cell_init: #ifdef CONFIG_AFS_FSCACHE fscache_unregister_netfs(&afs_cache_netfs); error_cache: #endif - afs_callback_update_kill(); - afs_vlocation_purge(); - afs_cell_purge(); afs_proc_cleanup(); rcu_barrier(); printk(KERN_ERR "kAFS: failed to register: %d\n", ret); -- cgit v1.2.3 From 6088c0587706b2cf21ce50c11576718bff5fae0c Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Sun, 8 Aug 2010 14:15:22 +0100 Subject: jffs2: Update copyright notices Signed-off-by: David Woodhouse --- fs/jffs2/background.c | 1 + fs/jffs2/build.c | 1 + fs/jffs2/compr.c | 5 +++-- fs/jffs2/compr.h | 1 + fs/jffs2/compr_lzo.c | 1 + fs/jffs2/compr_rtime.c | 1 + fs/jffs2/compr_rubin.c | 1 + fs/jffs2/compr_zlib.c | 1 + fs/jffs2/debug.c | 1 + fs/jffs2/debug.h | 1 + fs/jffs2/dir.c | 1 + fs/jffs2/erase.c | 1 + fs/jffs2/file.c | 1 + fs/jffs2/fs.c | 1 + fs/jffs2/gc.c | 1 + fs/jffs2/ioctl.c | 1 + fs/jffs2/jffs2_fs_i.h | 1 + fs/jffs2/jffs2_fs_sb.h | 1 + include/linux/jffs2.h | 3 ++- 19 files changed, 22 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/jffs2/background.c b/fs/jffs2/background.c index 55f1dde2fa8b..404111b016c9 100644 --- a/fs/jffs2/background.c +++ b/fs/jffs2/background.c @@ -2,6 +2,7 @@ * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright © 2001-2007 Red Hat, Inc. + * Copyright © 2004-2010 David Woodhouse * * Created by David Woodhouse * diff --git a/fs/jffs2/build.c b/fs/jffs2/build.c index c5e1450d79f9..a906f538d11c 100644 --- a/fs/jffs2/build.c +++ b/fs/jffs2/build.c @@ -2,6 +2,7 @@ * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright © 2001-2007 Red Hat, Inc. + * Copyright © 2004-2010 David Woodhouse * * Created by David Woodhouse * diff --git a/fs/jffs2/compr.c b/fs/jffs2/compr.c index f0294410868d..617a1e5694c1 100644 --- a/fs/jffs2/compr.c +++ b/fs/jffs2/compr.c @@ -2,11 +2,12 @@ * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright © 2001-2007 Red Hat, Inc. - * Created by Arjan van de Ven - * + * Copyright © 2004-2010 David Woodhouse * Copyright © 2004 Ferenc Havasi , * University of Szeged, Hungary * + * Created by Arjan van de Ven + * * For licensing information, see the file 'LICENCE' in this directory. * */ diff --git a/fs/jffs2/compr.h b/fs/jffs2/compr.h index 7d1d72faa774..e471a9106fd9 100644 --- a/fs/jffs2/compr.h +++ b/fs/jffs2/compr.h @@ -3,6 +3,7 @@ * * Copyright © 2004 Ferenc Havasi , * University of Szeged, Hungary + * Copyright © 2004-2010 David Woodhouse * * For licensing information, see the file 'LICENCE' in this directory. * diff --git a/fs/jffs2/compr_lzo.c b/fs/jffs2/compr_lzo.c index cd02acafde8a..ed25ae7c98eb 100644 --- a/fs/jffs2/compr_lzo.c +++ b/fs/jffs2/compr_lzo.c @@ -2,6 +2,7 @@ * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright © 2007 Nokia Corporation. All rights reserved. + * Copyright © 2004-2010 David Woodhouse * * Created by Richard Purdie * diff --git a/fs/jffs2/compr_rtime.c b/fs/jffs2/compr_rtime.c index 546d1538d076..9696ad9ef5f7 100644 --- a/fs/jffs2/compr_rtime.c +++ b/fs/jffs2/compr_rtime.c @@ -2,6 +2,7 @@ * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright © 2001-2007 Red Hat, Inc. + * Copyright © 2004-2010 David Woodhouse * * Created by Arjan van de Ven * diff --git a/fs/jffs2/compr_rubin.c b/fs/jffs2/compr_rubin.c index 170d289ac785..a12b4f763373 100644 --- a/fs/jffs2/compr_rubin.c +++ b/fs/jffs2/compr_rubin.c @@ -2,6 +2,7 @@ * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright © 2001-2007 Red Hat, Inc. + * Copyright © 2004-2010 David Woodhouse * * Created by Arjan van de Ven * diff --git a/fs/jffs2/compr_zlib.c b/fs/jffs2/compr_zlib.c index b46661a42758..97fc45de6f81 100644 --- a/fs/jffs2/compr_zlib.c +++ b/fs/jffs2/compr_zlib.c @@ -2,6 +2,7 @@ * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright © 2001-2007 Red Hat, Inc. + * Copyright © 2004-2010 David Woodhouse * * Created by David Woodhouse * diff --git a/fs/jffs2/debug.c b/fs/jffs2/debug.c index ec3538413926..e0b76c87a91a 100644 --- a/fs/jffs2/debug.c +++ b/fs/jffs2/debug.c @@ -2,6 +2,7 @@ * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright © 2001-2007 Red Hat, Inc. + * Copyright © 2004-2010 David Woodhouse * * Created by David Woodhouse * diff --git a/fs/jffs2/debug.h b/fs/jffs2/debug.h index a113ecc3bafe..c4f8eef5ca68 100644 --- a/fs/jffs2/debug.h +++ b/fs/jffs2/debug.h @@ -2,6 +2,7 @@ * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright © 2001-2007 Red Hat, Inc. + * Copyright © 2004-2010 David Woodhouse * * Created by David Woodhouse * diff --git a/fs/jffs2/dir.c b/fs/jffs2/dir.c index 166062a68230..1aaf98df49d0 100644 --- a/fs/jffs2/dir.c +++ b/fs/jffs2/dir.c @@ -2,6 +2,7 @@ * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright © 2001-2007 Red Hat, Inc. + * Copyright © 2004-2010 David Woodhouse * * Created by David Woodhouse * diff --git a/fs/jffs2/erase.c b/fs/jffs2/erase.c index 6286ad9b00f7..abac961f617b 100644 --- a/fs/jffs2/erase.c +++ b/fs/jffs2/erase.c @@ -2,6 +2,7 @@ * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright © 2001-2007 Red Hat, Inc. + * Copyright © 2004-2010 David Woodhouse * * Created by David Woodhouse * diff --git a/fs/jffs2/file.c b/fs/jffs2/file.c index e7291c161a19..52749127aa6c 100644 --- a/fs/jffs2/file.c +++ b/fs/jffs2/file.c @@ -2,6 +2,7 @@ * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright © 2001-2007 Red Hat, Inc. + * Copyright © 2004-2010 David Woodhouse * * Created by David Woodhouse * diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c index 26037e2d6154..29d0970f9da3 100644 --- a/fs/jffs2/fs.c +++ b/fs/jffs2/fs.c @@ -2,6 +2,7 @@ * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright © 2001-2007 Red Hat, Inc. + * Copyright © 2004-2010 David Woodhouse * * Created by David Woodhouse * diff --git a/fs/jffs2/gc.c b/fs/jffs2/gc.c index f5e96bd656e8..846a79452497 100644 --- a/fs/jffs2/gc.c +++ b/fs/jffs2/gc.c @@ -2,6 +2,7 @@ * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright © 2001-2007 Red Hat, Inc. + * Copyright © 2004-2010 David Woodhouse * * Created by David Woodhouse * diff --git a/fs/jffs2/ioctl.c b/fs/jffs2/ioctl.c index 9d41f43e47bb..859a598af020 100644 --- a/fs/jffs2/ioctl.c +++ b/fs/jffs2/ioctl.c @@ -2,6 +2,7 @@ * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright © 2001-2007 Red Hat, Inc. + * Copyright © 2004-2010 David Woodhouse * * Created by David Woodhouse * diff --git a/fs/jffs2/jffs2_fs_i.h b/fs/jffs2/jffs2_fs_i.h index c6923da98263..2e4a86763c07 100644 --- a/fs/jffs2/jffs2_fs_i.h +++ b/fs/jffs2/jffs2_fs_i.h @@ -2,6 +2,7 @@ * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright © 2001-2007 Red Hat, Inc. + * Copyright © 2004-2010 David Woodhouse * * Created by David Woodhouse * diff --git a/fs/jffs2/jffs2_fs_sb.h b/fs/jffs2/jffs2_fs_sb.h index 85ef6dbb1be7..6784bc89add1 100644 --- a/fs/jffs2/jffs2_fs_sb.h +++ b/fs/jffs2/jffs2_fs_sb.h @@ -2,6 +2,7 @@ * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright © 2001-2007 Red Hat, Inc. + * Copyright © 2004-2010 David Woodhouse * * Created by David Woodhouse * diff --git a/include/linux/jffs2.h b/include/linux/jffs2.h index 0874ab59ffef..b9898894ec8d 100644 --- a/include/linux/jffs2.h +++ b/include/linux/jffs2.h @@ -1,7 +1,8 @@ /* * JFFS2 -- Journalling Flash File System, Version 2. * - * Copyright (C) 2001-2003 Red Hat, Inc. + * Copyright © 2001-2007 Red Hat, Inc. + * Copyright © 2004-2010 David Woodhouse * * Created by David Woodhouse * -- cgit v1.2.3 From ffc18879903e55487bc5ac3c774b99a07de06029 Mon Sep 17 00:00:00 2001 From: Bill Pemberton Date: Tue, 3 Aug 2010 15:19:30 -0400 Subject: omfs: fix uninitialized variable warning quiet the warning: fs/omfs/file.c: In function 'omfs_get_block': fs/omfs/file.c:225: warning: 'new_block' may be used uninitialized in this function new_block is used properly by the call to omfs_grow_extent() Signed-off-by: Bill Pemberton Signed-off-by: Bob Copeland --- fs/omfs/file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/omfs/file.c b/fs/omfs/file.c index 76bc21b91a8a..d821d468e5a2 100644 --- a/fs/omfs/file.c +++ b/fs/omfs/file.c @@ -222,7 +222,7 @@ static int omfs_get_block(struct inode *inode, sector_t block, struct buffer_head *bh; sector_t next, offset; int ret; - u64 new_block; + u64 uninitialized_var(new_block); u32 max_extents; int extent_count; struct omfs_extent *oe; -- cgit v1.2.3 From 6ae0185fe201eae0548dace2a84acb5050fc8606 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Sun, 8 Aug 2010 21:19:42 +0100 Subject: mtd: Remove obsolete include Signed-off-by: David Woodhouse --- drivers/mtd/chips/cfi_cmdset_0001.c | 1 - drivers/mtd/chips/cfi_cmdset_0002.c | 1 - drivers/mtd/chips/cfi_cmdset_0020.c | 1 - drivers/mtd/chips/cfi_util.c | 1 - drivers/mtd/chips/chipreg.c | 1 - drivers/mtd/chips/map_absent.c | 1 - drivers/mtd/chips/map_ram.c | 1 - drivers/mtd/chips/map_rom.c | 1 - drivers/mtd/devices/docecc.c | 1 - drivers/mtd/devices/docprobe.c | 1 - drivers/mtd/devices/mtdram.c | 1 - drivers/mtd/devices/pmc551.c | 1 - drivers/mtd/inftlmount.c | 1 - drivers/mtd/mtdchar.c | 1 - drivers/mtd/mtdcore.c | 1 - drivers/mtd/mtdpart.c | 1 - drivers/mtd/nand/diskonchip.c | 1 - drivers/mtd/nand/nand_base.c | 1 - drivers/mtd/nand/nand_bbt.c | 1 - drivers/mtd/nand/rtc_from4.c | 1 - drivers/mtd/onenand/onenand_bbt.c | 1 - fs/jffs2/nodelist.h | 1 - include/linux/mtd/compatmac.h | 10 ---------- include/linux/mtd/map.h | 1 - include/linux/mtd/mtd.h | 1 - 25 files changed, 34 deletions(-) delete mode 100644 include/linux/mtd/compatmac.h (limited to 'fs') diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index 97d5546f9ea4..9e2b7e9e0ad9 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -34,7 +34,6 @@ #include #include #include -#include #include /* #define CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE */ diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index bd20d1ff1b0d..3e6c47bdce53 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -33,7 +33,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/mtd/chips/cfi_cmdset_0020.c b/drivers/mtd/chips/cfi_cmdset_0020.c index e54e8c169d76..314af1f5a370 100644 --- a/drivers/mtd/chips/cfi_cmdset_0020.c +++ b/drivers/mtd/chips/cfi_cmdset_0020.c @@ -33,7 +33,6 @@ #include #include #include -#include static int cfi_staa_read(struct mtd_info *, loff_t, size_t, size_t *, u_char *); diff --git a/drivers/mtd/chips/cfi_util.c b/drivers/mtd/chips/cfi_util.c index d7c2c672757e..e503b2ca894d 100644 --- a/drivers/mtd/chips/cfi_util.c +++ b/drivers/mtd/chips/cfi_util.c @@ -22,7 +22,6 @@ #include #include #include -#include int __xipram cfi_qry_present(struct map_info *map, __u32 base, struct cfi_private *cfi) diff --git a/drivers/mtd/chips/chipreg.c b/drivers/mtd/chips/chipreg.c index c85760968227..da1f96f385c7 100644 --- a/drivers/mtd/chips/chipreg.c +++ b/drivers/mtd/chips/chipreg.c @@ -10,7 +10,6 @@ #include #include #include -#include static DEFINE_SPINLOCK(chip_drvs_lock); static LIST_HEAD(chip_drvs_list); diff --git a/drivers/mtd/chips/map_absent.c b/drivers/mtd/chips/map_absent.c index 494d30d0631a..f2b872946871 100644 --- a/drivers/mtd/chips/map_absent.c +++ b/drivers/mtd/chips/map_absent.c @@ -25,7 +25,6 @@ #include #include #include -#include static int map_absent_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); static int map_absent_write (struct mtd_info *, loff_t, size_t, size_t *, const u_char *); diff --git a/drivers/mtd/chips/map_ram.c b/drivers/mtd/chips/map_ram.c index 6bdc50c727e7..67640ccb2d41 100644 --- a/drivers/mtd/chips/map_ram.c +++ b/drivers/mtd/chips/map_ram.c @@ -13,7 +13,6 @@ #include #include #include -#include static int mapram_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); diff --git a/drivers/mtd/chips/map_rom.c b/drivers/mtd/chips/map_rom.c index 076090a67b90..593f73d480d2 100644 --- a/drivers/mtd/chips/map_rom.c +++ b/drivers/mtd/chips/map_rom.c @@ -13,7 +13,6 @@ #include #include #include -#include static int maprom_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); static int maprom_write (struct mtd_info *, loff_t, size_t, size_t *, const u_char *); diff --git a/drivers/mtd/devices/docecc.c b/drivers/mtd/devices/docecc.c index a19cda52da5c..a99838bb2dc0 100644 --- a/drivers/mtd/devices/docecc.c +++ b/drivers/mtd/devices/docecc.c @@ -31,7 +31,6 @@ #include #include -#include /* for min() in older kernels */ #include #include diff --git a/drivers/mtd/devices/docprobe.c b/drivers/mtd/devices/docprobe.c index 6e62922942b1..d374603493a7 100644 --- a/drivers/mtd/devices/docprobe.c +++ b/drivers/mtd/devices/docprobe.c @@ -49,7 +49,6 @@ #include #include #include -#include /* Where to look for the devices? */ #ifndef CONFIG_MTD_DOCPROBE_ADDRESS diff --git a/drivers/mtd/devices/mtdram.c b/drivers/mtd/devices/mtdram.c index fce5ff7589aa..26a6e809013d 100644 --- a/drivers/mtd/devices/mtdram.c +++ b/drivers/mtd/devices/mtdram.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include diff --git a/drivers/mtd/devices/pmc551.c b/drivers/mtd/devices/pmc551.c index fc8ea0a57ac2..ef0aba0ce58f 100644 --- a/drivers/mtd/devices/pmc551.c +++ b/drivers/mtd/devices/pmc551.c @@ -98,7 +98,6 @@ #include #include -#include static struct mtd_info *pmc551list; diff --git a/drivers/mtd/inftlmount.c b/drivers/mtd/inftlmount.c index 7772d2618d9f..104052e774b0 100644 --- a/drivers/mtd/inftlmount.c +++ b/drivers/mtd/inftlmount.c @@ -34,7 +34,6 @@ #include #include #include -#include /* * find_boot_record: Find the INFTL Media Header and its Spare copy which diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index f6d76f1adca0..638827a25b77 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -33,7 +33,6 @@ #include #include -#include #include diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 93a11f3def44..527cebf58da4 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -31,7 +31,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index fc5507410278..dc6558568876 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -29,7 +29,6 @@ #include #include #include -#include /* Our partition linked list */ static LIST_HEAD(mtd_partitions); diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c index 51315f55f905..b7f8de7b2780 100644 --- a/drivers/mtd/nand/diskonchip.c +++ b/drivers/mtd/nand/diskonchip.c @@ -29,7 +29,6 @@ #include #include #include -#include #include #include diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index ee6a6f866b50..16a1714df008 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -42,7 +42,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index 469de17107e5..5fedf4a74f16 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -55,7 +55,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/mtd/nand/rtc_from4.c b/drivers/mtd/nand/rtc_from4.c index a033c4cd8e16..67440b5beef8 100644 --- a/drivers/mtd/nand/rtc_from4.c +++ b/drivers/mtd/nand/rtc_from4.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/mtd/onenand/onenand_bbt.c b/drivers/mtd/onenand/onenand_bbt.c index a91fcac1af01..01ab5b3c453b 100644 --- a/drivers/mtd/onenand/onenand_bbt.c +++ b/drivers/mtd/onenand/onenand_bbt.c @@ -15,7 +15,6 @@ #include #include #include -#include /** * check_short_pattern - [GENERIC] check if a pattern is in the buffer diff --git a/fs/jffs2/nodelist.h b/fs/jffs2/nodelist.h index a881a42f19e3..523a91691052 100644 --- a/fs/jffs2/nodelist.h +++ b/fs/jffs2/nodelist.h @@ -24,7 +24,6 @@ #ifdef __ECOS #include "os-ecos.h" #else -#include /* For compatibility with older kernels */ #include "os-linux.h" #endif diff --git a/include/linux/mtd/compatmac.h b/include/linux/mtd/compatmac.h deleted file mode 100644 index 7d1300d9bd51..000000000000 --- a/include/linux/mtd/compatmac.h +++ /dev/null @@ -1,10 +0,0 @@ - -#ifndef __LINUX_MTD_COMPATMAC_H__ -#define __LINUX_MTD_COMPATMAC_H__ - -/* Nothing to see here. We write 2.5-compatible code and this - file makes it all OK in older kernels, but it's empty in _current_ - kernels. Include guard just to make GCC ignore it in future inclusions - anyway... */ - -#endif /* __LINUX_MTD_COMPATMAC_H__ */ diff --git a/include/linux/mtd/map.h b/include/linux/mtd/map.h index eea3a4fb7405..a9e6ba46865e 100644 --- a/include/linux/mtd/map.h +++ b/include/linux/mtd/map.h @@ -27,7 +27,6 @@ #include #include -#include #include #include diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index eae914e97f33..8485e42a9b09 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -26,7 +26,6 @@ #include #include -#include #include #include -- cgit v1.2.3 From c9243f5bdd6637b2bb7dc254b54d9edf957ef17e Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Sun, 4 Jul 2010 00:15:07 +0200 Subject: autofs/autofs4: Move compat_ioctl handling into fs Handling of autofs ioctl numbers does not need to be generic and can easily be done directly in autofs itself. This also pushes the BKL into autofs and autofs4 ioctl methods. Signed-off-by: Arnd Bergmann Acked-by: H. Peter Anvin Cc: Al Viro Cc: Ian Kent Cc: Autofs Cc: John Kacur Signed-off-by: Frederic Weisbecker --- fs/autofs/root.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++--- fs/autofs4/root.c | 49 ++++++++++++++++++++++++++++++++++++ fs/compat_ioctl.c | 36 -------------------------- include/linux/auto_fs.h | 1 + 4 files changed, 114 insertions(+), 39 deletions(-) (limited to 'fs') diff --git a/fs/autofs/root.c b/fs/autofs/root.c index 9a0520b50663..11b1ea786d00 100644 --- a/fs/autofs/root.c +++ b/fs/autofs/root.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include "autofs_i.h" @@ -25,13 +26,17 @@ static int autofs_root_symlink(struct inode *,struct dentry *,const char *); static int autofs_root_unlink(struct inode *,struct dentry *); static int autofs_root_rmdir(struct inode *,struct dentry *); static int autofs_root_mkdir(struct inode *,struct dentry *,int); -static int autofs_root_ioctl(struct inode *, struct file *,unsigned int,unsigned long); +static long autofs_root_ioctl(struct file *,unsigned int,unsigned long); +static long autofs_root_compat_ioctl(struct file *,unsigned int,unsigned long); const struct file_operations autofs_root_operations = { .llseek = generic_file_llseek, .read = generic_read_dir, .readdir = autofs_root_readdir, - .ioctl = autofs_root_ioctl, + .unlocked_ioctl = autofs_root_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = autofs_root_compat_ioctl, +#endif }; const struct inode_operations autofs_root_inode_operations = { @@ -492,6 +497,25 @@ static int autofs_root_mkdir(struct inode *dir, struct dentry *dentry, int mode) } /* Get/set timeout ioctl() operation */ +#ifdef CONFIG_COMPAT +static inline int autofs_compat_get_set_timeout(struct autofs_sb_info *sbi, + unsigned int __user *p) +{ + unsigned long ntimeout; + + if (get_user(ntimeout, p) || + put_user(sbi->exp_timeout / HZ, p)) + return -EFAULT; + + if (ntimeout > UINT_MAX/HZ) + sbi->exp_timeout = 0; + else + sbi->exp_timeout = ntimeout * HZ; + + return 0; +} +#endif + static inline int autofs_get_set_timeout(struct autofs_sb_info *sbi, unsigned long __user *p) { @@ -546,7 +570,7 @@ static inline int autofs_expire_run(struct super_block *sb, * ioctl()'s on the root directory is the chief method for the daemon to * generate kernel reactions */ -static int autofs_root_ioctl(struct inode *inode, struct file *filp, +static int autofs_do_root_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { struct autofs_sb_info *sbi = autofs_sbi(inode->i_sb); @@ -571,6 +595,10 @@ static int autofs_root_ioctl(struct inode *inode, struct file *filp, return 0; case AUTOFS_IOC_PROTOVER: /* Get protocol version */ return autofs_get_protover(argp); +#ifdef CONFIG_COMPAT + case AUTOFS_IOC_SETTIMEOUT32: + return autofs_compat_get_set_timeout(sbi, argp); +#endif case AUTOFS_IOC_SETTIMEOUT: return autofs_get_set_timeout(sbi, argp); case AUTOFS_IOC_EXPIRE: @@ -579,4 +607,37 @@ static int autofs_root_ioctl(struct inode *inode, struct file *filp, default: return -ENOSYS; } + +} + +static long autofs_root_ioctl(struct file *filp, + unsigned int cmd, unsigned long arg) +{ + int ret; + + lock_kernel(); + ret = autofs_do_root_ioctl(filp->f_path.dentry->d_inode, + filp, cmd, arg); + unlock_kernel(); + + return ret; +} + +#ifdef CONFIG_COMPAT +static long autofs_root_compat_ioctl(struct file *filp, + unsigned int cmd, unsigned long arg) +{ + struct inode *inode = filp->f_path.dentry->d_inode; + int ret; + + lock_kernel(); + if (cmd == AUTOFS_IOC_READY || cmd == AUTOFS_IOC_FAIL) + ret = autofs_do_root_ioctl(inode, filp, cmd, arg); + else + ret = autofs_do_root_ioctl(inode, filp, cmd, + (unsigned long)compat_ptr(arg)); + unlock_kernel(); + + return ret; } +#endif diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c index db4117ed7803..48e056e70fd6 100644 --- a/fs/autofs4/root.c +++ b/fs/autofs4/root.c @@ -18,7 +18,9 @@ #include #include #include +#include #include + #include "autofs_i.h" static int autofs4_dir_symlink(struct inode *,struct dentry *,const char *); @@ -26,6 +28,7 @@ static int autofs4_dir_unlink(struct inode *,struct dentry *); static int autofs4_dir_rmdir(struct inode *,struct dentry *); static int autofs4_dir_mkdir(struct inode *,struct dentry *,int); static long autofs4_root_ioctl(struct file *,unsigned int,unsigned long); +static long autofs4_root_compat_ioctl(struct file *,unsigned int,unsigned long); static int autofs4_dir_open(struct inode *inode, struct file *file); static struct dentry *autofs4_lookup(struct inode *,struct dentry *, struct nameidata *); static void *autofs4_follow_link(struct dentry *, struct nameidata *); @@ -40,6 +43,9 @@ const struct file_operations autofs4_root_operations = { .readdir = dcache_readdir, .llseek = dcache_dir_lseek, .unlocked_ioctl = autofs4_root_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = autofs4_root_compat_ioctl, +#endif }; const struct file_operations autofs4_dir_operations = { @@ -840,6 +846,26 @@ static int autofs4_dir_mkdir(struct inode *dir, struct dentry *dentry, int mode) } /* Get/set timeout ioctl() operation */ +#ifdef CONFIG_COMPAT +static inline int autofs4_compat_get_set_timeout(struct autofs_sb_info *sbi, + compat_ulong_t __user *p) +{ + int rv; + unsigned long ntimeout; + + if ((rv = get_user(ntimeout, p)) || + (rv = put_user(sbi->exp_timeout/HZ, p))) + return rv; + + if (ntimeout > UINT_MAX/HZ) + sbi->exp_timeout = 0; + else + sbi->exp_timeout = ntimeout * HZ; + + return 0; +} +#endif + static inline int autofs4_get_set_timeout(struct autofs_sb_info *sbi, unsigned long __user *p) { @@ -933,6 +959,10 @@ static int autofs4_root_ioctl_unlocked(struct inode *inode, struct file *filp, return autofs4_get_protosubver(sbi, p); case AUTOFS_IOC_SETTIMEOUT: return autofs4_get_set_timeout(sbi, p); +#ifdef CONFIG_COMPAT + case AUTOFS_IOC_SETTIMEOUT32: + return autofs4_compat_get_set_timeout(sbi, p); +#endif case AUTOFS_IOC_ASKUMOUNT: return autofs4_ask_umount(filp->f_path.mnt, p); @@ -961,3 +991,22 @@ static long autofs4_root_ioctl(struct file *filp, return ret; } + +#ifdef CONFIG_COMPAT +static long autofs4_root_compat_ioctl(struct file *filp, + unsigned int cmd, unsigned long arg) +{ + struct inode *inode = filp->f_path.dentry->d_inode; + int ret; + + lock_kernel(); + if (cmd == AUTOFS_IOC_READY || cmd == AUTOFS_IOC_FAIL) + ret = autofs4_root_ioctl_unlocked(inode, filp, cmd, arg); + else + ret = autofs4_root_ioctl_unlocked(inode, filp, cmd, + (unsigned long)compat_ptr(arg)); + unlock_kernel(); + + return ret; +} +#endif diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c index 641640dc7ae5..618f38136304 100644 --- a/fs/compat_ioctl.c +++ b/fs/compat_ioctl.c @@ -131,23 +131,6 @@ static int w_long(unsigned int fd, unsigned int cmd, return err; } -static int rw_long(unsigned int fd, unsigned int cmd, - compat_ulong_t __user *argp) -{ - mm_segment_t old_fs = get_fs(); - int err; - unsigned long val; - - if(get_user(val, argp)) - return -EFAULT; - set_fs (KERNEL_DS); - err = sys_ioctl(fd, cmd, (unsigned long)&val); - set_fs (old_fs); - if (!err && put_user(val, argp)) - return -EFAULT; - return err; -} - struct compat_video_event { int32_t type; compat_time_t timestamp; @@ -594,12 +577,6 @@ static int do_smb_getmountuid(unsigned int fd, unsigned int cmd, return err; } -static int ioc_settimeout(unsigned int fd, unsigned int cmd, - compat_ulong_t __user *argp) -{ - return rw_long(fd, AUTOFS_IOC_SETTIMEOUT, argp); -} - /* Bluetooth ioctls */ #define HCIUARTSETPROTO _IOW('U', 200, int) #define HCIUARTGETPROTO _IOR('U', 201, int) @@ -1281,13 +1258,6 @@ COMPATIBLE_IOCTL(SOUND_MIXER_PRIVATE5) COMPATIBLE_IOCTL(SOUND_MIXER_GETLEVELS) COMPATIBLE_IOCTL(SOUND_MIXER_SETLEVELS) COMPATIBLE_IOCTL(OSS_GETVERSION) -/* AUTOFS */ -COMPATIBLE_IOCTL(AUTOFS_IOC_CATATONIC) -COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOVER) -COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE) -COMPATIBLE_IOCTL(AUTOFS_IOC_EXPIRE_MULTI) -COMPATIBLE_IOCTL(AUTOFS_IOC_PROTOSUBVER) -COMPATIBLE_IOCTL(AUTOFS_IOC_ASKUMOUNT) /* Raw devices */ COMPATIBLE_IOCTL(RAW_SETBIND) COMPATIBLE_IOCTL(RAW_GETBIND) @@ -1552,9 +1522,6 @@ static long do_ioctl_trans(int fd, unsigned int cmd, case RAW_GETBIND: return raw_ioctl(fd, cmd, argp); #endif -#define AUTOFS_IOC_SETTIMEOUT32 _IOWR(0x93,0x64,unsigned int) - case AUTOFS_IOC_SETTIMEOUT32: - return ioc_settimeout(fd, cmd, argp); /* One SMB ioctl needs translations. */ #define SMB_IOC_GETMOUNTUID_32 _IOR('u', 1, compat_uid_t) case SMB_IOC_GETMOUNTUID_32: @@ -1609,9 +1576,6 @@ static long do_ioctl_trans(int fd, unsigned int cmd, case KDSKBMETA: case KDSKBLED: case KDSETLED: - /* AUTOFS */ - case AUTOFS_IOC_READY: - case AUTOFS_IOC_FAIL: /* NBD */ case NBD_SET_SOCK: case NBD_SET_BLKSIZE: diff --git a/include/linux/auto_fs.h b/include/linux/auto_fs.h index 7b09c8348fd3..da64e15004b6 100644 --- a/include/linux/auto_fs.h +++ b/include/linux/auto_fs.h @@ -79,6 +79,7 @@ struct autofs_packet_expire { #define AUTOFS_IOC_FAIL _IO(0x93,0x61) #define AUTOFS_IOC_CATATONIC _IO(0x93,0x62) #define AUTOFS_IOC_PROTOVER _IOR(0x93,0x63,int) +#define AUTOFS_IOC_SETTIMEOUT32 _IOWR(0x93,0x64,compat_ulong_t) #define AUTOFS_IOC_SETTIMEOUT _IOWR(0x93,0x64,unsigned long) #define AUTOFS_IOC_EXPIRE _IOR(0x93,0x65,struct autofs_packet_expire) -- cgit v1.2.3 From 66048c381bb1e3a7c6fc69c28721843d6ec11c8c Mon Sep 17 00:00:00 2001 From: Phillip Lougher Date: Sun, 8 Aug 2010 22:29:33 +0000 Subject: Squashfs: fix checkpatch.pl warnings Checkpatch.pl in 2.6.34 added a check for spaces between tabs. Signed-off-by: Phillip Lougher --- fs/squashfs/squashfs_fs.h | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'fs') diff --git a/fs/squashfs/squashfs_fs.h b/fs/squashfs/squashfs_fs.h index 8eabb808b78d..c5137fc9ab11 100644 --- a/fs/squashfs/squashfs_fs.h +++ b/fs/squashfs/squashfs_fs.h @@ -274,7 +274,7 @@ struct squashfs_base_inode { __le16 uid; __le16 guid; __le32 mtime; - __le32 inode_number; + __le32 inode_number; }; struct squashfs_ipc_inode { @@ -283,7 +283,7 @@ struct squashfs_ipc_inode { __le16 uid; __le16 guid; __le32 mtime; - __le32 inode_number; + __le32 inode_number; __le32 nlink; }; @@ -293,7 +293,7 @@ struct squashfs_lipc_inode { __le16 uid; __le16 guid; __le32 mtime; - __le32 inode_number; + __le32 inode_number; __le32 nlink; __le32 xattr; }; @@ -304,7 +304,7 @@ struct squashfs_dev_inode { __le16 uid; __le16 guid; __le32 mtime; - __le32 inode_number; + __le32 inode_number; __le32 nlink; __le32 rdev; }; @@ -315,7 +315,7 @@ struct squashfs_ldev_inode { __le16 uid; __le16 guid; __le32 mtime; - __le32 inode_number; + __le32 inode_number; __le32 nlink; __le32 rdev; __le32 xattr; @@ -327,7 +327,7 @@ struct squashfs_symlink_inode { __le16 uid; __le16 guid; __le32 mtime; - __le32 inode_number; + __le32 inode_number; __le32 nlink; __le32 symlink_size; char symlink[0]; @@ -339,7 +339,7 @@ struct squashfs_reg_inode { __le16 uid; __le16 guid; __le32 mtime; - __le32 inode_number; + __le32 inode_number; __le32 start_block; __le32 fragment; __le32 offset; @@ -353,7 +353,7 @@ struct squashfs_lreg_inode { __le16 uid; __le16 guid; __le32 mtime; - __le32 inode_number; + __le32 inode_number; __le64 start_block; __le64 file_size; __le64 sparse; @@ -370,7 +370,7 @@ struct squashfs_dir_inode { __le16 uid; __le16 guid; __le32 mtime; - __le32 inode_number; + __le32 inode_number; __le32 start_block; __le32 nlink; __le16 file_size; @@ -384,7 +384,7 @@ struct squashfs_ldir_inode { __le16 uid; __le16 guid; __le32 mtime; - __le32 inode_number; + __le32 inode_number; __le32 nlink; __le32 file_size; __le32 start_block; -- cgit v1.2.3 From a1275c3b21e433888994f7b979cd1129d384ec9c Mon Sep 17 00:00:00 2001 From: Prarit Bhargava Date: Thu, 27 May 2010 11:19:30 -0400 Subject: ecryptfs: Fix warning in ecryptfs_process_response() Fix warning seen with "make -j24 CONFIG_DEBUG_SECTION_MISMATCH=y V=1": fs/ecryptfs/messaging.c: In function 'ecryptfs_process_response': fs/ecryptfs/messaging.c:276: warning: 'daemon' may be used uninitialized in this function Signed-off-by: Prarit Bhargava Signed-off-by: Tyler Hicks --- fs/ecryptfs/messaging.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/ecryptfs/messaging.c b/fs/ecryptfs/messaging.c index 46c4dd8dfcc3..bcb68c0cb1f0 100644 --- a/fs/ecryptfs/messaging.c +++ b/fs/ecryptfs/messaging.c @@ -274,7 +274,7 @@ int ecryptfs_process_response(struct ecryptfs_message *msg, uid_t euid, struct user_namespace *user_ns, struct pid *pid, u32 seq) { - struct ecryptfs_daemon *daemon; + struct ecryptfs_daemon *uninitialized_var(daemon); struct ecryptfs_msg_ctx *msg_ctx; size_t msg_size; struct nsproxy *nsproxy; -- cgit v1.2.3 From c43f7b8fb03be8bcc579bfc4e6ab70eac887ab55 Mon Sep 17 00:00:00 2001 From: Tyler Hicks Date: Tue, 3 Nov 2009 11:45:11 -0600 Subject: eCryptfs: Handle ioctl calls with unlocked and compat functions Lower filesystems that only implemented unlocked_ioctl weren't being passed ioctl calls because eCryptfs only checked for lower_file->f_op->ioctl and returned -ENOTTY if it was NULL. eCryptfs shouldn't implement ioctl(), since it doesn't require the BKL. This patch introduces ecryptfs_unlocked_ioctl() and ecryptfs_compat_ioctl(), which passes the calls on to the lower file system. https://bugs.launchpad.net/ecryptfs/+bug/469664 Reported-by: James Dupin Cc: stable@kernel.org Signed-off-by: Tyler Hicks --- fs/ecryptfs/file.c | 56 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 35 insertions(+), 21 deletions(-) (limited to 'fs') diff --git a/fs/ecryptfs/file.c b/fs/ecryptfs/file.c index e8fcf4e2ed7d..6e4f84cf73e8 100644 --- a/fs/ecryptfs/file.c +++ b/fs/ecryptfs/file.c @@ -292,12 +292,40 @@ static int ecryptfs_fasync(int fd, struct file *file, int flag) return rc; } -static int ecryptfs_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg); +static long +ecryptfs_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct file *lower_file = NULL; + long rc = -ENOTTY; + + if (ecryptfs_file_to_private(file)) + lower_file = ecryptfs_file_to_lower(file); + if (lower_file && lower_file->f_op && lower_file->f_op->unlocked_ioctl) + rc = lower_file->f_op->unlocked_ioctl(lower_file, cmd, arg); + return rc; +} + +#ifdef CONFIG_COMPAT +static long +ecryptfs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct file *lower_file = NULL; + long rc = -ENOIOCTLCMD; + + if (ecryptfs_file_to_private(file)) + lower_file = ecryptfs_file_to_lower(file); + if (lower_file && lower_file->f_op && lower_file->f_op->compat_ioctl) + rc = lower_file->f_op->compat_ioctl(lower_file, cmd, arg); + return rc; +} +#endif const struct file_operations ecryptfs_dir_fops = { .readdir = ecryptfs_readdir, - .ioctl = ecryptfs_ioctl, + .unlocked_ioctl = ecryptfs_unlocked_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = ecryptfs_compat_ioctl, +#endif .open = ecryptfs_open, .flush = ecryptfs_flush, .release = ecryptfs_release, @@ -313,7 +341,10 @@ const struct file_operations ecryptfs_main_fops = { .write = do_sync_write, .aio_write = generic_file_aio_write, .readdir = ecryptfs_readdir, - .ioctl = ecryptfs_ioctl, + .unlocked_ioctl = ecryptfs_unlocked_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = ecryptfs_compat_ioctl, +#endif .mmap = generic_file_mmap, .open = ecryptfs_open, .flush = ecryptfs_flush, @@ -322,20 +353,3 @@ const struct file_operations ecryptfs_main_fops = { .fasync = ecryptfs_fasync, .splice_read = generic_file_splice_read, }; - -static int -ecryptfs_ioctl(struct inode *inode, struct file *file, unsigned int cmd, - unsigned long arg) -{ - int rc = 0; - struct file *lower_file = NULL; - - if (ecryptfs_file_to_private(file)) - lower_file = ecryptfs_file_to_lower(file); - if (lower_file && lower_file->f_op && lower_file->f_op->ioctl) - rc = lower_file->f_op->ioctl(ecryptfs_inode_to_lower(inode), - lower_file, cmd, arg); - else - rc = -ENOTTY; - return rc; -} -- cgit v1.2.3 From 31f73bee3e170b7cabb35db9e2f4bf7919b9d036 Mon Sep 17 00:00:00 2001 From: Lino Sanfilippo Date: Thu, 29 Jul 2010 13:01:36 +0200 Subject: ecryptfs: release reference to lower mount if interpose fails In ecryptfs_lookup_and_interpose_lower() the lower mount is not decremented if allocation of a dentry info struct failed. As a result the lower filesystem cant be unmounted any more (since it is considered busy). This patch corrects the reference counting. Signed-off-by: Lino Sanfilippo Cc: stable@kernel.org Signed-off-by: Tyler Hicks --- fs/ecryptfs/inode.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index 31ef5252f0fe..8cd617b66baa 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -264,7 +264,7 @@ int ecryptfs_lookup_and_interpose_lower(struct dentry *ecryptfs_dentry, printk(KERN_ERR "%s: Out of memory whilst attempting " "to allocate ecryptfs_dentry_info struct\n", __func__); - goto out_dput; + goto out_put; } ecryptfs_set_dentry_lower(ecryptfs_dentry, lower_dentry); ecryptfs_set_dentry_lower_mnt(ecryptfs_dentry, lower_mnt); @@ -339,8 +339,9 @@ int ecryptfs_lookup_and_interpose_lower(struct dentry *ecryptfs_dentry, out_free_kmem: kmem_cache_free(ecryptfs_header_cache_2, page_virt); goto out; -out_dput: +out_put: dput(lower_dentry); + mntput(lower_mnt); d_drop(ecryptfs_dentry); out: return rc; -- cgit v1.2.3 From ceeab92971e8af05c1e81a4ff2c271124b55bb9b Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Fri, 6 Aug 2010 22:58:49 +0200 Subject: fs/ecryptfs/file.c: introduce missing free The comments in the code indicate that file_info should be released if the function fails. This releasing is done at the label out_free, not out. The semantic match that finds this problem is as follows: (http://www.emn.fr/x-info/coccinelle/) // @r exists@ local idexpression x; statement S; expression E; identifier f,f1,l; position p1,p2; expression *ptr != NULL; @@ x@p1 = kmem_cache_zalloc(...); ... if (x == NULL) S <... when != x when != if (...) { <+...x...+> } ( x->f1 = E | (x->f1 == NULL || ...) | f(...,x->f1,...) ) ...> ( return <+...x...+>; | return@p2 ...; ) @script:python@ p1 << r.p1; p2 << r.p2; @@ print "* file: %s kmem_cache_zalloc %s" % (p1[0].file,p1[0].line) // Signed-off-by: Julia Lawall Cc: stable@kernel.org Signed-off-by: Tyler Hicks --- fs/ecryptfs/file.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/ecryptfs/file.c b/fs/ecryptfs/file.c index 6e4f84cf73e8..622c95140802 100644 --- a/fs/ecryptfs/file.c +++ b/fs/ecryptfs/file.c @@ -199,7 +199,7 @@ static int ecryptfs_open(struct inode *inode, struct file *file) "the persistent file for the dentry with name " "[%s]; rc = [%d]\n", __func__, ecryptfs_dentry->d_name.name, rc); - goto out; + goto out_free; } } if ((ecryptfs_inode_to_private(inode)->lower_file->f_flags & O_RDONLY) @@ -207,7 +207,7 @@ static int ecryptfs_open(struct inode *inode, struct file *file) rc = -EPERM; printk(KERN_WARNING "%s: Lower persistent file is RO; eCryptfs " "file must hence be opened RO\n", __func__); - goto out; + goto out_free; } ecryptfs_set_file_lower( file, ecryptfs_inode_to_private(inode)->lower_file); -- cgit v1.2.3 From 21edad32205e97dc7ccb81a85234c77e760364c8 Mon Sep 17 00:00:00 2001 From: Lino Sanfilippo Date: Thu, 15 Jul 2010 15:11:55 +0200 Subject: ecryptfs: dont call lookup_one_len to avoid NULL nameidata I have encountered the same problem that Eric Sandeen described in this post http://lkml.org/lkml/fancy/2010/4/23/467 while experimenting with stackable filesystems. The reason seems to be that ecryptfs calls lookup_one_len() to get the lower dentry, which in turn calls the lower parent dirs d_revalidate() with a NULL nameidata object. If ecryptfs is the underlaying filesystem, the NULL pointer dereference occurs, since ecryptfs is not prepared to handle a NULL nameidata. I know that this cant happen any more, since it is no longer allowed to mount ecryptfs upon itself. But maybe this patch it useful nevertheless, since the problem would still apply for an underlaying filesystem that implements d_revalidate() and is not prepared to handle a NULL nameidata (I dont know if there actually is such a fs). With this patch (against 2.6.35-rc5) ecryptfs uses the vfs_lookup_path() function instead of lookup_one_len() which ensures that the nameidata passed to the lower filesystems d_revalidate(). Signed-off-by: Lino Sanfilippo Signed-off-by: Tyler Hicks --- fs/ecryptfs/inode.c | 89 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 77 insertions(+), 12 deletions(-) (limited to 'fs') diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index 8cd617b66baa..a8acfc5d3476 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -347,6 +347,76 @@ out: return rc; } +/** + * ecryptfs_new_lower_dentry + * @ename: The name of the new dentry. + * @lower_dir_dentry: Parent directory of the new dentry. + * @nd: nameidata from last lookup. + * + * Create a new dentry or get it from lower parent dir. + */ +static struct dentry * +ecryptfs_new_lower_dentry(struct qstr *name, struct dentry *lower_dir_dentry, + struct nameidata *nd) +{ + struct dentry *new_dentry; + struct dentry *tmp; + struct inode *lower_dir_inode; + + lower_dir_inode = lower_dir_dentry->d_inode; + + tmp = d_alloc(lower_dir_dentry, name); + if (!tmp) + return ERR_PTR(-ENOMEM); + + mutex_lock(&lower_dir_inode->i_mutex); + new_dentry = lower_dir_inode->i_op->lookup(lower_dir_inode, tmp, nd); + mutex_unlock(&lower_dir_inode->i_mutex); + + if (!new_dentry) + new_dentry = tmp; + else + dput(tmp); + + return new_dentry; +} + + +/** + * ecryptfs_lookup_one_lower + * @ecryptfs_dentry: The eCryptfs dentry that we are looking up + * @lower_dir_dentry: lower parent directory + * + * Get the lower dentry from vfs. If lower dentry does not exist yet, + * create it. + */ +static struct dentry * +ecryptfs_lookup_one_lower(struct dentry *ecryptfs_dentry, + struct dentry *lower_dir_dentry) +{ + struct nameidata nd; + struct vfsmount *lower_mnt; + struct qstr *name; + int err; + + name = &ecryptfs_dentry->d_name; + lower_mnt = mntget(ecryptfs_dentry_to_lower_mnt( + ecryptfs_dentry->d_parent)); + err = vfs_path_lookup(lower_dir_dentry, lower_mnt, name->name , 0, &nd); + mntput(lower_mnt); + + if (!err) { + /* we dont need the mount */ + mntput(nd.path.mnt); + return nd.path.dentry; + } + if (err != -ENOENT) + return ERR_PTR(err); + + /* create a new lower dentry */ + return ecryptfs_new_lower_dentry(name, lower_dir_dentry, &nd); +} + /** * ecryptfs_lookup * @ecryptfs_dir_inode: The eCryptfs directory inode @@ -374,14 +444,12 @@ static struct dentry *ecryptfs_lookup(struct inode *ecryptfs_dir_inode, goto out_d_drop; } lower_dir_dentry = ecryptfs_dentry_to_lower(ecryptfs_dentry->d_parent); - mutex_lock(&lower_dir_dentry->d_inode->i_mutex); - lower_dentry = lookup_one_len(ecryptfs_dentry->d_name.name, - lower_dir_dentry, - ecryptfs_dentry->d_name.len); - mutex_unlock(&lower_dir_dentry->d_inode->i_mutex); + + lower_dentry = ecryptfs_lookup_one_lower(ecryptfs_dentry, + lower_dir_dentry); if (IS_ERR(lower_dentry)) { rc = PTR_ERR(lower_dentry); - ecryptfs_printk(KERN_DEBUG, "%s: lookup_one_len() returned " + ecryptfs_printk(KERN_DEBUG, "%s: lookup_one_lower() returned " "[%d] on lower_dentry = [%s]\n", __func__, rc, encrypted_and_encoded_name); goto out_d_drop; @@ -403,14 +471,11 @@ static struct dentry *ecryptfs_lookup(struct inode *ecryptfs_dir_inode, "filename; rc = [%d]\n", __func__, rc); goto out_d_drop; } - mutex_lock(&lower_dir_dentry->d_inode->i_mutex); - lower_dentry = lookup_one_len(encrypted_and_encoded_name, - lower_dir_dentry, - encrypted_and_encoded_name_size - 1); - mutex_unlock(&lower_dir_dentry->d_inode->i_mutex); + lower_dentry = ecryptfs_lookup_one_lower(ecryptfs_dentry, + lower_dir_dentry); if (IS_ERR(lower_dentry)) { rc = PTR_ERR(lower_dentry); - ecryptfs_printk(KERN_DEBUG, "%s: lookup_one_len() returned " + ecryptfs_printk(KERN_DEBUG, "%s: lookup_one_lower() returned " "[%d] on lower_dentry = [%s]\n", __func__, rc, encrypted_and_encoded_name); goto out_d_drop; -- cgit v1.2.3 From 005a59ec745d23f60222f7712adde48f64d7d3c8 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 21 Apr 2009 01:27:08 -0400 Subject: Deal with missing exports for hostfs Signed-off-by: Al Viro --- arch/um/include/shared/os.h | 3 +++ arch/um/kernel/ksyms.c | 3 +++ arch/um/os-Linux/file.c | 15 +++++++++++++++ arch/um/os-Linux/user_syms.c | 4 ++++ fs/hostfs/hostfs_user.c | 6 +++--- 5 files changed, 28 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/arch/um/include/shared/os.h b/arch/um/include/shared/os.h index cd40fddcf99d..c4617baaa4f2 100644 --- a/arch/um/include/shared/os.h +++ b/arch/um/include/shared/os.h @@ -161,6 +161,9 @@ extern int os_stat_filesystem(char *path, long *bsize_out, long *spare_out); extern int os_change_dir(char *dir); extern int os_fchange_dir(int fd); +extern unsigned os_major(unsigned long long dev); +extern unsigned os_minor(unsigned long long dev); +extern unsigned long long os_makedev(unsigned major, unsigned minor); /* start_up.c */ extern void os_early_checks(void); diff --git a/arch/um/kernel/ksyms.c b/arch/um/kernel/ksyms.c index 836fc9b94707..0ae0dfcfbffb 100644 --- a/arch/um/kernel/ksyms.c +++ b/arch/um/kernel/ksyms.c @@ -58,6 +58,9 @@ EXPORT_SYMBOL(os_accept_connection); EXPORT_SYMBOL(os_rcv_fd); EXPORT_SYMBOL(run_helper); EXPORT_SYMBOL(start_thread); +EXPORT_SYMBOL(os_major); +EXPORT_SYMBOL(os_minor); +EXPORT_SYMBOL(os_makedev); EXPORT_SYMBOL(add_sigio_fd); EXPORT_SYMBOL(ignore_sigio_fd); diff --git a/arch/um/os-Linux/file.c b/arch/um/os-Linux/file.c index b5afcfd0f861..140e587bc0ad 100644 --- a/arch/um/os-Linux/file.c +++ b/arch/um/os-Linux/file.c @@ -561,3 +561,18 @@ int os_lock_file(int fd, int excl) out: return err; } + +unsigned os_major(unsigned long long dev) +{ + return major(dev); +} + +unsigned os_minor(unsigned long long dev) +{ + return minor(dev); +} + +unsigned long long os_makedev(unsigned major, unsigned minor) +{ + return makedev(major, minor); +} diff --git a/arch/um/os-Linux/user_syms.c b/arch/um/os-Linux/user_syms.c index 89b48a116a89..05f5ea8e83d2 100644 --- a/arch/um/os-Linux/user_syms.c +++ b/arch/um/os-Linux/user_syms.c @@ -103,6 +103,10 @@ EXPORT_SYMBOL_PROTO(getuid); EXPORT_SYMBOL_PROTO(fsync); EXPORT_SYMBOL_PROTO(fdatasync); +EXPORT_SYMBOL_PROTO(lstat64); +EXPORT_SYMBOL_PROTO(fstat64); +EXPORT_SYMBOL_PROTO(mknod); + /* Export symbols used by GCC for the stack protector. */ extern void __stack_smash_handler(void *) __attribute__((weak)); EXPORT_SYMBOL(__stack_smash_handler); diff --git a/fs/hostfs/hostfs_user.c b/fs/hostfs/hostfs_user.c index b79424f93282..4b8c666ba28f 100644 --- a/fs/hostfs/hostfs_user.c +++ b/fs/hostfs/hostfs_user.c @@ -76,9 +76,9 @@ int file_type(const char *path, int *maj, int *min) * about its definition. */ if (maj != NULL) - *maj = major(buf.st_rdev); + *maj = os_major(buf.st_rdev); if (min != NULL) - *min = minor(buf.st_rdev); + *min = os_minor(buf.st_rdev); if (S_ISDIR(buf.st_mode)) return OS_TYPE_DIR; @@ -361,7 +361,7 @@ int do_mknod(const char *file, int mode, unsigned int major, unsigned int minor) { int err; - err = mknod(file, mode, makedev(major, minor)); + err = mknod(file, mode, os_makedev(major, minor)); if (err) return -errno; return 0; -- cgit v1.2.3 From 918377b696bff7384923a1ef4bf0af7626cb9b68 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 6 Jun 2010 23:56:02 -0400 Subject: missing include in hppfs Signed-off-by: Al Viro --- fs/hppfs/hppfs.c | 1 + 1 file changed, 1 insertion(+) (limited to 'fs') diff --git a/fs/hppfs/hppfs.c b/fs/hppfs/hppfs.c index 826c3f9d29ac..943ce751ce19 100644 --- a/fs/hppfs/hppfs.c +++ b/fs/hppfs/hppfs.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include "os.h" -- cgit v1.2.3 From 0e4f6a791b1e8cfde75a74e2f885642ecb3fe9d8 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 4 Jul 2010 12:18:57 +0400 Subject: Fix reiserfs_file_release() a) count file openers correctly; i_count use was completely wrong b) use new mutex for exclusion between final close/open/truncate, to protect tailpacking logics. i_mutex use was wrong and resulted in deadlocks. Signed-off-by: Al Viro --- fs/reiserfs/file.c | 50 ++++++++++++++++++++++++------------------- fs/reiserfs/inode.c | 2 -- fs/reiserfs/super.c | 2 ++ include/linux/reiserfs_fs_i.h | 4 ++-- 4 files changed, 32 insertions(+), 26 deletions(-) (limited to 'fs') diff --git a/fs/reiserfs/file.c b/fs/reiserfs/file.c index b82cdd8a45dd..6846371498b6 100644 --- a/fs/reiserfs/file.c +++ b/fs/reiserfs/file.c @@ -38,20 +38,24 @@ static int reiserfs_file_release(struct inode *inode, struct file *filp) BUG_ON(!S_ISREG(inode->i_mode)); + if (atomic_add_unless(&REISERFS_I(inode)->openers, -1, 1)) + return 0; + + mutex_lock(&(REISERFS_I(inode)->tailpack)); + + if (!atomic_dec_and_test(&REISERFS_I(inode)->openers)) { + mutex_unlock(&(REISERFS_I(inode)->tailpack)); + return 0; + } + /* fast out for when nothing needs to be done */ - if ((atomic_read(&inode->i_count) > 1 || - !(REISERFS_I(inode)->i_flags & i_pack_on_close_mask) || + if ((!(REISERFS_I(inode)->i_flags & i_pack_on_close_mask) || !tail_has_to_be_packed(inode)) && REISERFS_I(inode)->i_prealloc_count <= 0) { + mutex_unlock(&(REISERFS_I(inode)->tailpack)); return 0; } - mutex_lock(&inode->i_mutex); - - mutex_lock(&(REISERFS_I(inode)->i_mmap)); - if (REISERFS_I(inode)->i_flags & i_ever_mapped) - REISERFS_I(inode)->i_flags &= ~i_pack_on_close_mask; - reiserfs_write_lock(inode->i_sb); /* freeing preallocation only involves relogging blocks that * are already in the current transaction. preallocation gets @@ -94,9 +98,10 @@ static int reiserfs_file_release(struct inode *inode, struct file *filp) if (!err) err = jbegin_failure; - if (!err && atomic_read(&inode->i_count) <= 1 && + if (!err && (REISERFS_I(inode)->i_flags & i_pack_on_close_mask) && tail_has_to_be_packed(inode)) { + /* if regular file is released by last holder and it has been appended (we append by unformatted node only) or its direct item(s) had to be converted, then it may have to be @@ -104,27 +109,28 @@ static int reiserfs_file_release(struct inode *inode, struct file *filp) err = reiserfs_truncate_file(inode, 0); } out: - mutex_unlock(&(REISERFS_I(inode)->i_mmap)); - mutex_unlock(&inode->i_mutex); reiserfs_write_unlock(inode->i_sb); + mutex_unlock(&(REISERFS_I(inode)->tailpack)); return err; } -static int reiserfs_file_mmap(struct file *file, struct vm_area_struct *vma) +static int reiserfs_file_open(struct inode *inode, struct file *file) { - struct inode *inode; - - inode = file->f_path.dentry->d_inode; - mutex_lock(&(REISERFS_I(inode)->i_mmap)); - REISERFS_I(inode)->i_flags |= i_ever_mapped; - mutex_unlock(&(REISERFS_I(inode)->i_mmap)); - - return generic_file_mmap(file, vma); + int err = dquot_file_open(inode, file); + if (!atomic_inc_not_zero(&REISERFS_I(inode)->openers)) { + /* somebody might be tailpacking on final close; wait for it */ + mutex_lock(&(REISERFS_I(inode)->tailpack)); + atomic_inc(&REISERFS_I(inode)->openers); + mutex_unlock(&(REISERFS_I(inode)->tailpack)); + } + return err; } static void reiserfs_vfs_truncate_file(struct inode *inode) { + mutex_lock(&(REISERFS_I(inode)->tailpack)); reiserfs_truncate_file(inode, 1); + mutex_unlock(&(REISERFS_I(inode)->tailpack)); } /* Sync a reiserfs file. */ @@ -288,8 +294,8 @@ const struct file_operations reiserfs_file_operations = { #ifdef CONFIG_COMPAT .compat_ioctl = reiserfs_compat_ioctl, #endif - .mmap = reiserfs_file_mmap, - .open = dquot_file_open, + .mmap = generic_file_mmap, + .open = reiserfs_file_open, .release = reiserfs_file_release, .fsync = reiserfs_sync_file, .aio_read = generic_file_aio_read, diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c index 0f22fdaf54ac..6edac85c2b93 100644 --- a/fs/reiserfs/inode.c +++ b/fs/reiserfs/inode.c @@ -1138,7 +1138,6 @@ static void init_inode(struct inode *inode, struct treepath *path) REISERFS_I(inode)->i_prealloc_count = 0; REISERFS_I(inode)->i_trans_id = 0; REISERFS_I(inode)->i_jl = NULL; - mutex_init(&(REISERFS_I(inode)->i_mmap)); reiserfs_init_xattr_rwsem(inode); if (stat_data_v1(ih)) { @@ -1841,7 +1840,6 @@ int reiserfs_new_inode(struct reiserfs_transaction_handle *th, REISERFS_I(inode)->i_attrs = REISERFS_I(dir)->i_attrs & REISERFS_INHERIT_MASK; sd_attrs_to_i_attrs(REISERFS_I(inode)->i_attrs, inode); - mutex_init(&(REISERFS_I(inode)->i_mmap)); reiserfs_init_xattr_rwsem(inode); /* key to search for correct place for new stat data */ diff --git a/fs/reiserfs/super.c b/fs/reiserfs/super.c index 9822fa15118b..1e1ee9056eb6 100644 --- a/fs/reiserfs/super.c +++ b/fs/reiserfs/super.c @@ -525,6 +525,8 @@ static struct inode *reiserfs_alloc_inode(struct super_block *sb) kmem_cache_alloc(reiserfs_inode_cachep, GFP_KERNEL); if (!ei) return NULL; + atomic_set(&ei->openers, 0); + mutex_init(&ei->tailpack); return &ei->vfs_inode; } diff --git a/include/linux/reiserfs_fs_i.h b/include/linux/reiserfs_fs_i.h index 89f4d3abbf5a..97959bdfe214 100644 --- a/include/linux/reiserfs_fs_i.h +++ b/include/linux/reiserfs_fs_i.h @@ -25,7 +25,6 @@ typedef enum { i_link_saved_truncate_mask = 0x0020, i_has_xattr_dir = 0x0040, i_data_log = 0x0080, - i_ever_mapped = 0x0100 } reiserfs_inode_flags; struct reiserfs_inode_info { @@ -53,7 +52,8 @@ struct reiserfs_inode_info { ** flushed */ unsigned int i_trans_id; struct reiserfs_journal_list *i_jl; - struct mutex i_mmap; + atomic_t openers; + struct mutex tailpack; #ifdef CONFIG_REISERFS_FS_XATTR struct rw_semaphore i_xattr_sem; #endif -- cgit v1.2.3 From 256249584bda1a9357e2d29987a37f5b2df035f6 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 4 Jul 2010 12:23:11 +0400 Subject: fix leak in __logfs_create() if kmalloc fails, we still need to drop the inode, as we do on other failure exits. Signed-off-by: Al Viro --- fs/logfs/dir.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/logfs/dir.c b/fs/logfs/dir.c index 72d1893ddd36..675cc49197fe 100644 --- a/fs/logfs/dir.c +++ b/fs/logfs/dir.c @@ -434,8 +434,11 @@ static int __logfs_create(struct inode *dir, struct dentry *dentry, int ret; ta = kzalloc(sizeof(*ta), GFP_KERNEL); - if (!ta) + if (!ta) { + inode->i_nlink--; + iput(inode); return -ENOMEM; + } ta->state = CREATE_1; ta->ino = inode->i_ino; -- cgit v1.2.3 From eafdc7d190a944c755a9fe68573c193e6e0217e7 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 4 Jun 2010 11:29:53 +0200 Subject: sort out blockdev_direct_IO variants Move the call to vmtruncate to get rid of accessive blocks to the callers in prepearation of the new truncate calling sequence. This was only done for DIO_LOCKING filesystems, so the __blockdev_direct_IO_newtrunc variant was not needed anyway. Get rid of blockdev_direct_IO_no_locking and its _newtrunc variant while at it as just opencoding the two additional paramters is shorted than the name suffix. Signed-off-by: Christoph Hellwig Signed-off-by: Al Viro --- fs/block_dev.c | 5 ++- fs/direct-io.c | 74 ++++++++++++--------------------------------- fs/ext2/inode.c | 2 +- fs/ext3/inode.c | 11 +++++++ fs/ext4/inode.c | 15 +++++++-- fs/fat/inode.c | 4 +-- fs/gfs2/aops.c | 6 ++-- fs/hfs/inode.c | 17 ++++++++++- fs/hfsplus/inode.c | 17 ++++++++++- fs/jfs/inode.c | 17 ++++++++++- fs/nilfs2/inode.c | 13 ++++++++ fs/ocfs2/aops.c | 9 +++--- fs/reiserfs/inode.c | 17 ++++++++++- fs/xfs/linux-2.6/xfs_aops.c | 16 +++++----- include/linux/fs.h | 42 ++++--------------------- 15 files changed, 146 insertions(+), 119 deletions(-) (limited to 'fs') diff --git a/fs/block_dev.c b/fs/block_dev.c index 99d6af811747..65a0c26508e5 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -172,9 +172,8 @@ blkdev_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, struct file *file = iocb->ki_filp; struct inode *inode = file->f_mapping->host; - return blockdev_direct_IO_no_locking_newtrunc(rw, iocb, inode, - I_BDEV(inode), iov, offset, nr_segs, - blkdev_get_blocks, NULL); + return __blockdev_direct_IO(rw, iocb, inode, I_BDEV(inode), iov, offset, + nr_segs, blkdev_get_blocks, NULL, NULL, 0); } int __sync_blockdev(struct block_device *bdev, int wait) diff --git a/fs/direct-io.c b/fs/direct-io.c index a10cb91cadea..51f270b479b6 100644 --- a/fs/direct-io.c +++ b/fs/direct-io.c @@ -1136,8 +1136,27 @@ direct_io_worker(int rw, struct kiocb *iocb, struct inode *inode, return ret; } +/* + * This is a library function for use by filesystem drivers. + * + * The locking rules are governed by the flags parameter: + * - if the flags value contains DIO_LOCKING we use a fancy locking + * scheme for dumb filesystems. + * For writes this function is called under i_mutex and returns with + * i_mutex held, for reads, i_mutex is not held on entry, but it is + * taken and dropped again before returning. + * For reads and writes i_alloc_sem is taken in shared mode and released + * on I/O completion (which may happen asynchronously after returning to + * the caller). + * + * - if the flags value does NOT contain DIO_LOCKING we don't use any + * internal locking but rather rely on the filesystem to synchronize + * direct I/O reads/writes versus each other and truncate. + * For reads and writes both i_mutex and i_alloc_sem are not held on + * entry and are never taken. + */ ssize_t -__blockdev_direct_IO_newtrunc(int rw, struct kiocb *iocb, struct inode *inode, +__blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode, struct block_device *bdev, const struct iovec *iov, loff_t offset, unsigned long nr_segs, get_block_t get_block, dio_iodone_t end_io, dio_submit_t submit_io, int flags) @@ -1233,57 +1252,4 @@ __blockdev_direct_IO_newtrunc(int rw, struct kiocb *iocb, struct inode *inode, out: return retval; } -EXPORT_SYMBOL(__blockdev_direct_IO_newtrunc); - -/* - * This is a library function for use by filesystem drivers. - * - * The locking rules are governed by the flags parameter: - * - if the flags value contains DIO_LOCKING we use a fancy locking - * scheme for dumb filesystems. - * For writes this function is called under i_mutex and returns with - * i_mutex held, for reads, i_mutex is not held on entry, but it is - * taken and dropped again before returning. - * For reads and writes i_alloc_sem is taken in shared mode and released - * on I/O completion (which may happen asynchronously after returning to - * the caller). - * - * - if the flags value does NOT contain DIO_LOCKING we don't use any - * internal locking but rather rely on the filesystem to synchronize - * direct I/O reads/writes versus each other and truncate. - * For reads and writes both i_mutex and i_alloc_sem are not held on - * entry and are never taken. - */ -ssize_t -__blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode, - struct block_device *bdev, const struct iovec *iov, loff_t offset, - unsigned long nr_segs, get_block_t get_block, dio_iodone_t end_io, - dio_submit_t submit_io, int flags) -{ - ssize_t retval; - - retval = __blockdev_direct_IO_newtrunc(rw, iocb, inode, bdev, iov, - offset, nr_segs, get_block, end_io, submit_io, flags); - /* - * In case of error extending write may have instantiated a few - * blocks outside i_size. Trim these off again for DIO_LOCKING. - * NOTE: DIO_NO_LOCK/DIO_OWN_LOCK callers have to handle this in - * their own manner. This is a further example of where the old - * truncate sequence is inadequate. - * - * NOTE: filesystems with their own locking have to handle this - * on their own. - */ - if (flags & DIO_LOCKING) { - if (unlikely((rw & WRITE) && retval < 0)) { - loff_t isize = i_size_read(inode); - loff_t end = offset + iov_length(iov, nr_segs); - - if (end > isize) - vmtruncate(inode, isize); - } - } - - return retval; -} EXPORT_SYMBOL(__blockdev_direct_IO); diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index 3675088cb88c..f36e967e4fde 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -838,7 +838,7 @@ ext2_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, struct inode *inode = mapping->host; ssize_t ret; - ret = blockdev_direct_IO_newtrunc(rw, iocb, inode, inode->i_sb->s_bdev, + ret = blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov, offset, nr_segs, ext2_get_block, NULL); if (ret < 0 && (rw & WRITE)) ext2_write_failed(mapping, offset + iov_length(iov, nr_segs)); diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c index 735f0190ec2a..a66f3fe33672 100644 --- a/fs/ext3/inode.c +++ b/fs/ext3/inode.c @@ -1785,6 +1785,17 @@ retry: ret = blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov, offset, nr_segs, ext3_get_block, NULL); + /* + * In case of error extending write may have instantiated a few + * blocks outside i_size. Trim these off again. + */ + if (unlikely((rw & WRITE) && ret < 0)) { + loff_t isize = i_size_read(inode); + loff_t end = offset + iov_length(iov, nr_segs); + + if (end > isize) + vmtruncate(inode, isize); + } if (ret == -ENOSPC && ext3_should_retry_alloc(inode->i_sb, &retries)) goto retry; diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 0afc8c1d8cf3..d6a7701018a6 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -3545,15 +3545,24 @@ static ssize_t ext4_ind_direct_IO(int rw, struct kiocb *iocb, retry: if (rw == READ && ext4_should_dioread_nolock(inode)) - ret = blockdev_direct_IO_no_locking(rw, iocb, inode, + ret = __blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov, offset, nr_segs, - ext4_get_block, NULL); - else + ext4_get_block, NULL, NULL, 0); + else { ret = blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov, offset, nr_segs, ext4_get_block, NULL); + + if (unlikely((rw & WRITE) && ret < 0)) { + loff_t isize = i_size_read(inode); + loff_t end = offset + iov_length(iov, nr_segs); + + if (end > isize) + vmtruncate(inode, isize); + } + } if (ret == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries)) goto retry; diff --git a/fs/fat/inode.c b/fs/fat/inode.c index 7bf45aee56d7..ffe7c6fdc1ec 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -212,8 +212,8 @@ static ssize_t fat_direct_IO(int rw, struct kiocb *iocb, * FAT need to use the DIO_LOCKING for avoiding the race * condition of fat_get_block() and ->truncate(). */ - ret = blockdev_direct_IO_newtrunc(rw, iocb, inode, inode->i_sb->s_bdev, - iov, offset, nr_segs, fat_get_block, NULL); + ret = blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, + iov, offset, nr_segs, fat_get_block, NULL); if (ret < 0 && (rw & WRITE)) fat_write_failed(mapping, offset + iov_length(iov, nr_segs)); diff --git a/fs/gfs2/aops.c b/fs/gfs2/aops.c index 9f8b52500d63..703000d6e4d2 100644 --- a/fs/gfs2/aops.c +++ b/fs/gfs2/aops.c @@ -1047,9 +1047,9 @@ static ssize_t gfs2_direct_IO(int rw, struct kiocb *iocb, if (rv != 1) goto out; /* dio not valid, fall back to buffered i/o */ - rv = blockdev_direct_IO_no_locking(rw, iocb, inode, inode->i_sb->s_bdev, - iov, offset, nr_segs, - gfs2_get_block_direct, NULL); + rv = __blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov, + offset, nr_segs, gfs2_get_block_direct, + NULL, NULL, 0); out: gfs2_glock_dq_m(1, &gh); gfs2_holder_uninit(&gh); diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c index 14f5cb1b9fdc..07b2464b5716 100644 --- a/fs/hfs/inode.c +++ b/fs/hfs/inode.c @@ -112,9 +112,24 @@ static ssize_t hfs_direct_IO(int rw, struct kiocb *iocb, { struct file *file = iocb->ki_filp; struct inode *inode = file->f_path.dentry->d_inode->i_mapping->host; + ssize_t ret; - return blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov, + ret = blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov, offset, nr_segs, hfs_get_block, NULL); + + /* + * In case of error extending write may have instantiated a few + * blocks outside i_size. Trim these off again. + */ + if (unlikely((rw & WRITE) && ret < 0)) { + loff_t isize = i_size_read(inode); + loff_t end = offset + iov_length(iov, nr_segs); + + if (end > isize) + vmtruncate(inode, isize); + } + + return ret; } static int hfs_writepages(struct address_space *mapping, diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c index 9bbb82924a22..486021773911 100644 --- a/fs/hfsplus/inode.c +++ b/fs/hfsplus/inode.c @@ -105,9 +105,24 @@ static ssize_t hfsplus_direct_IO(int rw, struct kiocb *iocb, { struct file *file = iocb->ki_filp; struct inode *inode = file->f_path.dentry->d_inode->i_mapping->host; + ssize_t ret; - return blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov, + ret = blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov, offset, nr_segs, hfsplus_get_block, NULL); + + /* + * In case of error extending write may have instantiated a few + * blocks outside i_size. Trim these off again. + */ + if (unlikely((rw & WRITE) && ret < 0)) { + loff_t isize = i_size_read(inode); + loff_t end = offset + iov_length(iov, nr_segs); + + if (end > isize) + vmtruncate(inode, isize); + } + + return ret; } static int hfsplus_writepages(struct address_space *mapping, diff --git a/fs/jfs/inode.c b/fs/jfs/inode.c index ed9ba6fe04f5..79e6cda28181 100644 --- a/fs/jfs/inode.c +++ b/fs/jfs/inode.c @@ -317,9 +317,24 @@ static ssize_t jfs_direct_IO(int rw, struct kiocb *iocb, { struct file *file = iocb->ki_filp; struct inode *inode = file->f_mapping->host; + ssize_t ret; - return blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov, + ret = blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov, offset, nr_segs, jfs_get_block, NULL); + + /* + * In case of error extending write may have instantiated a few + * blocks outside i_size. Trim these off again. + */ + if (unlikely((rw & WRITE) && ret < 0)) { + loff_t isize = i_size_read(inode); + loff_t end = offset + iov_length(iov, nr_segs); + + if (end > isize) + vmtruncate(inode, isize); + } + + return ret; } const struct address_space_operations jfs_aops = { diff --git a/fs/nilfs2/inode.c b/fs/nilfs2/inode.c index 39e038ac8fcb..1dd9e6a7d787 100644 --- a/fs/nilfs2/inode.c +++ b/fs/nilfs2/inode.c @@ -237,6 +237,19 @@ nilfs_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, /* Needs synchronization with the cleaner */ size = blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov, offset, nr_segs, nilfs_get_block, NULL); + + /* + * In case of error extending write may have instantiated a few + * blocks outside i_size. Trim these off again. + */ + if (unlikely((rw & WRITE) && size < 0)) { + loff_t isize = i_size_read(inode); + loff_t end = offset + iov_length(iov, nr_segs); + + if (end > isize) + vmtruncate(inode, isize); + } + return size; } diff --git a/fs/ocfs2/aops.c b/fs/ocfs2/aops.c index 96337a4fbbdf..0de69c9a08be 100644 --- a/fs/ocfs2/aops.c +++ b/fs/ocfs2/aops.c @@ -643,11 +643,10 @@ static ssize_t ocfs2_direct_IO(int rw, if (i_size_read(inode) <= offset) return 0; - ret = blockdev_direct_IO_no_locking(rw, iocb, inode, - inode->i_sb->s_bdev, iov, offset, - nr_segs, - ocfs2_direct_IO_get_blocks, - ocfs2_dio_end_io); + ret = __blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, + iov, offset, nr_segs, + ocfs2_direct_IO_get_blocks, + ocfs2_dio_end_io, NULL, 0); mlog_exit(ret); return ret; diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c index 6edac85c2b93..4c1fb548ab64 100644 --- a/fs/reiserfs/inode.c +++ b/fs/reiserfs/inode.c @@ -3057,10 +3057,25 @@ static ssize_t reiserfs_direct_IO(int rw, struct kiocb *iocb, { struct file *file = iocb->ki_filp; struct inode *inode = file->f_mapping->host; + ssize_t ret; - return blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov, + ret = blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov, offset, nr_segs, reiserfs_get_blocks_direct_io, NULL); + + /* + * In case of error extending write may have instantiated a few + * blocks outside i_size. Trim these off again. + */ + if (unlikely((rw & WRITE) && ret < 0)) { + loff_t isize = i_size_read(inode); + loff_t end = offset + iov_length(iov, nr_segs); + + if (end > isize) + vmtruncate(inode, isize); + } + + return ret; } int reiserfs_setattr(struct dentry *dentry, struct iattr *attr) diff --git a/fs/xfs/linux-2.6/xfs_aops.c b/fs/xfs/linux-2.6/xfs_aops.c index d24e78f32f3e..7968d41e27ad 100644 --- a/fs/xfs/linux-2.6/xfs_aops.c +++ b/fs/xfs/linux-2.6/xfs_aops.c @@ -1478,17 +1478,17 @@ xfs_vm_direct_IO( if (rw & WRITE) { iocb->private = xfs_alloc_ioend(inode, IO_NEW); - ret = blockdev_direct_IO_no_locking(rw, iocb, inode, bdev, iov, - offset, nr_segs, - xfs_get_blocks_direct, - xfs_end_io_direct_write); + ret = __blockdev_direct_IO(rw, iocb, inode, bdev, iov, + offset, nr_segs, + xfs_get_blocks_direct, + xfs_end_io_direct_write, NULL, 0); if (ret != -EIOCBQUEUED && iocb->private) xfs_destroy_ioend(iocb->private); } else { - ret = blockdev_direct_IO_no_locking(rw, iocb, inode, bdev, iov, - offset, nr_segs, - xfs_get_blocks_direct, - NULL); + ret = __blockdev_direct_IO(rw, iocb, inode, bdev, iov, + offset, nr_segs, + xfs_get_blocks_direct, + NULL, NULL, 0); } return ret; diff --git a/include/linux/fs.h b/include/linux/fs.h index f91affb7d530..b347b2d5666f 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2269,16 +2269,6 @@ static inline int xip_truncate_page(struct address_space *mapping, loff_t from) struct bio; typedef void (dio_submit_t)(int rw, struct bio *bio, struct inode *inode, loff_t file_offset); -void dio_end_io(struct bio *bio, int error); - -ssize_t __blockdev_direct_IO_newtrunc(int rw, struct kiocb *iocb, struct inode *inode, - struct block_device *bdev, const struct iovec *iov, loff_t offset, - unsigned long nr_segs, get_block_t get_block, dio_iodone_t end_io, - dio_submit_t submit_io, int lock_type); -ssize_t __blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode, - struct block_device *bdev, const struct iovec *iov, loff_t offset, - unsigned long nr_segs, get_block_t get_block, dio_iodone_t end_io, - dio_submit_t submit_io, int lock_type); enum { /* need locking between buffered and direct access */ @@ -2288,24 +2278,13 @@ enum { DIO_SKIP_HOLES = 0x02, }; -static inline ssize_t blockdev_direct_IO_newtrunc(int rw, struct kiocb *iocb, - struct inode *inode, struct block_device *bdev, const struct iovec *iov, - loff_t offset, unsigned long nr_segs, get_block_t get_block, - dio_iodone_t end_io) -{ - return __blockdev_direct_IO_newtrunc(rw, iocb, inode, bdev, iov, offset, - nr_segs, get_block, end_io, NULL, - DIO_LOCKING | DIO_SKIP_HOLES); -} +void dio_end_io(struct bio *bio, int error); + +ssize_t __blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode, + struct block_device *bdev, const struct iovec *iov, loff_t offset, + unsigned long nr_segs, get_block_t get_block, dio_iodone_t end_io, + dio_submit_t submit_io, int flags); -static inline ssize_t blockdev_direct_IO_no_locking_newtrunc(int rw, struct kiocb *iocb, - struct inode *inode, struct block_device *bdev, const struct iovec *iov, - loff_t offset, unsigned long nr_segs, get_block_t get_block, - dio_iodone_t end_io) -{ - return __blockdev_direct_IO_newtrunc(rw, iocb, inode, bdev, iov, offset, - nr_segs, get_block, end_io, NULL, 0); -} static inline ssize_t blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode, struct block_device *bdev, const struct iovec *iov, loff_t offset, unsigned long nr_segs, get_block_t get_block, @@ -2315,15 +2294,6 @@ static inline ssize_t blockdev_direct_IO(int rw, struct kiocb *iocb, nr_segs, get_block, end_io, NULL, DIO_LOCKING | DIO_SKIP_HOLES); } - -static inline ssize_t blockdev_direct_IO_no_locking(int rw, struct kiocb *iocb, - struct inode *inode, struct block_device *bdev, const struct iovec *iov, - loff_t offset, unsigned long nr_segs, get_block_t get_block, - dio_iodone_t end_io) -{ - return __blockdev_direct_IO(rw, iocb, inode, bdev, iov, offset, - nr_segs, get_block, end_io, NULL, 0); -} #endif extern const struct file_operations generic_ro_fops; -- cgit v1.2.3 From ea0f04e59543bafb3d2cbe37a0d375acb0bb2c34 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 4 Jun 2010 11:29:54 +0200 Subject: get rid of nobh_write_begin_newtrunc Move the call to vmtruncate to get rid of accessive blocks to the only remaining caller and rename the non-truncating version to nobh_write_begin. Get rid of the superflous file argument to it while we're at it. Signed-off-by: Christoph Hellwig Signed-off-by: Al Viro --- fs/buffer.c | 37 ++++--------------------------------- fs/ext2/inode.c | 9 ++------- fs/jfs/inode.c | 11 ++++++++++- include/linux/buffer_head.h | 6 +----- 4 files changed, 17 insertions(+), 46 deletions(-) (limited to 'fs') diff --git a/fs/buffer.c b/fs/buffer.c index d54812b198e9..559daf76bca4 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -2510,11 +2510,11 @@ static void attach_nobh_buffers(struct page *page, struct buffer_head *head) } /* - * Filesystems implementing the new truncate sequence should use the - * _newtrunc postfix variant which won't incorrectly call vmtruncate. + * On entry, the page is fully not uptodate. + * On exit the page is fully uptodate in the areas outside (from,to) * The filesystem needs to handle block truncation upon failure. */ -int nobh_write_begin_newtrunc(struct file *file, struct address_space *mapping, +int nobh_write_begin(struct address_space *mapping, loff_t pos, unsigned len, unsigned flags, struct page **pagep, void **fsdata, get_block_t *get_block) @@ -2547,7 +2547,7 @@ int nobh_write_begin_newtrunc(struct file *file, struct address_space *mapping, unlock_page(page); page_cache_release(page); *pagep = NULL; - return block_write_begin_newtrunc(file, mapping, pos, len, + return block_write_begin_newtrunc(NULL, mapping, pos, len, flags, pagep, fsdata, get_block); } @@ -2654,35 +2654,6 @@ out_release: return ret; } -EXPORT_SYMBOL(nobh_write_begin_newtrunc); - -/* - * On entry, the page is fully not uptodate. - * On exit the page is fully uptodate in the areas outside (from,to) - */ -int nobh_write_begin(struct file *file, struct address_space *mapping, - loff_t pos, unsigned len, unsigned flags, - struct page **pagep, void **fsdata, - get_block_t *get_block) -{ - int ret; - - ret = nobh_write_begin_newtrunc(file, mapping, pos, len, flags, - pagep, fsdata, get_block); - - /* - * prepare_write() may have instantiated a few blocks - * outside i_size. Trim these off again. Don't need - * i_size_read because we hold i_mutex. - */ - if (unlikely(ret)) { - loff_t isize = mapping->host->i_size; - if (pos + len > isize) - vmtruncate(mapping->host, isize); - } - - return ret; -} EXPORT_SYMBOL(nobh_write_begin); int nobh_write_end(struct file *file, struct address_space *mapping, diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index f36e967e4fde..348805cd4109 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -806,13 +806,8 @@ ext2_nobh_write_begin(struct file *file, struct address_space *mapping, { int ret; - /* - * Dir-in-pagecache still uses ext2_write_begin. Would have to rework - * directory handling code to pass around offsets rather than struct - * pages in order to make this work easily. - */ - ret = nobh_write_begin_newtrunc(file, mapping, pos, len, flags, pagep, - fsdata, ext2_get_block); + ret = nobh_write_begin(mapping, pos, len, flags, pagep, fsdata, + ext2_get_block); if (ret < 0) ext2_write_failed(mapping, pos + len); return ret; diff --git a/fs/jfs/inode.c b/fs/jfs/inode.c index 79e6cda28181..c38dc1806281 100644 --- a/fs/jfs/inode.c +++ b/fs/jfs/inode.c @@ -303,8 +303,17 @@ static int jfs_write_begin(struct file *file, struct address_space *mapping, loff_t pos, unsigned len, unsigned flags, struct page **pagep, void **fsdata) { - return nobh_write_begin(file, mapping, pos, len, flags, pagep, fsdata, + int ret; + + ret = nobh_write_begin(mapping, pos, len, flags, pagep, fsdata, jfs_get_block); + if (unlikely(ret)) { + loff_t isize = mapping->host->i_size; + if (pos + len > isize) + vmtruncate(mapping->host, isize); + } + + return ret; } static sector_t jfs_bmap(struct address_space *mapping, sector_t block) diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h index 1b9ba193b789..cfda5f0b2a4b 100644 --- a/include/linux/buffer_head.h +++ b/include/linux/buffer_head.h @@ -231,11 +231,7 @@ void block_sync_page(struct page *); sector_t generic_block_bmap(struct address_space *, sector_t, get_block_t *); int block_truncate_page(struct address_space *, loff_t, get_block_t *); int file_fsync(struct file *, int); -int nobh_write_begin_newtrunc(struct file *, struct address_space *, - loff_t, unsigned, unsigned, - struct page **, void **, get_block_t*); -int nobh_write_begin(struct file *, struct address_space *, - loff_t, unsigned, unsigned, +int nobh_write_begin(struct address_space *, loff_t, unsigned, unsigned, struct page **, void **, get_block_t*); int nobh_write_end(struct file *, struct address_space *, loff_t, unsigned, unsigned, -- cgit v1.2.3 From 282dc178849882289d30e58b54be6b2799b351aa Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 4 Jun 2010 11:29:55 +0200 Subject: get rid of cont_write_begin_newtrunc Move the call to vmtruncate to get rid of accessive blocks to the callers in preparation of the new truncate sequence and rename the non-truncating version to cont_write_begin. Signed-off-by: Christoph Hellwig Signed-off-by: Al Viro --- fs/adfs/inode.c | 11 ++++++++++- fs/affs/file.c | 11 ++++++++++- fs/buffer.c | 21 +-------------------- fs/fat/inode.c | 2 +- fs/hfs/inode.c | 11 ++++++++++- fs/hfsplus/inode.c | 11 ++++++++++- fs/hpfs/file.c | 11 ++++++++++- fs/qnx4/inode.c | 11 ++++++++++- include/linux/buffer_head.h | 3 --- 9 files changed, 62 insertions(+), 30 deletions(-) (limited to 'fs') diff --git a/fs/adfs/inode.c b/fs/adfs/inode.c index 6f850b06ab62..b3dec193036b 100644 --- a/fs/adfs/inode.c +++ b/fs/adfs/inode.c @@ -50,10 +50,19 @@ static int adfs_write_begin(struct file *file, struct address_space *mapping, loff_t pos, unsigned len, unsigned flags, struct page **pagep, void **fsdata) { + int ret; + *pagep = NULL; - return cont_write_begin(file, mapping, pos, len, flags, pagep, fsdata, + ret = cont_write_begin(file, mapping, pos, len, flags, pagep, fsdata, adfs_get_block, &ADFS_I(mapping->host)->mmu_private); + if (unlikely(ret)) { + loff_t isize = mapping->host->i_size; + if (pos + len > isize) + vmtruncate(mapping->host, isize); + } + + return ret; } static sector_t _adfs_bmap(struct address_space *mapping, sector_t block) diff --git a/fs/affs/file.c b/fs/affs/file.c index 322710c3eedf..c4a9875bd1a6 100644 --- a/fs/affs/file.c +++ b/fs/affs/file.c @@ -406,10 +406,19 @@ static int affs_write_begin(struct file *file, struct address_space *mapping, loff_t pos, unsigned len, unsigned flags, struct page **pagep, void **fsdata) { + int ret; + *pagep = NULL; - return cont_write_begin(file, mapping, pos, len, flags, pagep, fsdata, + ret = cont_write_begin(file, mapping, pos, len, flags, pagep, fsdata, affs_get_block, &AFFS_I(mapping->host)->mmu_private); + if (unlikely(ret)) { + loff_t isize = mapping->host->i_size; + if (pos + len > isize) + vmtruncate(mapping->host, isize); + } + + return ret; } static sector_t _affs_bmap(struct address_space *mapping, sector_t block) diff --git a/fs/buffer.c b/fs/buffer.c index 559daf76bca4..14529ec759b9 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -2351,7 +2351,7 @@ out: * For moronic filesystems that do not allow holes in file. * We may have to extend the file. */ -int cont_write_begin_newtrunc(struct file *file, struct address_space *mapping, +int cont_write_begin(struct file *file, struct address_space *mapping, loff_t pos, unsigned len, unsigned flags, struct page **pagep, void **fsdata, get_block_t *get_block, loff_t *bytes) @@ -2377,25 +2377,6 @@ int cont_write_begin_newtrunc(struct file *file, struct address_space *mapping, out: return err; } -EXPORT_SYMBOL(cont_write_begin_newtrunc); - -int cont_write_begin(struct file *file, struct address_space *mapping, - loff_t pos, unsigned len, unsigned flags, - struct page **pagep, void **fsdata, - get_block_t *get_block, loff_t *bytes) -{ - int ret; - - ret = cont_write_begin_newtrunc(file, mapping, pos, len, flags, - pagep, fsdata, get_block, bytes); - if (unlikely(ret)) { - loff_t isize = mapping->host->i_size; - if (pos + len > isize) - vmtruncate(mapping->host, isize); - } - - return ret; -} EXPORT_SYMBOL(cont_write_begin); int block_prepare_write(struct page *page, unsigned from, unsigned to, diff --git a/fs/fat/inode.c b/fs/fat/inode.c index ffe7c6fdc1ec..ec6a699a4023 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -159,7 +159,7 @@ static int fat_write_begin(struct file *file, struct address_space *mapping, int err; *pagep = NULL; - err = cont_write_begin_newtrunc(file, mapping, pos, len, flags, + err = cont_write_begin(file, mapping, pos, len, flags, pagep, fsdata, fat_get_block, &MSDOS_I(mapping->host)->mmu_private); if (err < 0) diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c index 07b2464b5716..8df18e63eb6b 100644 --- a/fs/hfs/inode.c +++ b/fs/hfs/inode.c @@ -39,10 +39,19 @@ static int hfs_write_begin(struct file *file, struct address_space *mapping, loff_t pos, unsigned len, unsigned flags, struct page **pagep, void **fsdata) { + int ret; + *pagep = NULL; - return cont_write_begin(file, mapping, pos, len, flags, pagep, fsdata, + ret = cont_write_begin(file, mapping, pos, len, flags, pagep, fsdata, hfs_get_block, &HFS_I(mapping->host)->phys_size); + if (unlikely(ret)) { + loff_t isize = mapping->host->i_size; + if (pos + len > isize) + vmtruncate(mapping->host, isize); + } + + return ret; } static sector_t hfs_bmap(struct address_space *mapping, sector_t block) diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c index 486021773911..88bf1b562641 100644 --- a/fs/hfsplus/inode.c +++ b/fs/hfsplus/inode.c @@ -31,10 +31,19 @@ static int hfsplus_write_begin(struct file *file, struct address_space *mapping, loff_t pos, unsigned len, unsigned flags, struct page **pagep, void **fsdata) { + int ret; + *pagep = NULL; - return cont_write_begin(file, mapping, pos, len, flags, pagep, fsdata, + ret = cont_write_begin(file, mapping, pos, len, flags, pagep, fsdata, hfsplus_get_block, &HFSPLUS_I(mapping->host).phys_size); + if (unlikely(ret)) { + loff_t isize = mapping->host->i_size; + if (pos + len > isize) + vmtruncate(mapping->host, isize); + } + + return ret; } static sector_t hfsplus_bmap(struct address_space *mapping, sector_t block) diff --git a/fs/hpfs/file.c b/fs/hpfs/file.c index a9ae9bfa752f..c0340887c7ea 100644 --- a/fs/hpfs/file.c +++ b/fs/hpfs/file.c @@ -97,10 +97,19 @@ static int hpfs_write_begin(struct file *file, struct address_space *mapping, loff_t pos, unsigned len, unsigned flags, struct page **pagep, void **fsdata) { + int ret; + *pagep = NULL; - return cont_write_begin(file, mapping, pos, len, flags, pagep, fsdata, + ret = cont_write_begin(file, mapping, pos, len, flags, pagep, fsdata, hpfs_get_block, &hpfs_i(mapping->host)->mmu_private); + if (unlikely(ret)) { + loff_t isize = mapping->host->i_size; + if (pos + len > isize) + vmtruncate(mapping->host, isize); + } + + return ret; } static sector_t _hpfs_bmap(struct address_space *mapping, sector_t block) diff --git a/fs/qnx4/inode.c b/fs/qnx4/inode.c index 277575ddc05c..16829722be93 100644 --- a/fs/qnx4/inode.c +++ b/fs/qnx4/inode.c @@ -320,10 +320,19 @@ static int qnx4_write_begin(struct file *file, struct address_space *mapping, struct page **pagep, void **fsdata) { struct qnx4_inode_info *qnx4_inode = qnx4_i(mapping->host); + int ret; + *pagep = NULL; - return cont_write_begin(file, mapping, pos, len, flags, pagep, fsdata, + ret = cont_write_begin(file, mapping, pos, len, flags, pagep, fsdata, qnx4_get_block, &qnx4_inode->mmu_private); + if (unlikely(ret)) { + loff_t isize = mapping->host->i_size; + if (pos + len > isize) + vmtruncate(mapping->host, isize); + } + + return ret; } static sector_t qnx4_bmap(struct address_space *mapping, sector_t block) { diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h index cfda5f0b2a4b..7638647f0424 100644 --- a/include/linux/buffer_head.h +++ b/include/linux/buffer_head.h @@ -217,9 +217,6 @@ int generic_write_end(struct file *, struct address_space *, struct page *, void *); void page_zero_new_buffers(struct page *page, unsigned from, unsigned to); int block_prepare_write(struct page*, unsigned, unsigned, get_block_t*); -int cont_write_begin_newtrunc(struct file *, struct address_space *, loff_t, - unsigned, unsigned, struct page **, void **, - get_block_t *, loff_t *); int cont_write_begin(struct file *, struct address_space *, loff_t, unsigned, unsigned, struct page **, void **, get_block_t *, loff_t *); -- cgit v1.2.3 From f4e420dc423148fba637af1ab618fa8896dfb2d6 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 4 Jun 2010 11:29:56 +0200 Subject: clean up write_begin usage for directories in pagecache For filesystem that implement directories in pagecache we call block_write_begin with an already allocated page for this code, while the normal regular file write path uses the default block_write_begin behaviour. Get rid of the __foofs_write_begin helper and opencode the normal write_begin call in foofs_write_begin, while adding a new foofs_prepare_chunk helper for the directory code. The added benefit is that foofs_prepare_chunk has a much saner calling convention. Note that the interruptible flag passed into block_write_begin is always ignored if we already pass in a page (see next patch for details), and we never were doing truncations of exessive blocks for this case either so we can switch directly to block_write_begin_newtrunc. Signed-off-by: Christoph Hellwig Signed-off-by: Al Viro --- fs/ext2/dir.c | 24 ++++++++++++------------ fs/ext2/ext2.h | 3 --- fs/ext2/inode.c | 11 ++--------- fs/minix/dir.c | 21 +++++++-------------- fs/minix/inode.c | 11 +++++------ fs/minix/minix.h | 4 +--- fs/nilfs2/dir.c | 26 +++++++------------------- fs/sysv/dir.c | 21 +++++++-------------- fs/sysv/itree.c | 11 +++++------ fs/sysv/sysv.h | 4 +--- fs/ufs/dir.c | 13 ++++--------- fs/ufs/inode.c | 11 +++++------ fs/ufs/util.h | 4 +--- 13 files changed, 57 insertions(+), 107 deletions(-) (limited to 'fs') diff --git a/fs/ext2/dir.c b/fs/ext2/dir.c index 7516957273ed..6b946bae11cf 100644 --- a/fs/ext2/dir.c +++ b/fs/ext2/dir.c @@ -448,6 +448,12 @@ ino_t ext2_inode_by_name(struct inode *dir, struct qstr *child) return res; } +static int ext2_prepare_chunk(struct page *page, loff_t pos, unsigned len) +{ + return block_write_begin_newtrunc(NULL, page->mapping, pos, len, 0, + &page, NULL, ext2_get_block); +} + /* Releases the page */ void ext2_set_link(struct inode *dir, struct ext2_dir_entry_2 *de, struct page *page, struct inode *inode, int update_times) @@ -458,8 +464,7 @@ void ext2_set_link(struct inode *dir, struct ext2_dir_entry_2 *de, int err; lock_page(page); - err = __ext2_write_begin(NULL, page->mapping, pos, len, - AOP_FLAG_UNINTERRUPTIBLE, &page, NULL); + err = ext2_prepare_chunk(page, pos, len); BUG_ON(err); de->inode = cpu_to_le32(inode->i_ino); ext2_set_de_type(de, inode); @@ -542,8 +547,7 @@ int ext2_add_link (struct dentry *dentry, struct inode *inode) got_it: pos = page_offset(page) + (char*)de - (char*)page_address(page); - err = __ext2_write_begin(NULL, page->mapping, pos, rec_len, 0, - &page, NULL); + err = ext2_prepare_chunk(page, pos, rec_len); if (err) goto out_unlock; if (de->inode) { @@ -576,8 +580,7 @@ out_unlock: */ int ext2_delete_entry (struct ext2_dir_entry_2 * dir, struct page * page ) { - struct address_space *mapping = page->mapping; - struct inode *inode = mapping->host; + struct inode *inode = page->mapping->host; char *kaddr = page_address(page); unsigned from = ((char*)dir - kaddr) & ~(ext2_chunk_size(inode)-1); unsigned to = ((char *)dir - kaddr) + @@ -601,8 +604,7 @@ int ext2_delete_entry (struct ext2_dir_entry_2 * dir, struct page * page ) from = (char*)pde - (char*)page_address(page); pos = page_offset(page) + from; lock_page(page); - err = __ext2_write_begin(NULL, page->mapping, pos, to - from, 0, - &page, NULL); + err = ext2_prepare_chunk(page, pos, to - from); BUG_ON(err); if (pde) pde->rec_len = ext2_rec_len_to_disk(to - from); @@ -621,8 +623,7 @@ out: */ int ext2_make_empty(struct inode *inode, struct inode *parent) { - struct address_space *mapping = inode->i_mapping; - struct page *page = grab_cache_page(mapping, 0); + struct page *page = grab_cache_page(inode->i_mapping, 0); unsigned chunk_size = ext2_chunk_size(inode); struct ext2_dir_entry_2 * de; int err; @@ -631,8 +632,7 @@ int ext2_make_empty(struct inode *inode, struct inode *parent) if (!page) return -ENOMEM; - err = __ext2_write_begin(NULL, page->mapping, 0, chunk_size, 0, - &page, NULL); + err = ext2_prepare_chunk(page, 0, chunk_size); if (err) { unlock_page(page); goto fail; diff --git a/fs/ext2/ext2.h b/fs/ext2/ext2.h index 52b34f1d2738..8f53d11bf957 100644 --- a/fs/ext2/ext2.h +++ b/fs/ext2/ext2.h @@ -127,9 +127,6 @@ extern void ext2_set_inode_flags(struct inode *inode); extern void ext2_get_inode_flags(struct ext2_inode_info *); extern int ext2_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, u64 start, u64 len); -int __ext2_write_begin(struct file *file, struct address_space *mapping, - loff_t pos, unsigned len, unsigned flags, - struct page **pagep, void **fsdata); /* ioctl.c */ extern long ext2_ioctl(struct file *, unsigned int, unsigned long); diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index 348805cd4109..2f4dfbcd7696 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -765,14 +765,6 @@ ext2_readpages(struct file *file, struct address_space *mapping, return mpage_readpages(mapping, pages, nr_pages, ext2_get_block); } -int __ext2_write_begin(struct file *file, struct address_space *mapping, - loff_t pos, unsigned len, unsigned flags, - struct page **pagep, void **fsdata) -{ - return block_write_begin_newtrunc(file, mapping, pos, len, flags, - pagep, fsdata, ext2_get_block); -} - static int ext2_write_begin(struct file *file, struct address_space *mapping, loff_t pos, unsigned len, unsigned flags, @@ -781,7 +773,8 @@ ext2_write_begin(struct file *file, struct address_space *mapping, int ret; *pagep = NULL; - ret = __ext2_write_begin(file, mapping, pos, len, flags, pagep, fsdata); + ret = block_write_begin_newtrunc(file, mapping, pos, len, flags, + pagep, fsdata, ext2_get_block); if (ret < 0) ext2_write_failed(mapping, pos + len); return ret; diff --git a/fs/minix/dir.c b/fs/minix/dir.c index 1dbf921ca44b..085a9262c692 100644 --- a/fs/minix/dir.c +++ b/fs/minix/dir.c @@ -271,8 +271,7 @@ int minix_add_link(struct dentry *dentry, struct inode *inode) got_it: pos = page_offset(page) + p - (char *)page_address(page); - err = __minix_write_begin(NULL, page->mapping, pos, sbi->s_dirsize, - AOP_FLAG_UNINTERRUPTIBLE, &page, NULL); + err = minix_prepare_chunk(page, pos, sbi->s_dirsize); if (err) goto out_unlock; memcpy (namx, name, namelen); @@ -297,8 +296,7 @@ out_unlock: int minix_delete_entry(struct minix_dir_entry *de, struct page *page) { - struct address_space *mapping = page->mapping; - struct inode *inode = (struct inode*)mapping->host; + struct inode *inode = page->mapping->host; char *kaddr = page_address(page); loff_t pos = page_offset(page) + (char*)de - kaddr; struct minix_sb_info *sbi = minix_sb(inode->i_sb); @@ -306,8 +304,7 @@ int minix_delete_entry(struct minix_dir_entry *de, struct page *page) int err; lock_page(page); - err = __minix_write_begin(NULL, mapping, pos, len, - AOP_FLAG_UNINTERRUPTIBLE, &page, NULL); + err = minix_prepare_chunk(page, pos, len); if (err == 0) { if (sbi->s_version == MINIX_V3) ((minix3_dirent *) de)->inode = 0; @@ -325,16 +322,14 @@ int minix_delete_entry(struct minix_dir_entry *de, struct page *page) int minix_make_empty(struct inode *inode, struct inode *dir) { - struct address_space *mapping = inode->i_mapping; - struct page *page = grab_cache_page(mapping, 0); + struct page *page = grab_cache_page(inode->i_mapping, 0); struct minix_sb_info *sbi = minix_sb(inode->i_sb); char *kaddr; int err; if (!page) return -ENOMEM; - err = __minix_write_begin(NULL, mapping, 0, 2 * sbi->s_dirsize, - AOP_FLAG_UNINTERRUPTIBLE, &page, NULL); + err = minix_prepare_chunk(page, 0, 2 * sbi->s_dirsize); if (err) { unlock_page(page); goto fail; @@ -425,8 +420,7 @@ not_empty: void minix_set_link(struct minix_dir_entry *de, struct page *page, struct inode *inode) { - struct address_space *mapping = page->mapping; - struct inode *dir = mapping->host; + struct inode *dir = page->mapping->host; struct minix_sb_info *sbi = minix_sb(dir->i_sb); loff_t pos = page_offset(page) + (char *)de-(char*)page_address(page); @@ -434,8 +428,7 @@ void minix_set_link(struct minix_dir_entry *de, struct page *page, lock_page(page); - err = __minix_write_begin(NULL, mapping, pos, sbi->s_dirsize, - AOP_FLAG_UNINTERRUPTIBLE, &page, NULL); + err = minix_prepare_chunk(page, pos, sbi->s_dirsize); if (err == 0) { if (sbi->s_version == MINIX_V3) ((minix3_dirent *) de)->inode = inode->i_ino; diff --git a/fs/minix/inode.c b/fs/minix/inode.c index 756f8c93780c..f4abe45229bb 100644 --- a/fs/minix/inode.c +++ b/fs/minix/inode.c @@ -357,12 +357,10 @@ static int minix_readpage(struct file *file, struct page *page) return block_read_full_page(page,minix_get_block); } -int __minix_write_begin(struct file *file, struct address_space *mapping, - loff_t pos, unsigned len, unsigned flags, - struct page **pagep, void **fsdata) +int minix_prepare_chunk(struct page *page, loff_t pos, unsigned len) { - return block_write_begin(file, mapping, pos, len, flags, pagep, fsdata, - minix_get_block); + return block_write_begin_newtrunc(NULL, page->mapping, pos, len, 0, + &page, NULL, minix_get_block); } static int minix_write_begin(struct file *file, struct address_space *mapping, @@ -370,7 +368,8 @@ static int minix_write_begin(struct file *file, struct address_space *mapping, struct page **pagep, void **fsdata) { *pagep = NULL; - return __minix_write_begin(file, mapping, pos, len, flags, pagep, fsdata); + return block_write_begin(file, mapping, pos, len, flags, pagep, fsdata, + minix_get_block); } static sector_t minix_bmap(struct address_space *mapping, sector_t block) diff --git a/fs/minix/minix.h b/fs/minix/minix.h index 111f34ee9e3b..407b1c84911e 100644 --- a/fs/minix/minix.h +++ b/fs/minix/minix.h @@ -53,9 +53,7 @@ extern int minix_new_block(struct inode * inode); extern void minix_free_block(struct inode *inode, unsigned long block); extern unsigned long minix_count_free_blocks(struct minix_sb_info *sbi); extern int minix_getattr(struct vfsmount *, struct dentry *, struct kstat *); -extern int __minix_write_begin(struct file *file, struct address_space *mapping, - loff_t pos, unsigned len, unsigned flags, - struct page **pagep, void **fsdata); +extern int minix_prepare_chunk(struct page *page, loff_t pos, unsigned len); extern void V1_minix_truncate(struct inode *); extern void V2_minix_truncate(struct inode *); diff --git a/fs/nilfs2/dir.c b/fs/nilfs2/dir.c index 85c89dfc71f0..fc2bcfa599a3 100644 --- a/fs/nilfs2/dir.c +++ b/fs/nilfs2/dir.c @@ -80,23 +80,11 @@ static unsigned nilfs_last_byte(struct inode *inode, unsigned long page_nr) return last_byte; } -static int nilfs_prepare_chunk_uninterruptible(struct page *page, - struct address_space *mapping, - unsigned from, unsigned to) +static int nilfs_prepare_chunk(struct page *page, unsigned from, unsigned to) { loff_t pos = page_offset(page) + from; - return block_write_begin(NULL, mapping, pos, to - from, - AOP_FLAG_UNINTERRUPTIBLE, &page, - NULL, nilfs_get_block); -} - -static int nilfs_prepare_chunk(struct page *page, - struct address_space *mapping, - unsigned from, unsigned to) -{ - loff_t pos = page_offset(page) + from; - return block_write_begin(NULL, mapping, pos, to - from, 0, &page, - NULL, nilfs_get_block); + return block_write_begin_newtrunc(NULL, page->mapping, pos, to - from, + 0, &page, NULL, nilfs_get_block); } static void nilfs_commit_chunk(struct page *page, @@ -449,7 +437,7 @@ void nilfs_set_link(struct inode *dir, struct nilfs_dir_entry *de, int err; lock_page(page); - err = nilfs_prepare_chunk_uninterruptible(page, mapping, from, to); + err = nilfs_prepare_chunk(page, from, to); BUG_ON(err); de->inode = cpu_to_le64(inode->i_ino); nilfs_set_de_type(de, inode); @@ -530,7 +518,7 @@ int nilfs_add_link(struct dentry *dentry, struct inode *inode) got_it: from = (char *)de - (char *)page_address(page); to = from + rec_len; - err = nilfs_prepare_chunk(page, page->mapping, from, to); + err = nilfs_prepare_chunk(page, from, to); if (err) goto out_unlock; if (de->inode) { @@ -587,7 +575,7 @@ int nilfs_delete_entry(struct nilfs_dir_entry *dir, struct page *page) if (pde) from = (char *)pde - (char *)page_address(page); lock_page(page); - err = nilfs_prepare_chunk(page, mapping, from, to); + err = nilfs_prepare_chunk(page, from, to); BUG_ON(err); if (pde) pde->rec_len = cpu_to_le16(to - from); @@ -615,7 +603,7 @@ int nilfs_make_empty(struct inode *inode, struct inode *parent) if (!page) return -ENOMEM; - err = nilfs_prepare_chunk(page, mapping, 0, chunk_size); + err = nilfs_prepare_chunk(page, 0, chunk_size); if (unlikely(err)) { unlock_page(page); goto fail; diff --git a/fs/sysv/dir.c b/fs/sysv/dir.c index 79941e4964a4..a77c42157620 100644 --- a/fs/sysv/dir.c +++ b/fs/sysv/dir.c @@ -218,8 +218,7 @@ got_it: pos = page_offset(page) + (char*)de - (char*)page_address(page); lock_page(page); - err = __sysv_write_begin(NULL, page->mapping, pos, SYSV_DIRSIZE, - AOP_FLAG_UNINTERRUPTIBLE, &page, NULL); + err = sysv_prepare_chunk(page, pos, SYSV_DIRSIZE); if (err) goto out_unlock; memcpy (de->name, name, namelen); @@ -239,15 +238,13 @@ out_unlock: int sysv_delete_entry(struct sysv_dir_entry *de, struct page *page) { - struct address_space *mapping = page->mapping; - struct inode *inode = (struct inode*)mapping->host; + struct inode *inode = page->mapping->host; char *kaddr = (char*)page_address(page); loff_t pos = page_offset(page) + (char *)de - kaddr; int err; lock_page(page); - err = __sysv_write_begin(NULL, mapping, pos, SYSV_DIRSIZE, - AOP_FLAG_UNINTERRUPTIBLE, &page, NULL); + err = sysv_prepare_chunk(page, pos, SYSV_DIRSIZE); BUG_ON(err); de->inode = 0; err = dir_commit_chunk(page, pos, SYSV_DIRSIZE); @@ -259,16 +256,14 @@ int sysv_delete_entry(struct sysv_dir_entry *de, struct page *page) int sysv_make_empty(struct inode *inode, struct inode *dir) { - struct address_space *mapping = inode->i_mapping; - struct page *page = grab_cache_page(mapping, 0); + struct page *page = grab_cache_page(inode->i_mapping, 0); struct sysv_dir_entry * de; char *base; int err; if (!page) return -ENOMEM; - err = __sysv_write_begin(NULL, mapping, 0, 2 * SYSV_DIRSIZE, - AOP_FLAG_UNINTERRUPTIBLE, &page, NULL); + err = sysv_prepare_chunk(page, 0, 2 * SYSV_DIRSIZE); if (err) { unlock_page(page); goto fail; @@ -341,15 +336,13 @@ not_empty: void sysv_set_link(struct sysv_dir_entry *de, struct page *page, struct inode *inode) { - struct address_space *mapping = page->mapping; - struct inode *dir = mapping->host; + struct inode *dir = page->mapping->host; loff_t pos = page_offset(page) + (char *)de-(char*)page_address(page); int err; lock_page(page); - err = __sysv_write_begin(NULL, mapping, pos, SYSV_DIRSIZE, - AOP_FLAG_UNINTERRUPTIBLE, &page, NULL); + err = sysv_prepare_chunk(page, pos, SYSV_DIRSIZE); BUG_ON(err); de->inode = cpu_to_fs16(SYSV_SB(inode->i_sb), inode->i_ino); err = dir_commit_chunk(page, pos, SYSV_DIRSIZE); diff --git a/fs/sysv/itree.c b/fs/sysv/itree.c index f042eec464c2..4068f485cfd6 100644 --- a/fs/sysv/itree.c +++ b/fs/sysv/itree.c @@ -459,12 +459,10 @@ static int sysv_readpage(struct file *file, struct page *page) return block_read_full_page(page,get_block); } -int __sysv_write_begin(struct file *file, struct address_space *mapping, - loff_t pos, unsigned len, unsigned flags, - struct page **pagep, void **fsdata) +int sysv_prepare_chunk(struct page *page, loff_t pos, unsigned len) { - return block_write_begin(file, mapping, pos, len, flags, pagep, fsdata, - get_block); + return block_write_begin_newtrunc(NULL, page->mapping, pos, len, 0, + &page, NULL, get_block); } static int sysv_write_begin(struct file *file, struct address_space *mapping, @@ -472,7 +470,8 @@ static int sysv_write_begin(struct file *file, struct address_space *mapping, struct page **pagep, void **fsdata) { *pagep = NULL; - return __sysv_write_begin(file, mapping, pos, len, flags, pagep, fsdata); + return block_write_begin(file, mapping, pos, len, flags, pagep, fsdata, + get_block); } static sector_t sysv_bmap(struct address_space *mapping, sector_t block) diff --git a/fs/sysv/sysv.h b/fs/sysv/sysv.h index 94cb9b4d76c2..bb55cdb394bf 100644 --- a/fs/sysv/sysv.h +++ b/fs/sysv/sysv.h @@ -136,9 +136,7 @@ extern unsigned long sysv_count_free_blocks(struct super_block *); /* itree.c */ extern void sysv_truncate(struct inode *); -extern int __sysv_write_begin(struct file *file, struct address_space *mapping, - loff_t pos, unsigned len, unsigned flags, - struct page **pagep, void **fsdata); +extern int sysv_prepare_chunk(struct page *page, loff_t pos, unsigned len); /* inode.c */ extern struct inode *sysv_iget(struct super_block *, unsigned int); diff --git a/fs/ufs/dir.c b/fs/ufs/dir.c index ec784756dc65..dbc90994715a 100644 --- a/fs/ufs/dir.c +++ b/fs/ufs/dir.c @@ -95,8 +95,7 @@ void ufs_set_link(struct inode *dir, struct ufs_dir_entry *de, int err; lock_page(page); - err = __ufs_write_begin(NULL, page->mapping, pos, len, - AOP_FLAG_UNINTERRUPTIBLE, &page, NULL); + err = ufs_prepare_chunk(page, pos, len); BUG_ON(err); de->d_ino = cpu_to_fs32(dir->i_sb, inode->i_ino); @@ -381,8 +380,7 @@ int ufs_add_link(struct dentry *dentry, struct inode *inode) got_it: pos = page_offset(page) + (char*)de - (char*)page_address(page); - err = __ufs_write_begin(NULL, page->mapping, pos, rec_len, - AOP_FLAG_UNINTERRUPTIBLE, &page, NULL); + err = ufs_prepare_chunk(page, pos, rec_len); if (err) goto out_unlock; if (de->d_ino) { @@ -518,7 +516,6 @@ int ufs_delete_entry(struct inode *inode, struct ufs_dir_entry *dir, struct page * page) { struct super_block *sb = inode->i_sb; - struct address_space *mapping = page->mapping; char *kaddr = page_address(page); unsigned from = ((char*)dir - kaddr) & ~(UFS_SB(sb)->s_uspi->s_dirblksize - 1); unsigned to = ((char*)dir - kaddr) + fs16_to_cpu(sb, dir->d_reclen); @@ -549,8 +546,7 @@ int ufs_delete_entry(struct inode *inode, struct ufs_dir_entry *dir, pos = page_offset(page) + from; lock_page(page); - err = __ufs_write_begin(NULL, mapping, pos, to - from, - AOP_FLAG_UNINTERRUPTIBLE, &page, NULL); + err = ufs_prepare_chunk(page, pos, to - from); BUG_ON(err); if (pde) pde->d_reclen = cpu_to_fs16(sb, to - from); @@ -577,8 +573,7 @@ int ufs_make_empty(struct inode * inode, struct inode *dir) if (!page) return -ENOMEM; - err = __ufs_write_begin(NULL, mapping, 0, chunk_size, - AOP_FLAG_UNINTERRUPTIBLE, &page, NULL); + err = ufs_prepare_chunk(page, 0, chunk_size); if (err) { unlock_page(page); goto fail; diff --git a/fs/ufs/inode.c b/fs/ufs/inode.c index 73fe773aa034..a9555b1ffd28 100644 --- a/fs/ufs/inode.c +++ b/fs/ufs/inode.c @@ -558,12 +558,10 @@ static int ufs_readpage(struct file *file, struct page *page) return block_read_full_page(page,ufs_getfrag_block); } -int __ufs_write_begin(struct file *file, struct address_space *mapping, - loff_t pos, unsigned len, unsigned flags, - struct page **pagep, void **fsdata) +int ufs_prepare_chunk(struct page *page, loff_t pos, unsigned len) { - return block_write_begin(file, mapping, pos, len, flags, pagep, fsdata, - ufs_getfrag_block); + return block_write_begin_newtrunc(NULL, page->mapping, pos, len, 0, + &page, NULL, ufs_getfrag_block); } static int ufs_write_begin(struct file *file, struct address_space *mapping, @@ -571,7 +569,8 @@ static int ufs_write_begin(struct file *file, struct address_space *mapping, struct page **pagep, void **fsdata) { *pagep = NULL; - return __ufs_write_begin(file, mapping, pos, len, flags, pagep, fsdata); + return block_write_begin(file, mapping, pos, len, flags, pagep, fsdata, + ufs_getfrag_block); } static sector_t ufs_bmap(struct address_space *mapping, sector_t block) diff --git a/fs/ufs/util.h b/fs/ufs/util.h index 23ceed8c8fb9..0466036912f1 100644 --- a/fs/ufs/util.h +++ b/fs/ufs/util.h @@ -257,9 +257,7 @@ ufs_set_inode_gid(struct super_block *sb, struct ufs_inode *inode, u32 value) extern dev_t ufs_get_inode_dev(struct super_block *, struct ufs_inode_info *); extern void ufs_set_inode_dev(struct super_block *, struct ufs_inode_info *, dev_t); -extern int __ufs_write_begin(struct file *file, struct address_space *mapping, - loff_t pos, unsigned len, unsigned flags, - struct page **pagep, void **fsdata); +extern int ufs_prepare_chunk(struct page *page, loff_t pos, unsigned len); /* * These functions manipulate ufs buffers -- cgit v1.2.3 From 6e1db88d536adcbbfe562b2d4b7d6425784fff12 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 4 Jun 2010 11:29:57 +0200 Subject: introduce __block_write_begin Split up the block_write_begin implementation - __block_write_begin is a new trivial wrapper for block_prepare_write that always takes an already allocated page and can be either called from block_write_begin or filesystem code that already has a page allocated. Remove the handling of already allocated pages from block_write_begin after switching all callers that do it to __block_write_begin. Signed-off-by: Christoph Hellwig Signed-off-by: Al Viro --- fs/buffer.c | 69 +++++++++++++++++---------------------------- fs/ext2/dir.c | 3 +- fs/ext3/inode.c | 3 +- fs/ext4/inode.c | 11 +++----- fs/minix/inode.c | 3 +- fs/nilfs2/dir.c | 3 +- fs/reiserfs/inode.c | 3 +- fs/sysv/itree.c | 3 +- fs/ufs/inode.c | 3 +- include/linux/buffer_head.h | 2 ++ 10 files changed, 39 insertions(+), 64 deletions(-) (limited to 'fs') diff --git a/fs/buffer.c b/fs/buffer.c index 14529ec759b9..c319c49da511 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -1833,9 +1833,10 @@ void page_zero_new_buffers(struct page *page, unsigned from, unsigned to) } EXPORT_SYMBOL(page_zero_new_buffers); -static int __block_prepare_write(struct inode *inode, struct page *page, - unsigned from, unsigned to, get_block_t *get_block) +int block_prepare_write(struct page *page, unsigned from, unsigned to, + get_block_t *get_block) { + struct inode *inode = page->mapping->host; unsigned block_start, block_end; sector_t block; int err = 0; @@ -1908,10 +1909,13 @@ static int __block_prepare_write(struct inode *inode, struct page *page, if (!buffer_uptodate(*wait_bh)) err = -EIO; } - if (unlikely(err)) + if (unlikely(err)) { page_zero_new_buffers(page, from, to); + ClearPageUptodate(page); + } return err; } +EXPORT_SYMBOL(block_prepare_write); static int __block_commit_write(struct inode *inode, struct page *page, unsigned from, unsigned to) @@ -1948,6 +1952,15 @@ static int __block_commit_write(struct inode *inode, struct page *page, return 0; } +int __block_write_begin(struct page *page, loff_t pos, unsigned len, + get_block_t *get_block) +{ + unsigned start = pos & (PAGE_CACHE_SIZE - 1); + + return block_prepare_write(page, start, start + len, get_block); +} +EXPORT_SYMBOL(__block_write_begin); + /* * Filesystems implementing the new truncate sequence should use the * _newtrunc postfix variant which won't incorrectly call vmtruncate. @@ -1958,41 +1971,22 @@ int block_write_begin_newtrunc(struct file *file, struct address_space *mapping, struct page **pagep, void **fsdata, get_block_t *get_block) { - struct inode *inode = mapping->host; - int status = 0; + pgoff_t index = pos >> PAGE_CACHE_SHIFT; struct page *page; - pgoff_t index; - unsigned start, end; - int ownpage = 0; + int status; - index = pos >> PAGE_CACHE_SHIFT; - start = pos & (PAGE_CACHE_SIZE - 1); - end = start + len; - - page = *pagep; - if (page == NULL) { - ownpage = 1; - page = grab_cache_page_write_begin(mapping, index, flags); - if (!page) { - status = -ENOMEM; - goto out; - } - *pagep = page; - } else - BUG_ON(!PageLocked(page)); + page = grab_cache_page_write_begin(mapping, index, flags); + if (!page) + return -ENOMEM; - status = __block_prepare_write(inode, page, start, end, get_block); + status = __block_write_begin(page, pos, len, get_block); if (unlikely(status)) { - ClearPageUptodate(page); - - if (ownpage) { - unlock_page(page); - page_cache_release(page); - *pagep = NULL; - } + unlock_page(page); + page_cache_release(page); + page = NULL; } -out: + *pagep = page; return status; } EXPORT_SYMBOL(block_write_begin_newtrunc); @@ -2379,17 +2373,6 @@ out: } EXPORT_SYMBOL(cont_write_begin); -int block_prepare_write(struct page *page, unsigned from, unsigned to, - get_block_t *get_block) -{ - struct inode *inode = page->mapping->host; - int err = __block_prepare_write(inode, page, from, to, get_block); - if (err) - ClearPageUptodate(page); - return err; -} -EXPORT_SYMBOL(block_prepare_write); - int block_commit_write(struct page *page, unsigned from, unsigned to) { struct inode *inode = page->mapping->host; diff --git a/fs/ext2/dir.c b/fs/ext2/dir.c index 6b946bae11cf..764109886ec0 100644 --- a/fs/ext2/dir.c +++ b/fs/ext2/dir.c @@ -450,8 +450,7 @@ ino_t ext2_inode_by_name(struct inode *dir, struct qstr *child) static int ext2_prepare_chunk(struct page *page, loff_t pos, unsigned len) { - return block_write_begin_newtrunc(NULL, page->mapping, pos, len, 0, - &page, NULL, ext2_get_block); + return __block_write_begin(page, pos, len, ext2_get_block); } /* Releases the page */ diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c index a66f3fe33672..5c6f07eefa4a 100644 --- a/fs/ext3/inode.c +++ b/fs/ext3/inode.c @@ -1196,8 +1196,7 @@ retry: ret = PTR_ERR(handle); goto out; } - ret = block_write_begin(file, mapping, pos, len, flags, pagep, fsdata, - ext3_get_block); + ret = __block_write_begin(page, pos, len, ext3_get_block); if (ret) goto write_begin_failed; diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index d6a7701018a6..3da3c9646e5e 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -1578,11 +1578,9 @@ retry: *pagep = page; if (ext4_should_dioread_nolock(inode)) - ret = block_write_begin(file, mapping, pos, len, flags, pagep, - fsdata, ext4_get_block_write); + ret = __block_write_begin(page, pos, len, ext4_get_block_write); else - ret = block_write_begin(file, mapping, pos, len, flags, pagep, - fsdata, ext4_get_block); + ret = __block_write_begin(page, pos, len, ext4_get_block); if (!ret && ext4_should_journal_data(inode)) { ret = walk_page_buffers(handle, page_buffers(page), @@ -1593,7 +1591,7 @@ retry: unlock_page(page); page_cache_release(page); /* - * block_write_begin may have instantiated a few blocks + * __block_write_begin may have instantiated a few blocks * outside i_size. Trim these off again. Don't need * i_size_read because we hold i_mutex. * @@ -3185,8 +3183,7 @@ retry: } *pagep = page; - ret = block_write_begin(file, mapping, pos, len, flags, pagep, fsdata, - ext4_da_get_block_prep); + ret = __block_write_begin(page, pos, len, ext4_da_get_block_prep); if (ret < 0) { unlock_page(page); ext4_journal_stop(handle); diff --git a/fs/minix/inode.c b/fs/minix/inode.c index f4abe45229bb..6b29e73f0ca6 100644 --- a/fs/minix/inode.c +++ b/fs/minix/inode.c @@ -359,8 +359,7 @@ static int minix_readpage(struct file *file, struct page *page) int minix_prepare_chunk(struct page *page, loff_t pos, unsigned len) { - return block_write_begin_newtrunc(NULL, page->mapping, pos, len, 0, - &page, NULL, minix_get_block); + return __block_write_begin(page, pos, len, minix_get_block); } static int minix_write_begin(struct file *file, struct address_space *mapping, diff --git a/fs/nilfs2/dir.c b/fs/nilfs2/dir.c index fc2bcfa599a3..d14e3b94d81f 100644 --- a/fs/nilfs2/dir.c +++ b/fs/nilfs2/dir.c @@ -83,8 +83,7 @@ static unsigned nilfs_last_byte(struct inode *inode, unsigned long page_nr) static int nilfs_prepare_chunk(struct page *page, unsigned from, unsigned to) { loff_t pos = page_offset(page) + from; - return block_write_begin_newtrunc(NULL, page->mapping, pos, to - from, - 0, &page, NULL, nilfs_get_block); + return __block_write_begin(page, pos, to - from, nilfs_get_block); } static void nilfs_commit_chunk(struct page *page, diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c index 4c1fb548ab64..045729f5674a 100644 --- a/fs/reiserfs/inode.c +++ b/fs/reiserfs/inode.c @@ -2585,8 +2585,7 @@ static int reiserfs_write_begin(struct file *file, old_ref = th->t_refcount; th->t_refcount++; } - ret = block_write_begin(file, mapping, pos, len, flags, pagep, fsdata, - reiserfs_get_block); + ret = __block_write_begin(page, pos, len, reiserfs_get_block); if (ret && reiserfs_transaction_running(inode->i_sb)) { struct reiserfs_transaction_handle *th = current->journal_info; /* this gets a little ugly. If reiserfs_get_block returned an diff --git a/fs/sysv/itree.c b/fs/sysv/itree.c index 4068f485cfd6..82a005c3d7eb 100644 --- a/fs/sysv/itree.c +++ b/fs/sysv/itree.c @@ -461,8 +461,7 @@ static int sysv_readpage(struct file *file, struct page *page) int sysv_prepare_chunk(struct page *page, loff_t pos, unsigned len) { - return block_write_begin_newtrunc(NULL, page->mapping, pos, len, 0, - &page, NULL, get_block); + return __block_write_begin(page, pos, len, get_block); } static int sysv_write_begin(struct file *file, struct address_space *mapping, diff --git a/fs/ufs/inode.c b/fs/ufs/inode.c index a9555b1ffd28..45ce32391f8f 100644 --- a/fs/ufs/inode.c +++ b/fs/ufs/inode.c @@ -560,8 +560,7 @@ static int ufs_readpage(struct file *file, struct page *page) int ufs_prepare_chunk(struct page *page, loff_t pos, unsigned len) { - return block_write_begin_newtrunc(NULL, page->mapping, pos, len, 0, - &page, NULL, ufs_getfrag_block); + return __block_write_begin(page, pos, len, ufs_getfrag_block); } static int ufs_write_begin(struct file *file, struct address_space *mapping, diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h index 7638647f0424..accc9f81bb63 100644 --- a/include/linux/buffer_head.h +++ b/include/linux/buffer_head.h @@ -209,6 +209,8 @@ int block_write_begin_newtrunc(struct file *, struct address_space *, int block_write_begin(struct file *, struct address_space *, loff_t, unsigned, unsigned, struct page **, void **, get_block_t*); +int __block_write_begin(struct page *page, loff_t pos, unsigned len, + get_block_t *get_block); int block_write_end(struct file *, struct address_space *, loff_t, unsigned, unsigned, struct page *, void *); -- cgit v1.2.3 From 155130a4f7848b1aac439cab6bda1a175507c71c Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 4 Jun 2010 11:29:58 +0200 Subject: get rid of block_write_begin_newtrunc Move the call to vmtruncate to get rid of accessive blocks to the callers in preparation of the new truncate sequence and rename the non-truncating version to block_write_begin. While we're at it also remove several unused arguments to block_write_begin. Signed-off-by: Christoph Hellwig Signed-off-by: Al Viro --- fs/bfs/file.c | 14 ++++++++--- fs/block_dev.c | 5 ++-- fs/buffer.c | 61 +++++++-------------------------------------- fs/ext2/inode.c | 5 ++-- fs/minix/inode.c | 12 +++++++-- fs/nilfs2/inode.c | 12 ++++++--- fs/nilfs2/recovery.c | 11 +++++--- fs/omfs/file.c | 14 ++++++++--- fs/sysv/itree.c | 13 +++++++--- fs/udf/inode.c | 13 +++++++--- fs/ufs/inode.c | 12 +++++++-- fs/xfs/linux-2.6/xfs_aops.c | 14 ++++++++--- include/linux/buffer_head.h | 8 ++---- 13 files changed, 103 insertions(+), 91 deletions(-) (limited to 'fs') diff --git a/fs/bfs/file.c b/fs/bfs/file.c index 88b9a3ff44e4..8fc2e9c9739d 100644 --- a/fs/bfs/file.c +++ b/fs/bfs/file.c @@ -168,9 +168,17 @@ static int bfs_write_begin(struct file *file, struct address_space *mapping, loff_t pos, unsigned len, unsigned flags, struct page **pagep, void **fsdata) { - *pagep = NULL; - return block_write_begin(file, mapping, pos, len, flags, - pagep, fsdata, bfs_get_block); + int ret; + + ret = block_write_begin(mapping, pos, len, flags, pagep, + bfs_get_block); + if (unlikely(ret)) { + loff_t isize = mapping->host->i_size; + if (pos + len > isize) + vmtruncate(mapping->host, isize); + } + + return ret; } static sector_t bfs_bmap(struct address_space *mapping, sector_t block) diff --git a/fs/block_dev.c b/fs/block_dev.c index 65a0c26508e5..63c9d6076205 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -308,9 +308,8 @@ static int blkdev_write_begin(struct file *file, struct address_space *mapping, loff_t pos, unsigned len, unsigned flags, struct page **pagep, void **fsdata) { - *pagep = NULL; - return block_write_begin_newtrunc(file, mapping, pos, len, flags, - pagep, fsdata, blkdev_get_block); + return block_write_begin(mapping, pos, len, flags, pagep, + blkdev_get_block); } static int blkdev_write_end(struct file *file, struct address_space *mapping, diff --git a/fs/buffer.c b/fs/buffer.c index c319c49da511..50efa339e051 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -1962,14 +1962,13 @@ int __block_write_begin(struct page *page, loff_t pos, unsigned len, EXPORT_SYMBOL(__block_write_begin); /* - * Filesystems implementing the new truncate sequence should use the - * _newtrunc postfix variant which won't incorrectly call vmtruncate. + * block_write_begin takes care of the basic task of block allocation and + * bringing partial write blocks uptodate first. + * * The filesystem needs to handle block truncation upon failure. */ -int block_write_begin_newtrunc(struct file *file, struct address_space *mapping, - loff_t pos, unsigned len, unsigned flags, - struct page **pagep, void **fsdata, - get_block_t *get_block) +int block_write_begin(struct address_space *mapping, loff_t pos, unsigned len, + unsigned flags, struct page **pagep, get_block_t *get_block) { pgoff_t index = pos >> PAGE_CACHE_SHIFT; struct page *page; @@ -1989,44 +1988,6 @@ int block_write_begin_newtrunc(struct file *file, struct address_space *mapping, *pagep = page; return status; } -EXPORT_SYMBOL(block_write_begin_newtrunc); - -/* - * block_write_begin takes care of the basic task of block allocation and - * bringing partial write blocks uptodate first. - * - * If *pagep is not NULL, then block_write_begin uses the locked page - * at *pagep rather than allocating its own. In this case, the page will - * not be unlocked or deallocated on failure. - */ -int block_write_begin(struct file *file, struct address_space *mapping, - loff_t pos, unsigned len, unsigned flags, - struct page **pagep, void **fsdata, - get_block_t *get_block) -{ - int ret; - - ret = block_write_begin_newtrunc(file, mapping, pos, len, flags, - pagep, fsdata, get_block); - - /* - * prepare_write() may have instantiated a few blocks - * outside i_size. Trim these off again. Don't need - * i_size_read because we hold i_mutex. - * - * Filesystems which pass down their own page also cannot - * call into vmtruncate here because it would lead to lock - * inversion problems (*pagep is locked). This is a further - * example of where the old truncate sequence is inadequate. - */ - if (unlikely(ret) && *pagep == NULL) { - loff_t isize = mapping->host->i_size; - if (pos + len > isize) - vmtruncate(mapping->host, isize); - } - - return ret; -} EXPORT_SYMBOL(block_write_begin); int block_write_end(struct file *file, struct address_space *mapping, @@ -2357,7 +2318,7 @@ int cont_write_begin(struct file *file, struct address_space *mapping, err = cont_expand_zero(file, mapping, pos, bytes); if (err) - goto out; + return err; zerofrom = *bytes & ~PAGE_CACHE_MASK; if (pos+len > *bytes && zerofrom & (blocksize-1)) { @@ -2365,11 +2326,7 @@ int cont_write_begin(struct file *file, struct address_space *mapping, (*bytes)++; } - *pagep = NULL; - err = block_write_begin_newtrunc(file, mapping, pos, len, - flags, pagep, fsdata, get_block); -out: - return err; + return block_write_begin(mapping, pos, len, flags, pagep, get_block); } EXPORT_SYMBOL(cont_write_begin); @@ -2511,8 +2468,8 @@ int nobh_write_begin(struct address_space *mapping, unlock_page(page); page_cache_release(page); *pagep = NULL; - return block_write_begin_newtrunc(NULL, mapping, pos, len, - flags, pagep, fsdata, get_block); + return block_write_begin(mapping, pos, len, flags, pagep, + get_block); } if (PageMappedToDisk(page)) diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index 2f4dfbcd7696..74dfe5f73330 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -772,9 +772,8 @@ ext2_write_begin(struct file *file, struct address_space *mapping, { int ret; - *pagep = NULL; - ret = block_write_begin_newtrunc(file, mapping, pos, len, flags, - pagep, fsdata, ext2_get_block); + ret = block_write_begin(mapping, pos, len, flags, pagep, + ext2_get_block); if (ret < 0) ext2_write_failed(mapping, pos + len); return ret; diff --git a/fs/minix/inode.c b/fs/minix/inode.c index 6b29e73f0ca6..125062f55ef2 100644 --- a/fs/minix/inode.c +++ b/fs/minix/inode.c @@ -366,9 +366,17 @@ static int minix_write_begin(struct file *file, struct address_space *mapping, loff_t pos, unsigned len, unsigned flags, struct page **pagep, void **fsdata) { - *pagep = NULL; - return block_write_begin(file, mapping, pos, len, flags, pagep, fsdata, + int ret; + + ret = block_write_begin(mapping, pos, len, flags, pagep, minix_get_block); + if (unlikely(ret)) { + loff_t isize = mapping->host->i_size; + if (pos + len > isize) + vmtruncate(mapping->host, isize); + } + + return ret; } static sector_t minix_bmap(struct address_space *mapping, sector_t block) diff --git a/fs/nilfs2/inode.c b/fs/nilfs2/inode.c index 1dd9e6a7d787..5c694ece172e 100644 --- a/fs/nilfs2/inode.c +++ b/fs/nilfs2/inode.c @@ -197,11 +197,15 @@ static int nilfs_write_begin(struct file *file, struct address_space *mapping, if (unlikely(err)) return err; - *pagep = NULL; - err = block_write_begin(file, mapping, pos, len, flags, pagep, - fsdata, nilfs_get_block); - if (unlikely(err)) + err = block_write_begin(mapping, pos, len, flags, pagep, + nilfs_get_block); + if (unlikely(err)) { + loff_t isize = mapping->host->i_size; + if (pos + len > isize) + vmtruncate(mapping->host, isize); + nilfs_transaction_abort(inode->i_sb); + } return err; } diff --git a/fs/nilfs2/recovery.c b/fs/nilfs2/recovery.c index bae2a516b4ee..2f11f0868d87 100644 --- a/fs/nilfs2/recovery.c +++ b/fs/nilfs2/recovery.c @@ -505,11 +505,14 @@ static int recover_dsync_blocks(struct nilfs_sb_info *sbi, } pos = rb->blkoff << inode->i_blkbits; - page = NULL; - err = block_write_begin(NULL, inode->i_mapping, pos, blocksize, - 0, &page, NULL, nilfs_get_block); - if (unlikely(err)) + err = block_write_begin(inode->i_mapping, pos, blocksize, + 0, &page, nilfs_get_block); + if (unlikely(err)) { + loff_t isize = inode->i_size; + if (pos + blocksize > isize) + vmtruncate(inode, isize); goto failed_inode; + } err = nilfs_recovery_copy_block(sbi, rb, page); if (unlikely(err)) diff --git a/fs/omfs/file.c b/fs/omfs/file.c index 6e7a3291bbe8..810cff346468 100644 --- a/fs/omfs/file.c +++ b/fs/omfs/file.c @@ -312,9 +312,17 @@ static int omfs_write_begin(struct file *file, struct address_space *mapping, loff_t pos, unsigned len, unsigned flags, struct page **pagep, void **fsdata) { - *pagep = NULL; - return block_write_begin(file, mapping, pos, len, flags, - pagep, fsdata, omfs_get_block); + int ret; + + ret = block_write_begin(mapping, pos, len, flags, pagep, + omfs_get_block); + if (unlikely(ret)) { + loff_t isize = mapping->host->i_size; + if (pos + len > isize) + vmtruncate(mapping->host, isize); + } + + return ret; } static sector_t omfs_bmap(struct address_space *mapping, sector_t block) diff --git a/fs/sysv/itree.c b/fs/sysv/itree.c index 82a005c3d7eb..9ca66276315e 100644 --- a/fs/sysv/itree.c +++ b/fs/sysv/itree.c @@ -468,9 +468,16 @@ static int sysv_write_begin(struct file *file, struct address_space *mapping, loff_t pos, unsigned len, unsigned flags, struct page **pagep, void **fsdata) { - *pagep = NULL; - return block_write_begin(file, mapping, pos, len, flags, pagep, fsdata, - get_block); + int ret; + + ret = block_write_begin(mapping, pos, len, flags, pagep, get_block); + if (unlikely(ret)) { + loff_t isize = mapping->host->i_size; + if (pos + len > isize) + vmtruncate(mapping->host, isize); + } + + return ret; } static sector_t sysv_bmap(struct address_space *mapping, sector_t block) diff --git a/fs/udf/inode.c b/fs/udf/inode.c index 124852bcf6fe..ecddcc2ed746 100644 --- a/fs/udf/inode.c +++ b/fs/udf/inode.c @@ -127,9 +127,16 @@ static int udf_write_begin(struct file *file, struct address_space *mapping, loff_t pos, unsigned len, unsigned flags, struct page **pagep, void **fsdata) { - *pagep = NULL; - return block_write_begin(file, mapping, pos, len, flags, pagep, fsdata, - udf_get_block); + int ret; + + ret = block_write_begin(mapping, pos, len, flags, pagep, udf_get_block); + if (unlikely(ret)) { + loff_t isize = mapping->host->i_size; + if (pos + len > isize) + vmtruncate(mapping->host, isize); + } + + return ret; } static sector_t udf_bmap(struct address_space *mapping, sector_t block) diff --git a/fs/ufs/inode.c b/fs/ufs/inode.c index 45ce32391f8f..45cafa937a4b 100644 --- a/fs/ufs/inode.c +++ b/fs/ufs/inode.c @@ -567,9 +567,17 @@ static int ufs_write_begin(struct file *file, struct address_space *mapping, loff_t pos, unsigned len, unsigned flags, struct page **pagep, void **fsdata) { - *pagep = NULL; - return block_write_begin(file, mapping, pos, len, flags, pagep, fsdata, + int ret; + + ret = block_write_begin(mapping, pos, len, flags, pagep, ufs_getfrag_block); + if (unlikely(ret)) { + loff_t isize = mapping->host->i_size; + if (pos + len > isize) + vmtruncate(mapping->host, isize); + } + + return ret; } static sector_t ufs_bmap(struct address_space *mapping, sector_t block) diff --git a/fs/xfs/linux-2.6/xfs_aops.c b/fs/xfs/linux-2.6/xfs_aops.c index 7968d41e27ad..bf7aad0d78b8 100644 --- a/fs/xfs/linux-2.6/xfs_aops.c +++ b/fs/xfs/linux-2.6/xfs_aops.c @@ -1504,9 +1504,17 @@ xfs_vm_write_begin( struct page **pagep, void **fsdata) { - *pagep = NULL; - return block_write_begin(file, mapping, pos, len, flags | AOP_FLAG_NOFS, - pagep, fsdata, xfs_get_blocks); + int ret; + + ret = block_write_begin(mapping, pos, len, flags | AOP_FLAG_NOFS, + pagep, xfs_get_blocks); + if (unlikely(ret)) { + loff_t isize = mapping->host->i_size; + if (pos + len > isize) + vmtruncate(mapping->host, isize); + } + + return ret; } STATIC sector_t diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h index accc9f81bb63..3f69054f86d9 100644 --- a/include/linux/buffer_head.h +++ b/include/linux/buffer_head.h @@ -203,12 +203,8 @@ int block_write_full_page_endio(struct page *page, get_block_t *get_block, int block_read_full_page(struct page*, get_block_t*); int block_is_partially_uptodate(struct page *page, read_descriptor_t *desc, unsigned long from); -int block_write_begin_newtrunc(struct file *, struct address_space *, - loff_t, unsigned, unsigned, - struct page **, void **, get_block_t*); -int block_write_begin(struct file *, struct address_space *, - loff_t, unsigned, unsigned, - struct page **, void **, get_block_t*); +int block_write_begin(struct address_space *mapping, loff_t pos, unsigned len, + unsigned flags, struct page **pagep, get_block_t *get_block); int __block_write_begin(struct page *page, loff_t pos, unsigned len, get_block_t *get_block); int block_write_end(struct file *, struct address_space *, -- cgit v1.2.3 From d39aae9ec447dda84d9a2850743a78a535a71c90 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 4 Jun 2010 11:29:59 +0200 Subject: add missing setattr methods For the new truncate sequence every filesystem that wants to truncate on-disk state needs a seattr method. Convert the remaining filesystems that implement the truncate inode operation to have its own setattr method. Signed-off-by: Christoph Hellwig Signed-off-by: Al Viro --- fs/hfsplus/inode.c | 12 ++++++++++++ fs/minix/file.c | 12 ++++++++++++ fs/omfs/file.c | 12 ++++++++++++ fs/sysv/file.c | 12 ++++++++++++ fs/udf/file.c | 12 ++++++++++++ 5 files changed, 60 insertions(+) (limited to 'fs') diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c index 88bf1b562641..d6ebe53fbdbf 100644 --- a/fs/hfsplus/inode.c +++ b/fs/hfsplus/inode.c @@ -290,9 +290,21 @@ static int hfsplus_file_release(struct inode *inode, struct file *file) return 0; } +static int hfsplus_setattr(struct dentry *dentry, struct iattr *attr) +{ + struct inode *inode = dentry->d_inode; + int error; + + error = inode_change_ok(inode, attr); + if (error) + return error; + return inode_setattr(inode, attr); +} + static const struct inode_operations hfsplus_file_inode_operations = { .lookup = hfsplus_file_lookup, .truncate = hfsplus_file_truncate, + .setattr = hfsplus_setattr, .setxattr = hfsplus_setxattr, .getxattr = hfsplus_getxattr, .listxattr = hfsplus_listxattr, diff --git a/fs/minix/file.c b/fs/minix/file.c index d5320ff23faf..7a45dd1fe2e5 100644 --- a/fs/minix/file.c +++ b/fs/minix/file.c @@ -23,7 +23,19 @@ const struct file_operations minix_file_operations = { .splice_read = generic_file_splice_read, }; +static int minix_setattr(struct dentry *dentry, struct iattr *attr) +{ + struct inode *inode = dentry->d_inode; + int error; + + error = inode_change_ok(inode, attr); + if (error) + return error; + return inode_setattr(inode, attr); +} + const struct inode_operations minix_file_inode_operations = { .truncate = minix_truncate, + .setattr = minix_setattr, .getattr = minix_getattr, }; diff --git a/fs/omfs/file.c b/fs/omfs/file.c index 810cff346468..78c9f0c1a2f3 100644 --- a/fs/omfs/file.c +++ b/fs/omfs/file.c @@ -341,7 +341,19 @@ const struct file_operations omfs_file_operations = { .splice_read = generic_file_splice_read, }; +static int omfs_setattr(struct dentry *dentry, struct iattr *attr) +{ + struct inode *inode = dentry->d_inode; + int error; + + error = inode_change_ok(inode, attr); + if (error) + return error; + return inode_setattr(inode, attr); +} + const struct inode_operations omfs_file_inops = { + .setattr = omfs_setattr, .truncate = omfs_truncate }; diff --git a/fs/sysv/file.c b/fs/sysv/file.c index 750cc22349bd..94f6319292a1 100644 --- a/fs/sysv/file.c +++ b/fs/sysv/file.c @@ -30,7 +30,19 @@ const struct file_operations sysv_file_operations = { .splice_read = generic_file_splice_read, }; +static int sysv_setattr(struct dentry *dentry, struct iattr *attr) +{ + struct inode *inode = dentry->d_inode; + int error; + + error = inode_change_ok(inode, attr); + if (error) + return error; + return inode_setattr(inode, attr); +} + const struct inode_operations sysv_file_inode_operations = { .truncate = sysv_truncate, + .setattr = sysv_setattr, .getattr = sysv_getattr, }; diff --git a/fs/udf/file.c b/fs/udf/file.c index 94e06d6bddbd..7376032c89ce 100644 --- a/fs/udf/file.c +++ b/fs/udf/file.c @@ -228,6 +228,18 @@ const struct file_operations udf_file_operations = { .llseek = generic_file_llseek, }; +static int udf_setattr(struct dentry *dentry, struct iattr *attr) +{ + struct inode *inode = dentry->d_inode; + int error; + + error = inode_change_ok(inode, attr); + if (error) + return error; + return inode_setattr(inode, attr); +} + const struct inode_operations udf_file_inode_operations = { + .setattr = udf_setattr, .truncate = udf_truncate, }; -- cgit v1.2.3 From 6a1a90ad1b0edb556a7550a6ef8a8756f0304dd5 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 4 Jun 2010 11:30:00 +0200 Subject: rename generic_setattr Despite its name it's now a generic implementation of ->setattr, but rather a helper to copy attributes from a struct iattr to the inode. Rename it to setattr_copy to reflect this fact. Signed-off-by: Christoph Hellwig Signed-off-by: Al Viro --- fs/attr.c | 14 +++++++------- fs/ext2/inode.c | 2 +- fs/fat/file.c | 2 +- fs/libfs.c | 3 +-- fs/ramfs/file-nommu.c | 2 +- fs/sysfs/inode.c | 2 +- include/linux/fs.h | 2 +- mm/shmem.c | 2 +- 8 files changed, 14 insertions(+), 15 deletions(-) (limited to 'fs') diff --git a/fs/attr.c b/fs/attr.c index b4fa3b0aa596..1f6a895e24e9 100644 --- a/fs/attr.c +++ b/fs/attr.c @@ -105,13 +105,13 @@ out_big: EXPORT_SYMBOL(inode_newsize_ok); /** - * generic_setattr - copy simple metadata updates into the generic inode + * setattr_copy - copy simple metadata updates into the generic inode * @inode: the inode to be updated * @attr: the new attributes * - * generic_setattr must be called with i_mutex held. + * setattr_copy must be called with i_mutex held. * - * generic_setattr updates the inode's metadata with that specified + * setattr_copy updates the inode's metadata with that specified * in attr. Noticably missing is inode size update, which is more complex * as it requires pagecache updates. See simple_setsize. * @@ -119,7 +119,7 @@ EXPORT_SYMBOL(inode_newsize_ok); * that for "simple" filesystems, the struct inode is the inode storage. * The caller is free to mark the inode dirty afterwards if needed. */ -void generic_setattr(struct inode *inode, const struct iattr *attr) +void setattr_copy(struct inode *inode, const struct iattr *attr) { unsigned int ia_valid = attr->ia_valid; @@ -144,11 +144,11 @@ void generic_setattr(struct inode *inode, const struct iattr *attr) inode->i_mode = mode; } } -EXPORT_SYMBOL(generic_setattr); +EXPORT_SYMBOL(setattr_copy); /* * note this function is deprecated, the new truncate sequence should be - * used instead -- see eg. simple_setsize, generic_setattr. + * used instead -- see eg. simple_setsize, setattr_copy. */ int inode_setattr(struct inode *inode, const struct iattr *attr) { @@ -163,7 +163,7 @@ int inode_setattr(struct inode *inode, const struct iattr *attr) return error; } - generic_setattr(inode, attr); + setattr_copy(inode, attr); mark_inode_dirty(inode); diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index 74dfe5f73330..7dee7b3f3688 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -1544,7 +1544,7 @@ int ext2_setattr(struct dentry *dentry, struct iattr *iattr) if (error) return error; } - generic_setattr(inode, iattr); + setattr_copy(inode, iattr); if (iattr->ia_valid & ATTR_MODE) error = ext2_acl_chmod(inode); mark_inode_dirty(inode); diff --git a/fs/fat/file.c b/fs/fat/file.c index 990dfae022e5..20813d2c7d61 100644 --- a/fs/fat/file.c +++ b/fs/fat/file.c @@ -446,7 +446,7 @@ int fat_setattr(struct dentry *dentry, struct iattr *attr) goto out; } - generic_setattr(inode, attr); + setattr_copy(inode, attr); mark_inode_dirty(inode); out: return error; diff --git a/fs/libfs.c b/fs/libfs.c index dcaf972cbf1b..861a88797716 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -395,8 +395,7 @@ int simple_setattr(struct dentry *dentry, struct iattr *iattr) return error; } - generic_setattr(inode, iattr); - + setattr_copy(inode, iattr); return error; } EXPORT_SYMBOL(simple_setattr); diff --git a/fs/ramfs/file-nommu.c b/fs/ramfs/file-nommu.c index d532c20fc179..8d44f0347b27 100644 --- a/fs/ramfs/file-nommu.c +++ b/fs/ramfs/file-nommu.c @@ -183,7 +183,7 @@ static int ramfs_nommu_setattr(struct dentry *dentry, struct iattr *ia) } } - generic_setattr(inode, ia); + setattr_copy(inode, ia); out: ia->ia_valid = old_ia_valid; return ret; diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index 0835a3b70e03..7e187fbd3d47 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -122,7 +122,7 @@ int sysfs_setattr(struct dentry *dentry, struct iattr *iattr) goto out; /* this ignores size changes */ - generic_setattr(inode, iattr); + setattr_copy(inode, iattr); out: mutex_unlock(&sysfs_mutex); diff --git a/include/linux/fs.h b/include/linux/fs.h index b347b2d5666f..8ebb5f01a418 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2393,7 +2393,7 @@ extern int buffer_migrate_page(struct address_space *, extern int inode_change_ok(const struct inode *, struct iattr *); extern int inode_newsize_ok(const struct inode *, loff_t offset); extern int __must_check inode_setattr(struct inode *, const struct iattr *); -extern void generic_setattr(struct inode *inode, const struct iattr *attr); +extern void setattr_copy(struct inode *inode, const struct iattr *attr); extern void file_update_time(struct file *file); diff --git a/mm/shmem.c b/mm/shmem.c index f65f84062db5..3b58ad65d26c 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -811,7 +811,7 @@ static int shmem_notify_change(struct dentry *dentry, struct iattr *attr) error = inode_change_ok(inode, attr); if (!error) - generic_setattr(inode, attr); + setattr_copy(inode, attr); #ifdef CONFIG_TMPFS_POSIX_ACL if (!error && (attr->ia_valid & ATTR_MODE)) error = generic_acl_chmod(inode); -- cgit v1.2.3 From eef2380c187890816b73b1a4cb89a09203759469 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 4 Jun 2010 11:30:01 +0200 Subject: default to simple_setattr With the new truncate sequence every filesystem that wants to support file size changes on disk needs to implement its own ->setattr. So instead of calling inode_setattr which supports size changes call into a simple method that doesn't support this. simple_setattr is almost what we want except that it does not mark the inode dirty after changes. Given that marking the inode dirty is a no-op for the simple in-memory filesystems that use simple_setattr currently just add the mark_inode_dirty call. Also add a WARN_ON for the presence of a truncate method to simple_setattr to catch new instances of it during the transition period. Signed-off-by: Christoph Hellwig Signed-off-by: Al Viro --- fs/attr.c | 9 +++------ fs/libfs.c | 16 +++++++++++----- 2 files changed, 14 insertions(+), 11 deletions(-) (limited to 'fs') diff --git a/fs/attr.c b/fs/attr.c index 1f6a895e24e9..aeac826f4774 100644 --- a/fs/attr.c +++ b/fs/attr.c @@ -237,13 +237,10 @@ int notify_change(struct dentry * dentry, struct iattr * attr) if (ia_valid & ATTR_SIZE) down_write(&dentry->d_inode->i_alloc_sem); - if (inode->i_op && inode->i_op->setattr) { + if (inode->i_op->setattr) error = inode->i_op->setattr(dentry, attr); - } else { - error = inode_change_ok(inode, attr); - if (!error) - error = inode_setattr(inode, attr); - } + else + error = simple_setattr(dentry, attr); if (ia_valid & ATTR_SIZE) up_write(&dentry->d_inode->i_alloc_sem); diff --git a/fs/libfs.c b/fs/libfs.c index 861a88797716..40562224b718 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -370,21 +370,26 @@ int simple_setsize(struct inode *inode, loff_t newsize) EXPORT_SYMBOL(simple_setsize); /** - * simple_setattr - setattr for simple in-memory filesystem + * simple_setattr - setattr for simple filesystem * @dentry: dentry * @iattr: iattr structure * * Returns 0 on success, -error on failure. * - * simple_setattr implements setattr for an in-memory filesystem which - * does not store its own file data or metadata (eg. uses the page cache - * and inode cache as its data store). + * simple_setattr is a simple ->setattr implementation without a proper + * implementation of size changes. + * + * It can either be used for in-memory filesystems or special files + * on simple regular filesystems. Anything that needs to change on-disk + * or wire state on size changes needs its own setattr method. */ int simple_setattr(struct dentry *dentry, struct iattr *iattr) { struct inode *inode = dentry->d_inode; int error; + WARN_ON_ONCE(inode->i_op->truncate); + error = inode_change_ok(inode, iattr); if (error) return error; @@ -396,7 +401,8 @@ int simple_setattr(struct dentry *dentry, struct iattr *iattr) } setattr_copy(inode, iattr); - return error; + mark_inode_dirty(inode); + return 0; } EXPORT_SYMBOL(simple_setattr); -- cgit v1.2.3 From 1025774ce411f2bd4b059ad7b53f0003569b74fa Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 4 Jun 2010 11:30:02 +0200 Subject: remove inode_setattr Replace inode_setattr with opencoded variants of it in all callers. This moves the remaining call to vmtruncate into the filesystem methods where it can be replaced with the proper truncate sequence. In a few cases it was obvious that we would never end up calling vmtruncate so it was left out in the opencoded variant: spufs: explicitly checks for ATTR_SIZE earlier btrfs,hugetlbfs,logfs,dlmfs: explicitly clears ATTR_SIZE earlier ufs: contains an opencoded simple_seattr + truncate that sets the filesize just above In addition to that ncpfs called inode_setattr with handcrafted iattrs, which allowed to trim down the opencoded variant. Signed-off-by: Christoph Hellwig Signed-off-by: Al Viro --- arch/powerpc/platforms/cell/spufs/inode.c | 4 +- drivers/staging/pohmelfs/inode.c | 14 +++-- fs/9p/vfs_inode.c | 15 ++++- fs/affs/inode.c | 13 ++++- fs/attr.c | 25 -------- fs/btrfs/inode.c | 12 ++-- fs/cifs/inode.c | 45 ++++++++++---- fs/exofs/inode.c | 14 ++++- fs/ext3/inode.c | 12 +++- fs/ext4/inode.c | 16 +++-- fs/gfs2/inode.c | 25 +++++--- fs/gfs2/ops_inode.c | 12 +++- fs/gfs2/xattr.c | 24 ++++++-- fs/hfs/inode.c | 12 +++- fs/hfsplus/inode.c | 12 +++- fs/hostfs/hostfs_kern.c | 18 +++++- fs/hpfs/inode.c | 12 +++- fs/hugetlbfs/inode.c | 17 +++--- fs/jfs/file.c | 14 ++++- fs/logfs/file.c | 18 +++--- fs/minix/file.c | 12 +++- fs/ncpfs/inode.c | 24 ++++---- fs/nilfs2/inode.c | 25 ++++++-- fs/ntfs/inode.c | 3 - fs/ocfs2/dlmfs/dlmfs.c | 8 ++- fs/ocfs2/file.c | 16 +++-- fs/omfs/file.c | 12 +++- fs/proc/base.c | 16 ++++- fs/proc/generic.c | 18 ++++-- fs/proc/proc_sysctl.c | 15 ++++- fs/reiserfs/inode.c | 97 +++++++++++++++++-------------- fs/sysv/file.c | 12 +++- fs/udf/file.c | 12 +++- fs/ufs/truncate.c | 5 +- include/linux/fs.h | 1 - 35 files changed, 416 insertions(+), 194 deletions(-) (limited to 'fs') diff --git a/arch/powerpc/platforms/cell/spufs/inode.c b/arch/powerpc/platforms/cell/spufs/inode.c index e5e5f823d687..32625f366fb5 100644 --- a/arch/powerpc/platforms/cell/spufs/inode.c +++ b/arch/powerpc/platforms/cell/spufs/inode.c @@ -110,7 +110,9 @@ spufs_setattr(struct dentry *dentry, struct iattr *attr) if ((attr->ia_valid & ATTR_SIZE) && (attr->ia_size != inode->i_size)) return -EINVAL; - return inode_setattr(inode, attr); + setattr_copy(inode, attr); + mark_inode_dirty(inode); + return 0; } diff --git a/drivers/staging/pohmelfs/inode.c b/drivers/staging/pohmelfs/inode.c index 643b413d9f0f..e818f53ccfd7 100644 --- a/drivers/staging/pohmelfs/inode.c +++ b/drivers/staging/pohmelfs/inode.c @@ -968,12 +968,18 @@ int pohmelfs_setattr_raw(struct inode *inode, struct iattr *attr) goto err_out_exit; } - err = inode_setattr(inode, attr); - if (err) { - dprintk("%s: ino: %llu, failed to set the attributes.\n", __func__, POHMELFS_I(inode)->ino); - goto err_out_exit; + if ((attr->ia_valid & ATTR_SIZE) && + attr->ia_size != i_size_read(inode)) { + err = vmtruncate(inode, attr->ia_size); + if (err) { + dprintk("%s: ino: %llu, failed to set the attributes.\n", __func__, POHMELFS_I(inode)->ino); + goto err_out_exit; + } } + setattr_copy(inode, attr); + mark_inode_dirty(inode); + dprintk("%s: ino: %llu, mode: %o -> %o, uid: %u -> %u, gid: %u -> %u, size: %llu -> %llu.\n", __func__, POHMELFS_I(inode)->ino, inode->i_mode, attr->ia_mode, inode->i_uid, attr->ia_uid, inode->i_gid, attr->ia_gid, inode->i_size, attr->ia_size); diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index 4331b3b5ee1c..4b3ad6ac9a41 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -896,10 +896,19 @@ static int v9fs_vfs_setattr(struct dentry *dentry, struct iattr *iattr) } retval = p9_client_wstat(fid, &wstat); - if (retval >= 0) - retval = inode_setattr(dentry->d_inode, iattr); + if (retval < 0) + return retval; + + if ((iattr->ia_valid & ATTR_SIZE) && + iattr->ia_size != i_size_read(dentry->d_inode)) { + retval = vmtruncate(dentry->d_inode, iattr->ia_size); + if (retval) + return retval; + } - return retval; + setattr_copy(dentry->d_inode, iattr); + mark_inode_dirty(dentry->d_inode); + return 0; } /** diff --git a/fs/affs/inode.c b/fs/affs/inode.c index f4b2a4ee4f91..6883d5fb84cf 100644 --- a/fs/affs/inode.c +++ b/fs/affs/inode.c @@ -235,8 +235,17 @@ affs_notify_change(struct dentry *dentry, struct iattr *attr) goto out; } - error = inode_setattr(inode, attr); - if (!error && (attr->ia_valid & ATTR_MODE)) + if ((attr->ia_valid & ATTR_SIZE) && + attr->ia_size != i_size_read(inode)) { + error = vmtruncate(inode, attr->ia_size); + if (error) + return error; + } + + setattr_copy(inode, attr); + mark_inode_dirty(inode); + + if (attr->ia_valid & ATTR_MODE) mode_to_prot(inode); out: return error; diff --git a/fs/attr.c b/fs/attr.c index aeac826f4774..ed44d8ae8bf1 100644 --- a/fs/attr.c +++ b/fs/attr.c @@ -146,31 +146,6 @@ void setattr_copy(struct inode *inode, const struct iattr *attr) } EXPORT_SYMBOL(setattr_copy); -/* - * note this function is deprecated, the new truncate sequence should be - * used instead -- see eg. simple_setsize, setattr_copy. - */ -int inode_setattr(struct inode *inode, const struct iattr *attr) -{ - unsigned int ia_valid = attr->ia_valid; - - if (ia_valid & ATTR_SIZE && - attr->ia_size != i_size_read(inode)) { - int error; - - error = vmtruncate(inode, attr->ia_size); - if (error) - return error; - } - - setattr_copy(inode, attr); - - mark_inode_dirty(inode); - - return 0; -} -EXPORT_SYMBOL(inode_setattr); - int notify_change(struct dentry * dentry, struct iattr * attr) { struct inode *inode = dentry->d_inode; diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 1bff92ad4744..7f9e0536db1a 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -3656,13 +3656,15 @@ static int btrfs_setattr(struct dentry *dentry, struct iattr *attr) if (err) return err; } - attr->ia_valid &= ~ATTR_SIZE; - if (attr->ia_valid) - err = inode_setattr(inode, attr); + if (attr->ia_valid) { + setattr_copy(inode, attr); + mark_inode_dirty(inode); + + if (attr->ia_valid & ATTR_MODE) + err = btrfs_acl_chmod(inode); + } - if (!err && ((attr->ia_valid & ATTR_MODE))) - err = btrfs_acl_chmod(inode); return err; } diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index a15b3a9bbff4..9c6a40f5cc57 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -1889,18 +1889,27 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs) CIFS_MOUNT_MAP_SPECIAL_CHR); } - if (!rc) { - rc = inode_setattr(inode, attrs); + if (rc) + goto out; - /* force revalidate when any of these times are set since some - of the fs types (eg ext3, fat) do not have fine enough - time granularity to match protocol, and we do not have a - a way (yet) to query the server fs's time granularity (and - whether it rounds times down). - */ - if (!rc && (attrs->ia_valid & (ATTR_MTIME | ATTR_CTIME))) - cifsInode->time = 0; + if ((attrs->ia_valid & ATTR_SIZE) && + attrs->ia_size != i_size_read(inode)) { + rc = vmtruncate(inode, attrs->ia_size); + if (rc) + goto out; } + + setattr_copy(inode, attrs); + mark_inode_dirty(inode); + + /* force revalidate when any of these times are set since some + of the fs types (eg ext3, fat) do not have fine enough + time granularity to match protocol, and we do not have a + a way (yet) to query the server fs's time granularity (and + whether it rounds times down). + */ + if (attrs->ia_valid & (ATTR_MTIME | ATTR_CTIME)) + cifsInode->time = 0; out: kfree(args); kfree(full_path); @@ -2040,8 +2049,20 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs) /* do not need local check to inode_check_ok since the server does that */ - if (!rc) - rc = inode_setattr(inode, attrs); + if (rc) + goto cifs_setattr_exit; + + if ((attrs->ia_valid & ATTR_SIZE) && + attrs->ia_size != i_size_read(inode)) { + rc = vmtruncate(inode, attrs->ia_size); + if (rc) + goto cifs_setattr_exit; + } + + setattr_copy(inode, attrs); + mark_inode_dirty(inode); + return 0; + cifs_setattr_exit: kfree(full_path); FreeXid(xid); diff --git a/fs/exofs/inode.c b/fs/exofs/inode.c index 4bb6ef822e46..4bfc1f4fd013 100644 --- a/fs/exofs/inode.c +++ b/fs/exofs/inode.c @@ -887,8 +887,18 @@ int exofs_setattr(struct dentry *dentry, struct iattr *iattr) if (error) return error; - error = inode_setattr(inode, iattr); - return error; + if ((iattr->ia_valid & ATTR_SIZE) && + iattr->ia_size != i_size_read(inode)) { + int error; + + error = vmtruncate(inode, iattr->ia_size); + if (error) + return error; + } + + setattr_copy(inode, iattr); + mark_inode_dirty(inode); + return 0; } static const struct osd_attr g_attr_inode_file_layout = ATTR_DEF( diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c index 5c6f07eefa4a..b04d11936683 100644 --- a/fs/ext3/inode.c +++ b/fs/ext3/inode.c @@ -3208,9 +3208,17 @@ int ext3_setattr(struct dentry *dentry, struct iattr *attr) ext3_journal_stop(handle); } - rc = inode_setattr(inode, attr); + if ((attr->ia_valid & ATTR_SIZE) && + attr->ia_size != i_size_read(inode)) { + rc = vmtruncate(inode, attr->ia_size); + if (rc) + goto err_out; + } + + setattr_copy(inode, attr); + mark_inode_dirty(inode); - if (!rc && (ia_valid & ATTR_MODE)) + if (ia_valid & ATTR_MODE) rc = ext3_acl_chmod(inode); err_out: diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 3da3c9646e5e..1fb390359bc5 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -5539,11 +5539,19 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr) ext4_truncate(inode); } - rc = inode_setattr(inode, attr); + if ((attr->ia_valid & ATTR_SIZE) && + attr->ia_size != i_size_read(inode)) + rc = vmtruncate(inode, attr->ia_size); - /* If inode_setattr's call to ext4_truncate failed to get a - * transaction handle at all, we need to clean up the in-core - * orphan list manually. */ + if (!rc) { + setattr_copy(inode, attr); + mark_inode_dirty(inode); + } + + /* + * If the call to ext4_truncate failed to get a transaction handle at + * all, we need to clean up the in-core orphan list manually. + */ if (inode->i_nlink) ext4_orphan_del(NULL, inode); diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c index f03afd9c44bc..6c023a3b5d25 100644 --- a/fs/gfs2/inode.c +++ b/fs/gfs2/inode.c @@ -991,18 +991,29 @@ fail: static int __gfs2_setattr_simple(struct gfs2_inode *ip, struct iattr *attr) { + struct inode *inode = &ip->i_inode; struct buffer_head *dibh; int error; error = gfs2_meta_inode_buffer(ip, &dibh); - if (!error) { - error = inode_setattr(&ip->i_inode, attr); - gfs2_assert_warn(GFS2_SB(&ip->i_inode), !error); - gfs2_trans_add_bh(ip->i_gl, dibh, 1); - gfs2_dinode_out(ip, dibh->b_data); - brelse(dibh); + if (error) + return error; + + if ((attr->ia_valid & ATTR_SIZE) && + attr->ia_size != i_size_read(inode)) { + error = vmtruncate(inode, attr->ia_size); + if (error) + return error; } - return error; + + setattr_copy(inode, attr); + mark_inode_dirty(inode); + + gfs2_assert_warn(GFS2_SB(inode), !error); + gfs2_trans_add_bh(ip->i_gl, dibh, 1); + gfs2_dinode_out(ip, dibh->b_data); + brelse(dibh); + return 0; } /** diff --git a/fs/gfs2/ops_inode.c b/fs/gfs2/ops_inode.c index 98cdd05f3316..d7d410a4ca42 100644 --- a/fs/gfs2/ops_inode.c +++ b/fs/gfs2/ops_inode.c @@ -1136,8 +1136,16 @@ static int setattr_chown(struct inode *inode, struct iattr *attr) if (error) goto out_end_trans; - error = inode_setattr(inode, attr); - gfs2_assert_warn(sdp, !error); + if ((attr->ia_valid & ATTR_SIZE) && + attr->ia_size != i_size_read(inode)) { + int error; + + error = vmtruncate(inode, attr->ia_size); + gfs2_assert_warn(sdp, !error); + } + + setattr_copy(inode, attr); + mark_inode_dirty(inode); gfs2_trans_add_bh(ip->i_gl, dibh, 1); gfs2_dinode_out(ip, dibh->b_data); diff --git a/fs/gfs2/xattr.c b/fs/gfs2/xattr.c index 82f93da00d1b..776af6eb4bcb 100644 --- a/fs/gfs2/xattr.c +++ b/fs/gfs2/xattr.c @@ -1296,6 +1296,7 @@ fail: int gfs2_xattr_acl_chmod(struct gfs2_inode *ip, struct iattr *attr, char *data) { + struct inode *inode = &ip->i_inode; struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); struct gfs2_ea_location el; struct buffer_head *dibh; @@ -1321,14 +1322,25 @@ int gfs2_xattr_acl_chmod(struct gfs2_inode *ip, struct iattr *attr, char *data) return error; error = gfs2_meta_inode_buffer(ip, &dibh); - if (!error) { - error = inode_setattr(&ip->i_inode, attr); - gfs2_assert_warn(GFS2_SB(&ip->i_inode), !error); - gfs2_trans_add_bh(ip->i_gl, dibh, 1); - gfs2_dinode_out(ip, dibh->b_data); - brelse(dibh); + if (error) + goto out_trans_end; + + if ((attr->ia_valid & ATTR_SIZE) && + attr->ia_size != i_size_read(inode)) { + int error; + + error = vmtruncate(inode, attr->ia_size); + gfs2_assert_warn(GFS2_SB(inode), !error); } + setattr_copy(inode, attr); + mark_inode_dirty(inode); + + gfs2_trans_add_bh(ip->i_gl, dibh, 1); + gfs2_dinode_out(ip, dibh->b_data); + brelse(dibh); + +out_trans_end: gfs2_trans_end(sdp); return error; } diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c index 8df18e63eb6b..87de671baa83 100644 --- a/fs/hfs/inode.c +++ b/fs/hfs/inode.c @@ -612,10 +612,16 @@ int hfs_inode_setattr(struct dentry *dentry, struct iattr * attr) attr->ia_mode = inode->i_mode & ~S_IWUGO; attr->ia_mode &= S_ISDIR(inode->i_mode) ? ~hsb->s_dir_umask: ~hsb->s_file_umask; } - error = inode_setattr(inode, attr); - if (error) - return error; + if ((attr->ia_valid & ATTR_SIZE) && + attr->ia_size != i_size_read(inode)) { + error = vmtruncate(inode, attr->ia_size); + if (error) + return error; + } + + setattr_copy(inode, attr); + mark_inode_dirty(inode); return 0; } diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c index d6ebe53fbdbf..654c5a8ddf1c 100644 --- a/fs/hfsplus/inode.c +++ b/fs/hfsplus/inode.c @@ -298,7 +298,17 @@ static int hfsplus_setattr(struct dentry *dentry, struct iattr *attr) error = inode_change_ok(inode, attr); if (error) return error; - return inode_setattr(inode, attr); + + if ((attr->ia_valid & ATTR_SIZE) && + attr->ia_size != i_size_read(inode)) { + error = vmtruncate(inode, attr->ia_size); + if (error) + return error; + } + + setattr_copy(inode, attr); + mark_inode_dirty(inode); + return 0; } static const struct inode_operations hfsplus_file_inode_operations = { diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c index 87ac1891a185..7943ff11d489 100644 --- a/fs/hostfs/hostfs_kern.c +++ b/fs/hostfs/hostfs_kern.c @@ -849,13 +849,14 @@ int hostfs_permission(struct inode *ino, int desired) int hostfs_setattr(struct dentry *dentry, struct iattr *attr) { + struct inode *inode = dentry->d_inode; struct hostfs_iattr attrs; char *name; int err; - int fd = HOSTFS_I(dentry->d_inode)->fd; + int fd = HOSTFS_I(inode)->fd; - err = inode_change_ok(dentry->d_inode, attr); + err = inode_change_ok(inode, attr); if (err) return err; @@ -905,7 +906,18 @@ int hostfs_setattr(struct dentry *dentry, struct iattr *attr) if (err) return err; - return inode_setattr(dentry->d_inode, attr); + if ((attr->ia_valid & ATTR_SIZE) && + attr->ia_size != i_size_read(inode)) { + int error; + + error = vmtruncate(inode, attr->ia_size); + if (err) + return err; + } + + setattr_copy(inode, attr); + mark_inode_dirty(inode); + return 0; } static const struct inode_operations hostfs_iops = { diff --git a/fs/hpfs/inode.c b/fs/hpfs/inode.c index 1042a9bc97f3..3f3b397fd4e6 100644 --- a/fs/hpfs/inode.c +++ b/fs/hpfs/inode.c @@ -277,9 +277,15 @@ int hpfs_setattr(struct dentry *dentry, struct iattr *attr) if (error) goto out_unlock; - error = inode_setattr(inode, attr); - if (error) - goto out_unlock; + if ((attr->ia_valid & ATTR_SIZE) && + attr->ia_size != i_size_read(inode)) { + error = vmtruncate(inode, attr->ia_size); + if (error) + return error; + } + + setattr_copy(inode, attr); + mark_inode_dirty(inode); hpfs_write_inode(inode); diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c index a4e9a7ec3691..d5f019d48b09 100644 --- a/fs/hugetlbfs/inode.c +++ b/fs/hugetlbfs/inode.c @@ -448,19 +448,20 @@ static int hugetlbfs_setattr(struct dentry *dentry, struct iattr *attr) error = inode_change_ok(inode, attr); if (error) - goto out; + return error; if (ia_valid & ATTR_SIZE) { error = -EINVAL; - if (!(attr->ia_size & ~huge_page_mask(h))) - error = hugetlb_vmtruncate(inode, attr->ia_size); + if (attr->ia_size & ~huge_page_mask(h)) + return -EINVAL; + error = hugetlb_vmtruncate(inode, attr->ia_size); if (error) - goto out; - attr->ia_valid &= ~ATTR_SIZE; + return error; } - error = inode_setattr(inode, attr); -out: - return error; + + setattr_copy(inode, attr); + mark_inode_dirty(inode); + return 0; } static struct inode *hugetlbfs_get_inode(struct super_block *sb, uid_t uid, diff --git a/fs/jfs/file.c b/fs/jfs/file.c index 127263cc8657..c5ce6c1d1ff4 100644 --- a/fs/jfs/file.c +++ b/fs/jfs/file.c @@ -17,6 +17,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include #include #include #include "jfs_incore.h" @@ -107,11 +108,18 @@ int jfs_setattr(struct dentry *dentry, struct iattr *iattr) return rc; } - rc = inode_setattr(inode, iattr); + if ((iattr->ia_valid & ATTR_SIZE) && + iattr->ia_size != i_size_read(inode)) { + rc = vmtruncate(inode, iattr->ia_size); + if (rc) + return rc; + } - if (!rc && (iattr->ia_valid & ATTR_MODE)) - rc = jfs_acl_chmod(inode); + setattr_copy(inode, iattr); + mark_inode_dirty(inode); + if (iattr->ia_valid & ATTR_MODE) + rc = jfs_acl_chmod(inode); return rc; } diff --git a/fs/logfs/file.c b/fs/logfs/file.c index abe1cafbd4c2..23b4d03bbd26 100644 --- a/fs/logfs/file.c +++ b/fs/logfs/file.c @@ -232,15 +232,19 @@ static int logfs_setattr(struct dentry *dentry, struct iattr *attr) struct inode *inode = dentry->d_inode; int err = 0; - if (attr->ia_valid & ATTR_SIZE) + if (attr->ia_valid & ATTR_SIZE) { err = logfs_truncate(inode, attr->ia_size); - attr->ia_valid &= ~ATTR_SIZE; + if (err) + return err; + } - if (!err) - err = inode_change_ok(inode, attr); - if (!err) - err = inode_setattr(inode, attr); - return err; + err = inode_change_ok(inode, attr); + if (err) + return err; + + setattr_copy(inode, attr); + mark_inode_dirty(inode); + return 0; } const struct inode_operations logfs_reg_iops = { diff --git a/fs/minix/file.c b/fs/minix/file.c index 7a45dd1fe2e5..4493ce695ab8 100644 --- a/fs/minix/file.c +++ b/fs/minix/file.c @@ -31,7 +31,17 @@ static int minix_setattr(struct dentry *dentry, struct iattr *attr) error = inode_change_ok(inode, attr); if (error) return error; - return inode_setattr(inode, attr); + + if ((attr->ia_valid & ATTR_SIZE) && + attr->ia_size != i_size_read(inode)) { + error = vmtruncate(inode, attr->ia_size); + if (error) + return error; + } + + setattr_copy(inode, attr); + mark_inode_dirty(inode); + return 0; } const struct inode_operations minix_file_inode_operations = { diff --git a/fs/ncpfs/inode.c b/fs/ncpfs/inode.c index fa3385154023..b4e8aaae14be 100644 --- a/fs/ncpfs/inode.c +++ b/fs/ncpfs/inode.c @@ -924,9 +924,8 @@ int ncp_notify_change(struct dentry *dentry, struct iattr *attr) tmpattr.ia_valid = ATTR_MODE; tmpattr.ia_mode = attr->ia_mode; - result = inode_setattr(inode, &tmpattr); - if (result) - goto out; + setattr_copy(inode, &tmpattr); + mark_inode_dirty(inode); } } #endif @@ -954,15 +953,12 @@ int ncp_notify_change(struct dentry *dentry, struct iattr *attr) result = ncp_make_closed(inode); if (result) goto out; - { - struct iattr tmpattr; - - tmpattr.ia_valid = ATTR_SIZE; - tmpattr.ia_size = attr->ia_size; - - result = inode_setattr(inode, &tmpattr); + + if (attr->ia_size != i_size_read(inode)) { + result = vmtruncate(inode, attr->ia_size); if (result) goto out; + mark_inode_dirty(inode); } } if ((attr->ia_valid & ATTR_CTIME) != 0) { @@ -1002,8 +998,12 @@ int ncp_notify_change(struct dentry *dentry, struct iattr *attr) NCP_FINFO(inode)->nwattr = info.attributes; #endif } - if (!result) - result = inode_setattr(inode, attr); + if (result) + goto out; + + setattr_copy(inode, attr); + mark_inode_dirty(inode); + out: unlock_kernel(); return result; diff --git a/fs/nilfs2/inode.c b/fs/nilfs2/inode.c index 5c694ece172e..051d279abb37 100644 --- a/fs/nilfs2/inode.c +++ b/fs/nilfs2/inode.c @@ -656,14 +656,27 @@ int nilfs_setattr(struct dentry *dentry, struct iattr *iattr) err = nilfs_transaction_begin(sb, &ti, 0); if (unlikely(err)) return err; - err = inode_setattr(inode, iattr); - if (!err && (iattr->ia_valid & ATTR_MODE)) + + if ((iattr->ia_valid & ATTR_SIZE) && + iattr->ia_size != i_size_read(inode)) { + err = vmtruncate(inode, iattr->ia_size); + if (unlikely(err)) + goto out_err; + } + + setattr_copy(inode, iattr); + mark_inode_dirty(inode); + + if (iattr->ia_valid & ATTR_MODE) { err = nilfs_acl_chmod(inode); - if (likely(!err)) - err = nilfs_transaction_commit(sb); - else - nilfs_transaction_abort(sb); + if (unlikely(err)) + goto out_err; + } + + return nilfs_transaction_commit(sb); +out_err: + nilfs_transaction_abort(sb); return err; } diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c index 4b57fb1eac2a..fdef8f729c3a 100644 --- a/fs/ntfs/inode.c +++ b/fs/ntfs/inode.c @@ -2879,9 +2879,6 @@ void ntfs_truncate_vfs(struct inode *vi) { * * Called with ->i_mutex held. For the ATTR_SIZE (i.e. ->truncate) case, also * called with ->i_alloc_sem held for writing. - * - * Basically this is a copy of generic notify_change() and inode_setattr() - * functionality, except we intercept and abort changes in i_size. */ int ntfs_setattr(struct dentry *dentry, struct iattr *attr) { diff --git a/fs/ocfs2/dlmfs/dlmfs.c b/fs/ocfs2/dlmfs/dlmfs.c index b83d6107a1f5..85e4ccaedd1f 100644 --- a/fs/ocfs2/dlmfs/dlmfs.c +++ b/fs/ocfs2/dlmfs/dlmfs.c @@ -214,10 +214,12 @@ static int dlmfs_file_setattr(struct dentry *dentry, struct iattr *attr) attr->ia_valid &= ~ATTR_SIZE; error = inode_change_ok(inode, attr); - if (!error) - error = inode_setattr(inode, attr); + if (error) + return error; - return error; + setattr_copy(inode, attr); + mark_inode_dirty(inode); + return 0; } static unsigned int dlmfs_file_poll(struct file *file, poll_table *wait) diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index 2b10b36d1577..584cf8ac167a 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c @@ -1238,13 +1238,21 @@ int ocfs2_setattr(struct dentry *dentry, struct iattr *attr) * Otherwise, we could get into problems with truncate as * ip_alloc_sem is used there to protect against i_size * changes. + * + * XXX: this means the conditional below can probably be removed. */ - status = inode_setattr(inode, attr); - if (status < 0) { - mlog_errno(status); - goto bail_commit; + if ((attr->ia_valid & ATTR_SIZE) && + attr->ia_size != i_size_read(inode)) { + status = vmtruncate(inode, attr->ia_size); + if (status) { + mlog_errno(status); + goto bail_commit; + } } + setattr_copy(inode, attr); + mark_inode_dirty(inode); + status = ocfs2_mark_inode_dirty(handle, inode, bh); if (status < 0) mlog_errno(status); diff --git a/fs/omfs/file.c b/fs/omfs/file.c index 78c9f0c1a2f3..5542c284dc1c 100644 --- a/fs/omfs/file.c +++ b/fs/omfs/file.c @@ -349,7 +349,17 @@ static int omfs_setattr(struct dentry *dentry, struct iattr *attr) error = inode_change_ok(inode, attr); if (error) return error; - return inode_setattr(inode, attr); + + if ((attr->ia_valid & ATTR_SIZE) && + attr->ia_size != i_size_read(inode)) { + error = vmtruncate(inode, attr->ia_size); + if (error) + return error; + } + + setattr_copy(inode, attr); + mark_inode_dirty(inode); + return 0; } const struct inode_operations omfs_file_inops = { diff --git a/fs/proc/base.c b/fs/proc/base.c index acb7ef80ea4f..a49d9dd06d1d 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -561,9 +561,19 @@ static int proc_setattr(struct dentry *dentry, struct iattr *attr) return -EPERM; error = inode_change_ok(inode, attr); - if (!error) - error = inode_setattr(inode, attr); - return error; + if (error) + return error; + + if ((attr->ia_valid & ATTR_SIZE) && + attr->ia_size != i_size_read(inode)) { + error = vmtruncate(inode, attr->ia_size); + if (error) + return error; + } + + setattr_copy(inode, attr); + mark_inode_dirty(inode); + return 0; } static const struct inode_operations proc_def_inode_operations = { diff --git a/fs/proc/generic.c b/fs/proc/generic.c index 2791907744ed..dd29f0337661 100644 --- a/fs/proc/generic.c +++ b/fs/proc/generic.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -258,17 +259,22 @@ static int proc_notify_change(struct dentry *dentry, struct iattr *iattr) error = inode_change_ok(inode, iattr); if (error) - goto out; + return error; - error = inode_setattr(inode, iattr); - if (error) - goto out; + if ((iattr->ia_valid & ATTR_SIZE) && + iattr->ia_size != i_size_read(inode)) { + error = vmtruncate(inode, iattr->ia_size); + if (error) + return error; + } + + setattr_copy(inode, iattr); + mark_inode_dirty(inode); de->uid = inode->i_uid; de->gid = inode->i_gid; de->mode = inode->i_mode; -out: - return error; + return 0; } static int proc_getattr(struct vfsmount *mnt, struct dentry *dentry, diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c index 6ff9981f0a18..5be436ea088e 100644 --- a/fs/proc/proc_sysctl.c +++ b/fs/proc/proc_sysctl.c @@ -329,10 +329,19 @@ static int proc_sys_setattr(struct dentry *dentry, struct iattr *attr) return -EPERM; error = inode_change_ok(inode, attr); - if (!error) - error = inode_setattr(inode, attr); + if (error) + return error; + + if ((attr->ia_valid & ATTR_SIZE) && + attr->ia_size != i_size_read(inode)) { + error = vmtruncate(inode, attr->ia_size); + if (error) + return error; + } - return error; + setattr_copy(inode, attr); + mark_inode_dirty(inode); + return 0; } static int proc_sys_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c index 045729f5674a..2b8dc5c22867 100644 --- a/fs/reiserfs/inode.c +++ b/fs/reiserfs/inode.c @@ -3134,55 +3134,62 @@ int reiserfs_setattr(struct dentry *dentry, struct iattr *attr) } error = inode_change_ok(inode, attr); - if (!error) { - if ((ia_valid & ATTR_UID && attr->ia_uid != inode->i_uid) || - (ia_valid & ATTR_GID && attr->ia_gid != inode->i_gid)) { - error = reiserfs_chown_xattrs(inode, attr); + if (error) + goto out; - if (!error) { - struct reiserfs_transaction_handle th; - int jbegin_count = - 2 * - (REISERFS_QUOTA_INIT_BLOCKS(inode->i_sb) + - REISERFS_QUOTA_DEL_BLOCKS(inode->i_sb)) + - 2; - - /* (user+group)*(old+new) structure - we count quota info and , inode write (sb, inode) */ - error = - journal_begin(&th, inode->i_sb, - jbegin_count); - if (error) - goto out; - error = dquot_transfer(inode, attr); - if (error) { - journal_end(&th, inode->i_sb, - jbegin_count); - goto out; - } - /* Update corresponding info in inode so that everything is in - * one transaction */ - if (attr->ia_valid & ATTR_UID) - inode->i_uid = attr->ia_uid; - if (attr->ia_valid & ATTR_GID) - inode->i_gid = attr->ia_gid; - mark_inode_dirty(inode); - error = - journal_end(&th, inode->i_sb, jbegin_count); - } - } - if (!error) { - /* - * Relax the lock here, as it might truncate the - * inode pages and wait for inode pages locks. - * To release such page lock, the owner needs the - * reiserfs lock - */ - reiserfs_write_unlock_once(inode->i_sb, depth); - error = inode_setattr(inode, attr); - depth = reiserfs_write_lock_once(inode->i_sb); + if ((ia_valid & ATTR_UID && attr->ia_uid != inode->i_uid) || + (ia_valid & ATTR_GID && attr->ia_gid != inode->i_gid)) { + struct reiserfs_transaction_handle th; + int jbegin_count = + 2 * + (REISERFS_QUOTA_INIT_BLOCKS(inode->i_sb) + + REISERFS_QUOTA_DEL_BLOCKS(inode->i_sb)) + + 2; + + error = reiserfs_chown_xattrs(inode, attr); + + if (error) + return error; + + /* (user+group)*(old+new) structure - we count quota info and , inode write (sb, inode) */ + error = journal_begin(&th, inode->i_sb, jbegin_count); + if (error) + goto out; + error = dquot_transfer(inode, attr); + if (error) { + journal_end(&th, inode->i_sb, jbegin_count); + goto out; } + + /* Update corresponding info in inode so that everything is in + * one transaction */ + if (attr->ia_valid & ATTR_UID) + inode->i_uid = attr->ia_uid; + if (attr->ia_valid & ATTR_GID) + inode->i_gid = attr->ia_gid; + mark_inode_dirty(inode); + error = journal_end(&th, inode->i_sb, jbegin_count); + if (error) + goto out; } + /* + * Relax the lock here, as it might truncate the + * inode pages and wait for inode pages locks. + * To release such page lock, the owner needs the + * reiserfs lock + */ + reiserfs_write_unlock_once(inode->i_sb, depth); + if ((attr->ia_valid & ATTR_SIZE) && + attr->ia_size != i_size_read(inode)) + error = vmtruncate(inode, attr->ia_size); + + if (!error) { + setattr_copy(inode, attr); + mark_inode_dirty(inode); + } + depth = reiserfs_write_lock_once(inode->i_sb); + if (!error && reiserfs_posixacl(inode->i_sb)) { if (attr->ia_valid & ATTR_MODE) error = reiserfs_acl_chmod(inode); diff --git a/fs/sysv/file.c b/fs/sysv/file.c index 94f6319292a1..0a65939508e9 100644 --- a/fs/sysv/file.c +++ b/fs/sysv/file.c @@ -38,7 +38,17 @@ static int sysv_setattr(struct dentry *dentry, struct iattr *attr) error = inode_change_ok(inode, attr); if (error) return error; - return inode_setattr(inode, attr); + + if ((attr->ia_valid & ATTR_SIZE) && + attr->ia_size != i_size_read(inode)) { + error = vmtruncate(inode, attr->ia_size); + if (error) + return error; + } + + setattr_copy(inode, attr); + mark_inode_dirty(inode); + return 0; } const struct inode_operations sysv_file_inode_operations = { diff --git a/fs/udf/file.c b/fs/udf/file.c index 7376032c89ce..04bb5bf07630 100644 --- a/fs/udf/file.c +++ b/fs/udf/file.c @@ -236,7 +236,17 @@ static int udf_setattr(struct dentry *dentry, struct iattr *attr) error = inode_change_ok(inode, attr); if (error) return error; - return inode_setattr(inode, attr); + + if ((attr->ia_valid & ATTR_SIZE) && + attr->ia_size != i_size_read(inode)) { + error = vmtruncate(inode, attr->ia_size); + if (error) + return error; + } + + setattr_copy(inode, attr); + mark_inode_dirty(inode); + return 0; } const struct inode_operations udf_file_inode_operations = { diff --git a/fs/ufs/truncate.c b/fs/ufs/truncate.c index 589e01a465ba..085e11623b7b 100644 --- a/fs/ufs/truncate.c +++ b/fs/ufs/truncate.c @@ -525,7 +525,10 @@ int ufs_setattr(struct dentry *dentry, struct iattr *attr) if (error) return error; } - return inode_setattr(inode, attr); + + setattr_copy(inode, attr); + mark_inode_dirty(inode); + return 0; } const struct inode_operations ufs_file_inode_operations = { diff --git a/include/linux/fs.h b/include/linux/fs.h index 8ebb5f01a418..6ecb83c00a6d 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2392,7 +2392,6 @@ extern int buffer_migrate_page(struct address_space *, extern int inode_change_ok(const struct inode *, struct iattr *); extern int inode_newsize_ok(const struct inode *, loff_t offset); -extern int __must_check inode_setattr(struct inode *, const struct iattr *); extern void setattr_copy(struct inode *inode, const struct iattr *attr); extern void file_update_time(struct file *file); -- cgit v1.2.3 From db78b877f7744bec4a9d9f9e7d10da3931d7cd39 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 4 Jun 2010 11:30:03 +0200 Subject: always call inode_change_ok early in ->setattr Make sure we call inode_change_ok before doing any changes in ->setattr, and make sure to call it even if our fs wants to ignore normal UNIX permissions, but use the ATTR_FORCE to skip those. Signed-off-by: Christoph Hellwig Signed-off-by: Al Viro --- fs/cifs/inode.c | 29 +++++++++++++---------------- fs/fat/file.c | 30 +++++++++++++++--------------- fs/fuse/dir.c | 11 ++++++----- fs/logfs/file.c | 8 ++++---- fs/reiserfs/inode.c | 8 ++++---- mm/shmem.c | 10 ++++++---- 6 files changed, 48 insertions(+), 48 deletions(-) (limited to 'fs') diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 9c6a40f5cc57..b95f4a5af013 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -1796,14 +1796,12 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs) xid = GetXid(); - if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM) == 0) { - /* check if we have permission to change attrs */ - rc = inode_change_ok(inode, attrs); - if (rc < 0) - goto out; - else - rc = 0; - } + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM) + attrs->ia_valid |= ATTR_FORCE; + + rc = inode_change_ok(inode, attrs); + if (rc < 0) + goto out; full_path = build_path_from_dentry(direntry); if (full_path == NULL) { @@ -1934,14 +1932,13 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs) cFYI(1, "setattr on file %s attrs->iavalid 0x%x", direntry->d_name.name, attrs->ia_valid); - if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM) == 0) { - /* check if we have permission to change attrs */ - rc = inode_change_ok(inode, attrs); - if (rc < 0) { - FreeXid(xid); - return rc; - } else - rc = 0; + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM) + attrs->ia_valid |= ATTR_FORCE; + + rc = inode_change_ok(inode, attrs); + if (rc < 0) { + FreeXid(xid); + return rc; } full_path = build_path_from_dentry(direntry); diff --git a/fs/fat/file.c b/fs/fat/file.c index 20813d2c7d61..b2eedcee7516 100644 --- a/fs/fat/file.c +++ b/fs/fat/file.c @@ -387,21 +387,6 @@ int fat_setattr(struct dentry *dentry, struct iattr *attr) unsigned int ia_valid; int error; - /* - * Expand the file. Since inode_setattr() updates ->i_size - * before calling the ->truncate(), but FAT needs to fill the - * hole before it. XXX: this is no longer true with new truncate - * sequence. - */ - if (attr->ia_valid & ATTR_SIZE) { - if (attr->ia_size > inode->i_size) { - error = fat_cont_expand(inode, attr->ia_size); - if (error || attr->ia_valid == ATTR_SIZE) - goto out; - attr->ia_valid &= ~ATTR_SIZE; - } - } - /* Check for setting the inode time. */ ia_valid = attr->ia_valid; if (ia_valid & TIMES_SET_FLAGS) { @@ -417,6 +402,21 @@ int fat_setattr(struct dentry *dentry, struct iattr *attr) goto out; } + /* + * Expand the file. Since inode_setattr() updates ->i_size + * before calling the ->truncate(), but FAT needs to fill the + * hole before it. XXX: this is no longer true with new truncate + * sequence. + */ + if (attr->ia_valid & ATTR_SIZE) { + if (attr->ia_size > inode->i_size) { + error = fat_cont_expand(inode, attr->ia_size); + if (error || attr->ia_valid == ATTR_SIZE) + goto out; + attr->ia_valid &= ~ATTR_SIZE; + } + } + if (((attr->ia_valid & ATTR_UID) && (attr->ia_uid != sbi->options.fs_uid)) || ((attr->ia_valid & ATTR_GID) && diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 3cdc5f78a406..43a9b3730a98 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -1270,11 +1270,12 @@ static int fuse_do_setattr(struct dentry *entry, struct iattr *attr, if (!fuse_allow_task(fc, current)) return -EACCES; - if (fc->flags & FUSE_DEFAULT_PERMISSIONS) { - err = inode_change_ok(inode, attr); - if (err) - return err; - } + if (!(fc->flags & FUSE_DEFAULT_PERMISSIONS)) + attr->ia_valid |= ATTR_FORCE; + + err = inode_change_ok(inode, attr); + if (err) + return err; if ((attr->ia_valid & ATTR_OPEN) && fc->atomic_o_trunc) return 0; diff --git a/fs/logfs/file.c b/fs/logfs/file.c index 23b4d03bbd26..4dd0f7c06e39 100644 --- a/fs/logfs/file.c +++ b/fs/logfs/file.c @@ -232,16 +232,16 @@ static int logfs_setattr(struct dentry *dentry, struct iattr *attr) struct inode *inode = dentry->d_inode; int err = 0; + err = inode_change_ok(inode, attr); + if (err) + return err; + if (attr->ia_valid & ATTR_SIZE) { err = logfs_truncate(inode, attr->ia_size); if (err) return err; } - err = inode_change_ok(inode, attr); - if (err) - return err; - setattr_copy(inode, attr); mark_inode_dirty(inode); return 0; diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c index 2b8dc5c22867..46ba1cfc2df3 100644 --- a/fs/reiserfs/inode.c +++ b/fs/reiserfs/inode.c @@ -3084,6 +3084,10 @@ int reiserfs_setattr(struct dentry *dentry, struct iattr *attr) int depth; int error; + error = inode_change_ok(inode, attr); + if (error) + return error; + /* must be turned off for recursive notify_change calls */ ia_valid = attr->ia_valid &= ~(ATTR_KILL_SUID|ATTR_KILL_SGID); @@ -3133,10 +3137,6 @@ int reiserfs_setattr(struct dentry *dentry, struct iattr *attr) goto out; } - error = inode_change_ok(inode, attr); - if (error) - goto out; - if ((ia_valid & ATTR_UID && attr->ia_uid != inode->i_uid) || (ia_valid & ATTR_GID && attr->ia_gid != inode->i_gid)) { struct reiserfs_transaction_handle th; diff --git a/mm/shmem.c b/mm/shmem.c index 3b58ad65d26c..0a43505eeaec 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -767,6 +767,10 @@ static int shmem_notify_change(struct dentry *dentry, struct iattr *attr) loff_t newsize = attr->ia_size; int error; + error = inode_change_ok(inode, attr); + if (error) + return error; + if (S_ISREG(inode->i_mode) && (attr->ia_valid & ATTR_SIZE) && newsize != inode->i_size) { struct page *page = NULL; @@ -809,11 +813,9 @@ static int shmem_notify_change(struct dentry *dentry, struct iattr *attr) shmem_truncate_range(inode, newsize, (loff_t)-1); } - error = inode_change_ok(inode, attr); - if (!error) - setattr_copy(inode, attr); + setattr_copy(inode, attr); #ifdef CONFIG_TMPFS_POSIX_ACL - if (!error && (attr->ia_valid & ATTR_MODE)) + if (attr->ia_valid & ATTR_MODE) error = generic_acl_chmod(inode); #endif return error; -- cgit v1.2.3 From 2c27c65ed0696f0b5df2dad2cf6462d72164d547 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 4 Jun 2010 11:30:04 +0200 Subject: check ATTR_SIZE contraints in inode_change_ok Make sure we check the truncate constraints early on in ->setattr by adding those checks to inode_change_ok. Also clean up and document inode_change_ok to make this obvious. As a fallout we don't have to call inode_newsize_ok from simple_setsize and simplify it down to a truncate_setsize which doesn't return an error. This simplifies a lot of setattr implementations and means we use truncate_setsize almost everywhere. Get rid of fat_setsize now that it's trivial and mark ext2_setsize static to make the calling convention obvious. Keep the inode_newsize_ok in vmtruncate for now as all callers need an audit for its removal anyway. Note: setattr code in ecryptfs doesn't call inode_change_ok at all and needs a deeper audit, but that is left for later. Signed-off-by: Christoph Hellwig Signed-off-by: Al Viro --- fs/adfs/inode.c | 5 +---- fs/attr.c | 44 ++++++++++++++++++++++++++++++-------------- fs/ecryptfs/inode.c | 18 ++++++++++++++---- fs/ext2/inode.c | 12 ++---------- fs/fat/fat.h | 1 - fs/fat/file.c | 17 ++--------------- fs/fuse/dir.c | 6 +----- fs/gfs2/aops.c | 4 ++-- fs/gfs2/ops_inode.c | 6 ++---- fs/jffs2/fs.c | 4 ++-- fs/libfs.c | 51 ++------------------------------------------------- fs/ocfs2/file.c | 6 +++--- fs/ramfs/file-nommu.c | 5 ++--- fs/smbfs/inode.c | 4 +--- fs/ubifs/file.c | 23 ++++++++--------------- fs/ubifs/ubifs.h | 2 +- fs/ufs/truncate.c | 11 +++-------- include/linux/fs.h | 1 - include/linux/mm.h | 1 + mm/shmem.c | 5 ++--- mm/truncate.c | 38 +++++++++++++++++++++++++++++--------- 21 files changed, 108 insertions(+), 156 deletions(-) (limited to 'fs') diff --git a/fs/adfs/inode.c b/fs/adfs/inode.c index b3dec193036b..65794b8fe79e 100644 --- a/fs/adfs/inode.c +++ b/fs/adfs/inode.c @@ -333,10 +333,7 @@ adfs_notify_change(struct dentry *dentry, struct iattr *attr) /* XXX: this is missing some actual on-disk truncation.. */ if (ia_valid & ATTR_SIZE) - error = simple_setsize(inode, attr->ia_size); - - if (error) - goto out; + truncate_setsize(inode, attr->ia_size); if (ia_valid & ATTR_MTIME) { inode->i_mtime = attr->ia_mtime; diff --git a/fs/attr.c b/fs/attr.c index ed44d8ae8bf1..7ca41811afa1 100644 --- a/fs/attr.c +++ b/fs/attr.c @@ -14,35 +14,53 @@ #include #include -/* Taken over from the old code... */ - -/* POSIX UID/GID verification for setting inode attributes. */ +/** + * inode_change_ok - check if attribute changes to an inode are allowed + * @inode: inode to check + * @attr: attributes to change + * + * Check if we are allowed to change the attributes contained in @attr + * in the given inode. This includes the normal unix access permission + * checks, as well as checks for rlimits and others. + * + * Should be called as the first thing in ->setattr implementations, + * possibly after taking additional locks. + */ int inode_change_ok(const struct inode *inode, struct iattr *attr) { - int retval = -EPERM; unsigned int ia_valid = attr->ia_valid; + /* + * First check size constraints. These can't be overriden using + * ATTR_FORCE. + */ + if (ia_valid & ATTR_SIZE) { + int error = inode_newsize_ok(inode, attr->ia_size); + if (error) + return error; + } + /* If force is set do it anyway. */ if (ia_valid & ATTR_FORCE) - goto fine; + return 0; /* Make sure a caller can chown. */ if ((ia_valid & ATTR_UID) && (current_fsuid() != inode->i_uid || attr->ia_uid != inode->i_uid) && !capable(CAP_CHOWN)) - goto error; + return -EPERM; /* Make sure caller can chgrp. */ if ((ia_valid & ATTR_GID) && (current_fsuid() != inode->i_uid || (!in_group_p(attr->ia_gid) && attr->ia_gid != inode->i_gid)) && !capable(CAP_CHOWN)) - goto error; + return -EPERM; /* Make sure a caller can chmod. */ if (ia_valid & ATTR_MODE) { if (!is_owner_or_cap(inode)) - goto error; + return -EPERM; /* Also check the setgid bit! */ if (!in_group_p((ia_valid & ATTR_GID) ? attr->ia_gid : inode->i_gid) && !capable(CAP_FSETID)) @@ -52,12 +70,10 @@ int inode_change_ok(const struct inode *inode, struct iattr *attr) /* Check for setting the inode time. */ if (ia_valid & (ATTR_MTIME_SET | ATTR_ATIME_SET | ATTR_TIMES_SET)) { if (!is_owner_or_cap(inode)) - goto error; + return -EPERM; } -fine: - retval = 0; -error: - return retval; + + return 0; } EXPORT_SYMBOL(inode_change_ok); @@ -113,7 +129,7 @@ EXPORT_SYMBOL(inode_newsize_ok); * * setattr_copy updates the inode's metadata with that specified * in attr. Noticably missing is inode size update, which is more complex - * as it requires pagecache updates. See simple_setsize. + * as it requires pagecache updates. * * The inode is not marked as dirty after this operation. The rationale is * that for "simple" filesystems, the struct inode is the inode storage. diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index 31ef5252f0fe..82900b063b1e 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -804,10 +804,20 @@ static int truncate_upper(struct dentry *dentry, struct iattr *ia, size_t num_zeros = (PAGE_CACHE_SIZE - (ia->ia_size & ~PAGE_CACHE_MASK)); + + /* + * XXX(truncate) this should really happen at the begginning + * of ->setattr. But the code is too messy to that as part + * of a larger patch. ecryptfs is also totally missing out + * on the inode_change_ok check at the beginning of + * ->setattr while would include this. + */ + rc = inode_newsize_ok(inode, ia->ia_size); + if (rc) + goto out; + if (!(crypt_stat->flags & ECRYPTFS_ENCRYPTED)) { - rc = simple_setsize(inode, ia->ia_size); - if (rc) - goto out; + truncate_setsize(inode, ia->ia_size); lower_ia->ia_size = ia->ia_size; lower_ia->ia_valid |= ATTR_SIZE; goto out; @@ -830,7 +840,7 @@ static int truncate_upper(struct dentry *dentry, struct iattr *ia, goto out; } } - simple_setsize(inode, ia->ia_size); + truncate_setsize(inode, ia->ia_size); rc = ecryptfs_write_inode_size_to_metadata(inode); if (rc) { printk(KERN_ERR "Problem with " diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index 7dee7b3f3688..069620b30d4d 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -1156,15 +1156,10 @@ static void ext2_truncate_blocks(struct inode *inode, loff_t offset) __ext2_truncate_blocks(inode, offset); } -int ext2_setsize(struct inode *inode, loff_t newsize) +static int ext2_setsize(struct inode *inode, loff_t newsize) { - loff_t oldsize; int error; - error = inode_newsize_ok(inode, newsize); - if (error) - return error; - if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))) return -EINVAL; @@ -1184,10 +1179,7 @@ int ext2_setsize(struct inode *inode, loff_t newsize) if (error) return error; - oldsize = inode->i_size; - i_size_write(inode, newsize); - truncate_pagecache(inode, oldsize, newsize); - + truncate_setsize(inode, newsize); __ext2_truncate_blocks(inode, newsize); inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC; diff --git a/fs/fat/fat.h b/fs/fat/fat.h index 27ac25725954..d75a77f85c28 100644 --- a/fs/fat/fat.h +++ b/fs/fat/fat.h @@ -306,7 +306,6 @@ extern long fat_generic_ioctl(struct file *filp, unsigned int cmd, extern const struct file_operations fat_file_operations; extern const struct inode_operations fat_file_inode_operations; extern int fat_setattr(struct dentry * dentry, struct iattr * attr); -extern int fat_setsize(struct inode *inode, loff_t offset); extern void fat_truncate_blocks(struct inode *inode, loff_t offset); extern int fat_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat); diff --git a/fs/fat/file.c b/fs/fat/file.c index b2eedcee7516..7257752b6d5d 100644 --- a/fs/fat/file.c +++ b/fs/fat/file.c @@ -364,18 +364,6 @@ static int fat_allow_set_time(struct msdos_sb_info *sbi, struct inode *inode) return 0; } -int fat_setsize(struct inode *inode, loff_t offset) -{ - int error; - - error = simple_setsize(inode, offset); - if (error) - return error; - fat_truncate_blocks(inode, offset); - - return error; -} - #define TIMES_SET_FLAGS (ATTR_MTIME_SET | ATTR_ATIME_SET | ATTR_TIMES_SET) /* valid file mode bits */ #define FAT_VALID_MODE (S_IFREG | S_IFDIR | S_IRWXUGO) @@ -441,9 +429,8 @@ int fat_setattr(struct dentry *dentry, struct iattr *attr) } if (attr->ia_valid & ATTR_SIZE) { - error = fat_setsize(inode, attr->ia_size); - if (error) - goto out; + truncate_setsize(inode, attr->ia_size); + fat_truncate_blocks(inode, attr->ia_size); } setattr_copy(inode, attr); diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 43a9b3730a98..3978a42d4f04 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -1280,12 +1280,8 @@ static int fuse_do_setattr(struct dentry *entry, struct iattr *attr, if ((attr->ia_valid & ATTR_OPEN) && fc->atomic_o_trunc) return 0; - if (attr->ia_valid & ATTR_SIZE) { - err = inode_newsize_ok(inode, attr->ia_size); - if (err) - return err; + if (attr->ia_valid & ATTR_SIZE) is_truncate = true; - } req = fuse_get_req(fc); if (IS_ERR(req)) diff --git a/fs/gfs2/aops.c b/fs/gfs2/aops.c index 703000d6e4d2..54fe087bf54c 100644 --- a/fs/gfs2/aops.c +++ b/fs/gfs2/aops.c @@ -702,12 +702,12 @@ out: page_cache_release(page); /* - * XXX(hch): the call below should probably be replaced with + * XXX(truncate): the call below should probably be replaced with * a call to the gfs2-specific truncate blocks helper to actually * release disk blocks.. */ if (pos + len > ip->i_inode.i_size) - simple_setsize(&ip->i_inode, ip->i_inode.i_size); + truncate_setsize(&ip->i_inode, ip->i_inode.i_size); out_endtrans: gfs2_trans_end(sdp); out_trans_fail: diff --git a/fs/gfs2/ops_inode.c b/fs/gfs2/ops_inode.c index d7d410a4ca42..1009be2c9737 100644 --- a/fs/gfs2/ops_inode.c +++ b/fs/gfs2/ops_inode.c @@ -1072,7 +1072,7 @@ int gfs2_permission(struct inode *inode, int mask) } /* - * XXX: should be changed to have proper ordering by opencoding simple_setsize + * XXX(truncate): the truncate_setsize calls should be moved to the end. */ static int setattr_size(struct inode *inode, struct iattr *attr) { @@ -1084,10 +1084,8 @@ static int setattr_size(struct inode *inode, struct iattr *attr) error = gfs2_trans_begin(sdp, 0, sdp->sd_jdesc->jd_blocks); if (error) return error; - error = simple_setsize(inode, attr->ia_size); + truncate_setsize(inode, attr->ia_size); gfs2_trans_end(sdp); - if (error) - return error; } error = gfs2_truncatei(ip, attr->ia_size); diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c index 459d39d1ea0b..1b2426604fe3 100644 --- a/fs/jffs2/fs.c +++ b/fs/jffs2/fs.c @@ -169,13 +169,13 @@ int jffs2_do_setattr (struct inode *inode, struct iattr *iattr) mutex_unlock(&f->sem); jffs2_complete_reservation(c); - /* We have to do the simple_setsize() without f->sem held, since + /* We have to do the truncate_setsize() without f->sem held, since some pages may be locked and waiting for it in readpage(). We are protected from a simultaneous write() extending i_size back past iattr->ia_size, because do_truncate() holds the generic inode semaphore. */ if (ivalid & ATTR_SIZE && inode->i_size > iattr->ia_size) { - simple_setsize(inode, iattr->ia_size); + truncate_setsize(inode, iattr->ia_size); inode->i_blocks = (inode->i_size + 511) >> 9; } diff --git a/fs/libfs.c b/fs/libfs.c index 40562224b718..0a9da95317f7 100644 --- a/fs/libfs.c +++ b/fs/libfs.c @@ -326,49 +326,6 @@ int simple_rename(struct inode *old_dir, struct dentry *old_dentry, return 0; } -/** - * simple_setsize - handle core mm and vfs requirements for file size change - * @inode: inode - * @newsize: new file size - * - * Returns 0 on success, -error on failure. - * - * simple_setsize must be called with inode_mutex held. - * - * simple_setsize will check that the requested new size is OK (see - * inode_newsize_ok), and then will perform the necessary i_size update - * and pagecache truncation (if necessary). It will be typically be called - * from the filesystem's setattr function when ATTR_SIZE is passed in. - * - * The inode itself must have correct permissions and attributes to allow - * i_size to be changed, this function then just checks that the new size - * requested is valid. - * - * In the case of simple in-memory filesystems with inodes stored solely - * in the inode cache, and file data in the pagecache, nothing more needs - * to be done to satisfy a truncate request. Filesystems with on-disk - * blocks for example will need to free them in the case of truncate, in - * that case it may be easier not to use simple_setsize (but each of its - * components will likely be required at some point to update pagecache - * and inode etc). - */ -int simple_setsize(struct inode *inode, loff_t newsize) -{ - loff_t oldsize; - int error; - - error = inode_newsize_ok(inode, newsize); - if (error) - return error; - - oldsize = inode->i_size; - i_size_write(inode, newsize); - truncate_pagecache(inode, oldsize, newsize); - - return error; -} -EXPORT_SYMBOL(simple_setsize); - /** * simple_setattr - setattr for simple filesystem * @dentry: dentry @@ -394,12 +351,8 @@ int simple_setattr(struct dentry *dentry, struct iattr *iattr) if (error) return error; - if (iattr->ia_valid & ATTR_SIZE) { - error = simple_setsize(inode, iattr->ia_size); - if (error) - return error; - } - + if (iattr->ia_valid & ATTR_SIZE) + truncate_setsize(inode, iattr->ia_size); setattr_copy(inode, iattr); mark_inode_dirty(inode); return 0; diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index 584cf8ac167a..81296b4e3646 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c @@ -1233,7 +1233,7 @@ int ocfs2_setattr(struct dentry *dentry, struct iattr *attr) } /* - * This will intentionally not wind up calling simple_setsize(), + * This will intentionally not wind up calling truncate_setsize(), * since all the work for a size change has been done above. * Otherwise, we could get into problems with truncate as * ip_alloc_sem is used there to protect against i_size @@ -2308,12 +2308,12 @@ relock: * blocks outside i_size. Trim these off again. * Don't need i_size_read because we hold i_mutex. * - * XXX(hch): this looks buggy because ocfs2 did not + * XXX(truncate): this looks buggy because ocfs2 did not * actually implement ->truncate. Take a look at * the new truncate sequence and update this accordingly */ if (*ppos + count > inode->i_size) - simple_setsize(inode, inode->i_size); + truncate_setsize(inode, inode->i_size); ret = written; goto out_dio; } diff --git a/fs/ramfs/file-nommu.c b/fs/ramfs/file-nommu.c index 8d44f0347b27..9eead2c796b7 100644 --- a/fs/ramfs/file-nommu.c +++ b/fs/ramfs/file-nommu.c @@ -146,9 +146,8 @@ static int ramfs_nommu_resize(struct inode *inode, loff_t newsize, loff_t size) return ret; } - ret = simple_setsize(inode, newsize); - - return ret; + truncate_setsize(inode, newsize); + return 0; } /*****************************************************************************/ diff --git a/fs/smbfs/inode.c b/fs/smbfs/inode.c index 9551cb6f7fe4..e338f0a5a70d 100644 --- a/fs/smbfs/inode.c +++ b/fs/smbfs/inode.c @@ -714,9 +714,7 @@ smb_notify_change(struct dentry *dentry, struct iattr *attr) error = server->ops->truncate(inode, attr->ia_size); if (error) goto out; - error = simple_setsize(inode, attr->ia_size); - if (error) - goto out; + truncate_setsize(inode, attr->ia_size); refresh = 1; } diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c index 12f445cee9f7..03ae894c45de 100644 --- a/fs/ubifs/file.c +++ b/fs/ubifs/file.c @@ -967,14 +967,15 @@ static int do_writepage(struct page *page, int len) * the page locked, and it locks @ui_mutex. However, write-back does take inode * @i_mutex, which means other VFS operations may be run on this inode at the * same time. And the problematic one is truncation to smaller size, from where - * we have to call 'simple_setsize()', which first changes @inode->i_size, then + * we have to call 'truncate_setsize()', which first changes @inode->i_size, then * drops the truncated pages. And while dropping the pages, it takes the page - * lock. This means that 'do_truncation()' cannot call 'simple_setsize()' with + * lock. This means that 'do_truncation()' cannot call 'truncate_setsize()' with * @ui_mutex locked, because it would deadlock with 'ubifs_writepage()'. This * means that @inode->i_size is changed while @ui_mutex is unlocked. * - * XXX: with the new truncate the above is not true anymore, the simple_setsize - * calls can be replaced with the individual components. + * XXX(truncate): with the new truncate sequence this is not true anymore, + * and the calls to truncate_setsize can be move around freely. They should + * be moved to the very end of the truncate sequence. * * But in 'ubifs_writepage()' we have to guarantee that we do not write beyond * inode size. How do we do this if @inode->i_size may became smaller while we @@ -1128,9 +1129,7 @@ static int do_truncation(struct ubifs_info *c, struct inode *inode, budgeted = 0; } - err = simple_setsize(inode, new_size); - if (err) - goto out_budg; + truncate_setsize(inode, new_size); if (offset) { pgoff_t index = new_size >> PAGE_CACHE_SHIFT; @@ -1217,16 +1216,14 @@ static int do_setattr(struct ubifs_info *c, struct inode *inode, if (attr->ia_valid & ATTR_SIZE) { dbg_gen("size %lld -> %lld", inode->i_size, new_size); - err = simple_setsize(inode, new_size); - if (err) - goto out; + truncate_setsize(inode, new_size); } mutex_lock(&ui->ui_mutex); if (attr->ia_valid & ATTR_SIZE) { /* Truncation changes inode [mc]time */ inode->i_mtime = inode->i_ctime = ubifs_current_time(inode); - /* 'simple_setsize()' changed @i_size, update @ui_size */ + /* 'truncate_setsize()' changed @i_size, update @ui_size */ ui->ui_size = inode->i_size; } @@ -1248,10 +1245,6 @@ static int do_setattr(struct ubifs_info *c, struct inode *inode, if (IS_SYNC(inode)) err = inode->i_sb->s_op->write_inode(inode, NULL); return err; - -out: - ubifs_release_budget(c, &req); - return err; } int ubifs_setattr(struct dentry *dentry, struct iattr *attr) diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h index 04310878f449..0c9876b396dd 100644 --- a/fs/ubifs/ubifs.h +++ b/fs/ubifs/ubifs.h @@ -379,7 +379,7 @@ struct ubifs_gced_idx_leb { * The @ui_size is a "shadow" variable for @inode->i_size and UBIFS uses * @ui_size instead of @inode->i_size. The reason for this is that UBIFS cannot * make sure @inode->i_size is always changed under @ui_mutex, because it - * cannot call 'simple_setsize()' with @ui_mutex locked, because it would deadlock + * cannot call 'truncate_setsize()' with @ui_mutex locked, because it would deadlock * with 'ubifs_writepage()' (see file.c). All the other inode fields are * changed under @ui_mutex, so they do not need "shadow" fields. Note, one * could consider to rework locking and base it on "shadow" fields. diff --git a/fs/ufs/truncate.c b/fs/ufs/truncate.c index 085e11623b7b..34d5cb135320 100644 --- a/fs/ufs/truncate.c +++ b/fs/ufs/truncate.c @@ -500,11 +500,6 @@ out: return err; } -/* - * TODO: - * - truncate case should use proper ordering instead of using - * simple_setsize - */ int ufs_setattr(struct dentry *dentry, struct iattr *attr) { struct inode *inode = dentry->d_inode; @@ -518,9 +513,9 @@ int ufs_setattr(struct dentry *dentry, struct iattr *attr) if (ia_valid & ATTR_SIZE && attr->ia_size != inode->i_size) { loff_t old_i_size = inode->i_size; - error = simple_setsize(inode, attr->ia_size); - if (error) - return error; + /* XXX(truncate): truncate_setsize should be called last */ + truncate_setsize(inode, attr->ia_size); + error = ufs_truncate(inode, old_i_size); if (error) return error; diff --git a/include/linux/fs.h b/include/linux/fs.h index 6ecb83c00a6d..5547b1b027db 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2355,7 +2355,6 @@ extern int simple_link(struct dentry *, struct inode *, struct dentry *); extern int simple_unlink(struct inode *, struct dentry *); extern int simple_rmdir(struct inode *, struct dentry *); extern int simple_rename(struct inode *, struct dentry *, struct inode *, struct dentry *); -extern int simple_setsize(struct inode *, loff_t); extern int noop_fsync(struct file *, int); extern int simple_empty(struct dentry *); extern int simple_readpage(struct file *file, struct page *page); diff --git a/include/linux/mm.h b/include/linux/mm.h index a2b48041b910..980164ea10ee 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -815,6 +815,7 @@ static inline void unmap_shared_mapping_range(struct address_space *mapping, } extern void truncate_pagecache(struct inode *inode, loff_t old, loff_t new); +extern void truncate_setsize(struct inode *inode, loff_t newsize); extern int vmtruncate(struct inode *inode, loff_t offset); extern int vmtruncate_range(struct inode *inode, loff_t offset, loff_t end); diff --git a/mm/shmem.c b/mm/shmem.c index 0a43505eeaec..33222ba256fb 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -805,11 +805,10 @@ static int shmem_notify_change(struct dentry *dentry, struct iattr *attr) } } - error = simple_setsize(inode, newsize); + /* XXX(truncate): truncate_setsize should be called last */ + truncate_setsize(inode, newsize); if (page) page_cache_release(page); - if (error) - return error; shmem_truncate_range(inode, newsize, (loff_t)-1); } diff --git a/mm/truncate.c b/mm/truncate.c index 937571b8b233..ba887bff48c5 100644 --- a/mm/truncate.c +++ b/mm/truncate.c @@ -540,29 +540,49 @@ void truncate_pagecache(struct inode *inode, loff_t old, loff_t new) } EXPORT_SYMBOL(truncate_pagecache); +/** + * truncate_setsize - update inode and pagecache for a new file size + * @inode: inode + * @newsize: new file size + * + * truncate_setsize updastes i_size update and performs pagecache + * truncation (if necessary) for a file size updates. It will be + * typically be called from the filesystem's setattr function when + * ATTR_SIZE is passed in. + * + * Must be called with inode_mutex held and after all filesystem + * specific block truncation has been performed. + */ +void truncate_setsize(struct inode *inode, loff_t newsize) +{ + loff_t oldsize; + + oldsize = inode->i_size; + i_size_write(inode, newsize); + + truncate_pagecache(inode, oldsize, newsize); +} +EXPORT_SYMBOL(truncate_setsize); + /** * vmtruncate - unmap mappings "freed" by truncate() syscall * @inode: inode of the file used * @offset: file offset to start truncating * - * NOTE! We have to be ready to update the memory sharing - * between the file and the memory map for a potential last - * incomplete page. Ugly, but necessary. - * - * This function is deprecated and simple_setsize or truncate_pagecache - * should be used instead. + * This function is deprecated and truncate_setsize or truncate_pagecache + * should be used instead, together with filesystem specific block truncation. */ int vmtruncate(struct inode *inode, loff_t offset) { int error; - error = simple_setsize(inode, offset); + error = inode_newsize_ok(inode, offset); if (error) return error; + truncate_setsize(inode, offset); if (inode->i_op->truncate) inode->i_op->truncate(inode); - - return error; + return 0; } EXPORT_SYMBOL(vmtruncate); -- cgit v1.2.3 From 41cce647f8dbe26941bed2158fad0839aab7a294 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 8 Jun 2010 13:24:56 -0400 Subject: jffs2: don't open-code iget_failed() Signed-off-by: Al Viro --- fs/jffs2/dir.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) (limited to 'fs') diff --git a/fs/jffs2/dir.c b/fs/jffs2/dir.c index 166062a68230..5fd3b5cecda5 100644 --- a/fs/jffs2/dir.c +++ b/fs/jffs2/dir.c @@ -232,9 +232,7 @@ static int jffs2_create(struct inode *dir_i, struct dentry *dentry, int mode, return 0; fail: - make_bad_inode(inode); - unlock_new_inode(inode); - iput(inode); + iget_failed(inode); jffs2_free_raw_inode(ri); return ret; } @@ -454,9 +452,7 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char return 0; fail: - make_bad_inode(inode); - unlock_new_inode(inode); - iput(inode); + iget_failed(inode); return ret; } @@ -601,9 +597,7 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode) return 0; fail: - make_bad_inode(inode); - unlock_new_inode(inode); - iput(inode); + iget_failed(inode); return ret; } @@ -778,9 +772,7 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, de return 0; fail: - make_bad_inode(inode); - unlock_new_inode(inode); - iput(inode); + iget_failed(inode); return ret; } -- cgit v1.2.3 From 2f246fd0f126f3b3c23a4e6b7109350e83356bd6 Mon Sep 17 00:00:00 2001 From: Boaz Harrosh Date: Wed, 9 Jun 2010 18:23:18 +0300 Subject: exofs: New truncate sequence These changes are crafted based on the similar conversion done to ext2 by Nick Piggin. * Remove the deprecated ->truncate vector. Let exofs_setattr take care of on-disk size updates. * Call truncate_pagecache on the unused pages if write_begin/end fails. * Cleanup exofs_delete_inode that did stupid inode writes and updates on an inode that will be removed. * And finally get rid of exofs_get_block. We never had any blocks it was all for calling nobh_truncate_page. nobh_truncate_page is not actually needed in exofs since the last page is complete and gone, just like all the other pages. There is no partial blocks in exofs. I've tested with this patch, and there are no apparent failures, so far. CC: Nick Piggin CC: Christoph Hellwig Signed-off-by: Boaz Harrosh Signed-off-by: Al Viro --- fs/exofs/exofs.h | 1 - fs/exofs/file.c | 1 - fs/exofs/inode.c | 115 ++++++++++++++++++++----------------------------------- 3 files changed, 42 insertions(+), 75 deletions(-) (limited to 'fs') diff --git a/fs/exofs/exofs.h b/fs/exofs/exofs.h index 22721b2fd890..0706ce996c84 100644 --- a/fs/exofs/exofs.h +++ b/fs/exofs/exofs.h @@ -256,7 +256,6 @@ static inline int exofs_oi_read(struct exofs_i_info *oi, } /* inode.c */ -void exofs_truncate(struct inode *inode); int exofs_setattr(struct dentry *, struct iattr *); int exofs_write_begin(struct file *file, struct address_space *mapping, loff_t pos, unsigned len, unsigned flags, diff --git a/fs/exofs/file.c b/fs/exofs/file.c index fef6899be397..f9bfe2b501d5 100644 --- a/fs/exofs/file.c +++ b/fs/exofs/file.c @@ -86,6 +86,5 @@ const struct file_operations exofs_file_operations = { }; const struct inode_operations exofs_file_inode_operations = { - .truncate = exofs_truncate, .setattr = exofs_setattr, }; diff --git a/fs/exofs/inode.c b/fs/exofs/inode.c index 4bfc1f4fd013..ccd0ce3eea75 100644 --- a/fs/exofs/inode.c +++ b/fs/exofs/inode.c @@ -697,6 +697,13 @@ static int exofs_writepage(struct page *page, struct writeback_control *wbc) return write_exec(&pcol); } +/* i_mutex held using inode->i_size directly */ +static void _write_failed(struct inode *inode, loff_t to) +{ + if (to > inode->i_size) + truncate_pagecache(inode, to, inode->i_size); +} + int exofs_write_begin(struct file *file, struct address_space *mapping, loff_t pos, unsigned len, unsigned flags, struct page **pagep, void **fsdata) @@ -710,7 +717,7 @@ int exofs_write_begin(struct file *file, struct address_space *mapping, fsdata); if (ret) { EXOFS_DBGMSG("simple_write_begin faild\n"); - return ret; + goto out; } page = *pagep; @@ -725,6 +732,9 @@ int exofs_write_begin(struct file *file, struct address_space *mapping, EXOFS_DBGMSG("__readpage_filler faild\n"); } } +out: + if (unlikely(ret)) + _write_failed(mapping->host, pos + len); return ret; } @@ -750,6 +760,10 @@ static int exofs_write_end(struct file *file, struct address_space *mapping, int ret; ret = simple_write_end(file, mapping,pos, len, copied, page, fsdata); + if (unlikely(ret)) + _write_failed(inode, pos + len); + + /* TODO: once simple_write_end marks inode dirty remove */ if (i_size != inode->i_size) mark_inode_dirty(inode); return ret; @@ -808,91 +822,49 @@ static inline int exofs_inode_is_fast_symlink(struct inode *inode) return S_ISLNK(inode->i_mode) && (oi->i_data[0] != 0); } -/* - * get_block_t - Fill in a buffer_head - * An OSD takes care of block allocation so we just fake an allocation by - * putting in the inode's sector_t in the buffer_head. - * TODO: What about the case of create==0 and @iblock does not exist in the - * object? - */ -static int exofs_get_block(struct inode *inode, sector_t iblock, - struct buffer_head *bh_result, int create) -{ - map_bh(bh_result, inode->i_sb, iblock); - return 0; -} - const struct osd_attr g_attr_logical_length = ATTR_DEF( OSD_APAGE_OBJECT_INFORMATION, OSD_ATTR_OI_LOGICAL_LENGTH, 8); -static int _do_truncate(struct inode *inode) +static int _do_truncate(struct inode *inode, loff_t newsize) { struct exofs_i_info *oi = exofs_i(inode); - loff_t isize = i_size_read(inode); int ret; inode->i_mtime = inode->i_ctime = CURRENT_TIME; - nobh_truncate_page(inode->i_mapping, isize, exofs_get_block); + ret = exofs_oi_truncate(oi, (u64)newsize); + if (likely(!ret)) + truncate_setsize(inode, newsize); - ret = exofs_oi_truncate(oi, (u64)isize); - EXOFS_DBGMSG("(0x%lx) size=0x%llx\n", inode->i_ino, isize); + EXOFS_DBGMSG("(0x%lx) size=0x%llx ret=>%d\n", + inode->i_ino, newsize, ret); return ret; } /* - * Truncate a file to the specified size - all we have to do is set the size - * attribute. We make sure the object exists first. - */ -void exofs_truncate(struct inode *inode) -{ - struct exofs_i_info *oi = exofs_i(inode); - int ret; - - if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) - || S_ISLNK(inode->i_mode))) - return; - if (exofs_inode_is_fast_symlink(inode)) - return; - if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) - return; - - /* if we are about to truncate an object, and it hasn't been - * created yet, wait - */ - if (unlikely(wait_obj_created(oi))) - goto fail; - - ret = _do_truncate(inode); - if (ret) - goto fail; - -out: - mark_inode_dirty(inode); - return; -fail: - make_bad_inode(inode); - goto out; -} - -/* - * Set inode attributes - just call generic functions. + * Set inode attributes - update size attribute on OSD if needed, + * otherwise just call generic functions. */ int exofs_setattr(struct dentry *dentry, struct iattr *iattr) { struct inode *inode = dentry->d_inode; int error; + /* if we are about to modify an object, and it hasn't been + * created yet, wait + */ + error = wait_obj_created(exofs_i(inode)); + if (unlikely(error)) + return error; + error = inode_change_ok(inode, iattr); - if (error) + if (unlikely(error)) return error; if ((iattr->ia_valid & ATTR_SIZE) && iattr->ia_size != i_size_read(inode)) { - int error; - - error = vmtruncate(inode, iattr->ia_size); - if (error) + error = _do_truncate(inode, iattr->ia_size); + if (unlikely(error)) return error; } @@ -1345,28 +1317,25 @@ void exofs_delete_inode(struct inode *inode) truncate_inode_pages(&inode->i_data, 0); + /* TODO: should do better here */ if (is_bad_inode(inode)) goto no_delete; - mark_inode_dirty(inode); - exofs_update_inode(inode, inode_needs_sync(inode)); - inode->i_size = 0; - if (inode->i_blocks) - exofs_truncate(inode); - clear_inode(inode); - ret = exofs_get_io_state(&sbi->layout, &ios); - if (unlikely(ret)) { - EXOFS_ERR("%s: exofs_get_io_state failed\n", __func__); - return; - } - /* if we are deleting an obj that hasn't been created yet, wait */ if (!obj_created(oi)) { BUG_ON(!obj_2bcreated(oi)); wait_event(oi->i_wq, obj_created(oi)); + /* ignore the error attempt a remove anyway */ + } + + /* Now Remove the OSD objects */ + ret = exofs_get_io_state(&sbi->layout, &ios); + if (unlikely(ret)) { + EXOFS_ERR("%s: exofs_get_io_state failed\n", __func__); + return; } ios->obj.id = exofs_oi_objno(oi); -- cgit v1.2.3 From fa9b227e9019ebaeeb06224ba531a490f91144b3 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 14 Jun 2010 05:17:31 -0400 Subject: xfs: new truncate sequence Convert XFS to the new truncate sequence. We still can have errors after updating the file size in xfs_setattr, but these are real I/O errors and lead to a transaction abort and filesystem shutdown, so they are not an issue. Errors from ->write_begin and write_end can now be handled correctly because we can actually get rid of the delalloc extents while previous the buffer state was stipped in block_invalidatepage. There is still no error handling for ->direct_IO, because doing so will need some major restructuring given that we only have the iolock shared and do not hold i_mutex at all. Fortunately leaving the normally allocated blocks behind there is not a major issue and this will get cleaned up by xfs_free_eofblock later. Note: the patch is against Al's vfs.git tree as that contains the nessecary preparations. I'd prefer to get it applied there so that we can get some testing in linux-next. Signed-off-by: Christoph Hellwig Signed-off-by: Al Viro --- fs/xfs/linux-2.6/xfs_aops.c | 42 ++++++++++++++++++++++++++++++++++++------ fs/xfs/linux-2.6/xfs_iops.c | 16 ---------------- fs/xfs/linux-2.6/xfs_linux.h | 2 -- fs/xfs/xfs_vnodeops.c | 38 ++++++++++++++++++++------------------ 4 files changed, 56 insertions(+), 42 deletions(-) (limited to 'fs') diff --git a/fs/xfs/linux-2.6/xfs_aops.c b/fs/xfs/linux-2.6/xfs_aops.c index bf7aad0d78b8..15412fe15c3a 100644 --- a/fs/xfs/linux-2.6/xfs_aops.c +++ b/fs/xfs/linux-2.6/xfs_aops.c @@ -1494,6 +1494,22 @@ xfs_vm_direct_IO( return ret; } +STATIC void +xfs_vm_write_failed( + struct address_space *mapping, + loff_t to) +{ + struct inode *inode = mapping->host; + + if (to > inode->i_size) { + struct iattr ia = { + .ia_valid = ATTR_SIZE | ATTR_FORCE, + .ia_size = inode->i_size, + }; + xfs_setattr(XFS_I(inode), &ia, XFS_ATTR_NOLOCK); + } +} + STATIC int xfs_vm_write_begin( struct file *file, @@ -1508,12 +1524,26 @@ xfs_vm_write_begin( ret = block_write_begin(mapping, pos, len, flags | AOP_FLAG_NOFS, pagep, xfs_get_blocks); - if (unlikely(ret)) { - loff_t isize = mapping->host->i_size; - if (pos + len > isize) - vmtruncate(mapping->host, isize); - } + if (unlikely(ret)) + xfs_vm_write_failed(mapping, pos + len); + return ret; +} + +STATIC int +xfs_vm_write_end( + struct file *file, + struct address_space *mapping, + loff_t pos, + unsigned len, + unsigned copied, + struct page *page, + void *fsdata) +{ + int ret; + ret = generic_write_end(file, mapping, pos, len, copied, page, fsdata); + if (unlikely(ret < len)) + xfs_vm_write_failed(mapping, pos + len); return ret; } @@ -1559,7 +1589,7 @@ const struct address_space_operations xfs_address_space_operations = { .releasepage = xfs_vm_releasepage, .invalidatepage = xfs_vm_invalidatepage, .write_begin = xfs_vm_write_begin, - .write_end = generic_write_end, + .write_end = xfs_vm_write_end, .bmap = xfs_vm_bmap, .direct_IO = xfs_vm_direct_IO, .migratepage = buffer_migrate_page, diff --git a/fs/xfs/linux-2.6/xfs_iops.c b/fs/xfs/linux-2.6/xfs_iops.c index 536b81e63a3d..62dd349facee 100644 --- a/fs/xfs/linux-2.6/xfs_iops.c +++ b/fs/xfs/linux-2.6/xfs_iops.c @@ -540,21 +540,6 @@ xfs_vn_setattr( return -xfs_setattr(XFS_I(dentry->d_inode), iattr, 0); } -/* - * block_truncate_page can return an error, but we can't propagate it - * at all here. Leave a complaint + stack trace in the syslog because - * this could be bad. If it is bad, we need to propagate the error further. - */ -STATIC void -xfs_vn_truncate( - struct inode *inode) -{ - int error; - error = block_truncate_page(inode->i_mapping, inode->i_size, - xfs_get_blocks); - WARN_ON(error); -} - STATIC long xfs_vn_fallocate( struct inode *inode, @@ -694,7 +679,6 @@ xfs_vn_fiemap( static const struct inode_operations xfs_inode_operations = { .check_acl = xfs_check_acl, - .truncate = xfs_vn_truncate, .getattr = xfs_vn_getattr, .setattr = xfs_vn_setattr, .setxattr = generic_setxattr, diff --git a/fs/xfs/linux-2.6/xfs_linux.h b/fs/xfs/linux-2.6/xfs_linux.h index 998a9d7fb9c8..2fa0bd9ebc7f 100644 --- a/fs/xfs/linux-2.6/xfs_linux.h +++ b/fs/xfs/linux-2.6/xfs_linux.h @@ -156,8 +156,6 @@ */ #define xfs_sort(a,n,s,fn) sort(a,n,s,fn,NULL) #define xfs_stack_trace() dump_stack() -#define xfs_itruncate_data(ip, off) \ - (-vmtruncate(VFS_I(ip), (off))) /* Move the kernel do_div definition off to one side */ diff --git a/fs/xfs/xfs_vnodeops.c b/fs/xfs/xfs_vnodeops.c index 3ac137dd531b..66d585c6917c 100644 --- a/fs/xfs/xfs_vnodeops.c +++ b/fs/xfs/xfs_vnodeops.c @@ -221,8 +221,11 @@ xfs_setattr( * transaction to modify the i_size. */ code = xfs_zero_eof(ip, iattr->ia_size, ip->i_size); + if (code) + goto error_return; } xfs_iunlock(ip, XFS_ILOCK_EXCL); + lock_flags &= ~XFS_ILOCK_EXCL; /* * We are going to log the inode size change in this @@ -236,36 +239,35 @@ xfs_setattr( * really care about here and prevents waiting for other data * not within the range we care about here. */ - if (!code && - ip->i_size != ip->i_d.di_size && + if (ip->i_size != ip->i_d.di_size && iattr->ia_size > ip->i_d.di_size) { code = xfs_flush_pages(ip, ip->i_d.di_size, iattr->ia_size, XBF_ASYNC, FI_NONE); + if (code) + goto error_return; } /* wait for all I/O to complete */ xfs_ioend_wait(ip); - if (!code) - code = xfs_itruncate_data(ip, iattr->ia_size); - if (code) { - ASSERT(tp == NULL); - lock_flags &= ~XFS_ILOCK_EXCL; - ASSERT(lock_flags == XFS_IOLOCK_EXCL || !need_iolock); + code = -block_truncate_page(inode->i_mapping, iattr->ia_size, + xfs_get_blocks); + if (code) goto error_return; - } + tp = xfs_trans_alloc(mp, XFS_TRANS_SETATTR_SIZE); - if ((code = xfs_trans_reserve(tp, 0, - XFS_ITRUNCATE_LOG_RES(mp), 0, - XFS_TRANS_PERM_LOG_RES, - XFS_ITRUNCATE_LOG_COUNT))) { - xfs_trans_cancel(tp, 0); - if (need_iolock) - xfs_iunlock(ip, XFS_IOLOCK_EXCL); - return code; - } + code = xfs_trans_reserve(tp, 0, XFS_ITRUNCATE_LOG_RES(mp), 0, + XFS_TRANS_PERM_LOG_RES, + XFS_ITRUNCATE_LOG_COUNT); + if (code) + goto error_return; + + truncate_setsize(inode, iattr->ia_size); + commit_flags = XFS_TRANS_RELEASE_LOG_RES; + lock_flags |= XFS_ILOCK_EXCL; + xfs_ilock(ip, XFS_ILOCK_EXCL); xfs_trans_ijoin(tp, ip); -- cgit v1.2.3 From b5fc510c48f631882ccec3c0f02a25d5b67de09f Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 4 Jul 2010 12:24:09 +0400 Subject: get rid of file_fsync() Copy and simplify in the only two users remaining. Signed-off-by: Al Viro --- fs/hfs/inode.c | 26 +++++++++++++++++++++++++- fs/hfsplus/hfsplus_fs.h | 1 + fs/hfsplus/inode.c | 27 ++++++++++++++++++++++++++- fs/hfsplus/super.c | 2 +- fs/sync.c | 25 ------------------------- include/linux/buffer_head.h | 1 - 6 files changed, 53 insertions(+), 29 deletions(-) (limited to 'fs') diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c index 87de671baa83..93ceec8fbb8f 100644 --- a/fs/hfs/inode.c +++ b/fs/hfs/inode.c @@ -625,6 +625,30 @@ int hfs_inode_setattr(struct dentry *dentry, struct iattr * attr) return 0; } +static int hfs_file_fsync(struct file *filp, int datasync) +{ + struct inode *inode = filp->f_mapping->host; + struct super_block * sb; + int ret, err; + + /* sync the inode to buffers */ + ret = write_inode_now(inode, 0); + + /* sync the superblock to buffers */ + sb = inode->i_sb; + if (sb->s_dirt) { + lock_super(sb); + sb->s_dirt = 0; + if (!(sb->s_flags & MS_RDONLY)) + hfs_mdb_commit(sb); + unlock_super(sb); + } + /* .. finally sync the buffers to disk */ + err = sync_blockdev(sb->s_bdev); + if (!ret) + ret = err; + return ret; +} static const struct file_operations hfs_file_operations = { .llseek = generic_file_llseek, @@ -634,7 +658,7 @@ static const struct file_operations hfs_file_operations = { .aio_write = generic_file_aio_write, .mmap = generic_file_mmap, .splice_read = generic_file_splice_read, - .fsync = file_fsync, + .fsync = hfs_file_fsync, .open = hfs_file_open, .release = hfs_file_release, }; diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h index 6505c30ad965..dc856be3c2b0 100644 --- a/fs/hfsplus/hfsplus_fs.h +++ b/fs/hfsplus/hfsplus_fs.h @@ -351,6 +351,7 @@ int hfsplus_show_options(struct seq_file *, struct vfsmount *); /* super.c */ struct inode *hfsplus_iget(struct super_block *, unsigned long); +int hfsplus_sync_fs(struct super_block *sb, int wait); /* tables.c */ extern u16 hfsplus_case_fold_table[]; diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c index 654c5a8ddf1c..c5a979d62c65 100644 --- a/fs/hfsplus/inode.c +++ b/fs/hfsplus/inode.c @@ -311,6 +311,31 @@ static int hfsplus_setattr(struct dentry *dentry, struct iattr *attr) return 0; } +static int hfsplus_file_fsync(struct file *filp, int datasync) +{ + struct inode *inode = filp->f_mapping->host; + struct super_block * sb; + int ret, err; + + /* sync the inode to buffers */ + ret = write_inode_now(inode, 0); + + /* sync the superblock to buffers */ + sb = inode->i_sb; + if (sb->s_dirt) { + if (!(sb->s_flags & MS_RDONLY)) + hfsplus_sync_fs(sb, 1); + else + sb->s_dirt = 0; + } + + /* .. finally sync the buffers to disk */ + err = sync_blockdev(sb->s_bdev); + if (!ret) + ret = err; + return ret; +} + static const struct inode_operations hfsplus_file_inode_operations = { .lookup = hfsplus_file_lookup, .truncate = hfsplus_file_truncate, @@ -328,7 +353,7 @@ static const struct file_operations hfsplus_file_operations = { .aio_write = generic_file_aio_write, .mmap = generic_file_mmap, .splice_read = generic_file_splice_read, - .fsync = file_fsync, + .fsync = hfsplus_file_fsync, .open = hfsplus_file_open, .release = hfsplus_file_release, .unlocked_ioctl = hfsplus_ioctl, diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c index 74b473a8ef92..a32c241e4e45 100644 --- a/fs/hfsplus/super.c +++ b/fs/hfsplus/super.c @@ -154,7 +154,7 @@ static void hfsplus_clear_inode(struct inode *inode) } } -static int hfsplus_sync_fs(struct super_block *sb, int wait) +int hfsplus_sync_fs(struct super_block *sb, int wait) { struct hfsplus_vh *vhdr = HFSPLUS_SB(sb).s_vhdr; diff --git a/fs/sync.c b/fs/sync.c index 15aa6f03b2da..ba76b9623e7e 100644 --- a/fs/sync.c +++ b/fs/sync.c @@ -128,31 +128,6 @@ void emergency_sync(void) } } -/* - * Generic function to fsync a file. - */ -int file_fsync(struct file *filp, int datasync) -{ - struct inode *inode = filp->f_mapping->host; - struct super_block * sb; - int ret, err; - - /* sync the inode to buffers */ - ret = write_inode_now(inode, 0); - - /* sync the superblock to buffers */ - sb = inode->i_sb; - if (sb->s_dirt && sb->s_op->write_super) - sb->s_op->write_super(sb); - - /* .. finally sync the buffers to disk */ - err = sync_blockdev(sb->s_bdev); - if (!ret) - ret = err; - return ret; -} -EXPORT_SYMBOL(file_fsync); - /** * vfs_fsync_range - helper to sync a range of data & metadata to disk * @file: file to sync diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h index 3f69054f86d9..620f1d1088cb 100644 --- a/include/linux/buffer_head.h +++ b/include/linux/buffer_head.h @@ -225,7 +225,6 @@ int block_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf, void block_sync_page(struct page *); sector_t generic_block_bmap(struct address_space *, sector_t, get_block_t *); int block_truncate_page(struct address_space *, loff_t, get_block_t *); -int file_fsync(struct file *, int); int nobh_write_begin(struct address_space *, loff_t, unsigned, unsigned, struct page **, void **, get_block_t*); int nobh_write_end(struct file *, struct address_space *, -- cgit v1.2.3 From a4ffdde6e56fdf8c34ddadc2674d6eb978083369 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 2 Jun 2010 17:38:30 -0400 Subject: simplify checks for I_CLEAR/I_FREEING add I_CLEAR instead of replacing I_FREEING with it. I_CLEAR is equivalent to I_FREEING for almost all code looking at either; it's there to keep track of having called clear_inode() exactly once per inode lifetime, at some point after having set I_FREEING. I_CLEAR and I_FREEING never get set at the same time with the current code, so we can switch to setting i_flags to I_FREEING | I_CLEAR instead of I_CLEAR without loss of information. As the result of such change, checks become simpler and the amount of code that needs to know about I_CLEAR shrinks a lot. Signed-off-by: Al Viro --- fs/btrfs/inode.c | 2 +- fs/drop_caches.c | 2 +- fs/fs-writeback.c | 8 ++++---- fs/gfs2/inode.c | 2 +- fs/inode.c | 16 ++++++++-------- fs/nilfs2/gcdat.c | 2 +- fs/notify/inode_mark.c | 6 +++--- fs/notify/inotify/inotify.c | 7 +++---- fs/quota/dquot.c | 2 +- fs/xfs/linux-2.6/xfs_iops.c | 4 ++-- include/linux/fs.h | 4 ++-- 11 files changed, 27 insertions(+), 28 deletions(-) (limited to 'fs') diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 7f9e0536db1a..95eac0116963 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -3860,7 +3860,7 @@ again: p = &parent->rb_right; else { WARN_ON(!(entry->vfs_inode.i_state & - (I_WILL_FREE | I_FREEING | I_CLEAR))); + (I_WILL_FREE | I_FREEING))); rb_erase(parent, &root->inode_tree); RB_CLEAR_NODE(parent); spin_unlock(&root->inode_lock); diff --git a/fs/drop_caches.c b/fs/drop_caches.c index 83c4f600786a..2195c213ab2f 100644 --- a/fs/drop_caches.c +++ b/fs/drop_caches.c @@ -18,7 +18,7 @@ static void drop_pagecache_sb(struct super_block *sb, void *unused) spin_lock(&inode_lock); list_for_each_entry(inode, &sb->s_inodes, i_sb_list) { - if (inode->i_state & (I_FREEING|I_CLEAR|I_WILL_FREE|I_NEW)) + if (inode->i_state & (I_FREEING|I_WILL_FREE|I_NEW)) continue; if (inode->i_mapping->nrpages == 0) continue; diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index d5be1693ac93..7608880b5c58 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -352,7 +352,7 @@ writeback_single_inode(struct inode *inode, struct writeback_control *wbc) spin_lock(&inode_lock); inode->i_state &= ~I_SYNC; - if (!(inode->i_state & (I_FREEING | I_CLEAR))) { + if (!(inode->i_state & I_FREEING)) { if ((inode->i_state & I_DIRTY_PAGES) && wbc->for_kupdate) { /* * More pages get dirtied by a fast dirtier. @@ -499,7 +499,7 @@ static int writeback_sb_inodes(struct super_block *sb, struct bdi_writeback *wb, if (inode_dirtied_after(inode, wbc->wb_start)) return 1; - BUG_ON(inode->i_state & (I_FREEING | I_CLEAR)); + BUG_ON(inode->i_state & I_FREEING); __iget(inode); pages_skipped = wbc->pages_skipped; writeback_single_inode(inode, wbc); @@ -935,7 +935,7 @@ void __mark_inode_dirty(struct inode *inode, int flags) if (hlist_unhashed(&inode->i_hash)) goto out; } - if (inode->i_state & (I_FREEING|I_CLEAR)) + if (inode->i_state & I_FREEING) goto out; /* @@ -1001,7 +1001,7 @@ static void wait_sb_inodes(struct super_block *sb) list_for_each_entry(inode, &sb->s_inodes, i_sb_list) { struct address_space *mapping; - if (inode->i_state & (I_FREEING|I_CLEAR|I_WILL_FREE|I_NEW)) + if (inode->i_state & (I_FREEING|I_WILL_FREE|I_NEW)) continue; mapping = inode->i_mapping; if (mapping->nrpages == 0) diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c index 6c023a3b5d25..08140f185a37 100644 --- a/fs/gfs2/inode.c +++ b/fs/gfs2/inode.c @@ -84,7 +84,7 @@ static int iget_skip_test(struct inode *inode, void *opaque) struct gfs2_skip_data *data = opaque; if (ip->i_no_addr == data->no_addr) { - if (inode->i_state & (I_FREEING|I_CLEAR|I_WILL_FREE)){ + if (inode->i_state & (I_FREEING|I_WILL_FREE)){ data->skipped = 1; return 0; } diff --git a/fs/inode.c b/fs/inode.c index 722860b323a9..71fe079ca32a 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -317,7 +317,7 @@ void clear_inode(struct inode *inode) bd_forget(inode); if (S_ISCHR(inode->i_mode) && inode->i_cdev) cd_forget(inode); - inode->i_state = I_CLEAR; + inode->i_state = I_FREEING | I_CLEAR; } EXPORT_SYMBOL(clear_inode); @@ -553,7 +553,7 @@ repeat: continue; if (!test(inode, data)) continue; - if (inode->i_state & (I_FREEING|I_CLEAR|I_WILL_FREE)) { + if (inode->i_state & (I_FREEING|I_WILL_FREE)) { __wait_on_freeing_inode(inode); goto repeat; } @@ -578,7 +578,7 @@ repeat: continue; if (inode->i_sb != sb) continue; - if (inode->i_state & (I_FREEING|I_CLEAR|I_WILL_FREE)) { + if (inode->i_state & (I_FREEING|I_WILL_FREE)) { __wait_on_freeing_inode(inode); goto repeat; } @@ -840,7 +840,7 @@ EXPORT_SYMBOL(iunique); struct inode *igrab(struct inode *inode) { spin_lock(&inode_lock); - if (!(inode->i_state & (I_FREEING|I_CLEAR|I_WILL_FREE))) + if (!(inode->i_state & (I_FREEING|I_WILL_FREE))) __iget(inode); else /* @@ -1089,7 +1089,7 @@ int insert_inode_locked(struct inode *inode) continue; if (old->i_sb != sb) continue; - if (old->i_state & (I_FREEING|I_CLEAR|I_WILL_FREE)) + if (old->i_state & (I_FREEING|I_WILL_FREE)) continue; break; } @@ -1128,7 +1128,7 @@ int insert_inode_locked4(struct inode *inode, unsigned long hashval, continue; if (!test(old, data)) continue; - if (old->i_state & (I_FREEING|I_CLEAR|I_WILL_FREE)) + if (old->i_state & (I_FREEING|I_WILL_FREE)) continue; break; } @@ -1218,7 +1218,7 @@ void generic_delete_inode(struct inode *inode) hlist_del_init(&inode->i_hash); spin_unlock(&inode_lock); wake_up_inode(inode); - BUG_ON(inode->i_state != I_CLEAR); + BUG_ON(inode->i_state != (I_FREEING | I_CLEAR)); destroy_inode(inode); } EXPORT_SYMBOL(generic_delete_inode); @@ -1322,7 +1322,7 @@ static inline void iput_final(struct inode *inode) void iput(struct inode *inode) { if (inode) { - BUG_ON(inode->i_state == I_CLEAR); + BUG_ON(inode->i_state & I_CLEAR); if (atomic_dec_and_lock(&inode->i_count, &inode_lock)) iput_final(inode); diff --git a/fs/nilfs2/gcdat.c b/fs/nilfs2/gcdat.c index dd5f7e0a95f6..84a45d1d5464 100644 --- a/fs/nilfs2/gcdat.c +++ b/fs/nilfs2/gcdat.c @@ -78,7 +78,7 @@ void nilfs_clear_gcdat_inode(struct the_nilfs *nilfs) struct inode *gcdat = nilfs->ns_gc_dat; struct nilfs_inode_info *gii = NILFS_I(gcdat); - gcdat->i_state = I_CLEAR; + gcdat->i_state = I_FREEING | I_CLEAR; gii->i_flags = 0; nilfs_palloc_clear_cache(gcdat); diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c index 0399bcbe09c8..152b83ec005d 100644 --- a/fs/notify/inode_mark.c +++ b/fs/notify/inode_mark.c @@ -369,11 +369,11 @@ void fsnotify_unmount_inodes(struct list_head *list) struct inode *need_iput_tmp; /* - * We cannot __iget() an inode in state I_CLEAR, I_FREEING, + * We cannot __iget() an inode in state I_FREEING, * I_WILL_FREE, or I_NEW which is fine because by that point * the inode cannot have any associated watches. */ - if (inode->i_state & (I_CLEAR|I_FREEING|I_WILL_FREE|I_NEW)) + if (inode->i_state & (I_FREEING|I_WILL_FREE|I_NEW)) continue; /* @@ -397,7 +397,7 @@ void fsnotify_unmount_inodes(struct list_head *list) /* In case the dropping of a reference would nuke next_i. */ if ((&next_i->i_sb_list != list) && atomic_read(&next_i->i_count) && - !(next_i->i_state & (I_CLEAR | I_FREEING | I_WILL_FREE))) { + !(next_i->i_state & (I_FREEING | I_WILL_FREE))) { __iget(next_i); need_iput = next_i; } diff --git a/fs/notify/inotify/inotify.c b/fs/notify/inotify/inotify.c index 27b75ebc7460..cf6b0429a257 100644 --- a/fs/notify/inotify/inotify.c +++ b/fs/notify/inotify/inotify.c @@ -377,11 +377,11 @@ void inotify_unmount_inodes(struct list_head *list) struct list_head *watches; /* - * We cannot __iget() an inode in state I_CLEAR, I_FREEING, + * We cannot __iget() an inode in state I_FREEING, * I_WILL_FREE, or I_NEW which is fine because by that point * the inode cannot have any associated watches. */ - if (inode->i_state & (I_CLEAR|I_FREEING|I_WILL_FREE|I_NEW)) + if (inode->i_state & (I_FREEING|I_WILL_FREE|I_NEW)) continue; /* @@ -403,8 +403,7 @@ void inotify_unmount_inodes(struct list_head *list) /* In case the dropping of a reference would nuke next_i. */ if ((&next_i->i_sb_list != list) && atomic_read(&next_i->i_count) && - !(next_i->i_state & (I_CLEAR | I_FREEING | - I_WILL_FREE))) { + !(next_i->i_state & (I_FREEING|I_WILL_FREE))) { __iget(next_i); need_iput = next_i; } diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c index 437d2ca2de97..5cec3e2348f1 100644 --- a/fs/quota/dquot.c +++ b/fs/quota/dquot.c @@ -885,7 +885,7 @@ static void add_dquot_ref(struct super_block *sb, int type) spin_lock(&inode_lock); list_for_each_entry(inode, &sb->s_inodes, i_sb_list) { - if (inode->i_state & (I_FREEING|I_CLEAR|I_WILL_FREE|I_NEW)) + if (inode->i_state & (I_FREEING|I_WILL_FREE|I_NEW)) continue; #ifdef CONFIG_QUOTA_DEBUG if (unlikely(inode_get_rsv_space(inode) > 0)) diff --git a/fs/xfs/linux-2.6/xfs_iops.c b/fs/xfs/linux-2.6/xfs_iops.c index 62dd349facee..68be25dcd301 100644 --- a/fs/xfs/linux-2.6/xfs_iops.c +++ b/fs/xfs/linux-2.6/xfs_iops.c @@ -80,7 +80,7 @@ xfs_mark_inode_dirty_sync( { struct inode *inode = VFS_I(ip); - if (!(inode->i_state & (I_WILL_FREE|I_FREEING|I_CLEAR))) + if (!(inode->i_state & (I_WILL_FREE|I_FREEING))) mark_inode_dirty_sync(inode); } @@ -90,7 +90,7 @@ xfs_mark_inode_dirty( { struct inode *inode = VFS_I(ip); - if (!(inode->i_state & (I_WILL_FREE|I_FREEING|I_CLEAR))) + if (!(inode->i_state & (I_WILL_FREE|I_FREEING))) mark_inode_dirty(inode); } diff --git a/include/linux/fs.h b/include/linux/fs.h index 5547b1b027db..218693d8d446 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1616,8 +1616,8 @@ struct super_operations { * I_FREEING Set when inode is about to be freed but still has dirty * pages or buffers attached or the inode itself is still * dirty. - * I_CLEAR Set by clear_inode(). In this state the inode is clean - * and can be destroyed. + * I_CLEAR Added by clear_inode(). In this state the inode is clean + * and can be destroyed. Inode keeps I_FREEING. * * Inodes that are I_WILL_FREE, I_FREEING or I_CLEAR are * prohibited for many purposes. iget() must wait for -- cgit v1.2.3 From b4272d4c810532e1a4dea111433a0af56d3bd2b7 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 4 Jun 2010 19:33:20 -0400 Subject: unify fs/inode.c callers of clear_inode() For now, just a straightforward merge Signed-off-by: Al Viro --- fs/inode.c | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) (limited to 'fs') diff --git a/fs/inode.c b/fs/inode.c index 71fe079ca32a..60cb25969762 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -321,6 +321,19 @@ void clear_inode(struct inode *inode) } EXPORT_SYMBOL(clear_inode); +static void evict(struct inode *inode, int delete) +{ + const struct super_operations *op = inode->i_sb->s_op; + + if (delete && op->delete_inode) { + op->delete_inode(inode); + } else { + if (inode->i_data.nrpages) + truncate_inode_pages(&inode->i_data, 0); + clear_inode(inode); + } +} + /* * dispose_list - dispose of the contents of a local list * @head: the head of the list to free @@ -338,9 +351,7 @@ static void dispose_list(struct list_head *head) inode = list_first_entry(head, struct inode, i_list); list_del(&inode->i_list); - if (inode->i_data.nrpages) - truncate_inode_pages(&inode->i_data, 0); - clear_inode(inode); + evict(inode, 0); spin_lock(&inode_lock); hlist_del_init(&inode->i_hash); @@ -1194,8 +1205,6 @@ EXPORT_SYMBOL(remove_inode_hash); */ void generic_delete_inode(struct inode *inode) { - const struct super_operations *op = inode->i_sb->s_op; - list_del_init(&inode->i_list); list_del_init(&inode->i_sb_list); WARN_ON(inode->i_state & I_NEW); @@ -1203,17 +1212,8 @@ void generic_delete_inode(struct inode *inode) inodes_stat.nr_inodes--; spin_unlock(&inode_lock); - if (op->delete_inode) { - void (*delete)(struct inode *) = op->delete_inode; - /* Filesystems implementing their own - * s_op->delete_inode are required to call - * truncate_inode_pages and clear_inode() - * internally */ - delete(inode); - } else { - truncate_inode_pages(&inode->i_data, 0); - clear_inode(inode); - } + evict(inode, 1); + spin_lock(&inode_lock); hlist_del_init(&inode->i_hash); spin_unlock(&inode_lock); @@ -1268,9 +1268,7 @@ static void generic_forget_inode(struct inode *inode) { if (!generic_detach_inode(inode)) return; - if (inode->i_data.nrpages) - truncate_inode_pages(&inode->i_data, 0); - clear_inode(inode); + evict(inode, 0); wake_up_inode(inode); destroy_inode(inode); } -- cgit v1.2.3 From be7ce4161f9e6bf2497f90337d1214aa6ee06e15 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 4 Jun 2010 19:40:39 -0400 Subject: New method - evict_inode() Hybrid of ->clear_inode() and ->delete_inode(); if present, does all fs work to be done when in-core inode is about to be gone, for whatever reason. Signed-off-by: Al Viro --- fs/inode.c | 4 +++- include/linux/fs.h | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/inode.c b/fs/inode.c index 60cb25969762..474a72f571a4 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -325,7 +325,9 @@ static void evict(struct inode *inode, int delete) { const struct super_operations *op = inode->i_sb->s_op; - if (delete && op->delete_inode) { + if (op->evict_inode) { + op->evict_inode(inode); + } else if (delete && op->delete_inode) { op->delete_inode(inode); } else { if (inode->i_data.nrpages) diff --git a/include/linux/fs.h b/include/linux/fs.h index 218693d8d446..ce50be4b0b41 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1563,6 +1563,7 @@ struct super_operations { void (*dirty_inode) (struct inode *); int (*write_inode) (struct inode *, struct writeback_control *wbc); void (*drop_inode) (struct inode *); + void (*evict_inode) (struct inode *); void (*delete_inode) (struct inode *); void (*put_super) (struct super_block *); void (*write_super) (struct super_block *); -- cgit v1.2.3 From 2bbbda308f5ca027d4fd721f914c0cab88d49aec Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 4 Jun 2010 19:52:12 -0400 Subject: switch hugetlbfs to ->evict_inode() The first spoils - hugetlb can use default ->drop_inode() now. Signed-off-by: Al Viro --- fs/hugetlbfs/inode.c | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) (limited to 'fs') diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c index d5f019d48b09..bf1a2f400e70 100644 --- a/fs/hugetlbfs/inode.c +++ b/fs/hugetlbfs/inode.c @@ -371,29 +371,12 @@ static void truncate_hugepages(struct inode *inode, loff_t lstart) hugetlb_unreserve_pages(inode, start, freed); } -static void hugetlbfs_delete_inode(struct inode *inode) +static void hugetlbfs_evict_inode(struct inode *inode) { truncate_hugepages(inode, 0); clear_inode(inode); } -static void hugetlbfs_forget_inode(struct inode *inode) __releases(inode_lock) -{ - if (generic_detach_inode(inode)) { - truncate_hugepages(inode, 0); - clear_inode(inode); - destroy_inode(inode); - } -} - -static void hugetlbfs_drop_inode(struct inode *inode) -{ - if (!inode->i_nlink) - generic_delete_inode(inode); - else - hugetlbfs_forget_inode(inode); -} - static inline void hugetlb_vmtruncate_list(struct prio_tree_root *root, pgoff_t pgoff) { @@ -713,9 +696,8 @@ static const struct inode_operations hugetlbfs_inode_operations = { static const struct super_operations hugetlbfs_ops = { .alloc_inode = hugetlbfs_alloc_inode, .destroy_inode = hugetlbfs_destroy_inode, + .evict_inode = hugetlbfs_evict_inode, .statfs = hugetlbfs_statfs, - .delete_inode = hugetlbfs_delete_inode, - .drop_inode = hugetlbfs_drop_inode, .put_super = hugetlbfs_put_super, .show_options = generic_show_options, }; -- cgit v1.2.3 From c6287315cb958e740466555ca5e9d007f25b39bd Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 4 Jun 2010 19:56:17 -0400 Subject: generic_detach_inode() can be static now Signed-off-by: Al Viro --- fs/inode.c | 3 +-- include/linux/fs.h | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/inode.c b/fs/inode.c index 474a72f571a4..256e620c6416 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -1234,7 +1234,7 @@ EXPORT_SYMBOL(generic_delete_inode); * * Returns 1 if inode should be completely destroyed. */ -int generic_detach_inode(struct inode *inode) +static int generic_detach_inode(struct inode *inode) { struct super_block *sb = inode->i_sb; @@ -1264,7 +1264,6 @@ int generic_detach_inode(struct inode *inode) spin_unlock(&inode_lock); return 1; } -EXPORT_SYMBOL_GPL(generic_detach_inode); static void generic_forget_inode(struct inode *inode) { diff --git a/include/linux/fs.h b/include/linux/fs.h index ce50be4b0b41..e0ecb8e75ebf 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2167,7 +2167,6 @@ extern ino_t iunique(struct super_block *, ino_t); extern int inode_needs_sync(struct inode *inode); extern void generic_delete_inode(struct inode *inode); extern void generic_drop_inode(struct inode *inode); -extern int generic_detach_inode(struct inode *inode); extern struct inode *ilookup5_nowait(struct super_block *sb, unsigned long hashval, int (*test)(struct inode *, void *), -- cgit v1.2.3 From 661074e91b1da1ee262dfde6dd836deacccb9def Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 4 Jun 2010 20:19:55 -0400 Subject: Take ->i_bdev/->i_cdev handling out of clear_inode() All call chains to clear_inode() pass through evict_inode() and clear_inode() should be called by evict_inode() exactly once. So we can pull i_bdev/i_cdev detaching up to evict_inode() itself. Signed-off-by: Al Viro --- fs/inode.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/inode.c b/fs/inode.c index 256e620c6416..9aff7deaf816 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -313,10 +313,6 @@ void clear_inode(struct inode *inode) inode_sync_wait(inode); if (inode->i_sb->s_op->clear_inode) inode->i_sb->s_op->clear_inode(inode); - if (S_ISBLK(inode->i_mode) && inode->i_bdev) - bd_forget(inode); - if (S_ISCHR(inode->i_mode) && inode->i_cdev) - cd_forget(inode); inode->i_state = I_FREEING | I_CLEAR; } EXPORT_SYMBOL(clear_inode); @@ -334,6 +330,10 @@ static void evict(struct inode *inode, int delete) truncate_inode_pages(&inode->i_data, 0); clear_inode(inode); } + if (S_ISBLK(inode->i_mode) && inode->i_bdev) + bd_forget(inode); + if (S_ISCHR(inode->i_mode) && inode->i_cdev) + cd_forget(inode); } /* -- cgit v1.2.3 From b0683aa638b3326c6fc22e5290dfa75e08bd83f5 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 4 Jun 2010 20:55:25 -0400 Subject: new helper: end_writeback() Essentially, the minimal variant of ->evict_inode(). It's a trimmed-down clear_inode(), sans any fs callbacks. Once it returns we know that no async writeback will be happening; every ->evict_inode() instance should do that once and do that before doing anything ->write_inode() could interfere with (e.g. freeing the on-disk inode). Signed-off-by: Al Viro --- fs/hugetlbfs/inode.c | 2 +- fs/inode.c | 12 ++++++++++++ include/linux/fs.h | 1 + 3 files changed, 14 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c index bf1a2f400e70..6e5bd42f3860 100644 --- a/fs/hugetlbfs/inode.c +++ b/fs/hugetlbfs/inode.c @@ -374,7 +374,7 @@ static void truncate_hugepages(struct inode *inode, loff_t lstart) static void hugetlbfs_evict_inode(struct inode *inode) { truncate_hugepages(inode, 0); - clear_inode(inode); + end_writeback(inode); } static inline void diff --git a/fs/inode.c b/fs/inode.c index 9aff7deaf816..93e7a5ecbc26 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -294,6 +294,18 @@ void __iget(struct inode *inode) inodes_stat.nr_unused--; } +void end_writeback(struct inode *inode) +{ + might_sleep(); + BUG_ON(inode->i_data.nrpages); + BUG_ON(!list_empty(&inode->i_data.private_list)); + BUG_ON(!(inode->i_state & I_FREEING)); + BUG_ON(inode->i_state & I_CLEAR); + inode_sync_wait(inode); + inode->i_state = I_FREEING | I_CLEAR; +} +EXPORT_SYMBOL(end_writeback); + /** * clear_inode - clear an inode * @inode: inode to clear diff --git a/include/linux/fs.h b/include/linux/fs.h index e0ecb8e75ebf..3c23c1dcb1bd 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2184,6 +2184,7 @@ extern void unlock_new_inode(struct inode *); extern void __iget(struct inode * inode); extern void iget_failed(struct inode *); extern void clear_inode(struct inode *); +extern void end_writeback(struct inode *); extern void destroy_inode(struct inode *); extern void __destroy_inode(struct inode *); extern struct inode *new_inode(struct super_block *); -- cgit v1.2.3 From 77b8a75f5bb461951148a7211ef30eecac5cb662 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 4 Jun 2010 21:19:01 -0400 Subject: simplify get_cramfs_inode() simply don't hash the inodes that don't have real inumber instead of skipping them during iget5_locked(); as the result, simple iget_locked() would do and we can get rid of cramfs ->drop_inode() as well. Signed-off-by: Al Viro --- fs/cramfs/inode.c | 88 ++++++++++++++++++++++++------------------------------- 1 file changed, 38 insertions(+), 50 deletions(-) (limited to 'fs') diff --git a/fs/cramfs/inode.c b/fs/cramfs/inode.c index dd3634e4c967..a53b130b366c 100644 --- a/fs/cramfs/inode.c +++ b/fs/cramfs/inode.c @@ -39,66 +39,55 @@ static DEFINE_MUTEX(read_mutex); #define CRAMINO(x) (((x)->offset && (x)->size)?(x)->offset<<2:1) #define OFFSET(x) ((x)->i_ino) - -static int cramfs_iget5_test(struct inode *inode, void *opaque) -{ - struct cramfs_inode *cramfs_inode = opaque; - return inode->i_ino == CRAMINO(cramfs_inode) && inode->i_ino != 1; -} - -static int cramfs_iget5_set(struct inode *inode, void *opaque) +static void setup_inode(struct inode *inode, struct cramfs_inode * cramfs_inode) { - struct cramfs_inode *cramfs_inode = opaque; - inode->i_ino = CRAMINO(cramfs_inode); - return 0; + static struct timespec zerotime; + inode->i_mode = cramfs_inode->mode; + inode->i_uid = cramfs_inode->uid; + inode->i_size = cramfs_inode->size; + inode->i_blocks = (cramfs_inode->size - 1) / 512 + 1; + inode->i_gid = cramfs_inode->gid; + /* Struct copy intentional */ + inode->i_mtime = inode->i_atime = inode->i_ctime = zerotime; + /* inode->i_nlink is left 1 - arguably wrong for directories, + but it's the best we can do without reading the directory + contents. 1 yields the right result in GNU find, even + without -noleaf option. */ + if (S_ISREG(inode->i_mode)) { + inode->i_fop = &generic_ro_fops; + inode->i_data.a_ops = &cramfs_aops; + } else if (S_ISDIR(inode->i_mode)) { + inode->i_op = &cramfs_dir_inode_operations; + inode->i_fop = &cramfs_directory_operations; + } else if (S_ISLNK(inode->i_mode)) { + inode->i_op = &page_symlink_inode_operations; + inode->i_data.a_ops = &cramfs_aops; + } else { + init_special_inode(inode, inode->i_mode, + old_decode_dev(cramfs_inode->size)); + } } static struct inode *get_cramfs_inode(struct super_block *sb, struct cramfs_inode * cramfs_inode) { - struct inode *inode = iget5_locked(sb, CRAMINO(cramfs_inode), - cramfs_iget5_test, cramfs_iget5_set, - cramfs_inode); - static struct timespec zerotime; - - if (inode && (inode->i_state & I_NEW)) { - inode->i_mode = cramfs_inode->mode; - inode->i_uid = cramfs_inode->uid; - inode->i_size = cramfs_inode->size; - inode->i_blocks = (cramfs_inode->size - 1) / 512 + 1; - inode->i_gid = cramfs_inode->gid; - /* Struct copy intentional */ - inode->i_mtime = inode->i_atime = inode->i_ctime = zerotime; - /* inode->i_nlink is left 1 - arguably wrong for directories, - but it's the best we can do without reading the directory - contents. 1 yields the right result in GNU find, even - without -noleaf option. */ - if (S_ISREG(inode->i_mode)) { - inode->i_fop = &generic_ro_fops; - inode->i_data.a_ops = &cramfs_aops; - } else if (S_ISDIR(inode->i_mode)) { - inode->i_op = &cramfs_dir_inode_operations; - inode->i_fop = &cramfs_directory_operations; - } else if (S_ISLNK(inode->i_mode)) { - inode->i_op = &page_symlink_inode_operations; - inode->i_data.a_ops = &cramfs_aops; - } else { - init_special_inode(inode, inode->i_mode, - old_decode_dev(cramfs_inode->size)); + struct inode *inode; + if (CRAMINO(cramfs_inode) == 1) { + inode = new_inode(sb); + if (inode) { + inode->i_ino = 1; + setup_inode(inode, cramfs_inode); + } + } else { + inode = iget_locked(sb, CRAMINO(cramfs_inode)); + if (inode) { + setup_inode(inode, cramfs_inode); + unlock_new_inode(inode); } - unlock_new_inode(inode); } return inode; } -static void cramfs_drop_inode(struct inode *inode) -{ - if (inode->i_ino == 1) - generic_delete_inode(inode); - else - generic_drop_inode(inode); -} - /* * We have our own block cache: don't fill up the buffer cache * with the rom-image, because the way the filesystem is set @@ -542,7 +531,6 @@ static const struct super_operations cramfs_ops = { .put_super = cramfs_put_super, .remount_fs = cramfs_remount, .statfs = cramfs_statfs, - .drop_inode = cramfs_drop_inode, }; static int cramfs_get_sb(struct file_system_type *fs_type, -- cgit v1.2.3 From 8267952b362b67a5cb5371d6894a772a13e6874c Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 4 Jun 2010 22:17:56 -0400 Subject: switch procfs to ->evict_inode() Signed-off-by: Al Viro --- fs/proc/inode.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/proc/inode.c b/fs/proc/inode.c index aea8502e58a3..23561cda7245 100644 --- a/fs/proc/inode.c +++ b/fs/proc/inode.c @@ -25,11 +25,12 @@ #include "internal.h" -static void proc_delete_inode(struct inode *inode) +static void proc_evict_inode(struct inode *inode) { struct proc_dir_entry *de; truncate_inode_pages(&inode->i_data, 0); + end_writeback(inode); /* Stop tracking associated processes */ put_pid(PROC_I(inode)->pid); @@ -40,7 +41,6 @@ static void proc_delete_inode(struct inode *inode) pde_put(de); if (PROC_I(inode)->sysctl) sysctl_head_put(PROC_I(inode)->sysctl); - clear_inode(inode); } struct vfsmount *proc_mnt; @@ -91,7 +91,7 @@ static const struct super_operations proc_sops = { .alloc_inode = proc_alloc_inode, .destroy_inode = proc_destroy_inode, .drop_inode = generic_delete_inode, - .delete_inode = proc_delete_inode, + .evict_inode = proc_evict_inode, .statfs = simple_statfs, }; -- cgit v1.2.3 From 01cd9fef6eb3caae06415861de5b53224b722549 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 4 Jun 2010 22:21:54 -0400 Subject: switch sysfs to ->evict_inode() Signed-off-by: Al Viro --- fs/sysfs/inode.c | 6 +++--- fs/sysfs/mount.c | 2 +- fs/sysfs/sysfs.h | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'fs') diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index 7e187fbd3d47..cffb1fd8ba33 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -312,15 +312,15 @@ struct inode * sysfs_get_inode(struct super_block *sb, struct sysfs_dirent *sd) * The sysfs_dirent serves as both an inode and a directory entry for sysfs. * To prevent the sysfs inode numbers from being freed prematurely we take a * reference to sysfs_dirent from the sysfs inode. A - * super_operations.delete_inode() implementation is needed to drop that + * super_operations.evict_inode() implementation is needed to drop that * reference upon inode destruction. */ -void sysfs_delete_inode(struct inode *inode) +void sysfs_evict_inode(struct inode *inode) { struct sysfs_dirent *sd = inode->i_private; truncate_inode_pages(&inode->i_data, 0); - clear_inode(inode); + end_writeback(inode); sysfs_put(sd); } diff --git a/fs/sysfs/mount.c b/fs/sysfs/mount.c index 281c0c9bc39f..f2af22574c50 100644 --- a/fs/sysfs/mount.c +++ b/fs/sysfs/mount.c @@ -29,7 +29,7 @@ struct kmem_cache *sysfs_dir_cachep; static const struct super_operations sysfs_ops = { .statfs = simple_statfs, .drop_inode = generic_delete_inode, - .delete_inode = sysfs_delete_inode, + .evict_inode = sysfs_evict_inode, }; struct sysfs_dirent sysfs_root = { diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index 6a13105b5594..d9be60a2e956 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -198,7 +198,7 @@ static inline void __sysfs_put(struct sysfs_dirent *sd) * inode.c */ struct inode *sysfs_get_inode(struct super_block *sb, struct sysfs_dirent *sd); -void sysfs_delete_inode(struct inode *inode); +void sysfs_evict_inode(struct inode *inode); int sysfs_sd_setattr(struct sysfs_dirent *sd, struct iattr *iattr); int sysfs_permission(struct inode *inode, int mask); int sysfs_setattr(struct dentry *dentry, struct iattr *iattr); -- cgit v1.2.3 From 5ccb4a78d8c0e27985afec32cc4894d48e7b876e Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 4 Jun 2010 22:27:38 -0400 Subject: switch minix to ->evict_inode(), fix write_inode/delete_inode race We need to wait for completion of possible writeback in progress before we clear on-disk inode during deletion. Signed-off-by: Al Viro --- fs/minix/bitmap.c | 6 ++---- fs/minix/inode.c | 15 ++++++++++----- 2 files changed, 12 insertions(+), 9 deletions(-) (limited to 'fs') diff --git a/fs/minix/bitmap.c b/fs/minix/bitmap.c index 482779fe4e7c..3f32bcb0d9bd 100644 --- a/fs/minix/bitmap.c +++ b/fs/minix/bitmap.c @@ -200,13 +200,13 @@ void minix_free_inode(struct inode * inode) ino = inode->i_ino; if (ino < 1 || ino > sbi->s_ninodes) { printk("minix_free_inode: inode 0 or nonexistent inode\n"); - goto out; + return; } bit = ino & ((1<>= k; if (ino >= sbi->s_imap_blocks) { printk("minix_free_inode: nonexistent imap in superblock\n"); - goto out; + return; } minix_clear_inode(inode); /* clear on-disk copy */ @@ -217,8 +217,6 @@ void minix_free_inode(struct inode * inode) printk("minix_free_inode: bit %lu already cleared\n", bit); spin_unlock(&bitmap_lock); mark_buffer_dirty(bh); - out: - clear_inode(inode); /* clear in-memory copy */ } struct inode *minix_new_inode(const struct inode *dir, int mode, int *error) diff --git a/fs/minix/inode.c b/fs/minix/inode.c index 125062f55ef2..e39d6bf2e8fb 100644 --- a/fs/minix/inode.c +++ b/fs/minix/inode.c @@ -24,12 +24,17 @@ static int minix_write_inode(struct inode *inode, static int minix_statfs(struct dentry *dentry, struct kstatfs *buf); static int minix_remount (struct super_block * sb, int * flags, char * data); -static void minix_delete_inode(struct inode *inode) +static void minix_evict_inode(struct inode *inode) { truncate_inode_pages(&inode->i_data, 0); - inode->i_size = 0; - minix_truncate(inode); - minix_free_inode(inode); + if (!inode->i_nlink) { + inode->i_size = 0; + minix_truncate(inode); + } + invalidate_inode_buffers(inode); + end_writeback(inode); + if (!inode->i_nlink) + minix_free_inode(inode); } static void minix_put_super(struct super_block *sb) @@ -96,7 +101,7 @@ static const struct super_operations minix_sops = { .alloc_inode = minix_alloc_inode, .destroy_inode = minix_destroy_inode, .write_inode = minix_write_inode, - .delete_inode = minix_delete_inode, + .evict_inode = minix_evict_inode, .put_super = minix_put_super, .statfs = minix_statfs, .remount_fs = minix_remount, -- cgit v1.2.3 From 3889717d2851bf38015c0b291026c07c02264623 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 22 Jul 2010 01:13:36 +0400 Subject: ext2: switch to dquot_free_block_nodirty() brute-force conversion Signed-off-by: Al Viro --- fs/ext2/balloc.c | 12 ++++++++---- fs/ext2/xattr.c | 12 ++++++++---- 2 files changed, 16 insertions(+), 8 deletions(-) (limited to 'fs') diff --git a/fs/ext2/balloc.c b/fs/ext2/balloc.c index e8766a396776..db69c1206f13 100644 --- a/fs/ext2/balloc.c +++ b/fs/ext2/balloc.c @@ -571,7 +571,8 @@ do_more: error_return: brelse(bitmap_bh); release_blocks(sb, freed); - dquot_free_block(inode, freed); + dquot_free_block_nodirty(inode, freed); + mark_inode_dirty(inode); } /** @@ -1418,7 +1419,8 @@ allocated: *errp = 0; brelse(bitmap_bh); - dquot_free_block(inode, *count-num); + dquot_free_block_nodirty(inode, *count-num); + mark_inode_dirty(inode); *count = num; return ret_block; @@ -1428,8 +1430,10 @@ out: /* * Undo the block allocation */ - if (!performed_allocation) - dquot_free_block(inode, *count); + if (!performed_allocation) { + dquot_free_block_nodirty(inode, *count); + mark_inode_dirty(inode); + } brelse(bitmap_bh); return 0; } diff --git a/fs/ext2/xattr.c b/fs/ext2/xattr.c index 7c3915780b19..0fa24e814d8a 100644 --- a/fs/ext2/xattr.c +++ b/fs/ext2/xattr.c @@ -703,8 +703,10 @@ ext2_xattr_set2(struct inode *inode, struct buffer_head *old_bh, * written (only some dirty data were not) so we just proceed * as if nothing happened and cleanup the unused block */ if (error && error != -ENOSPC) { - if (new_bh && new_bh != old_bh) - dquot_free_block(inode, 1); + if (new_bh && new_bh != old_bh) { + dquot_free_block_nodirty(inode, 1); + mark_inode_dirty(inode); + } goto cleanup; } } else @@ -736,7 +738,8 @@ ext2_xattr_set2(struct inode *inode, struct buffer_head *old_bh, le32_add_cpu(&HDR(old_bh)->h_refcount, -1); if (ce) mb_cache_entry_release(ce); - dquot_free_block(inode, 1); + dquot_free_block_nodirty(inode, 1); + mark_inode_dirty(inode); mark_buffer_dirty(old_bh); ea_bdebug(old_bh, "refcount now=%d", le32_to_cpu(HDR(old_bh)->h_refcount)); @@ -799,7 +802,8 @@ ext2_xattr_delete_inode(struct inode *inode) mark_buffer_dirty(bh); if (IS_SYNC(inode)) sync_dirty_buffer(bh); - dquot_free_block(inode, 1); + dquot_free_block_nodirty(inode, 1); + mark_inode_dirty(inode); } EXT2_I(inode)->i_file_acl = 0; -- cgit v1.2.3 From addacc7d6f0f0bcce12adf9fe9e6455e1dfd74da Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 22 Jul 2010 01:19:42 +0400 Subject: Take dirtying the inode to callers of ext2_free_blocks() Signed-off-by: Al Viro --- fs/ext2/balloc.c | 1 - fs/ext2/inode.c | 6 ++++-- fs/ext2/xattr.c | 3 +++ 3 files changed, 7 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/ext2/balloc.c b/fs/ext2/balloc.c index db69c1206f13..c6c684b44ea1 100644 --- a/fs/ext2/balloc.c +++ b/fs/ext2/balloc.c @@ -572,7 +572,6 @@ error_return: brelse(bitmap_bh); release_blocks(sb, freed); dquot_free_block_nodirty(inode, freed); - mark_inode_dirty(inode); } /** diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index 069620b30d4d..e8af26dd6715 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -423,6 +423,8 @@ static int ext2_alloc_blocks(struct inode *inode, failed_out: for (i = 0; i 0) { - mark_inode_dirty(inode); ext2_free_blocks (inode, block_to_free, count); + mark_inode_dirty(inode); } } diff --git a/fs/ext2/xattr.c b/fs/ext2/xattr.c index 0fa24e814d8a..25ff041058a7 100644 --- a/fs/ext2/xattr.c +++ b/fs/ext2/xattr.c @@ -674,6 +674,7 @@ ext2_xattr_set2(struct inode *inode, struct buffer_head *old_bh, new_bh = sb_getblk(sb, block); if (!new_bh) { ext2_free_blocks(inode, block, 1); + mark_inode_dirty(inode); error = -EIO; goto cleanup; } @@ -729,6 +730,7 @@ ext2_xattr_set2(struct inode *inode, struct buffer_head *old_bh, mb_cache_entry_free(ce); ea_bdebug(old_bh, "freeing"); ext2_free_blocks(inode, old_bh->b_blocknr, 1); + mark_inode_dirty(inode); /* We let our caller release old_bh, so we * need to duplicate the buffer before. */ get_bh(old_bh); @@ -789,6 +791,7 @@ ext2_xattr_delete_inode(struct inode *inode) if (ce) mb_cache_entry_free(ce); ext2_free_blocks(inode, EXT2_I(inode)->i_file_acl, 1); + mark_inode_dirty(inode); get_bh(bh); bforget(bh); unlock_buffer(bh); -- cgit v1.2.3 From 3937871d91e4f43e4aaf0b214c68a7857c0e6e80 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 22 Jul 2010 01:22:47 +0400 Subject: Don't dirty the victim in ext2_xattr_delete_inode() ... it's beyond fs-writeback reach already - writeback won't be started at that point. Signed-off-by: Al Viro --- fs/ext2/xattr.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'fs') diff --git a/fs/ext2/xattr.c b/fs/ext2/xattr.c index 25ff041058a7..5ab87e6edffc 100644 --- a/fs/ext2/xattr.c +++ b/fs/ext2/xattr.c @@ -791,7 +791,6 @@ ext2_xattr_delete_inode(struct inode *inode) if (ce) mb_cache_entry_free(ce); ext2_free_blocks(inode, EXT2_I(inode)->i_file_acl, 1); - mark_inode_dirty(inode); get_bh(bh); bforget(bh); unlock_buffer(bh); @@ -806,7 +805,6 @@ ext2_xattr_delete_inode(struct inode *inode) if (IS_SYNC(inode)) sync_dirty_buffer(bh); dquot_free_block_nodirty(inode, 1); - mark_inode_dirty(inode); } EXT2_I(inode)->i_file_acl = 0; -- cgit v1.2.3 From 72edc4d0873ba5165c0759264298bf5f55351c7a Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 4 Jun 2010 23:32:28 -0400 Subject: merge ext2 delete_inode and clear_inode, switch to ->evict_inode() Signed-off-by: Al Viro --- fs/ext2/ext2.h | 2 +- fs/ext2/ialloc.c | 13 ++++--------- fs/ext2/inode.c | 44 ++++++++++++++++++++++++++++++-------------- fs/ext2/super.c | 14 +------------- 4 files changed, 36 insertions(+), 37 deletions(-) (limited to 'fs') diff --git a/fs/ext2/ext2.h b/fs/ext2/ext2.h index 8f53d11bf957..416daa62242c 100644 --- a/fs/ext2/ext2.h +++ b/fs/ext2/ext2.h @@ -119,7 +119,7 @@ extern unsigned long ext2_count_free (struct buffer_head *, unsigned); /* inode.c */ extern struct inode *ext2_iget (struct super_block *, unsigned long); extern int ext2_write_inode (struct inode *, struct writeback_control *); -extern void ext2_delete_inode (struct inode *); +extern void ext2_evict_inode(struct inode *); extern int ext2_sync_inode (struct inode *); extern int ext2_get_block(struct inode *, sector_t, struct buffer_head *, int); extern int ext2_setattr (struct dentry *, struct iattr *); diff --git a/fs/ext2/ialloc.c b/fs/ext2/ialloc.c index 938dbc739d00..ad70479aabff 100644 --- a/fs/ext2/ialloc.c +++ b/fs/ext2/ialloc.c @@ -118,19 +118,14 @@ void ext2_free_inode (struct inode * inode) * Note: we must free any quota before locking the superblock, * as writing the quota to disk may need the lock as well. */ - if (!is_bad_inode(inode)) { - /* Quota is already initialized in iput() */ - ext2_xattr_delete_inode(inode); - dquot_free_inode(inode); - dquot_drop(inode); - } + /* Quota is already initialized in iput() */ + ext2_xattr_delete_inode(inode); + dquot_free_inode(inode); + dquot_drop(inode); es = EXT2_SB(sb)->s_es; is_directory = S_ISDIR(inode->i_mode); - /* Do this BEFORE marking the inode not in use or returning an error */ - clear_inode (inode); - if (ino < EXT2_FIRST_INO(sb) || ino > le32_to_cpu(es->s_inodes_count)) { ext2_error (sb, "ext2_free_inode", diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index e8af26dd6715..940c96168868 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -69,26 +69,42 @@ static void ext2_write_failed(struct address_space *mapping, loff_t to) /* * Called at the last iput() if i_nlink is zero. */ -void ext2_delete_inode (struct inode * inode) +void ext2_evict_inode(struct inode * inode) { - if (!is_bad_inode(inode)) + struct ext2_block_alloc_info *rsv; + int want_delete = 0; + + if (!inode->i_nlink && !is_bad_inode(inode)) { + want_delete = 1; dquot_initialize(inode); + } else { + dquot_drop(inode); + } + truncate_inode_pages(&inode->i_data, 0); - if (is_bad_inode(inode)) - goto no_delete; - EXT2_I(inode)->i_dtime = get_seconds(); - mark_inode_dirty(inode); - __ext2_write_inode(inode, inode_needs_sync(inode)); + if (want_delete) { + /* set dtime */ + EXT2_I(inode)->i_dtime = get_seconds(); + mark_inode_dirty(inode); + __ext2_write_inode(inode, inode_needs_sync(inode)); + /* truncate to 0 */ + inode->i_size = 0; + if (inode->i_blocks) + ext2_truncate_blocks(inode, 0); + } - inode->i_size = 0; - if (inode->i_blocks) - ext2_truncate_blocks(inode, 0); - ext2_free_inode (inode); + invalidate_inode_buffers(inode); + end_writeback(inode); + + ext2_discard_reservation(inode); + rsv = EXT2_I(inode)->i_block_alloc_info; + EXT2_I(inode)->i_block_alloc_info = NULL; + if (unlikely(rsv)) + kfree(rsv); - return; -no_delete: - clear_inode(inode); /* We must guarantee clearing of inode... */ + if (want_delete) + ext2_free_inode(inode); } typedef struct { diff --git a/fs/ext2/super.c b/fs/ext2/super.c index 7ff43f4a59cd..1ec602673ea8 100644 --- a/fs/ext2/super.c +++ b/fs/ext2/super.c @@ -195,17 +195,6 @@ static void destroy_inodecache(void) kmem_cache_destroy(ext2_inode_cachep); } -static void ext2_clear_inode(struct inode *inode) -{ - struct ext2_block_alloc_info *rsv = EXT2_I(inode)->i_block_alloc_info; - - dquot_drop(inode); - ext2_discard_reservation(inode); - EXT2_I(inode)->i_block_alloc_info = NULL; - if (unlikely(rsv)) - kfree(rsv); -} - static int ext2_show_options(struct seq_file *seq, struct vfsmount *vfs) { struct super_block *sb = vfs->mnt_sb; @@ -299,13 +288,12 @@ static const struct super_operations ext2_sops = { .alloc_inode = ext2_alloc_inode, .destroy_inode = ext2_destroy_inode, .write_inode = ext2_write_inode, - .delete_inode = ext2_delete_inode, + .evict_inode = ext2_evict_inode, .put_super = ext2_put_super, .write_super = ext2_write_super, .sync_fs = ext2_sync_fs, .statfs = ext2_statfs, .remount_fs = ext2_remount, - .clear_inode = ext2_clear_inode, .show_options = ext2_show_options, #ifdef CONFIG_QUOTA .quota_read = ext2_quota_read, -- cgit v1.2.3 From d299eadc098743ea0cfbf9502fb04abf1d39ce36 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 5 Jun 2010 19:16:20 -0400 Subject: switch sysv to ->evict_inode() Signed-off-by: Al Viro --- fs/sysv/ialloc.c | 1 - fs/sysv/inode.c | 15 ++++++++++----- 2 files changed, 10 insertions(+), 6 deletions(-) (limited to 'fs') diff --git a/fs/sysv/ialloc.c b/fs/sysv/ialloc.c index fcc498ec9b33..0c96c98bd1db 100644 --- a/fs/sysv/ialloc.c +++ b/fs/sysv/ialloc.c @@ -113,7 +113,6 @@ void sysv_free_inode(struct inode * inode) return; } raw_inode = sysv_raw_inode(sb, ino, &bh); - clear_inode(inode); if (!raw_inode) { printk("sysv_free_inode: unable to read inode block on device " "%s\n", inode->i_sb->s_id); diff --git a/fs/sysv/inode.c b/fs/sysv/inode.c index d4a5380b5669..613a5056e880 100644 --- a/fs/sysv/inode.c +++ b/fs/sysv/inode.c @@ -308,12 +308,17 @@ int sysv_sync_inode(struct inode *inode) return __sysv_write_inode(inode, 1); } -static void sysv_delete_inode(struct inode *inode) +static void sysv_evict_inode(struct inode *inode) { truncate_inode_pages(&inode->i_data, 0); - inode->i_size = 0; - sysv_truncate(inode); - sysv_free_inode(inode); + if (!inode->i_nlink) { + inode->i_size = 0; + sysv_truncate(inode); + } + invalidate_inode_buffers(inode); + end_writeback(inode); + if (!inode->i_nlink) + sysv_free_inode(inode); } static struct kmem_cache *sysv_inode_cachep; @@ -344,7 +349,7 @@ const struct super_operations sysv_sops = { .alloc_inode = sysv_alloc_inode, .destroy_inode = sysv_destroy_inode, .write_inode = sysv_write_inode, - .delete_inode = sysv_delete_inode, + .evict_inode = sysv_evict_inode, .put_super = sysv_put_super, .write_super = sysv_write_super, .sync_fs = sysv_sync_fs, -- cgit v1.2.3 From d3b4f9ae184b0a3982dbe000ddf88952f090dc28 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 5 Jun 2010 19:22:50 -0400 Subject: switch smbfs to evict_inode() NB: treatment of inode hash is completely braindead there Signed-off-by: Al Viro --- fs/smbfs/inode.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/smbfs/inode.c b/fs/smbfs/inode.c index e338f0a5a70d..450c91941988 100644 --- a/fs/smbfs/inode.c +++ b/fs/smbfs/inode.c @@ -46,7 +46,7 @@ #define SMB_TTL_DEFAULT 1000 -static void smb_delete_inode(struct inode *); +static void smb_evict_inode(struct inode *); static void smb_put_super(struct super_block *); static int smb_statfs(struct dentry *, struct kstatfs *); static int smb_show_options(struct seq_file *, struct vfsmount *); @@ -102,7 +102,7 @@ static const struct super_operations smb_sops = .alloc_inode = smb_alloc_inode, .destroy_inode = smb_destroy_inode, .drop_inode = generic_delete_inode, - .delete_inode = smb_delete_inode, + .evict_inode = smb_evict_inode, .put_super = smb_put_super, .statfs = smb_statfs, .show_options = smb_show_options, @@ -324,15 +324,15 @@ out: * All blocking cleanup operations need to go here to avoid races. */ static void -smb_delete_inode(struct inode *ino) +smb_evict_inode(struct inode *ino) { DEBUG1("ino=%ld\n", ino->i_ino); truncate_inode_pages(&ino->i_data, 0); + end_writeback(ino); lock_kernel(); if (smb_close(ino)) PARANOIA("could not close inode %ld\n", ino->i_ino); unlock_kernel(); - clear_inode(ino); } static struct option opts[] = { -- cgit v1.2.3 From deee3ce466a3e2cfb54c93b8fd22bbccd19ea7d6 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 5 Jun 2010 19:28:32 -0400 Subject: covert fatfs to ->evict_inode() Signed-off-by: Al Viro --- fs/fat/inode.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) (limited to 'fs') diff --git a/fs/fat/inode.c b/fs/fat/inode.c index ec6a699a4023..830058057d33 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -263,7 +263,7 @@ static const struct address_space_operations fat_aops = { * check if the location is still valid and retry if it * isn't. Otherwise we do changes. * 5. Spinlock is used to protect hash/unhash/location check/lookup - * 6. fat_clear_inode() unhashes the F-d-c entry. + * 6. fat_evict_inode() unhashes the F-d-c entry. * 7. lookup() and readdir() do igrab() if they find a F-d-c entry * and consider negative result as cache miss. */ @@ -448,16 +448,15 @@ out: EXPORT_SYMBOL_GPL(fat_build_inode); -static void fat_delete_inode(struct inode *inode) +static void fat_evict_inode(struct inode *inode) { truncate_inode_pages(&inode->i_data, 0); - inode->i_size = 0; - fat_truncate_blocks(inode, 0); - clear_inode(inode); -} - -static void fat_clear_inode(struct inode *inode) -{ + if (!inode->i_nlink) { + inode->i_size = 0; + fat_truncate_blocks(inode, 0); + } + invalidate_inode_buffers(inode); + end_writeback(inode); fat_cache_inval_inode(inode); fat_detach(inode); } @@ -674,12 +673,11 @@ static const struct super_operations fat_sops = { .alloc_inode = fat_alloc_inode, .destroy_inode = fat_destroy_inode, .write_inode = fat_write_inode, - .delete_inode = fat_delete_inode, + .evict_inode = fat_evict_inode, .put_super = fat_put_super, .write_super = fat_write_super, .sync_fs = fat_sync_fs, .statfs = fat_statfs, - .clear_inode = fat_clear_inode, .remount_fs = fat_remount, .show_options = fat_show_options, -- cgit v1.2.3 From 58e8268c7bae538ccb8b7eccc817c1c28bcd4da2 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 5 Jun 2010 19:40:56 -0400 Subject: switch ufs to ->evict_inode() Signed-off-by: Al Viro --- fs/ufs/ialloc.c | 2 -- fs/ufs/inode.c | 43 ++++++++++++++++++++++++++----------------- fs/ufs/super.c | 2 +- fs/ufs/ufs.h | 2 +- 4 files changed, 28 insertions(+), 21 deletions(-) (limited to 'fs') diff --git a/fs/ufs/ialloc.c b/fs/ufs/ialloc.c index 594480e537d2..428017e018fe 100644 --- a/fs/ufs/ialloc.c +++ b/fs/ufs/ialloc.c @@ -94,8 +94,6 @@ void ufs_free_inode (struct inode * inode) is_directory = S_ISDIR(inode->i_mode); - clear_inode (inode); - if (ubh_isclr (UCPI_UBH(ucpi), ucpi->c_iusedoff, bit)) ufs_error(sb, "ufs_free_inode", "bit already cleared for inode %u", ino); else { diff --git a/fs/ufs/inode.c b/fs/ufs/inode.c index 45cafa937a4b..2b251f2093af 100644 --- a/fs/ufs/inode.c +++ b/fs/ufs/inode.c @@ -911,24 +911,33 @@ int ufs_sync_inode (struct inode *inode) return ufs_update_inode (inode, 1); } -void ufs_delete_inode (struct inode * inode) +void ufs_evict_inode(struct inode * inode) { - loff_t old_i_size; + int want_delete = 0; + + if (!inode->i_nlink && !is_bad_inode(inode)) + want_delete = 1; truncate_inode_pages(&inode->i_data, 0); - if (is_bad_inode(inode)) - goto no_delete; - /*UFS_I(inode)->i_dtime = CURRENT_TIME;*/ - lock_kernel(); - mark_inode_dirty(inode); - ufs_update_inode(inode, IS_SYNC(inode)); - old_i_size = inode->i_size; - inode->i_size = 0; - if (inode->i_blocks && ufs_truncate(inode, old_i_size)) - ufs_warning(inode->i_sb, __func__, "ufs_truncate failed\n"); - ufs_free_inode (inode); - unlock_kernel(); - return; -no_delete: - clear_inode(inode); /* We must guarantee clearing of inode... */ + if (want_delete) { + loff_t old_i_size; + /*UFS_I(inode)->i_dtime = CURRENT_TIME;*/ + lock_kernel(); + mark_inode_dirty(inode); + ufs_update_inode(inode, IS_SYNC(inode)); + old_i_size = inode->i_size; + inode->i_size = 0; + if (inode->i_blocks && ufs_truncate(inode, old_i_size)) + ufs_warning(inode->i_sb, __func__, "ufs_truncate failed\n"); + unlock_kernel(); + } + + invalidate_inode_buffers(inode); + end_writeback(inode); + + if (want_delete) { + lock_kernel(); + ufs_free_inode (inode); + unlock_kernel(); + } } diff --git a/fs/ufs/super.c b/fs/ufs/super.c index 3ec5a9eb6efb..d510c1b91817 100644 --- a/fs/ufs/super.c +++ b/fs/ufs/super.c @@ -1440,7 +1440,7 @@ static const struct super_operations ufs_super_ops = { .alloc_inode = ufs_alloc_inode, .destroy_inode = ufs_destroy_inode, .write_inode = ufs_write_inode, - .delete_inode = ufs_delete_inode, + .evict_inode = ufs_evict_inode, .put_super = ufs_put_super, .write_super = ufs_write_super, .sync_fs = ufs_sync_fs, diff --git a/fs/ufs/ufs.h b/fs/ufs/ufs.h index 179ae6b3180a..c08782e1b48a 100644 --- a/fs/ufs/ufs.h +++ b/fs/ufs/ufs.h @@ -108,7 +108,7 @@ extern struct inode * ufs_new_inode (struct inode *, int); extern struct inode *ufs_iget(struct super_block *, unsigned long); extern int ufs_write_inode (struct inode *, struct writeback_control *); extern int ufs_sync_inode (struct inode *); -extern void ufs_delete_inode (struct inode *); +extern void ufs_evict_inode (struct inode *); extern struct buffer_head * ufs_bread (struct inode *, unsigned, int, int *); extern int ufs_getfrag_block (struct inode *inode, sector_t fragment, struct buffer_head *bh_result, int create); -- cgit v1.2.3 From ac14a95b5239d37b6082c3791b88d7ab4e8e444c Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 6 Jun 2010 07:08:19 -0400 Subject: convert ext3 to ->evict_inode() Signed-off-by: Al Viro --- fs/ext3/ialloc.c | 12 ------------ fs/ext3/inode.c | 37 +++++++++++++++++++++++++++---------- fs/ext3/super.c | 14 +------------- include/linux/ext3_fs.h | 2 +- 4 files changed, 29 insertions(+), 36 deletions(-) (limited to 'fs') diff --git a/fs/ext3/ialloc.c b/fs/ext3/ialloc.c index 498021eb88fb..4ab72db3559e 100644 --- a/fs/ext3/ialloc.c +++ b/fs/ext3/ialloc.c @@ -119,20 +119,8 @@ void ext3_free_inode (handle_t *handle, struct inode * inode) ino = inode->i_ino; ext3_debug ("freeing inode %lu\n", ino); - /* - * Note: we must free any quota before locking the superblock, - * as writing the quota to disk may need the lock as well. - */ - dquot_initialize(inode); - ext3_xattr_delete_inode(handle, inode); - dquot_free_inode(inode); - dquot_drop(inode); - is_directory = S_ISDIR(inode->i_mode); - /* Do this BEFORE marking the inode not in use or returning an error */ - clear_inode (inode); - es = EXT3_SB(sb)->s_es; if (ino < EXT3_FIRST_INO(sb) || ino > le32_to_cpu(es->s_inodes_count)) { ext3_error (sb, "ext3_free_inode", diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c index b04d11936683..cc55cecf9fbc 100644 --- a/fs/ext3/inode.c +++ b/fs/ext3/inode.c @@ -190,18 +190,28 @@ static int truncate_restart_transaction(handle_t *handle, struct inode *inode) } /* - * Called at the last iput() if i_nlink is zero. + * Called at inode eviction from icache */ -void ext3_delete_inode (struct inode * inode) +void ext3_evict_inode (struct inode *inode) { + struct ext3_block_alloc_info *rsv; handle_t *handle; + int want_delete = 0; - if (!is_bad_inode(inode)) + if (!inode->i_nlink && !is_bad_inode(inode)) { dquot_initialize(inode); + want_delete = 1; + } truncate_inode_pages(&inode->i_data, 0); - if (is_bad_inode(inode)) + ext3_discard_reservation(inode); + rsv = EXT3_I(inode)->i_block_alloc_info; + EXT3_I(inode)->i_block_alloc_info = NULL; + if (unlikely(rsv)) + kfree(rsv); + + if (!want_delete) goto no_delete; handle = start_transaction(inode); @@ -238,15 +248,22 @@ void ext3_delete_inode (struct inode * inode) * having errors), but we can't free the inode if the mark_dirty * fails. */ - if (ext3_mark_inode_dirty(handle, inode)) - /* If that failed, just do the required in-core inode clear. */ - clear_inode(inode); - else + if (ext3_mark_inode_dirty(handle, inode)) { + /* If that failed, just dquot_drop() and be done with that */ + dquot_drop(inode); + end_writeback(inode); + } else { + ext3_xattr_delete_inode(handle, inode); + dquot_free_inode(inode); + dquot_drop(inode); + end_writeback(inode); ext3_free_inode(handle, inode); + } ext3_journal_stop(handle); return; no_delete: - clear_inode(inode); /* We must guarantee clearing of inode... */ + end_writeback(inode); + dquot_drop(inode); } typedef struct { @@ -2564,7 +2581,7 @@ out_stop: * If this was a simple ftruncate(), and the file will remain alive * then we need to clear up the orphan record which we created above. * However, if this was a real unlink then we were called by - * ext3_delete_inode(), and we allow that function to clean up the + * ext3_evict_inode(), and we allow that function to clean up the * orphan info for us. */ if (inode->i_nlink) diff --git a/fs/ext3/super.c b/fs/ext3/super.c index 6c953bb255e7..a951fd5c081c 100644 --- a/fs/ext3/super.c +++ b/fs/ext3/super.c @@ -527,17 +527,6 @@ static void destroy_inodecache(void) kmem_cache_destroy(ext3_inode_cachep); } -static void ext3_clear_inode(struct inode *inode) -{ - struct ext3_block_alloc_info *rsv = EXT3_I(inode)->i_block_alloc_info; - - dquot_drop(inode); - ext3_discard_reservation(inode); - EXT3_I(inode)->i_block_alloc_info = NULL; - if (unlikely(rsv)) - kfree(rsv); -} - static inline void ext3_show_quota_options(struct seq_file *seq, struct super_block *sb) { #if defined(CONFIG_QUOTA) @@ -783,14 +772,13 @@ static const struct super_operations ext3_sops = { .destroy_inode = ext3_destroy_inode, .write_inode = ext3_write_inode, .dirty_inode = ext3_dirty_inode, - .delete_inode = ext3_delete_inode, + .evict_inode = ext3_evict_inode, .put_super = ext3_put_super, .sync_fs = ext3_sync_fs, .freeze_fs = ext3_freeze, .unfreeze_fs = ext3_unfreeze, .statfs = ext3_statfs, .remount_fs = ext3_remount, - .clear_inode = ext3_clear_inode, .show_options = ext3_show_options, #ifdef CONFIG_QUOTA .quota_read = ext3_quota_read, diff --git a/include/linux/ext3_fs.h b/include/linux/ext3_fs.h index 7fc62d4550b2..e7cb21766992 100644 --- a/include/linux/ext3_fs.h +++ b/include/linux/ext3_fs.h @@ -896,7 +896,7 @@ int ext3_get_blocks_handle(handle_t *handle, struct inode *inode, extern struct inode *ext3_iget(struct super_block *, unsigned long); extern int ext3_write_inode (struct inode *, struct writeback_control *); extern int ext3_setattr (struct dentry *, struct iattr *); -extern void ext3_delete_inode (struct inode *); +extern void ext3_evict_inode (struct inode *); extern int ext3_sync_inode (handle_t *, struct inode *); extern void ext3_discard_reservation (struct inode *); extern void ext3_dirty_inode(struct inode *); -- cgit v1.2.3 From 9df2f85128def59185f8a1c584f8af41df17405a Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 6 Jun 2010 09:50:39 -0400 Subject: switch bfs to ->evict_inode(), clean up Signed-off-by: Al Viro --- fs/bfs/inode.c | 70 +++++++++++++++++++++++++++------------------------------- 1 file changed, 32 insertions(+), 38 deletions(-) (limited to 'fs') diff --git a/fs/bfs/inode.c b/fs/bfs/inode.c index f22a7d3dc362..0499822b1568 100644 --- a/fs/bfs/inode.c +++ b/fs/bfs/inode.c @@ -99,6 +99,24 @@ error: return ERR_PTR(-EIO); } +static struct bfs_inode *find_inode(struct super_block *sb, u16 ino, struct buffer_head **p) +{ + if ((ino < BFS_ROOT_INO) || (ino > BFS_SB(sb)->si_lasti)) { + printf("Bad inode number %s:%08x\n", sb->s_id, ino); + return ERR_PTR(-EIO); + } + + ino -= BFS_ROOT_INO; + + *p = sb_bread(sb, 1 + ino / BFS_INODES_PER_BLOCK); + if (!*p) { + printf("Unable to read inode %s:%08x\n", sb->s_id, ino); + return ERR_PTR(-EIO); + } + + return (struct bfs_inode *)(*p)->b_data + ino % BFS_INODES_PER_BLOCK; +} + static int bfs_write_inode(struct inode *inode, struct writeback_control *wbc) { struct bfs_sb_info *info = BFS_SB(inode->i_sb); @@ -106,28 +124,15 @@ static int bfs_write_inode(struct inode *inode, struct writeback_control *wbc) unsigned long i_sblock; struct bfs_inode *di; struct buffer_head *bh; - int block, off; int err = 0; dprintf("ino=%08x\n", ino); - if ((ino < BFS_ROOT_INO) || (ino > BFS_SB(inode->i_sb)->si_lasti)) { - printf("Bad inode number %s:%08x\n", inode->i_sb->s_id, ino); - return -EIO; - } + di = find_inode(inode->i_sb, ino, &bh); + if (IS_ERR(di)) + return PTR_ERR(di); mutex_lock(&info->bfs_lock); - block = (ino - BFS_ROOT_INO) / BFS_INODES_PER_BLOCK + 1; - bh = sb_bread(inode->i_sb, block); - if (!bh) { - printf("Unable to read inode %s:%08x\n", - inode->i_sb->s_id, ino); - mutex_unlock(&info->bfs_lock); - return -EIO; - } - - off = (ino - BFS_ROOT_INO) % BFS_INODES_PER_BLOCK; - di = (struct bfs_inode *)bh->b_data + off; if (ino == BFS_ROOT_INO) di->i_vtype = cpu_to_le32(BFS_VDIR); @@ -158,12 +163,11 @@ static int bfs_write_inode(struct inode *inode, struct writeback_control *wbc) return err; } -static void bfs_delete_inode(struct inode *inode) +static void bfs_evict_inode(struct inode *inode) { unsigned long ino = inode->i_ino; struct bfs_inode *di; struct buffer_head *bh; - int block, off; struct super_block *s = inode->i_sb; struct bfs_sb_info *info = BFS_SB(s); struct bfs_inode_info *bi = BFS_I(inode); @@ -171,28 +175,19 @@ static void bfs_delete_inode(struct inode *inode) dprintf("ino=%08lx\n", ino); truncate_inode_pages(&inode->i_data, 0); + invalidate_inode_buffers(inode); + end_writeback(inode); - if ((ino < BFS_ROOT_INO) || (ino > info->si_lasti)) { - printf("invalid ino=%08lx\n", ino); + if (inode->i_nlink) return; - } - - inode->i_size = 0; - inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC; - mutex_lock(&info->bfs_lock); - mark_inode_dirty(inode); - block = (ino - BFS_ROOT_INO) / BFS_INODES_PER_BLOCK + 1; - bh = sb_bread(s, block); - if (!bh) { - printf("Unable to read inode %s:%08lx\n", - inode->i_sb->s_id, ino); - mutex_unlock(&info->bfs_lock); + di = find_inode(s, inode->i_ino, &bh); + if (IS_ERR(di)) return; - } - off = (ino - BFS_ROOT_INO) % BFS_INODES_PER_BLOCK; - di = (struct bfs_inode *)bh->b_data + off; - memset((void *)di, 0, sizeof(struct bfs_inode)); + + mutex_lock(&info->bfs_lock); + /* clear on-disk inode */ + memset(di, 0, sizeof(struct bfs_inode)); mark_buffer_dirty(bh); brelse(bh); @@ -214,7 +209,6 @@ static void bfs_delete_inode(struct inode *inode) mark_buffer_dirty(info->si_sbh); } mutex_unlock(&info->bfs_lock); - clear_inode(inode); } static int bfs_sync_fs(struct super_block *sb, int wait) @@ -319,7 +313,7 @@ static const struct super_operations bfs_sops = { .alloc_inode = bfs_alloc_inode, .destroy_inode = bfs_destroy_inode, .write_inode = bfs_write_inode, - .delete_inode = bfs_delete_inode, + .evict_inode = bfs_evict_inode, .put_super = bfs_put_super, .write_super = bfs_write_super, .sync_fs = bfs_sync_fs, -- cgit v1.2.3 From 69c9e750176b409559b2361fbb28fa7bbf3c5461 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 6 Jun 2010 10:12:01 -0400 Subject: switch omfs to ->evict_inode() Signed-off-by: Al Viro --- fs/omfs/inode.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/omfs/inode.c b/fs/omfs/inode.c index 089839a6cc64..56121debc22b 100644 --- a/fs/omfs/inode.c +++ b/fs/omfs/inode.c @@ -175,9 +175,13 @@ int omfs_sync_inode(struct inode *inode) * called when an entry is deleted, need to clear the bits in the * bitmaps. */ -static void omfs_delete_inode(struct inode *inode) +static void omfs_evict_inode(struct inode *inode) { truncate_inode_pages(&inode->i_data, 0); + end_writeback(inode); + + if (inode->i_nlink) + return; if (S_ISREG(inode->i_mode)) { inode->i_size = 0; @@ -185,7 +189,6 @@ static void omfs_delete_inode(struct inode *inode) } omfs_clear_range(inode->i_sb, inode->i_ino, 2); - clear_inode(inode); } struct inode *omfs_iget(struct super_block *sb, ino_t ino) @@ -284,7 +287,7 @@ static int omfs_statfs(struct dentry *dentry, struct kstatfs *buf) static const struct super_operations omfs_sops = { .write_inode = omfs_write_inode, - .delete_inode = omfs_delete_inode, + .evict_inode = omfs_evict_inode, .put_super = omfs_put_super, .statfs = omfs_statfs, .show_options = generic_show_options, -- cgit v1.2.3 From f053ddde7575090e09e2f5c4233d8a19f0925b93 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 6 Jun 2010 10:16:41 -0400 Subject: switch affs to ->evict_inode() Signed-off-by: Al Viro --- fs/affs/affs.h | 3 +-- fs/affs/inode.c | 25 ++++++++++++------------- fs/affs/super.c | 3 +-- 3 files changed, 14 insertions(+), 17 deletions(-) (limited to 'fs') diff --git a/fs/affs/affs.h b/fs/affs/affs.h index f05b6155ccc8..a8cbdeb34025 100644 --- a/fs/affs/affs.h +++ b/fs/affs/affs.h @@ -171,8 +171,7 @@ extern int affs_rename(struct inode *old_dir, struct dentry *old_dentry, extern unsigned long affs_parent_ino(struct inode *dir); extern struct inode *affs_new_inode(struct inode *dir); extern int affs_notify_change(struct dentry *dentry, struct iattr *attr); -extern void affs_delete_inode(struct inode *inode); -extern void affs_clear_inode(struct inode *inode); +extern void affs_evict_inode(struct inode *inode); extern struct inode *affs_iget(struct super_block *sb, unsigned long ino); extern int affs_write_inode(struct inode *inode, diff --git a/fs/affs/inode.c b/fs/affs/inode.c index 6883d5fb84cf..3a0fdec175ba 100644 --- a/fs/affs/inode.c +++ b/fs/affs/inode.c @@ -252,23 +252,19 @@ out: } void -affs_delete_inode(struct inode *inode) -{ - pr_debug("AFFS: delete_inode(ino=%lu, nlink=%u)\n", inode->i_ino, inode->i_nlink); - truncate_inode_pages(&inode->i_data, 0); - inode->i_size = 0; - affs_truncate(inode); - clear_inode(inode); - affs_free_block(inode->i_sb, inode->i_ino); -} - -void -affs_clear_inode(struct inode *inode) +affs_evict_inode(struct inode *inode) { unsigned long cache_page; + pr_debug("AFFS: evict_inode(ino=%lu, nlink=%u)\n", inode->i_ino, inode->i_nlink); + truncate_inode_pages(&inode->i_data, 0); - pr_debug("AFFS: clear_inode(ino=%lu, nlink=%u)\n", inode->i_ino, inode->i_nlink); + if (!inode->i_nlink) { + inode->i_size = 0; + affs_truncate(inode); + } + invalidate_inode_buffers(inode); + end_writeback(inode); affs_free_prealloc(inode); cache_page = (unsigned long)AFFS_I(inode)->i_lc; if (cache_page) { @@ -280,6 +276,9 @@ affs_clear_inode(struct inode *inode) affs_brelse(AFFS_I(inode)->i_ext_bh); AFFS_I(inode)->i_ext_last = ~1; AFFS_I(inode)->i_ext_bh = NULL; + + if (!inode->i_nlink) + affs_free_block(inode->i_sb, inode->i_ino); } struct inode * diff --git a/fs/affs/super.c b/fs/affs/super.c index 16a3e4765f68..2c804a87c142 100644 --- a/fs/affs/super.c +++ b/fs/affs/super.c @@ -140,8 +140,7 @@ static const struct super_operations affs_sops = { .alloc_inode = affs_alloc_inode, .destroy_inode = affs_destroy_inode, .write_inode = affs_write_inode, - .delete_inode = affs_delete_inode, - .clear_inode = affs_clear_inode, + .evict_inode = affs_evict_inode, .put_super = affs_put_super, .write_super = affs_write_super, .sync_fs = affs_sync_fs, -- cgit v1.2.3 From e971a6d7b9daebfe2c11c590377d3933410ab929 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 6 Jun 2010 15:16:17 -0400 Subject: stop icache pollution in hostfs, switch to ->evict_inode() Signed-off-by: Al Viro --- fs/hostfs/hostfs_kern.c | 30 ++++++++---------------------- 1 file changed, 8 insertions(+), 22 deletions(-) (limited to 'fs') diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c index 7943ff11d489..fab5f5a1e6f0 100644 --- a/fs/hostfs/hostfs_kern.c +++ b/fs/hostfs/hostfs_kern.c @@ -241,16 +241,13 @@ static struct inode *hostfs_iget(struct super_block *sb) struct inode *inode; long ret; - inode = iget_locked(sb, 0); + inode = new_inode(sb); if (!inode) return ERR_PTR(-ENOMEM); - if (inode->i_state & I_NEW) { - ret = hostfs_read_inode(inode); - if (ret < 0) { - iget_failed(inode); - return ERR_PTR(ret); - } - unlock_new_inode(inode); + ret = hostfs_read_inode(inode); + if (ret < 0) { + iput(inode); + return ERR_PTR(ret); } return inode; } @@ -299,29 +296,19 @@ static struct inode *hostfs_alloc_inode(struct super_block *sb) return &hi->vfs_inode; } -static void hostfs_delete_inode(struct inode *inode) +static void hostfs_evict_inode(struct inode *inode) { truncate_inode_pages(&inode->i_data, 0); + end_writeback(inode); if (HOSTFS_I(inode)->fd != -1) { close_file(&HOSTFS_I(inode)->fd); HOSTFS_I(inode)->fd = -1; } - clear_inode(inode); } static void hostfs_destroy_inode(struct inode *inode) { kfree(HOSTFS_I(inode)->host_filename); - - /* - * XXX: This should not happen, probably. The check is here for - * additional safety. - */ - if (HOSTFS_I(inode)->fd != -1) { - close_file(&HOSTFS_I(inode)->fd); - printk(KERN_DEBUG "Closing host fd in .destroy_inode\n"); - } - kfree(HOSTFS_I(inode)); } @@ -339,9 +326,8 @@ static int hostfs_show_options(struct seq_file *seq, struct vfsmount *vfs) static const struct super_operations hostfs_sbops = { .alloc_inode = hostfs_alloc_inode, - .drop_inode = generic_delete_inode, - .delete_inode = hostfs_delete_inode, .destroy_inode = hostfs_destroy_inode, + .evict_inode = hostfs_evict_inode, .statfs = hostfs_statfs, .show_options = hostfs_show_options, }; -- cgit v1.2.3 From 601d2c38b93130d387091c28d13abea40924e518 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 6 Jun 2010 17:53:01 -0400 Subject: hostfs: don't keep a field in each inode when we are using it only in root Signed-off-by: Al Viro --- fs/hostfs/hostfs_kern.c | 39 +++++++++++++++------------------------ 1 file changed, 15 insertions(+), 24 deletions(-) (limited to 'fs') diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c index fab5f5a1e6f0..7e6750499b8b 100644 --- a/fs/hostfs/hostfs_kern.c +++ b/fs/hostfs/hostfs_kern.c @@ -19,7 +19,6 @@ #include "kern.h" struct hostfs_inode_info { - char *host_filename; int fd; fmode_t mode; struct inode vfs_inode; @@ -103,7 +102,7 @@ static char *dentry_name(struct dentry *dentry, int extra) parent = parent->d_parent; } - root = HOSTFS_I(parent->d_inode)->host_filename; + root = parent->d_sb->s_fs_info; len += strlen(root); name = kmalloc(len + extra + 1, GFP_KERNEL); if (name == NULL) @@ -266,7 +265,7 @@ int hostfs_statfs(struct dentry *dentry, struct kstatfs *sf) long long f_files; long long f_ffree; - err = do_statfs(HOSTFS_I(dentry->d_sb->s_root->d_inode)->host_filename, + err = do_statfs(dentry->d_sb->s_fs_info, &sf->f_bsize, &f_blocks, &f_bfree, &f_bavail, &f_files, &f_ffree, &sf->f_fsid, sizeof(sf->f_fsid), &sf->f_namelen, sf->f_spare); @@ -285,13 +284,10 @@ static struct inode *hostfs_alloc_inode(struct super_block *sb) { struct hostfs_inode_info *hi; - hi = kmalloc(sizeof(*hi), GFP_KERNEL); + hi = kzalloc(sizeof(*hi), GFP_KERNEL); if (hi == NULL) return NULL; - - *hi = ((struct hostfs_inode_info) { .host_filename = NULL, - .fd = -1, - .mode = 0 }); + hi->fd = -1; inode_init_once(&hi->vfs_inode); return &hi->vfs_inode; } @@ -308,14 +304,12 @@ static void hostfs_evict_inode(struct inode *inode) static void hostfs_destroy_inode(struct inode *inode) { - kfree(HOSTFS_I(inode)->host_filename); kfree(HOSTFS_I(inode)); } static int hostfs_show_options(struct seq_file *seq, struct vfsmount *vfs) { - struct inode *root = vfs->mnt_sb->s_root->d_inode; - const char *root_path = HOSTFS_I(root)->host_filename; + const char *root_path = vfs->mnt_sb->s_fs_info; size_t offset = strlen(root_ino) + 1; if (strlen(root_path) > offset) @@ -978,8 +972,8 @@ static int hostfs_fill_sb_common(struct super_block *sb, void *d, int silent) req_root = ""; err = -ENOMEM; - host_root_path = kmalloc(strlen(root_ino) + 1 - + strlen(req_root) + 1, GFP_KERNEL); + sb->s_fs_info = host_root_path = + kmalloc(strlen(root_ino) + strlen(req_root) + 2, GFP_KERNEL); if (host_root_path == NULL) goto out; @@ -988,20 +982,13 @@ static int hostfs_fill_sb_common(struct super_block *sb, void *d, int silent) root_inode = hostfs_iget(sb); if (IS_ERR(root_inode)) { err = PTR_ERR(root_inode); - goto out_free; + goto out; } err = init_inode(root_inode, NULL); if (err) goto out_put; - HOSTFS_I(root_inode)->host_filename = host_root_path; - /* - * Avoid that in the error path, iput(root_inode) frees again - * host_root_path through hostfs_destroy_inode! - */ - host_root_path = NULL; - err = -ENOMEM; sb->s_root = d_alloc_root(root_inode); if (sb->s_root == NULL) @@ -1019,8 +1006,6 @@ static int hostfs_fill_sb_common(struct super_block *sb, void *d, int silent) out_put: iput(root_inode); -out_free: - kfree(host_root_path); out: return err; } @@ -1032,11 +1017,17 @@ static int hostfs_read_sb(struct file_system_type *type, return get_sb_nodev(type, flags, data, hostfs_fill_sb_common, mnt); } +static void hostfs_kill_sb(struct super_block *s) +{ + kill_anon_super(s); + kfree(s->s_fs_info); +} + static struct file_system_type hostfs_type = { .owner = THIS_MODULE, .name = "hostfs", .get_sb = hostfs_read_sb, - .kill_sb = kill_anon_super, + .kill_sb = hostfs_kill_sb, .fs_flags = 0, }; -- cgit v1.2.3 From 52b209f7b848a28987ed133dc2b48f304b1dc6b8 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 6 Jun 2010 18:43:19 -0400 Subject: get rid of hostfs_read_inode() There are only two call sites; in one (hostfs_iget()) it's actually a no-op and in another (fill_super()) it's easier to expand the damn thing and use what we know about its arguments to simplify it. Signed-off-by: Al Viro --- fs/hostfs/hostfs_kern.c | 69 ++++++++++++------------------------------------- 1 file changed, 16 insertions(+), 53 deletions(-) (limited to 'fs') diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c index 7e6750499b8b..25d79298a98e 100644 --- a/fs/hostfs/hostfs_kern.c +++ b/fs/hostfs/hostfs_kern.c @@ -204,50 +204,11 @@ static char *follow_link(char *link) return ERR_PTR(n); } -static int hostfs_read_inode(struct inode *ino) -{ - char *name; - int err = 0; - - /* - * Unfortunately, we are called from iget() when we don't have a dentry - * allocated yet. - */ - if (list_empty(&ino->i_dentry)) - goto out; - - err = -ENOMEM; - name = inode_name(ino, 0); - if (name == NULL) - goto out; - - if (file_type(name, NULL, NULL) == OS_TYPE_SYMLINK) { - name = follow_link(name); - if (IS_ERR(name)) { - err = PTR_ERR(name); - goto out; - } - } - - err = read_name(ino, name); - kfree(name); - out: - return err; -} - static struct inode *hostfs_iget(struct super_block *sb) { - struct inode *inode; - long ret; - - inode = new_inode(sb); + struct inode *inode = new_inode(sb); if (!inode) return ERR_PTR(-ENOMEM); - ret = hostfs_read_inode(inode); - if (ret < 0) { - iput(inode); - return ERR_PTR(ret); - } return inode; } @@ -979,13 +940,23 @@ static int hostfs_fill_sb_common(struct super_block *sb, void *d, int silent) sprintf(host_root_path, "%s/%s", root_ino, req_root); - root_inode = hostfs_iget(sb); - if (IS_ERR(root_inode)) { - err = PTR_ERR(root_inode); + root_inode = new_inode(sb); + if (!root_inode) goto out; - } - err = init_inode(root_inode, NULL); + root_inode->i_op = &hostfs_dir_iops; + root_inode->i_fop = &hostfs_dir_fops; + + if (file_type(host_root_path, NULL, NULL) == OS_TYPE_SYMLINK) { + char *name = follow_link(host_root_path); + if (IS_ERR(name)) + err = PTR_ERR(name); + else + err = read_name(root_inode, name); + kfree(name); + } else { + err = read_name(root_inode, host_root_path); + } if (err) goto out_put; @@ -994,14 +965,6 @@ static int hostfs_fill_sb_common(struct super_block *sb, void *d, int silent) if (sb->s_root == NULL) goto out_put; - err = hostfs_read_inode(root_inode); - if (err) { - /* No iput in this case because the dput does that for us */ - dput(sb->s_root); - sb->s_root = NULL; - goto out; - } - return 0; out_put: -- cgit v1.2.3 From 5e2df28cc62fdc3f4900de23f4ec69e6312f78a4 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 6 Jun 2010 19:38:18 -0400 Subject: hostfs: pass pathname to init_inode() We will calculate it in all callers anyway, so there's no need to duplicate that inside. Moreover, that way we lose all failure exits in init_inode(), so it doesn't need to return anything. Signed-off-by: Al Viro --- fs/hostfs/hostfs_kern.c | 45 +++++++++++++++------------------------------ 1 file changed, 15 insertions(+), 30 deletions(-) (limited to 'fs') diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c index 25d79298a98e..5a77ed3dfd7e 100644 --- a/fs/hostfs/hostfs_kern.c +++ b/fs/hostfs/hostfs_kern.c @@ -485,25 +485,16 @@ static const struct address_space_operations hostfs_aops = { .write_end = hostfs_write_end, }; -static int init_inode(struct inode *inode, struct dentry *dentry) +static void init_inode(struct inode *inode, char *path) { - char *name; - int type, err = -ENOMEM; + int type; int maj, min; dev_t rdev = 0; - if (dentry) { - name = dentry_name(dentry, 0); - if (name == NULL) - goto out; - type = file_type(name, &maj, &min); - /* Reencode maj and min with the kernel encoding.*/ - rdev = MKDEV(maj, min); - kfree(name); - } - else type = OS_TYPE_DIR; + type = file_type(path, &maj, &min); + /* Reencode maj and min with the kernel encoding.*/ + rdev = MKDEV(maj, min); - err = 0; if (type == OS_TYPE_SYMLINK) inode->i_op = &page_symlink_inode_operations; else if (type == OS_TYPE_DIR) @@ -531,8 +522,6 @@ static int init_inode(struct inode *inode, struct dentry *dentry) init_special_inode(inode, S_IFSOCK, 0); break; } - out: - return err; } int hostfs_create(struct inode *dir, struct dentry *dentry, int mode, @@ -548,10 +537,6 @@ int hostfs_create(struct inode *dir, struct dentry *dentry, int mode, goto out; } - error = init_inode(inode, dentry); - if (error) - goto out_put; - error = -ENOMEM; name = dentry_name(dentry, 0); if (name == NULL) @@ -561,9 +546,12 @@ int hostfs_create(struct inode *dir, struct dentry *dentry, int mode, mode & S_IRUSR, mode & S_IWUSR, mode & S_IXUSR, mode & S_IRGRP, mode & S_IWGRP, mode & S_IXGRP, mode & S_IROTH, mode & S_IWOTH, mode & S_IXOTH); - if (fd < 0) + if (fd < 0) { error = fd; - else error = read_name(inode, name); + } else { + error = read_name(inode, name); + init_inode(inode, name); + } kfree(name); if (error) @@ -593,16 +581,14 @@ struct dentry *hostfs_lookup(struct inode *ino, struct dentry *dentry, goto out; } - err = init_inode(inode, dentry); - if (err) - goto out_put; - err = -ENOMEM; name = dentry_name(dentry, 0); if (name == NULL) goto out_put; err = read_name(inode, name); + init_inode(inode, name); + kfree(name); if (err == -ENOENT) { iput(inode); @@ -717,10 +703,6 @@ int hostfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev) goto out; } - err = init_inode(inode, dentry); - if (err) - goto out_put; - err = -ENOMEM; name = dentry_name(dentry, 0); if (name == NULL) @@ -732,6 +714,9 @@ int hostfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev) goto out_free; err = read_name(inode, name); + init_inode(inode, name); + if (err) + goto out_put; kfree(name); if (err) goto out_put; -- cgit v1.2.3 From 39b743c6199a317ffac67fcae1dd05be3142633a Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 6 Jun 2010 20:08:56 -0400 Subject: switch stat_file() to passing a single struct rather than fsckloads of pointers Signed-off-by: Al Viro --- fs/hostfs/hostfs.h | 20 +++++++++---- fs/hostfs/hostfs_kern.c | 31 ++++++++------------ fs/hostfs/hostfs_user.c | 75 ++++++++++++++++++++----------------------------- 3 files changed, 58 insertions(+), 68 deletions(-) (limited to 'fs') diff --git a/fs/hostfs/hostfs.h b/fs/hostfs/hostfs.h index 2f34f8f2134b..3a52edef9948 100644 --- a/fs/hostfs/hostfs.h +++ b/fs/hostfs/hostfs.h @@ -53,11 +53,21 @@ struct hostfs_iattr { struct timespec ia_ctime; }; -extern int stat_file(const char *path, unsigned long long *inode_out, - int *mode_out, int *nlink_out, int *uid_out, int *gid_out, - unsigned long long *size_out, struct timespec *atime_out, - struct timespec *mtime_out, struct timespec *ctime_out, - int *blksize_out, unsigned long long *blocks_out, int fd); +struct hostfs_stat { + unsigned long long ino; + unsigned int mode; + unsigned int nlink; + unsigned int uid; + unsigned int gid; + unsigned long long size; + struct timespec atime, mtime, ctime; + unsigned int blksize; + unsigned long long blocks; + unsigned int maj; + unsigned int min; +}; + +extern int stat_file(const char *path, struct hostfs_stat *p, int fd); extern int access_file(char *path, int r, int w, int x); extern int open_file(char *path, int r, int w, int append); extern int file_type(const char *path, int *maj, int *min); diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c index 5a77ed3dfd7e..420a826ae0f2 100644 --- a/fs/hostfs/hostfs_kern.c +++ b/fs/hostfs/hostfs_kern.c @@ -131,28 +131,21 @@ static char *inode_name(struct inode *ino, int extra) static int read_name(struct inode *ino, char *name) { - /* - * The non-int inode fields are copied into ints by stat_file and - * then copied into the inode because passing the actual pointers - * in and having them treated as int * breaks on big-endian machines - */ - int err; - int i_mode, i_nlink, i_blksize; - unsigned long long i_size; - unsigned long long i_ino; - unsigned long long i_blocks; - - err = stat_file(name, &i_ino, &i_mode, &i_nlink, &ino->i_uid, - &ino->i_gid, &i_size, &ino->i_atime, &ino->i_mtime, - &ino->i_ctime, &i_blksize, &i_blocks, -1); + struct hostfs_stat st; + int err = stat_file(name, &st, -1); if (err) return err; - ino->i_ino = i_ino; - ino->i_mode = i_mode; - ino->i_nlink = i_nlink; - ino->i_size = i_size; - ino->i_blocks = i_blocks; + ino->i_ino = st.ino; + ino->i_mode = st.mode; + ino->i_nlink = st.nlink; + ino->i_uid = st.uid; + ino->i_gid = st.gid; + ino->i_atime = st.atime; + ino->i_mtime = st.mtime; + ino->i_ctime = st.ctime; + ino->i_size = st.size; + ino->i_blocks = st.blocks; return 0; } diff --git a/fs/hostfs/hostfs_user.c b/fs/hostfs/hostfs_user.c index 4b8c666ba28f..701d454a6791 100644 --- a/fs/hostfs/hostfs_user.c +++ b/fs/hostfs/hostfs_user.c @@ -19,11 +19,27 @@ #include "user.h" #include -int stat_file(const char *path, unsigned long long *inode_out, int *mode_out, - int *nlink_out, int *uid_out, int *gid_out, - unsigned long long *size_out, struct timespec *atime_out, - struct timespec *mtime_out, struct timespec *ctime_out, - int *blksize_out, unsigned long long *blocks_out, int fd) +static void stat64_to_hostfs(const struct stat64 *buf, struct hostfs_stat *p) +{ + p->ino = buf->st_ino; + p->mode = buf->st_mode; + p->nlink = buf->st_nlink; + p->uid = buf->st_uid; + p->gid = buf->st_gid; + p->size = buf->st_size; + p->atime.tv_sec = buf->st_atime; + p->atime.tv_nsec = 0; + p->ctime.tv_sec = buf->st_ctime; + p->ctime.tv_nsec = 0; + p->mtime.tv_sec = buf->st_mtime; + p->mtime.tv_nsec = 0; + p->blksize = buf->st_blksize; + p->blocks = buf->st_blocks; + p->maj = os_major(buf->st_rdev); + p->min = os_minor(buf->st_rdev); +} + +int stat_file(const char *path, struct hostfs_stat *p, int fd) { struct stat64 buf; @@ -33,35 +49,7 @@ int stat_file(const char *path, unsigned long long *inode_out, int *mode_out, } else if (lstat64(path, &buf) < 0) { return -errno; } - - if (inode_out != NULL) - *inode_out = buf.st_ino; - if (mode_out != NULL) - *mode_out = buf.st_mode; - if (nlink_out != NULL) - *nlink_out = buf.st_nlink; - if (uid_out != NULL) - *uid_out = buf.st_uid; - if (gid_out != NULL) - *gid_out = buf.st_gid; - if (size_out != NULL) - *size_out = buf.st_size; - if (atime_out != NULL) { - atime_out->tv_sec = buf.st_atime; - atime_out->tv_nsec = 0; - } - if (mtime_out != NULL) { - mtime_out->tv_sec = buf.st_mtime; - mtime_out->tv_nsec = 0; - } - if (ctime_out != NULL) { - ctime_out->tv_sec = buf.st_ctime; - ctime_out->tv_nsec = 0; - } - if (blksize_out != NULL) - *blksize_out = buf.st_blksize; - if (blocks_out != NULL) - *blocks_out = buf.st_blocks; + stat64_to_hostfs(&buf, p); return 0; } @@ -235,8 +223,8 @@ int file_create(char *name, int ur, int uw, int ux, int gr, int set_attr(const char *file, struct hostfs_iattr *attrs, int fd) { + struct hostfs_stat st; struct timeval times[2]; - struct timespec atime_ts, mtime_ts; int err, ma; if (attrs->ia_valid & HOSTFS_ATTR_MODE) { @@ -279,15 +267,14 @@ int set_attr(const char *file, struct hostfs_iattr *attrs, int fd) */ ma = (HOSTFS_ATTR_ATIME_SET | HOSTFS_ATTR_MTIME_SET); if (attrs->ia_valid & ma) { - err = stat_file(file, NULL, NULL, NULL, NULL, NULL, NULL, - &atime_ts, &mtime_ts, NULL, NULL, NULL, fd); + err = stat_file(file, &st, fd); if (err != 0) return err; - times[0].tv_sec = atime_ts.tv_sec; - times[0].tv_usec = atime_ts.tv_nsec / 1000; - times[1].tv_sec = mtime_ts.tv_sec; - times[1].tv_usec = mtime_ts.tv_nsec / 1000; + times[0].tv_sec = st.atime.tv_sec; + times[0].tv_usec = st.atime.tv_nsec / 1000; + times[1].tv_sec = st.mtime.tv_sec; + times[1].tv_usec = st.mtime.tv_nsec / 1000; if (attrs->ia_valid & HOSTFS_ATTR_ATIME_SET) { times[0].tv_sec = attrs->ia_atime.tv_sec; @@ -308,9 +295,9 @@ int set_attr(const char *file, struct hostfs_iattr *attrs, int fd) /* Note: ctime is not handled */ if (attrs->ia_valid & (HOSTFS_ATTR_ATIME | HOSTFS_ATTR_MTIME)) { - err = stat_file(file, NULL, NULL, NULL, NULL, NULL, NULL, - &attrs->ia_atime, &attrs->ia_mtime, NULL, - NULL, NULL, fd); + err = stat_file(file, &st, fd); + attrs->ia_atime = st.atime; + attrs->ia_mtime = st.mtime; if (err != 0) return err; } -- cgit v1.2.3 From 4754b825571a6f2f7655245e420e8e486c4458f6 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 6 Jun 2010 20:33:12 -0400 Subject: hostfs: get rid of file_type(), fold init_inode() Signed-off-by: Al Viro --- fs/hostfs/hostfs.h | 1 - fs/hostfs/hostfs_kern.c | 107 ++++++++++++++++++++---------------------------- fs/hostfs/hostfs_user.c | 30 -------------- 3 files changed, 45 insertions(+), 93 deletions(-) (limited to 'fs') diff --git a/fs/hostfs/hostfs.h b/fs/hostfs/hostfs.h index 3a52edef9948..ea87e224ed97 100644 --- a/fs/hostfs/hostfs.h +++ b/fs/hostfs/hostfs.h @@ -70,7 +70,6 @@ struct hostfs_stat { extern int stat_file(const char *path, struct hostfs_stat *p, int fd); extern int access_file(char *path, int r, int w, int x); extern int open_file(char *path, int r, int w, int append); -extern int file_type(const char *path, int *maj, int *min); extern void *open_dir(char *path, int *err_out); extern char *read_dir(void *stream, unsigned long long *pos, unsigned long long *ino_out, int *len_out); diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c index 420a826ae0f2..b29a2b878f46 100644 --- a/fs/hostfs/hostfs_kern.c +++ b/fs/hostfs/hostfs_kern.c @@ -129,26 +129,6 @@ static char *inode_name(struct inode *ino, int extra) return dentry_name(dentry, extra); } -static int read_name(struct inode *ino, char *name) -{ - struct hostfs_stat st; - int err = stat_file(name, &st, -1); - if (err) - return err; - - ino->i_ino = st.ino; - ino->i_mode = st.mode; - ino->i_nlink = st.nlink; - ino->i_uid = st.uid; - ino->i_gid = st.gid; - ino->i_atime = st.atime; - ino->i_mtime = st.mtime; - ino->i_ctime = st.ctime; - ino->i_size = st.size; - ino->i_blocks = st.blocks; - return 0; -} - static char *follow_link(char *link) { int len, n; @@ -478,43 +458,51 @@ static const struct address_space_operations hostfs_aops = { .write_end = hostfs_write_end, }; -static void init_inode(struct inode *inode, char *path) +static int read_name(struct inode *ino, char *name) { - int type; - int maj, min; - dev_t rdev = 0; + dev_t rdev; + struct hostfs_stat st; + int err = stat_file(name, &st, -1); + if (err) + return err; - type = file_type(path, &maj, &min); /* Reencode maj and min with the kernel encoding.*/ - rdev = MKDEV(maj, min); + rdev = MKDEV(st.maj, st.min); - if (type == OS_TYPE_SYMLINK) - inode->i_op = &page_symlink_inode_operations; - else if (type == OS_TYPE_DIR) - inode->i_op = &hostfs_dir_iops; - else inode->i_op = &hostfs_iops; - - if (type == OS_TYPE_DIR) inode->i_fop = &hostfs_dir_fops; - else inode->i_fop = &hostfs_file_fops; - - if (type == OS_TYPE_SYMLINK) - inode->i_mapping->a_ops = &hostfs_link_aops; - else inode->i_mapping->a_ops = &hostfs_aops; - - switch (type) { - case OS_TYPE_CHARDEV: - init_special_inode(inode, S_IFCHR, rdev); - break; - case OS_TYPE_BLOCKDEV: - init_special_inode(inode, S_IFBLK, rdev); + switch (st.mode & S_IFMT) { + case S_IFLNK: + ino->i_op = &page_symlink_inode_operations; + ino->i_mapping->a_ops = &hostfs_link_aops; break; - case OS_TYPE_FIFO: - init_special_inode(inode, S_IFIFO, 0); + case S_IFDIR: + ino->i_op = &hostfs_dir_iops; + ino->i_fop = &hostfs_dir_fops; break; - case OS_TYPE_SOCK: - init_special_inode(inode, S_IFSOCK, 0); + case S_IFCHR: + case S_IFBLK: + case S_IFIFO: + case S_IFSOCK: + init_special_inode(ino, st.mode & S_IFMT, rdev); + ino->i_op = &hostfs_iops; break; + + default: + ino->i_op = &hostfs_iops; + ino->i_fop = &hostfs_file_fops; + ino->i_mapping->a_ops = &hostfs_aops; } + + ino->i_ino = st.ino; + ino->i_mode = st.mode; + ino->i_nlink = st.nlink; + ino->i_uid = st.uid; + ino->i_gid = st.gid; + ino->i_atime = st.atime; + ino->i_mtime = st.mtime; + ino->i_ctime = st.ctime; + ino->i_size = st.size; + ino->i_blocks = st.blocks; + return 0; } int hostfs_create(struct inode *dir, struct dentry *dentry, int mode, @@ -539,12 +527,10 @@ int hostfs_create(struct inode *dir, struct dentry *dentry, int mode, mode & S_IRUSR, mode & S_IWUSR, mode & S_IXUSR, mode & S_IRGRP, mode & S_IWGRP, mode & S_IXGRP, mode & S_IROTH, mode & S_IWOTH, mode & S_IXOTH); - if (fd < 0) { + if (fd < 0) error = fd; - } else { + else error = read_name(inode, name); - init_inode(inode, name); - } kfree(name); if (error) @@ -580,7 +566,6 @@ struct dentry *hostfs_lookup(struct inode *ino, struct dentry *dentry, goto out_put; err = read_name(inode, name); - init_inode(inode, name); kfree(name); if (err == -ENOENT) { @@ -707,7 +692,6 @@ int hostfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev) goto out_free; err = read_name(inode, name); - init_inode(inode, name); if (err) goto out_put; kfree(name); @@ -922,21 +906,20 @@ static int hostfs_fill_sb_common(struct super_block *sb, void *d, int silent) if (!root_inode) goto out; - root_inode->i_op = &hostfs_dir_iops; - root_inode->i_fop = &hostfs_dir_fops; + err = read_name(root_inode, host_root_path); + if (err) + goto out_put; - if (file_type(host_root_path, NULL, NULL) == OS_TYPE_SYMLINK) { + if (S_ISLNK(root_inode->i_mode)) { char *name = follow_link(host_root_path); if (IS_ERR(name)) err = PTR_ERR(name); else err = read_name(root_inode, name); kfree(name); - } else { - err = read_name(root_inode, host_root_path); + if (err) + goto out_put; } - if (err) - goto out_put; err = -ENOMEM; sb->s_root = d_alloc_root(root_inode); diff --git a/fs/hostfs/hostfs_user.c b/fs/hostfs/hostfs_user.c index 701d454a6791..91ebfcefa409 100644 --- a/fs/hostfs/hostfs_user.c +++ b/fs/hostfs/hostfs_user.c @@ -53,36 +53,6 @@ int stat_file(const char *path, struct hostfs_stat *p, int fd) return 0; } -int file_type(const char *path, int *maj, int *min) -{ - struct stat64 buf; - - if (lstat64(path, &buf) < 0) - return -errno; - /* - * We cannot pass rdev as is because glibc and the kernel disagree - * about its definition. - */ - if (maj != NULL) - *maj = os_major(buf.st_rdev); - if (min != NULL) - *min = os_minor(buf.st_rdev); - - if (S_ISDIR(buf.st_mode)) - return OS_TYPE_DIR; - else if (S_ISLNK(buf.st_mode)) - return OS_TYPE_SYMLINK; - else if (S_ISCHR(buf.st_mode)) - return OS_TYPE_CHARDEV; - else if (S_ISBLK(buf.st_mode)) - return OS_TYPE_BLOCKDEV; - else if (S_ISFIFO(buf.st_mode)) - return OS_TYPE_FIFO; - else if (S_ISSOCK(buf.st_mode)) - return OS_TYPE_SOCK; - else return OS_TYPE_FILE; -} - int access_file(char *path, int r, int w, int x) { int mode = 0; -- cgit v1.2.3 From c5322220eb91b9e56ac7b69eb690d9d20fac5725 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 6 Jun 2010 20:42:10 -0400 Subject: hostfs: get rid of inode_dentry_name() it's equivalent to dentry_name() anyway Signed-off-by: Al Viro --- fs/hostfs/hostfs_kern.c | 55 ++++++++++++++++++------------------------------- 1 file changed, 20 insertions(+), 35 deletions(-) (limited to 'fs') diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c index b29a2b878f46..3841fb1ca5a2 100644 --- a/fs/hostfs/hostfs_kern.c +++ b/fs/hostfs/hostfs_kern.c @@ -89,7 +89,7 @@ __uml_setup("hostfs=", hostfs_args, ); #endif -static char *dentry_name(struct dentry *dentry, int extra) +static char *dentry_name(struct dentry *dentry) { struct dentry *parent; char *root, *name; @@ -104,7 +104,7 @@ static char *dentry_name(struct dentry *dentry, int extra) root = parent->d_sb->s_fs_info; len += strlen(root); - name = kmalloc(len + extra + 1, GFP_KERNEL); + name = kmalloc(len + 1, GFP_KERNEL); if (name == NULL) return NULL; @@ -121,12 +121,12 @@ static char *dentry_name(struct dentry *dentry, int extra) return name; } -static char *inode_name(struct inode *ino, int extra) +static char *inode_name(struct inode *ino) { struct dentry *dentry; dentry = list_entry(ino->i_dentry.next, struct dentry, d_alias); - return dentry_name(dentry, extra); + return dentry_name(dentry); } static char *follow_link(char *link) @@ -267,7 +267,7 @@ int hostfs_readdir(struct file *file, void *ent, filldir_t filldir) unsigned long long next, ino; int error, len; - name = dentry_name(file->f_path.dentry, 0); + name = dentry_name(file->f_path.dentry); if (name == NULL) return -ENOMEM; dir = open_dir(name, &error); @@ -312,7 +312,7 @@ int hostfs_file_open(struct inode *ino, struct file *file) if (w) r = 1; - name = dentry_name(file->f_path.dentry, 0); + name = dentry_name(file->f_path.dentry); if (name == NULL) return -ENOMEM; @@ -519,7 +519,7 @@ int hostfs_create(struct inode *dir, struct dentry *dentry, int mode, } error = -ENOMEM; - name = dentry_name(dentry, 0); + name = dentry_name(dentry); if (name == NULL) goto out_put; @@ -561,7 +561,7 @@ struct dentry *hostfs_lookup(struct inode *ino, struct dentry *dentry, } err = -ENOMEM; - name = dentry_name(dentry, 0); + name = dentry_name(dentry); if (name == NULL) goto out_put; @@ -585,29 +585,14 @@ struct dentry *hostfs_lookup(struct inode *ino, struct dentry *dentry, return ERR_PTR(err); } -static char *inode_dentry_name(struct inode *ino, struct dentry *dentry) -{ - char *file; - int len; - - file = inode_name(ino, dentry->d_name.len + 1); - if (file == NULL) - return NULL; - strcat(file, "/"); - len = strlen(file); - strncat(file, dentry->d_name.name, dentry->d_name.len); - file[len + dentry->d_name.len] = '\0'; - return file; -} - int hostfs_link(struct dentry *to, struct inode *ino, struct dentry *from) { char *from_name, *to_name; int err; - if ((from_name = inode_dentry_name(ino, from)) == NULL) + if ((from_name = dentry_name(from)) == NULL) return -ENOMEM; - to_name = dentry_name(to, 0); + to_name = dentry_name(to); if (to_name == NULL) { kfree(from_name); return -ENOMEM; @@ -623,7 +608,7 @@ int hostfs_unlink(struct inode *ino, struct dentry *dentry) char *file; int err; - if ((file = inode_dentry_name(ino, dentry)) == NULL) + if ((file = dentry_name(dentry)) == NULL) return -ENOMEM; if (append) return -EPERM; @@ -638,7 +623,7 @@ int hostfs_symlink(struct inode *ino, struct dentry *dentry, const char *to) char *file; int err; - if ((file = inode_dentry_name(ino, dentry)) == NULL) + if ((file = dentry_name(dentry)) == NULL) return -ENOMEM; err = make_symlink(file, to); kfree(file); @@ -650,7 +635,7 @@ int hostfs_mkdir(struct inode *ino, struct dentry *dentry, int mode) char *file; int err; - if ((file = inode_dentry_name(ino, dentry)) == NULL) + if ((file = dentry_name(dentry)) == NULL) return -ENOMEM; err = do_mkdir(file, mode); kfree(file); @@ -662,7 +647,7 @@ int hostfs_rmdir(struct inode *ino, struct dentry *dentry) char *file; int err; - if ((file = inode_dentry_name(ino, dentry)) == NULL) + if ((file = dentry_name(dentry)) == NULL) return -ENOMEM; err = do_rmdir(file); kfree(file); @@ -682,7 +667,7 @@ int hostfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev) } err = -ENOMEM; - name = dentry_name(dentry, 0); + name = dentry_name(dentry); if (name == NULL) goto out_put; @@ -715,9 +700,9 @@ int hostfs_rename(struct inode *from_ino, struct dentry *from, char *from_name, *to_name; int err; - if ((from_name = inode_dentry_name(from_ino, from)) == NULL) + if ((from_name = dentry_name(from)) == NULL) return -ENOMEM; - if ((to_name = inode_dentry_name(to_ino, to)) == NULL) { + if ((to_name = dentry_name(to)) == NULL) { kfree(from_name); return -ENOMEM; } @@ -735,7 +720,7 @@ int hostfs_permission(struct inode *ino, int desired) if (desired & MAY_READ) r = 1; if (desired & MAY_WRITE) w = 1; if (desired & MAY_EXEC) x = 1; - name = inode_name(ino, 0); + name = inode_name(ino); if (name == NULL) return -ENOMEM; @@ -801,7 +786,7 @@ int hostfs_setattr(struct dentry *dentry, struct iattr *attr) if (attr->ia_valid & ATTR_MTIME_SET) { attrs.ia_valid |= HOSTFS_ATTR_MTIME_SET; } - name = dentry_name(dentry, 0); + name = dentry_name(dentry); if (name == NULL) return -ENOMEM; err = set_attr(name, &attrs, fd); @@ -856,7 +841,7 @@ int hostfs_link_readpage(struct file *file, struct page *page) int err; buffer = kmap(page); - name = inode_name(page->mapping->host, 0); + name = inode_name(page->mapping->host); if (name == NULL) return -ENOMEM; err = hostfs_do_readlink(name, buffer, PAGE_CACHE_SIZE); -- cgit v1.2.3 From d0352d3ed722b134dacc21836c1763e7e3523662 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 6 Jun 2010 21:51:16 -0400 Subject: hostfs: sanitize symlinks Signed-off-by: Al Viro --- fs/hostfs/hostfs_kern.c | 61 ++++++++++++++++++++++++++++--------------------- 1 file changed, 35 insertions(+), 26 deletions(-) (limited to 'fs') diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c index 3841fb1ca5a2..10bb71b1548b 100644 --- a/fs/hostfs/hostfs_kern.c +++ b/fs/hostfs/hostfs_kern.c @@ -14,6 +14,7 @@ #include #include #include +#include #include "hostfs.h" #include "init.h" #include "kern.h" @@ -48,7 +49,7 @@ static int append = 0; static const struct inode_operations hostfs_iops; static const struct inode_operations hostfs_dir_iops; -static const struct address_space_operations hostfs_link_aops; +static const struct inode_operations hostfs_link_iops; #ifndef MODULE static int __init hostfs_args(char *options, int *add) @@ -471,8 +472,7 @@ static int read_name(struct inode *ino, char *name) switch (st.mode & S_IFMT) { case S_IFLNK: - ino->i_op = &page_symlink_inode_operations; - ino->i_mapping->a_ops = &hostfs_link_aops; + ino->i_op = &hostfs_link_iops; break; case S_IFDIR: ino->i_op = &hostfs_dir_iops; @@ -835,32 +835,41 @@ static const struct inode_operations hostfs_dir_iops = { .setattr = hostfs_setattr, }; -int hostfs_link_readpage(struct file *file, struct page *page) -{ - char *buffer, *name; - int err; - - buffer = kmap(page); - name = inode_name(page->mapping->host); - if (name == NULL) - return -ENOMEM; - err = hostfs_do_readlink(name, buffer, PAGE_CACHE_SIZE); - kfree(name); - if (err == PAGE_CACHE_SIZE) - err = -E2BIG; - else if (err > 0) { - flush_dcache_page(page); - SetPageUptodate(page); - if (PageError(page)) ClearPageError(page); - err = 0; +static void *hostfs_follow_link(struct dentry *dentry, struct nameidata *nd) +{ + char *link = __getname(); + if (link) { + char *path = dentry_name(dentry); + int err = -ENOMEM; + if (path) { + int err = hostfs_do_readlink(path, link, PATH_MAX); + if (err == PATH_MAX) + err = -E2BIG; + kfree(path); + } + if (err < 0) { + __putname(link); + link = ERR_PTR(err); + } + } else { + link = ERR_PTR(-ENOMEM); } - kunmap(page); - unlock_page(page); - return err; + + nd_set_link(nd, link); + return NULL; +} + +static void hostfs_put_link(struct dentry *dentry, struct nameidata *nd, void *cookie) +{ + char *s = nd_get_link(nd); + if (!IS_ERR(s)) + __putname(s); } -static const struct address_space_operations hostfs_link_aops = { - .readpage = hostfs_link_readpage, +static const struct inode_operations hostfs_link_iops = { + .readlink = generic_readlink, + .follow_link = hostfs_follow_link, + .put_link = hostfs_put_link, }; static int hostfs_fill_sb_common(struct super_block *sb, void *d, int silent) -- cgit v1.2.3 From c103135c14e03fc9a9e5f0adc01df9ad272cf2a1 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 6 Jun 2010 22:31:14 -0400 Subject: new helper: __dentry_path() builds path relative to fs root, called under dcache_lock, doesn't append any nonsense to unlinked ones. Signed-off-by: Al Viro --- fs/dcache.c | 27 ++++++++++++++++++++++----- include/linux/dcache.h | 1 + 2 files changed, 23 insertions(+), 5 deletions(-) (limited to 'fs') diff --git a/fs/dcache.c b/fs/dcache.c index 86d4db15473e..caf08574982f 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -2049,16 +2049,12 @@ char *dynamic_dname(struct dentry *dentry, char *buffer, int buflen, /* * Write full pathname from the root of the filesystem into the buffer. */ -char *dentry_path(struct dentry *dentry, char *buf, int buflen) +char *__dentry_path(struct dentry *dentry, char *buf, int buflen) { char *end = buf + buflen; char *retval; - spin_lock(&dcache_lock); prepend(&end, &buflen, "\0", 1); - if (d_unlinked(dentry) && - (prepend(&end, &buflen, "//deleted", 9) != 0)) - goto Elong; if (buflen < 1) goto Elong; /* Get '/' right */ @@ -2076,7 +2072,28 @@ char *dentry_path(struct dentry *dentry, char *buf, int buflen) retval = end; dentry = parent; } + return retval; +Elong: + return ERR_PTR(-ENAMETOOLONG); +} +EXPORT_SYMBOL(__dentry_path); + +char *dentry_path(struct dentry *dentry, char *buf, int buflen) +{ + char *p = NULL; + char *retval; + + spin_lock(&dcache_lock); + if (d_unlinked(dentry)) { + p = buf + buflen; + if (prepend(&p, &buflen, "//deleted", 10) != 0) + goto Elong; + buflen++; + } + retval = __dentry_path(dentry, buf, buflen); spin_unlock(&dcache_lock); + if (!IS_ERR(retval) && p) + *p = '/'; /* restore '/' overriden with '\0' */ return retval; Elong: spin_unlock(&dcache_lock); diff --git a/include/linux/dcache.h b/include/linux/dcache.h index eebb617c17d8..d23be0386e2d 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -315,6 +315,7 @@ extern char *dynamic_dname(struct dentry *, char *, int, const char *, ...); extern char *__d_path(const struct path *path, struct path *root, char *, int); extern char *d_path(const struct path *, char *, int); +extern char *__dentry_path(struct dentry *, char *, int); extern char *dentry_path(struct dentry *, char *, int); /* Allocation counts.. */ -- cgit v1.2.3 From e9193059b1b3733695d5b80e667778311695aa73 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 6 Jun 2010 23:16:34 -0400 Subject: hostfs: fix races in dentry_name() and inode_name() calculating size, then doing allocation, then filling the path is a Bad Idea(tm), since the ancestors can be renamed, leading to buffer overrun. Signed-off-by: Al Viro --- fs/hostfs/hostfs_kern.c | 106 +++++++++++++++++++++++++++--------------------- 1 file changed, 60 insertions(+), 46 deletions(-) (limited to 'fs') diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c index 10bb71b1548b..79783a0b2f4d 100644 --- a/fs/hostfs/hostfs_kern.c +++ b/fs/hostfs/hostfs_kern.c @@ -90,44 +90,58 @@ __uml_setup("hostfs=", hostfs_args, ); #endif -static char *dentry_name(struct dentry *dentry) +static char *__dentry_name(struct dentry *dentry, char *name) { - struct dentry *parent; - char *root, *name; - int len; + char *p = __dentry_path(dentry, name, PATH_MAX); + char *root; + size_t len; - len = 0; - parent = dentry; - while (parent->d_parent != parent) { - len += parent->d_name.len + 1; - parent = parent->d_parent; - } + spin_unlock(&dcache_lock); - root = parent->d_sb->s_fs_info; - len += strlen(root); - name = kmalloc(len + 1, GFP_KERNEL); - if (name == NULL) + root = dentry->d_sb->s_fs_info; + len = strlen(root); + if (IS_ERR(p)) { + __putname(name); return NULL; - - name[len] = '\0'; - parent = dentry; - while (parent->d_parent != parent) { - len -= parent->d_name.len + 1; - name[len] = '/'; - strncpy(&name[len + 1], parent->d_name.name, - parent->d_name.len); - parent = parent->d_parent; } - strncpy(name, root, strlen(root)); + strncpy(name, root, PATH_MAX); + if (len > p - name) { + __putname(name); + return NULL; + } + if (p > name + len) { + char *s = name + len; + while ((*s++ = *p++) != '\0') + ; + } return name; } +static char *dentry_name(struct dentry *dentry) +{ + char *name = __getname(); + if (!name) + return NULL; + + spin_lock(&dcache_lock); + return __dentry_name(dentry, name); /* will unlock */ +} + static char *inode_name(struct inode *ino) { struct dentry *dentry; + char *name = __getname(); + if (!name) + return NULL; - dentry = list_entry(ino->i_dentry.next, struct dentry, d_alias); - return dentry_name(dentry); + spin_lock(&dcache_lock); + if (list_empty(&ino->i_dentry)) { + spin_unlock(&dcache_lock); + __putname(name); + return NULL; + } + dentry = list_first_entry(&ino->i_dentry, struct dentry, d_alias); + return __dentry_name(dentry, name); /* will unlock */ } static char *follow_link(char *link) @@ -272,7 +286,7 @@ int hostfs_readdir(struct file *file, void *ent, filldir_t filldir) if (name == NULL) return -ENOMEM; dir = open_dir(name, &error); - kfree(name); + __putname(name); if (dir == NULL) return -error; next = file->f_pos; @@ -318,7 +332,7 @@ int hostfs_file_open(struct inode *ino, struct file *file) return -ENOMEM; fd = open_file(name, r, w, append); - kfree(name); + __putname(name); if (fd < 0) return fd; FILE_HOSTFS_I(file)->fd = fd; @@ -532,7 +546,7 @@ int hostfs_create(struct inode *dir, struct dentry *dentry, int mode, else error = read_name(inode, name); - kfree(name); + __putname(name); if (error) goto out_put; @@ -567,7 +581,7 @@ struct dentry *hostfs_lookup(struct inode *ino, struct dentry *dentry, err = read_name(inode, name); - kfree(name); + __putname(name); if (err == -ENOENT) { iput(inode); inode = NULL; @@ -594,12 +608,12 @@ int hostfs_link(struct dentry *to, struct inode *ino, struct dentry *from) return -ENOMEM; to_name = dentry_name(to); if (to_name == NULL) { - kfree(from_name); + __putname(from_name); return -ENOMEM; } err = link_file(to_name, from_name); - kfree(from_name); - kfree(to_name); + __putname(from_name); + __putname(to_name); return err; } @@ -614,7 +628,7 @@ int hostfs_unlink(struct inode *ino, struct dentry *dentry) return -EPERM; err = unlink_file(file); - kfree(file); + __putname(file); return err; } @@ -626,7 +640,7 @@ int hostfs_symlink(struct inode *ino, struct dentry *dentry, const char *to) if ((file = dentry_name(dentry)) == NULL) return -ENOMEM; err = make_symlink(file, to); - kfree(file); + __putname(file); return err; } @@ -638,7 +652,7 @@ int hostfs_mkdir(struct inode *ino, struct dentry *dentry, int mode) if ((file = dentry_name(dentry)) == NULL) return -ENOMEM; err = do_mkdir(file, mode); - kfree(file); + __putname(file); return err; } @@ -650,7 +664,7 @@ int hostfs_rmdir(struct inode *ino, struct dentry *dentry) if ((file = dentry_name(dentry)) == NULL) return -ENOMEM; err = do_rmdir(file); - kfree(file); + __putname(file); return err; } @@ -673,13 +687,13 @@ int hostfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev) init_special_inode(inode, mode, dev); err = do_mknod(name, mode, MAJOR(dev), MINOR(dev)); - if (err) + if (!err) goto out_free; err = read_name(inode, name); + __putname(name); if (err) goto out_put; - kfree(name); if (err) goto out_put; @@ -687,7 +701,7 @@ int hostfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev) return 0; out_free: - kfree(name); + __putname(name); out_put: iput(inode); out: @@ -703,12 +717,12 @@ int hostfs_rename(struct inode *from_ino, struct dentry *from, if ((from_name = dentry_name(from)) == NULL) return -ENOMEM; if ((to_name = dentry_name(to)) == NULL) { - kfree(from_name); + __putname(from_name); return -ENOMEM; } err = rename_file(from_name, to_name); - kfree(from_name); - kfree(to_name); + __putname(from_name); + __putname(to_name); return err; } @@ -729,7 +743,7 @@ int hostfs_permission(struct inode *ino, int desired) err = 0; else err = access_file(name, r, w, x); - kfree(name); + __putname(name); if (!err) err = generic_permission(ino, desired, NULL); return err; @@ -790,7 +804,7 @@ int hostfs_setattr(struct dentry *dentry, struct iattr *attr) if (name == NULL) return -ENOMEM; err = set_attr(name, &attrs, fd); - kfree(name); + __putname(name); if (err) return err; @@ -845,7 +859,7 @@ static void *hostfs_follow_link(struct dentry *dentry, struct nameidata *nd) int err = hostfs_do_readlink(path, link, PATH_MAX); if (err == PATH_MAX) err = -E2BIG; - kfree(path); + __putname(path); } if (err < 0) { __putname(link); -- cgit v1.2.3 From f8d7e1877e5121841bc9a4d284a04dbc13f45bea Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 6 Jun 2010 23:19:04 -0400 Subject: leak in hostfs_unlink() Signed-off-by: Al Viro --- fs/hostfs/hostfs_kern.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c index 79783a0b2f4d..8130ce93a06a 100644 --- a/fs/hostfs/hostfs_kern.c +++ b/fs/hostfs/hostfs_kern.c @@ -622,11 +622,12 @@ int hostfs_unlink(struct inode *ino, struct dentry *dentry) char *file; int err; - if ((file = dentry_name(dentry)) == NULL) - return -ENOMEM; if (append) return -EPERM; + if ((file = dentry_name(dentry)) == NULL) + return -ENOMEM; + err = unlink_file(file); __putname(file); return err; -- cgit v1.2.3 From f8ad850f11e11d10e7de1a16ca53cb193afc9313 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 6 Jun 2010 23:49:18 -0400 Subject: try to get rid of races in hostfs open() In case of mode mismatch, do *not* blindly close the descriptor another openers might be using right now. Open the underlying file with currently sufficient mode, then * if current mode has grown so that it's sufficient for us now, just close our new fd * if current mode has grown and our fd is *not* enough to cover it, close and repeat. * otherwise, install our fd if the file hadn't been opened at all or dup2() our fd over the current one (and close our fd). Critical section is protected by mutex; yes, system-wide. All we do under it is a bunch of comparison and maybe an overwriting dup2() on host. Signed-off-by: Al Viro --- fs/hostfs/hostfs.h | 1 + fs/hostfs/hostfs_kern.c | 43 +++++++++++++++++++++++++++++++------------ fs/hostfs/hostfs_user.c | 5 +++++ 3 files changed, 37 insertions(+), 12 deletions(-) (limited to 'fs') diff --git a/fs/hostfs/hostfs.h b/fs/hostfs/hostfs.h index ea87e224ed97..6bbd75c5589b 100644 --- a/fs/hostfs/hostfs.h +++ b/fs/hostfs/hostfs.h @@ -74,6 +74,7 @@ extern void *open_dir(char *path, int *err_out); extern char *read_dir(void *stream, unsigned long long *pos, unsigned long long *ino_out, int *len_out); extern void close_file(void *stream); +extern int replace_file(int oldfd, int fd); extern void close_dir(void *stream); extern int read_file(int fd, unsigned long long *offset, char *buf, int len); extern int write_file(int fd, unsigned long long *offset, const char *buf, diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c index 8130ce93a06a..dd1e55535a4e 100644 --- a/fs/hostfs/hostfs_kern.c +++ b/fs/hostfs/hostfs_kern.c @@ -302,27 +302,22 @@ int hostfs_readdir(struct file *file, void *ent, filldir_t filldir) int hostfs_file_open(struct inode *ino, struct file *file) { + static DEFINE_MUTEX(open_mutex); char *name; fmode_t mode = 0; + int err; int r = 0, w = 0, fd; mode = file->f_mode & (FMODE_READ | FMODE_WRITE); if ((mode & HOSTFS_I(ino)->mode) == mode) return 0; - /* - * The file may already have been opened, but with the wrong access, - * so this resets things and reopens the file with the new access. - */ - if (HOSTFS_I(ino)->fd != -1) { - close_file(&HOSTFS_I(ino)->fd); - HOSTFS_I(ino)->fd = -1; - } + mode |= HOSTFS_I(ino)->mode; - HOSTFS_I(ino)->mode |= mode; - if (HOSTFS_I(ino)->mode & FMODE_READ) +retry: + if (mode & FMODE_READ) r = 1; - if (HOSTFS_I(ino)->mode & FMODE_WRITE) + if (mode & FMODE_WRITE) w = 1; if (w) r = 1; @@ -335,7 +330,31 @@ int hostfs_file_open(struct inode *ino, struct file *file) __putname(name); if (fd < 0) return fd; - FILE_HOSTFS_I(file)->fd = fd; + + mutex_lock(&open_mutex); + /* somebody else had handled it first? */ + if ((mode & HOSTFS_I(ino)->mode) == mode) { + mutex_unlock(&open_mutex); + return 0; + } + if ((mode | HOSTFS_I(ino)->mode) != mode) { + mode |= HOSTFS_I(ino)->mode; + mutex_unlock(&open_mutex); + close_file(&fd); + goto retry; + } + if (HOSTFS_I(ino)->fd == -1) { + HOSTFS_I(ino)->fd = fd; + } else { + err = replace_file(fd, HOSTFS_I(ino)->fd); + close_file(&fd); + if (err < 0) { + mutex_unlock(&open_mutex); + return err; + } + } + HOSTFS_I(ino)->mode = mode; + mutex_unlock(&open_mutex); return 0; } diff --git a/fs/hostfs/hostfs_user.c b/fs/hostfs/hostfs_user.c index 91ebfcefa409..6777aa06ce2c 100644 --- a/fs/hostfs/hostfs_user.c +++ b/fs/hostfs/hostfs_user.c @@ -160,6 +160,11 @@ int fsync_file(int fd, int datasync) return 0; } +int replace_file(int oldfd, int fd) +{ + return dup2(oldfd, fd); +} + void close_file(void *stream) { close(*((int *) stream)); -- cgit v1.2.3 From 33b0daaa5557e9dadf4c27407fae7d316bab5686 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 7 Jun 2010 00:12:50 -0400 Subject: switch hppfs to ->evict_inode() Signed-off-by: Al Viro --- fs/hppfs/hppfs.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/hppfs/hppfs.c b/fs/hppfs/hppfs.c index 943ce751ce19..7b027720d820 100644 --- a/fs/hppfs/hppfs.c +++ b/fs/hppfs/hppfs.c @@ -624,12 +624,11 @@ static struct inode *hppfs_alloc_inode(struct super_block *sb) return &hi->vfs_inode; } -void hppfs_delete_inode(struct inode *ino) +void hppfs_evict_inode(struct inode *ino) { + end_writeback(ino); dput(HPPFS_I(ino)->proc_dentry); mntput(ino->i_sb->s_fs_info); - - clear_inode(ino); } static void hppfs_destroy_inode(struct inode *inode) @@ -640,7 +639,7 @@ static void hppfs_destroy_inode(struct inode *inode) static const struct super_operations hppfs_sbops = { .alloc_inode = hppfs_alloc_inode, .destroy_inode = hppfs_destroy_inode, - .delete_inode = hppfs_delete_inode, + .evict_inode = hppfs_evict_inode, .statfs = hppfs_statfs, }; -- cgit v1.2.3 From ea544009206baa03d606161656618900260b48e5 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 7 Jun 2010 00:18:40 -0400 Subject: switch hpfs to ->evict_inode() Signed-off-by: Al Viro --- fs/hpfs/hpfs_fn.h | 2 +- fs/hpfs/inode.c | 12 +++++++----- fs/hpfs/super.c | 2 +- 3 files changed, 9 insertions(+), 7 deletions(-) (limited to 'fs') diff --git a/fs/hpfs/hpfs_fn.h b/fs/hpfs/hpfs_fn.h index 75f9d4324851..b59eac0232a0 100644 --- a/fs/hpfs/hpfs_fn.h +++ b/fs/hpfs/hpfs_fn.h @@ -281,7 +281,7 @@ void hpfs_write_inode(struct inode *); void hpfs_write_inode_nolock(struct inode *); int hpfs_setattr(struct dentry *, struct iattr *); void hpfs_write_if_changed(struct inode *); -void hpfs_delete_inode(struct inode *); +void hpfs_evict_inode(struct inode *); /* map.c */ diff --git a/fs/hpfs/inode.c b/fs/hpfs/inode.c index 3f3b397fd4e6..56f0da1cfd10 100644 --- a/fs/hpfs/inode.c +++ b/fs/hpfs/inode.c @@ -302,11 +302,13 @@ void hpfs_write_if_changed(struct inode *inode) hpfs_write_inode(inode); } -void hpfs_delete_inode(struct inode *inode) +void hpfs_evict_inode(struct inode *inode) { truncate_inode_pages(&inode->i_data, 0); - lock_kernel(); - hpfs_remove_fnode(inode->i_sb, inode->i_ino); - unlock_kernel(); - clear_inode(inode); + end_writeback(inode); + if (!inode->i_nlink) { + lock_kernel(); + hpfs_remove_fnode(inode->i_sb, inode->i_ino); + unlock_kernel(); + } } diff --git a/fs/hpfs/super.c b/fs/hpfs/super.c index aa53842c599c..2607010be2fe 100644 --- a/fs/hpfs/super.c +++ b/fs/hpfs/super.c @@ -450,7 +450,7 @@ static const struct super_operations hpfs_sops = { .alloc_inode = hpfs_alloc_inode, .destroy_inode = hpfs_destroy_inode, - .delete_inode = hpfs_delete_inode, + .evict_inode = hpfs_evict_inode, .put_super = hpfs_put_super, .statfs = hpfs_statfs, .remount_fs = hpfs_remount_fs, -- cgit v1.2.3 From 62aff86fdf18657d9eca7878654415f94f16d027 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 7 Jun 2010 00:28:54 -0400 Subject: switch jfs to ->evict_inode() Signed-off-by: Al Viro --- fs/jfs/inode.c | 35 ++++++++++++++++++----------------- fs/jfs/jfs_inode.h | 2 +- fs/jfs/super.c | 8 +------- 3 files changed, 20 insertions(+), 25 deletions(-) (limited to 'fs') diff --git a/fs/jfs/inode.c b/fs/jfs/inode.c index c38dc1806281..9978803ceedc 100644 --- a/fs/jfs/inode.c +++ b/fs/jfs/inode.c @@ -145,31 +145,32 @@ int jfs_write_inode(struct inode *inode, struct writeback_control *wbc) return 0; } -void jfs_delete_inode(struct inode *inode) +void jfs_evict_inode(struct inode *inode) { - jfs_info("In jfs_delete_inode, inode = 0x%p", inode); + jfs_info("In jfs_evict_inode, inode = 0x%p", inode); - if (!is_bad_inode(inode)) + if (!inode->i_nlink && !is_bad_inode(inode)) { dquot_initialize(inode); - if (!is_bad_inode(inode) && - (JFS_IP(inode)->fileset == FILESYSTEM_I)) { - truncate_inode_pages(&inode->i_data, 0); + if (JFS_IP(inode)->fileset == FILESYSTEM_I) { + truncate_inode_pages(&inode->i_data, 0); - if (test_cflag(COMMIT_Freewmap, inode)) - jfs_free_zero_link(inode); + if (test_cflag(COMMIT_Freewmap, inode)) + jfs_free_zero_link(inode); - diFree(inode); + diFree(inode); - /* - * Free the inode from the quota allocation. - */ - dquot_initialize(inode); - dquot_free_inode(inode); - dquot_drop(inode); + /* + * Free the inode from the quota allocation. + */ + dquot_initialize(inode); + dquot_free_inode(inode); + } + } else { + truncate_inode_pages(&inode->i_data, 0); } - - clear_inode(inode); + end_writeback(inode); + dquot_drop(inode); } void jfs_dirty_inode(struct inode *inode) diff --git a/fs/jfs/jfs_inode.h b/fs/jfs/jfs_inode.h index 11042b1f44b5..155e91eff07d 100644 --- a/fs/jfs/jfs_inode.h +++ b/fs/jfs/jfs_inode.h @@ -27,7 +27,7 @@ extern long jfs_compat_ioctl(struct file *, unsigned int, unsigned long); extern struct inode *jfs_iget(struct super_block *, unsigned long); extern int jfs_commit_inode(struct inode *, int); extern int jfs_write_inode(struct inode *, struct writeback_control *); -extern void jfs_delete_inode(struct inode *); +extern void jfs_evict_inode(struct inode *); extern void jfs_dirty_inode(struct inode *); extern void jfs_truncate(struct inode *); extern void jfs_truncate_nolock(struct inode *, loff_t); diff --git a/fs/jfs/super.c b/fs/jfs/super.c index b38f96bef829..ec8c3e4baca3 100644 --- a/fs/jfs/super.c +++ b/fs/jfs/super.c @@ -132,11 +132,6 @@ static void jfs_destroy_inode(struct inode *inode) kmem_cache_free(jfs_inode_cachep, ji); } -static void jfs_clear_inode(struct inode *inode) -{ - dquot_drop(inode); -} - static int jfs_statfs(struct dentry *dentry, struct kstatfs *buf) { struct jfs_sb_info *sbi = JFS_SBI(dentry->d_sb); @@ -765,8 +760,7 @@ static const struct super_operations jfs_super_operations = { .destroy_inode = jfs_destroy_inode, .dirty_inode = jfs_dirty_inode, .write_inode = jfs_write_inode, - .delete_inode = jfs_delete_inode, - .clear_inode = jfs_clear_inode, + .evict_inode = jfs_evict_inode, .put_super = jfs_put_super, .sync_fs = jfs_sync_fs, .freeze_fs = jfs_freeze, -- cgit v1.2.3 From d640e1b50885b5beb61ccacdebf9f3f05ee2119c Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 7 Jun 2010 00:34:05 -0400 Subject: switch ubifs to ->evict_inode() Signed-off-by: Al Viro --- fs/ubifs/super.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c index 4d2f2157dd3f..899066dd0c14 100644 --- a/fs/ubifs/super.c +++ b/fs/ubifs/super.c @@ -327,7 +327,7 @@ static int ubifs_write_inode(struct inode *inode, struct writeback_control *wbc) return err; } -static void ubifs_delete_inode(struct inode *inode) +static void ubifs_evict_inode(struct inode *inode) { int err; struct ubifs_info *c = inode->i_sb->s_fs_info; @@ -343,9 +343,12 @@ static void ubifs_delete_inode(struct inode *inode) dbg_gen("inode %lu, mode %#x", inode->i_ino, (int)inode->i_mode); ubifs_assert(!atomic_read(&inode->i_count)); - ubifs_assert(inode->i_nlink == 0); truncate_inode_pages(&inode->i_data, 0); + + if (inode->i_nlink) + goto done; + if (is_bad_inode(inode)) goto out; @@ -367,7 +370,8 @@ out: c->nospace = c->nospace_rp = 0; smp_wmb(); } - clear_inode(inode); +done: + end_writeback(inode); } static void ubifs_dirty_inode(struct inode *inode) @@ -1824,7 +1828,7 @@ const struct super_operations ubifs_super_operations = { .destroy_inode = ubifs_destroy_inode, .put_super = ubifs_put_super, .write_inode = ubifs_write_inode, - .delete_inode = ubifs_delete_inode, + .evict_inode = ubifs_evict_inode, .statfs = ubifs_statfs, .dirty_inode = ubifs_dirty_inode, .remount_fs = ubifs_remount_fs, -- cgit v1.2.3 From 3aac2b62e0f345c8a637cf94dc62e9000de9d8b6 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 7 Jun 2010 00:43:39 -0400 Subject: switch udf to ->evict_inode() Signed-off-by: Al Viro --- fs/udf/ialloc.c | 2 -- fs/udf/inode.c | 48 +++++++++++++++++++----------------------------- fs/udf/super.c | 3 +-- fs/udf/udfdecl.h | 3 +-- 4 files changed, 21 insertions(+), 35 deletions(-) (limited to 'fs') diff --git a/fs/udf/ialloc.c b/fs/udf/ialloc.c index 18cd7111185d..75d9304d0dc3 100644 --- a/fs/udf/ialloc.c +++ b/fs/udf/ialloc.c @@ -31,8 +31,6 @@ void udf_free_inode(struct inode *inode) struct super_block *sb = inode->i_sb; struct udf_sb_info *sbi = UDF_SB(sb); - clear_inode(inode); - mutex_lock(&sbi->s_alloc_mutex); if (sbi->s_lvid_bh) { struct logicalVolIntegrityDescImpUse *lvidiu = diff --git a/fs/udf/inode.c b/fs/udf/inode.c index ecddcc2ed746..fc48f37aa2dd 100644 --- a/fs/udf/inode.c +++ b/fs/udf/inode.c @@ -68,37 +68,23 @@ static void udf_update_extents(struct inode *, static int udf_get_block(struct inode *, sector_t, struct buffer_head *, int); -void udf_delete_inode(struct inode *inode) -{ - truncate_inode_pages(&inode->i_data, 0); - - if (is_bad_inode(inode)) - goto no_delete; - - inode->i_size = 0; - udf_truncate(inode); - lock_kernel(); - - udf_update_inode(inode, IS_SYNC(inode)); - udf_free_inode(inode); - - unlock_kernel(); - return; - -no_delete: - clear_inode(inode); -} - -/* - * If we are going to release inode from memory, we truncate last inode extent - * to proper length. We could use drop_inode() but it's called under inode_lock - * and thus we cannot mark inode dirty there. We use clear_inode() but we have - * to make sure to write inode as it's not written automatically. - */ -void udf_clear_inode(struct inode *inode) +void udf_evict_inode(struct inode *inode) { struct udf_inode_info *iinfo = UDF_I(inode); + int want_delete = 0; + truncate_inode_pages(&inode->i_data, 0); + + if (!inode->i_nlink && !is_bad_inode(inode)) { + want_delete = 1; + inode->i_size = 0; + udf_truncate(inode); + lock_kernel(); + udf_update_inode(inode, IS_SYNC(inode)); + unlock_kernel(); + } + invalidate_inode_buffers(inode); + end_writeback(inode); if (iinfo->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB && inode->i_size != iinfo->i_lenExtents) { printk(KERN_WARNING "UDF-fs (%s): Inode %lu (mode %o) has " @@ -108,9 +94,13 @@ void udf_clear_inode(struct inode *inode) (unsigned long long)inode->i_size, (unsigned long long)iinfo->i_lenExtents); } - kfree(iinfo->i_ext.i_data); iinfo->i_ext.i_data = NULL; + if (want_delete) { + lock_kernel(); + udf_free_inode(inode); + unlock_kernel(); + } } static int udf_writepage(struct page *page, struct writeback_control *wbc) diff --git a/fs/udf/super.c b/fs/udf/super.c index 612d1e2e285a..f9f4a9a0ea2b 100644 --- a/fs/udf/super.c +++ b/fs/udf/super.c @@ -175,8 +175,7 @@ static const struct super_operations udf_sb_ops = { .alloc_inode = udf_alloc_inode, .destroy_inode = udf_destroy_inode, .write_inode = udf_write_inode, - .delete_inode = udf_delete_inode, - .clear_inode = udf_clear_inode, + .evict_inode = udf_evict_inode, .put_super = udf_put_super, .sync_fs = udf_sync_fs, .statfs = udf_statfs, diff --git a/fs/udf/udfdecl.h b/fs/udf/udfdecl.h index 2bac0354891f..6995ab1f4305 100644 --- a/fs/udf/udfdecl.h +++ b/fs/udf/udfdecl.h @@ -139,8 +139,7 @@ extern struct buffer_head *udf_expand_dir_adinicb(struct inode *, int *, int *); extern struct buffer_head *udf_bread(struct inode *, int, int, int *); extern void udf_truncate(struct inode *); extern void udf_read_inode(struct inode *); -extern void udf_delete_inode(struct inode *); -extern void udf_clear_inode(struct inode *); +extern void udf_evict_inode(struct inode *); extern int udf_write_inode(struct inode *, struct writeback_control *wbc); extern long udf_block_map(struct inode *, sector_t); extern int udf_extend_file(struct inode *, struct extent_position *, -- cgit v1.2.3 From 94ee8494ac84606f06d522a2c016d40aabffb378 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 7 Jun 2010 00:45:56 -0400 Subject: switch ncpfs to ->evict_inode() Signed-off-by: Al Viro --- fs/ncpfs/inode.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'fs') diff --git a/fs/ncpfs/inode.c b/fs/ncpfs/inode.c index b4e8aaae14be..c0434313f920 100644 --- a/fs/ncpfs/inode.c +++ b/fs/ncpfs/inode.c @@ -43,7 +43,7 @@ #define NCP_DEFAULT_TIME_OUT 10 #define NCP_DEFAULT_RETRY_COUNT 20 -static void ncp_delete_inode(struct inode *); +static void ncp_evict_inode(struct inode *); static void ncp_put_super(struct super_block *); static int ncp_statfs(struct dentry *, struct kstatfs *); static int ncp_show_options(struct seq_file *, struct vfsmount *); @@ -100,7 +100,7 @@ static const struct super_operations ncp_sops = .alloc_inode = ncp_alloc_inode, .destroy_inode = ncp_destroy_inode, .drop_inode = generic_delete_inode, - .delete_inode = ncp_delete_inode, + .evict_inode = ncp_evict_inode, .put_super = ncp_put_super, .statfs = ncp_statfs, .remount_fs = ncp_remount, @@ -282,19 +282,19 @@ ncp_iget(struct super_block *sb, struct ncp_entry_info *info) } static void -ncp_delete_inode(struct inode *inode) +ncp_evict_inode(struct inode *inode) { truncate_inode_pages(&inode->i_data, 0); + end_writeback(inode); if (S_ISDIR(inode->i_mode)) { - DDPRINTK("ncp_delete_inode: put directory %ld\n", inode->i_ino); + DDPRINTK("ncp_evict_inode: put directory %ld\n", inode->i_ino); } if (ncp_make_closed(inode) != 0) { /* We can't do anything but complain. */ - printk(KERN_ERR "ncp_delete_inode: could not close\n"); + printk(KERN_ERR "ncp_evict_inode: could not close\n"); } - clear_inode(inode); } static void ncp_stop_tasks(struct ncp_server *server) { -- cgit v1.2.3 From 066d92dcbfa5842d98f6c4c671220cef50a9720f Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 8 Jun 2010 21:28:10 -0400 Subject: convert ocfs2 to ->evict_inode() Signed-off-by: Al Viro --- fs/ocfs2/inode.c | 21 ++++++++++++++------- fs/ocfs2/inode.h | 3 +-- fs/ocfs2/super.c | 3 +-- 3 files changed, 16 insertions(+), 11 deletions(-) (limited to 'fs') diff --git a/fs/ocfs2/inode.c b/fs/ocfs2/inode.c index abb0a95cc717..eb7fd07c90f2 100644 --- a/fs/ocfs2/inode.c +++ b/fs/ocfs2/inode.c @@ -969,7 +969,7 @@ static void ocfs2_cleanup_delete_inode(struct inode *inode, truncate_inode_pages(&inode->i_data, 0); } -void ocfs2_delete_inode(struct inode *inode) +static void ocfs2_delete_inode(struct inode *inode) { int wipe, status; sigset_t oldset; @@ -1075,20 +1075,17 @@ bail_unlock_nfs_sync: bail_unblock: ocfs2_unblock_signals(&oldset); bail: - clear_inode(inode); mlog_exit_void(); } -void ocfs2_clear_inode(struct inode *inode) +static void ocfs2_clear_inode(struct inode *inode) { int status; struct ocfs2_inode_info *oi = OCFS2_I(inode); mlog_entry_void(); - if (!inode) - goto bail; - + end_writeback(inode); mlog(0, "Clearing inode: %llu, nlink = %u\n", (unsigned long long)OCFS2_I(inode)->ip_blkno, inode->i_nlink); @@ -1180,10 +1177,20 @@ void ocfs2_clear_inode(struct inode *inode) jbd2_journal_release_jbd_inode(OCFS2_SB(inode->i_sb)->journal->j_journal, &oi->ip_jinode); -bail: mlog_exit_void(); } +void ocfs2_evict_inode(struct inode *inode) +{ + if (!inode->i_nlink || + (OCFS2_I(inode)->ip_flags & OCFS2_INODE_MAYBE_ORPHANED)) { + ocfs2_delete_inode(inode); + } else { + truncate_inode_pages(&inode->i_data, 0); + } + ocfs2_clear_inode(inode); +} + /* Called under inode_lock, with no more references on the * struct inode, so it's safe here to check the flags field * and to manipulate i_nlink without any other locks. */ diff --git a/fs/ocfs2/inode.h b/fs/ocfs2/inode.h index 9f5f5fcadc45..975eedd7b243 100644 --- a/fs/ocfs2/inode.h +++ b/fs/ocfs2/inode.h @@ -123,8 +123,7 @@ static inline struct ocfs2_caching_info *INODE_CACHE(struct inode *inode) return &OCFS2_I(inode)->ip_metadata_cache; } -void ocfs2_clear_inode(struct inode *inode); -void ocfs2_delete_inode(struct inode *inode); +void ocfs2_evict_inode(struct inode *inode); void ocfs2_drop_inode(struct inode *inode); /* Flags for ocfs2_iget() */ diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c index 0eaa929a4dbf..ae1a4437a980 100644 --- a/fs/ocfs2/super.c +++ b/fs/ocfs2/super.c @@ -145,8 +145,7 @@ static const struct super_operations ocfs2_sops = { .alloc_inode = ocfs2_alloc_inode, .destroy_inode = ocfs2_destroy_inode, .drop_inode = ocfs2_drop_inode, - .clear_inode = ocfs2_clear_inode, - .delete_inode = ocfs2_delete_inode, + .evict_inode = ocfs2_evict_inode, .sync_fs = ocfs2_sync_fs, .put_super = ocfs2_put_super, .remount_fs = ocfs2_remount, -- cgit v1.2.3 From d5c1515cf374951f07e5bf97b6ff3718d3401b6f Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 7 Jun 2010 11:05:19 -0400 Subject: switch gfs2 to ->evict_inode() Signed-off-by: Al Viro --- fs/gfs2/super.c | 39 +++++++++++++++------------------------ 1 file changed, 15 insertions(+), 24 deletions(-) (limited to 'fs') diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c index 4d1aad38f1b1..555f5a417c67 100644 --- a/fs/gfs2/super.c +++ b/fs/gfs2/super.c @@ -1203,25 +1203,6 @@ static void gfs2_drop_inode(struct inode *inode) generic_drop_inode(inode); } -/** - * gfs2_clear_inode - Deallocate an inode when VFS is done with it - * @inode: The VFS inode - * - */ - -static void gfs2_clear_inode(struct inode *inode) -{ - struct gfs2_inode *ip = GFS2_I(inode); - - ip->i_gl->gl_object = NULL; - gfs2_glock_put(ip->i_gl); - ip->i_gl = NULL; - if (ip->i_iopen_gh.gh_gl) { - ip->i_iopen_gh.gh_gl->gl_object = NULL; - gfs2_glock_dq_uninit(&ip->i_iopen_gh); - } -} - static int is_ancestor(const struct dentry *d1, const struct dentry *d2) { do { @@ -1347,13 +1328,16 @@ static int gfs2_show_options(struct seq_file *s, struct vfsmount *mnt) * is safe, just less efficient. */ -static void gfs2_delete_inode(struct inode *inode) +static void gfs2_evict_inode(struct inode *inode) { struct gfs2_sbd *sdp = inode->i_sb->s_fs_info; struct gfs2_inode *ip = GFS2_I(inode); struct gfs2_holder gh; int error; + if (inode->i_nlink) + goto out; + error = gfs2_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &gh); if (unlikely(error)) { gfs2_glock_dq_uninit(&ip->i_iopen_gh); @@ -1407,10 +1391,18 @@ out_unlock: gfs2_holder_uninit(&ip->i_iopen_gh); gfs2_glock_dq_uninit(&gh); if (error && error != GLR_TRYFAILED && error != -EROFS) - fs_warn(sdp, "gfs2_delete_inode: %d\n", error); + fs_warn(sdp, "gfs2_evict_inode: %d\n", error); out: truncate_inode_pages(&inode->i_data, 0); - clear_inode(inode); + end_writeback(inode); + + ip->i_gl->gl_object = NULL; + gfs2_glock_put(ip->i_gl); + ip->i_gl = NULL; + if (ip->i_iopen_gh.gh_gl) { + ip->i_iopen_gh.gh_gl->gl_object = NULL; + gfs2_glock_dq_uninit(&ip->i_iopen_gh); + } } static struct inode *gfs2_alloc_inode(struct super_block *sb) @@ -1434,14 +1426,13 @@ const struct super_operations gfs2_super_ops = { .alloc_inode = gfs2_alloc_inode, .destroy_inode = gfs2_destroy_inode, .write_inode = gfs2_write_inode, - .delete_inode = gfs2_delete_inode, + .evict_inode = gfs2_evict_inode, .put_super = gfs2_put_super, .sync_fs = gfs2_sync_fs, .freeze_fs = gfs2_freeze, .unfreeze_fs = gfs2_unfreeze, .statfs = gfs2_statfs, .remount_fs = gfs2_remount_fs, - .clear_inode = gfs2_clear_inode, .drop_inode = gfs2_drop_inode, .show_options = gfs2_show_options, }; -- cgit v1.2.3 From bd55597520a2eaa0d71dd7683513a14bfd1bdf5c Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 7 Jun 2010 11:35:40 -0400 Subject: convert btrfs to ->evict_inode() NB: do we want btrfs_wait_ordered_range() on eviction of inodes with positive i_nlink on subvolume with zero root_refs? If not, btrfs_evict_inode() can be simplified by unconditionally bailing out in case of i_nlink > 0 in the very beginning... Signed-off-by: Al Viro --- fs/btrfs/ctree.h | 2 +- fs/btrfs/inode.c | 8 ++++++-- fs/btrfs/super.c | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 29c20092847e..394d5422ab6a 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -2389,7 +2389,7 @@ unsigned long btrfs_force_ra(struct address_space *mapping, pgoff_t offset, pgoff_t last_index); int btrfs_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf); int btrfs_readpage(struct file *file, struct page *page); -void btrfs_delete_inode(struct inode *inode); +void btrfs_evict_inode(struct inode *inode); void btrfs_put_inode(struct inode *inode); int btrfs_write_inode(struct inode *inode, struct writeback_control *wbc); void btrfs_dirty_inode(struct inode *inode); diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 95eac0116963..ce02199ec4e5 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -3668,7 +3668,7 @@ static int btrfs_setattr(struct dentry *dentry, struct iattr *attr) return err; } -void btrfs_delete_inode(struct inode *inode) +void btrfs_evict_inode(struct inode *inode) { struct btrfs_trans_handle *trans; struct btrfs_root *root = BTRFS_I(inode)->root; @@ -3676,10 +3676,14 @@ void btrfs_delete_inode(struct inode *inode) int ret; truncate_inode_pages(&inode->i_data, 0); + if (inode->i_nlink && btrfs_root_refs(&root->root_item) != 0) + goto no_delete; + if (is_bad_inode(inode)) { btrfs_orphan_del(NULL, inode); goto no_delete; } + /* do we really want it for ->i_nlink > 0 and zero btrfs_root_refs? */ btrfs_wait_ordered_range(inode, 0, (u64)-1); if (root->fs_info->log_root_recovering) { @@ -3729,7 +3733,7 @@ void btrfs_delete_inode(struct inode *inode) btrfs_end_transaction(trans, root); btrfs_btree_balance_dirty(root, nr); no_delete: - clear_inode(inode); + end_writeback(inode); return; } diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index f2393b390318..1776dbd8dc98 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -797,7 +797,7 @@ static int btrfs_unfreeze(struct super_block *sb) static const struct super_operations btrfs_super_ops = { .drop_inode = btrfs_drop_inode, - .delete_inode = btrfs_delete_inode, + .evict_inode = btrfs_evict_inode, .put_super = btrfs_put_super, .sync_fs = btrfs_sync_fs, .show_options = btrfs_show_options, -- cgit v1.2.3 From 845a2cc0507055278e0fa722ed0f8c791b7401dd Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 7 Jun 2010 11:37:37 -0400 Subject: convert reiserfs to ->evict_inode() Signed-off-by: Al Viro --- fs/reiserfs/inode.c | 13 ++++++++++--- fs/reiserfs/super.c | 8 +------- include/linux/reiserfs_fs.h | 2 +- 3 files changed, 12 insertions(+), 11 deletions(-) (limited to 'fs') diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c index 46ba1cfc2df3..a94e08b339fc 100644 --- a/fs/reiserfs/inode.c +++ b/fs/reiserfs/inode.c @@ -25,7 +25,7 @@ int reiserfs_commit_write(struct file *f, struct page *page, int reiserfs_prepare_write(struct file *f, struct page *page, unsigned from, unsigned to); -void reiserfs_delete_inode(struct inode *inode) +void reiserfs_evict_inode(struct inode *inode) { /* We need blocks for transaction + (user+group) quota update (possibly delete) */ int jbegin_count = @@ -35,10 +35,12 @@ void reiserfs_delete_inode(struct inode *inode) int depth; int err; - if (!is_bad_inode(inode)) + if (!inode->i_nlink && !is_bad_inode(inode)) dquot_initialize(inode); truncate_inode_pages(&inode->i_data, 0); + if (inode->i_nlink) + goto no_delete; depth = reiserfs_write_lock_once(inode->i_sb); @@ -77,9 +79,14 @@ void reiserfs_delete_inode(struct inode *inode) ; } out: - clear_inode(inode); /* note this must go after the journal_end to prevent deadlock */ + end_writeback(inode); /* note this must go after the journal_end to prevent deadlock */ + dquot_drop(inode); inode->i_blocks = 0; reiserfs_write_unlock_once(inode->i_sb, depth); + +no_delete: + end_writeback(inode); + dquot_drop(inode); } static void _make_cpu_key(struct cpu_key *key, int version, __u32 dirid, diff --git a/fs/reiserfs/super.c b/fs/reiserfs/super.c index 1e1ee9056eb6..e15ff612002d 100644 --- a/fs/reiserfs/super.c +++ b/fs/reiserfs/super.c @@ -591,11 +591,6 @@ out: reiserfs_write_unlock_once(inode->i_sb, lock_depth); } -static void reiserfs_clear_inode(struct inode *inode) -{ - dquot_drop(inode); -} - #ifdef CONFIG_QUOTA static ssize_t reiserfs_quota_write(struct super_block *, int, const char *, size_t, loff_t); @@ -608,8 +603,7 @@ static const struct super_operations reiserfs_sops = { .destroy_inode = reiserfs_destroy_inode, .write_inode = reiserfs_write_inode, .dirty_inode = reiserfs_dirty_inode, - .clear_inode = reiserfs_clear_inode, - .delete_inode = reiserfs_delete_inode, + .evict_inode = reiserfs_evict_inode, .put_super = reiserfs_put_super, .write_super = reiserfs_write_super, .sync_fs = reiserfs_sync_fs, diff --git a/include/linux/reiserfs_fs.h b/include/linux/reiserfs_fs.h index 3b603f474186..2a464ae147ce 100644 --- a/include/linux/reiserfs_fs.h +++ b/include/linux/reiserfs_fs.h @@ -2033,7 +2033,7 @@ void reiserfs_read_locked_inode(struct inode *inode, struct reiserfs_iget_args *args); int reiserfs_find_actor(struct inode *inode, void *p); int reiserfs_init_locked_inode(struct inode *inode, void *p); -void reiserfs_delete_inode(struct inode *inode); +void reiserfs_evict_inode(struct inode *inode); int reiserfs_write_inode(struct inode *inode, struct writeback_control *wbc); int reiserfs_get_block(struct inode *inode, sector_t block, struct buffer_head *bh_result, int create); -- cgit v1.2.3 From 4ec70c9b46b032e7f1b41b543c607d6a33b78a1a Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 7 Jun 2010 11:42:26 -0400 Subject: convert exofs to ->evict_inode() Signed-off-by: Al Viro --- fs/exofs/exofs.h | 2 +- fs/exofs/inode.c | 8 ++++---- fs/exofs/super.c | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'fs') diff --git a/fs/exofs/exofs.h b/fs/exofs/exofs.h index 0706ce996c84..2dc925fa1010 100644 --- a/fs/exofs/exofs.h +++ b/fs/exofs/exofs.h @@ -263,7 +263,7 @@ int exofs_write_begin(struct file *file, struct address_space *mapping, extern struct inode *exofs_iget(struct super_block *, unsigned long); struct inode *exofs_new_inode(struct inode *, int); extern int exofs_write_inode(struct inode *, struct writeback_control *wbc); -extern void exofs_delete_inode(struct inode *); +extern void exofs_evict_inode(struct inode *); /* dir.c: */ int exofs_add_link(struct dentry *, struct inode *); diff --git a/fs/exofs/inode.c b/fs/exofs/inode.c index ccd0ce3eea75..088cb476b68a 100644 --- a/fs/exofs/inode.c +++ b/fs/exofs/inode.c @@ -1307,7 +1307,7 @@ static void delete_done(struct exofs_io_state *ios, void *p) * from the OSD here. We make sure the object was created before we try and * delete it. */ -void exofs_delete_inode(struct inode *inode) +void exofs_evict_inode(struct inode *inode) { struct exofs_i_info *oi = exofs_i(inode); struct super_block *sb = inode->i_sb; @@ -1318,11 +1318,11 @@ void exofs_delete_inode(struct inode *inode) truncate_inode_pages(&inode->i_data, 0); /* TODO: should do better here */ - if (is_bad_inode(inode)) + if (inode->i_nlink || is_bad_inode(inode)) goto no_delete; inode->i_size = 0; - clear_inode(inode); + end_writeback(inode); /* if we are deleting an obj that hasn't been created yet, wait */ if (!obj_created(oi)) { @@ -1353,5 +1353,5 @@ void exofs_delete_inode(struct inode *inode) return; no_delete: - clear_inode(inode); + end_writeback(inode); } diff --git a/fs/exofs/super.c b/fs/exofs/super.c index 03149b9a5178..32cfd61def5f 100644 --- a/fs/exofs/super.c +++ b/fs/exofs/super.c @@ -743,7 +743,7 @@ static const struct super_operations exofs_sops = { .alloc_inode = exofs_alloc_inode, .destroy_inode = exofs_destroy_inode, .write_inode = exofs_write_inode, - .delete_inode = exofs_delete_inode, + .evict_inode = exofs_evict_inode, .put_super = exofs_put_super, .write_super = exofs_write_super, .sync_fs = exofs_sync_fs, -- cgit v1.2.3 From 6fd1e5c994c392ebdbe45600051b2a32ec4860f1 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 7 Jun 2010 11:55:00 -0400 Subject: convert nilfs2 to ->evict_inode() [folded build fix from sfr] Signed-off-by: Al Viro --- fs/nilfs2/inode.c | 28 ++++++++++++++++++++++++---- fs/nilfs2/nilfs.h | 2 +- fs/nilfs2/super.c | 20 +------------------- 3 files changed, 26 insertions(+), 24 deletions(-) (limited to 'fs') diff --git a/fs/nilfs2/inode.c b/fs/nilfs2/inode.c index 051d279abb37..eccb2f2e2315 100644 --- a/fs/nilfs2/inode.c +++ b/fs/nilfs2/inode.c @@ -27,6 +27,7 @@ #include #include #include "nilfs.h" +#include "btnode.h" #include "segment.h" #include "page.h" #include "mdt.h" @@ -354,7 +355,6 @@ void nilfs_free_inode(struct inode *inode) struct super_block *sb = inode->i_sb; struct nilfs_sb_info *sbi = NILFS_SB(sb); - clear_inode(inode); /* XXX: check error code? Is there any thing I can do? */ (void) nilfs_ifile_delete_inode(sbi->s_ifile, inode->i_ino); atomic_dec(&sbi->s_inodes_count); @@ -614,16 +614,34 @@ void nilfs_truncate(struct inode *inode) But truncate has no return value. */ } -void nilfs_delete_inode(struct inode *inode) +static void nilfs_clear_inode(struct inode *inode) +{ + struct nilfs_inode_info *ii = NILFS_I(inode); + + /* + * Free resources allocated in nilfs_read_inode(), here. + */ + BUG_ON(!list_empty(&ii->i_dirty)); + brelse(ii->i_bh); + ii->i_bh = NULL; + + if (test_bit(NILFS_I_BMAP, &ii->i_state)) + nilfs_bmap_clear(ii->i_bmap); + + nilfs_btnode_cache_clear(&ii->i_btnode_cache); +} + +void nilfs_evict_inode(struct inode *inode) { struct nilfs_transaction_info ti; struct super_block *sb = inode->i_sb; struct nilfs_inode_info *ii = NILFS_I(inode); - if (unlikely(is_bad_inode(inode))) { + if (inode->i_nlink || unlikely(is_bad_inode(inode))) { if (inode->i_data.nrpages) truncate_inode_pages(&inode->i_data, 0); - clear_inode(inode); + end_writeback(inode); + nilfs_clear_inode(inode); return; } nilfs_transaction_begin(sb, &ti, 0); /* never fails */ @@ -633,6 +651,8 @@ void nilfs_delete_inode(struct inode *inode) nilfs_truncate_bmap(ii, 0); nilfs_mark_inode_dirty(inode); + end_writeback(inode); + nilfs_clear_inode(inode); nilfs_free_inode(inode); /* nilfs_free_inode() marks inode buffer dirty */ if (IS_SYNC(inode)) diff --git a/fs/nilfs2/nilfs.h b/fs/nilfs2/nilfs.h index 47d6d7928122..f03279748099 100644 --- a/fs/nilfs2/nilfs.h +++ b/fs/nilfs2/nilfs.h @@ -245,7 +245,7 @@ extern void nilfs_write_inode_common(struct inode *, struct nilfs_inode *, int); extern struct inode *nilfs_iget(struct super_block *, unsigned long); extern void nilfs_update_inode(struct inode *, struct buffer_head *); extern void nilfs_truncate(struct inode *); -extern void nilfs_delete_inode(struct inode *); +extern void nilfs_evict_inode(struct inode *); extern int nilfs_setattr(struct dentry *, struct iattr *); extern int nilfs_load_inode_block(struct nilfs_sb_info *, struct inode *, struct buffer_head **); diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c index 414ef68931cf..7c7572a4e138 100644 --- a/fs/nilfs2/super.c +++ b/fs/nilfs2/super.c @@ -159,23 +159,6 @@ void nilfs_destroy_inode(struct inode *inode) kmem_cache_free(nilfs_inode_cachep, NILFS_I(inode)); } -static void nilfs_clear_inode(struct inode *inode) -{ - struct nilfs_inode_info *ii = NILFS_I(inode); - - /* - * Free resources allocated in nilfs_read_inode(), here. - */ - BUG_ON(!list_empty(&ii->i_dirty)); - brelse(ii->i_bh); - ii->i_bh = NULL; - - if (test_bit(NILFS_I_BMAP, &ii->i_state)) - nilfs_bmap_clear(ii->i_bmap); - - nilfs_btnode_cache_clear(&ii->i_btnode_cache); -} - static int nilfs_sync_super(struct nilfs_sb_info *sbi, int dupsb) { struct the_nilfs *nilfs = sbi->s_nilfs; @@ -467,7 +450,7 @@ static const struct super_operations nilfs_sops = { /* .write_inode = nilfs_write_inode, */ /* .put_inode = nilfs_put_inode, */ /* .drop_inode = nilfs_drop_inode, */ - .delete_inode = nilfs_delete_inode, + .evict_inode = nilfs_evict_inode, .put_super = nilfs_put_super, /* .write_super = nilfs_write_super, */ .sync_fs = nilfs_sync_fs, @@ -475,7 +458,6 @@ static const struct super_operations nilfs_sops = { /* .unlockfs */ .statfs = nilfs_statfs, .remount_fs = nilfs_remount, - .clear_inode = nilfs_clear_inode, /* .umount_begin */ .show_options = nilfs_show_options }; -- cgit v1.2.3 From 8e22c1a4e429e9facf309c7e7a03ba9cdfd7b106 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 7 Jun 2010 12:22:31 -0400 Subject: logfs: get rid of magical inodes ordering problems at ->kill_sb() time are solved by doing iput() of these suckers in ->put_super() Signed-off-by: Al Viro --- fs/logfs/inode.c | 44 ++++++++++++++++---------------------------- fs/logfs/journal.c | 2 -- fs/logfs/logfs.h | 1 - fs/logfs/readwrite.c | 1 - fs/logfs/segment.c | 1 - fs/logfs/super.c | 23 +++++++++++++++-------- 6 files changed, 31 insertions(+), 41 deletions(-) (limited to 'fs') diff --git a/fs/logfs/inode.c b/fs/logfs/inode.c index f602e230e162..7811a2a35935 100644 --- a/fs/logfs/inode.c +++ b/fs/logfs/inode.c @@ -235,33 +235,21 @@ static struct inode *logfs_alloc_inode(struct super_block *sb) * purpose is to create a new inode that will not trigger the warning if such * an inode is still in use. An ugly hack, no doubt. Suggections for * improvement are welcome. + * + * AV: that's what ->put_super() is for... */ struct inode *logfs_new_meta_inode(struct super_block *sb, u64 ino) { struct inode *inode; - inode = logfs_alloc_inode(sb); + inode = new_inode(sb); if (!inode) return ERR_PTR(-ENOMEM); inode->i_mode = S_IFREG; inode->i_ino = ino; - inode->i_sb = sb; - - /* This is a blatant copy of alloc_inode code. We'd need alloc_inode - * to be nonstatic, alas. */ - { - struct address_space * const mapping = &inode->i_data; - - mapping->a_ops = &logfs_reg_aops; - mapping->host = inode; - mapping->flags = 0; - mapping_set_gfp_mask(mapping, GFP_NOFS); - mapping->assoc_mapping = NULL; - mapping->backing_dev_info = &default_backing_dev_info; - inode->i_mapping = mapping; - inode->i_nlink = 1; - } + inode->i_data.a_ops = &logfs_reg_aops; + mapping_set_gfp_mask(&inode->i_data, GFP_NOFS); return inode; } @@ -277,7 +265,7 @@ struct inode *logfs_read_meta_inode(struct super_block *sb, u64 ino) err = logfs_read_inode(inode); if (err) { - destroy_meta_inode(inode); + iput(inode); return ERR_PTR(err); } logfs_inode_setops(inode); @@ -298,16 +286,6 @@ static int logfs_write_inode(struct inode *inode, struct writeback_control *wbc) return ret; } -void destroy_meta_inode(struct inode *inode) -{ - if (inode) { - if (inode->i_data.nrpages) - truncate_inode_pages(&inode->i_data, 0); - logfs_clear_inode(inode); - kmem_cache_free(logfs_inode_cache, logfs_inode(inode)); - } -} - /* called with inode_lock held */ static void logfs_drop_inode(struct inode *inode) { @@ -384,12 +362,22 @@ static int logfs_sync_fs(struct super_block *sb, int wait) return 0; } +static void logfs_put_super(struct super_block *sb) +{ + struct logfs_super *super = logfs_super(sb); + /* kill the meta-inodes */ + iput(super->s_master_inode); + iput(super->s_segfile_inode); + iput(super->s_mapping_inode); +} + const struct super_operations logfs_super_operations = { .alloc_inode = logfs_alloc_inode, .clear_inode = logfs_clear_inode, .delete_inode = logfs_delete_inode, .destroy_inode = logfs_destroy_inode, .drop_inode = logfs_drop_inode, + .put_super = logfs_put_super, .write_inode = logfs_write_inode, .statfs = logfs_statfs, .sync_fs = logfs_sync_fs, diff --git a/fs/logfs/journal.c b/fs/logfs/journal.c index 4b0e0616b357..f46ee8b0e135 100644 --- a/fs/logfs/journal.c +++ b/fs/logfs/journal.c @@ -889,8 +889,6 @@ void logfs_cleanup_journal(struct super_block *sb) struct logfs_super *super = logfs_super(sb); btree_grim_visitor32(&super->s_reserved_segments, 0, NULL); - destroy_meta_inode(super->s_master_inode); - super->s_master_inode = NULL; kfree(super->s_compressed_je); kfree(super->s_je); diff --git a/fs/logfs/logfs.h b/fs/logfs/logfs.h index c838c4d72111..5e65171dad23 100644 --- a/fs/logfs/logfs.h +++ b/fs/logfs/logfs.h @@ -525,7 +525,6 @@ struct inode *logfs_new_meta_inode(struct super_block *sb, u64 ino); struct inode *logfs_read_meta_inode(struct super_block *sb, u64 ino); int logfs_init_inode_cache(void); void logfs_destroy_inode_cache(void); -void destroy_meta_inode(struct inode *inode); void logfs_set_blocks(struct inode *inode, u64 no); /* these logically belong into inode.c but actually reside in readwrite.c */ int logfs_read_inode(struct inode *inode); diff --git a/fs/logfs/readwrite.c b/fs/logfs/readwrite.c index 0718d112a1a5..580d126d597d 100644 --- a/fs/logfs/readwrite.c +++ b/fs/logfs/readwrite.c @@ -2272,7 +2272,6 @@ void logfs_cleanup_rw(struct super_block *sb) { struct logfs_super *super = logfs_super(sb); - destroy_meta_inode(super->s_segfile_inode); logfs_mempool_destroy(super->s_block_pool); logfs_mempool_destroy(super->s_shadow_pool); } diff --git a/fs/logfs/segment.c b/fs/logfs/segment.c index a9657afb70ad..9d5187353255 100644 --- a/fs/logfs/segment.c +++ b/fs/logfs/segment.c @@ -929,5 +929,4 @@ void logfs_cleanup_areas(struct super_block *sb) for_each_area(i) free_area(super->s_area[i]); free_area(super->s_journal_area); - destroy_meta_inode(super->s_mapping_inode); } diff --git a/fs/logfs/super.c b/fs/logfs/super.c index d651e10a1e9c..5336155c5d81 100644 --- a/fs/logfs/super.c +++ b/fs/logfs/super.c @@ -342,24 +342,27 @@ static int logfs_get_sb_final(struct super_block *sb, struct vfsmount *mnt) goto fail; } + /* at that point we know that ->put_super() will be called */ super->s_erase_page = alloc_pages(GFP_KERNEL, 0); if (!super->s_erase_page) - goto fail; + return -ENOMEM; memset(page_address(super->s_erase_page), 0xFF, PAGE_SIZE); /* FIXME: check for read-only mounts */ err = logfs_make_writeable(sb); - if (err) - goto fail1; + if (err) { + __free_page(super->s_erase_page); + return err; + } log_super("LogFS: Finished mounting\n"); simple_set_mnt(mnt, sb); return 0; -fail1: - __free_page(super->s_erase_page); fail: - iput(logfs_super(sb)->s_master_inode); + iput(super->s_master_inode); + iput(super->s_segfile_inode); + iput(super->s_mapping_inode); return -EIO; } @@ -580,10 +583,14 @@ int logfs_get_sb_device(struct file_system_type *type, int flags, sb->s_flags |= MS_ACTIVE; err = logfs_get_sb_final(sb, mnt); if (err) - goto err1; - return 0; + deactivate_locked_super(sb); + return err; err1: + /* no ->s_root, no ->put_super() */ + iput(super->s_master_inode); + iput(super->s_segfile_inode); + iput(super->s_mapping_inode); deactivate_locked_super(sb); return err; err0: -- cgit v1.2.3 From 7da08fd17a6e42d80f0f3897a5cbd682e77bcdb4 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 7 Jun 2010 13:11:34 -0400 Subject: convert logfs to ->evict_inode() Signed-off-by: Al Viro --- fs/logfs/inode.c | 3 +-- fs/logfs/logfs.h | 3 +-- fs/logfs/readwrite.c | 61 +++++++++++++++++++++++++--------------------------- 3 files changed, 31 insertions(+), 36 deletions(-) (limited to 'fs') diff --git a/fs/logfs/inode.c b/fs/logfs/inode.c index 7811a2a35935..78be674d95c8 100644 --- a/fs/logfs/inode.c +++ b/fs/logfs/inode.c @@ -373,9 +373,8 @@ static void logfs_put_super(struct super_block *sb) const struct super_operations logfs_super_operations = { .alloc_inode = logfs_alloc_inode, - .clear_inode = logfs_clear_inode, - .delete_inode = logfs_delete_inode, .destroy_inode = logfs_destroy_inode, + .evict_inode = logfs_evict_inode, .drop_inode = logfs_drop_inode, .put_super = logfs_put_super, .write_inode = logfs_write_inode, diff --git a/fs/logfs/logfs.h b/fs/logfs/logfs.h index 5e65171dad23..5e3b72077951 100644 --- a/fs/logfs/logfs.h +++ b/fs/logfs/logfs.h @@ -529,8 +529,7 @@ void logfs_set_blocks(struct inode *inode, u64 no); /* these logically belong into inode.c but actually reside in readwrite.c */ int logfs_read_inode(struct inode *inode); int __logfs_write_inode(struct inode *inode, long flags); -void logfs_delete_inode(struct inode *inode); -void logfs_clear_inode(struct inode *inode); +void logfs_evict_inode(struct inode *inode); /* journal.c */ void logfs_write_anchor(struct super_block *sb); diff --git a/fs/logfs/readwrite.c b/fs/logfs/readwrite.c index 580d126d597d..6127baf0e188 100644 --- a/fs/logfs/readwrite.c +++ b/fs/logfs/readwrite.c @@ -1972,31 +1972,6 @@ static struct page *inode_to_page(struct inode *inode) return page; } -/* Cheaper version of write_inode. All changes are concealed in - * aliases, which are moved back. No write to the medium happens. - */ -void logfs_clear_inode(struct inode *inode) -{ - struct super_block *sb = inode->i_sb; - struct logfs_inode *li = logfs_inode(inode); - struct logfs_block *block = li->li_block; - struct page *page; - - /* Only deleted files may be dirty at this point */ - BUG_ON(inode->i_state & I_DIRTY && inode->i_nlink); - if (!block) - return; - if ((logfs_super(sb)->s_flags & LOGFS_SB_FLAG_SHUTDOWN)) { - block->ops->free_block(inode->i_sb, block); - return; - } - - BUG_ON(inode->i_ino < LOGFS_RESERVED_INOS); - page = inode_to_page(inode); - BUG_ON(!page); /* FIXME: Use emergency page */ - logfs_put_write_page(page); -} - static int do_write_inode(struct inode *inode) { struct super_block *sb = inode->i_sb; @@ -2164,18 +2139,40 @@ static int do_delete_inode(struct inode *inode) * ZOMBIE inodes have already been deleted before and should remain dead, * if it weren't for valid checking. No need to kill them again here. */ -void logfs_delete_inode(struct inode *inode) +void logfs_evict_inode(struct inode *inode) { + struct super_block *sb = inode->i_sb; struct logfs_inode *li = logfs_inode(inode); + struct logfs_block *block = li->li_block; + struct page *page; - if (!(li->li_flags & LOGFS_IF_ZOMBIE)) { - li->li_flags |= LOGFS_IF_ZOMBIE; - if (i_size_read(inode) > 0) - logfs_truncate(inode, 0); - do_delete_inode(inode); + if (!inode->i_nlink) { + if (!(li->li_flags & LOGFS_IF_ZOMBIE)) { + li->li_flags |= LOGFS_IF_ZOMBIE; + if (i_size_read(inode) > 0) + logfs_truncate(inode, 0); + do_delete_inode(inode); + } } truncate_inode_pages(&inode->i_data, 0); - clear_inode(inode); + end_writeback(inode); + + /* Cheaper version of write_inode. All changes are concealed in + * aliases, which are moved back. No write to the medium happens. + */ + /* Only deleted files may be dirty at this point */ + BUG_ON(inode->i_state & I_DIRTY && inode->i_nlink); + if (!block) + return; + if ((logfs_super(sb)->s_flags & LOGFS_SB_FLAG_SHUTDOWN)) { + block->ops->free_block(inode->i_sb, block); + return; + } + + BUG_ON(inode->i_ino < LOGFS_RESERVED_INOS); + page = inode_to_page(inode); + BUG_ON(!page); /* FIXME: Use emergency page */ + logfs_put_write_page(page); } void btree_write_block(struct logfs_block *block) -- cgit v1.2.3 From 0930fcc1ee2f0a810b938bc283a3a262d7adccbb Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 7 Jun 2010 13:16:22 -0400 Subject: convert ext4 to ->evict_inode() pretty much brute-force... Signed-off-by: Al Viro --- fs/ext4/ext4.h | 3 ++- fs/ext4/ialloc.c | 2 +- fs/ext4/inode.c | 11 ++++++++--- fs/ext4/super.c | 10 +++++----- 4 files changed, 16 insertions(+), 10 deletions(-) (limited to 'fs') diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 19a4de57128a..6a0d52ca1434 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -1571,7 +1571,8 @@ extern int ext4_write_inode(struct inode *, struct writeback_control *); extern int ext4_setattr(struct dentry *, struct iattr *); extern int ext4_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat); -extern void ext4_delete_inode(struct inode *); +extern void ext4_evict_inode(struct inode *); +extern void ext4_clear_inode(struct inode *); extern int ext4_sync_inode(handle_t *, struct inode *); extern void ext4_dirty_inode(struct inode *); extern int ext4_change_inode_journal_flag(struct inode *, int); diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c index 25c4b3173fd9..07e791a856da 100644 --- a/fs/ext4/ialloc.c +++ b/fs/ext4/ialloc.c @@ -222,7 +222,7 @@ void ext4_free_inode(handle_t *handle, struct inode *inode) is_directory = S_ISDIR(inode->i_mode); /* Do this BEFORE marking the inode not in use or returning an error */ - clear_inode(inode); + ext4_clear_inode(inode); es = EXT4_SB(sb)->s_es; if (ino < EXT4_FIRST_INO(sb) || ino > le32_to_cpu(es->s_inodes_count)) { diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 1fb390359bc5..c6d365f9c663 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -167,11 +167,16 @@ int ext4_truncate_restart_trans(handle_t *handle, struct inode *inode, /* * Called at the last iput() if i_nlink is zero. */ -void ext4_delete_inode(struct inode *inode) +void ext4_evict_inode(struct inode *inode) { handle_t *handle; int err; + if (inode->i_nlink) { + truncate_inode_pages(&inode->i_data, 0); + goto no_delete; + } + if (!is_bad_inode(inode)) dquot_initialize(inode); @@ -245,13 +250,13 @@ void ext4_delete_inode(struct inode *inode) */ if (ext4_mark_inode_dirty(handle, inode)) /* If that failed, just do the required in-core inode clear. */ - clear_inode(inode); + ext4_clear_inode(inode); else ext4_free_inode(handle, inode); ext4_journal_stop(handle); return; no_delete: - clear_inode(inode); /* We must guarantee clearing of inode... */ + ext4_clear_inode(inode); /* We must guarantee clearing of inode... */ } typedef struct { diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 4e8983a9811b..f627a6a4c317 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -813,8 +813,10 @@ static void destroy_inodecache(void) kmem_cache_destroy(ext4_inode_cachep); } -static void ext4_clear_inode(struct inode *inode) +void ext4_clear_inode(struct inode *inode) { + invalidate_inode_buffers(inode); + end_writeback(inode); dquot_drop(inode); ext4_discard_preallocations(inode); if (EXT4_JOURNAL(inode)) @@ -1100,14 +1102,13 @@ static const struct super_operations ext4_sops = { .destroy_inode = ext4_destroy_inode, .write_inode = ext4_write_inode, .dirty_inode = ext4_dirty_inode, - .delete_inode = ext4_delete_inode, + .evict_inode = ext4_evict_inode, .put_super = ext4_put_super, .sync_fs = ext4_sync_fs, .freeze_fs = ext4_freeze, .unfreeze_fs = ext4_unfreeze, .statfs = ext4_statfs, .remount_fs = ext4_remount, - .clear_inode = ext4_clear_inode, .show_options = ext4_show_options, #ifdef CONFIG_QUOTA .quota_read = ext4_quota_read, @@ -1121,12 +1122,11 @@ static const struct super_operations ext4_nojournal_sops = { .destroy_inode = ext4_destroy_inode, .write_inode = ext4_write_inode, .dirty_inode = ext4_dirty_inode, - .delete_inode = ext4_delete_inode, + .evict_inode = ext4_evict_inode, .write_super = ext4_write_super, .put_super = ext4_put_super, .statfs = ext4_statfs, .remount_fs = ext4_remount, - .clear_inode = ext4_clear_inode, .show_options = ext4_show_options, #ifdef CONFIG_QUOTA .quota_read = ext4_quota_read, -- cgit v1.2.3 From 07958f9f5b9e8422c15368a1733a52ea99009896 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 7 Jun 2010 13:20:09 -0400 Subject: ->delete_inode() is gone Signed-off-by: Al Viro --- fs/inode.c | 2 -- include/linux/fs.h | 1 - 2 files changed, 3 deletions(-) (limited to 'fs') diff --git a/fs/inode.c b/fs/inode.c index 93e7a5ecbc26..7a1bea9cb8ee 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -335,8 +335,6 @@ static void evict(struct inode *inode, int delete) if (op->evict_inode) { op->evict_inode(inode); - } else if (delete && op->delete_inode) { - op->delete_inode(inode); } else { if (inode->i_data.nrpages) truncate_inode_pages(&inode->i_data, 0); diff --git a/include/linux/fs.h b/include/linux/fs.h index 3c23c1dcb1bd..2b1254771e46 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1564,7 +1564,6 @@ struct super_operations { int (*write_inode) (struct inode *, struct writeback_control *wbc); void (*drop_inode) (struct inode *); void (*evict_inode) (struct inode *); - void (*delete_inode) (struct inode *); void (*put_super) (struct super_block *); void (*write_super) (struct super_block *); int (*sync_fs)(struct super_block *sb, int wait); -- cgit v1.2.3 From 644da5960ded137c339bc69bc2aeac54f73aad59 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 7 Jun 2010 13:21:05 -0400 Subject: fs/inode.c:evict() doesn't care about delete vs. non-delete paths now Signed-off-by: Al Viro --- fs/inode.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/inode.c b/fs/inode.c index 7a1bea9cb8ee..8320bef7177e 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -329,7 +329,7 @@ void clear_inode(struct inode *inode) } EXPORT_SYMBOL(clear_inode); -static void evict(struct inode *inode, int delete) +static void evict(struct inode *inode) { const struct super_operations *op = inode->i_sb->s_op; @@ -363,7 +363,7 @@ static void dispose_list(struct list_head *head) inode = list_first_entry(head, struct inode, i_list); list_del(&inode->i_list); - evict(inode, 0); + evict(inode); spin_lock(&inode_lock); hlist_del_init(&inode->i_hash); @@ -1224,7 +1224,7 @@ void generic_delete_inode(struct inode *inode) inodes_stat.nr_inodes--; spin_unlock(&inode_lock); - evict(inode, 1); + evict(inode); spin_lock(&inode_lock); hlist_del_init(&inode->i_hash); @@ -1279,7 +1279,7 @@ static void generic_forget_inode(struct inode *inode) { if (!generic_detach_inode(inode)) return; - evict(inode, 0); + evict(inode); wake_up_inode(inode); destroy_inode(inode); } -- cgit v1.2.3 From 30140837f256558c943636245ab90897a9455a70 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 7 Jun 2010 13:23:20 -0400 Subject: fs/inode.c:clear_inode() is gone Signed-off-by: Al Viro --- fs/inode.c | 28 ++++------------------------ include/linux/fs.h | 1 - 2 files changed, 4 insertions(+), 25 deletions(-) (limited to 'fs') diff --git a/fs/inode.c b/fs/inode.c index 8320bef7177e..82ca3562a688 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -306,29 +306,6 @@ void end_writeback(struct inode *inode) } EXPORT_SYMBOL(end_writeback); -/** - * clear_inode - clear an inode - * @inode: inode to clear - * - * This is called by the filesystem to tell us - * that the inode is no longer useful. We just - * terminate it with extreme prejudice. - */ -void clear_inode(struct inode *inode) -{ - might_sleep(); - invalidate_inode_buffers(inode); - - BUG_ON(inode->i_data.nrpages); - BUG_ON(!(inode->i_state & I_FREEING)); - BUG_ON(inode->i_state & I_CLEAR); - inode_sync_wait(inode); - if (inode->i_sb->s_op->clear_inode) - inode->i_sb->s_op->clear_inode(inode); - inode->i_state = I_FREEING | I_CLEAR; -} -EXPORT_SYMBOL(clear_inode); - static void evict(struct inode *inode) { const struct super_operations *op = inode->i_sb->s_op; @@ -338,7 +315,10 @@ static void evict(struct inode *inode) } else { if (inode->i_data.nrpages) truncate_inode_pages(&inode->i_data, 0); - clear_inode(inode); + invalidate_inode_buffers(inode); + end_writeback(inode); + if (op->clear_inode) + op->clear_inode(inode); } if (S_ISBLK(inode->i_mode) && inode->i_bdev) bd_forget(inode); diff --git a/include/linux/fs.h b/include/linux/fs.h index 2b1254771e46..4eaa6b2e35db 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2182,7 +2182,6 @@ extern void unlock_new_inode(struct inode *); extern void __iget(struct inode * inode); extern void iget_failed(struct inode *); -extern void clear_inode(struct inode *); extern void end_writeback(struct inode *); extern void destroy_inode(struct inode *); extern void __destroy_inode(struct inode *); -- cgit v1.2.3 From 45321ac54316eaeeebde0b5f728a1791e500974c Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 7 Jun 2010 13:43:19 -0400 Subject: Make ->drop_inode() just return whether inode needs to be dropped ... and let iput_final() do the actual eviction or retention Signed-off-by: Al Viro --- drivers/staging/pohmelfs/inode.c | 4 +- fs/btrfs/ctree.h | 2 +- fs/btrfs/inode.c | 11 ++-- fs/cifs/cifsfs.c | 9 ++-- fs/gfs2/super.c | 4 +- fs/inode.c | 113 ++++++++++++--------------------------- fs/logfs/inode.c | 4 +- fs/ocfs2/inode.c | 8 +-- fs/ocfs2/inode.h | 2 +- include/linux/fs.h | 6 +-- 10 files changed, 60 insertions(+), 103 deletions(-) (limited to 'fs') diff --git a/drivers/staging/pohmelfs/inode.c b/drivers/staging/pohmelfs/inode.c index e818f53ccfd7..100e3a3c1b10 100644 --- a/drivers/staging/pohmelfs/inode.c +++ b/drivers/staging/pohmelfs/inode.c @@ -1223,7 +1223,7 @@ void pohmelfs_fill_inode(struct inode *inode, struct netfs_inode_info *info) } } -static void pohmelfs_drop_inode(struct inode *inode) +static int pohmelfs_drop_inode(struct inode *inode) { struct pohmelfs_sb *psb = POHMELFS_SB(inode->i_sb); struct pohmelfs_inode *pi = POHMELFS_I(inode); @@ -1232,7 +1232,7 @@ static void pohmelfs_drop_inode(struct inode *inode) list_del_init(&pi->inode_entry); spin_unlock(&psb->ino_lock); - generic_drop_inode(inode); + return generic_drop_inode(inode); } static struct pohmelfs_inode *pohmelfs_get_inode_from_list(struct pohmelfs_sb *psb, diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 394d5422ab6a..eaf286abad17 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -2395,7 +2395,7 @@ int btrfs_write_inode(struct inode *inode, struct writeback_control *wbc); void btrfs_dirty_inode(struct inode *inode); struct inode *btrfs_alloc_inode(struct super_block *sb); void btrfs_destroy_inode(struct inode *inode); -void btrfs_drop_inode(struct inode *inode); +int btrfs_drop_inode(struct inode *inode); int btrfs_init_cachep(void); void btrfs_destroy_cachep(void); long btrfs_ioctl_trans_end(struct file *file); diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index ce02199ec4e5..2c54f04a0bf5 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -3943,7 +3943,7 @@ again: if (atomic_read(&inode->i_count) > 1) d_prune_aliases(inode); /* - * btrfs_drop_inode will remove it from + * btrfs_drop_inode will have it removed from * the inode cache when its usage count * hits zero. */ @@ -6337,13 +6337,14 @@ free: kmem_cache_free(btrfs_inode_cachep, BTRFS_I(inode)); } -void btrfs_drop_inode(struct inode *inode) +int btrfs_drop_inode(struct inode *inode) { struct btrfs_root *root = BTRFS_I(inode)->root; - if (inode->i_nlink > 0 && btrfs_root_refs(&root->root_item) == 0) - generic_delete_inode(inode); + + if (btrfs_root_refs(&root->root_item) == 0) + return 1; else - generic_drop_inode(inode); + return generic_drop_inode(inode); } static void init_once(void *foo) diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 8a2cf129e535..20914f5627dd 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -480,14 +480,13 @@ static int cifs_remount(struct super_block *sb, int *flags, char *data) return 0; } -void cifs_drop_inode(struct inode *inode) +static int cifs_drop_inode(struct inode *inode) { struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) - return generic_drop_inode(inode); - - return generic_delete_inode(inode); + /* no serverino => unconditional eviction */ + return !(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) || + generic_drop_inode(inode); } static const struct super_operations cifs_super_ops = { diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c index 555f5a417c67..fa865ab37f12 100644 --- a/fs/gfs2/super.c +++ b/fs/gfs2/super.c @@ -1191,7 +1191,7 @@ static int gfs2_remount_fs(struct super_block *sb, int *flags, char *data) * node for later deallocation. */ -static void gfs2_drop_inode(struct inode *inode) +static int gfs2_drop_inode(struct inode *inode) { struct gfs2_inode *ip = GFS2_I(inode); @@ -1200,7 +1200,7 @@ static void gfs2_drop_inode(struct inode *inode) if (gl && test_bit(GLF_DEMOTE, &gl->gl_flags)) clear_nlink(inode); } - generic_drop_inode(inode); + return generic_drop_inode(inode); } static int is_ancestor(const struct dentry *d1, const struct dentry *d2) diff --git a/fs/inode.c b/fs/inode.c index 82ca3562a688..0e077619cbf6 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -1183,58 +1183,51 @@ void remove_inode_hash(struct inode *inode) } EXPORT_SYMBOL(remove_inode_hash); +int generic_delete_inode(struct inode *inode) +{ + return 1; +} +EXPORT_SYMBOL(generic_delete_inode); + /* - * Tell the filesystem that this inode is no longer of any interest and should - * be completely destroyed. - * - * We leave the inode in the inode hash table until *after* the filesystem's - * ->delete_inode completes. This ensures that an iget (such as nfsd might - * instigate) will always find up-to-date information either in the hash or on - * disk. - * - * I_FREEING is set so that no-one will take a new reference to the inode while - * it is being deleted. + * Normal UNIX filesystem behaviour: delete the + * inode when the usage count drops to zero, and + * i_nlink is zero. */ -void generic_delete_inode(struct inode *inode) +int generic_drop_inode(struct inode *inode) { - list_del_init(&inode->i_list); - list_del_init(&inode->i_sb_list); - WARN_ON(inode->i_state & I_NEW); - inode->i_state |= I_FREEING; - inodes_stat.nr_inodes--; - spin_unlock(&inode_lock); - - evict(inode); - - spin_lock(&inode_lock); - hlist_del_init(&inode->i_hash); - spin_unlock(&inode_lock); - wake_up_inode(inode); - BUG_ON(inode->i_state != (I_FREEING | I_CLEAR)); - destroy_inode(inode); + return !inode->i_nlink || hlist_unhashed(&inode->i_hash); } -EXPORT_SYMBOL(generic_delete_inode); +EXPORT_SYMBOL_GPL(generic_drop_inode); -/** - * generic_detach_inode - remove inode from inode lists - * @inode: inode to remove - * - * Remove inode from inode lists, write it if it's dirty. This is just an - * internal VFS helper exported for hugetlbfs. Do not use! +/* + * Called when we're dropping the last reference + * to an inode. * - * Returns 1 if inode should be completely destroyed. + * Call the FS "drop_inode()" function, defaulting to + * the legacy UNIX filesystem behaviour. If it tells + * us to evict inode, do so. Otherwise, retain inode + * in cache if fs is alive, sync and evict if fs is + * shutting down. */ -static int generic_detach_inode(struct inode *inode) +static void iput_final(struct inode *inode) { struct super_block *sb = inode->i_sb; + const struct super_operations *op = inode->i_sb->s_op; + int drop; + + if (op && op->drop_inode) + drop = op->drop_inode(inode); + else + drop = generic_drop_inode(inode); - if (!hlist_unhashed(&inode->i_hash)) { + if (!drop) { if (!(inode->i_state & (I_DIRTY|I_SYNC))) list_move(&inode->i_list, &inode_unused); inodes_stat.nr_unused++; if (sb->s_flags & MS_ACTIVE) { spin_unlock(&inode_lock); - return 0; + return; } WARN_ON(inode->i_state & I_NEW); inode->i_state |= I_WILL_FREE; @@ -1252,53 +1245,15 @@ static int generic_detach_inode(struct inode *inode) inode->i_state |= I_FREEING; inodes_stat.nr_inodes--; spin_unlock(&inode_lock); - return 1; -} - -static void generic_forget_inode(struct inode *inode) -{ - if (!generic_detach_inode(inode)) - return; evict(inode); + spin_lock(&inode_lock); + hlist_del_init(&inode->i_hash); + spin_unlock(&inode_lock); wake_up_inode(inode); + BUG_ON(inode->i_state != (I_FREEING | I_CLEAR)); destroy_inode(inode); } -/* - * Normal UNIX filesystem behaviour: delete the - * inode when the usage count drops to zero, and - * i_nlink is zero. - */ -void generic_drop_inode(struct inode *inode) -{ - if (!inode->i_nlink) - generic_delete_inode(inode); - else - generic_forget_inode(inode); -} -EXPORT_SYMBOL_GPL(generic_drop_inode); - -/* - * Called when we're dropping the last reference - * to an inode. - * - * Call the FS "drop()" function, defaulting to - * the legacy UNIX filesystem behaviour.. - * - * NOTE! NOTE! NOTE! We're called with the inode lock - * held, and the drop function is supposed to release - * the lock! - */ -static inline void iput_final(struct inode *inode) -{ - const struct super_operations *op = inode->i_sb->s_op; - void (*drop)(struct inode *) = generic_drop_inode; - - if (op && op->drop_inode) - drop = op->drop_inode; - drop(inode); -} - /** * iput - put an inode * @inode: inode to put diff --git a/fs/logfs/inode.c b/fs/logfs/inode.c index 78be674d95c8..d8c71ece098f 100644 --- a/fs/logfs/inode.c +++ b/fs/logfs/inode.c @@ -287,7 +287,7 @@ static int logfs_write_inode(struct inode *inode, struct writeback_control *wbc) } /* called with inode_lock held */ -static void logfs_drop_inode(struct inode *inode) +static int logfs_drop_inode(struct inode *inode) { struct logfs_super *super = logfs_super(inode->i_sb); struct logfs_inode *li = logfs_inode(inode); @@ -295,7 +295,7 @@ static void logfs_drop_inode(struct inode *inode) spin_lock(&logfs_inode_lock); list_move(&li->li_freeing_list, &super->s_freeing_list); spin_unlock(&logfs_inode_lock); - generic_drop_inode(inode); + return generic_drop_inode(inode); } static void logfs_set_ino_generation(struct super_block *sb, diff --git a/fs/ocfs2/inode.c b/fs/ocfs2/inode.c index eb7fd07c90f2..0492464916b1 100644 --- a/fs/ocfs2/inode.c +++ b/fs/ocfs2/inode.c @@ -1194,9 +1194,10 @@ void ocfs2_evict_inode(struct inode *inode) /* Called under inode_lock, with no more references on the * struct inode, so it's safe here to check the flags field * and to manipulate i_nlink without any other locks. */ -void ocfs2_drop_inode(struct inode *inode) +int ocfs2_drop_inode(struct inode *inode) { struct ocfs2_inode_info *oi = OCFS2_I(inode); + int res; mlog_entry_void(); @@ -1204,11 +1205,12 @@ void ocfs2_drop_inode(struct inode *inode) (unsigned long long)oi->ip_blkno, inode->i_nlink, oi->ip_flags); if (oi->ip_flags & OCFS2_INODE_MAYBE_ORPHANED) - generic_delete_inode(inode); + res = 1; else - generic_drop_inode(inode); + res = generic_drop_inode(inode); mlog_exit_void(); + return res; } /* diff --git a/fs/ocfs2/inode.h b/fs/ocfs2/inode.h index 975eedd7b243..6de5a869db30 100644 --- a/fs/ocfs2/inode.h +++ b/fs/ocfs2/inode.h @@ -124,7 +124,7 @@ static inline struct ocfs2_caching_info *INODE_CACHE(struct inode *inode) } void ocfs2_evict_inode(struct inode *inode); -void ocfs2_drop_inode(struct inode *inode); +int ocfs2_drop_inode(struct inode *inode); /* Flags for ocfs2_iget() */ #define OCFS2_FI_FLAG_SYSFILE 0x1 diff --git a/include/linux/fs.h b/include/linux/fs.h index 4eaa6b2e35db..8553adbda57b 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1562,7 +1562,7 @@ struct super_operations { void (*dirty_inode) (struct inode *); int (*write_inode) (struct inode *, struct writeback_control *wbc); - void (*drop_inode) (struct inode *); + int (*drop_inode) (struct inode *); void (*evict_inode) (struct inode *); void (*put_super) (struct super_block *); void (*write_super) (struct super_block *); @@ -2164,8 +2164,8 @@ extern void iput(struct inode *); extern struct inode * igrab(struct inode *); extern ino_t iunique(struct super_block *, ino_t); extern int inode_needs_sync(struct inode *inode); -extern void generic_delete_inode(struct inode *inode); -extern void generic_drop_inode(struct inode *inode); +extern int generic_delete_inode(struct inode *inode); +extern int generic_drop_inode(struct inode *inode); extern struct inode *ilookup5_nowait(struct super_block *sb, unsigned long hashval, int (*test)(struct inode *, void *), -- cgit v1.2.3 From b57922d97fd6f79b6dbe6db0c4fd30d219fa08c1 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 7 Jun 2010 14:34:48 -0400 Subject: convert remaining ->clear_inode() to ->evict_inode() Signed-off-by: Al Viro --- fs/9p/v9fs_vfs.h | 2 +- fs/9p/vfs_inode.c | 4 +++- fs/9p/vfs_super.c | 4 ++-- fs/afs/inode.c | 5 ++++- fs/afs/internal.h | 2 +- fs/afs/super.c | 2 +- fs/binfmt_misc.c | 5 +++-- fs/block_dev.c | 7 +++++-- fs/cifs/cifsfs.c | 6 ++++-- fs/coda/inode.c | 8 +++++--- fs/ecryptfs/super.c | 8 +++++--- fs/freevxfs/vxfs_extern.h | 2 +- fs/freevxfs/vxfs_inode.c | 8 +++++--- fs/freevxfs/vxfs_super.c | 2 +- fs/fuse/inode.c | 6 ++++-- fs/hfs/hfs_fs.h | 2 +- fs/hfs/inode.c | 4 +++- fs/hfs/super.c | 2 +- fs/hfsplus/super.c | 8 +++++--- fs/inode.c | 2 -- fs/jffs2/fs.c | 6 ++++-- fs/jffs2/os-linux.h | 2 +- fs/jffs2/super.c | 2 +- fs/jffs2/xattr.c | 2 +- fs/nfs/inode.c | 13 +++++++++++-- fs/nfs/internal.h | 4 ++-- fs/nfs/super.c | 4 ++-- fs/ntfs/inode.c | 7 +++++-- fs/ntfs/inode.h | 2 +- fs/ntfs/super.c | 2 +- fs/ocfs2/dlmfs/dlmfs.c | 7 +++---- fs/xfs/linux-2.6/xfs_super.c | 8 +++++--- fs/xfs/linux-2.6/xfs_trace.h | 2 +- include/linux/fs.h | 3 +-- 34 files changed, 94 insertions(+), 59 deletions(-) (limited to 'fs') diff --git a/fs/9p/v9fs_vfs.h b/fs/9p/v9fs_vfs.h index 32ef4009d030..3d056fe01b50 100644 --- a/fs/9p/v9fs_vfs.h +++ b/fs/9p/v9fs_vfs.h @@ -52,7 +52,7 @@ void v9fs_destroy_inode(struct inode *inode); #endif struct inode *v9fs_get_inode(struct super_block *sb, int mode); -void v9fs_clear_inode(struct inode *inode); +void v9fs_evict_inode(struct inode *inode); ino_t v9fs_qid2ino(struct p9_qid *qid); void v9fs_stat2inode(struct p9_wstat *, struct inode *, struct super_block *); int v9fs_dir_release(struct inode *inode, struct file *filp); diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index 4b3ad6ac9a41..b81ce206508d 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -387,8 +387,10 @@ error: * @inode: inode to release * */ -void v9fs_clear_inode(struct inode *inode) +void v9fs_evict_inode(struct inode *inode) { + truncate_inode_pages(inode->i_mapping, 0); + end_writeback(inode); filemap_fdatawrite(inode->i_mapping); #ifdef CONFIG_9P_FSCACHE diff --git a/fs/9p/vfs_super.c b/fs/9p/vfs_super.c index be74d020436e..c6122bf547df 100644 --- a/fs/9p/vfs_super.c +++ b/fs/9p/vfs_super.c @@ -257,7 +257,7 @@ static const struct super_operations v9fs_super_ops = { .destroy_inode = v9fs_destroy_inode, #endif .statfs = simple_statfs, - .clear_inode = v9fs_clear_inode, + .evict_inode = v9fs_evict_inode, .show_options = generic_show_options, .umount_begin = v9fs_umount_begin, }; @@ -268,7 +268,7 @@ static const struct super_operations v9fs_super_ops_dotl = { .destroy_inode = v9fs_destroy_inode, #endif .statfs = v9fs_statfs, - .clear_inode = v9fs_clear_inode, + .evict_inode = v9fs_evict_inode, .show_options = generic_show_options, .umount_begin = v9fs_umount_begin, }; diff --git a/fs/afs/inode.c b/fs/afs/inode.c index d00b312e3110..320ffef11574 100644 --- a/fs/afs/inode.c +++ b/fs/afs/inode.c @@ -316,7 +316,7 @@ int afs_getattr(struct vfsmount *mnt, struct dentry *dentry, /* * clear an AFS inode */ -void afs_clear_inode(struct inode *inode) +void afs_evict_inode(struct inode *inode) { struct afs_permits *permits; struct afs_vnode *vnode; @@ -335,6 +335,9 @@ void afs_clear_inode(struct inode *inode) ASSERTCMP(inode->i_ino, ==, vnode->fid.vnode); + truncate_inode_pages(&inode->i_data, 0); + end_writeback(inode); + afs_give_up_callback(vnode); if (vnode->server) { diff --git a/fs/afs/internal.h b/fs/afs/internal.h index 5f679b77ce24..8679089ce9a1 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -565,7 +565,7 @@ extern void afs_zap_data(struct afs_vnode *); extern int afs_validate(struct afs_vnode *, struct key *); extern int afs_getattr(struct vfsmount *, struct dentry *, struct kstat *); extern int afs_setattr(struct dentry *, struct iattr *); -extern void afs_clear_inode(struct inode *); +extern void afs_evict_inode(struct inode *); /* * main.c diff --git a/fs/afs/super.c b/fs/afs/super.c index e932e5a3a0c1..9cf80f02da16 100644 --- a/fs/afs/super.c +++ b/fs/afs/super.c @@ -49,7 +49,7 @@ static const struct super_operations afs_super_ops = { .statfs = afs_statfs, .alloc_inode = afs_alloc_inode, .destroy_inode = afs_destroy_inode, - .clear_inode = afs_clear_inode, + .evict_inode = afs_evict_inode, .put_super = afs_put_super, .show_options = generic_show_options, }; diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c index c4e83537ead7..9e60fd201716 100644 --- a/fs/binfmt_misc.c +++ b/fs/binfmt_misc.c @@ -502,8 +502,9 @@ static struct inode *bm_get_inode(struct super_block *sb, int mode) return inode; } -static void bm_clear_inode(struct inode *inode) +static void bm_evict_inode(struct inode *inode) { + end_writeback(inode); kfree(inode->i_private); } @@ -685,7 +686,7 @@ static const struct file_operations bm_status_operations = { static const struct super_operations s_ops = { .statfs = simple_statfs, - .clear_inode = bm_clear_inode, + .evict_inode = bm_evict_inode, }; static int bm_fill_super(struct super_block * sb, void * data, int silent) diff --git a/fs/block_dev.c b/fs/block_dev.c index 63c9d6076205..de7b4d0c7e30 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -426,10 +426,13 @@ static inline void __bd_forget(struct inode *inode) inode->i_mapping = &inode->i_data; } -static void bdev_clear_inode(struct inode *inode) +static void bdev_evict_inode(struct inode *inode) { struct block_device *bdev = &BDEV_I(inode)->bdev; struct list_head *p; + truncate_inode_pages(&inode->i_data, 0); + invalidate_inode_buffers(inode); /* is it needed here? */ + end_writeback(inode); spin_lock(&bdev_lock); while ( (p = bdev->bd_inodes.next) != &bdev->bd_inodes ) { __bd_forget(list_entry(p, struct inode, i_devices)); @@ -443,7 +446,7 @@ static const struct super_operations bdev_sops = { .alloc_inode = bdev_alloc_inode, .destroy_inode = bdev_destroy_inode, .drop_inode = generic_delete_inode, - .clear_inode = bdev_clear_inode, + .evict_inode = bdev_evict_inode, }; static int bd_get_sb(struct file_system_type *fs_type, diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 20914f5627dd..5574a42b7bb6 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -330,8 +330,10 @@ cifs_destroy_inode(struct inode *inode) } static void -cifs_clear_inode(struct inode *inode) +cifs_evict_inode(struct inode *inode) { + truncate_inode_pages(&inode->i_data, 0); + end_writeback(inode); cifs_fscache_release_inode_cookie(inode); } @@ -495,7 +497,7 @@ static const struct super_operations cifs_super_ops = { .alloc_inode = cifs_alloc_inode, .destroy_inode = cifs_destroy_inode, .drop_inode = cifs_drop_inode, - .clear_inode = cifs_clear_inode, + .evict_inode = cifs_evict_inode, /* .delete_inode = cifs_delete_inode, */ /* Do not need above function unless later we add lazy close of inodes or unless the kernel forgets to call us with the same number of releases (closes) diff --git a/fs/coda/inode.c b/fs/coda/inode.c index d97f9935a028..6526e6f21ecf 100644 --- a/fs/coda/inode.c +++ b/fs/coda/inode.c @@ -35,7 +35,7 @@ #include "coda_int.h" /* VFS super_block ops */ -static void coda_clear_inode(struct inode *); +static void coda_evict_inode(struct inode *); static void coda_put_super(struct super_block *); static int coda_statfs(struct dentry *dentry, struct kstatfs *buf); @@ -93,7 +93,7 @@ static const struct super_operations coda_super_operations = { .alloc_inode = coda_alloc_inode, .destroy_inode = coda_destroy_inode, - .clear_inode = coda_clear_inode, + .evict_inode = coda_evict_inode, .put_super = coda_put_super, .statfs = coda_statfs, .remount_fs = coda_remount, @@ -224,8 +224,10 @@ static void coda_put_super(struct super_block *sb) printk("Coda: Bye bye.\n"); } -static void coda_clear_inode(struct inode *inode) +static void coda_evict_inode(struct inode *inode) { + truncate_inode_pages(&inode->i_data, 0); + end_writeback(inode); coda_cache_clear_inode(inode); } diff --git a/fs/ecryptfs/super.c b/fs/ecryptfs/super.c index 0435886e4a9f..4b5de6c6e0fa 100644 --- a/fs/ecryptfs/super.c +++ b/fs/ecryptfs/super.c @@ -122,7 +122,7 @@ static int ecryptfs_statfs(struct dentry *dentry, struct kstatfs *buf) } /** - * ecryptfs_clear_inode + * ecryptfs_evict_inode * @inode - The ecryptfs inode * * Called by iput() when the inode reference count reached zero @@ -131,8 +131,10 @@ static int ecryptfs_statfs(struct dentry *dentry, struct kstatfs *buf) * on the inode free list. We use this to drop out reference to the * lower inode. */ -static void ecryptfs_clear_inode(struct inode *inode) +static void ecryptfs_evict_inode(struct inode *inode) { + truncate_inode_pages(&inode->i_data, 0); + end_writeback(inode); iput(ecryptfs_inode_to_lower(inode)); } @@ -184,6 +186,6 @@ const struct super_operations ecryptfs_sops = { .drop_inode = generic_delete_inode, .statfs = ecryptfs_statfs, .remount_fs = NULL, - .clear_inode = ecryptfs_clear_inode, + .evict_inode = ecryptfs_evict_inode, .show_options = ecryptfs_show_options }; diff --git a/fs/freevxfs/vxfs_extern.h b/fs/freevxfs/vxfs_extern.h index 50ab5eecb99b..881aa3d217f0 100644 --- a/fs/freevxfs/vxfs_extern.h +++ b/fs/freevxfs/vxfs_extern.h @@ -63,7 +63,7 @@ extern void vxfs_put_fake_inode(struct inode *); extern struct vxfs_inode_info * vxfs_blkiget(struct super_block *, u_long, ino_t); extern struct vxfs_inode_info * vxfs_stiget(struct super_block *, ino_t); extern struct inode * vxfs_iget(struct super_block *, ino_t); -extern void vxfs_clear_inode(struct inode *); +extern void vxfs_evict_inode(struct inode *); /* vxfs_lookup.c */ extern const struct inode_operations vxfs_dir_inode_ops; diff --git a/fs/freevxfs/vxfs_inode.c b/fs/freevxfs/vxfs_inode.c index 03a6ea5e99f7..79d1b4ea13e7 100644 --- a/fs/freevxfs/vxfs_inode.c +++ b/fs/freevxfs/vxfs_inode.c @@ -337,15 +337,17 @@ vxfs_iget(struct super_block *sbp, ino_t ino) } /** - * vxfs_clear_inode - remove inode from main memory + * vxfs_evict_inode - remove inode from main memory * @ip: inode to discard. * * Description: - * vxfs_clear_inode() is called on the final iput and frees the private + * vxfs_evict_inode() is called on the final iput and frees the private * inode area. */ void -vxfs_clear_inode(struct inode *ip) +vxfs_evict_inode(struct inode *ip) { + truncate_inode_pages(&ip->i_data, 0); + end_writeback(ip); kmem_cache_free(vxfs_inode_cachep, ip->i_private); } diff --git a/fs/freevxfs/vxfs_super.c b/fs/freevxfs/vxfs_super.c index 1e8af939b3e4..1f3ffd93b357 100644 --- a/fs/freevxfs/vxfs_super.c +++ b/fs/freevxfs/vxfs_super.c @@ -61,7 +61,7 @@ static int vxfs_statfs(struct dentry *, struct kstatfs *); static int vxfs_remount(struct super_block *, int *, char *); static const struct super_operations vxfs_super_ops = { - .clear_inode = vxfs_clear_inode, + .evict_inode = vxfs_evict_inode, .put_super = vxfs_put_super, .statfs = vxfs_statfs, .remount_fs = vxfs_remount, diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index ec14d19ce501..da9e6e11374c 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -122,8 +122,10 @@ void fuse_send_forget(struct fuse_conn *fc, struct fuse_req *req, fuse_request_send_noreply(fc, req); } -static void fuse_clear_inode(struct inode *inode) +static void fuse_evict_inode(struct inode *inode) { + truncate_inode_pages(&inode->i_data, 0); + end_writeback(inode); if (inode->i_sb->s_flags & MS_ACTIVE) { struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_inode *fi = get_fuse_inode(inode); @@ -736,7 +738,7 @@ static const struct export_operations fuse_export_operations = { static const struct super_operations fuse_super_operations = { .alloc_inode = fuse_alloc_inode, .destroy_inode = fuse_destroy_inode, - .clear_inode = fuse_clear_inode, + .evict_inode = fuse_evict_inode, .drop_inode = generic_delete_inode, .remount_fs = fuse_remount_fs, .put_super = fuse_put_super, diff --git a/fs/hfs/hfs_fs.h b/fs/hfs/hfs_fs.h index fe35e3b626c4..4f55651aaa51 100644 --- a/fs/hfs/hfs_fs.h +++ b/fs/hfs/hfs_fs.h @@ -193,7 +193,7 @@ extern int hfs_inode_setattr(struct dentry *, struct iattr *); extern void hfs_inode_read_fork(struct inode *inode, struct hfs_extent *ext, __be32 log_size, __be32 phys_size, u32 clump_size); extern struct inode *hfs_iget(struct super_block *, struct hfs_cat_key *, hfs_cat_rec *); -extern void hfs_clear_inode(struct inode *); +extern void hfs_evict_inode(struct inode *); extern void hfs_delete_inode(struct inode *); /* attr.c */ diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c index 93ceec8fbb8f..397b7adc7ce6 100644 --- a/fs/hfs/inode.c +++ b/fs/hfs/inode.c @@ -531,8 +531,10 @@ out: return NULL; } -void hfs_clear_inode(struct inode *inode) +void hfs_evict_inode(struct inode *inode) { + truncate_inode_pages(&inode->i_data, 0); + end_writeback(inode); if (HFS_IS_RSRC(inode) && HFS_I(inode)->rsrc_inode) { HFS_I(HFS_I(inode)->rsrc_inode)->rsrc_inode = NULL; iput(HFS_I(inode)->rsrc_inode); diff --git a/fs/hfs/super.c b/fs/hfs/super.c index 0a81eb7111f3..34235d4bf08b 100644 --- a/fs/hfs/super.c +++ b/fs/hfs/super.c @@ -181,7 +181,7 @@ static const struct super_operations hfs_super_operations = { .alloc_inode = hfs_alloc_inode, .destroy_inode = hfs_destroy_inode, .write_inode = hfs_write_inode, - .clear_inode = hfs_clear_inode, + .evict_inode = hfs_evict_inode, .put_super = hfs_put_super, .write_super = hfs_write_super, .sync_fs = hfs_sync_fs, diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c index a32c241e4e45..3b55c050c742 100644 --- a/fs/hfsplus/super.c +++ b/fs/hfsplus/super.c @@ -145,9 +145,11 @@ static int hfsplus_write_inode(struct inode *inode, return ret; } -static void hfsplus_clear_inode(struct inode *inode) +static void hfsplus_evict_inode(struct inode *inode) { - dprint(DBG_INODE, "hfsplus_clear_inode: %lu\n", inode->i_ino); + dprint(DBG_INODE, "hfsplus_evict_inode: %lu\n", inode->i_ino); + truncate_inode_pages(&inode->i_data, 0); + end_writeback(inode); if (HFSPLUS_IS_RSRC(inode)) { HFSPLUS_I(HFSPLUS_I(inode).rsrc_inode).rsrc_inode = NULL; iput(HFSPLUS_I(inode).rsrc_inode); @@ -293,7 +295,7 @@ static const struct super_operations hfsplus_sops = { .alloc_inode = hfsplus_alloc_inode, .destroy_inode = hfsplus_destroy_inode, .write_inode = hfsplus_write_inode, - .clear_inode = hfsplus_clear_inode, + .evict_inode = hfsplus_evict_inode, .put_super = hfsplus_put_super, .write_super = hfsplus_write_super, .sync_fs = hfsplus_sync_fs, diff --git a/fs/inode.c b/fs/inode.c index 0e077619cbf6..5daeb0b8fb59 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -317,8 +317,6 @@ static void evict(struct inode *inode) truncate_inode_pages(&inode->i_data, 0); invalidate_inode_buffers(inode); end_writeback(inode); - if (op->clear_inode) - op->clear_inode(inode); } if (S_ISBLK(inode->i_mode) && inode->i_bdev) bd_forget(inode); diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c index 1b2426604fe3..ac0638f04969 100644 --- a/fs/jffs2/fs.c +++ b/fs/jffs2/fs.c @@ -225,7 +225,7 @@ int jffs2_statfs(struct dentry *dentry, struct kstatfs *buf) } -void jffs2_clear_inode (struct inode *inode) +void jffs2_evict_inode (struct inode *inode) { /* We can forget about this inode for now - drop all * the nodelists associated with it, etc. @@ -233,7 +233,9 @@ void jffs2_clear_inode (struct inode *inode) struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); - D1(printk(KERN_DEBUG "jffs2_clear_inode(): ino #%lu mode %o\n", inode->i_ino, inode->i_mode)); + D1(printk(KERN_DEBUG "jffs2_evict_inode(): ino #%lu mode %o\n", inode->i_ino, inode->i_mode)); + truncate_inode_pages(&inode->i_data, 0); + end_writeback(inode); jffs2_do_clear_inode(c, f); } diff --git a/fs/jffs2/os-linux.h b/fs/jffs2/os-linux.h index 4791aacf3084..00bae7cc2e48 100644 --- a/fs/jffs2/os-linux.h +++ b/fs/jffs2/os-linux.h @@ -171,7 +171,7 @@ extern const struct inode_operations jffs2_symlink_inode_operations; int jffs2_setattr (struct dentry *, struct iattr *); int jffs2_do_setattr (struct inode *, struct iattr *); struct inode *jffs2_iget(struct super_block *, unsigned long); -void jffs2_clear_inode (struct inode *); +void jffs2_evict_inode (struct inode *); void jffs2_dirty_inode(struct inode *inode); struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_inode *ri); diff --git a/fs/jffs2/super.c b/fs/jffs2/super.c index 511e2d609d12..662bba099501 100644 --- a/fs/jffs2/super.c +++ b/fs/jffs2/super.c @@ -135,7 +135,7 @@ static const struct super_operations jffs2_super_operations = .write_super = jffs2_write_super, .statfs = jffs2_statfs, .remount_fs = jffs2_remount_fs, - .clear_inode = jffs2_clear_inode, + .evict_inode = jffs2_evict_inode, .dirty_inode = jffs2_dirty_inode, .sync_fs = jffs2_sync_fs, }; diff --git a/fs/jffs2/xattr.c b/fs/jffs2/xattr.c index d258e261bdc7..9b572ca40a49 100644 --- a/fs/jffs2/xattr.c +++ b/fs/jffs2/xattr.c @@ -588,7 +588,7 @@ static void delete_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *re void jffs2_xattr_delete_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic) { - /* It's called from jffs2_clear_inode() on inode removing. + /* It's called from jffs2_evict_inode() on inode removing. When an inode with XATTR is removed, those XATTRs must be removed. */ struct jffs2_xattr_ref *ref, *_ref; diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 099b3518feea..c211b8168e5b 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c @@ -98,7 +98,7 @@ u64 nfs_compat_user_ino64(u64 fileid) return ino; } -void nfs_clear_inode(struct inode *inode) +static void nfs_clear_inode(struct inode *inode) { /* * The following should never happen... @@ -110,6 +110,13 @@ void nfs_clear_inode(struct inode *inode) nfs_fscache_release_inode_cookie(inode); } +void nfs_evict_inode(struct inode *inode) +{ + truncate_inode_pages(&inode->i_data, 0); + end_writeback(inode); + nfs_clear_inode(inode); +} + /** * nfs_sync_mapping - helper to flush all mmapped dirty data to disk */ @@ -1338,8 +1345,10 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr) * to open() calls that passed nfs_atomic_lookup, but failed to call * nfs_open(). */ -void nfs4_clear_inode(struct inode *inode) +void nfs4_evict_inode(struct inode *inode) { + truncate_inode_pages(&inode->i_data, 0); + end_writeback(inode); /* If we are holding a delegation, return it! */ nfs_inode_return_delegation_noreclaim(inode); /* First call standard NFS clear_inode() code */ diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index e70f44b9b3f4..f168ebdf7c6d 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -213,9 +213,9 @@ extern struct workqueue_struct *nfsiod_workqueue; extern struct inode *nfs_alloc_inode(struct super_block *sb); extern void nfs_destroy_inode(struct inode *); extern int nfs_write_inode(struct inode *, struct writeback_control *); -extern void nfs_clear_inode(struct inode *); +extern void nfs_evict_inode(struct inode *); #ifdef CONFIG_NFS_V4 -extern void nfs4_clear_inode(struct inode *); +extern void nfs4_evict_inode(struct inode *); #endif void nfs_zap_acl_cache(struct inode *inode); extern int nfs_wait_bit_killable(void *word); diff --git a/fs/nfs/super.c b/fs/nfs/super.c index f9df16de4a56..ef2b7e468a7e 100644 --- a/fs/nfs/super.c +++ b/fs/nfs/super.c @@ -270,7 +270,7 @@ static const struct super_operations nfs_sops = { .write_inode = nfs_write_inode, .put_super = nfs_put_super, .statfs = nfs_statfs, - .clear_inode = nfs_clear_inode, + .evict_inode = nfs_evict_inode, .umount_begin = nfs_umount_begin, .show_options = nfs_show_options, .show_stats = nfs_show_stats, @@ -340,7 +340,7 @@ static const struct super_operations nfs4_sops = { .write_inode = nfs_write_inode, .put_super = nfs_put_super, .statfs = nfs_statfs, - .clear_inode = nfs4_clear_inode, + .evict_inode = nfs4_evict_inode, .umount_begin = nfs_umount_begin, .show_options = nfs_show_options, .show_stats = nfs_show_stats, diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c index fdef8f729c3a..93622b175fc7 100644 --- a/fs/ntfs/inode.c +++ b/fs/ntfs/inode.c @@ -2238,7 +2238,7 @@ void ntfs_clear_extent_inode(ntfs_inode *ni) } /** - * ntfs_clear_big_inode - clean up the ntfs specific part of an inode + * ntfs_evict_big_inode - clean up the ntfs specific part of an inode * @vi: vfs inode pending annihilation * * When the VFS is going to remove an inode from memory, ntfs_clear_big_inode() @@ -2247,10 +2247,13 @@ void ntfs_clear_extent_inode(ntfs_inode *ni) * * If the MFT record is dirty, we commit it before doing anything else. */ -void ntfs_clear_big_inode(struct inode *vi) +void ntfs_evict_big_inode(struct inode *vi) { ntfs_inode *ni = NTFS_I(vi); + truncate_inode_pages(&vi->i_data, 0); + end_writeback(vi); + #ifdef NTFS_RW if (NInoDirty(ni)) { bool was_bad = (is_bad_inode(vi)); diff --git a/fs/ntfs/inode.h b/fs/ntfs/inode.h index 9a113544605d..2dabf813456c 100644 --- a/fs/ntfs/inode.h +++ b/fs/ntfs/inode.h @@ -279,7 +279,7 @@ extern struct inode *ntfs_index_iget(struct inode *base_vi, ntfschar *name, extern struct inode *ntfs_alloc_big_inode(struct super_block *sb); extern void ntfs_destroy_big_inode(struct inode *inode); -extern void ntfs_clear_big_inode(struct inode *vi); +extern void ntfs_evict_big_inode(struct inode *vi); extern void __ntfs_init_inode(struct super_block *sb, ntfs_inode *ni); diff --git a/fs/ntfs/super.c b/fs/ntfs/super.c index 0de1db6cddbf..512806171bfa 100644 --- a/fs/ntfs/super.c +++ b/fs/ntfs/super.c @@ -2700,7 +2700,7 @@ static const struct super_operations ntfs_sops = { .put_super = ntfs_put_super, /* Syscall: umount. */ .statfs = ntfs_statfs, /* Syscall: statfs */ .remount_fs = ntfs_remount, /* Syscall: mount -o remount. */ - .clear_inode = ntfs_clear_big_inode, /* VFS: Called when an inode is + .evict_inode = ntfs_evict_big_inode, /* VFS: Called when an inode is removed from memory. */ //.umount_begin = NULL, /* Forced umount. */ .show_options = ntfs_show_options, /* Show mount options in diff --git a/fs/ocfs2/dlmfs/dlmfs.c b/fs/ocfs2/dlmfs/dlmfs.c index 85e4ccaedd1f..a43ebb11ad3b 100644 --- a/fs/ocfs2/dlmfs/dlmfs.c +++ b/fs/ocfs2/dlmfs/dlmfs.c @@ -357,13 +357,12 @@ static void dlmfs_destroy_inode(struct inode *inode) kmem_cache_free(dlmfs_inode_cache, DLMFS_I(inode)); } -static void dlmfs_clear_inode(struct inode *inode) +static void dlmfs_evict_inode(struct inode *inode) { int status; struct dlmfs_inode_private *ip; - if (!inode) - return; + end_writeback(inode); mlog(0, "inode %lu\n", inode->i_ino); @@ -633,7 +632,7 @@ static const struct super_operations dlmfs_ops = { .statfs = simple_statfs, .alloc_inode = dlmfs_alloc_inode, .destroy_inode = dlmfs_destroy_inode, - .clear_inode = dlmfs_clear_inode, + .evict_inode = dlmfs_evict_inode, .drop_inode = generic_delete_inode, }; diff --git a/fs/xfs/linux-2.6/xfs_super.c b/fs/xfs/linux-2.6/xfs_super.c index 758df94690ed..15c35b62ff14 100644 --- a/fs/xfs/linux-2.6/xfs_super.c +++ b/fs/xfs/linux-2.6/xfs_super.c @@ -1100,13 +1100,15 @@ xfs_fs_write_inode( } STATIC void -xfs_fs_clear_inode( +xfs_fs_evict_inode( struct inode *inode) { xfs_inode_t *ip = XFS_I(inode); - trace_xfs_clear_inode(ip); + trace_xfs_evict_inode(ip); + truncate_inode_pages(&inode->i_data, 0); + end_writeback(inode); XFS_STATS_INC(vn_rele); XFS_STATS_INC(vn_remove); XFS_STATS_DEC(vn_active); @@ -1622,7 +1624,7 @@ static const struct super_operations xfs_super_operations = { .destroy_inode = xfs_fs_destroy_inode, .dirty_inode = xfs_fs_dirty_inode, .write_inode = xfs_fs_write_inode, - .clear_inode = xfs_fs_clear_inode, + .evict_inode = xfs_fs_evict_inode, .put_super = xfs_fs_put_super, .sync_fs = xfs_fs_sync_fs, .freeze_fs = xfs_fs_freeze, diff --git a/fs/xfs/linux-2.6/xfs_trace.h b/fs/xfs/linux-2.6/xfs_trace.h index c657cdca2cd2..be5dffd282a1 100644 --- a/fs/xfs/linux-2.6/xfs_trace.h +++ b/fs/xfs/linux-2.6/xfs_trace.h @@ -581,7 +581,7 @@ DEFINE_INODE_EVENT(xfs_ioctl_setattr); DEFINE_INODE_EVENT(xfs_file_fsync); DEFINE_INODE_EVENT(xfs_destroy_inode); DEFINE_INODE_EVENT(xfs_write_inode); -DEFINE_INODE_EVENT(xfs_clear_inode); +DEFINE_INODE_EVENT(xfs_evict_inode); DEFINE_INODE_EVENT(xfs_dquot_dqalloc); DEFINE_INODE_EVENT(xfs_dquot_dqdetach); diff --git a/include/linux/fs.h b/include/linux/fs.h index 8553adbda57b..dec9ac598859 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1571,7 +1571,6 @@ struct super_operations { int (*unfreeze_fs) (struct super_block *); int (*statfs) (struct dentry *, struct kstatfs *); int (*remount_fs) (struct super_block *, int *, char *); - void (*clear_inode) (struct inode *); void (*umount_begin) (struct super_block *); int (*show_options)(struct seq_file *, struct vfsmount *); @@ -1616,7 +1615,7 @@ struct super_operations { * I_FREEING Set when inode is about to be freed but still has dirty * pages or buffers attached or the inode itself is still * dirty. - * I_CLEAR Added by clear_inode(). In this state the inode is clean + * I_CLEAR Added by end_writeback(). In this state the inode is clean * and can be destroyed. Inode keeps I_FREEING. * * Inodes that are I_WILL_FREE, I_FREEING or I_CLEAR are -- cgit v1.2.3 From b70a3e0702dee2ed9435e06a8bde7d9fa2228895 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 7 Jun 2010 14:35:46 -0400 Subject: All filesystems that need invalidate_inode_buffers() are doing that explicitly Signed-off-by: Al Viro --- fs/inode.c | 1 - 1 file changed, 1 deletion(-) (limited to 'fs') diff --git a/fs/inode.c b/fs/inode.c index 5daeb0b8fb59..2575244640a8 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -315,7 +315,6 @@ static void evict(struct inode *inode) } else { if (inode->i_data.nrpages) truncate_inode_pages(&inode->i_data, 0); - invalidate_inode_buffers(inode); end_writeback(inode); } if (S_ISBLK(inode->i_mode) && inode->i_bdev) -- cgit v1.2.3 From ebabe9a9001af0af56c0c2780ca1576246e7a74b Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 7 Jul 2010 18:53:11 +0200 Subject: pass a struct path to vfs_statfs We'll need the path to implement the flags field for statvfs support. We do have it available in all callers except: - ecryptfs_statfs. This one doesn't actually need vfs_statfs but just needs to do a caller to the lower filesystem statfs method. - sys_ustat. Add a non-exported statfs_by_dentry helper for it which doesn't won't be able to fill out the flags field later on. In addition rename the helpers for statfs vs fstatfs to do_*statfs instead of the misleading vfs prefix. Signed-off-by: Christoph Hellwig Signed-off-by: Al Viro --- arch/alpha/kernel/osf_sys.c | 8 ++++---- arch/parisc/hpux/sys_hpux.c | 10 ++++----- fs/cachefiles/bind.c | 2 +- fs/cachefiles/daemon.c | 6 +++++- fs/compat.c | 10 ++++----- fs/ecryptfs/super.c | 6 +++++- fs/nfsd/nfs4xdr.c | 6 +++++- fs/nfsd/vfs.c | 10 +++++++-- fs/statfs.c | 50 +++++++++++++++++++++++---------------------- include/linux/fs.h | 3 ++- kernel/acct.c | 2 +- 11 files changed, 67 insertions(+), 46 deletions(-) (limited to 'fs') diff --git a/arch/alpha/kernel/osf_sys.c b/arch/alpha/kernel/osf_sys.c index de9d39717808..88131c6e42e3 100644 --- a/arch/alpha/kernel/osf_sys.c +++ b/arch/alpha/kernel/osf_sys.c @@ -234,11 +234,11 @@ linux_to_osf_statfs(struct kstatfs *linux_stat, struct osf_statfs __user *osf_st } static int -do_osf_statfs(struct dentry * dentry, struct osf_statfs __user *buffer, +do_osf_statfs(struct path *path, struct osf_statfs __user *buffer, unsigned long bufsiz) { struct kstatfs linux_stat; - int error = vfs_statfs(dentry, &linux_stat); + int error = vfs_statfs(path, &linux_stat); if (!error) error = linux_to_osf_statfs(&linux_stat, buffer, bufsiz); return error; @@ -252,7 +252,7 @@ SYSCALL_DEFINE3(osf_statfs, char __user *, pathname, retval = user_path(pathname, &path); if (!retval) { - retval = do_osf_statfs(path.dentry, buffer, bufsiz); + retval = do_osf_statfs(&path buffer, bufsiz); path_put(&path); } return retval; @@ -267,7 +267,7 @@ SYSCALL_DEFINE3(osf_fstatfs, unsigned long, fd, retval = -EBADF; file = fget(fd); if (file) { - retval = do_osf_statfs(file->f_path.dentry, buffer, bufsiz); + retval = do_osf_statfs(&file->f_path, buffer, bufsiz); fput(file); } return retval; diff --git a/arch/parisc/hpux/sys_hpux.c b/arch/parisc/hpux/sys_hpux.c index 92343bd35fa3..ba430a03bc7a 100644 --- a/arch/parisc/hpux/sys_hpux.c +++ b/arch/parisc/hpux/sys_hpux.c @@ -145,7 +145,7 @@ static int hpux_ustat(dev_t dev, struct hpux_ustat __user *ubuf) s = user_get_super(dev); if (s == NULL) goto out; - err = vfs_statfs(s->s_root, &sbuf); + err = statfs_by_dentry(s->s_root, &sbuf); drop_super(s); if (err) goto out; @@ -186,12 +186,12 @@ struct hpux_statfs { int16_t f_pad; }; -static int vfs_statfs_hpux(struct dentry *dentry, struct hpux_statfs *buf) +static int do_statfs_hpux(struct path *path, struct hpux_statfs *buf) { struct kstatfs st; int retval; - retval = vfs_statfs(dentry, &st); + retval = vfs_statfs(path, &st); if (retval) return retval; @@ -219,7 +219,7 @@ asmlinkage long hpux_statfs(const char __user *pathname, error = user_path(pathname, &path); if (!error) { struct hpux_statfs tmp; - error = vfs_statfs_hpux(path.dentry, &tmp); + error = do_statfs_hpux(&path, &tmp); if (!error && copy_to_user(buf, &tmp, sizeof(tmp))) error = -EFAULT; path_put(&path); @@ -237,7 +237,7 @@ asmlinkage long hpux_fstatfs(unsigned int fd, struct hpux_statfs __user * buf) file = fget(fd); if (!file) goto out; - error = vfs_statfs_hpux(file->f_path.dentry, &tmp); + error = do_statfs_hpux(&file->f_path, &tmp); if (!error && copy_to_user(buf, &tmp, sizeof(tmp))) error = -EFAULT; fput(file); diff --git a/fs/cachefiles/bind.c b/fs/cachefiles/bind.c index 2906077ac798..a2603e7c0bb5 100644 --- a/fs/cachefiles/bind.c +++ b/fs/cachefiles/bind.c @@ -146,7 +146,7 @@ static int cachefiles_daemon_add_cache(struct cachefiles_cache *cache) goto error_unsupported; /* get the cache size and blocksize */ - ret = vfs_statfs(root, &stats); + ret = vfs_statfs(&path, &stats); if (ret < 0) goto error_unsupported; diff --git a/fs/cachefiles/daemon.c b/fs/cachefiles/daemon.c index c2413561ea75..24eb0d37241a 100644 --- a/fs/cachefiles/daemon.c +++ b/fs/cachefiles/daemon.c @@ -683,6 +683,10 @@ int cachefiles_has_space(struct cachefiles_cache *cache, unsigned fnr, unsigned bnr) { struct kstatfs stats; + struct path path = { + .mnt = cache->mnt, + .dentry = cache->mnt->mnt_root, + }; int ret; //_enter("{%llu,%llu,%llu,%llu,%llu,%llu},%u,%u", @@ -697,7 +701,7 @@ int cachefiles_has_space(struct cachefiles_cache *cache, /* find out how many pages of blockdev are available */ memset(&stats, 0, sizeof(stats)); - ret = vfs_statfs(cache->mnt->mnt_root, &stats); + ret = vfs_statfs(&path, &stats); if (ret < 0) { if (ret == -EIO) cachefiles_io_error(cache, "statfs failed"); diff --git a/fs/compat.c b/fs/compat.c index 6490d2134ff3..fc6c2adf2f6b 100644 --- a/fs/compat.c +++ b/fs/compat.c @@ -266,7 +266,7 @@ asmlinkage long compat_sys_statfs(const char __user *pathname, struct compat_sta error = user_path(pathname, &path); if (!error) { struct kstatfs tmp; - error = vfs_statfs(path.dentry, &tmp); + error = vfs_statfs(&path, &tmp); if (!error) error = put_compat_statfs(buf, &tmp); path_put(&path); @@ -284,7 +284,7 @@ asmlinkage long compat_sys_fstatfs(unsigned int fd, struct compat_statfs __user file = fget(fd); if (!file) goto out; - error = vfs_statfs(file->f_path.dentry, &tmp); + error = vfs_statfs(&file->f_path, &tmp); if (!error) error = put_compat_statfs(buf, &tmp); fput(file); @@ -334,7 +334,7 @@ asmlinkage long compat_sys_statfs64(const char __user *pathname, compat_size_t s error = user_path(pathname, &path); if (!error) { struct kstatfs tmp; - error = vfs_statfs(path.dentry, &tmp); + error = vfs_statfs(&path, &tmp); if (!error) error = put_compat_statfs64(buf, &tmp); path_put(&path); @@ -355,7 +355,7 @@ asmlinkage long compat_sys_fstatfs64(unsigned int fd, compat_size_t sz, struct c file = fget(fd); if (!file) goto out; - error = vfs_statfs(file->f_path.dentry, &tmp); + error = vfs_statfs(&file->f_path, &tmp); if (!error) error = put_compat_statfs64(buf, &tmp); fput(file); @@ -378,7 +378,7 @@ asmlinkage long compat_sys_ustat(unsigned dev, struct compat_ustat __user *u) sb = user_get_super(new_decode_dev(dev)); if (!sb) return -EINVAL; - err = vfs_statfs(sb->s_root, &sbuf); + err = statfs_by_dentry(sb->s_root, &sbuf); drop_super(sb); if (err) return err; diff --git a/fs/ecryptfs/super.c b/fs/ecryptfs/super.c index 4b5de6c6e0fa..f7fc286a3aa9 100644 --- a/fs/ecryptfs/super.c +++ b/fs/ecryptfs/super.c @@ -118,7 +118,11 @@ void ecryptfs_init_inode(struct inode *inode, struct inode *lower_inode) */ static int ecryptfs_statfs(struct dentry *dentry, struct kstatfs *buf) { - return vfs_statfs(ecryptfs_dentry_to_lower(dentry), buf); + struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry); + + if (!lower_dentry->d_sb->s_op->statfs) + return -ENOSYS; + return lower_dentry->d_sb->s_op->statfs(lower_dentry, buf); } /** diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index ac17a7080239..4d6154f66e04 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -1756,6 +1756,10 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp, struct nfs4_acl *acl = NULL; struct nfsd4_compoundres *resp = rqstp->rq_resp; u32 minorversion = resp->cstate.minorversion; + struct path path = { + .mnt = exp->ex_path.mnt, + .dentry = dentry, + }; BUG_ON(bmval1 & NFSD_WRITEONLY_ATTRS_WORD1); BUG_ON(bmval0 & ~nfsd_suppattrs0(minorversion)); @@ -1776,7 +1780,7 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp, FATTR4_WORD0_MAXNAME)) || (bmval1 & (FATTR4_WORD1_SPACE_AVAIL | FATTR4_WORD1_SPACE_FREE | FATTR4_WORD1_SPACE_TOTAL))) { - err = vfs_statfs(dentry, &statfs); + err = vfs_statfs(&path, &statfs); if (err) goto out_nfserr; } diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 3c111120b619..f6f1a718642f 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -2019,8 +2019,14 @@ out: __be32 nfsd_statfs(struct svc_rqst *rqstp, struct svc_fh *fhp, struct kstatfs *stat, int access) { - __be32 err = fh_verify(rqstp, fhp, 0, NFSD_MAY_NOP | access); - if (!err && vfs_statfs(fhp->fh_dentry,stat)) + struct path path = { + .mnt = fhp->fh_export->ex_path.mnt, + .dentry = fhp->fh_dentry, + }; + __be32 err; + + err = fh_verify(rqstp, fhp, 0, NFSD_MAY_NOP | access); + if (!err && vfs_statfs(&path, stat)) err = nfserr_io; return err; } diff --git a/fs/statfs.c b/fs/statfs.c index 4ef021f3b612..6a305709a4da 100644 --- a/fs/statfs.c +++ b/fs/statfs.c @@ -7,33 +7,35 @@ #include #include -int vfs_statfs(struct dentry *dentry, struct kstatfs *buf) +int statfs_by_dentry(struct dentry *dentry, struct kstatfs *buf) { - int retval = -ENODEV; - - if (dentry) { - retval = -ENOSYS; - if (dentry->d_sb->s_op->statfs) { - memset(buf, 0, sizeof(*buf)); - retval = security_sb_statfs(dentry); - if (retval) - return retval; - retval = dentry->d_sb->s_op->statfs(dentry, buf); - if (retval == 0 && buf->f_frsize == 0) - buf->f_frsize = buf->f_bsize; - } - } + int retval; + + if (!dentry->d_sb->s_op->statfs) + return -ENOSYS; + + memset(buf, 0, sizeof(*buf)); + retval = security_sb_statfs(dentry); + if (retval) + return retval; + retval = dentry->d_sb->s_op->statfs(dentry, buf); + if (retval == 0 && buf->f_frsize == 0) + buf->f_frsize = buf->f_bsize; return retval; } +int vfs_statfs(struct path *path, struct kstatfs *buf) +{ + return statfs_by_dentry(path->dentry, buf); +} EXPORT_SYMBOL(vfs_statfs); -static int vfs_statfs_native(struct dentry *dentry, struct statfs *buf) +static int do_statfs_native(struct path *path, struct statfs *buf) { struct kstatfs st; int retval; - retval = vfs_statfs(dentry, &st); + retval = vfs_statfs(path, &st); if (retval) return retval; @@ -72,12 +74,12 @@ static int vfs_statfs_native(struct dentry *dentry, struct statfs *buf) return 0; } -static int vfs_statfs64(struct dentry *dentry, struct statfs64 *buf) +static int do_statfs64(struct path *path, struct statfs64 *buf) { struct kstatfs st; int retval; - retval = vfs_statfs(dentry, &st); + retval = vfs_statfs(path, &st); if (retval) return retval; @@ -107,7 +109,7 @@ SYSCALL_DEFINE2(statfs, const char __user *, pathname, struct statfs __user *, b error = user_path(pathname, &path); if (!error) { struct statfs tmp; - error = vfs_statfs_native(path.dentry, &tmp); + error = do_statfs_native(&path, &tmp); if (!error && copy_to_user(buf, &tmp, sizeof(tmp))) error = -EFAULT; path_put(&path); @@ -125,7 +127,7 @@ SYSCALL_DEFINE3(statfs64, const char __user *, pathname, size_t, sz, struct stat error = user_path(pathname, &path); if (!error) { struct statfs64 tmp; - error = vfs_statfs64(path.dentry, &tmp); + error = do_statfs64(&path, &tmp); if (!error && copy_to_user(buf, &tmp, sizeof(tmp))) error = -EFAULT; path_put(&path); @@ -143,7 +145,7 @@ SYSCALL_DEFINE2(fstatfs, unsigned int, fd, struct statfs __user *, buf) file = fget(fd); if (!file) goto out; - error = vfs_statfs_native(file->f_path.dentry, &tmp); + error = do_statfs_native(&file->f_path, &tmp); if (!error && copy_to_user(buf, &tmp, sizeof(tmp))) error = -EFAULT; fput(file); @@ -164,7 +166,7 @@ SYSCALL_DEFINE3(fstatfs64, unsigned int, fd, size_t, sz, struct statfs64 __user file = fget(fd); if (!file) goto out; - error = vfs_statfs64(file->f_path.dentry, &tmp); + error = do_statfs64(&file->f_path, &tmp); if (!error && copy_to_user(buf, &tmp, sizeof(tmp))) error = -EFAULT; fput(file); @@ -183,7 +185,7 @@ SYSCALL_DEFINE2(ustat, unsigned, dev, struct ustat __user *, ubuf) if (!s) return -EINVAL; - err = vfs_statfs(s->s_root, &sbuf); + err = statfs_by_dentry(s->s_root, &sbuf); drop_super(s); if (err) return err; diff --git a/include/linux/fs.h b/include/linux/fs.h index dec9ac598859..9bedf4219f83 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1813,7 +1813,8 @@ extern struct vfsmount *collect_mounts(struct path *); extern void drop_collected_mounts(struct vfsmount *); extern int iterate_mounts(int (*)(struct vfsmount *, void *), void *, struct vfsmount *); -extern int vfs_statfs(struct dentry *, struct kstatfs *); +extern int vfs_statfs(struct path *, struct kstatfs *); +extern int statfs_by_dentry(struct dentry *, struct kstatfs *); extern int freeze_super(struct super_block *super); extern int thaw_super(struct super_block *super); diff --git a/kernel/acct.c b/kernel/acct.c index 385b88461c29..fa7eb3de2ddc 100644 --- a/kernel/acct.c +++ b/kernel/acct.c @@ -122,7 +122,7 @@ static int check_free_space(struct bsd_acct_struct *acct, struct file *file) spin_unlock(&acct_lock); /* May block */ - if (vfs_statfs(file->f_path.dentry, &sbuf)) + if (vfs_statfs(&file->f_path, &sbuf)) return res; suspend = sbuf.f_blocks * SUSPEND; resume = sbuf.f_blocks * RESUME; -- cgit v1.2.3 From 365b18189789bfa1acd9939e6312b8a4b4577b28 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 7 Jul 2010 18:53:25 +0200 Subject: add f_flags to struct statfs(64) Add a flags field to help glibc implementing statvfs(3) efficiently. We copy the flag values from glibc, and add a new ST_VALID flag to denote that f_flags is implemented. Signed-off-by: Christoph Hellwig Signed-off-by: Al Viro --- arch/mips/include/asm/statfs.h | 12 +++++++---- arch/s390/include/asm/statfs.h | 9 +++++--- fs/statfs.c | 47 +++++++++++++++++++++++++++++++++++++++++- include/asm-generic/statfs.h | 9 +++++--- include/linux/statfs.h | 25 ++++++++++++++++++++-- 5 files changed, 89 insertions(+), 13 deletions(-) (limited to 'fs') diff --git a/arch/mips/include/asm/statfs.h b/arch/mips/include/asm/statfs.h index c3ddf973c1c0..0f805c7a42a5 100644 --- a/arch/mips/include/asm/statfs.h +++ b/arch/mips/include/asm/statfs.h @@ -33,7 +33,8 @@ struct statfs { /* Linux specials */ __kernel_fsid_t f_fsid; long f_namelen; - long f_spare[6]; + long f_flags; + long f_spare[5]; }; #if (_MIPS_SIM == _MIPS_SIM_ABI32) || (_MIPS_SIM == _MIPS_SIM_NABI32) @@ -53,7 +54,8 @@ struct statfs64 { __u64 f_bavail; __kernel_fsid_t f_fsid; __u32 f_namelen; - __u32 f_spare[6]; + __u32 f_flags; + __u32 f_spare[5]; }; #endif /* _MIPS_SIM == _MIPS_SIM_ABI32 */ @@ -73,7 +75,8 @@ struct statfs64 { /* Same as struct statfs */ /* Linux specials */ __kernel_fsid_t f_fsid; long f_namelen; - long f_spare[6]; + long f_flags; + long f_spare[5]; }; struct compat_statfs64 { @@ -88,7 +91,8 @@ struct compat_statfs64 { __u64 f_bavail; __kernel_fsid_t f_fsid; __u32 f_namelen; - __u32 f_spare[6]; + __u32 f_flags; + __u32 f_spare[5]; }; #endif /* _MIPS_SIM == _MIPS_SIM_ABI64 */ diff --git a/arch/s390/include/asm/statfs.h b/arch/s390/include/asm/statfs.h index 06cc70307ece..3be7fbd406c8 100644 --- a/arch/s390/include/asm/statfs.h +++ b/arch/s390/include/asm/statfs.h @@ -33,7 +33,8 @@ struct statfs { __kernel_fsid_t f_fsid; int f_namelen; int f_frsize; - int f_spare[5]; + int f_flags; + int f_spare[4]; }; struct statfs64 { @@ -47,7 +48,8 @@ struct statfs64 { __kernel_fsid_t f_fsid; int f_namelen; int f_frsize; - int f_spare[5]; + int f_flags; + int f_spare[4]; }; struct compat_statfs64 { @@ -61,7 +63,8 @@ struct compat_statfs64 { __kernel_fsid_t f_fsid; __u32 f_namelen; __u32 f_frsize; - __u32 f_spare[5]; + __u32 f_flags; + __u32 f_spare[4]; }; #endif /* __s390x__ */ diff --git a/fs/statfs.c b/fs/statfs.c index 6a305709a4da..30ea8c8a996b 100644 --- a/fs/statfs.c +++ b/fs/statfs.c @@ -2,11 +2,49 @@ #include #include #include +#include #include #include #include #include +static int flags_by_mnt(int mnt_flags) +{ + int flags = 0; + + if (mnt_flags & MNT_READONLY) + flags |= ST_RDONLY; + if (mnt_flags & MNT_NOSUID) + flags |= ST_NOSUID; + if (mnt_flags & MNT_NODEV) + flags |= ST_NODEV; + if (mnt_flags & MNT_NOEXEC) + flags |= ST_NOEXEC; + if (mnt_flags & MNT_NOATIME) + flags |= ST_NOATIME; + if (mnt_flags & MNT_NODIRATIME) + flags |= ST_NODIRATIME; + if (mnt_flags & MNT_RELATIME) + flags |= ST_RELATIME; + return flags; +} + +static int flags_by_sb(int s_flags) +{ + int flags = 0; + if (s_flags & MS_SYNCHRONOUS) + flags |= ST_SYNCHRONOUS; + if (s_flags & MS_MANDLOCK) + flags |= ST_MANDLOCK; + return flags; +} + +static int calculate_f_flags(struct vfsmount *mnt) +{ + return ST_VALID | flags_by_mnt(mnt->mnt_flags) | + flags_by_sb(mnt->mnt_sb->s_flags); +} + int statfs_by_dentry(struct dentry *dentry, struct kstatfs *buf) { int retval; @@ -26,7 +64,12 @@ int statfs_by_dentry(struct dentry *dentry, struct kstatfs *buf) int vfs_statfs(struct path *path, struct kstatfs *buf) { - return statfs_by_dentry(path->dentry, buf); + int error; + + error = statfs_by_dentry(path->dentry, buf); + if (!error) + buf->f_flags = calculate_f_flags(path->mnt); + return error; } EXPORT_SYMBOL(vfs_statfs); @@ -69,6 +112,7 @@ static int do_statfs_native(struct path *path, struct statfs *buf) buf->f_fsid = st.f_fsid; buf->f_namelen = st.f_namelen; buf->f_frsize = st.f_frsize; + buf->f_flags = st.f_flags; memset(buf->f_spare, 0, sizeof(buf->f_spare)); } return 0; @@ -96,6 +140,7 @@ static int do_statfs64(struct path *path, struct statfs64 *buf) buf->f_fsid = st.f_fsid; buf->f_namelen = st.f_namelen; buf->f_frsize = st.f_frsize; + buf->f_flags = st.f_flags; memset(buf->f_spare, 0, sizeof(buf->f_spare)); } return 0; diff --git a/include/asm-generic/statfs.h b/include/asm-generic/statfs.h index 3b4fb3e52f0d..0fd28e028de1 100644 --- a/include/asm-generic/statfs.h +++ b/include/asm-generic/statfs.h @@ -33,7 +33,8 @@ struct statfs { __kernel_fsid_t f_fsid; __statfs_word f_namelen; __statfs_word f_frsize; - __statfs_word f_spare[5]; + __statfs_word f_flags; + __statfs_word f_spare[4]; }; /* @@ -55,7 +56,8 @@ struct statfs64 { __kernel_fsid_t f_fsid; __statfs_word f_namelen; __statfs_word f_frsize; - __statfs_word f_spare[5]; + __statfs_word f_flags; + __statfs_word f_spare[4]; } ARCH_PACK_STATFS64; /* @@ -77,7 +79,8 @@ struct compat_statfs64 { __kernel_fsid_t f_fsid; __u32 f_namelen; __u32 f_frsize; - __u32 f_spare[5]; + __u32 f_flags; + __u32 f_spare[4]; } ARCH_PACK_COMPAT_STATFS64; #endif diff --git a/include/linux/statfs.h b/include/linux/statfs.h index b34cc829f98d..0166d320a75d 100644 --- a/include/linux/statfs.h +++ b/include/linux/statfs.h @@ -2,7 +2,6 @@ #define _LINUX_STATFS_H #include - #include struct kstatfs { @@ -16,7 +15,29 @@ struct kstatfs { __kernel_fsid_t f_fsid; long f_namelen; long f_frsize; - long f_spare[5]; + long f_flags; + long f_spare[4]; }; +/* + * Definitions for the flag in f_flag. + * + * Generally these flags are equivalent to the MS_ flags used in the mount + * ABI. The exception is ST_VALID which has the same value as MS_REMOUNT + * which doesn't make any sense for statfs. + */ +#define ST_RDONLY 0x0001 /* mount read-only */ +#define ST_NOSUID 0x0002 /* ignore suid and sgid bits */ +#define ST_NODEV 0x0004 /* disallow access to device special files */ +#define ST_NOEXEC 0x0008 /* disallow program execution */ +#define ST_SYNCHRONOUS 0x0010 /* writes are synced at once */ +#define ST_VALID 0x0020 /* f_flags support is implemented */ +#define ST_MANDLOCK 0x0040 /* allow mandatory locks on an FS */ +/* 0x0080 used for ST_WRITE in glibc */ +/* 0x0100 used for ST_APPEND in glibc */ +/* 0x0200 used for ST_IMMUTABLE in glibc */ +#define ST_NOATIME 0x0400 /* do not update access times */ +#define ST_NODIRATIME 0x0800 /* do not update directory access times */ +#define ST_RELATIME 0x1000 /* update atime relative to mtime/ctime */ + #endif -- cgit v1.2.3 From 2aec7c523291621ebb68ba8e0bd9b52a26bb76ee Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Mon, 19 Jul 2010 18:19:41 +0200 Subject: mbcache: Remove unused features The mbcache code was written to support a variable number of indexes, but all the existing users use exactly one index. Simplify to code to support only that case. There are also no users of the cache entry free operation, and none of the users keep extra data in cache entries. Remove those features as well. Signed-off-by: Andreas Gruenbacher Signed-off-by: Al Viro --- fs/ext2/xattr.c | 12 ++--- fs/ext3/xattr.c | 12 ++--- fs/ext4/xattr.c | 12 ++--- fs/mbcache.c | 141 ++++++++++++++---------------------------------- include/linux/mbcache.h | 20 ++----- 5 files changed, 60 insertions(+), 137 deletions(-) (limited to 'fs') diff --git a/fs/ext2/xattr.c b/fs/ext2/xattr.c index 5ab87e6edffc..8c29ae15129e 100644 --- a/fs/ext2/xattr.c +++ b/fs/ext2/xattr.c @@ -843,7 +843,7 @@ ext2_xattr_cache_insert(struct buffer_head *bh) ce = mb_cache_entry_alloc(ext2_xattr_cache, GFP_NOFS); if (!ce) return -ENOMEM; - error = mb_cache_entry_insert(ce, bh->b_bdev, bh->b_blocknr, &hash); + error = mb_cache_entry_insert(ce, bh->b_bdev, bh->b_blocknr, hash); if (error) { mb_cache_entry_free(ce); if (error == -EBUSY) { @@ -917,8 +917,8 @@ ext2_xattr_cache_find(struct inode *inode, struct ext2_xattr_header *header) return NULL; /* never share */ ea_idebug(inode, "looking for cached blocks [%x]", (int)hash); again: - ce = mb_cache_entry_find_first(ext2_xattr_cache, 0, - inode->i_sb->s_bdev, hash); + ce = mb_cache_entry_find_first(ext2_xattr_cache, inode->i_sb->s_bdev, + hash); while (ce) { struct buffer_head *bh; @@ -950,7 +950,7 @@ again: unlock_buffer(bh); brelse(bh); } - ce = mb_cache_entry_find_next(ce, 0, inode->i_sb->s_bdev, hash); + ce = mb_cache_entry_find_next(ce, inode->i_sb->s_bdev, hash); } return NULL; } @@ -1026,9 +1026,7 @@ static void ext2_xattr_rehash(struct ext2_xattr_header *header, int __init init_ext2_xattr(void) { - ext2_xattr_cache = mb_cache_create("ext2_xattr", NULL, - sizeof(struct mb_cache_entry) + - sizeof(((struct mb_cache_entry *) 0)->e_indexes[0]), 1, 6); + ext2_xattr_cache = mb_cache_create("ext2_xattr", 6); if (!ext2_xattr_cache) return -ENOMEM; return 0; diff --git a/fs/ext3/xattr.c b/fs/ext3/xattr.c index 71fb8d65e54c..e69dc6dfaa89 100644 --- a/fs/ext3/xattr.c +++ b/fs/ext3/xattr.c @@ -1139,7 +1139,7 @@ ext3_xattr_cache_insert(struct buffer_head *bh) ea_bdebug(bh, "out of memory"); return; } - error = mb_cache_entry_insert(ce, bh->b_bdev, bh->b_blocknr, &hash); + error = mb_cache_entry_insert(ce, bh->b_bdev, bh->b_blocknr, hash); if (error) { mb_cache_entry_free(ce); if (error == -EBUSY) { @@ -1211,8 +1211,8 @@ ext3_xattr_cache_find(struct inode *inode, struct ext3_xattr_header *header, return NULL; /* never share */ ea_idebug(inode, "looking for cached blocks [%x]", (int)hash); again: - ce = mb_cache_entry_find_first(ext3_xattr_cache, 0, - inode->i_sb->s_bdev, hash); + ce = mb_cache_entry_find_first(ext3_xattr_cache, inode->i_sb->s_bdev, + hash); while (ce) { struct buffer_head *bh; @@ -1237,7 +1237,7 @@ again: return bh; } brelse(bh); - ce = mb_cache_entry_find_next(ce, 0, inode->i_sb->s_bdev, hash); + ce = mb_cache_entry_find_next(ce, inode->i_sb->s_bdev, hash); } return NULL; } @@ -1313,9 +1313,7 @@ static void ext3_xattr_rehash(struct ext3_xattr_header *header, int __init init_ext3_xattr(void) { - ext3_xattr_cache = mb_cache_create("ext3_xattr", NULL, - sizeof(struct mb_cache_entry) + - sizeof(((struct mb_cache_entry *) 0)->e_indexes[0]), 1, 6); + ext3_xattr_cache = mb_cache_create("ext3_xattr", 6); if (!ext3_xattr_cache) return -ENOMEM; return 0; diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c index 04338009793a..1c93198353e7 100644 --- a/fs/ext4/xattr.c +++ b/fs/ext4/xattr.c @@ -1418,7 +1418,7 @@ ext4_xattr_cache_insert(struct buffer_head *bh) ea_bdebug(bh, "out of memory"); return; } - error = mb_cache_entry_insert(ce, bh->b_bdev, bh->b_blocknr, &hash); + error = mb_cache_entry_insert(ce, bh->b_bdev, bh->b_blocknr, hash); if (error) { mb_cache_entry_free(ce); if (error == -EBUSY) { @@ -1490,8 +1490,8 @@ ext4_xattr_cache_find(struct inode *inode, struct ext4_xattr_header *header, return NULL; /* never share */ ea_idebug(inode, "looking for cached blocks [%x]", (int)hash); again: - ce = mb_cache_entry_find_first(ext4_xattr_cache, 0, - inode->i_sb->s_bdev, hash); + ce = mb_cache_entry_find_first(ext4_xattr_cache, inode->i_sb->s_bdev, + hash); while (ce) { struct buffer_head *bh; @@ -1515,7 +1515,7 @@ again: return bh; } brelse(bh); - ce = mb_cache_entry_find_next(ce, 0, inode->i_sb->s_bdev, hash); + ce = mb_cache_entry_find_next(ce, inode->i_sb->s_bdev, hash); } return NULL; } @@ -1591,9 +1591,7 @@ static void ext4_xattr_rehash(struct ext4_xattr_header *header, int __init init_ext4_xattr(void) { - ext4_xattr_cache = mb_cache_create("ext4_xattr", NULL, - sizeof(struct mb_cache_entry) + - sizeof(((struct mb_cache_entry *) 0)->e_indexes[0]), 1, 6); + ext4_xattr_cache = mb_cache_create("ext4_xattr", 6); if (!ext4_xattr_cache) return -ENOMEM; return 0; diff --git a/fs/mbcache.c b/fs/mbcache.c index e28f21b95344..8a2cbd823079 100644 --- a/fs/mbcache.c +++ b/fs/mbcache.c @@ -79,15 +79,11 @@ EXPORT_SYMBOL(mb_cache_entry_find_next); struct mb_cache { struct list_head c_cache_list; const char *c_name; - struct mb_cache_op c_op; atomic_t c_entry_count; int c_bucket_bits; -#ifndef MB_CACHE_INDEXES_COUNT - int c_indexes_count; -#endif - struct kmem_cache *c_entry_cache; + struct kmem_cache *c_entry_cache; struct list_head *c_block_hash; - struct list_head *c_indexes_hash[0]; + struct list_head *c_index_hash; }; @@ -101,16 +97,6 @@ static LIST_HEAD(mb_cache_list); static LIST_HEAD(mb_cache_lru_list); static DEFINE_SPINLOCK(mb_cache_spinlock); -static inline int -mb_cache_indexes(struct mb_cache *cache) -{ -#ifdef MB_CACHE_INDEXES_COUNT - return MB_CACHE_INDEXES_COUNT; -#else - return cache->c_indexes_count; -#endif -} - /* * What the mbcache registers as to get shrunk dynamically. */ @@ -132,12 +118,9 @@ __mb_cache_entry_is_hashed(struct mb_cache_entry *ce) static void __mb_cache_entry_unhash(struct mb_cache_entry *ce) { - int n; - if (__mb_cache_entry_is_hashed(ce)) { list_del_init(&ce->e_block_list); - for (n=0; ne_cache); n++) - list_del(&ce->e_indexes[n].o_list); + list_del(&ce->e_index.o_list); } } @@ -148,16 +131,8 @@ __mb_cache_entry_forget(struct mb_cache_entry *ce, gfp_t gfp_mask) struct mb_cache *cache = ce->e_cache; mb_assert(!(ce->e_used || ce->e_queued)); - if (cache->c_op.free && cache->c_op.free(ce, gfp_mask)) { - /* free failed -- put back on the lru list - for freeing later. */ - spin_lock(&mb_cache_spinlock); - list_add(&ce->e_lru_list, &mb_cache_lru_list); - spin_unlock(&mb_cache_spinlock); - } else { - kmem_cache_free(cache->c_entry_cache, ce); - atomic_dec(&cache->c_entry_count); - } + kmem_cache_free(cache->c_entry_cache, ce); + atomic_dec(&cache->c_entry_count); } @@ -243,72 +218,49 @@ out: * memory was available. * * @name: name of the cache (informal) - * @cache_op: contains the callback called when freeing a cache entry - * @entry_size: The size of a cache entry, including - * struct mb_cache_entry - * @indexes_count: number of additional indexes in the cache. Must equal - * MB_CACHE_INDEXES_COUNT if the number of indexes is - * hardwired. * @bucket_bits: log2(number of hash buckets) */ struct mb_cache * -mb_cache_create(const char *name, struct mb_cache_op *cache_op, - size_t entry_size, int indexes_count, int bucket_bits) +mb_cache_create(const char *name, int bucket_bits) { - int m=0, n, bucket_count = 1 << bucket_bits; + int n, bucket_count = 1 << bucket_bits; struct mb_cache *cache = NULL; - if(entry_size < sizeof(struct mb_cache_entry) + - indexes_count * sizeof(((struct mb_cache_entry *) 0)->e_indexes[0])) - return NULL; - - cache = kmalloc(sizeof(struct mb_cache) + - indexes_count * sizeof(struct list_head), GFP_KERNEL); + cache = kmalloc(sizeof(struct mb_cache), GFP_KERNEL); if (!cache) - goto fail; + return NULL; cache->c_name = name; - cache->c_op.free = NULL; - if (cache_op) - cache->c_op.free = cache_op->free; atomic_set(&cache->c_entry_count, 0); cache->c_bucket_bits = bucket_bits; -#ifdef MB_CACHE_INDEXES_COUNT - mb_assert(indexes_count == MB_CACHE_INDEXES_COUNT); -#else - cache->c_indexes_count = indexes_count; -#endif cache->c_block_hash = kmalloc(bucket_count * sizeof(struct list_head), GFP_KERNEL); if (!cache->c_block_hash) goto fail; for (n=0; nc_block_hash[n]); - for (m=0; mc_indexes_hash[m] = kmalloc(bucket_count * - sizeof(struct list_head), - GFP_KERNEL); - if (!cache->c_indexes_hash[m]) - goto fail; - for (n=0; nc_indexes_hash[m][n]); - } - cache->c_entry_cache = kmem_cache_create(name, entry_size, 0, + cache->c_index_hash = kmalloc(bucket_count * sizeof(struct list_head), + GFP_KERNEL); + if (!cache->c_index_hash) + goto fail; + for (n=0; nc_index_hash[n]); + cache->c_entry_cache = kmem_cache_create(name, + sizeof(struct mb_cache_entry), 0, SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD, NULL); if (!cache->c_entry_cache) - goto fail; + goto fail2; spin_lock(&mb_cache_spinlock); list_add(&cache->c_cache_list, &mb_cache_list); spin_unlock(&mb_cache_spinlock); return cache; +fail2: + kfree(cache->c_index_hash); + fail: - if (cache) { - while (--m >= 0) - kfree(cache->c_indexes_hash[m]); - kfree(cache->c_block_hash); - kfree(cache); - } + kfree(cache->c_block_hash); + kfree(cache); return NULL; } @@ -357,7 +309,6 @@ mb_cache_destroy(struct mb_cache *cache) { LIST_HEAD(free_list); struct list_head *l, *ltmp; - int n; spin_lock(&mb_cache_spinlock); list_for_each_safe(l, ltmp, &mb_cache_lru_list) { @@ -384,8 +335,7 @@ mb_cache_destroy(struct mb_cache *cache) kmem_cache_destroy(cache->c_entry_cache); - for (n=0; n < mb_cache_indexes(cache); n++) - kfree(cache->c_indexes_hash[n]); + kfree(cache->c_index_hash); kfree(cache->c_block_hash); kfree(cache); } @@ -429,17 +379,16 @@ mb_cache_entry_alloc(struct mb_cache *cache, gfp_t gfp_flags) * * @bdev: device the cache entry belongs to * @block: block number - * @keys: array of additional keys. There must be indexes_count entries - * in the array (as specified when creating the cache). + * @key: lookup key */ int mb_cache_entry_insert(struct mb_cache_entry *ce, struct block_device *bdev, - sector_t block, unsigned int keys[]) + sector_t block, unsigned int key) { struct mb_cache *cache = ce->e_cache; unsigned int bucket; struct list_head *l; - int error = -EBUSY, n; + int error = -EBUSY; bucket = hash_long((unsigned long)bdev + (block & 0xffffffff), cache->c_bucket_bits); @@ -454,12 +403,9 @@ mb_cache_entry_insert(struct mb_cache_entry *ce, struct block_device *bdev, ce->e_bdev = bdev; ce->e_block = block; list_add(&ce->e_block_list, &cache->c_block_hash[bucket]); - for (n=0; ne_indexes[n].o_key = keys[n]; - bucket = hash_long(keys[n], cache->c_bucket_bits); - list_add(&ce->e_indexes[n].o_list, - &cache->c_indexes_hash[n][bucket]); - } + ce->e_index.o_key = key; + bucket = hash_long(key, cache->c_bucket_bits); + list_add(&ce->e_index.o_list, &cache->c_index_hash[bucket]); error = 0; out: spin_unlock(&mb_cache_spinlock); @@ -555,13 +501,12 @@ cleanup: static struct mb_cache_entry * __mb_cache_entry_find(struct list_head *l, struct list_head *head, - int index, struct block_device *bdev, unsigned int key) + struct block_device *bdev, unsigned int key) { while (l != head) { struct mb_cache_entry *ce = - list_entry(l, struct mb_cache_entry, - e_indexes[index].o_list); - if (ce->e_bdev == bdev && ce->e_indexes[index].o_key == key) { + list_entry(l, struct mb_cache_entry, e_index.o_list); + if (ce->e_bdev == bdev && ce->e_index.o_key == key) { DEFINE_WAIT(wait); if (!list_empty(&ce->e_lru_list)) @@ -603,23 +548,20 @@ __mb_cache_entry_find(struct list_head *l, struct list_head *head, * returned cache entry is locked for shared access ("multiple readers"). * * @cache: the cache to search - * @index: the number of the additonal index to search (0<=indexc_bucket_bits); struct list_head *l; struct mb_cache_entry *ce; - mb_assert(index < mb_cache_indexes(cache)); spin_lock(&mb_cache_spinlock); - l = cache->c_indexes_hash[index][bucket].next; - ce = __mb_cache_entry_find(l, &cache->c_indexes_hash[index][bucket], - index, bdev, key); + l = cache->c_index_hash[bucket].next; + ce = __mb_cache_entry_find(l, &cache->c_index_hash[bucket], bdev, key); spin_unlock(&mb_cache_spinlock); return ce; } @@ -640,12 +582,11 @@ mb_cache_entry_find_first(struct mb_cache *cache, int index, * } * * @prev: The previous match - * @index: the number of the additonal index to search (0<=indexe_cache; @@ -653,11 +594,9 @@ mb_cache_entry_find_next(struct mb_cache_entry *prev, int index, struct list_head *l; struct mb_cache_entry *ce; - mb_assert(index < mb_cache_indexes(cache)); spin_lock(&mb_cache_spinlock); - l = prev->e_indexes[index].o_list.next; - ce = __mb_cache_entry_find(l, &cache->c_indexes_hash[index][bucket], - index, bdev, key); + l = prev->e_index.o_list.next; + ce = __mb_cache_entry_find(l, &cache->c_index_hash[bucket], bdev, key); __mb_cache_entry_release_unlock(prev); return ce; } diff --git a/include/linux/mbcache.h b/include/linux/mbcache.h index a09b84e4fdb4..54cbbac1e71d 100644 --- a/include/linux/mbcache.h +++ b/include/linux/mbcache.h @@ -4,9 +4,6 @@ (C) 2001 by Andreas Gruenbacher, */ -/* Hardwire the number of additional indexes */ -#define MB_CACHE_INDEXES_COUNT 1 - struct mb_cache_entry { struct list_head e_lru_list; struct mb_cache *e_cache; @@ -18,17 +15,12 @@ struct mb_cache_entry { struct { struct list_head o_list; unsigned int o_key; - } e_indexes[0]; -}; - -struct mb_cache_op { - int (*free)(struct mb_cache_entry *, gfp_t); + } e_index; }; /* Functions on caches */ -struct mb_cache * mb_cache_create(const char *, struct mb_cache_op *, size_t, - int, int); +struct mb_cache *mb_cache_create(const char *, int); void mb_cache_shrink(struct block_device *); void mb_cache_destroy(struct mb_cache *); @@ -36,17 +28,15 @@ void mb_cache_destroy(struct mb_cache *); struct mb_cache_entry *mb_cache_entry_alloc(struct mb_cache *, gfp_t); int mb_cache_entry_insert(struct mb_cache_entry *, struct block_device *, - sector_t, unsigned int[]); + sector_t, unsigned int); void mb_cache_entry_release(struct mb_cache_entry *); void mb_cache_entry_free(struct mb_cache_entry *); struct mb_cache_entry *mb_cache_entry_get(struct mb_cache *, struct block_device *, sector_t); -#if !defined(MB_CACHE_INDEXES_COUNT) || (MB_CACHE_INDEXES_COUNT > 0) -struct mb_cache_entry *mb_cache_entry_find_first(struct mb_cache *cache, int, +struct mb_cache_entry *mb_cache_entry_find_first(struct mb_cache *cache, struct block_device *, unsigned int); -struct mb_cache_entry *mb_cache_entry_find_next(struct mb_cache_entry *, int, +struct mb_cache_entry *mb_cache_entry_find_next(struct mb_cache_entry *, struct block_device *, unsigned int); -#endif -- cgit v1.2.3 From e566d48c9bd56f57e25e855a21e06ca2c2525795 Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Wed, 21 Jul 2010 19:44:45 +0200 Subject: mbcache: fix shrinker function return value The shrinker function is supposed to return the number of cache entries after shrinking, not before shrinking. Fix that. Based on a patch from Wang Sheng-Hui . Signed-off-by: Andreas Gruenbacher Signed-off-by: Al Viro --- fs/mbcache.c | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) (limited to 'fs') diff --git a/fs/mbcache.c b/fs/mbcache.c index 8a2cbd823079..cf4e6cdfd15b 100644 --- a/fs/mbcache.c +++ b/fs/mbcache.c @@ -176,22 +176,12 @@ static int mb_cache_shrink_fn(struct shrinker *shrink, int nr_to_scan, gfp_t gfp_mask) { LIST_HEAD(free_list); - struct list_head *l, *ltmp; + struct mb_cache *cache; + struct mb_cache_entry *entry, *tmp; int count = 0; - spin_lock(&mb_cache_spinlock); - list_for_each(l, &mb_cache_list) { - struct mb_cache *cache = - list_entry(l, struct mb_cache, c_cache_list); - mb_debug("cache %s (%d)", cache->c_name, - atomic_read(&cache->c_entry_count)); - count += atomic_read(&cache->c_entry_count); - } mb_debug("trying to free %d entries", nr_to_scan); - if (nr_to_scan == 0) { - spin_unlock(&mb_cache_spinlock); - goto out; - } + spin_lock(&mb_cache_spinlock); while (nr_to_scan-- && !list_empty(&mb_cache_lru_list)) { struct mb_cache_entry *ce = list_entry(mb_cache_lru_list.next, @@ -199,12 +189,15 @@ mb_cache_shrink_fn(struct shrinker *shrink, int nr_to_scan, gfp_t gfp_mask) list_move_tail(&ce->e_lru_list, &free_list); __mb_cache_entry_unhash(ce); } + list_for_each_entry(cache, &mb_cache_list, c_cache_list) { + mb_debug("cache %s (%d)", cache->c_name, + atomic_read(&cache->c_entry_count)); + count += atomic_read(&cache->c_entry_count); + } spin_unlock(&mb_cache_spinlock); - list_for_each_safe(l, ltmp, &free_list) { - __mb_cache_entry_forget(list_entry(l, struct mb_cache_entry, - e_lru_list), gfp_mask); + list_for_each_entry_safe(entry, tmp, &free_list, e_lru_list) { + __mb_cache_entry_forget(entry, gfp_mask); } -out: return (count / 100) * sysctl_vfs_cache_pressure; } -- cgit v1.2.3 From 1b9474635e21eef0f3e69fd1c7b1b9598ffdddd3 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sun, 18 Jul 2010 17:51:21 -0400 Subject: cifs: truncate fallout Remove the calls to inode_newsize_ok given that we already did it as part of inode_change_ok in the beginning of cifs_setattr_(no)unix. No need to call ->truncate if cifs doesn't have one, so remove the explicit call in cifs_vmtruncate, and replace the calls to vmtruncate with truncate_setsize which is vmtruncate minus inode_newsize_ok and the call to ->truncate. Rename cifs_vmtruncate to cifs_setsize to match the new calling conventions. Question 1: why does cifs do the pagecache munging and i_size update twice for each setattr call, once opencoded in cifs_vmtruncate, and once using the VFS helpers? Question 2: what is supposed to be protected by i_lock in cifs_vmtruncate? Do we need it around the call to inode_change_ok? [AV: fixed build breakage] Signed-off-by: Christoph Hellwig Signed-off-by: Al Viro --- fs/cifs/inode.c | 30 +++++++----------------------- 1 file changed, 7 insertions(+), 23 deletions(-) (limited to 'fs') diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index b95f4a5af013..ddbe8a84c51d 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -1679,26 +1679,16 @@ static int cifs_truncate_page(struct address_space *mapping, loff_t from) return rc; } -static int cifs_vmtruncate(struct inode *inode, loff_t offset) +static void cifs_setsize(struct inode *inode, loff_t offset) { loff_t oldsize; - int err; spin_lock(&inode->i_lock); - err = inode_newsize_ok(inode, offset); - if (err) { - spin_unlock(&inode->i_lock); - goto out; - } - oldsize = inode->i_size; i_size_write(inode, offset); spin_unlock(&inode->i_lock); + truncate_pagecache(inode, oldsize, offset); - if (inode->i_op->truncate) - inode->i_op->truncate(inode); -out: - return err; } static int @@ -1771,7 +1761,7 @@ cifs_set_file_size(struct inode *inode, struct iattr *attrs, if (rc == 0) { cifsInode->server_eof = attrs->ia_size; - rc = cifs_vmtruncate(inode, attrs->ia_size); + cifs_setsize(inode, attrs->ia_size); cifs_truncate_page(inode->i_mapping, inode->i_size); } @@ -1891,11 +1881,8 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs) goto out; if ((attrs->ia_valid & ATTR_SIZE) && - attrs->ia_size != i_size_read(inode)) { - rc = vmtruncate(inode, attrs->ia_size); - if (rc) - goto out; - } + attrs->ia_size != i_size_read(inode)) + truncate_setsize(inode, attrs->ia_size); setattr_copy(inode, attrs); mark_inode_dirty(inode); @@ -2050,11 +2037,8 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs) goto cifs_setattr_exit; if ((attrs->ia_valid & ATTR_SIZE) && - attrs->ia_size != i_size_read(inode)) { - rc = vmtruncate(inode, attrs->ia_size); - if (rc) - goto cifs_setattr_exit; - } + attrs->ia_size != i_size_read(inode)) + truncate_setsize(inode, attrs->ia_size); setattr_copy(inode, attrs); mark_inode_dirty(inode); -- cgit v1.2.3 From 669d5f1f608f7de29bb467bb239517a414e43777 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Mon, 5 Jul 2010 15:14:59 +0300 Subject: AFFS: clean up dirty flag usage In 'affs_write_super()': remove ancient and wrong commented code, remove unneeded 'clean' variable, so the function becomes a bit cleaner and simpler. In 'affs_remount(): remove unnecessary SB dirty flag changes. Tested-by: Artem Bityutskiy Signed-off-by: Artem Bityutskiy Signed-off-by: Al Viro --- fs/affs/super.c | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) (limited to 'fs') diff --git a/fs/affs/super.c b/fs/affs/super.c index 2c804a87c142..fde3b9ae700f 100644 --- a/fs/affs/super.c +++ b/fs/affs/super.c @@ -61,20 +61,13 @@ affs_put_super(struct super_block *sb) static void affs_write_super(struct super_block *sb) { - int clean = 2; - lock_super(sb); - if (!(sb->s_flags & MS_RDONLY)) { - // if (sbi->s_bitmap[i].bm_bh) { - // if (buffer_dirty(sbi->s_bitmap[i].bm_bh)) { - // clean = 0; - affs_commit_super(sb, clean); - sb->s_dirt = !clean; /* redo until bitmap synced */ - } else - sb->s_dirt = 0; + if (!(sb->s_flags & MS_RDONLY)) + affs_commit_super(sb, 2); + sb->s_dirt = 0; unlock_super(sb); - pr_debug("AFFS: write_super() at %lu, clean=%d\n", get_seconds(), clean); + pr_debug("AFFS: write_super() at %lu, clean=2\n", get_seconds()); } static int @@ -553,9 +546,7 @@ affs_remount(struct super_block *sb, int *flags, char *data) return 0; } if (*flags & MS_RDONLY) { - sb->s_dirt = 1; - while (sb->s_dirt) - affs_write_super(sb); + affs_write_super(sb); affs_free_bitmap(sb); } else res = affs_init_bitmap(sb, flags); -- cgit v1.2.3 From 7435d50611b04c1155a939a9f373154a53606592 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Mon, 5 Jul 2010 15:15:00 +0300 Subject: AFFS: wait for sb synchronization when needed AFFS does not ever wait for superblock synchronization in ->put_super(), ->write_super, and ->sync_fs(). However, it should wait for synchronization in ->put_super() because it is about to be unmounted, in ->write_super() because this is periodic SB synchronization performed from a separate kernel thread, and in ->sync_fs() it should respect the 'wait' flag. This patch fixes the situation. Also, in ->put_super(), do not write the SB if it is not dirty. Tested-by: Artem Bityutskiy Signed-off-by: Artem Bityutskiy Signed-off-by: Al Viro --- fs/affs/super.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'fs') diff --git a/fs/affs/super.c b/fs/affs/super.c index fde3b9ae700f..33c4e7eef470 100644 --- a/fs/affs/super.c +++ b/fs/affs/super.c @@ -26,7 +26,7 @@ static int affs_statfs(struct dentry *dentry, struct kstatfs *buf); static int affs_remount (struct super_block *sb, int *flags, char *data); static void -affs_commit_super(struct super_block *sb, int clean) +affs_commit_super(struct super_block *sb, int wait, int clean) { struct affs_sb_info *sbi = AFFS_SB(sb); struct buffer_head *bh = sbi->s_root_bh; @@ -36,6 +36,8 @@ affs_commit_super(struct super_block *sb, int clean) secs_to_datestamp(get_seconds(), &tail->disk_change); affs_fix_checksum(sb, bh); mark_buffer_dirty(bh); + if (wait) + sync_dirty_buffer(bh); } static void @@ -46,8 +48,8 @@ affs_put_super(struct super_block *sb) lock_kernel(); - if (!(sb->s_flags & MS_RDONLY)) - affs_commit_super(sb, 1); + if (!(sb->s_flags & MS_RDONLY) && sb->s_dirt) + affs_commit_super(sb, 1, 1); kfree(sbi->s_prefix); affs_free_bitmap(sb); @@ -63,7 +65,7 @@ affs_write_super(struct super_block *sb) { lock_super(sb); if (!(sb->s_flags & MS_RDONLY)) - affs_commit_super(sb, 2); + affs_commit_super(sb, 1, 2); sb->s_dirt = 0; unlock_super(sb); @@ -74,7 +76,7 @@ static int affs_sync_fs(struct super_block *sb, int wait) { lock_super(sb); - affs_commit_super(sb, 2); + affs_commit_super(sb, wait, 2); sb->s_dirt = 0; unlock_super(sb); return 0; -- cgit v1.2.3 From 4e29d50a28c267bd1d1731a9fb8f773663d93e23 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Mon, 5 Jul 2010 15:15:01 +0300 Subject: BFS: clean up the superblock usage BFS is a very simple FS and its superblocks contains only static information and is never changed. However, the BFS code for some misterious reasons marked its buffer head as dirty from time to time, but nothing in that buffer was ever changed. This patch removes all the BFS superblock manipulation, simply because it is not needed. It removes: 1. The si_sbh filed from 'struct bfs_sb_info' because it is not needed. We only need to read the SB once on mount to get the start of data blocks and the FS size. After this, we can forget about the SB. 2. All instances of 'mark_buffer_dirty(sbh)' for BFS SB because it is never changed. 3. The '->sync_fs()' method because there is nothing to sync (inodes are synched by VFS). 4. The '->write_super()' method, again, because the SB is never changed. Tested-by: Artem Bityutskiy Signed-off-by: Artem Bityutskiy Signed-off-by: Al Viro --- fs/bfs/bfs.h | 1 - fs/bfs/file.c | 3 --- fs/bfs/inode.c | 46 +++++++--------------------------------------- 3 files changed, 7 insertions(+), 43 deletions(-) (limited to 'fs') diff --git a/fs/bfs/bfs.h b/fs/bfs/bfs.h index 7109e451abf7..f7f87e233dd9 100644 --- a/fs/bfs/bfs.h +++ b/fs/bfs/bfs.h @@ -17,7 +17,6 @@ struct bfs_sb_info { unsigned long si_lf_eblk; unsigned long si_lasti; unsigned long *si_imap; - struct buffer_head *si_sbh; /* buffer header w/superblock */ struct mutex bfs_lock; }; diff --git a/fs/bfs/file.c b/fs/bfs/file.c index 8fc2e9c9739d..eb67edd0f8ea 100644 --- a/fs/bfs/file.c +++ b/fs/bfs/file.c @@ -70,7 +70,6 @@ static int bfs_get_block(struct inode *inode, sector_t block, struct super_block *sb = inode->i_sb; struct bfs_sb_info *info = BFS_SB(sb); struct bfs_inode_info *bi = BFS_I(inode); - struct buffer_head *sbh = info->si_sbh; phys = bi->i_sblock + block; if (!create) { @@ -112,7 +111,6 @@ static int bfs_get_block(struct inode *inode, sector_t block, info->si_freeb -= phys - bi->i_eblock; info->si_lf_eblk = bi->i_eblock = phys; mark_inode_dirty(inode); - mark_buffer_dirty(sbh); err = 0; goto out; } @@ -147,7 +145,6 @@ static int bfs_get_block(struct inode *inode, sector_t block, */ info->si_freeb -= bi->i_eblock - bi->i_sblock + 1 - inode->i_blocks; mark_inode_dirty(inode); - mark_buffer_dirty(sbh); map_bh(bh_result, sb, phys); out: mutex_unlock(&info->bfs_lock); diff --git a/fs/bfs/inode.c b/fs/bfs/inode.c index 0499822b1568..c4daf0f5fc02 100644 --- a/fs/bfs/inode.c +++ b/fs/bfs/inode.c @@ -31,7 +31,6 @@ MODULE_LICENSE("GPL"); #define dprintf(x...) #endif -static void bfs_write_super(struct super_block *s); void dump_imap(const char *prefix, struct super_block *s); struct inode *bfs_iget(struct super_block *sb, unsigned long ino) @@ -204,33 +203,11 @@ static void bfs_evict_inode(struct inode *inode) * "last block of the last file" even if there is no * real file there, saves us 1 gap. */ - if (info->si_lf_eblk == bi->i_eblock) { + if (info->si_lf_eblk == bi->i_eblock) info->si_lf_eblk = bi->i_sblock - 1; - mark_buffer_dirty(info->si_sbh); - } mutex_unlock(&info->bfs_lock); } -static int bfs_sync_fs(struct super_block *sb, int wait) -{ - struct bfs_sb_info *info = BFS_SB(sb); - - mutex_lock(&info->bfs_lock); - mark_buffer_dirty(info->si_sbh); - sb->s_dirt = 0; - mutex_unlock(&info->bfs_lock); - - return 0; -} - -static void bfs_write_super(struct super_block *sb) -{ - if (!(sb->s_flags & MS_RDONLY)) - bfs_sync_fs(sb, 1); - else - sb->s_dirt = 0; -} - static void bfs_put_super(struct super_block *s) { struct bfs_sb_info *info = BFS_SB(s); @@ -240,10 +217,6 @@ static void bfs_put_super(struct super_block *s) lock_kernel(); - if (s->s_dirt) - bfs_write_super(s); - - brelse(info->si_sbh); mutex_destroy(&info->bfs_lock); kfree(info->si_imap); kfree(info); @@ -315,8 +288,6 @@ static const struct super_operations bfs_sops = { .write_inode = bfs_write_inode, .evict_inode = bfs_evict_inode, .put_super = bfs_put_super, - .write_super = bfs_write_super, - .sync_fs = bfs_sync_fs, .statfs = bfs_statfs, }; @@ -343,7 +314,7 @@ void dump_imap(const char *prefix, struct super_block *s) static int bfs_fill_super(struct super_block *s, void *data, int silent) { - struct buffer_head *bh; + struct buffer_head *bh, *sbh; struct bfs_super_block *bfs_sb; struct inode *inode; unsigned i, imap_len; @@ -359,10 +330,10 @@ static int bfs_fill_super(struct super_block *s, void *data, int silent) sb_set_blocksize(s, BFS_BSIZE); - info->si_sbh = sb_bread(s, 0); - if (!info->si_sbh) + sbh = sb_bread(s, 0); + if (!sbh) goto out; - bfs_sb = (struct bfs_super_block *)info->si_sbh->b_data; + bfs_sb = (struct bfs_super_block *)sbh->b_data; if (le32_to_cpu(bfs_sb->s_magic) != BFS_MAGIC) { if (!silent) printf("No BFS filesystem on %s (magic=%08x)\n", @@ -466,10 +437,7 @@ static int bfs_fill_super(struct super_block *s, void *data, int silent) info->si_lf_eblk = eblock; } brelse(bh); - if (!(s->s_flags & MS_RDONLY)) { - mark_buffer_dirty(info->si_sbh); - s->s_dirt = 1; - } + brelse(sbh); dump_imap("read_super", s); return 0; @@ -479,7 +447,7 @@ out3: out2: kfree(info->si_imap); out1: - brelse(info->si_sbh); + brelse(sbh); out: mutex_destroy(&info->bfs_lock); kfree(info); -- cgit v1.2.3 From 696ac96c2757963cd6751c26215e3c6d328705aa Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Mon, 5 Jul 2010 15:15:02 +0300 Subject: btrfs: remove junk sb_dirt change BTRFS does not define a '->write_super()' method, so it should not mark its superblock as dirty. This looks like some left-over. Signed-off-by: Artem Bityutskiy Acked-by: Chris Mason Signed-off-by: Al Viro --- fs/btrfs/inode.c | 1 - 1 file changed, 1 deletion(-) (limited to 'fs') diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 2c54f04a0bf5..8976c3343a96 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -2938,7 +2938,6 @@ int btrfs_unlink_subvol(struct btrfs_trans_handle *trans, dir->i_mtime = dir->i_ctime = CURRENT_TIME; ret = btrfs_update_inode(trans, root, dir); BUG_ON(ret); - dir->i_sb->s_dirt = 1; btrfs_free_path(path); return 0; -- cgit v1.2.3 From 315671f3b8091bc8e15035ffeba5f7bff7b8edec Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Mon, 5 Jul 2010 15:15:03 +0300 Subject: sysv: do not mark superblock dirty on mount I did not find any docs about this file-system, and I have no possibility to test my changes. Thus, this is untested. Signed-off-by: Artem Bityutskiy Signed-off-by: Al Viro --- fs/sysv/super.c | 1 - 1 file changed, 1 deletion(-) (limited to 'fs') diff --git a/fs/sysv/super.c b/fs/sysv/super.c index 5a903da54551..0e44a6253352 100644 --- a/fs/sysv/super.c +++ b/fs/sysv/super.c @@ -347,7 +347,6 @@ static int complete_read_super(struct super_block *sb, int silent, int size) sb->s_flags |= MS_RDONLY; if (sbi->s_truncate) sb->s_root->d_op = &sysv_dentry_operations; - sb->s_dirt = 1; return 1; } -- cgit v1.2.3 From 719f2c879f4dda7d7f303bd387d37cd96db29d31 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Mon, 5 Jul 2010 15:15:04 +0300 Subject: sysv: do not mark superblock dirty on remount No need to mark the superblock as dirty in sysv_remount, synchronize it instead (only if mounting R/O). I did not find any docs about this file-system, and I have no possibility to test my changes. Thus, this is untested. I see other issues in sysv, e.g., why sysv_sync_fs writes only in the FSTYPE_SYSV4 case? However, it marks its SB bh's dirty for all types, and does not wait for them ever. With zero docs I'm unable to fix this. Signed-off-by: Artem Bityutskiy Signed-off-by: Al Viro --- fs/sysv/inode.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/sysv/inode.c b/fs/sysv/inode.c index 613a5056e880..de44d067b9e6 100644 --- a/fs/sysv/inode.c +++ b/fs/sysv/inode.c @@ -71,8 +71,8 @@ static int sysv_remount(struct super_block *sb, int *flags, char *data) lock_super(sb); if (sbi->s_forced_ro) *flags |= MS_RDONLY; - if (!(*flags & MS_RDONLY)) - sb->s_dirt = 1; + if (*flags & MS_RDONLY) + sysv_write_super(sb); unlock_super(sb); return 0; } -- cgit v1.2.3 From 4f331f01b9c43bf001d3ffee578a97a1e0633eac Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 20 Jul 2010 15:18:07 -0700 Subject: vfs: don't hold s_umount over close_bdev_exclusive() call Fix an obscure AB-BA deadlock in get_sb_bdev(). When a superblock is mounted more than once get_sb_bdev() calls close_bdev_exclusive() to drop the extra bdev reference while holding s_umount. However, sb->s_umount nests inside bd_mutex during __invalidate_device() and close_bdev_exclusive() acquires bd_mutex during blkdev_put(); thus creating an AB-BA deadlock. This condition doesn't trigger frequently. For this condition to be visible to lockdep, the filesystem must occupy the whole device (as __invalidate_device() only grabs bd_mutex for the whole device), the FS must be mounted more than once and partition rescan should be issued while the FS is still mounted. Fix it by dropping s_umount over close_bdev_exclusive(). Signed-off-by: Tejun Heo Reported-by: Ciprian Docan Cc: Al Viro Acked-by: Jens Axboe Signed-off-by: Andrew Morton Signed-off-by: Al Viro --- fs/super.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'fs') diff --git a/fs/super.c b/fs/super.c index 938119ab8dcb..3479ca6f005f 100644 --- a/fs/super.c +++ b/fs/super.c @@ -773,7 +773,16 @@ int get_sb_bdev(struct file_system_type *fs_type, goto error_bdev; } + /* + * s_umount nests inside bd_mutex during + * __invalidate_device(). close_bdev_exclusive() + * acquires bd_mutex and can't be called under + * s_umount. Drop s_umount temporarily. This is safe + * as we're holding an active reference. + */ + up_write(&s->s_umount); close_bdev_exclusive(bdev, mode); + down_write(&s->s_umount); } else { char b[BDEVNAME_SIZE]; -- cgit v1.2.3 From 7a4dec53897ecd3367efb1e12fe8a4edc47dc0e9 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 9 Aug 2010 12:05:43 -0400 Subject: Fix sget() race with failing mount If sget() finds a matching superblock being set up, it'll grab an active reference to it and grab s_umount. That's fine - we'll wait for completion of foofs_get_sb() that way. However, if said foofs_get_sb() fails we'll end up holding the halfway-created superblock. deactivate_locked_super() called by foofs_get_sb() will just unlock the sucker since we are holding another active reference to it. What we need is a way to tell if superblock has been successfully set up. Unfortunately, neither ->s_root nor the check for MS_ACTIVE quite fit. Cheap and easy way, suitable for backport: new flag set by the (only) caller of ->get_sb(). If that flag isn't present by the time sget() grabbed s_umount on preexisting superblock it has found, it's seeing a stillborn and should just bury it with deactivate_locked_super() (and repeat the search). Longer term we want to set that flag in ->get_sb() instances (and check for it to distinguish between "sget() found us a live sb" and "sget() has allocated an sb, we need to set it up" in there, instead of checking ->s_root as we do now). Signed-off-by: Al Viro Cc: stable@kernel.org --- fs/namespace.c | 2 +- fs/super.c | 6 ++++++ include/linux/fs.h | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/namespace.c b/fs/namespace.c index 88058de59c7c..32dcd24bbc9a 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -1984,7 +1984,7 @@ long do_mount(char *dev_name, char *dir_name, char *type_page, if (flags & MS_RDONLY) mnt_flags |= MNT_READONLY; - flags &= ~(MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_ACTIVE | + flags &= ~(MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_ACTIVE | MS_BORN | MS_NOATIME | MS_NODIRATIME | MS_RELATIME| MS_KERNMOUNT | MS_STRICTATIME); diff --git a/fs/super.c b/fs/super.c index 3479ca6f005f..bd9eea4bb2bb 100644 --- a/fs/super.c +++ b/fs/super.c @@ -305,8 +305,13 @@ retry: if (s) { up_write(&s->s_umount); destroy_super(s); + s = NULL; } down_write(&old->s_umount); + if (unlikely(!(old->s_flags & MS_BORN))) { + deactivate_locked_super(old); + goto retry; + } return old; } } @@ -918,6 +923,7 @@ vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void goto out_free_secdata; BUG_ON(!mnt->mnt_sb); WARN_ON(!mnt->mnt_sb->s_bdi); + mnt->mnt_sb->s_flags |= MS_BORN; error = security_sb_kern_mount(mnt->mnt_sb, flags, secdata); if (error) diff --git a/include/linux/fs.h b/include/linux/fs.h index 9bedf4219f83..58e4b035e282 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -209,6 +209,7 @@ struct inodes_stat_t { #define MS_KERNMOUNT (1<<22) /* this is a kern_mount call */ #define MS_I_VERSION (1<<23) /* Update inode I_version field */ #define MS_STRICTATIME (1<<24) /* Always perform atime updates */ +#define MS_BORN (1<<29) #define MS_ACTIVE (1<<30) #define MS_NOUSER (1<<31) -- cgit v1.2.3 From dca332528bc69e05f67161e1ed59929633d5e63d Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 25 Jul 2010 02:31:46 +0400 Subject: no need for list_for_each_entry_safe()/resetting with superblock list just delay __put_super() a bit Signed-off-by: Al Viro --- fs/dcache.c | 12 +++++++----- fs/super.c | 36 +++++++++++++++++++++--------------- 2 files changed, 28 insertions(+), 20 deletions(-) (limited to 'fs') diff --git a/fs/dcache.c b/fs/dcache.c index caf08574982f..9f2c13417969 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -536,7 +536,7 @@ restart: */ static void prune_dcache(int count) { - struct super_block *sb, *n; + struct super_block *sb, *p = NULL; int w_count; int unused = dentry_stat.nr_unused; int prune_ratio; @@ -550,7 +550,7 @@ static void prune_dcache(int count) else prune_ratio = unused / count; spin_lock(&sb_lock); - list_for_each_entry_safe(sb, n, &super_blocks, s_list) { + list_for_each_entry(sb, &super_blocks, s_list) { if (list_empty(&sb->s_instances)) continue; if (sb->s_nr_dentry_unused == 0) @@ -590,14 +590,16 @@ static void prune_dcache(int count) up_read(&sb->s_umount); } spin_lock(&sb_lock); - /* lock was dropped, must reset next */ - list_safe_reset_next(sb, n, s_list); + if (p) + __put_super(p); count -= pruned; - __put_super(sb); + p = sb; /* more work left to do? */ if (count <= 0) break; } + if (p) + __put_super(p); spin_unlock(&sb_lock); spin_unlock(&dcache_lock); } diff --git a/fs/super.c b/fs/super.c index bd9eea4bb2bb..9674ab2c8718 100644 --- a/fs/super.c +++ b/fs/super.c @@ -363,10 +363,10 @@ EXPORT_SYMBOL(drop_super); */ void sync_supers(void) { - struct super_block *sb, *n; + struct super_block *sb, *p = NULL; spin_lock(&sb_lock); - list_for_each_entry_safe(sb, n, &super_blocks, s_list) { + list_for_each_entry(sb, &super_blocks, s_list) { if (list_empty(&sb->s_instances)) continue; if (sb->s_op->write_super && sb->s_dirt) { @@ -379,11 +379,13 @@ void sync_supers(void) up_read(&sb->s_umount); spin_lock(&sb_lock); - /* lock was dropped, must reset next */ - list_safe_reset_next(sb, n, s_list); - __put_super(sb); + if (p) + __put_super(p); + p = sb; } } + if (p) + __put_super(p); spin_unlock(&sb_lock); } @@ -397,10 +399,10 @@ void sync_supers(void) */ void iterate_supers(void (*f)(struct super_block *, void *), void *arg) { - struct super_block *sb, *n; + struct super_block *sb, *p = NULL; spin_lock(&sb_lock); - list_for_each_entry_safe(sb, n, &super_blocks, s_list) { + list_for_each_entry(sb, &super_blocks, s_list) { if (list_empty(&sb->s_instances)) continue; sb->s_count++; @@ -412,10 +414,12 @@ void iterate_supers(void (*f)(struct super_block *, void *), void *arg) up_read(&sb->s_umount); spin_lock(&sb_lock); - /* lock was dropped, must reset next */ - list_safe_reset_next(sb, n, s_list); - __put_super(sb); + if (p) + __put_super(p); + p = sb; } + if (p) + __put_super(p); spin_unlock(&sb_lock); } @@ -577,10 +581,10 @@ int do_remount_sb(struct super_block *sb, int flags, void *data, int force) static void do_emergency_remount(struct work_struct *work) { - struct super_block *sb, *n; + struct super_block *sb, *p = NULL; spin_lock(&sb_lock); - list_for_each_entry_safe(sb, n, &super_blocks, s_list) { + list_for_each_entry(sb, &super_blocks, s_list) { if (list_empty(&sb->s_instances)) continue; sb->s_count++; @@ -594,10 +598,12 @@ static void do_emergency_remount(struct work_struct *work) } up_write(&sb->s_umount); spin_lock(&sb_lock); - /* lock was dropped, must reset next */ - list_safe_reset_next(sb, n, s_list); - __put_super(sb); + if (p) + __put_super(p); + p = sb; } + if (p) + __put_super(p); spin_unlock(&sb_lock); kfree(work); printk("Emergency Remount complete\n"); -- cgit v1.2.3 From 6d0bf00512b3b1b5d09d9a44919983eec1cc6fd0 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Mon, 9 Aug 2010 17:28:38 -0400 Subject: ext4: clean up compiler warning in start_this_handle() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix the compiler warning: fs/jbd2/transaction.c: In function ‘start_this_handle’: fs/jbd2/transaction.c:98: warning: unused variable ‘ts’ Signed-off-by: "Theodore Ts'o" --- fs/jbd2/transaction.c | 46 +++++++++++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 19 deletions(-) (limited to 'fs') diff --git a/fs/jbd2/transaction.c b/fs/jbd2/transaction.c index d95cc9d0401d..f3479d6e0a83 100644 --- a/fs/jbd2/transaction.c +++ b/fs/jbd2/transaction.c @@ -81,6 +81,32 @@ jbd2_get_transaction(journal_t *journal, transaction_t *transaction) * of that one update. */ +/* + * Update transiaction's maximum wait time, if debugging is enabled. + * + * In order for t_max_wait to be reliable, it must be protected by a + * lock. But doing so will mean that start_this_handle() can not be + * run in parallel on SMP systems, which limits our scalability. So + * unless debugging is enabled, we no longer update t_max_wait, which + * means that maximum wait time reported by the jbd2_run_stats + * tracepoint will always be zero. + */ +static inline void update_t_max_wait(transaction_t *transaction) +{ +#ifdef CONFIG_JBD2_DEBUG + unsigned long ts = jiffies; + + if (jbd2_journal_enable_debug && + time_after(transaction->t_start, ts)) { + ts = jbd2_time_diff(ts, transaction->t_start); + spin_lock(&transaction->t_handle_lock); + if (ts > transaction->t_max_wait) + transaction->t_max_wait = ts; + spin_unlock(&transaction->t_handle_lock); + } +#endif +} + /* * start_this_handle: Given a handle, deal with any locking or stalling * needed to make sure that there is enough journal space for the handle @@ -95,7 +121,6 @@ static int start_this_handle(journal_t *journal, handle_t *handle, int needed; int nblocks = handle->h_buffer_credits; transaction_t *new_transaction = NULL; - unsigned long ts = jiffies; if (nblocks > journal->j_max_transaction_buffers) { printk(KERN_ERR "JBD: %s wants too many credits (%d > %d)\n", @@ -241,25 +266,8 @@ repeat: /* OK, account for the buffers that this operation expects to * use and add the handle to the running transaction. - * - * In order for t_max_wait to be reliable, it must be - * protected by a lock. But doing so will mean that - * start_this_handle() can not be run in parallel on SMP - * systems, which limits our scalability. So we only enable - * it when debugging is enabled. We may want to use a - * separate flag, eventually, so we can enable this - * independently of debugging. */ -#ifdef CONFIG_JBD2_DEBUG - if (jbd2_journal_enable_debug && - time_after(transaction->t_start, ts)) { - ts = jbd2_time_diff(ts, transaction->t_start); - spin_lock(&transaction->t_handle_lock); - if (ts > transaction->t_max_wait) - transaction->t_max_wait = ts; - spin_unlock(&transaction->t_handle_lock); - } -#endif + update_t_max_wait(transaction); handle->h_transaction = transaction; atomic_inc(&transaction->t_updates); atomic_inc(&transaction->t_handle_count); -- cgit v1.2.3 From 26ebc984913b6a8d86d724b3a79d2ed4ed574612 Mon Sep 17 00:00:00 2001 From: KOSAKI Motohiro Date: Mon, 9 Aug 2010 17:19:37 -0700 Subject: oom: /proc//oom_score treat kernel thread honestly If a kernel thread is using use_mm(), badness() returns a positive value. This is not a big issue because caller take care of it correctly. But there is one exception, /proc//oom_score calls badness() directly and doesn't care that the task is a regular process. Another example, /proc/1/oom_score return !0 value. But it's unkillable. This incorrectness makes administration a little confusing. This patch fixes it. Signed-off-by: KOSAKI Motohiro Cc: Minchan Kim Cc: David Rientjes Cc: KAMEZAWA Hiroyuki Cc: Oleg Nesterov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/proc/base.c | 5 +++-- mm/oom_kill.c | 13 +++++++------ 2 files changed, 10 insertions(+), 8 deletions(-) (limited to 'fs') diff --git a/fs/proc/base.c b/fs/proc/base.c index acb7ef80ea4f..fc23f62bb0b8 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -428,7 +428,8 @@ static const struct file_operations proc_lstats_operations = { #endif /* The badness from the OOM killer */ -unsigned long badness(struct task_struct *p, unsigned long uptime); +unsigned long badness(struct task_struct *p, struct mem_cgroup *mem, + nodemask_t *nodemask, unsigned long uptime); static int proc_oom_score(struct task_struct *task, char *buffer) { unsigned long points = 0; @@ -437,7 +438,7 @@ static int proc_oom_score(struct task_struct *task, char *buffer) do_posix_clock_monotonic_gettime(&uptime); read_lock(&tasklist_lock); if (pid_alive(task)) - points = badness(task, uptime.tv_sec); + points = badness(task, NULL, NULL, uptime.tv_sec); read_unlock(&tasklist_lock); return sprintf(buffer, "%lu\n", points); } diff --git a/mm/oom_kill.c b/mm/oom_kill.c index 3999747aef48..867bd26274b4 100644 --- a/mm/oom_kill.c +++ b/mm/oom_kill.c @@ -139,8 +139,8 @@ static bool oom_unkillable_task(struct task_struct *p, struct mem_cgroup *mem, * algorithm has been meticulously tuned to meet the principle * of least surprise ... (be careful when you change it) */ - -unsigned long badness(struct task_struct *p, unsigned long uptime) +unsigned long badness(struct task_struct *p, struct mem_cgroup *mem, + const nodemask_t *nodemask, unsigned long uptime) { unsigned long points, cpu_time, run_time; struct task_struct *child; @@ -150,6 +150,8 @@ unsigned long badness(struct task_struct *p, unsigned long uptime) unsigned long utime; unsigned long stime; + if (oom_unkillable_task(p, mem, nodemask)) + return 0; if (oom_adj == OOM_DISABLE) return 0; @@ -351,7 +353,7 @@ static struct task_struct *select_bad_process(unsigned long *ppoints, if (p->signal->oom_adj == OOM_DISABLE) continue; - points = badness(p, uptime.tv_sec); + points = badness(p, mem, nodemask, uptime.tv_sec); if (points > *ppoints || !chosen) { chosen = p; *ppoints = points; @@ -482,11 +484,10 @@ static int oom_kill_process(struct task_struct *p, gfp_t gfp_mask, int order, if (child->mm == p->mm) continue; - if (oom_unkillable_task(p, mem, nodemask)) - continue; /* badness() returns 0 if the thread is unkillable */ - child_points = badness(child, uptime.tv_sec); + child_points = badness(child, mem, nodemask, + uptime.tv_sec); if (child_points > victim_points) { victim = child; victim_points = child_points; -- cgit v1.2.3 From 74bcbf40546bb7500f2a7ba4ff3cc056a6bd004a Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Mon, 9 Aug 2010 17:19:43 -0700 Subject: oom: move badness() declaration into oom.h Cc: Minchan Kim Cc: David Rientjes Cc: KAMEZAWA Hiroyuki Cc: Oleg Nesterov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/proc/base.c | 3 --- include/linux/oom.h | 6 ++++++ 2 files changed, 6 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/proc/base.c b/fs/proc/base.c index fc23f62bb0b8..5949d0ac30f2 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -427,9 +427,6 @@ static const struct file_operations proc_lstats_operations = { #endif -/* The badness from the OOM killer */ -unsigned long badness(struct task_struct *p, struct mem_cgroup *mem, - nodemask_t *nodemask, unsigned long uptime); static int proc_oom_score(struct task_struct *task, char *buffer) { unsigned long points = 0; diff --git a/include/linux/oom.h b/include/linux/oom.h index 9d7c34a741e7..40e5e3a6bc20 100644 --- a/include/linux/oom.h +++ b/include/linux/oom.h @@ -14,6 +14,8 @@ struct zonelist; struct notifier_block; +struct mem_cgroup; +struct task_struct; /* * Types of limitations to the nodes from which allocations may occur @@ -45,6 +47,10 @@ static inline void oom_killer_enable(void) oom_killer_disabled = false; } +/* The badness from the OOM killer */ +extern unsigned long badness(struct task_struct *p, struct mem_cgroup *mem, + const nodemask_t *nodemask, unsigned long uptime); + /* sysctls */ extern int sysctl_oom_dump_tasks; extern int sysctl_oom_kill_allocating_task; -- cgit v1.2.3 From a63d83f427fbce97a6cea0db2e64b0eb8435cd10 Mon Sep 17 00:00:00 2001 From: David Rientjes Date: Mon, 9 Aug 2010 17:19:46 -0700 Subject: oom: badness heuristic rewrite This a complete rewrite of the oom killer's badness() heuristic which is used to determine which task to kill in oom conditions. The goal is to make it as simple and predictable as possible so the results are better understood and we end up killing the task which will lead to the most memory freeing while still respecting the fine-tuning from userspace. Instead of basing the heuristic on mm->total_vm for each task, the task's rss and swap space is used instead. This is a better indication of the amount of memory that will be freeable if the oom killed task is chosen and subsequently exits. This helps specifically in cases where KDE or GNOME is chosen for oom kill on desktop systems instead of a memory hogging task. The baseline for the heuristic is a proportion of memory that each task is currently using in memory plus swap compared to the amount of "allowable" memory. "Allowable," in this sense, means the system-wide resources for unconstrained oom conditions, the set of mempolicy nodes, the mems attached to current's cpuset, or a memory controller's limit. The proportion is given on a scale of 0 (never kill) to 1000 (always kill), roughly meaning that if a task has a badness() score of 500 that the task consumes approximately 50% of allowable memory resident in RAM or in swap space. The proportion is always relative to the amount of "allowable" memory and not the total amount of RAM systemwide so that mempolicies and cpusets may operate in isolation; they shall not need to know the true size of the machine on which they are running if they are bound to a specific set of nodes or mems, respectively. Root tasks are given 3% extra memory just like __vm_enough_memory() provides in LSMs. In the event of two tasks consuming similar amounts of memory, it is generally better to save root's task. Because of the change in the badness() heuristic's baseline, it is also necessary to introduce a new user interface to tune it. It's not possible to redefine the meaning of /proc/pid/oom_adj with a new scale since the ABI cannot be changed for backward compatability. Instead, a new tunable, /proc/pid/oom_score_adj, is added that ranges from -1000 to +1000. It may be used to polarize the heuristic such that certain tasks are never considered for oom kill while others may always be considered. The value is added directly into the badness() score so a value of -500, for example, means to discount 50% of its memory consumption in comparison to other tasks either on the system, bound to the mempolicy, in the cpuset, or sharing the same memory controller. /proc/pid/oom_adj is changed so that its meaning is rescaled into the units used by /proc/pid/oom_score_adj, and vice versa. Changing one of these per-task tunables will rescale the value of the other to an equivalent meaning. Although /proc/pid/oom_adj was originally defined as a bitshift on the badness score, it now shares the same linear growth as /proc/pid/oom_score_adj but with different granularity. This is required so the ABI is not broken with userspace applications and allows oom_adj to be deprecated for future removal. Signed-off-by: David Rientjes Cc: Nick Piggin Cc: KAMEZAWA Hiroyuki Cc: KOSAKI Motohiro Cc: Oleg Nesterov Cc: Balbir Singh Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/filesystems/proc.txt | 94 ++++++++------ fs/proc/base.c | 94 +++++++++++++- include/linux/memcontrol.h | 8 ++ include/linux/oom.h | 14 +- include/linux/sched.h | 3 +- kernel/fork.c | 1 + mm/memcontrol.c | 18 +++ mm/oom_kill.c | 259 ++++++++++++++++--------------------- 8 files changed, 300 insertions(+), 191 deletions(-) (limited to 'fs') diff --git a/Documentation/filesystems/proc.txt b/Documentation/filesystems/proc.txt index 8fe8895894d8..cf1295c2bb66 100644 --- a/Documentation/filesystems/proc.txt +++ b/Documentation/filesystems/proc.txt @@ -33,7 +33,8 @@ Table of Contents 2 Modifying System Parameters 3 Per-Process Parameters - 3.1 /proc//oom_adj - Adjust the oom-killer score + 3.1 /proc//oom_adj & /proc//oom_score_adj - Adjust the oom-killer + score 3.2 /proc//oom_score - Display current oom-killer score 3.3 /proc//io - Display the IO accounting fields 3.4 /proc//coredump_filter - Core dump filtering settings @@ -1234,42 +1235,61 @@ of the kernel. CHAPTER 3: PER-PROCESS PARAMETERS ------------------------------------------------------------------------------ -3.1 /proc//oom_adj - Adjust the oom-killer score ------------------------------------------------------- - -This file can be used to adjust the score used to select which processes -should be killed in an out-of-memory situation. Giving it a high score will -increase the likelihood of this process being killed by the oom-killer. Valid -values are in the range -16 to +15, plus the special value -17, which disables -oom-killing altogether for this process. - -The process to be killed in an out-of-memory situation is selected among all others -based on its badness score. This value equals the original memory size of the process -and is then updated according to its CPU time (utime + stime) and the -run time (uptime - start time). The longer it runs the smaller is the score. -Badness score is divided by the square root of the CPU time and then by -the double square root of the run time. - -Swapped out tasks are killed first. Half of each child's memory size is added to -the parent's score if they do not share the same memory. Thus forking servers -are the prime candidates to be killed. Having only one 'hungry' child will make -parent less preferable than the child. - -/proc//oom_score shows process' current badness score. - -The following heuristics are then applied: - * if the task was reniced, its score doubles - * superuser or direct hardware access tasks (CAP_SYS_ADMIN, CAP_SYS_RESOURCE - or CAP_SYS_RAWIO) have their score divided by 4 - * if oom condition happened in one cpuset and checked process does not belong - to it, its score is divided by 8 - * the resulting score is multiplied by two to the power of oom_adj, i.e. - points <<= oom_adj when it is positive and - points >>= -(oom_adj) otherwise - -The task with the highest badness score is then selected and its children -are killed, process itself will be killed in an OOM situation when it does -not have children or some of them disabled oom like described above. +3.1 /proc//oom_adj & /proc//oom_score_adj- Adjust the oom-killer score +-------------------------------------------------------------------------------- + +These file can be used to adjust the badness heuristic used to select which +process gets killed in out of memory conditions. + +The badness heuristic assigns a value to each candidate task ranging from 0 +(never kill) to 1000 (always kill) to determine which process is targeted. The +units are roughly a proportion along that range of allowed memory the process +may allocate from based on an estimation of its current memory and swap use. +For example, if a task is using all allowed memory, its badness score will be +1000. If it is using half of its allowed memory, its score will be 500. + +There is an additional factor included in the badness score: root +processes are given 3% extra memory over other tasks. + +The amount of "allowed" memory depends on the context in which the oom killer +was called. If it is due to the memory assigned to the allocating task's cpuset +being exhausted, the allowed memory represents the set of mems assigned to that +cpuset. If it is due to a mempolicy's node(s) being exhausted, the allowed +memory represents the set of mempolicy nodes. If it is due to a memory +limit (or swap limit) being reached, the allowed memory is that configured +limit. Finally, if it is due to the entire system being out of memory, the +allowed memory represents all allocatable resources. + +The value of /proc//oom_score_adj is added to the badness score before it +is used to determine which task to kill. Acceptable values range from -1000 +(OOM_SCORE_ADJ_MIN) to +1000 (OOM_SCORE_ADJ_MAX). This allows userspace to +polarize the preference for oom killing either by always preferring a certain +task or completely disabling it. The lowest possible value, -1000, is +equivalent to disabling oom killing entirely for that task since it will always +report a badness score of 0. + +Consequently, it is very simple for userspace to define the amount of memory to +consider for each task. Setting a /proc//oom_score_adj value of +500, for +example, is roughly equivalent to allowing the remainder of tasks sharing the +same system, cpuset, mempolicy, or memory controller resources to use at least +50% more memory. A value of -500, on the other hand, would be roughly +equivalent to discounting 50% of the task's allowed memory from being considered +as scoring against the task. + +For backwards compatibility with previous kernels, /proc//oom_adj may also +be used to tune the badness score. Its acceptable values range from -16 +(OOM_ADJUST_MIN) to +15 (OOM_ADJUST_MAX) and a special value of -17 +(OOM_DISABLE) to disable oom killing entirely for that task. Its value is +scaled linearly with /proc//oom_score_adj. + +Writing to /proc//oom_score_adj or /proc//oom_adj will change the +other with its scaled value. + +Caveat: when a parent task is selected, the oom killer will sacrifice any first +generation children with seperate address spaces instead, if possible. This +avoids servers and important system daemons from being killed and loses the +minimal amount of work. + 3.2 /proc//oom_score - Display current oom-killer score ------------------------------------------------------------- diff --git a/fs/proc/base.c b/fs/proc/base.c index 5949d0ac30f2..f923b728388a 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -63,6 +63,7 @@ #include #include #include +#include #include #include #include @@ -430,12 +431,11 @@ static const struct file_operations proc_lstats_operations = { static int proc_oom_score(struct task_struct *task, char *buffer) { unsigned long points = 0; - struct timespec uptime; - do_posix_clock_monotonic_gettime(&uptime); read_lock(&tasklist_lock); if (pid_alive(task)) - points = badness(task, NULL, NULL, uptime.tv_sec); + points = oom_badness(task, NULL, NULL, + totalram_pages + total_swap_pages); read_unlock(&tasklist_lock); return sprintf(buffer, "%lu\n", points); } @@ -1038,7 +1038,15 @@ static ssize_t oom_adjust_write(struct file *file, const char __user *buf, } task->signal->oom_adj = oom_adjust; - + /* + * Scale /proc/pid/oom_score_adj appropriately ensuring that a maximum + * value is always attainable. + */ + if (task->signal->oom_adj == OOM_ADJUST_MAX) + task->signal->oom_score_adj = OOM_SCORE_ADJ_MAX; + else + task->signal->oom_score_adj = (oom_adjust * OOM_SCORE_ADJ_MAX) / + -OOM_DISABLE; unlock_task_sighand(task, &flags); put_task_struct(task); @@ -1051,6 +1059,82 @@ static const struct file_operations proc_oom_adjust_operations = { .llseek = generic_file_llseek, }; +static ssize_t oom_score_adj_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct task_struct *task = get_proc_task(file->f_path.dentry->d_inode); + char buffer[PROC_NUMBUF]; + int oom_score_adj = OOM_SCORE_ADJ_MIN; + unsigned long flags; + size_t len; + + if (!task) + return -ESRCH; + if (lock_task_sighand(task, &flags)) { + oom_score_adj = task->signal->oom_score_adj; + unlock_task_sighand(task, &flags); + } + put_task_struct(task); + len = snprintf(buffer, sizeof(buffer), "%d\n", oom_score_adj); + return simple_read_from_buffer(buf, count, ppos, buffer, len); +} + +static ssize_t oom_score_adj_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct task_struct *task; + char buffer[PROC_NUMBUF]; + unsigned long flags; + long oom_score_adj; + int err; + + memset(buffer, 0, sizeof(buffer)); + if (count > sizeof(buffer) - 1) + count = sizeof(buffer) - 1; + if (copy_from_user(buffer, buf, count)) + return -EFAULT; + + err = strict_strtol(strstrip(buffer), 0, &oom_score_adj); + if (err) + return -EINVAL; + if (oom_score_adj < OOM_SCORE_ADJ_MIN || + oom_score_adj > OOM_SCORE_ADJ_MAX) + return -EINVAL; + + task = get_proc_task(file->f_path.dentry->d_inode); + if (!task) + return -ESRCH; + if (!lock_task_sighand(task, &flags)) { + put_task_struct(task); + return -ESRCH; + } + if (oom_score_adj < task->signal->oom_score_adj && + !capable(CAP_SYS_RESOURCE)) { + unlock_task_sighand(task, &flags); + put_task_struct(task); + return -EACCES; + } + + task->signal->oom_score_adj = oom_score_adj; + /* + * Scale /proc/pid/oom_adj appropriately ensuring that OOM_DISABLE is + * always attainable. + */ + if (task->signal->oom_score_adj == OOM_SCORE_ADJ_MIN) + task->signal->oom_adj = OOM_DISABLE; + else + task->signal->oom_adj = (oom_score_adj * OOM_ADJUST_MAX) / + OOM_SCORE_ADJ_MAX; + unlock_task_sighand(task, &flags); + put_task_struct(task); + return count; +} + +static const struct file_operations proc_oom_score_adj_operations = { + .read = oom_score_adj_read, + .write = oom_score_adj_write, +}; + #ifdef CONFIG_AUDITSYSCALL #define TMPBUFLEN 21 static ssize_t proc_loginuid_read(struct file * file, char __user * buf, @@ -2623,6 +2707,7 @@ static const struct pid_entry tgid_base_stuff[] = { #endif INF("oom_score", S_IRUGO, proc_oom_score), REG("oom_adj", S_IRUGO|S_IWUSR, proc_oom_adjust_operations), + REG("oom_score_adj", S_IRUGO|S_IWUSR, proc_oom_score_adj_operations), #ifdef CONFIG_AUDITSYSCALL REG("loginuid", S_IWUSR|S_IRUGO, proc_loginuid_operations), REG("sessionid", S_IRUGO, proc_sessionid_operations), @@ -2957,6 +3042,7 @@ static const struct pid_entry tid_base_stuff[] = { #endif INF("oom_score", S_IRUGO, proc_oom_score), REG("oom_adj", S_IRUGO|S_IWUSR, proc_oom_adjust_operations), + REG("oom_score_adj", S_IRUGO|S_IWUSR, proc_oom_score_adj_operations), #ifdef CONFIG_AUDITSYSCALL REG("loginuid", S_IWUSR|S_IRUGO, proc_loginuid_operations), REG("sessionid", S_IRUSR, proc_sessionid_operations), diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h index 9f1afd361583..73564cac38c7 100644 --- a/include/linux/memcontrol.h +++ b/include/linux/memcontrol.h @@ -125,6 +125,8 @@ void mem_cgroup_update_file_mapped(struct page *page, int val); unsigned long mem_cgroup_soft_limit_reclaim(struct zone *zone, int order, gfp_t gfp_mask, int nid, int zid); +u64 mem_cgroup_get_limit(struct mem_cgroup *mem); + #else /* CONFIG_CGROUP_MEM_RES_CTLR */ struct mem_cgroup; @@ -304,6 +306,12 @@ unsigned long mem_cgroup_soft_limit_reclaim(struct zone *zone, int order, return 0; } +static inline +u64 mem_cgroup_get_limit(struct mem_cgroup *mem) +{ + return 0; +} + #endif /* CONFIG_CGROUP_MEM_CONT */ #endif /* _LINUX_MEMCONTROL_H */ diff --git a/include/linux/oom.h b/include/linux/oom.h index 40e5e3a6bc20..73b8d7b6dd19 100644 --- a/include/linux/oom.h +++ b/include/linux/oom.h @@ -1,14 +1,24 @@ #ifndef __INCLUDE_LINUX_OOM_H #define __INCLUDE_LINUX_OOM_H -/* /proc//oom_adj set to -17 protects from the oom-killer */ +/* + * /proc//oom_adj set to -17 protects from the oom-killer + */ #define OOM_DISABLE (-17) /* inclusive */ #define OOM_ADJUST_MIN (-16) #define OOM_ADJUST_MAX 15 +/* + * /proc//oom_score_adj set to OOM_SCORE_ADJ_MIN disables oom killing for + * pid. + */ +#define OOM_SCORE_ADJ_MIN (-1000) +#define OOM_SCORE_ADJ_MAX 1000 + #ifdef __KERNEL__ +#include #include #include @@ -27,6 +37,8 @@ enum oom_constraint { CONSTRAINT_MEMCG, }; +extern unsigned int oom_badness(struct task_struct *p, struct mem_cgroup *mem, + const nodemask_t *nodemask, unsigned long totalpages); extern int try_set_zonelist_oom(struct zonelist *zonelist, gfp_t gfp_flags); extern void clear_zonelist_oom(struct zonelist *zonelist, gfp_t gfp_flags); diff --git a/include/linux/sched.h b/include/linux/sched.h index 9591907c4f79..ce160d68f5e7 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -621,7 +621,8 @@ struct signal_struct { struct tty_audit_buf *tty_audit_buf; #endif - int oom_adj; /* OOM kill score adjustment (bit shift) */ + int oom_adj; /* OOM kill score adjustment (bit shift) */ + int oom_score_adj; /* OOM kill score adjustment */ }; /* Context switch must be unlocked if interrupts are to be enabled */ diff --git a/kernel/fork.c b/kernel/fork.c index a82a65cef741..98b450876f93 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -899,6 +899,7 @@ static int copy_signal(unsigned long clone_flags, struct task_struct *tsk) tty_audit_fork(sig); sig->oom_adj = current->signal->oom_adj; + sig->oom_score_adj = current->signal->oom_score_adj; return 0; } diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 31abd1c2c0c5..de54ea0094a1 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -1126,6 +1126,24 @@ static int mem_cgroup_count_children(struct mem_cgroup *mem) return num; } +/* + * Return the memory (and swap, if configured) limit for a memcg. + */ +u64 mem_cgroup_get_limit(struct mem_cgroup *memcg) +{ + u64 limit; + u64 memsw; + + limit = res_counter_read_u64(&memcg->res, RES_LIMIT) + + total_swap_pages; + memsw = res_counter_read_u64(&memcg->memsw, RES_LIMIT); + /* + * If memsw is finite and limits the amount of swap space available + * to this memcg, return that limit. + */ + return min(limit, memsw); +} + /* * Visit the first child (need not be the first child as per the ordering * of the cgroup list, since we track last_scanned_child) of @mem and use diff --git a/mm/oom_kill.c b/mm/oom_kill.c index 0a4ca8a0234b..d3def05a33d9 100644 --- a/mm/oom_kill.c +++ b/mm/oom_kill.c @@ -4,6 +4,8 @@ * Copyright (C) 1998,2000 Rik van Riel * Thanks go out to Claus Fischer for some serious inspiration and * for goading me into coding this file... + * Copyright (C) 2010 Google, Inc. + * Rewritten by David Rientjes * * The routines in this file are used to kill a process when * we're seriously out of memory. This gets called from __alloc_pages() @@ -34,7 +36,6 @@ int sysctl_panic_on_oom; int sysctl_oom_kill_allocating_task; int sysctl_oom_dump_tasks = 1; static DEFINE_SPINLOCK(zone_scan_lock); -/* #define DEBUG */ #ifdef CONFIG_NUMA /** @@ -140,137 +141,76 @@ static bool oom_unkillable_task(struct task_struct *p, struct mem_cgroup *mem, } /** - * badness - calculate a numeric value for how bad this task has been + * oom_badness - heuristic function to determine which candidate task to kill * @p: task struct of which task we should calculate - * @uptime: current uptime in seconds + * @totalpages: total present RAM allowed for page allocation * - * The formula used is relatively simple and documented inline in the - * function. The main rationale is that we want to select a good task - * to kill when we run out of memory. - * - * Good in this context means that: - * 1) we lose the minimum amount of work done - * 2) we recover a large amount of memory - * 3) we don't kill anything innocent of eating tons of memory - * 4) we want to kill the minimum amount of processes (one) - * 5) we try to kill the process the user expects us to kill, this - * algorithm has been meticulously tuned to meet the principle - * of least surprise ... (be careful when you change it) + * The heuristic for determining which task to kill is made to be as simple and + * predictable as possible. The goal is to return the highest value for the + * task consuming the most memory to avoid subsequent oom failures. */ -unsigned long badness(struct task_struct *p, struct mem_cgroup *mem, - const nodemask_t *nodemask, unsigned long uptime) +unsigned int oom_badness(struct task_struct *p, struct mem_cgroup *mem, + const nodemask_t *nodemask, unsigned long totalpages) { - unsigned long points, cpu_time, run_time; - struct task_struct *child; - struct task_struct *c, *t; - int oom_adj = p->signal->oom_adj; - struct task_cputime task_time; - unsigned long utime; - unsigned long stime; + int points; if (oom_unkillable_task(p, mem, nodemask)) return 0; - if (oom_adj == OOM_DISABLE) - return 0; p = find_lock_task_mm(p); if (!p) return 0; /* - * The memory size of the process is the basis for the badness. - */ - points = p->mm->total_vm; - task_unlock(p); - - /* - * swapoff can easily use up all memory, so kill those first. - */ - if (p->flags & PF_OOM_ORIGIN) - return ULONG_MAX; - - /* - * Processes which fork a lot of child processes are likely - * a good choice. We add half the vmsize of the children if they - * have an own mm. This prevents forking servers to flood the - * machine with an endless amount of children. In case a single - * child is eating the vast majority of memory, adding only half - * to the parents will make the child our kill candidate of choice. + * Shortcut check for OOM_SCORE_ADJ_MIN so the entire heuristic doesn't + * need to be executed for something that cannot be killed. */ - t = p; - do { - list_for_each_entry(c, &t->children, sibling) { - child = find_lock_task_mm(c); - if (child) { - if (child->mm != p->mm) - points += child->mm->total_vm/2 + 1; - task_unlock(child); - } - } - } while_each_thread(p, t); + if (p->signal->oom_score_adj == OOM_SCORE_ADJ_MIN) { + task_unlock(p); + return 0; + } /* - * CPU time is in tens of seconds and run time is in thousands - * of seconds. There is no particular reason for this other than - * that it turned out to work very well in practice. + * When the PF_OOM_ORIGIN bit is set, it indicates the task should have + * priority for oom killing. */ - thread_group_cputime(p, &task_time); - utime = cputime_to_jiffies(task_time.utime); - stime = cputime_to_jiffies(task_time.stime); - cpu_time = (utime + stime) >> (SHIFT_HZ + 3); - - - if (uptime >= p->start_time.tv_sec) - run_time = (uptime - p->start_time.tv_sec) >> 10; - else - run_time = 0; - - if (cpu_time) - points /= int_sqrt(cpu_time); - if (run_time) - points /= int_sqrt(int_sqrt(run_time)); + if (p->flags & PF_OOM_ORIGIN) { + task_unlock(p); + return 1000; + } /* - * Niced processes are most likely less important, so double - * their badness points. + * The memory controller may have a limit of 0 bytes, so avoid a divide + * by zero, if necessary. */ - if (task_nice(p) > 0) - points *= 2; + if (!totalpages) + totalpages = 1; /* - * Superuser processes are usually more important, so we make it - * less likely that we kill those. + * The baseline for the badness score is the proportion of RAM that each + * task's rss and swap space use. */ - if (has_capability_noaudit(p, CAP_SYS_ADMIN) || - has_capability_noaudit(p, CAP_SYS_RESOURCE)) - points /= 4; + points = (get_mm_rss(p->mm) + get_mm_counter(p->mm, MM_SWAPENTS)) * 1000 / + totalpages; + task_unlock(p); /* - * We don't want to kill a process with direct hardware access. - * Not only could that mess up the hardware, but usually users - * tend to only have this flag set on applications they think - * of as important. + * Root processes get 3% bonus, just like the __vm_enough_memory() + * implementation used by LSMs. */ - if (has_capability_noaudit(p, CAP_SYS_RAWIO)) - points /= 4; + if (has_capability_noaudit(p, CAP_SYS_ADMIN)) + points -= 30; /* - * Adjust the score by oom_adj. + * /proc/pid/oom_score_adj ranges from -1000 to +1000 such that it may + * either completely disable oom killing or always prefer a certain + * task. */ - if (oom_adj) { - if (oom_adj > 0) { - if (!points) - points = 1; - points <<= oom_adj; - } else - points >>= -(oom_adj); - } + points += p->signal->oom_score_adj; -#ifdef DEBUG - printk(KERN_DEBUG "OOMkill: task %d (%s) got %lu points\n", - p->pid, p->comm, points); -#endif - return points; + if (points < 0) + return 0; + return (points < 1000) ? points : 1000; } /* @@ -278,12 +218,20 @@ unsigned long badness(struct task_struct *p, struct mem_cgroup *mem, */ #ifdef CONFIG_NUMA static enum oom_constraint constrained_alloc(struct zonelist *zonelist, - gfp_t gfp_mask, nodemask_t *nodemask) + gfp_t gfp_mask, nodemask_t *nodemask, + unsigned long *totalpages) { struct zone *zone; struct zoneref *z; enum zone_type high_zoneidx = gfp_zone(gfp_mask); + bool cpuset_limited = false; + int nid; + /* Default to all available memory */ + *totalpages = totalram_pages + total_swap_pages; + + if (!zonelist) + return CONSTRAINT_NONE; /* * Reach here only when __GFP_NOFAIL is used. So, we should avoid * to kill current.We have to random task kill in this case. @@ -293,26 +241,37 @@ static enum oom_constraint constrained_alloc(struct zonelist *zonelist, return CONSTRAINT_NONE; /* - * The nodemask here is a nodemask passed to alloc_pages(). Now, - * cpuset doesn't use this nodemask for its hardwall/softwall/hierarchy - * feature. mempolicy is an only user of nodemask here. - * check mempolicy's nodemask contains all N_HIGH_MEMORY + * This is not a __GFP_THISNODE allocation, so a truncated nodemask in + * the page allocator means a mempolicy is in effect. Cpuset policy + * is enforced in get_page_from_freelist(). */ - if (nodemask && !nodes_subset(node_states[N_HIGH_MEMORY], *nodemask)) + if (nodemask && !nodes_subset(node_states[N_HIGH_MEMORY], *nodemask)) { + *totalpages = total_swap_pages; + for_each_node_mask(nid, *nodemask) + *totalpages += node_spanned_pages(nid); return CONSTRAINT_MEMORY_POLICY; + } /* Check this allocation failure is caused by cpuset's wall function */ for_each_zone_zonelist_nodemask(zone, z, zonelist, high_zoneidx, nodemask) if (!cpuset_zone_allowed_softwall(zone, gfp_mask)) - return CONSTRAINT_CPUSET; + cpuset_limited = true; + if (cpuset_limited) { + *totalpages = total_swap_pages; + for_each_node_mask(nid, cpuset_current_mems_allowed) + *totalpages += node_spanned_pages(nid); + return CONSTRAINT_CPUSET; + } return CONSTRAINT_NONE; } #else static enum oom_constraint constrained_alloc(struct zonelist *zonelist, - gfp_t gfp_mask, nodemask_t *nodemask) + gfp_t gfp_mask, nodemask_t *nodemask, + unsigned long *totalpages) { + *totalpages = totalram_pages + total_swap_pages; return CONSTRAINT_NONE; } #endif @@ -323,17 +282,16 @@ static enum oom_constraint constrained_alloc(struct zonelist *zonelist, * * (not docbooked, we don't want this one cluttering up the manual) */ -static struct task_struct *select_bad_process(unsigned long *ppoints, - struct mem_cgroup *mem, const nodemask_t *nodemask) +static struct task_struct *select_bad_process(unsigned int *ppoints, + unsigned long totalpages, struct mem_cgroup *mem, + const nodemask_t *nodemask) { struct task_struct *p; struct task_struct *chosen = NULL; - struct timespec uptime; *ppoints = 0; - do_posix_clock_monotonic_gettime(&uptime); for_each_process(p) { - unsigned long points; + unsigned int points; if (oom_unkillable_task(p, mem, nodemask)) continue; @@ -365,11 +323,11 @@ static struct task_struct *select_bad_process(unsigned long *ppoints, return ERR_PTR(-1UL); chosen = p; - *ppoints = ULONG_MAX; + *ppoints = 1000; } - points = badness(p, mem, nodemask, uptime.tv_sec); - if (points > *ppoints || !chosen) { + points = oom_badness(p, mem, nodemask, totalpages); + if (points > *ppoints) { chosen = p; *ppoints = points; } @@ -384,7 +342,7 @@ static struct task_struct *select_bad_process(unsigned long *ppoints, * * Dumps the current memory state of all system tasks, excluding kernel threads. * State information includes task's pid, uid, tgid, vm size, rss, cpu, oom_adj - * score, and name. + * value, oom_score_adj value, and name. * * If the actual is non-NULL, only tasks that are a member of the mem_cgroup are * shown. @@ -396,8 +354,7 @@ static void dump_tasks(const struct mem_cgroup *mem) struct task_struct *p; struct task_struct *task; - printk(KERN_INFO "[ pid ] uid tgid total_vm rss cpu oom_adj " - "name\n"); + pr_info("[ pid ] uid tgid total_vm rss cpu oom_adj oom_score_adj name\n"); for_each_process(p) { if (p->flags & PF_KTHREAD) continue; @@ -414,10 +371,11 @@ static void dump_tasks(const struct mem_cgroup *mem) continue; } - printk(KERN_INFO "[%5d] %5d %5d %8lu %8lu %3u %3d %s\n", - task->pid, __task_cred(task)->uid, task->tgid, - task->mm->total_vm, get_mm_rss(task->mm), - task_cpu(task), task->signal->oom_adj, task->comm); + pr_info("[%5d] %5d %5d %8lu %8lu %3u %3d %5d %s\n", + task->pid, __task_cred(task)->uid, task->tgid, + task->mm->total_vm, get_mm_rss(task->mm), + task_cpu(task), task->signal->oom_adj, + task->signal->oom_score_adj, task->comm); task_unlock(task); } } @@ -427,8 +385,9 @@ static void dump_header(struct task_struct *p, gfp_t gfp_mask, int order, { task_lock(current); pr_warning("%s invoked oom-killer: gfp_mask=0x%x, order=%d, " - "oom_adj=%d\n", - current->comm, gfp_mask, order, current->signal->oom_adj); + "oom_adj=%d, oom_score_adj=%d\n", + current->comm, gfp_mask, order, current->signal->oom_adj, + current->signal->oom_score_adj); cpuset_print_task_mems_allowed(current); task_unlock(current); dump_stack(); @@ -468,14 +427,14 @@ static int oom_kill_task(struct task_struct *p, struct mem_cgroup *mem) #undef K static int oom_kill_process(struct task_struct *p, gfp_t gfp_mask, int order, - unsigned long points, struct mem_cgroup *mem, - nodemask_t *nodemask, const char *message) + unsigned int points, unsigned long totalpages, + struct mem_cgroup *mem, nodemask_t *nodemask, + const char *message) { struct task_struct *victim = p; struct task_struct *child; struct task_struct *t = p; - unsigned long victim_points = 0; - struct timespec uptime; + unsigned int victim_points = 0; if (printk_ratelimit()) dump_header(p, gfp_mask, order, mem); @@ -491,7 +450,7 @@ static int oom_kill_process(struct task_struct *p, gfp_t gfp_mask, int order, } task_lock(p); - pr_err("%s: Kill process %d (%s) score %lu or sacrifice child\n", + pr_err("%s: Kill process %d (%s) score %d or sacrifice child\n", message, task_pid_nr(p), p->comm, points); task_unlock(p); @@ -501,14 +460,15 @@ static int oom_kill_process(struct task_struct *p, gfp_t gfp_mask, int order, * parent. This attempts to lose the minimal amount of work done while * still freeing memory. */ - do_posix_clock_monotonic_gettime(&uptime); do { list_for_each_entry(child, &t->children, sibling) { - unsigned long child_points; + unsigned int child_points; - /* badness() returns 0 if the thread is unkillable */ - child_points = badness(child, mem, nodemask, - uptime.tv_sec); + /* + * oom_badness() returns 0 if the thread is unkillable + */ + child_points = oom_badness(child, mem, nodemask, + totalpages); if (child_points > victim_points) { victim = child; victim_points = child_points; @@ -546,17 +506,19 @@ static void check_panic_on_oom(enum oom_constraint constraint, gfp_t gfp_mask, #ifdef CONFIG_CGROUP_MEM_RES_CTLR void mem_cgroup_out_of_memory(struct mem_cgroup *mem, gfp_t gfp_mask) { - unsigned long points = 0; + unsigned long limit; + unsigned int points = 0; struct task_struct *p; check_panic_on_oom(CONSTRAINT_MEMCG, gfp_mask, 0); + limit = mem_cgroup_get_limit(mem) >> PAGE_SHIFT; read_lock(&tasklist_lock); retry: - p = select_bad_process(&points, mem, NULL); + p = select_bad_process(&points, limit, mem, NULL); if (!p || PTR_ERR(p) == -1UL) goto out; - if (oom_kill_process(p, gfp_mask, 0, points, mem, NULL, + if (oom_kill_process(p, gfp_mask, 0, points, limit, mem, NULL, "Memory cgroup out of memory")) goto retry; out: @@ -681,8 +643,9 @@ void out_of_memory(struct zonelist *zonelist, gfp_t gfp_mask, int order, nodemask_t *nodemask) { struct task_struct *p; + unsigned long totalpages; unsigned long freed = 0; - unsigned long points; + unsigned int points; enum oom_constraint constraint = CONSTRAINT_NONE; blocking_notifier_call_chain(&oom_notify_list, 0, &freed); @@ -705,8 +668,8 @@ void out_of_memory(struct zonelist *zonelist, gfp_t gfp_mask, * Check if there were limitations on the allocation (only relevant for * NUMA) that may require different handling. */ - if (zonelist) - constraint = constrained_alloc(zonelist, gfp_mask, nodemask); + constraint = constrained_alloc(zonelist, gfp_mask, nodemask, + &totalpages); check_panic_on_oom(constraint, gfp_mask, order); read_lock(&tasklist_lock); @@ -718,14 +681,14 @@ void out_of_memory(struct zonelist *zonelist, gfp_t gfp_mask, * non-zero, current could not be killed so we must fallback to * the tasklist scan. */ - if (!oom_kill_process(current, gfp_mask, order, 0, NULL, - nodemask, + if (!oom_kill_process(current, gfp_mask, order, 0, totalpages, + NULL, nodemask, "Out of memory (oom_kill_allocating_task)")) return; } retry: - p = select_bad_process(&points, NULL, + p = select_bad_process(&points, totalpages, NULL, constraint == CONSTRAINT_MEMORY_POLICY ? nodemask : NULL); if (PTR_ERR(p) == -1UL) @@ -738,8 +701,8 @@ retry: panic("Out of memory and no killable processes...\n"); } - if (oom_kill_process(p, gfp_mask, order, points, NULL, nodemask, - "Out of memory")) + if (oom_kill_process(p, gfp_mask, order, points, totalpages, NULL, + nodemask, "Out of memory")) goto retry; read_unlock(&tasklist_lock); -- cgit v1.2.3 From 51b1bd2ace1595b72956224deda349efa880b693 Mon Sep 17 00:00:00 2001 From: David Rientjes Date: Mon, 9 Aug 2010 17:19:47 -0700 Subject: oom: deprecate oom_adj tunable /proc/pid/oom_adj is now deprecated so that that it may eventually be removed. The target date for removal is August 2012. A warning will be printed to the kernel log if a task attempts to use this interface. Future warning will be suppressed until the kernel is rebooted to prevent spamming the kernel log. Signed-off-by: David Rientjes Cc: Nick Piggin Cc: KAMEZAWA Hiroyuki Cc: KOSAKI Motohiro Cc: Oleg Nesterov Cc: Balbir Singh Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/feature-removal-schedule.txt | 25 +++++++++++++++++++++++++ Documentation/filesystems/proc.txt | 3 +++ fs/proc/base.c | 8 ++++++++ include/linux/oom.h | 3 +++ 4 files changed, 39 insertions(+) (limited to 'fs') diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 71f0fea1058f..56cee4727b1a 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -151,6 +151,31 @@ Who: Eric Biederman --------------------------- +What: /proc//oom_adj +When: August 2012 +Why: /proc//oom_adj allows userspace to influence the oom killer's + badness heuristic used to determine which task to kill when the kernel + is out of memory. + + The badness heuristic has since been rewritten since the introduction of + this tunable such that its meaning is deprecated. The value was + implemented as a bitshift on a score generated by the badness() + function that did not have any precise units of measure. With the + rewrite, the score is given as a proportion of available memory to the + task allocating pages, so using a bitshift which grows the score + exponentially is, thus, impossible to tune with fine granularity. + + A much more powerful interface, /proc//oom_score_adj, was + introduced with the oom killer rewrite that allows users to increase or + decrease the badness() score linearly. This interface will replace + /proc//oom_adj. + + A warning will be emitted to the kernel log if an application uses this + deprecated interface. After it is printed once, future warnings will be + suppressed until the kernel is rebooted. + +--------------------------- + What: remove EXPORT_SYMBOL(kernel_thread) When: August 2006 Files: arch/*/kernel/*_ksyms.c diff --git a/Documentation/filesystems/proc.txt b/Documentation/filesystems/proc.txt index cf1295c2bb66..a6aca8740883 100644 --- a/Documentation/filesystems/proc.txt +++ b/Documentation/filesystems/proc.txt @@ -1285,6 +1285,9 @@ scaled linearly with /proc//oom_score_adj. Writing to /proc//oom_score_adj or /proc//oom_adj will change the other with its scaled value. +NOTICE: /proc//oom_adj is deprecated and will be removed, please see +Documentation/feature-removal-schedule.txt. + Caveat: when a parent task is selected, the oom killer will sacrifice any first generation children with seperate address spaces instead, if possible. This avoids servers and important system daemons from being killed and loses the diff --git a/fs/proc/base.c b/fs/proc/base.c index f923b728388a..69254a365ce2 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -1037,6 +1037,14 @@ static ssize_t oom_adjust_write(struct file *file, const char __user *buf, return -EACCES; } + /* + * Warn that /proc/pid/oom_adj is deprecated, see + * Documentation/feature-removal-schedule.txt. + */ + printk_once(KERN_WARNING "%s (%d): /proc/%d/oom_adj is deprecated, " + "please use /proc/%d/oom_score_adj instead.\n", + current->comm, task_pid_nr(current), + task_pid_nr(task), task_pid_nr(task)); task->signal->oom_adj = oom_adjust; /* * Scale /proc/pid/oom_score_adj appropriately ensuring that a maximum diff --git a/include/linux/oom.h b/include/linux/oom.h index 73b8d7b6dd19..f209b683e118 100644 --- a/include/linux/oom.h +++ b/include/linux/oom.h @@ -2,6 +2,9 @@ #define __INCLUDE_LINUX_OOM_H /* + * /proc//oom_adj is deprecated, see + * Documentation/feature-removal-schedule.txt. + * * /proc//oom_adj set to -17 protects from the oom-killer */ #define OOM_DISABLE (-17) -- cgit v1.2.3 From 7624ee72aa09334af072853457a5d46d9901c3f8 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Mon, 9 Aug 2010 17:20:03 -0700 Subject: mm: avoid resetting wb_start after each writeback round WB_SYNC_NONE writeback is done in rounds of 1024 pages so that we don't write out some huge inode for too long while starving writeout of other inodes. To avoid livelocks, we record time we started writeback in wbc->wb_start and do not write out inodes which were dirtied after this time. But currently, writeback_inodes_wb() resets wb_start each time it is called thus effectively invalidating this logic and making any WB_SYNC_NONE writeback prone to livelocks. This patch makes sure wb_start is set only once when we start writeback. Signed-off-by: Jan Kara Reviewed-by: Wu Fengguang Cc: Christoph Hellwig Acked-by: Jens Axboe Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/fs-writeback.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index d5be1693ac93..30ac305e8293 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -530,7 +530,8 @@ void writeback_inodes_wb(struct bdi_writeback *wb, { int ret = 0; - wbc->wb_start = jiffies; /* livelock avoidance */ + if (!wbc->wb_start) + wbc->wb_start = jiffies; /* livelock avoidance */ spin_lock(&inode_lock); if (!wbc->for_kupdate || list_empty(&wb->b_io)) queue_io(wb, wbc->older_than_this); @@ -559,7 +560,6 @@ static void __writeback_inodes_sb(struct super_block *sb, { WARN_ON(!rwsem_is_locked(&sb->s_umount)); - wbc->wb_start = jiffies; /* livelock avoidance */ spin_lock(&inode_lock); if (!wbc->for_kupdate || list_empty(&wb->b_io)) queue_io(wb, wbc->older_than_this); @@ -625,6 +625,7 @@ static long wb_writeback(struct bdi_writeback *wb, wbc.range_end = LLONG_MAX; } + wbc.wb_start = jiffies; /* livelock avoidance */ for (;;) { /* * Stop writeback when nr_pages has been consumed -- cgit v1.2.3 From 85c9fe8fcaf630225b26047b3a7cc5167739eced Mon Sep 17 00:00:00 2001 From: Kevin Winchester Date: Mon, 9 Aug 2010 17:20:22 -0700 Subject: vfs: fix warning: 'dirent' is used uninitialized in this function Using: gcc (GCC) 4.5.0 20100610 (prerelease) The following warnings appear: fs/readdir.c: In function `filldir64': fs/readdir.c:240:15: warning: `dirent' is used uninitialized in this function fs/readdir.c: In function `filldir': fs/readdir.c:155:15: warning: `dirent' is used uninitialized in this function fs/compat.c: In function `compat_filldir64': fs/compat.c:1071:11: warning: `dirent' is used uninitialized in this function fs/compat.c: In function `compat_filldir': fs/compat.c:984:15: warning: `dirent' is used uninitialized in this function The warnings are related to the use of the NAME_OFFSET() macro. Luckily, it appears as though the standard offsetof() macro is what is being implemented by NAME_OFFSET(), thus we can fix the warning and use a more standard code construct at the same time. Signed-off-by: Kevin Winchester Cc: Al Viro Cc: Christoph Hellwig Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/compat.c | 10 +++++----- fs/readdir.c | 8 +++++--- 2 files changed, 10 insertions(+), 8 deletions(-) (limited to 'fs') diff --git a/fs/compat.c b/fs/compat.c index c6fda9aeb864..5976bad85f65 100644 --- a/fs/compat.c +++ b/fs/compat.c @@ -15,6 +15,7 @@ * published by the Free Software Foundation. */ +#include #include #include #include @@ -891,8 +892,6 @@ asmlinkage long compat_sys_mount(char __user * dev_name, char __user * dir_name, return retval; } -#define NAME_OFFSET(de) ((int) ((de)->d_name - (char __user *) (de))) - struct compat_old_linux_dirent { compat_ulong_t d_ino; compat_ulong_t d_offset; @@ -981,7 +980,8 @@ static int compat_filldir(void *__buf, const char *name, int namlen, struct compat_linux_dirent __user * dirent; struct compat_getdents_callback *buf = __buf; compat_ulong_t d_ino; - int reclen = ALIGN(NAME_OFFSET(dirent) + namlen + 2, sizeof(compat_long_t)); + int reclen = ALIGN(offsetof(struct compat_linux_dirent, d_name) + + namlen + 2, sizeof(compat_long_t)); buf->error = -EINVAL; /* only used if we fail.. */ if (reclen > buf->count) @@ -1068,8 +1068,8 @@ static int compat_filldir64(void * __buf, const char * name, int namlen, loff_t { struct linux_dirent64 __user *dirent; struct compat_getdents_callback64 *buf = __buf; - int jj = NAME_OFFSET(dirent); - int reclen = ALIGN(jj + namlen + 1, sizeof(u64)); + int reclen = ALIGN(offsetof(struct linux_dirent64, d_name) + namlen + 1, + sizeof(u64)); u64 off; buf->error = -EINVAL; /* only used if we fail.. */ diff --git a/fs/readdir.c b/fs/readdir.c index 7723401f8d8b..356f71528ad6 100644 --- a/fs/readdir.c +++ b/fs/readdir.c @@ -4,6 +4,7 @@ * Copyright (C) 1995 Linus Torvalds */ +#include #include #include #include @@ -54,7 +55,6 @@ EXPORT_SYMBOL(vfs_readdir); * anyway. Thus the special "fillonedir()" function for that * case (the low-level handlers don't need to care about this). */ -#define NAME_OFFSET(de) ((int) ((de)->d_name - (char __user *) (de))) #ifdef __ARCH_WANT_OLD_READDIR @@ -152,7 +152,8 @@ static int filldir(void * __buf, const char * name, int namlen, loff_t offset, struct linux_dirent __user * dirent; struct getdents_callback * buf = (struct getdents_callback *) __buf; unsigned long d_ino; - int reclen = ALIGN(NAME_OFFSET(dirent) + namlen + 2, sizeof(long)); + int reclen = ALIGN(offsetof(struct linux_dirent, d_name) + namlen + 2, + sizeof(long)); buf->error = -EINVAL; /* only used if we fail.. */ if (reclen > buf->count) @@ -237,7 +238,8 @@ static int filldir64(void * __buf, const char * name, int namlen, loff_t offset, { struct linux_dirent64 __user *dirent; struct getdents_callback64 * buf = (struct getdents_callback64 *) __buf; - int reclen = ALIGN(NAME_OFFSET(dirent) + namlen + 1, sizeof(u64)); + int reclen = ALIGN(offsetof(struct linux_dirent64, d_name) + namlen + 1, + sizeof(u64)); buf->error = -EINVAL; /* only used if we fail.. */ if (reclen > buf->count) -- cgit v1.2.3 From 26df6d13406d1a53b0bda08bd712f1924affd7cd Mon Sep 17 00:00:00 2001 From: "hyc@symas.com" Date: Tue, 22 Jun 2010 10:14:49 -0700 Subject: tty: Add EXTPROC support for LINEMODE This patch is against the 2.6.34 source. Paraphrased from the 1989 BSD patch by David Borman @ cray.com: These are the changes needed for the kernel to support LINEMODE in the server. There is a new bit in the termios local flag word, EXTPROC. When this bit is set, several aspects of the terminal driver are disabled. Input line editing, character echo, and mapping of signals are all disabled. This allows the telnetd to turn off these functions when in linemode, but still keep track of what state the user wants the terminal to be in. New ioctl: TIOCSIG Generate a signal to processes in the current process group of the pty. There is a new mode for packet driver, the TIOCPKT_IOCTL bit. When packet mode is turned on in the pty, and the EXTPROC bit is set, then whenever the state of the pty is changed, the next read on the master side of the pty will have the TIOCPKT_IOCTL bit set. This allows the process on the server side of the pty to know when the state of the terminal has changed; it can then issue the appropriate ioctl to retrieve the new state. Since the original BSD patches accompanied the source code for telnet I've left that reference here, but obviously the feature is useful for any remote terminal protocol, including ssh. The corresponding feature has existed in the BSD tty driver since 1989. For historical reference, a good copy of the relevant files can be found here: http://anonsvn.mit.edu/viewvc/krb5/trunk/src/appl/telnet/?pathrev=17741 Signed-off-by: Howard Chu Cc: Alan Cox Signed-off-by: Greg Kroah-Hartman --- arch/alpha/include/asm/ioctls.h | 2 ++ arch/alpha/include/asm/termbits.h | 1 + arch/arm/include/asm/ioctls.h | 2 ++ arch/arm/include/asm/termbits.h | 1 + arch/avr32/include/asm/ioctls.h | 2 ++ arch/avr32/include/asm/termbits.h | 1 + arch/cris/include/asm/ioctls.h | 2 ++ arch/cris/include/asm/termbits.h | 1 + arch/frv/include/asm/ioctls.h | 2 ++ arch/frv/include/asm/termbits.h | 1 + arch/h8300/include/asm/ioctls.h | 2 ++ arch/h8300/include/asm/termbits.h | 1 + arch/ia64/include/asm/ioctls.h | 2 ++ arch/ia64/include/asm/termbits.h | 1 + arch/m32r/include/asm/ioctls.h | 2 ++ arch/m32r/include/asm/termbits.h | 1 + arch/m68k/include/asm/ioctls.h | 2 ++ arch/m68k/include/asm/termbits.h | 1 + arch/mips/include/asm/ioctls.h | 3 ++- arch/mips/include/asm/termbits.h | 1 + arch/mn10300/include/asm/ioctls.h | 2 ++ arch/mn10300/include/asm/termbits.h | 1 + arch/parisc/include/asm/ioctls.h | 2 ++ arch/parisc/include/asm/termbits.h | 1 + arch/powerpc/include/asm/ioctls.h | 2 ++ arch/powerpc/include/asm/termbits.h | 1 + arch/s390/include/asm/ioctls.h | 2 ++ arch/sh/include/asm/ioctls.h | 2 ++ arch/sparc/include/asm/ioctls.h | 2 ++ arch/sparc/include/asm/termbits.h | 1 + arch/xtensa/include/asm/ioctls.h | 2 ++ arch/xtensa/include/asm/termbits.h | 1 + drivers/char/n_tty.c | 17 ++++++++++++++--- drivers/char/pty.c | 21 +++++++++++++++++++++ drivers/char/tty_ioctl.c | 18 ++++++++++++------ fs/compat_ioctl.c | 1 + include/asm-generic/ioctls.h | 2 ++ include/asm-generic/termbits.h | 1 + include/linux/tty.h | 1 + 39 files changed, 101 insertions(+), 10 deletions(-) (limited to 'fs') diff --git a/arch/alpha/include/asm/ioctls.h b/arch/alpha/include/asm/ioctls.h index c7b0cc61ce5b..59617c3c2be6 100644 --- a/arch/alpha/include/asm/ioctls.h +++ b/arch/alpha/include/asm/ioctls.h @@ -80,6 +80,7 @@ # define TIOCPKT_START 8 # define TIOCPKT_NOSTOP 16 # define TIOCPKT_DOSTOP 32 +# define TIOCPKT_IOCTL 64 #define TIOCNOTTY 0x5422 @@ -91,6 +92,7 @@ #define TIOCGSID 0x5429 /* Return the session ID of FD */ #define TIOCGPTN _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */ #define TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */ +#define TIOCSIG _IOW('T',0x36, int) /* Generate signal on Pty slave */ #define TIOCSERCONFIG 0x5453 #define TIOCSERGWILD 0x5454 diff --git a/arch/alpha/include/asm/termbits.h b/arch/alpha/include/asm/termbits.h index ad854a4a3af6..879dd3589921 100644 --- a/arch/alpha/include/asm/termbits.h +++ b/arch/alpha/include/asm/termbits.h @@ -180,6 +180,7 @@ struct ktermios { #define FLUSHO 0x00800000 #define PENDIN 0x20000000 #define IEXTEN 0x00000400 +#define EXTPROC 0x10000000 /* Values for the ACTION argument to `tcflow'. */ #define TCOOFF 0 diff --git a/arch/arm/include/asm/ioctls.h b/arch/arm/include/asm/ioctls.h index 7f0b6d13296a..0b30894b5482 100644 --- a/arch/arm/include/asm/ioctls.h +++ b/arch/arm/include/asm/ioctls.h @@ -52,6 +52,7 @@ #define TCSETSF2 _IOW('T',0x2D, struct termios2) #define TIOCGPTN _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */ #define TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */ +#define TIOCSIG _IOW('T',0x36, int) /* Generate signal on Pty slave */ #define TIOCGRS485 0x542E #define TIOCSRS485 0x542F @@ -81,6 +82,7 @@ #define TIOCPKT_START 8 #define TIOCPKT_NOSTOP 16 #define TIOCPKT_DOSTOP 32 +#define TIOCPKT_IOCTL 64 #define TIOCSER_TEMT 0x01 /* Transmitter physically empty */ diff --git a/arch/arm/include/asm/termbits.h b/arch/arm/include/asm/termbits.h index f784d11f40b5..704135d28d1d 100644 --- a/arch/arm/include/asm/termbits.h +++ b/arch/arm/include/asm/termbits.h @@ -177,6 +177,7 @@ struct ktermios { #define FLUSHO 0010000 #define PENDIN 0040000 #define IEXTEN 0100000 +#define EXTPROC 0200000 /* tcflow() and TCXONC use these */ #define TCOOFF 0 diff --git a/arch/avr32/include/asm/ioctls.h b/arch/avr32/include/asm/ioctls.h index 143dafb3997e..b7dd324b46a9 100644 --- a/arch/avr32/include/asm/ioctls.h +++ b/arch/avr32/include/asm/ioctls.h @@ -53,6 +53,7 @@ #define TCSETSF2 _IOW('T',0x2D, struct termios2) #define TIOCGPTN _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */ #define TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */ +#define TIOCSIG _IOW('T',0x36, int) /* Generate signal on Pty slave */ #define TIOCGRS485 0x542E #define TIOCSRS485 0x542F @@ -82,6 +83,7 @@ #define TIOCPKT_START 8 #define TIOCPKT_NOSTOP 16 #define TIOCPKT_DOSTOP 32 +#define TIOCPKT_IOCTL 64 #define TIOCSER_TEMT 0x01 /* Transmitter physically empty */ diff --git a/arch/avr32/include/asm/termbits.h b/arch/avr32/include/asm/termbits.h index db2daab31fdb..366adc5ebb10 100644 --- a/arch/avr32/include/asm/termbits.h +++ b/arch/avr32/include/asm/termbits.h @@ -175,6 +175,7 @@ struct ktermios { #define FLUSHO 0010000 #define PENDIN 0040000 #define IEXTEN 0100000 +#define EXTPROC 0200000 /* tcflow() and TCXONC use these */ #define TCOOFF 0 diff --git a/arch/cris/include/asm/ioctls.h b/arch/cris/include/asm/ioctls.h index bb49142dc6ab..c9129ed37443 100644 --- a/arch/cris/include/asm/ioctls.h +++ b/arch/cris/include/asm/ioctls.h @@ -54,6 +54,7 @@ #define TCSETSF2 _IOW('T',0x2D, struct termios2) #define TIOCGPTN _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */ #define TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */ +#define TIOCSIG _IOW('T',0x36, int) /* Generate signal on Pty slave */ #define FIONCLEX 0x5450 /* these numbers need to be adjusted. */ #define FIOCLEX 0x5451 @@ -85,6 +86,7 @@ #define TIOCPKT_START 8 #define TIOCPKT_NOSTOP 16 #define TIOCPKT_DOSTOP 32 +#define TIOCPKT_IOCTL 64 #define TIOCSER_TEMT 0x01 /* Transmitter physically empty */ diff --git a/arch/cris/include/asm/termbits.h b/arch/cris/include/asm/termbits.h index 66e1a7492a0c..1c43bc874ccf 100644 --- a/arch/cris/include/asm/termbits.h +++ b/arch/cris/include/asm/termbits.h @@ -214,6 +214,7 @@ struct ktermios { #define FLUSHO 0010000 #define PENDIN 0040000 #define IEXTEN 0100000 +#define EXTPROC 0200000 /* tcflow() and TCXONC use these */ #define TCOOFF 0 diff --git a/arch/frv/include/asm/ioctls.h b/arch/frv/include/asm/ioctls.h index d0c30e31fbda..a993e3759ccf 100644 --- a/arch/frv/include/asm/ioctls.h +++ b/arch/frv/include/asm/ioctls.h @@ -53,6 +53,7 @@ #define TCSETSF2 _IOW('T',0x2D, struct termios2) #define TIOCGPTN _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */ #define TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */ +#define TIOCSIG _IOW('T',0x36, int) /* Generate signal on Pty slave */ #define FIONCLEX 0x5450 /* these numbers need to be adjusted. */ #define FIOCLEX 0x5451 @@ -79,6 +80,7 @@ #define TIOCPKT_START 8 #define TIOCPKT_NOSTOP 16 #define TIOCPKT_DOSTOP 32 +#define TIOCPKT_IOCTL 64 #define TIOCSER_TEMT 0x01 /* Transmitter physically empty */ diff --git a/arch/frv/include/asm/termbits.h b/arch/frv/include/asm/termbits.h index 5568492b5086..7722e19cc349 100644 --- a/arch/frv/include/asm/termbits.h +++ b/arch/frv/include/asm/termbits.h @@ -180,6 +180,7 @@ struct ktermios { #define FLUSHO 0010000 #define PENDIN 0040000 #define IEXTEN 0100000 +#define EXTPROC 0200000 /* tcflow() and TCXONC use these */ diff --git a/arch/h8300/include/asm/ioctls.h b/arch/h8300/include/asm/ioctls.h index 98a53d067269..b6b249f9f308 100644 --- a/arch/h8300/include/asm/ioctls.h +++ b/arch/h8300/include/asm/ioctls.h @@ -53,6 +53,7 @@ #define TCSETSF2 _IOW('T',0x2D, struct termios2) #define TIOCGPTN _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */ #define TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */ +#define TIOCSIG _IOW('T',0x36, int) /* Generate signal on Pty slave */ #define FIONCLEX 0x5450 /* these numbers need to be adjusted. */ #define FIOCLEX 0x5451 @@ -79,6 +80,7 @@ #define TIOCPKT_START 8 #define TIOCPKT_NOSTOP 16 #define TIOCPKT_DOSTOP 32 +#define TIOCPKT_IOCTL 64 #define TIOCSER_TEMT 0x01 /* Transmitter physically empty */ diff --git a/arch/h8300/include/asm/termbits.h b/arch/h8300/include/asm/termbits.h index 31eca81db3f7..3287a6244d74 100644 --- a/arch/h8300/include/asm/termbits.h +++ b/arch/h8300/include/asm/termbits.h @@ -179,6 +179,7 @@ struct ktermios { #define FLUSHO 0010000 #define PENDIN 0040000 #define IEXTEN 0100000 +#define EXTPROC 0200000 /* tcflow() and TCXONC use these */ diff --git a/arch/ia64/include/asm/ioctls.h b/arch/ia64/include/asm/ioctls.h index f0ee86c0b5f7..b79c385114ef 100644 --- a/arch/ia64/include/asm/ioctls.h +++ b/arch/ia64/include/asm/ioctls.h @@ -59,6 +59,7 @@ #define TCSETSF2 _IOW('T',0x2D, struct termios2) #define TIOCGPTN _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */ #define TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */ +#define TIOCSIG _IOW('T',0x36, int) /* Generate signal on Pty slave */ #define FIONCLEX 0x5450 /* these numbers need to be adjusted. */ #define FIOCLEX 0x5451 @@ -85,6 +86,7 @@ #define TIOCPKT_START 8 #define TIOCPKT_NOSTOP 16 #define TIOCPKT_DOSTOP 32 +#define TIOCPKT_IOCTL 64 #define TIOCSER_TEMT 0x01 /* Transmitter physically empty */ diff --git a/arch/ia64/include/asm/termbits.h b/arch/ia64/include/asm/termbits.h index 9f162e0089ad..c009b94e58d9 100644 --- a/arch/ia64/include/asm/termbits.h +++ b/arch/ia64/include/asm/termbits.h @@ -187,6 +187,7 @@ struct ktermios { #define FLUSHO 0010000 #define PENDIN 0040000 #define IEXTEN 0100000 +#define EXTPROC 0200000 /* tcflow() and TCXONC use these */ #define TCOOFF 0 diff --git a/arch/m32r/include/asm/ioctls.h b/arch/m32r/include/asm/ioctls.h index f4c13a52fe48..66288063a4c0 100644 --- a/arch/m32r/include/asm/ioctls.h +++ b/arch/m32r/include/asm/ioctls.h @@ -53,6 +53,7 @@ #define TCSETSF2 _IOW('T',0x2D, struct termios2) #define TIOCGPTN _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */ #define TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */ +#define TIOCSIG _IOW('T',0x36, int) /* Generate signal on Pty slave */ #define FIONCLEX 0x5450 #define FIOCLEX 0x5451 @@ -79,6 +80,7 @@ #define TIOCPKT_START 8 #define TIOCPKT_NOSTOP 16 #define TIOCPKT_DOSTOP 32 +#define TIOCPKT_IOCTL 64 #define TIOCSER_TEMT 0x01 /* Transmitter physically empty */ diff --git a/arch/m32r/include/asm/termbits.h b/arch/m32r/include/asm/termbits.h index bc104008b55b..957a3c688549 100644 --- a/arch/m32r/include/asm/termbits.h +++ b/arch/m32r/include/asm/termbits.h @@ -179,6 +179,7 @@ struct ktermios { #define FLUSHO 0010000 #define PENDIN 0040000 #define IEXTEN 0100000 +#define EXTPROC 0200000 /* tcflow() and TCXONC use these */ #define TCOOFF 0 diff --git a/arch/m68k/include/asm/ioctls.h b/arch/m68k/include/asm/ioctls.h index b8d2f4be7fd7..91a57d665460 100644 --- a/arch/m68k/include/asm/ioctls.h +++ b/arch/m68k/include/asm/ioctls.h @@ -52,6 +52,7 @@ #define TCSETSF2 _IOW('T',0x2D, struct termios2) #define TIOCGPTN _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */ #define TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */ +#define TIOCSIG _IOW('T',0x36, int) /* Generate signal on Pty slave */ #define FIONCLEX 0x5450 /* these numbers need to be adjusted. */ #define FIOCLEX 0x5451 @@ -78,6 +79,7 @@ #define TIOCPKT_START 8 #define TIOCPKT_NOSTOP 16 #define TIOCPKT_DOSTOP 32 +#define TIOCPKT_IOCTL 64 #define TIOCSER_TEMT 0x01 /* Transmitter physically empty */ diff --git a/arch/m68k/include/asm/termbits.h b/arch/m68k/include/asm/termbits.h index 8c14170996bb..aea1e37b765a 100644 --- a/arch/m68k/include/asm/termbits.h +++ b/arch/m68k/include/asm/termbits.h @@ -179,6 +179,7 @@ struct ktermios { #define FLUSHO 0010000 #define PENDIN 0040000 #define IEXTEN 0100000 +#define EXTPROC 0200000 /* tcflow() and TCXONC use these */ diff --git a/arch/mips/include/asm/ioctls.h b/arch/mips/include/asm/ioctls.h index 2fb9e1693bf7..d87cb0465693 100644 --- a/arch/mips/include/asm/ioctls.h +++ b/arch/mips/include/asm/ioctls.h @@ -41,7 +41,7 @@ #define TIOCPKT_START 0x08 /* start output */ #define TIOCPKT_NOSTOP 0x10 /* no more ^S, ^Q */ #define TIOCPKT_DOSTOP 0x20 /* now do ^S ^Q */ -/* #define TIOCPKT_IOCTL 0x40 state change of pty driver */ +#define TIOCPKT_IOCTL 0x40 /* state change of pty driver */ #define TIOCSWINSZ _IOW('t', 103, struct winsize) /* set window size */ #define TIOCGWINSZ _IOR('t', 104, struct winsize) /* get window size */ #define TIOCNOTTY 0x5471 /* void tty association */ @@ -83,6 +83,7 @@ #define TCSETSF2 _IOW('T', 0x2D, struct termios2) #define TIOCGPTN _IOR('T', 0x30, unsigned int) /* Get Pty Number (of pty-mux device) */ #define TIOCSPTLCK _IOW('T', 0x31, int) /* Lock/unlock Pty */ +#define TIOCSIG _IOW('T', 0x36, int) /* Generate signal on Pty slave */ /* I hope the range from 0x5480 on is free ... */ #define TIOCSCTTY 0x5480 /* become controlling tty */ diff --git a/arch/mips/include/asm/termbits.h b/arch/mips/include/asm/termbits.h index c83c68444e86..76630b396fac 100644 --- a/arch/mips/include/asm/termbits.h +++ b/arch/mips/include/asm/termbits.h @@ -203,6 +203,7 @@ struct ktermios { #define PENDIN 0040000 /* Retype pending input (state). */ #define TOSTOP 0100000 /* Send SIGTTOU for background output. */ #define ITOSTOP TOSTOP +#define EXTPROC 0200000 /* External processing on pty */ /* ioctl (fd, TIOCSERGETLSR, &result) where result may be as below */ #define TIOCSER_TEMT 0x01 /* Transmitter physically empty */ diff --git a/arch/mn10300/include/asm/ioctls.h b/arch/mn10300/include/asm/ioctls.h index 638219a99b1e..cb8cf1902234 100644 --- a/arch/mn10300/include/asm/ioctls.h +++ b/arch/mn10300/include/asm/ioctls.h @@ -54,6 +54,7 @@ #define TIOCGPTN _IOR('T', 0x30, unsigned int) /* Get Pty Number * (of pty-mux device) */ #define TIOCSPTLCK _IOW('T', 0x31, int) /* Lock/unlock Pty */ +#define TIOCSIG _IOW('T', 0x36, int) /* Generate signal on Pty slave */ #define FIONCLEX 0x5450 #define FIOCLEX 0x5451 @@ -80,6 +81,7 @@ #define TIOCPKT_START 8 #define TIOCPKT_NOSTOP 16 #define TIOCPKT_DOSTOP 32 +#define TIOCPKT_IOCTL 64 #define TIOCSER_TEMT 0x01 /* Transmitter physically empty */ diff --git a/arch/mn10300/include/asm/termbits.h b/arch/mn10300/include/asm/termbits.h index eb2b0dc1f696..130d42495972 100644 --- a/arch/mn10300/include/asm/termbits.h +++ b/arch/mn10300/include/asm/termbits.h @@ -180,6 +180,7 @@ struct ktermios { #define FLUSHO 0010000 #define PENDIN 0040000 #define IEXTEN 0100000 +#define EXTPROC 0200000 /* tcflow() and TCXONC use these */ #define TCOOFF 0 diff --git a/arch/parisc/include/asm/ioctls.h b/arch/parisc/include/asm/ioctls.h index 6cc497e52532..4e0614456bea 100644 --- a/arch/parisc/include/asm/ioctls.h +++ b/arch/parisc/include/asm/ioctls.h @@ -52,6 +52,7 @@ #define TCSETSF2 _IOW('T',0x2D, struct termios2) #define TIOCGPTN _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */ #define TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */ +#define TIOCSIG _IOW('T',0x36, int) /* Generate signal on Pty slave */ #define FIONCLEX 0x5450 /* these numbers need to be adjusted. */ #define FIOCLEX 0x5451 @@ -82,6 +83,7 @@ #define TIOCPKT_START 8 #define TIOCPKT_NOSTOP 16 #define TIOCPKT_DOSTOP 32 +#define TIOCPKT_IOCTL 64 #define TIOCSER_TEMT 0x01 /* Transmitter physically empty */ diff --git a/arch/parisc/include/asm/termbits.h b/arch/parisc/include/asm/termbits.h index d8bbc73b16b7..d1ab92177a5c 100644 --- a/arch/parisc/include/asm/termbits.h +++ b/arch/parisc/include/asm/termbits.h @@ -180,6 +180,7 @@ struct ktermios { #define FLUSHO 0010000 #define PENDIN 0040000 #define IEXTEN 0100000 +#define EXTPROC 0200000 /* tcflow() and TCXONC use these */ #define TCOOFF 0 diff --git a/arch/powerpc/include/asm/ioctls.h b/arch/powerpc/include/asm/ioctls.h index 1842186d872c..851920052e08 100644 --- a/arch/powerpc/include/asm/ioctls.h +++ b/arch/powerpc/include/asm/ioctls.h @@ -80,6 +80,7 @@ # define TIOCPKT_START 8 # define TIOCPKT_NOSTOP 16 # define TIOCPKT_DOSTOP 32 +# define TIOCPKT_IOCTL 64 #define TIOCNOTTY 0x5422 @@ -93,6 +94,7 @@ #define TIOCSRS485 0x542f #define TIOCGPTN _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */ #define TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */ +#define TIOCSIG _IOW('T',0x36, int) /* Generate signal on Pty slave */ #define TIOCSERCONFIG 0x5453 #define TIOCSERGWILD 0x5454 diff --git a/arch/powerpc/include/asm/termbits.h b/arch/powerpc/include/asm/termbits.h index 6698188ca550..549d700e18f2 100644 --- a/arch/powerpc/include/asm/termbits.h +++ b/arch/powerpc/include/asm/termbits.h @@ -189,6 +189,7 @@ struct ktermios { #define FLUSHO 0x00800000 #define PENDIN 0x20000000 #define IEXTEN 0x00000400 +#define EXTPROC 0x10000000 /* Values for the ACTION argument to `tcflow'. */ #define TCOOFF 0 diff --git a/arch/s390/include/asm/ioctls.h b/arch/s390/include/asm/ioctls.h index 40e481b1b461..2f3d8736361f 100644 --- a/arch/s390/include/asm/ioctls.h +++ b/arch/s390/include/asm/ioctls.h @@ -60,6 +60,7 @@ #define TCSETSF2 _IOW('T',0x2D, struct termios2) #define TIOCGPTN _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */ #define TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */ +#define TIOCSIG _IOW('T',0x36, int) /* Generate signal on Pty slave */ #define FIONCLEX 0x5450 /* these numbers need to be adjusted. */ #define FIOCLEX 0x5451 @@ -86,6 +87,7 @@ #define TIOCPKT_START 8 #define TIOCPKT_NOSTOP 16 #define TIOCPKT_DOSTOP 32 +#define TIOCPKT_IOCTL 64 #define TIOCSER_TEMT 0x01 /* Transmitter physically empty */ diff --git a/arch/sh/include/asm/ioctls.h b/arch/sh/include/asm/ioctls.h index c212c371a4a5..eb6c4c687972 100644 --- a/arch/sh/include/asm/ioctls.h +++ b/arch/sh/include/asm/ioctls.h @@ -69,6 +69,7 @@ # define TIOCPKT_START 8 # define TIOCPKT_NOSTOP 16 # define TIOCPKT_DOSTOP 32 +# define TIOCPKT_IOCTL 64 #define TIOCNOTTY _IO('T', 34) /* 0x5422 */ @@ -84,6 +85,7 @@ #define TCSETSF2 _IOW('T', 45, struct termios2) #define TIOCGPTN _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */ #define TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */ +#define TIOCSIG _IOW('T',0x36, int) /* Generate signal on Pty slave */ #define TIOCSERCONFIG _IO('T', 83) /* 0x5453 */ #define TIOCSERGWILD _IOR('T', 84, int) /* 0x5454 */ diff --git a/arch/sparc/include/asm/ioctls.h b/arch/sparc/include/asm/ioctls.h index 1fe6855c5c18..53f4ee009bdd 100644 --- a/arch/sparc/include/asm/ioctls.h +++ b/arch/sparc/include/asm/ioctls.h @@ -80,6 +80,7 @@ /* Get minor device of a pty master's FD -- Solaris equiv is ISPTM */ #define TIOCGPTN _IOR('t', 134, unsigned int) /* Get Pty Number */ #define TIOCSPTLCK _IOW('t', 135, int) /* Lock/unlock PTY */ +#define TIOCSIG _IOW('t', 136, int) /* Generate signal on Pty slave */ /* Little f */ #define FIOCLEX _IO('f', 1) @@ -132,5 +133,6 @@ #define TIOCPKT_START 8 #define TIOCPKT_NOSTOP 16 #define TIOCPKT_DOSTOP 32 +#define TIOCPKT_IOCTL 64 #endif /* !(_ASM_SPARC_IOCTLS_H) */ diff --git a/arch/sparc/include/asm/termbits.h b/arch/sparc/include/asm/termbits.h index d72dfed1f9d7..23b10ff08df2 100644 --- a/arch/sparc/include/asm/termbits.h +++ b/arch/sparc/include/asm/termbits.h @@ -225,6 +225,7 @@ struct ktermios { #define FLUSHO 0x00002000 #define PENDIN 0x00004000 #define IEXTEN 0x00008000 +#define EXTPROC 0x00010000 /* modem lines */ #define TIOCM_LE 0x001 diff --git a/arch/xtensa/include/asm/ioctls.h b/arch/xtensa/include/asm/ioctls.h index 0ffa942954b9..ab1800012ed9 100644 --- a/arch/xtensa/include/asm/ioctls.h +++ b/arch/xtensa/include/asm/ioctls.h @@ -81,6 +81,7 @@ # define TIOCPKT_START 8 # define TIOCPKT_NOSTOP 16 # define TIOCPKT_DOSTOP 32 +# define TIOCPKT_IOCTL 64 #define TIOCNOTTY _IO('T', 34) @@ -97,6 +98,7 @@ #define TCSETSF2 _IOW('T', 45, struct termios2) #define TIOCGPTN _IOR('T',0x30, unsigned int) /* Get Pty Number (of pty-mux device) */ #define TIOCSPTLCK _IOW('T',0x31, int) /* Lock/unlock Pty */ +#define TIOCSIG _IOW('T',0x36, int) /* Generate signal on Pty slave */ #define TIOCSERCONFIG _IO('T', 83) #define TIOCSERGWILD _IOR('T', 84, int) diff --git a/arch/xtensa/include/asm/termbits.h b/arch/xtensa/include/asm/termbits.h index 85aa6a3c0b6e..0d6c8715b24f 100644 --- a/arch/xtensa/include/asm/termbits.h +++ b/arch/xtensa/include/asm/termbits.h @@ -196,6 +196,7 @@ struct ktermios { #define FLUSHO 0010000 #define PENDIN 0040000 #define IEXTEN 0100000 +#define EXTPROC 0200000 /* tcflow() and TCXONC use these */ diff --git a/drivers/char/n_tty.c b/drivers/char/n_tty.c index bdae8327143c..428f4fe0b5f7 100644 --- a/drivers/char/n_tty.c +++ b/drivers/char/n_tty.c @@ -1102,6 +1102,11 @@ static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c) if (I_IUCLC(tty) && L_IEXTEN(tty)) c = tolower(c); + if (L_EXTPROC(tty)) { + put_tty_queue(c, tty); + return; + } + if (tty->stopped && !tty->flow_stopped && I_IXON(tty) && I_IXANY(tty) && c != START_CHAR(tty) && c != STOP_CHAR(tty) && c != INTR_CHAR(tty) && c != QUIT_CHAR(tty) && c != SUSP_CHAR(tty)) { @@ -1409,7 +1414,8 @@ static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp, n_tty_set_room(tty); - if (!tty->icanon && (tty->read_cnt >= tty->minimum_to_wake)) { + if ((!tty->icanon && (tty->read_cnt >= tty->minimum_to_wake)) || + L_EXTPROC(tty)) { kill_fasync(&tty->fasync, SIGIO, POLL_IN); if (waitqueue_active(&tty->read_wait)) wake_up_interruptible(&tty->read_wait); @@ -1585,7 +1591,7 @@ static int n_tty_open(struct tty_struct *tty) static inline int input_available_p(struct tty_struct *tty, int amt) { tty_flush_to_ldisc(tty); - if (tty->icanon) { + if (tty->icanon && !L_EXTPROC(tty)) { if (tty->canon_data) return 1; } else if (tty->read_cnt >= (amt ? amt : 1)) @@ -1632,6 +1638,11 @@ static int copy_from_read_buf(struct tty_struct *tty, spin_lock_irqsave(&tty->read_lock, flags); tty->read_tail = (tty->read_tail + n) & (N_TTY_BUF_SIZE-1); tty->read_cnt -= n; + /* Turn single EOF into zero-length read */ + if (L_EXTPROC(tty) && tty->icanon && n == 1) { + if (!tty->read_cnt && (*b)[n-1] == EOF_CHAR(tty)) + n--; + } spin_unlock_irqrestore(&tty->read_lock, flags); *b += n; *nr -= n; @@ -1812,7 +1823,7 @@ do_it_again: nr--; } - if (tty->icanon) { + if (tty->icanon && !L_EXTPROC(tty)) { /* N.B. avoid overrun if nr == 0 */ while (nr && tty->read_cnt) { int eol; diff --git a/drivers/char/pty.c b/drivers/char/pty.c index d83a43130df4..b640ef29be1c 100644 --- a/drivers/char/pty.c +++ b/drivers/char/pty.c @@ -171,6 +171,23 @@ static int pty_set_lock(struct tty_struct *tty, int __user *arg) return 0; } +/* Send a signal to the slave */ +static int pty_signal(struct tty_struct *tty, int sig) +{ + unsigned long flags; + struct pid *pgrp; + + if (tty->link) { + spin_lock_irqsave(&tty->link->ctrl_lock, flags); + pgrp = get_pid(tty->link->pgrp); + spin_unlock_irqrestore(&tty->link->ctrl_lock, flags); + + kill_pgrp(pgrp, sig, 1); + put_pid(pgrp); + } + return 0; +} + static void pty_flush_buffer(struct tty_struct *tty) { struct tty_struct *to = tty->link; @@ -321,6 +338,8 @@ static int pty_bsd_ioctl(struct tty_struct *tty, struct file *file, switch (cmd) { case TIOCSPTLCK: /* Set PT Lock (disallow slave open) */ return pty_set_lock(tty, (int __user *) arg); + case TIOCSIG: /* Send signal to other side of pty */ + return pty_signal(tty, (int) arg); } return -ENOIOCTLCMD; } @@ -476,6 +495,8 @@ static int pty_unix98_ioctl(struct tty_struct *tty, struct file *file, return pty_set_lock(tty, (int __user *)arg); case TIOCGPTN: /* Get PT Number */ return put_user(tty->index, (unsigned int __user *)arg); + case TIOCSIG: /* Send signal to other side of pty */ + return pty_signal(tty, (int) arg); } return -ENOIOCTLCMD; diff --git a/drivers/char/tty_ioctl.c b/drivers/char/tty_ioctl.c index 6bd5f8866c74..0c1889971459 100644 --- a/drivers/char/tty_ioctl.c +++ b/drivers/char/tty_ioctl.c @@ -517,19 +517,25 @@ static void change_termios(struct tty_struct *tty, struct ktermios *new_termios) /* See if packet mode change of state. */ if (tty->link && tty->link->packet) { + int extproc = (old_termios.c_lflag & EXTPROC) | + (tty->termios->c_lflag & EXTPROC); int old_flow = ((old_termios.c_iflag & IXON) && (old_termios.c_cc[VSTOP] == '\023') && (old_termios.c_cc[VSTART] == '\021')); int new_flow = (I_IXON(tty) && STOP_CHAR(tty) == '\023' && START_CHAR(tty) == '\021'); - if (old_flow != new_flow) { + if ((old_flow != new_flow) || extproc) { spin_lock_irqsave(&tty->ctrl_lock, flags); - tty->ctrl_status &= ~(TIOCPKT_DOSTOP | TIOCPKT_NOSTOP); - if (new_flow) - tty->ctrl_status |= TIOCPKT_DOSTOP; - else - tty->ctrl_status |= TIOCPKT_NOSTOP; + if (old_flow != new_flow) { + tty->ctrl_status &= ~(TIOCPKT_DOSTOP | TIOCPKT_NOSTOP); + if (new_flow) + tty->ctrl_status |= TIOCPKT_DOSTOP; + else + tty->ctrl_status |= TIOCPKT_NOSTOP; + } + if (extproc) + tty->ctrl_status |= TIOCPKT_IOCTL; spin_unlock_irqrestore(&tty->ctrl_lock, flags); wake_up_interruptible(&tty->link->read_wait); } diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c index 63ae85831464..fa4bc48810fd 100644 --- a/fs/compat_ioctl.c +++ b/fs/compat_ioctl.c @@ -969,6 +969,7 @@ COMPATIBLE_IOCTL(TIOCGPGRP) COMPATIBLE_IOCTL(TIOCGPTN) COMPATIBLE_IOCTL(TIOCSPTLCK) COMPATIBLE_IOCTL(TIOCSERGETLSR) +COMPATIBLE_IOCTL(TIOCSIG) #ifdef TCGETS2 COMPATIBLE_IOCTL(TCGETS2) COMPATIBLE_IOCTL(TCSETS2) diff --git a/include/asm-generic/ioctls.h b/include/asm-generic/ioctls.h index 1375fa1a7a4d..8554cb6a81b9 100644 --- a/include/asm-generic/ioctls.h +++ b/include/asm-generic/ioctls.h @@ -69,6 +69,7 @@ #define TCSETX 0x5433 #define TCSETXF 0x5434 #define TCSETXW 0x5435 +#define TIOCSIG _IOW('T', 0x36, int) /* pty: generate signal */ #define FIONCLEX 0x5450 #define FIOCLEX 0x5451 @@ -102,6 +103,7 @@ #define TIOCPKT_START 8 #define TIOCPKT_NOSTOP 16 #define TIOCPKT_DOSTOP 32 +#define TIOCPKT_IOCTL 64 #define TIOCSER_TEMT 0x01 /* Transmitter physically empty */ diff --git a/include/asm-generic/termbits.h b/include/asm-generic/termbits.h index 1c9773d48cb0..232b4781aef3 100644 --- a/include/asm-generic/termbits.h +++ b/include/asm-generic/termbits.h @@ -178,6 +178,7 @@ struct ktermios { #define FLUSHO 0010000 #define PENDIN 0040000 #define IEXTEN 0100000 +#define EXTPROC 0200000 /* tcflow() and TCXONC use these */ #define TCOOFF 0 diff --git a/include/linux/tty.h b/include/linux/tty.h index 7802a243ee13..2df60e4ff40e 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -179,6 +179,7 @@ struct tty_bufhead { #define L_FLUSHO(tty) _L_FLAG((tty), FLUSHO) #define L_PENDIN(tty) _L_FLAG((tty), PENDIN) #define L_IEXTEN(tty) _L_FLAG((tty), IEXTEN) +#define L_EXTPROC(tty) _L_FLAG((tty), EXTPROC) struct device; struct signal_struct; -- cgit v1.2.3 From e56fa10e92e077d456cbc33b7025032887772b33 Mon Sep 17 00:00:00 2001 From: Yehuda Sadeh Date: Mon, 17 May 2010 12:40:28 -0700 Subject: ceph: generalize mon requests, add pool op support Generalize the current statfs synchronous requests, and support pool_ops. Signed-off-by: Yehuda Sadeh Signed-off-by: Sage Weil --- fs/ceph/mon_client.c | 170 +++++++++++++++++++++++++++++++++++++++++++++------ fs/ceph/mon_client.h | 5 ++ 2 files changed, 158 insertions(+), 17 deletions(-) (limited to 'fs') diff --git a/fs/ceph/mon_client.c b/fs/ceph/mon_client.c index 54fe01c50706..b2a5a3e4a671 100644 --- a/fs/ceph/mon_client.c +++ b/fs/ceph/mon_client.c @@ -349,7 +349,7 @@ out: } /* - * statfs + * generic requests (e.g., statfs, poolop) */ static struct ceph_mon_generic_request *__lookup_generic_req( struct ceph_mon_client *monc, u64 tid) @@ -442,6 +442,35 @@ static struct ceph_msg *get_generic_reply(struct ceph_connection *con, return m; } +static int do_generic_request(struct ceph_mon_client *monc, + struct ceph_mon_generic_request *req) +{ + int err; + + /* register request */ + mutex_lock(&monc->mutex); + req->tid = ++monc->last_tid; + req->request->hdr.tid = cpu_to_le64(req->tid); + __insert_generic_request(monc, req); + monc->num_generic_requests++; + ceph_con_send(monc->con, ceph_msg_get(req->request)); + mutex_unlock(&monc->mutex); + + err = wait_for_completion_interruptible(&req->completion); + + mutex_lock(&monc->mutex); + rb_erase(&req->node, &monc->generic_request_tree); + monc->num_generic_requests--; + mutex_unlock(&monc->mutex); + + if (!err) + err = req->result; + return err; +} + +/* + * statfs + */ static void handle_statfs_reply(struct ceph_mon_client *monc, struct ceph_msg *msg) { @@ -468,7 +497,7 @@ static void handle_statfs_reply(struct ceph_mon_client *monc, return; bad: - pr_err("corrupt generic reply, no tid\n"); + pr_err("corrupt generic reply, tid %llu\n", tid); ceph_msg_dump(msg); } @@ -487,6 +516,7 @@ int ceph_monc_do_statfs(struct ceph_mon_client *monc, struct ceph_statfs *buf) kref_init(&req->kref); req->buf = buf; + req->buf_len = sizeof(*buf); init_completion(&req->completion); err = -ENOMEM; @@ -504,33 +534,134 @@ int ceph_monc_do_statfs(struct ceph_mon_client *monc, struct ceph_statfs *buf) h->monhdr.session_mon_tid = 0; h->fsid = monc->monmap->fsid; - /* register request */ - mutex_lock(&monc->mutex); - req->tid = ++monc->last_tid; - req->request->hdr.tid = cpu_to_le64(req->tid); - __insert_generic_request(monc, req); - monc->num_generic_requests++; - mutex_unlock(&monc->mutex); + err = do_generic_request(monc, req); - /* send request and wait */ - ceph_con_send(monc->con, ceph_msg_get(req->request)); - err = wait_for_completion_interruptible(&req->completion); +out: + kref_put(&req->kref, release_generic_request); + return err; +} + +/* + * pool ops + */ +static int get_poolop_reply_buf(const char *src, size_t src_len, + char *dst, size_t dst_len) +{ + u32 buf_len; + + if (src_len != sizeof(u32) + dst_len) + return -EINVAL; + + buf_len = le32_to_cpu(*(u32 *)src); + if (buf_len != dst_len) + return -EINVAL; + + memcpy(dst, src + sizeof(u32), dst_len); + return 0; +} + +static void handle_poolop_reply(struct ceph_mon_client *monc, + struct ceph_msg *msg) +{ + struct ceph_mon_generic_request *req; + struct ceph_mon_poolop_reply *reply = msg->front.iov_base; + u64 tid = le64_to_cpu(msg->hdr.tid); + + if (msg->front.iov_len < sizeof(*reply)) + goto bad; + dout("handle_poolop_reply %p tid %llu\n", msg, tid); mutex_lock(&monc->mutex); - rb_erase(&req->node, &monc->generic_request_tree); - monc->num_generic_requests--; + req = __lookup_generic_req(monc, tid); + if (req) { + if (req->buf_len && + get_poolop_reply_buf(msg->front.iov_base + sizeof(*reply), + msg->front.iov_len - sizeof(*reply), + req->buf, req->buf_len) < 0) { + mutex_unlock(&monc->mutex); + goto bad; + } + req->result = le32_to_cpu(reply->reply_code); + get_generic_request(req); + } mutex_unlock(&monc->mutex); + if (req) { + complete(&req->completion); + put_generic_request(req); + } + return; - if (!err) - err = req->result; +bad: + pr_err("corrupt generic reply, tid %llu\n", tid); + ceph_msg_dump(msg); +} + +/* + * Do a synchronous pool op. + */ +int ceph_monc_do_poolop(struct ceph_mon_client *monc, u32 op, + u32 pool, u64 snapid, + char *buf, int len) +{ + struct ceph_mon_generic_request *req; + struct ceph_mon_poolop *h; + int err; + + req = kzalloc(sizeof(*req), GFP_NOFS); + if (!req) + return -ENOMEM; + + kref_init(&req->kref); + req->buf = buf; + req->buf_len = len; + init_completion(&req->completion); + + err = -ENOMEM; + req->request = ceph_msg_new(CEPH_MSG_POOLOP, sizeof(*h), GFP_NOFS); + if (!req->request) + goto out; + req->reply = ceph_msg_new(CEPH_MSG_POOLOP_REPLY, 1024, GFP_NOFS); + if (!req->reply) + goto out; + + /* fill out request */ + req->request->hdr.version = cpu_to_le16(2); + h = req->request->front.iov_base; + h->monhdr.have_version = 0; + h->monhdr.session_mon = cpu_to_le16(-1); + h->monhdr.session_mon_tid = 0; + h->fsid = monc->monmap->fsid; + h->pool = cpu_to_le32(pool); + h->op = cpu_to_le32(op); + h->auid = 0; + h->snapid = cpu_to_le64(snapid); + h->name_len = 0; + + err = do_generic_request(monc, req); out: kref_put(&req->kref, release_generic_request); return err; } +int ceph_monc_create_snapid(struct ceph_mon_client *monc, + u32 pool, u64 *snapid) +{ + return ceph_monc_do_poolop(monc, POOL_OP_CREATE_UNMANAGED_SNAP, + pool, 0, (char *)snapid, sizeof(*snapid)); + +} + +int ceph_monc_delete_snapid(struct ceph_mon_client *monc, + u32 pool, u64 snapid) +{ + return ceph_monc_do_poolop(monc, POOL_OP_CREATE_UNMANAGED_SNAP, + pool, snapid, 0, 0); + +} + /* - * Resend pending statfs requests. + * Resend pending generic requests. */ static void __resend_generic_request(struct ceph_mon_client *monc) { @@ -783,6 +914,10 @@ static void dispatch(struct ceph_connection *con, struct ceph_msg *msg) handle_statfs_reply(monc, msg); break; + case CEPH_MSG_POOLOP_REPLY: + handle_poolop_reply(monc, msg); + break; + case CEPH_MSG_MON_MAP: ceph_monc_handle_map(monc, msg); break; @@ -820,6 +955,7 @@ static struct ceph_msg *mon_alloc_msg(struct ceph_connection *con, case CEPH_MSG_MON_SUBSCRIBE_ACK: m = ceph_msg_get(monc->m_subscribe_ack); break; + case CEPH_MSG_POOLOP_REPLY: case CEPH_MSG_STATFS_REPLY: return get_generic_reply(con, hdr, skip); case CEPH_MSG_AUTH_REPLY: diff --git a/fs/ceph/mon_client.h b/fs/ceph/mon_client.h index 174d794321d0..8e396f2c0963 100644 --- a/fs/ceph/mon_client.h +++ b/fs/ceph/mon_client.h @@ -50,6 +50,7 @@ struct ceph_mon_generic_request { struct rb_node node; int result; void *buf; + int buf_len; struct completion completion; struct ceph_msg *request; /* original request */ struct ceph_msg *reply; /* and reply */ @@ -111,6 +112,10 @@ extern int ceph_monc_open_session(struct ceph_mon_client *monc); extern int ceph_monc_validate_auth(struct ceph_mon_client *monc); +extern int ceph_monc_create_snapid(struct ceph_mon_client *monc, + u32 pool, u64 *snapid); +extern int ceph_monc_delete_snapid(struct ceph_mon_client *monc, + u32 pool, u64 snapid); #endif -- cgit v1.2.3 From aca0fa34bdaba39bfddddba8ca70dba4782e8fe6 Mon Sep 17 00:00:00 2001 From: Dave Kleikamp Date: Mon, 9 Aug 2010 15:57:38 -0500 Subject: jfs: don't allow os2 xattr namespace overlap with others It's currently possible to bypass xattr namespace access rules by prefixing valid xattr names with "os2.", since the os2 namespace stores extended attributes in a legacy format with no prefix. This patch adds checking to deny access to any valid namespace prefix following "os2.". Signed-off-by: Dave Kleikamp Reported-by: Sergey Vlasov Signed-off-by: Linus Torvalds --- fs/jfs/xattr.c | 87 +++++++++++++++++++++++++--------------------------------- 1 file changed, 38 insertions(+), 49 deletions(-) (limited to 'fs') diff --git a/fs/jfs/xattr.c b/fs/jfs/xattr.c index fa96bbb26343..2d7f165d0f1d 100644 --- a/fs/jfs/xattr.c +++ b/fs/jfs/xattr.c @@ -86,46 +86,25 @@ struct ea_buffer { #define EA_MALLOC 0x0008 +static int is_known_namespace(const char *name) +{ + if (strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN) && + strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN) && + strncmp(name, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN) && + strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN)) + return false; + + return true; +} + /* * These three routines are used to recognize on-disk extended attributes * that are in a recognized namespace. If the attribute is not recognized, * "os2." is prepended to the name */ -static inline int is_os2_xattr(struct jfs_ea *ea) +static int is_os2_xattr(struct jfs_ea *ea) { - /* - * Check for "system." - */ - if ((ea->namelen >= XATTR_SYSTEM_PREFIX_LEN) && - !strncmp(ea->name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN)) - return false; - /* - * Check for "user." - */ - if ((ea->namelen >= XATTR_USER_PREFIX_LEN) && - !strncmp(ea->name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)) - return false; - /* - * Check for "security." - */ - if ((ea->namelen >= XATTR_SECURITY_PREFIX_LEN) && - !strncmp(ea->name, XATTR_SECURITY_PREFIX, - XATTR_SECURITY_PREFIX_LEN)) - return false; - /* - * Check for "trusted." - */ - if ((ea->namelen >= XATTR_TRUSTED_PREFIX_LEN) && - !strncmp(ea->name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN)) - return false; - /* - * Add any other valid namespace prefixes here - */ - - /* - * We assume it's OS/2's flat namespace - */ - return true; + return !is_known_namespace(ea->name); } static inline int name_size(struct jfs_ea *ea) @@ -764,13 +743,23 @@ static int can_set_xattr(struct inode *inode, const char *name, if (!strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN)) return can_set_system_xattr(inode, name, value, value_len); + if (!strncmp(name, XATTR_OS2_PREFIX, XATTR_OS2_PREFIX_LEN)) { + /* + * This makes sure that we aren't trying to set an + * attribute in a different namespace by prefixing it + * with "os2." + */ + if (is_known_namespace(name + XATTR_OS2_PREFIX_LEN)) + return -EOPNOTSUPP; + return 0; + } + /* * Don't allow setting an attribute in an unknown namespace. */ if (strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN) && strncmp(name, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN) && - strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN) && - strncmp(name, XATTR_OS2_PREFIX, XATTR_OS2_PREFIX_LEN)) + strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN)) return -EOPNOTSUPP; return 0; @@ -952,19 +941,8 @@ ssize_t __jfs_getxattr(struct inode *inode, const char *name, void *data, int xattr_size; ssize_t size; int namelen = strlen(name); - char *os2name = NULL; char *value; - if (strncmp(name, XATTR_OS2_PREFIX, XATTR_OS2_PREFIX_LEN) == 0) { - os2name = kmalloc(namelen - XATTR_OS2_PREFIX_LEN + 1, - GFP_KERNEL); - if (!os2name) - return -ENOMEM; - strcpy(os2name, name + XATTR_OS2_PREFIX_LEN); - name = os2name; - namelen -= XATTR_OS2_PREFIX_LEN; - } - down_read(&JFS_IP(inode)->xattr_sem); xattr_size = ea_get(inode, &ea_buf, 0); @@ -1002,8 +980,6 @@ ssize_t __jfs_getxattr(struct inode *inode, const char *name, void *data, out: up_read(&JFS_IP(inode)->xattr_sem); - kfree(os2name); - return size; } @@ -1012,6 +988,19 @@ ssize_t jfs_getxattr(struct dentry *dentry, const char *name, void *data, { int err; + if (strncmp(name, XATTR_OS2_PREFIX, XATTR_OS2_PREFIX_LEN) == 0) { + /* + * skip past "os2." prefix + */ + name += XATTR_OS2_PREFIX_LEN; + /* + * Don't allow retrieving properly prefixed attributes + * by prepending them with "os2." + */ + if (is_known_namespace(name)) + return -EOPNOTSUPP; + } + err = __jfs_getxattr(dentry->d_inode, name, data, buf_size); return err; -- cgit v1.2.3 From 8cef9c67356eca3e6502444c8075a06c86872abf Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Tue, 22 Jun 2010 11:15:01 +1000 Subject: v9fs: fixup for inode_setattr being removed Signed-off-by: Stephen Rothwell Signed-off-by: Al Viro --- fs/9p/vfs_inode.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index d97c34a24f7a..c7c23eab9440 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -1263,10 +1263,19 @@ static int v9fs_vfs_setattr_dotl(struct dentry *dentry, struct iattr *iattr) return PTR_ERR(fid); retval = p9_client_setattr(fid, &p9attr); - if (retval >= 0) - retval = inode_setattr(dentry->d_inode, iattr); + if (retval < 0) + return retval; - return retval; + if ((iattr->ia_valid & ATTR_SIZE) && + iattr->ia_size != i_size_read(dentry->d_inode)) { + retval = vmtruncate(dentry->d_inode, iattr->ia_size); + if (retval) + return retval; + } + + setattr_copy(dentry->d_inode, iattr); + mark_inode_dirty(dentry->d_inode); + return 0; } /** -- cgit v1.2.3 From b76212d7f19420ab29d86e9d17d1ff36cfe0f922 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Mon, 19 Jul 2010 19:16:40 +0200 Subject: Add v7 alias So that the module gets autoloaded when a v7 filesystem is mounted. Cc: Christoph Hellwig Signed-off-by: Lubomir Rintel Signed-off-by: Al Viro --- fs/sysv/super.c | 1 + 1 file changed, 1 insertion(+) (limited to 'fs') diff --git a/fs/sysv/super.c b/fs/sysv/super.c index 0e44a6253352..2da3075aff78 100644 --- a/fs/sysv/super.c +++ b/fs/sysv/super.c @@ -559,4 +559,5 @@ static void __exit exit_sysv_fs(void) module_init(init_sysv_fs) module_exit(exit_sysv_fs) +MODULE_ALIAS("v7"); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 496ee9b8f349a8ae2065114c414a47e89bdeb930 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Thu, 22 Jul 2010 03:11:48 +0200 Subject: V7: Adjust sanity checks for some volumes Newly mkfs-ed filesystems from Seventh Edition have last modification time set to zero, but are otherwise perfectly valid. Also, tighten up other sanity checks to filter out most filesystems with different bytesex than we're using. Cc: Christoph Hellwig Signed-off-by: Lubomir Rintel Signed-off-by: Al Viro --- fs/sysv/super.c | 6 ++++-- include/linux/sysv_fs.h | 11 +++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/sysv/super.c b/fs/sysv/super.c index 2da3075aff78..5c0aab0b7e18 100644 --- a/fs/sysv/super.c +++ b/fs/sysv/super.c @@ -469,7 +469,7 @@ static int v7_fill_super(struct super_block *sb, void *data, int silent) v7sb = (struct v7_super_block *) bh->b_data; if (fs16_to_cpu(sbi, v7sb->s_nfree) > V7_NICFREE || fs16_to_cpu(sbi, v7sb->s_ninode) > V7_NICINOD || - fs32_to_cpu(sbi, v7sb->s_time) == 0) + fs32_to_cpu(sbi, v7sb->s_fsize) > V7_MAXSIZE) goto failed; /* plausibility check on root inode: it is a directory, @@ -479,7 +479,9 @@ static int v7_fill_super(struct super_block *sb, void *data, int silent) v7i = (struct sysv_inode *)(bh2->b_data + 64); if ((fs16_to_cpu(sbi, v7i->i_mode) & ~0777) != S_IFDIR || (fs32_to_cpu(sbi, v7i->i_size) == 0) || - (fs32_to_cpu(sbi, v7i->i_size) & 017) != 0) + (fs32_to_cpu(sbi, v7i->i_size) & 017) || + (fs32_to_cpu(sbi, v7i->i_size) > V7_NFILES * + sizeof (struct sysv_dir_entry))) goto failed; brelse(bh2); bh2 = NULL; diff --git a/include/linux/sysv_fs.h b/include/linux/sysv_fs.h index 96411306eec6..e47d6d90023d 100644 --- a/include/linux/sysv_fs.h +++ b/include/linux/sysv_fs.h @@ -148,6 +148,17 @@ struct v7_super_block { char s_fname[6]; /* file system name */ char s_fpack[6]; /* file system pack name */ }; +/* Constants to aid sanity checking */ +/* This is not a hard limit, nor enforced by v7 kernel. It's actually just + * the limit used by Seventh Edition's ls, though is high enough to assume + * that no reasonable file system would have that much entries in root + * directory. Thus, if we see anything higher, we just probably got the + * endiannes wrong. */ +#define V7_NFILES 1024 +/* The disk addresses are three-byte (despite direct block addresses being + * aligned word-wise in inode). If the most significant byte is non-zero, + * something is most likely wrong (not a filesystem, bad bytesex). */ +#define V7_MAXSIZE 0x00ffffff /* Coherent super-block data on disk */ #define COH_NICINOD 100 /* number of inode cache entries */ -- cgit v1.2.3 From 6d0b5456e14ec19edae7c18de4d355c58b133bd6 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Tue, 10 Aug 2010 18:03:34 -0700 Subject: fs/sysv/super.c: add support for non-PDP11 v7 filesystems This adds byte order autodetection (of PDP-11 and LE filesystems). No attempt is made to detect big-endian filesystems -- were there any? Tested with PDP-11 v7 filesystems and PC-IX maintenance floppy. [akpm@linux-foundation.org: coding-style fixes] [AV: parser.h inclusion was a rudiment of discarded stuff] Signed-off-by: Lubomir Rintel Cc: Christoph Hellwig Cc: Al Viro Signed-off-by: Andrew Morton Signed-off-by: Al Viro --- fs/sysv/super.c | 74 ++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 50 insertions(+), 24 deletions(-) (limited to 'fs') diff --git a/fs/sysv/super.c b/fs/sysv/super.c index 5c0aab0b7e18..a0b0cda6927e 100644 --- a/fs/sysv/super.c +++ b/fs/sysv/super.c @@ -434,12 +434,46 @@ Ebadsize: goto failed; } -static int v7_fill_super(struct super_block *sb, void *data, int silent) +static int v7_sanity_check(struct super_block *sb, struct buffer_head *bh) { - struct sysv_sb_info *sbi; - struct buffer_head *bh, *bh2 = NULL; struct v7_super_block *v7sb; struct sysv_inode *v7i; + struct buffer_head *bh2; + struct sysv_sb_info *sbi; + + sbi = sb->s_fs_info; + + /* plausibility check on superblock */ + v7sb = (struct v7_super_block *) bh->b_data; + if (fs16_to_cpu(sbi, v7sb->s_nfree) > V7_NICFREE || + fs16_to_cpu(sbi, v7sb->s_ninode) > V7_NICINOD || + fs32_to_cpu(sbi, v7sb->s_fsize) > V7_MAXSIZE) + return 0; + + /* plausibility check on root inode: it is a directory, + with a nonzero size that is a multiple of 16 */ + bh2 = sb_bread(sb, 2); + if (bh2 == NULL) + return 0; + + v7i = (struct sysv_inode *)(bh2->b_data + 64); + if ((fs16_to_cpu(sbi, v7i->i_mode) & ~0777) != S_IFDIR || + (fs32_to_cpu(sbi, v7i->i_size) == 0) || + (fs32_to_cpu(sbi, v7i->i_size) & 017) || + (fs32_to_cpu(sbi, v7i->i_size) > V7_NFILES * + sizeof(struct sysv_dir_entry))) { + brelse(bh2); + return 0; + } + + brelse(bh2); + return 1; +} + +static int v7_fill_super(struct super_block *sb, void *data, int silent) +{ + struct sysv_sb_info *sbi; + struct buffer_head *bh; if (440 != sizeof (struct v7_super_block)) panic("V7 FS: bad super-block size"); @@ -453,7 +487,6 @@ static int v7_fill_super(struct super_block *sb, void *data, int silent) sbi->s_sb = sb; sbi->s_block_base = 0; sbi->s_type = FSTYPE_V7; - sbi->s_bytesex = BYTESEX_PDP; sb->s_fs_info = sbi; sb_set_blocksize(sb, 512); @@ -465,34 +498,27 @@ static int v7_fill_super(struct super_block *sb, void *data, int silent) goto failed; } - /* plausibility check on superblock */ - v7sb = (struct v7_super_block *) bh->b_data; - if (fs16_to_cpu(sbi, v7sb->s_nfree) > V7_NICFREE || - fs16_to_cpu(sbi, v7sb->s_ninode) > V7_NICINOD || - fs32_to_cpu(sbi, v7sb->s_fsize) > V7_MAXSIZE) - goto failed; + /* Try PDP-11 UNIX */ + sbi->s_bytesex = BYTESEX_PDP; + if (v7_sanity_check(sb, bh)) + goto detected; - /* plausibility check on root inode: it is a directory, - with a nonzero size that is a multiple of 16 */ - if ((bh2 = sb_bread(sb, 2)) == NULL) - goto failed; - v7i = (struct sysv_inode *)(bh2->b_data + 64); - if ((fs16_to_cpu(sbi, v7i->i_mode) & ~0777) != S_IFDIR || - (fs32_to_cpu(sbi, v7i->i_size) == 0) || - (fs32_to_cpu(sbi, v7i->i_size) & 017) || - (fs32_to_cpu(sbi, v7i->i_size) > V7_NFILES * - sizeof (struct sysv_dir_entry))) - goto failed; - brelse(bh2); - bh2 = NULL; + /* Try PC/IX, v7/x86 */ + sbi->s_bytesex = BYTESEX_LE; + if (v7_sanity_check(sb, bh)) + goto detected; + goto failed; + +detected: sbi->s_bh1 = bh; sbi->s_bh2 = bh; if (complete_read_super(sb, silent, 1)) return 0; failed: - brelse(bh2); + printk(KERN_ERR "VFS: could not find a valid V7 on %s.\n", + sb->s_id); brelse(bh); kfree(sbi); return -EINVAL; -- cgit v1.2.3 From 542ce7a9bc6b3838832ae0f4f8de30c667af8ff3 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Tue, 10 Aug 2010 11:41:35 +0200 Subject: cachefiles: use path_get instead of lone dget Dentry references should not be acquired without a corresponding vfsmount ref. Signed-off-by: Miklos Szeredi Acked-by: David Howells Signed-off-by: Al Viro --- fs/cachefiles/daemon.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) (limited to 'fs') diff --git a/fs/cachefiles/daemon.c b/fs/cachefiles/daemon.c index 24eb0d37241a..72d4d0042826 100644 --- a/fs/cachefiles/daemon.c +++ b/fs/cachefiles/daemon.c @@ -553,7 +553,7 @@ static int cachefiles_daemon_tag(struct cachefiles_cache *cache, char *args) static int cachefiles_daemon_cull(struct cachefiles_cache *cache, char *args) { struct fs_struct *fs; - struct dentry *dir; + struct path path; const struct cred *saved_cred; int ret; @@ -575,22 +575,23 @@ static int cachefiles_daemon_cull(struct cachefiles_cache *cache, char *args) /* extract the directory dentry from the cwd */ fs = current->fs; read_lock(&fs->lock); - dir = dget(fs->pwd.dentry); + path = fs->pwd; + path_get(&path); read_unlock(&fs->lock); - if (!S_ISDIR(dir->d_inode->i_mode)) + if (!S_ISDIR(path.dentry->d_inode->i_mode)) goto notdir; cachefiles_begin_secure(cache, &saved_cred); - ret = cachefiles_cull(cache, dir, args); + ret = cachefiles_cull(cache, path.dentry, args); cachefiles_end_secure(cache, saved_cred); - dput(dir); + path_put(&path); _leave(" = %d", ret); return ret; notdir: - dput(dir); + path_put(&path); kerror("cull command requires dirfd to be a directory"); return -ENOTDIR; @@ -629,7 +630,7 @@ inval: static int cachefiles_daemon_inuse(struct cachefiles_cache *cache, char *args) { struct fs_struct *fs; - struct dentry *dir; + struct path path; const struct cred *saved_cred; int ret; @@ -651,22 +652,23 @@ static int cachefiles_daemon_inuse(struct cachefiles_cache *cache, char *args) /* extract the directory dentry from the cwd */ fs = current->fs; read_lock(&fs->lock); - dir = dget(fs->pwd.dentry); + path = fs->pwd; + path_get(&path); read_unlock(&fs->lock); - if (!S_ISDIR(dir->d_inode->i_mode)) + if (!S_ISDIR(path.dentry->d_inode->i_mode)) goto notdir; cachefiles_begin_secure(cache, &saved_cred); - ret = cachefiles_check_in_use(cache, dir, args); + ret = cachefiles_check_in_use(cache, path.dentry, args); cachefiles_end_secure(cache, saved_cred); - dput(dir); + path_put(&path); //_leave(" = %d", ret); return ret; notdir: - dput(dir); + path_put(&path); kerror("inuse command requires dirfd to be a directory"); return -ENOTDIR; -- cgit v1.2.3 From f7ad3c6be90809b53b7f0ae9d4eaa45ce2564a79 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Tue, 10 Aug 2010 11:41:36 +0200 Subject: vfs: add helpers to get root and pwd Add three helpers that retrieve a refcounted copy of the root and cwd from the supplied fs_struct. get_fs_root() get_fs_pwd() get_fs_root_and_pwd() Signed-off-by: Miklos Szeredi Signed-off-by: Al Viro --- fs/cachefiles/daemon.c | 14 ++------------ fs/dcache.c | 12 ++---------- fs/fs_struct.c | 7 +------ fs/namei.c | 15 +++------------ fs/namespace.c | 5 +---- fs/proc/base.c | 22 +++++++++++----------- include/linux/fs_struct.h | 27 +++++++++++++++++++++++++++ kernel/auditsc.c | 9 ++------- 8 files changed, 49 insertions(+), 62 deletions(-) (limited to 'fs') diff --git a/fs/cachefiles/daemon.c b/fs/cachefiles/daemon.c index 72d4d0042826..727caedcdd92 100644 --- a/fs/cachefiles/daemon.c +++ b/fs/cachefiles/daemon.c @@ -552,7 +552,6 @@ static int cachefiles_daemon_tag(struct cachefiles_cache *cache, char *args) */ static int cachefiles_daemon_cull(struct cachefiles_cache *cache, char *args) { - struct fs_struct *fs; struct path path; const struct cred *saved_cred; int ret; @@ -573,11 +572,7 @@ static int cachefiles_daemon_cull(struct cachefiles_cache *cache, char *args) } /* extract the directory dentry from the cwd */ - fs = current->fs; - read_lock(&fs->lock); - path = fs->pwd; - path_get(&path); - read_unlock(&fs->lock); + get_fs_pwd(current->fs, &path); if (!S_ISDIR(path.dentry->d_inode->i_mode)) goto notdir; @@ -629,7 +624,6 @@ inval: */ static int cachefiles_daemon_inuse(struct cachefiles_cache *cache, char *args) { - struct fs_struct *fs; struct path path; const struct cred *saved_cred; int ret; @@ -650,11 +644,7 @@ static int cachefiles_daemon_inuse(struct cachefiles_cache *cache, char *args) } /* extract the directory dentry from the cwd */ - fs = current->fs; - read_lock(&fs->lock); - path = fs->pwd; - path_get(&path); - read_unlock(&fs->lock); + get_fs_pwd(current->fs, &path); if (!S_ISDIR(path.dentry->d_inode->i_mode)) goto notdir; diff --git a/fs/dcache.c b/fs/dcache.c index 9f2c13417969..995d08069d26 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -2014,10 +2014,7 @@ char *d_path(const struct path *path, char *buf, int buflen) if (path->dentry->d_op && path->dentry->d_op->d_dname) return path->dentry->d_op->d_dname(path->dentry, buf, buflen); - read_lock(¤t->fs->lock); - root = current->fs->root; - path_get(&root); - read_unlock(¤t->fs->lock); + get_fs_root(current->fs, &root); spin_lock(&dcache_lock); tmp = root; res = __d_path(path, &tmp, buf, buflen); @@ -2129,12 +2126,7 @@ SYSCALL_DEFINE2(getcwd, char __user *, buf, unsigned long, size) if (!page) return -ENOMEM; - read_lock(¤t->fs->lock); - pwd = current->fs->pwd; - path_get(&pwd); - root = current->fs->root; - path_get(&root); - read_unlock(¤t->fs->lock); + get_fs_root_and_pwd(current->fs, &root, &pwd); error = -ENOENT; spin_lock(&dcache_lock); diff --git a/fs/fs_struct.c b/fs/fs_struct.c index eee059052db5..1ee40eb9a2c0 100644 --- a/fs/fs_struct.c +++ b/fs/fs_struct.c @@ -106,12 +106,7 @@ struct fs_struct *copy_fs_struct(struct fs_struct *old) fs->in_exec = 0; rwlock_init(&fs->lock); fs->umask = old->umask; - read_lock(&old->lock); - fs->root = old->root; - path_get(&old->root); - fs->pwd = old->pwd; - path_get(&old->pwd); - read_unlock(&old->lock); + get_fs_root_and_pwd(old, &fs->root, &fs->pwd); } return fs; } diff --git a/fs/namei.c b/fs/namei.c index 13ff4abdbdca..17ea76bf2fbe 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -483,13 +483,8 @@ ok: static __always_inline void set_root(struct nameidata *nd) { - if (!nd->root.mnt) { - struct fs_struct *fs = current->fs; - read_lock(&fs->lock); - nd->root = fs->root; - path_get(&nd->root); - read_unlock(&fs->lock); - } + if (!nd->root.mnt) + get_fs_root(current->fs, &nd->root); } static int link_path_walk(const char *, struct nameidata *); @@ -1015,11 +1010,7 @@ static int path_init(int dfd, const char *name, unsigned int flags, struct namei nd->path = nd->root; path_get(&nd->root); } else if (dfd == AT_FDCWD) { - struct fs_struct *fs = current->fs; - read_lock(&fs->lock); - nd->path = fs->pwd; - path_get(&fs->pwd); - read_unlock(&fs->lock); + get_fs_pwd(current->fs, &nd->path); } else { struct dentry *dentry; diff --git a/fs/namespace.c b/fs/namespace.c index 66c4f7e781cb..7972e51ccc06 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -2213,10 +2213,7 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root, goto out1; } - read_lock(¤t->fs->lock); - root = current->fs->root; - path_get(¤t->fs->root); - read_unlock(¤t->fs->lock); + get_fs_root(current->fs, &root); down_write(&namespace_sem); mutex_lock(&old.dentry->d_inode->i_mutex); error = -EINVAL; diff --git a/fs/proc/base.c b/fs/proc/base.c index c806dfb24e08..4cf5abf77ddf 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -149,18 +149,13 @@ static unsigned int pid_entry_count_dirs(const struct pid_entry *entries, return count; } -static int get_fs_path(struct task_struct *task, struct path *path, bool root) +static int get_task_root(struct task_struct *task, struct path *root) { - struct fs_struct *fs; int result = -ENOENT; task_lock(task); - fs = task->fs; - if (fs) { - read_lock(&fs->lock); - *path = root ? fs->root : fs->pwd; - path_get(path); - read_unlock(&fs->lock); + if (task->fs) { + get_fs_root(task->fs, root); result = 0; } task_unlock(task); @@ -173,7 +168,12 @@ static int proc_cwd_link(struct inode *inode, struct path *path) int result = -ENOENT; if (task) { - result = get_fs_path(task, path, 0); + task_lock(task); + if (task->fs) { + get_fs_pwd(task->fs, path); + result = 0; + } + task_unlock(task); put_task_struct(task); } return result; @@ -185,7 +185,7 @@ static int proc_root_link(struct inode *inode, struct path *path) int result = -ENOENT; if (task) { - result = get_fs_path(task, path, 1); + result = get_task_root(task, path); put_task_struct(task); } return result; @@ -597,7 +597,7 @@ static int mounts_open_common(struct inode *inode, struct file *file, get_mnt_ns(ns); } rcu_read_unlock(); - if (ns && get_fs_path(task, &root, 1) == 0) + if (ns && get_task_root(task, &root) == 0) ret = 0; put_task_struct(task); } diff --git a/include/linux/fs_struct.h b/include/linux/fs_struct.h index 78a05bfcd8eb..eca3d5202138 100644 --- a/include/linux/fs_struct.h +++ b/include/linux/fs_struct.h @@ -21,4 +21,31 @@ extern void free_fs_struct(struct fs_struct *); extern void daemonize_fs_struct(void); extern int unshare_fs_struct(void); +static inline void get_fs_root(struct fs_struct *fs, struct path *root) +{ + read_lock(&fs->lock); + *root = fs->root; + path_get(root); + read_unlock(&fs->lock); +} + +static inline void get_fs_pwd(struct fs_struct *fs, struct path *pwd) +{ + read_lock(&fs->lock); + *pwd = fs->pwd; + path_get(pwd); + read_unlock(&fs->lock); +} + +static inline void get_fs_root_and_pwd(struct fs_struct *fs, struct path *root, + struct path *pwd) +{ + read_lock(&fs->lock); + *root = fs->root; + path_get(root); + *pwd = fs->pwd; + path_get(pwd); + read_unlock(&fs->lock); +} + #endif /* _LINUX_FS_STRUCT_H */ diff --git a/kernel/auditsc.c b/kernel/auditsc.c index b87a63beb66c..1b31c130d034 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -1835,13 +1835,8 @@ void __audit_getname(const char *name) context->names[context->name_count].ino = (unsigned long)-1; context->names[context->name_count].osid = 0; ++context->name_count; - if (!context->pwd.dentry) { - read_lock(¤t->fs->lock); - context->pwd = current->fs->pwd; - path_get(¤t->fs->pwd); - read_unlock(¤t->fs->lock); - } - + if (!context->pwd.dentry) + get_fs_pwd(current->fs, &context->pwd); } /* audit_putname - intercept a putname request -- cgit v1.2.3 From 98dc568bc2ebefe4c0cb315a7fb7eff8bbb43176 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Tue, 10 Aug 2010 11:41:38 +0200 Subject: vfs: __d_path: dont prepend the name of the root dentry In the old times pseudo-filesystems set the name of theroot dentry to some prefix like "pipe:" and the name of the child dentry to "[123]" and relied on a hack in __d_path() to replace the preceding slash with the root's name to get "pipe:[123]". Then the d_dname() dentry operation was introduced which solved the same problem without having to pre-fill the name in each dentry. Currently the following pseudo filesystems exist in the kernel: perfmon mtd anon_inode bdev pipe socket Of these only perfmon, anon_inode, pipe and socket create sub-dentries, all of which have now been switched to using d_dname(). bdev and mtd only create inodes. This means that now the hack to overwrite the slash can be removed, so for unreachable paths (e.g. within a detached mount) the path string won't be polluted with garbage. For these cases a subsequent patch will add a prefix, indicating that the path is unreachable. Signed-off-by: Miklos Szeredi Signed-off-by: Al Viro --- fs/dcache.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/dcache.c b/fs/dcache.c index 995d08069d26..f1809e6b9fda 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -1970,9 +1970,15 @@ out: return retval; global_root: - retval += 1; /* hit the slash */ - if (prepend_name(&retval, &buflen, &dentry->d_name) != 0) - goto Elong; + /* + * Filesystems needing to implement special "root names" + * should do so with ->d_dname() + */ + if (IS_ROOT(dentry) && + (dentry->d_name.len != 1 || dentry->d_name.name[0] != '/')) { + WARN(1, "Root dentry has weird name <%.*s>\n", + (int) dentry->d_name.len, dentry->d_name.name); + } root->mnt = vfsmnt; root->dentry = dentry; goto out; -- cgit v1.2.3 From f2eb6575d5beba1e98d400463007d77555d1fc35 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Tue, 10 Aug 2010 11:41:39 +0200 Subject: vfs: add prepend_path() helper Split off prepend_path() from __d_path(). This new helper takes an end-of-buffer pointer and buffer-length pointer just like the other prepend_* functions. Move the " (deleted)" postfix out to __d_path(). This patch doesn't change any functionality but paves the way for the following patches. Signed-off-by: Miklos Szeredi Signed-off-by: Al Viro --- fs/dcache.c | 94 ++++++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 58 insertions(+), 36 deletions(-) (limited to 'fs') diff --git a/fs/dcache.c b/fs/dcache.c index f1809e6b9fda..d09d93819b4d 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -1905,48 +1905,30 @@ static int prepend_name(char **buffer, int *buflen, struct qstr *name) } /** - * __d_path - return the path of a dentry + * Prepend path string to a buffer + * * @path: the dentry/vfsmount to report * @root: root vfsmnt/dentry (may be modified by this function) - * @buffer: buffer to return value in - * @buflen: buffer length - * - * Convert a dentry into an ASCII path name. If the entry has been deleted - * the string " (deleted)" is appended. Note that this is ambiguous. - * - * Returns a pointer into the buffer or an error code if the - * path was too long. + * @buffer: pointer to the end of the buffer + * @buflen: pointer to buffer length * - * "buflen" should be positive. Caller holds the dcache_lock. + * Caller holds the dcache_lock. * * If path is not reachable from the supplied root, then the value of * root is changed (without modifying refcounts). */ -char *__d_path(const struct path *path, struct path *root, - char *buffer, int buflen) +static int prepend_path(const struct path *path, struct path *root, + char **buffer, int *buflen) { struct dentry *dentry = path->dentry; struct vfsmount *vfsmnt = path->mnt; - char *end = buffer + buflen; - char *retval; + bool slash = false; + int error = 0; spin_lock(&vfsmount_lock); - prepend(&end, &buflen, "\0", 1); - if (d_unlinked(dentry) && - (prepend(&end, &buflen, " (deleted)", 10) != 0)) - goto Elong; - - if (buflen < 1) - goto Elong; - /* Get '/' right */ - retval = end-1; - *retval = '/'; - - for (;;) { + while (dentry != root->dentry || vfsmnt != root->mnt) { struct dentry * parent; - if (dentry == root->dentry && vfsmnt == root->mnt) - break; if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) { /* Global root? */ if (vfsmnt->mnt_parent == vfsmnt) { @@ -1958,16 +1940,22 @@ char *__d_path(const struct path *path, struct path *root, } parent = dentry->d_parent; prefetch(parent); - if ((prepend_name(&end, &buflen, &dentry->d_name) != 0) || - (prepend(&end, &buflen, "/", 1) != 0)) - goto Elong; - retval = end; + error = prepend_name(buffer, buflen, &dentry->d_name); + if (!error) + error = prepend(buffer, buflen, "/", 1); + if (error) + break; + + slash = true; dentry = parent; } out: + if (!error && !slash) + error = prepend(buffer, buflen, "/", 1); + spin_unlock(&vfsmount_lock); - return retval; + return error; global_root: /* @@ -1982,10 +1970,44 @@ global_root: root->mnt = vfsmnt; root->dentry = dentry; goto out; +} -Elong: - retval = ERR_PTR(-ENAMETOOLONG); - goto out; +/** + * __d_path - return the path of a dentry + * @path: the dentry/vfsmount to report + * @root: root vfsmnt/dentry (may be modified by this function) + * @buffer: buffer to return value in + * @buflen: buffer length + * + * Convert a dentry into an ASCII path name. If the entry has been deleted + * the string " (deleted)" is appended. Note that this is ambiguous. + * + * Returns a pointer into the buffer or an error code if the + * path was too long. + * + * "buflen" should be positive. Caller holds the dcache_lock. + * + * If path is not reachable from the supplied root, then the value of + * root is changed (without modifying refcounts). + */ +char *__d_path(const struct path *path, struct path *root, + char *buf, int buflen) +{ + char *res = buf + buflen; + int error; + + prepend(&res, &buflen, "\0", 1); + if (d_unlinked(path->dentry)) { + error = prepend(&res, &buflen, " (deleted)", 10); + if (error) + return ERR_PTR(error); + } + + error = prepend_path(path, root, &res, &buflen); + if (error) + return ERR_PTR(error); + + return res; } /** -- cgit v1.2.3 From ffd1f4ed5bddccf2277e3d8525bcedf1983319f8 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Tue, 10 Aug 2010 11:41:40 +0200 Subject: vfs: only add " (deleted)" where necessary __d_path() has 4 callers: d_path() sys_getcwd() seq_path_root() tomoyo_realpath_from_path2() Of these the only one which needs the " (deleted)" ending is d_path(). sys_getcwd() checks for existence before calling __d_path(). seq_path_root() is used to show the mountpoint path in /proc/PID/mountinfo, which is always a positive. And tomoyo doesn't want the deleted ending. Create a helper "path_with_deleted()" as subsequent patches will need this in multiple places. Signed-off-by: Miklos Szeredi Signed-off-by: Al Viro --- fs/dcache.c | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) (limited to 'fs') diff --git a/fs/dcache.c b/fs/dcache.c index d09d93819b4d..7fccb00f498d 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -1979,8 +1979,7 @@ global_root: * @buffer: buffer to return value in * @buflen: buffer length * - * Convert a dentry into an ASCII path name. If the entry has been deleted - * the string " (deleted)" is appended. Note that this is ambiguous. + * Convert a dentry into an ASCII path name. * * Returns a pointer into the buffer or an error code if the * path was too long. @@ -1997,12 +1996,6 @@ char *__d_path(const struct path *path, struct path *root, int error; prepend(&res, &buflen, "\0", 1); - if (d_unlinked(path->dentry)) { - error = prepend(&res, &buflen, " (deleted)", 10); - if (error) - return ERR_PTR(error); - } - error = prepend_path(path, root, &res, &buflen); if (error) return ERR_PTR(error); @@ -2010,6 +2003,22 @@ char *__d_path(const struct path *path, struct path *root, return res; } +/* + * same as __d_path but appends "(deleted)" for unlinked files. + */ +static int path_with_deleted(const struct path *path, struct path *root, + char **buf, int *buflen) +{ + prepend(buf, buflen, "\0", 1); + if (d_unlinked(path->dentry)) { + int error = prepend(buf, buflen, " (deleted)", 10); + if (error) + return error; + } + + return prepend_path(path, root, buf, buflen); +} + /** * d_path - return the path of a dentry * @path: path to report @@ -2028,9 +2037,10 @@ char *__d_path(const struct path *path, struct path *root, */ char *d_path(const struct path *path, char *buf, int buflen) { - char *res; + char *res = buf + buflen; struct path root; struct path tmp; + int error; /* * We have various synthetic filesystems that never get mounted. On @@ -2045,7 +2055,9 @@ char *d_path(const struct path *path, char *buf, int buflen) get_fs_root(current->fs, &root); spin_lock(&dcache_lock); tmp = root; - res = __d_path(path, &tmp, buf, buflen); + error = path_with_deleted(path, &tmp, &res, &buflen); + if (error) + res = ERR_PTR(error); spin_unlock(&dcache_lock); path_put(&root); return res; -- cgit v1.2.3 From 8df9d1a4142311c084ffeeacb67cd34d190eff74 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Tue, 10 Aug 2010 11:41:41 +0200 Subject: vfs: show unreachable paths in getcwd and proc Prepend "(unreachable)" to path strings if the path is not reachable from the current root. Two places updated are - the return string from getcwd() - and symlinks under /proc/$PID. Other uses of d_path() are left unchanged (we know that some old software crashes if /proc/mounts is changed). Signed-off-by: Miklos Szeredi Signed-off-by: Al Viro --- fs/dcache.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++---- fs/proc/base.c | 2 +- include/linux/dcache.h | 1 + include/linux/path.h | 5 +++++ 4 files changed, 57 insertions(+), 5 deletions(-) (limited to 'fs') diff --git a/fs/dcache.c b/fs/dcache.c index 7fccb00f498d..166d35d56868 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -2019,6 +2019,11 @@ static int path_with_deleted(const struct path *path, struct path *root, return prepend_path(path, root, buf, buflen); } +static int prepend_unreachable(char **buffer, int *buflen) +{ + return prepend(buffer, buflen, "(unreachable)", 13); +} + /** * d_path - return the path of a dentry * @path: path to report @@ -2064,6 +2069,39 @@ char *d_path(const struct path *path, char *buf, int buflen) } EXPORT_SYMBOL(d_path); +/** + * d_path_with_unreachable - return the path of a dentry + * @path: path to report + * @buf: buffer to return value in + * @buflen: buffer length + * + * The difference from d_path() is that this prepends "(unreachable)" + * to paths which are unreachable from the current process' root. + */ +char *d_path_with_unreachable(const struct path *path, char *buf, int buflen) +{ + char *res = buf + buflen; + struct path root; + struct path tmp; + int error; + + if (path->dentry->d_op && path->dentry->d_op->d_dname) + return path->dentry->d_op->d_dname(path->dentry, buf, buflen); + + get_fs_root(current->fs, &root); + spin_lock(&dcache_lock); + tmp = root; + error = path_with_deleted(path, &tmp, &res, &buflen); + if (!error && !path_equal(&tmp, &root)) + error = prepend_unreachable(&res, &buflen); + spin_unlock(&dcache_lock); + path_put(&root); + if (error) + res = ERR_PTR(error); + + return res; +} + /* * Helper function for dentry_operations.d_dname() members */ @@ -2173,15 +2211,23 @@ SYSCALL_DEFINE2(getcwd, char __user *, buf, unsigned long, size) if (!d_unlinked(pwd.dentry)) { unsigned long len; struct path tmp = root; - char * cwd; + char *cwd = page + PAGE_SIZE; + int buflen = PAGE_SIZE; - cwd = __d_path(&pwd, &tmp, page, PAGE_SIZE); + prepend(&cwd, &buflen, "\0", 1); + error = prepend_path(&pwd, &tmp, &cwd, &buflen); spin_unlock(&dcache_lock); - error = PTR_ERR(cwd); - if (IS_ERR(cwd)) + if (error) goto out; + /* Unreachable from current root */ + if (!path_equal(&tmp, &root)) { + error = prepend_unreachable(&cwd, &buflen); + if (error) + goto out; + } + error = -ERANGE; len = PAGE_SIZE + page - cwd; if (len <= size) { diff --git a/fs/proc/base.c b/fs/proc/base.c index 4cf5abf77ddf..a1c43e7c8a7b 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -1526,7 +1526,7 @@ static int do_proc_readlink(struct path *path, char __user *buffer, int buflen) if (!tmp) return -ENOMEM; - pathname = d_path(path, tmp, PAGE_SIZE); + pathname = d_path_with_unreachable(path, tmp, PAGE_SIZE); len = PTR_ERR(pathname); if (IS_ERR(pathname)) goto out; diff --git a/include/linux/dcache.h b/include/linux/dcache.h index d23be0386e2d..6a4aea30aa09 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -315,6 +315,7 @@ extern char *dynamic_dname(struct dentry *, char *, int, const char *, ...); extern char *__d_path(const struct path *path, struct path *root, char *, int); extern char *d_path(const struct path *, char *, int); +extern char *d_path_with_unreachable(const struct path *, char *, int); extern char *__dentry_path(struct dentry *, char *, int); extern char *dentry_path(struct dentry *, char *, int); diff --git a/include/linux/path.h b/include/linux/path.h index 915e0c382a51..edc98dec6266 100644 --- a/include/linux/path.h +++ b/include/linux/path.h @@ -12,4 +12,9 @@ struct path { extern void path_get(struct path *); extern void path_put(struct path *); +static inline int path_equal(const struct path *path1, const struct path *path2) +{ + return path1->mnt == path2->mnt && path1->dentry == path2->dentry; +} + #endif /* _LINUX_PATH_H */ -- cgit v1.2.3 From 532490f0a5350fd92d838b7430a4c846bc8eac3f Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Mon, 2 Aug 2010 13:46:56 +0200 Subject: vfs: remove unused MNT_STRICTATIME Commit d0adde574b8487ef30f69e2d08bba769e4be513f added MNT_STRICTATIME but it isn't actually used (MS_STRICTATIME clears MNT_RELATIME and MNT_NOATIME rather than setting any mount flag). Signed-off-by: Miklos Szeredi Signed-off-by: Al Viro --- fs/namespace.c | 1 - include/linux/mount.h | 1 - 2 files changed, 2 deletions(-) (limited to 'fs') diff --git a/fs/namespace.c b/fs/namespace.c index 7972e51ccc06..2e10cb19c5b0 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -788,7 +788,6 @@ static void show_mnt_opts(struct seq_file *m, struct vfsmount *mnt) { MNT_NOATIME, ",noatime" }, { MNT_NODIRATIME, ",nodiratime" }, { MNT_RELATIME, ",relatime" }, - { MNT_STRICTATIME, ",strictatime" }, { 0, NULL } }; const struct proc_fs_info *fs_infop; diff --git a/include/linux/mount.h b/include/linux/mount.h index 907210bd9f9c..5e7a59408dd4 100644 --- a/include/linux/mount.h +++ b/include/linux/mount.h @@ -27,7 +27,6 @@ struct mnt_namespace; #define MNT_NODIRATIME 0x10 #define MNT_RELATIME 0x20 #define MNT_READONLY 0x40 /* does the user want this to be r/o? */ -#define MNT_STRICTATIME 0x80 #define MNT_SHRINKABLE 0x100 #define MNT_WRITE_HOLD 0x200 -- cgit v1.2.3 From 66a362a2aa8ffa72670259fa15e2a77a01cc2217 Mon Sep 17 00:00:00 2001 From: Jan Andres Date: Wed, 4 Aug 2010 22:52:46 +0200 Subject: isofs: Fix lseek() to position beyond 4 GB isofs supports files larger than 4 GB by using multi-extent files. However an lseek() to a position beyond 4 GB in such a file will fail with EINVAL, because s_maxbytes in the isofs superblock is initialized to 2^32-1, and generic_file_llseek() checks against that value. I therefore suggest increasing the value of s_maxbytes to have full support for large files in isofs. With multi-extent files, file size is only limited by the maximum size of the file system (8 TB), so this seems a reasonable value for s_maxbytes. Signed-off-by: Jan Andres Signed-off-by: Al Viro --- fs/isofs/inode.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c index 6b4dcd4f2943..5a44811b5027 100644 --- a/fs/isofs/inode.c +++ b/fs/isofs/inode.c @@ -722,7 +722,12 @@ root_found: } s->s_magic = ISOFS_SUPER_MAGIC; - s->s_maxbytes = 0xffffffff; /* We can handle files up to 4 GB */ + + /* + * With multi-extent files, file size is only limited by the maximum + * size of a file system, which is 8 TB. + */ + s->s_maxbytes = 0x80000000000LL; /* * The CDROM is read-only, has no nodes (devices) on it, and since -- cgit v1.2.3 From 9bbb9e5a33109b2832e2e63dcc7a132924ab374b Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 11 Aug 2010 23:04:12 -0600 Subject: param: use ops in struct kernel_param, rather than get and set fns directly This is more kernel-ish, saves some space, and also allows us to expand the ops without breaking all the callers who are happy for the new members to be NULL. The few places which defined their own param types are changed to the new scheme (more which crept in recently fixed in following patches). Since we're touching them anyway, we change get() and set() to take a const struct kernel_param (which they really are). This causes some harmless warnings until we fix them (in following patches). To reduce churn, module_param_call creates the ops struct so the callers don't have to change (and casts the functions to reduce warnings). The modern version which takes an ops struct is called module_param_cb. Signed-off-by: Rusty Russell Reviewed-by: Takashi Iwai Tested-by: Phil Carmody Cc: "David S. Miller" Cc: Ville Syrjala Cc: Dmitry Torokhov Cc: Alessandro Rubini Cc: Michal Januszewski Cc: Trond Myklebust Cc: "J. Bruce Fields" Cc: Neil Brown Cc: linux-kernel@vger.kernel.org Cc: linux-input@vger.kernel.org Cc: linux-fbdev-devel@lists.sourceforge.net Cc: linux-nfs@vger.kernel.org Cc: netdev@vger.kernel.org --- drivers/input/misc/ati_remote2.c | 26 +++++--- drivers/input/mouse/psmouse-base.c | 14 +++-- drivers/video/uvesafb.c | 7 ++- fs/nfs/callback.c | 11 ++-- include/linux/moduleparam.h | 123 ++++++++++++++++++++++--------------- kernel/params.c | 90 ++++++++++++++++++--------- net/sunrpc/xprtsock.c | 26 ++++---- 7 files changed, 186 insertions(+), 111 deletions(-) (limited to 'fs') diff --git a/drivers/input/misc/ati_remote2.c b/drivers/input/misc/ati_remote2.c index e148749b5851..23257652b8e8 100644 --- a/drivers/input/misc/ati_remote2.c +++ b/drivers/input/misc/ati_remote2.c @@ -38,7 +38,8 @@ enum { }; static int ati_remote2_set_mask(const char *val, - struct kernel_param *kp, unsigned int max) + const struct kernel_param *kp, + unsigned int max) { unsigned long mask; int ret; @@ -59,28 +60,31 @@ static int ati_remote2_set_mask(const char *val, } static int ati_remote2_set_channel_mask(const char *val, - struct kernel_param *kp) + const struct kernel_param *kp) { pr_debug("%s()\n", __func__); return ati_remote2_set_mask(val, kp, ATI_REMOTE2_MAX_CHANNEL_MASK); } -static int ati_remote2_get_channel_mask(char *buffer, struct kernel_param *kp) +static int ati_remote2_get_channel_mask(char *buffer, + const struct kernel_param *kp) { pr_debug("%s()\n", __func__); return sprintf(buffer, "0x%04x", *(unsigned int *)kp->arg); } -static int ati_remote2_set_mode_mask(const char *val, struct kernel_param *kp) +static int ati_remote2_set_mode_mask(const char *val, + const struct kernel_param *kp) { pr_debug("%s()\n", __func__); return ati_remote2_set_mask(val, kp, ATI_REMOTE2_MAX_MODE_MASK); } -static int ati_remote2_get_mode_mask(char *buffer, struct kernel_param *kp) +static int ati_remote2_get_mode_mask(char *buffer, + const struct kernel_param *kp) { pr_debug("%s()\n", __func__); @@ -89,15 +93,19 @@ static int ati_remote2_get_mode_mask(char *buffer, struct kernel_param *kp) static unsigned int channel_mask = ATI_REMOTE2_MAX_CHANNEL_MASK; #define param_check_channel_mask(name, p) __param_check(name, p, unsigned int) -#define param_set_channel_mask ati_remote2_set_channel_mask -#define param_get_channel_mask ati_remote2_get_channel_mask +static struct kernel_param_ops param_ops_channel_mask = { + .set = ati_remote2_set_channel_mask, + .get = ati_remote2_get_channel_mask, +}; module_param(channel_mask, channel_mask, 0644); MODULE_PARM_DESC(channel_mask, "Bitmask of channels to accept <15:Channel16>...<1:Channel2><0:Channel1>"); static unsigned int mode_mask = ATI_REMOTE2_MAX_MODE_MASK; #define param_check_mode_mask(name, p) __param_check(name, p, unsigned int) -#define param_set_mode_mask ati_remote2_set_mode_mask -#define param_get_mode_mask ati_remote2_get_mode_mask +static struct kernel_param_ops param_ops_mode_mask = { + .set = ati_remote2_set_mode_mask, + .get = ati_remote2_get_mode_mask, +}; module_param(mode_mask, mode_mask, 0644); MODULE_PARM_DESC(mode_mask, "Bitmask of modes to accept <4:PC><3:AUX4><2:AUX3><1:AUX2><0:AUX1>"); diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index 979c50215282..73a7af2542a8 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -39,11 +39,13 @@ MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); static unsigned int psmouse_max_proto = PSMOUSE_AUTO; -static int psmouse_set_maxproto(const char *val, struct kernel_param *kp); -static int psmouse_get_maxproto(char *buffer, struct kernel_param *kp); +static int psmouse_set_maxproto(const char *val, const struct kernel_param *); +static int psmouse_get_maxproto(char *buffer, const struct kernel_param *kp); +static struct kernel_param_ops param_ops_proto_abbrev = { + .set = psmouse_set_maxproto, + .get = psmouse_get_maxproto, +}; #define param_check_proto_abbrev(name, p) __param_check(name, p, unsigned int) -#define param_set_proto_abbrev psmouse_set_maxproto -#define param_get_proto_abbrev psmouse_get_maxproto module_param_named(proto, psmouse_max_proto, proto_abbrev, 0644); MODULE_PARM_DESC(proto, "Highest protocol extension to probe (bare, imps, exps, any). Useful for KVM switches."); @@ -1679,7 +1681,7 @@ static ssize_t psmouse_attr_set_resolution(struct psmouse *psmouse, void *data, } -static int psmouse_set_maxproto(const char *val, struct kernel_param *kp) +static int psmouse_set_maxproto(const char *val, const struct kernel_param *kp) { const struct psmouse_protocol *proto; @@ -1696,7 +1698,7 @@ static int psmouse_set_maxproto(const char *val, struct kernel_param *kp) return 0; } -static int psmouse_get_maxproto(char *buffer, struct kernel_param *kp) +static int psmouse_get_maxproto(char *buffer, const struct kernel_param *kp) { int type = *((unsigned int *)kp->arg); diff --git a/drivers/video/uvesafb.c b/drivers/video/uvesafb.c index 7b8839ebf3c4..52ec0959d462 100644 --- a/drivers/video/uvesafb.c +++ b/drivers/video/uvesafb.c @@ -1977,8 +1977,7 @@ static void __devexit uvesafb_exit(void) module_exit(uvesafb_exit); -#define param_get_scroll NULL -static int param_set_scroll(const char *val, struct kernel_param *kp) +static int param_set_scroll(const char *val, const struct kernel_param *kp) { ypan = 0; @@ -1993,7 +1992,9 @@ static int param_set_scroll(const char *val, struct kernel_param *kp) return 0; } - +static struct kernel_param_ops param_ops_scroll = { + .set = param_set_scroll, +}; #define param_check_scroll(name, p) __param_check(name, p, void) module_param_named(scroll, ypan, scroll, 0); diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c index 36dfdae95123..e17b49e2eabd 100644 --- a/fs/nfs/callback.c +++ b/fs/nfs/callback.c @@ -45,7 +45,7 @@ unsigned short nfs_callback_tcpport; unsigned short nfs_callback_tcpport6; #define NFS_CALLBACK_MAXPORTNR (65535U) -static int param_set_portnr(const char *val, struct kernel_param *kp) +static int param_set_portnr(const char *val, const struct kernel_param *kp) { unsigned long num; int ret; @@ -58,11 +58,10 @@ static int param_set_portnr(const char *val, struct kernel_param *kp) *((unsigned int *)kp->arg) = num; return 0; } - -static int param_get_portnr(char *buffer, struct kernel_param *kp) -{ - return param_get_uint(buffer, kp); -} +static struct kernel_param_ops param_ops_portnr = { + .set = param_set_portnr, + .get = param_get_uint, +}; #define param_check_portnr(name, p) __param_check(name, p, unsigned int); module_param_named(callback_tcpport, nfs_callback_set_tcpport, portnr, 0644); diff --git a/include/linux/moduleparam.h b/include/linux/moduleparam.h index 82a9124f7d75..02e5090ce32f 100644 --- a/include/linux/moduleparam.h +++ b/include/linux/moduleparam.h @@ -31,20 +31,21 @@ static const char __module_cat(name,__LINE__)[] \ struct kernel_param; -/* Returns 0, or -errno. arg is in kp->arg. */ -typedef int (*param_set_fn)(const char *val, struct kernel_param *kp); -/* Returns length written or -errno. Buffer is 4k (ie. be short!) */ -typedef int (*param_get_fn)(char *buffer, struct kernel_param *kp); +struct kernel_param_ops { + /* Returns 0, or -errno. arg is in kp->arg. */ + int (*set)(const char *val, const struct kernel_param *kp); + /* Returns length written or -errno. Buffer is 4k (ie. be short!) */ + int (*get)(char *buffer, const struct kernel_param *kp); +}; /* Flag bits for kernel_param.flags */ #define KPARAM_ISBOOL 2 struct kernel_param { const char *name; + const struct kernel_param_ops *ops; u16 perm; u16 flags; - param_set_fn set; - param_get_fn get; union { void *arg; const struct kparam_string *str; @@ -63,8 +64,7 @@ struct kparam_array { unsigned int max; unsigned int *num; - param_set_fn set; - param_get_fn get; + const struct kernel_param_ops *ops; unsigned int elemsize; void *elem; }; @@ -83,7 +83,7 @@ struct kparam_array parameters. perm sets the visibility in sysfs: 000 means it's not there, read bits mean it's readable, write bits mean it's writable. */ -#define __module_param_call(prefix, name, set, get, arg, isbool, perm) \ +#define __module_param_call(prefix, name, ops, arg, isbool, perm) \ /* Default value instead of permissions? */ \ static int __param_perm_check_##name __attribute__((unused)) = \ BUILD_BUG_ON_ZERO((perm) < 0 || (perm) > 0777 || ((perm) & 2)) \ @@ -92,20 +92,37 @@ struct kparam_array static struct kernel_param __moduleparam_const __param_##name \ __used \ __attribute__ ((unused,__section__ ("__param"),aligned(sizeof(void *)))) \ - = { __param_str_##name, perm, isbool ? KPARAM_ISBOOL : 0, \ - set, get, { arg } } + = { __param_str_##name, ops, perm, isbool ? KPARAM_ISBOOL : 0, \ + { arg } } + +/* Obsolete - use module_param_cb() */ +#define module_param_call(name, set, get, arg, perm) \ + static struct kernel_param_ops __param_ops_##name = \ + { (void *)set, (void *)get }; \ + __module_param_call(MODULE_PARAM_PREFIX, \ + name, &__param_ops_##name, arg, \ + __same_type(*(arg), bool), \ + (perm) + sizeof(__check_old_set_param(set))*0) + +/* We don't get oldget: it's often a new-style param_get_uint, etc. */ +static inline int +__check_old_set_param(int (*oldset)(const char *, struct kernel_param *)) +{ + return 0; +} -#define module_param_call(name, set, get, arg, perm) \ +#define module_param_cb(name, ops, arg, perm) \ __module_param_call(MODULE_PARAM_PREFIX, \ - name, set, get, arg, \ - __same_type(*(arg), bool), perm) + name, ops, arg, __same_type(*(arg), bool), perm) -/* Helper functions: type is byte, short, ushort, int, uint, long, - ulong, charp, bool or invbool, or XXX if you define param_get_XXX, - param_set_XXX and param_check_XXX. */ +/* + * Helper functions: type is byte, short, ushort, int, uint, long, + * ulong, charp, bool or invbool, or XXX if you define param_ops_XXX + * and param_check_XXX. + */ #define module_param_named(name, value, type, perm) \ param_check_##type(name, &(value)); \ - module_param_call(name, param_set_##type, param_get_##type, &value, perm); \ + module_param_cb(name, ¶m_ops_##type, &value, perm); \ __MODULE_PARM_TYPE(name, #type) #define module_param(name, type, perm) \ @@ -126,7 +143,7 @@ struct kparam_array */ #define core_param(name, var, type, perm) \ param_check_##type(name, &(var)); \ - __module_param_call("", name, param_set_##type, param_get_##type, \ + __module_param_call("", name, ¶m_ops_##type, \ &var, __same_type(var, bool), perm) #endif /* !MODULE */ @@ -135,7 +152,7 @@ struct kparam_array static const struct kparam_string __param_string_##name \ = { len, string }; \ __module_param_call(MODULE_PARAM_PREFIX, name, \ - param_set_copystring, param_get_string, \ + ¶m_ops_string, \ .str = &__param_string_##name, 0, perm); \ __MODULE_PARM_TYPE(name, "string") @@ -162,41 +179,50 @@ static inline void destroy_params(const struct kernel_param *params, #define __param_check(name, p, type) \ static inline type *__check_##name(void) { return(p); } -extern int param_set_byte(const char *val, struct kernel_param *kp); -extern int param_get_byte(char *buffer, struct kernel_param *kp); +extern struct kernel_param_ops param_ops_byte; +extern int param_set_byte(const char *val, const struct kernel_param *kp); +extern int param_get_byte(char *buffer, const struct kernel_param *kp); #define param_check_byte(name, p) __param_check(name, p, unsigned char) -extern int param_set_short(const char *val, struct kernel_param *kp); -extern int param_get_short(char *buffer, struct kernel_param *kp); +extern struct kernel_param_ops param_ops_short; +extern int param_set_short(const char *val, const struct kernel_param *kp); +extern int param_get_short(char *buffer, const struct kernel_param *kp); #define param_check_short(name, p) __param_check(name, p, short) -extern int param_set_ushort(const char *val, struct kernel_param *kp); -extern int param_get_ushort(char *buffer, struct kernel_param *kp); +extern struct kernel_param_ops param_ops_ushort; +extern int param_set_ushort(const char *val, const struct kernel_param *kp); +extern int param_get_ushort(char *buffer, const struct kernel_param *kp); #define param_check_ushort(name, p) __param_check(name, p, unsigned short) -extern int param_set_int(const char *val, struct kernel_param *kp); -extern int param_get_int(char *buffer, struct kernel_param *kp); +extern struct kernel_param_ops param_ops_int; +extern int param_set_int(const char *val, const struct kernel_param *kp); +extern int param_get_int(char *buffer, const struct kernel_param *kp); #define param_check_int(name, p) __param_check(name, p, int) -extern int param_set_uint(const char *val, struct kernel_param *kp); -extern int param_get_uint(char *buffer, struct kernel_param *kp); +extern struct kernel_param_ops param_ops_uint; +extern int param_set_uint(const char *val, const struct kernel_param *kp); +extern int param_get_uint(char *buffer, const struct kernel_param *kp); #define param_check_uint(name, p) __param_check(name, p, unsigned int) -extern int param_set_long(const char *val, struct kernel_param *kp); -extern int param_get_long(char *buffer, struct kernel_param *kp); +extern struct kernel_param_ops param_ops_long; +extern int param_set_long(const char *val, const struct kernel_param *kp); +extern int param_get_long(char *buffer, const struct kernel_param *kp); #define param_check_long(name, p) __param_check(name, p, long) -extern int param_set_ulong(const char *val, struct kernel_param *kp); -extern int param_get_ulong(char *buffer, struct kernel_param *kp); +extern struct kernel_param_ops param_ops_ulong; +extern int param_set_ulong(const char *val, const struct kernel_param *kp); +extern int param_get_ulong(char *buffer, const struct kernel_param *kp); #define param_check_ulong(name, p) __param_check(name, p, unsigned long) -extern int param_set_charp(const char *val, struct kernel_param *kp); -extern int param_get_charp(char *buffer, struct kernel_param *kp); +extern struct kernel_param_ops param_ops_charp; +extern int param_set_charp(const char *val, const struct kernel_param *kp); +extern int param_get_charp(char *buffer, const struct kernel_param *kp); #define param_check_charp(name, p) __param_check(name, p, char *) /* For historical reasons "bool" parameters can be (unsigned) "int". */ -extern int param_set_bool(const char *val, struct kernel_param *kp); -extern int param_get_bool(char *buffer, struct kernel_param *kp); +extern struct kernel_param_ops param_ops_bool; +extern int param_set_bool(const char *val, const struct kernel_param *kp); +extern int param_get_bool(char *buffer, const struct kernel_param *kp); #define param_check_bool(name, p) \ static inline void __check_##name(void) \ { \ @@ -205,17 +231,18 @@ extern int param_get_bool(char *buffer, struct kernel_param *kp); !__same_type(*(p), int)); \ } -extern int param_set_invbool(const char *val, struct kernel_param *kp); -extern int param_get_invbool(char *buffer, struct kernel_param *kp); +extern struct kernel_param_ops param_ops_invbool; +extern int param_set_invbool(const char *val, const struct kernel_param *kp); +extern int param_get_invbool(char *buffer, const struct kernel_param *kp); #define param_check_invbool(name, p) __param_check(name, p, bool) /* Comma-separated array: *nump is set to number they actually specified. */ #define module_param_array_named(name, array, type, nump, perm) \ static const struct kparam_array __param_arr_##name \ - = { ARRAY_SIZE(array), nump, param_set_##type, param_get_##type,\ + = { ARRAY_SIZE(array), nump, ¶m_ops_##type, \ sizeof(array[0]), array }; \ __module_param_call(MODULE_PARAM_PREFIX, name, \ - param_array_set, param_array_get, \ + ¶m_array_ops, \ .arr = &__param_arr_##name, \ __same_type(array[0], bool), perm); \ __MODULE_PARM_TYPE(name, "array of " #type) @@ -223,11 +250,11 @@ extern int param_get_invbool(char *buffer, struct kernel_param *kp); #define module_param_array(name, type, nump, perm) \ module_param_array_named(name, name, type, nump, perm) -extern int param_array_set(const char *val, struct kernel_param *kp); -extern int param_array_get(char *buffer, struct kernel_param *kp); +extern struct kernel_param_ops param_array_ops; -extern int param_set_copystring(const char *val, struct kernel_param *kp); -extern int param_get_string(char *buffer, struct kernel_param *kp); +extern struct kernel_param_ops param_ops_string; +extern int param_set_copystring(const char *val, const struct kernel_param *); +extern int param_get_string(char *buffer, const struct kernel_param *kp); /* for exporting parameters in /sys/parameters */ @@ -235,13 +262,13 @@ struct module; #if defined(CONFIG_SYSFS) && defined(CONFIG_MODULES) extern int module_param_sysfs_setup(struct module *mod, - struct kernel_param *kparam, + const struct kernel_param *kparam, unsigned int num_params); extern void module_param_sysfs_remove(struct module *mod); #else static inline int module_param_sysfs_setup(struct module *mod, - struct kernel_param *kparam, + const struct kernel_param *kparam, unsigned int num_params) { return 0; diff --git a/kernel/params.c b/kernel/params.c index 3e78fdb445e7..a550698ae02d 100644 --- a/kernel/params.c +++ b/kernel/params.c @@ -59,11 +59,11 @@ static int parse_one(char *param, for (i = 0; i < num_params; i++) { if (parameq(param, params[i].name)) { /* Noone handled NULL, so do it here. */ - if (!val && params[i].set != param_set_bool) + if (!val && params[i].ops->set != param_set_bool) return -EINVAL; DEBUGP("They are equal! Calling %p\n", - params[i].set); - return params[i].set(val, ¶ms[i]); + params[i].ops->set); + return params[i].ops->set(val, ¶ms[i]); } } @@ -179,7 +179,7 @@ int parse_args(const char *name, /* Lazy bastard, eh? */ #define STANDARD_PARAM_DEF(name, type, format, tmptype, strtolfn) \ - int param_set_##name(const char *val, struct kernel_param *kp) \ + int param_set_##name(const char *val, const struct kernel_param *kp) \ { \ tmptype l; \ int ret; \ @@ -190,12 +190,18 @@ int parse_args(const char *name, *((type *)kp->arg) = l; \ return 0; \ } \ - int param_get_##name(char *buffer, struct kernel_param *kp) \ + int param_get_##name(char *buffer, const struct kernel_param *kp) \ { \ return sprintf(buffer, format, *((type *)kp->arg)); \ } \ + struct kernel_param_ops param_ops_##name = { \ + .set = param_set_##name, \ + .get = param_get_##name, \ + }; \ EXPORT_SYMBOL(param_set_##name); \ - EXPORT_SYMBOL(param_get_##name) + EXPORT_SYMBOL(param_get_##name); \ + EXPORT_SYMBOL(param_ops_##name) + STANDARD_PARAM_DEF(byte, unsigned char, "%c", unsigned long, strict_strtoul); STANDARD_PARAM_DEF(short, short, "%hi", long, strict_strtol); @@ -205,7 +211,7 @@ STANDARD_PARAM_DEF(uint, unsigned int, "%u", unsigned long, strict_strtoul); STANDARD_PARAM_DEF(long, long, "%li", long, strict_strtol); STANDARD_PARAM_DEF(ulong, unsigned long, "%lu", unsigned long, strict_strtoul); -int param_set_charp(const char *val, struct kernel_param *kp) +int param_set_charp(const char *val, const struct kernel_param *kp) { if (strlen(val) > 1024) { printk(KERN_ERR "%s: string parameter too long\n", @@ -226,14 +232,20 @@ int param_set_charp(const char *val, struct kernel_param *kp) } EXPORT_SYMBOL(param_set_charp); -int param_get_charp(char *buffer, struct kernel_param *kp) +int param_get_charp(char *buffer, const struct kernel_param *kp) { return sprintf(buffer, "%s", *((char **)kp->arg)); } EXPORT_SYMBOL(param_get_charp); +struct kernel_param_ops param_ops_charp = { + .set = param_set_charp, + .get = param_get_charp, +}; +EXPORT_SYMBOL(param_ops_charp); + /* Actually could be a bool or an int, for historical reasons. */ -int param_set_bool(const char *val, struct kernel_param *kp) +int param_set_bool(const char *val, const struct kernel_param *kp) { bool v; @@ -260,7 +272,7 @@ int param_set_bool(const char *val, struct kernel_param *kp) } EXPORT_SYMBOL(param_set_bool); -int param_get_bool(char *buffer, struct kernel_param *kp) +int param_get_bool(char *buffer, const struct kernel_param *kp) { bool val; if (kp->flags & KPARAM_ISBOOL) @@ -273,8 +285,14 @@ int param_get_bool(char *buffer, struct kernel_param *kp) } EXPORT_SYMBOL(param_get_bool); +struct kernel_param_ops param_ops_bool = { + .set = param_set_bool, + .get = param_get_bool, +}; +EXPORT_SYMBOL(param_ops_bool); + /* This one must be bool. */ -int param_set_invbool(const char *val, struct kernel_param *kp) +int param_set_invbool(const char *val, const struct kernel_param *kp) { int ret; bool boolval; @@ -289,18 +307,24 @@ int param_set_invbool(const char *val, struct kernel_param *kp) } EXPORT_SYMBOL(param_set_invbool); -int param_get_invbool(char *buffer, struct kernel_param *kp) +int param_get_invbool(char *buffer, const struct kernel_param *kp) { return sprintf(buffer, "%c", (*(bool *)kp->arg) ? 'N' : 'Y'); } EXPORT_SYMBOL(param_get_invbool); +struct kernel_param_ops param_ops_invbool = { + .set = param_set_invbool, + .get = param_get_invbool, +}; +EXPORT_SYMBOL(param_ops_invbool); + /* We break the rule and mangle the string. */ static int param_array(const char *name, const char *val, unsigned int min, unsigned int max, void *elem, int elemsize, - int (*set)(const char *, struct kernel_param *kp), + int (*set)(const char *, const struct kernel_param *kp), u16 flags, unsigned int *num) { @@ -345,18 +369,17 @@ static int param_array(const char *name, return 0; } -int param_array_set(const char *val, struct kernel_param *kp) +static int param_array_set(const char *val, const struct kernel_param *kp) { const struct kparam_array *arr = kp->arr; unsigned int temp_num; return param_array(kp->name, val, 1, arr->max, arr->elem, - arr->elemsize, arr->set, kp->flags, + arr->elemsize, arr->ops->set, kp->flags, arr->num ?: &temp_num); } -EXPORT_SYMBOL(param_array_set); -int param_array_get(char *buffer, struct kernel_param *kp) +static int param_array_get(char *buffer, const struct kernel_param *kp) { int i, off, ret; const struct kparam_array *arr = kp->arr; @@ -367,7 +390,7 @@ int param_array_get(char *buffer, struct kernel_param *kp) if (i) buffer[off++] = ','; p.arg = arr->elem + arr->elemsize * i; - ret = arr->get(buffer + off, &p); + ret = arr->ops->get(buffer + off, &p); if (ret < 0) return ret; off += ret; @@ -375,9 +398,14 @@ int param_array_get(char *buffer, struct kernel_param *kp) buffer[off] = '\0'; return off; } -EXPORT_SYMBOL(param_array_get); -int param_set_copystring(const char *val, struct kernel_param *kp) +struct kernel_param_ops param_array_ops = { + .set = param_array_set, + .get = param_array_get, +}; +EXPORT_SYMBOL(param_array_ops); + +int param_set_copystring(const char *val, const struct kernel_param *kp) { const struct kparam_string *kps = kp->str; @@ -391,13 +419,19 @@ int param_set_copystring(const char *val, struct kernel_param *kp) } EXPORT_SYMBOL(param_set_copystring); -int param_get_string(char *buffer, struct kernel_param *kp) +int param_get_string(char *buffer, const struct kernel_param *kp) { const struct kparam_string *kps = kp->str; return strlcpy(buffer, kps->string, kps->maxlen); } EXPORT_SYMBOL(param_get_string); +struct kernel_param_ops param_ops_string = { + .set = param_set_copystring, + .get = param_get_string, +}; +EXPORT_SYMBOL(param_ops_string); + /* sysfs output in /sys/modules/XYZ/parameters/ */ #define to_module_attr(n) container_of(n, struct module_attribute, attr) #define to_module_kobject(n) container_of(n, struct module_kobject, kobj) @@ -407,7 +441,7 @@ extern struct kernel_param __start___param[], __stop___param[]; struct param_attribute { struct module_attribute mattr; - struct kernel_param *param; + const struct kernel_param *param; }; struct module_param_attrs @@ -426,10 +460,10 @@ static ssize_t param_attr_show(struct module_attribute *mattr, int count; struct param_attribute *attribute = to_param_attr(mattr); - if (!attribute->param->get) + if (!attribute->param->ops->get) return -EPERM; - count = attribute->param->get(buf, attribute->param); + count = attribute->param->ops->get(buf, attribute->param); if (count > 0) { strcat(buf, "\n"); ++count; @@ -445,10 +479,10 @@ static ssize_t param_attr_store(struct module_attribute *mattr, int err; struct param_attribute *attribute = to_param_attr(mattr); - if (!attribute->param->set) + if (!attribute->param->ops->set) return -EPERM; - err = attribute->param->set(buf, attribute->param); + err = attribute->param->ops->set(buf, attribute->param); if (!err) return len; return err; @@ -473,7 +507,7 @@ static ssize_t param_attr_store(struct module_attribute *mattr, * if there's an error. */ static __modinit int add_sysfs_param(struct module_kobject *mk, - struct kernel_param *kp, + const struct kernel_param *kp, const char *name) { struct module_param_attrs *new; @@ -555,7 +589,7 @@ static void free_module_param_attrs(struct module_kobject *mk) * /sys/module/[mod->name]/parameters/ */ int module_param_sysfs_setup(struct module *mod, - struct kernel_param *kparam, + const struct kernel_param *kparam, unsigned int num_params) { int i, err; diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index 7ca65c7005ea..49a62f0c4b87 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c @@ -2577,7 +2577,8 @@ void cleanup_socket_xprt(void) xprt_unregister_transport(&xs_bc_tcp_transport); } -static int param_set_uint_minmax(const char *val, struct kernel_param *kp, +static int param_set_uint_minmax(const char *val, + const struct kernel_param *kp, unsigned int min, unsigned int max) { unsigned long num; @@ -2592,34 +2593,37 @@ static int param_set_uint_minmax(const char *val, struct kernel_param *kp, return 0; } -static int param_set_portnr(const char *val, struct kernel_param *kp) +static int param_set_portnr(const char *val, const struct kernel_param *kp) { return param_set_uint_minmax(val, kp, RPC_MIN_RESVPORT, RPC_MAX_RESVPORT); } -static int param_get_portnr(char *buffer, struct kernel_param *kp) -{ - return param_get_uint(buffer, kp); -} +static struct kernel_param_ops param_ops_portnr = { + .set = param_set_portnr, + .get = param_get_uint, +}; + #define param_check_portnr(name, p) \ __param_check(name, p, unsigned int); module_param_named(min_resvport, xprt_min_resvport, portnr, 0644); module_param_named(max_resvport, xprt_max_resvport, portnr, 0644); -static int param_set_slot_table_size(const char *val, struct kernel_param *kp) +static int param_set_slot_table_size(const char *val, + const struct kernel_param *kp) { return param_set_uint_minmax(val, kp, RPC_MIN_SLOT_TABLE, RPC_MAX_SLOT_TABLE); } -static int param_get_slot_table_size(char *buffer, struct kernel_param *kp) -{ - return param_get_uint(buffer, kp); -} +static struct kernel_param_ops param_ops_slot_table_size = { + .set = param_set_slot_table_size, + .get = param_get_uint, +}; + #define param_check_slot_table_size(name, p) \ __param_check(name, p, unsigned int); -- cgit v1.2.3 From 58939473bacf08e7e7346673c6d70bc367bd091a Mon Sep 17 00:00:00 2001 From: Tony Battersby Date: Tue, 10 Aug 2010 18:01:29 -0700 Subject: vfs: improve comment describing fget_light() Improve the description of fget_light(), which is currently incorrect about needing a prior refcnt (judging by the way it is actually used). Signed-off-by: Tony Battersby Cc: Al Viro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/file_table.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) (limited to 'fs') diff --git a/fs/file_table.c b/fs/file_table.c index b8a0bb63cbd7..2fc3b3c08911 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -298,11 +298,20 @@ struct file *fget(unsigned int fd) EXPORT_SYMBOL(fget); /* - * Lightweight file lookup - no refcnt increment if fd table isn't shared. - * You can use this only if it is guranteed that the current task already - * holds a refcnt to that file. That check has to be done at fget() only - * and a flag is returned to be passed to the corresponding fput_light(). - * There must not be a cloning between an fget_light/fput_light pair. + * Lightweight file lookup - no refcnt increment if fd table isn't shared. + * + * You can use this instead of fget if you satisfy all of the following + * conditions: + * 1) You must call fput_light before exiting the syscall and returning control + * to userspace (i.e. you cannot remember the returned struct file * after + * returning to userspace). + * 2) You must not call filp_close on the returned struct file * in between + * calls to fget_light and fput_light. + * 3) You must not clone the current task in between the calls to fget_light + * and fput_light. + * + * The fput_needed flag returned by fget_light should be passed to the + * corresponding fput_light. */ struct file *fget_light(unsigned int fd, int *fput_needed) { -- cgit v1.2.3 From 454eedb8901da895fb602998fa588cd62875d07d Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Tue, 10 Aug 2010 18:01:29 -0700 Subject: vfs: O_* bit numbers uniqueness check The O_* bit numbers are defined in 20+ arch/*, and can silently overlap. Add a compile time check to ensure the uniqueness as suggested by David Miller. Signed-off-by: Wu Fengguang Cc: David Miller Cc: Stephen Rothwell Cc: Al Viro Cc: Christoph Hellwig Cc: Eric Paris Cc: Roland Dreier Cc: Jamie Lokier Cc: Andreas Schwab Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/fcntl.c | 15 +++++++++++++-- include/asm-generic/fcntl.h | 4 ++++ 2 files changed, 17 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/fcntl.c b/fs/fcntl.c index 9d175d623aab..6769fd0f35b8 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c @@ -767,11 +767,22 @@ void kill_fasync(struct fasync_struct **fp, int sig, int band) } EXPORT_SYMBOL(kill_fasync); -static int __init fasync_init(void) +static int __init fcntl_init(void) { + /* please add new bits here to ensure allocation uniqueness */ + BUILD_BUG_ON(19 - 1 /* for O_RDONLY being 0 */ != HWEIGHT32( + O_RDONLY | O_WRONLY | O_RDWR | + O_CREAT | O_EXCL | O_NOCTTY | + O_TRUNC | O_APPEND | O_NONBLOCK | + __O_SYNC | O_DSYNC | FASYNC | + O_DIRECT | O_LARGEFILE | O_DIRECTORY | + O_NOFOLLOW | O_NOATIME | O_CLOEXEC | + FMODE_EXEC + )); + fasync_cache = kmem_cache_create("fasync_cache", sizeof(struct fasync_struct), 0, SLAB_PANIC, NULL); return 0; } -module_init(fasync_init) +module_init(fcntl_init) diff --git a/include/asm-generic/fcntl.h b/include/asm-generic/fcntl.h index e3cbc38bdcc2..a70b2d2bfc14 100644 --- a/include/asm-generic/fcntl.h +++ b/include/asm-generic/fcntl.h @@ -11,6 +11,10 @@ * -Eric Paris */ +/* + * When introducing new O_* bits, please check its uniqueness in fcntl_init(). + */ + #define O_ACCMODE 00000003 #define O_RDONLY 00000000 #define O_WRONLY 00000001 -- cgit v1.2.3 From 06b1e104b7ea1bf5145643de5a3fce28b831ca4c Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Tue, 10 Aug 2010 18:01:33 -0700 Subject: vfs: clarify that nonseekable_open() will never fail Signed-off-by: Dmitry Torokhov Acked-by: Arnd Bergmann Acked-by: John Kacur Cc: Al Viro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/open.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/open.c b/fs/open.c index b715d06fbe36..630715f9f73d 100644 --- a/fs/open.c +++ b/fs/open.c @@ -1031,7 +1031,9 @@ EXPORT_SYMBOL(generic_file_open); /* * This is used by subsystems that don't want seekable - * file descriptors + * file descriptors. The function is not supposed to ever fail, the only + * reason it returns an 'int' and not 'void' is so that it can be plugged + * directly into file_operations structure. */ int nonseekable_open(struct inode *inode, struct file *filp) { -- cgit v1.2.3 From a892e2d7dcdfa6c76e60c50a8c7385c65587a2a6 Mon Sep 17 00:00:00 2001 From: Changli Gao Date: Tue, 10 Aug 2010 18:01:35 -0700 Subject: vfs: use kmalloc() to allocate fdmem if possible Use kmalloc() to allocate fdmem if possible. vmalloc() is used as a fallback solution for fdmem allocation. A new helper function __free_fdtable() is introduced to reduce the lines of code. A potential bug, vfree() a memory allocated by kmalloc(), is fixed. [akpm@linux-foundation.org: use __GFP_NOWARN, uninline alloc_fdmem() and free_fdmem()] Signed-off-by: Changli Gao Cc: Alexander Viro Cc: Jiri Slaby Cc: "Paul E. McKenney" Cc: Alexey Dobriyan Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Avi Kivity Cc: Tetsuo Handa Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/file.c | 57 +++++++++++++++++++++++---------------------------------- 1 file changed, 23 insertions(+), 34 deletions(-) (limited to 'fs') diff --git a/fs/file.c b/fs/file.c index cccaead962c2..0be344755c02 100644 --- a/fs/file.c +++ b/fs/file.c @@ -39,28 +39,27 @@ int sysctl_nr_open_max = 1024 * 1024; /* raised later */ */ static DEFINE_PER_CPU(struct fdtable_defer, fdtable_defer_list); -static inline void * alloc_fdmem(unsigned int size) +static inline void *alloc_fdmem(unsigned int size) { - if (size <= PAGE_SIZE) - return kmalloc(size, GFP_KERNEL); - else - return vmalloc(size); + void *data; + + data = kmalloc(size, GFP_KERNEL|__GFP_NOWARN); + if (data != NULL) + return data; + + return vmalloc(size); } -static inline void free_fdarr(struct fdtable *fdt) +static void free_fdmem(void *ptr) { - if (fdt->max_fds <= (PAGE_SIZE / sizeof(struct file *))) - kfree(fdt->fd); - else - vfree(fdt->fd); + is_vmalloc_addr(ptr) ? vfree(ptr) : kfree(ptr); } -static inline void free_fdset(struct fdtable *fdt) +static void __free_fdtable(struct fdtable *fdt) { - if (fdt->max_fds <= (PAGE_SIZE * BITS_PER_BYTE / 2)) - kfree(fdt->open_fds); - else - vfree(fdt->open_fds); + free_fdmem(fdt->fd); + free_fdmem(fdt->open_fds); + kfree(fdt); } static void free_fdtable_work(struct work_struct *work) @@ -75,9 +74,8 @@ static void free_fdtable_work(struct work_struct *work) spin_unlock_bh(&f->lock); while(fdt) { struct fdtable *next = fdt->next; - vfree(fdt->fd); - free_fdset(fdt); - kfree(fdt); + + __free_fdtable(fdt); fdt = next; } } @@ -98,7 +96,7 @@ void free_fdtable_rcu(struct rcu_head *rcu) container_of(fdt, struct files_struct, fdtab)); return; } - if (fdt->max_fds <= (PAGE_SIZE / sizeof(struct file *))) { + if (!is_vmalloc_addr(fdt->fd) && !is_vmalloc_addr(fdt->open_fds)) { kfree(fdt->fd); kfree(fdt->open_fds); kfree(fdt); @@ -183,7 +181,7 @@ static struct fdtable * alloc_fdtable(unsigned int nr) return fdt; out_arr: - free_fdarr(fdt); + free_fdmem(fdt->fd); out_fdt: kfree(fdt); out: @@ -213,9 +211,7 @@ static int expand_fdtable(struct files_struct *files, int nr) * caller and alloc_fdtable(). Cheaper to catch it here... */ if (unlikely(new_fdt->max_fds <= nr)) { - free_fdarr(new_fdt); - free_fdset(new_fdt); - kfree(new_fdt); + __free_fdtable(new_fdt); return -EMFILE; } /* @@ -231,9 +227,7 @@ static int expand_fdtable(struct files_struct *files, int nr) free_fdtable(cur_fdt); } else { /* Somebody else expanded, so undo our attempt */ - free_fdarr(new_fdt); - free_fdset(new_fdt); - kfree(new_fdt); + __free_fdtable(new_fdt); } return 1; } @@ -323,11 +317,8 @@ struct files_struct *dup_fd(struct files_struct *oldf, int *errorp) while (unlikely(open_files > new_fdt->max_fds)) { spin_unlock(&oldf->file_lock); - if (new_fdt != &newf->fdtab) { - free_fdarr(new_fdt); - free_fdset(new_fdt); - kfree(new_fdt); - } + if (new_fdt != &newf->fdtab) + __free_fdtable(new_fdt); new_fdt = alloc_fdtable(open_files - 1); if (!new_fdt) { @@ -337,9 +328,7 @@ struct files_struct *dup_fd(struct files_struct *oldf, int *errorp) /* beyond sysctl_nr_open; nothing to do */ if (unlikely(new_fdt->max_fds < open_files)) { - free_fdarr(new_fdt); - free_fdset(new_fdt); - kfree(new_fdt); + __free_fdtable(new_fdt); *errorp = -EMFILE; goto out_release; } -- cgit v1.2.3 From 5fc79d85d2ab7ce144bc75e06cab58126249afbb Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 10 Aug 2010 18:02:04 -0700 Subject: autofs4: remove unneeded null check in try_to_fill_dentry() After 97e7449a7ad: "autofs4: fix indirect mount pending expire race" we no longer assumed that "ino" can be null. The other null checks got removed but this was one was missed. Signed-off-by: Dan Carpenter Cc: Ian Kent Cc: Al Viro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/autofs4/root.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c index 48e056e70fd6..cb1bd38dc08c 100644 --- a/fs/autofs4/root.c +++ b/fs/autofs4/root.c @@ -204,8 +204,7 @@ static int try_to_fill_dentry(struct dentry *dentry, int flags) } /* Initialize expiry counter after successful mount */ - if (ino) - ino->last_used = jiffies; + ino->last_used = jiffies; spin_lock(&sbi->fs_lock); ino->flags &= ~AUTOFS_INF_PENDING; -- cgit v1.2.3 From b3397ad544172fb34ddcff5b04704b6c80838289 Mon Sep 17 00:00:00 2001 From: Changli Gao Date: Tue, 10 Aug 2010 18:02:48 -0700 Subject: reiserfs: remove unused local `wait' Signed-off-by: Changli Gao Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/reiserfs/journal.c | 1 - 1 file changed, 1 deletion(-) (limited to 'fs') diff --git a/fs/reiserfs/journal.c b/fs/reiserfs/journal.c index 19fbc810e8e7..1ec952b1f036 100644 --- a/fs/reiserfs/journal.c +++ b/fs/reiserfs/journal.c @@ -983,7 +983,6 @@ static int flush_older_commits(struct super_block *s, static int reiserfs_async_progress_wait(struct super_block *s) { - DEFINE_WAIT(wait); struct reiserfs_journal *j = SB_JOURNAL(s); if (atomic_read(&j->j_async_throttle)) { -- cgit v1.2.3 From b7300b78d1a87625975a799a109a2f98d77757c8 Mon Sep 17 00:00:00 2001 From: Chris Wright Date: Tue, 10 Aug 2010 18:02:55 -0700 Subject: blkdev: cgroup whitelist permission fix The cgroup device whitelist code gets confused when trying to grant permission to a disk partition that is not currently open. Part of blkdev_open() includes __blkdev_get() on the whole disk. Basically, the only ways to reliably allow a cgroup access to a partition on a block device when using the whitelist are to 1) also give it access to the whole block device or 2) make sure the partition is already open in a different context. The patch avoids the cgroup check for the whole disk case when opening a partition. Addresses https://bugzilla.redhat.com/show_bug.cgi?id=589662 Signed-off-by: Chris Wright Acked-by: Serge E. Hallyn Tested-by: Serge E. Hallyn Reported-by: Vivek Goyal Cc: Al Viro Cc: Christoph Hellwig Cc: "Daniel P. Berrange" Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/block_dev.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/block_dev.c b/fs/block_dev.c index 66411463b734..50e8c8582faa 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -1340,10 +1340,12 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part) /* * hooks: /n/, see "layering violations". */ - ret = devcgroup_inode_permission(bdev->bd_inode, perm); - if (ret != 0) { - bdput(bdev); - return ret; + if (!for_part) { + ret = devcgroup_inode_permission(bdev->bd_inode, perm); + if (ret != 0) { + bdput(bdev); + return ret; + } } restart: -- cgit v1.2.3 From a2a20c412c86e0bb46a9ab0dd31bcfe6d201b913 Mon Sep 17 00:00:00 2001 From: Nathan Lynch Date: Tue, 10 Aug 2010 18:03:08 -0700 Subject: signalfd: fill in ssi_int for posix timers and message queues If signalfd is used to consume a signal generated by a POSIX interval timer or POSIX message queue, the ssi_int field does not reflect the data (sigevent->sigev_value) supplied to timer_create(2) or mq_notify(3). (The ssi_ptr field, however, is filled in.) This behavior differs from signalfd's treatment of sigqueue-generated signals -- see the default case in signalfd_copyinfo. It also gives results that differ from the case when a signal is handled conventionally via a sigaction-registered handler. So, set signalfd_siginfo->ssi_int in the remaining cases (__SI_TIMER, __SI_MESGQ) where ssi_ptr is set. akpm: a non-back-compatible change. Merge into -stable to minimise the number of kernels which are in the field and which miss this feature. Signed-off-by: Nathan Lynch Acked-by: Davide Libenzi Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/signalfd.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'fs') diff --git a/fs/signalfd.c b/fs/signalfd.c index f329849ce3c0..1c5a6add779d 100644 --- a/fs/signalfd.c +++ b/fs/signalfd.c @@ -88,6 +88,7 @@ static int signalfd_copyinfo(struct signalfd_siginfo __user *uinfo, err |= __put_user(kinfo->si_tid, &uinfo->ssi_tid); err |= __put_user(kinfo->si_overrun, &uinfo->ssi_overrun); err |= __put_user((long) kinfo->si_ptr, &uinfo->ssi_ptr); + err |= __put_user(kinfo->si_int, &uinfo->ssi_int); break; case __SI_POLL: err |= __put_user(kinfo->si_band, &uinfo->ssi_band); @@ -111,6 +112,7 @@ static int signalfd_copyinfo(struct signalfd_siginfo __user *uinfo, err |= __put_user(kinfo->si_pid, &uinfo->ssi_pid); err |= __put_user(kinfo->si_uid, &uinfo->ssi_uid); err |= __put_user((long) kinfo->si_ptr, &uinfo->ssi_ptr); + err |= __put_user(kinfo->si_int, &uinfo->ssi_int); break; default: /* -- cgit v1.2.3 From cfbef3cb16658046c6faa8c9816018dd11ae62f0 Mon Sep 17 00:00:00 2001 From: "Robert P. J. Day" Date: Tue, 10 Aug 2010 18:03:08 -0700 Subject: procfs: simplify conditional processing of fs/proc.o. Since the entire fs/proc directory is conditionally included based on CONFIG_PROC_FS, it's redundant to check that same variable within that directory. Signed-off-by: Robert P. J. Day Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/proc/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/proc/Makefile b/fs/proc/Makefile index 11a7b5c68153..2758e2afc518 100644 --- a/fs/proc/Makefile +++ b/fs/proc/Makefile @@ -2,7 +2,7 @@ # Makefile for the Linux proc filesystem routines. # -obj-$(CONFIG_PROC_FS) += proc.o +obj-y += proc.o proc-y := nommu.o task_nommu.o proc-$(CONFIG_MMU) := mmu.o task_mmu.o -- cgit v1.2.3 From 9c867fbe06458a8957024236b574733fae0cefed Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Tue, 10 Aug 2010 18:03:14 -0700 Subject: partitions: fix sometimes unreadable partition strings Fix this garbage happening quite often: ==> sda: scsi 3:0:0:0: CD-ROM TOSHIBA ==> sda1 sda2 sda3 sda4 sda5 sda6 sda7 > Make "sda: sda1 ..." lines actually lines. Signed-off-by: Alexey Dobriyan Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/partitions/acorn.c | 35 +++++++++++++++----------- fs/partitions/amiga.c | 20 +++++++++++---- fs/partitions/atari.c | 12 ++++----- fs/partitions/check.c | 22 ++++++++++++++--- fs/partitions/check.h | 6 ++++- fs/partitions/efi.c | 2 +- fs/partitions/ibm.c | 17 ++++++++----- fs/partitions/karma.c | 2 +- fs/partitions/ldm.c | 4 +-- fs/partitions/mac.c | 4 +-- fs/partitions/msdos.c | 67 +++++++++++++++++++++++++++++++++----------------- fs/partitions/osf.c | 2 +- fs/partitions/sgi.c | 2 +- fs/partitions/sun.c | 2 +- fs/partitions/sysv68.c | 9 ++++--- fs/partitions/ultrix.c | 2 +- 16 files changed, 136 insertions(+), 72 deletions(-) (limited to 'fs') diff --git a/fs/partitions/acorn.c b/fs/partitions/acorn.c index 6921e7890be6..fbeb697374d5 100644 --- a/fs/partitions/acorn.c +++ b/fs/partitions/acorn.c @@ -45,8 +45,11 @@ adfs_partition(struct parsed_partitions *state, char *name, char *data, nr_sects = (le32_to_cpu(dr->disc_size_high) << 23) | (le32_to_cpu(dr->disc_size) >> 9); - if (name) - printk(" [%s]", name); + if (name) { + strlcat(state->pp_buf, " [", PAGE_SIZE); + strlcat(state->pp_buf, name, PAGE_SIZE); + strlcat(state->pp_buf, "]", PAGE_SIZE); + } put_partition(state, slot, first_sector, nr_sects); return dr; } @@ -81,14 +84,14 @@ static int riscix_partition(struct parsed_partitions *state, if (!rr) return -1; - printk(" [RISCiX]"); + strlcat(state->pp_buf, " [RISCiX]", PAGE_SIZE); if (rr->magic == RISCIX_MAGIC) { unsigned long size = nr_sects > 2 ? 2 : nr_sects; int part; - printk(" <"); + strlcat(state->pp_buf, " <", PAGE_SIZE); put_partition(state, slot++, first_sect, size); for (part = 0; part < 8; part++) { @@ -97,11 +100,13 @@ static int riscix_partition(struct parsed_partitions *state, put_partition(state, slot++, le32_to_cpu(rr->part[part].start), le32_to_cpu(rr->part[part].length)); - printk("(%s)", rr->part[part].name); + strlcat(state->pp_buf, "(", PAGE_SIZE); + strlcat(state->pp_buf, rr->part[part].name, PAGE_SIZE); + strlcat(state->pp_buf, ")", PAGE_SIZE); } } - printk(" >\n"); + strlcat(state->pp_buf, " >\n", PAGE_SIZE); } else { put_partition(state, slot++, first_sect, nr_sects); } @@ -131,7 +136,7 @@ static int linux_partition(struct parsed_partitions *state, struct linux_part *linuxp; unsigned long size = nr_sects > 2 ? 2 : nr_sects; - printk(" [Linux]"); + strlcat(state->pp_buf, " [Linux]", PAGE_SIZE); put_partition(state, slot++, first_sect, size); @@ -139,7 +144,7 @@ static int linux_partition(struct parsed_partitions *state, if (!linuxp) return -1; - printk(" <"); + strlcat(state->pp_buf, " <", PAGE_SIZE); while (linuxp->magic == cpu_to_le32(LINUX_NATIVE_MAGIC) || linuxp->magic == cpu_to_le32(LINUX_SWAP_MAGIC)) { if (slot == state->limit) @@ -149,7 +154,7 @@ static int linux_partition(struct parsed_partitions *state, le32_to_cpu(linuxp->nr_sects)); linuxp ++; } - printk(" >"); + strlcat(state->pp_buf, " >", PAGE_SIZE); put_dev_sector(sect); return slot; @@ -294,7 +299,7 @@ int adfspart_check_ADFS(struct parsed_partitions *state) break; } } - printk("\n"); + strlcat(state->pp_buf, "\n", PAGE_SIZE); return 1; } #endif @@ -367,7 +372,7 @@ int adfspart_check_ICS(struct parsed_partitions *state) return 0; } - printk(" [ICS]"); + strlcat(state->pp_buf, " [ICS]", PAGE_SIZE); for (slot = 1, p = (const struct ics_part *)data; p->size; p++) { u32 start = le32_to_cpu(p->start); @@ -401,7 +406,7 @@ int adfspart_check_ICS(struct parsed_partitions *state) } put_dev_sector(sect); - printk("\n"); + strlcat(state->pp_buf, "\n", PAGE_SIZE); return 1; } #endif @@ -461,7 +466,7 @@ int adfspart_check_POWERTEC(struct parsed_partitions *state) return 0; } - printk(" [POWERTEC]"); + strlcat(state->pp_buf, " [POWERTEC]", PAGE_SIZE); for (i = 0, p = (const struct ptec_part *)data; i < 12; i++, p++) { u32 start = le32_to_cpu(p->start); @@ -472,7 +477,7 @@ int adfspart_check_POWERTEC(struct parsed_partitions *state) } put_dev_sector(sect); - printk("\n"); + strlcat(state->pp_buf, "\n", PAGE_SIZE); return 1; } #endif @@ -543,7 +548,7 @@ int adfspart_check_EESOX(struct parsed_partitions *state) size = get_capacity(state->bdev->bd_disk); put_partition(state, slot++, start, size - start); - printk("\n"); + strlcat(state->pp_buf, "\n", PAGE_SIZE); } return i ? 1 : 0; diff --git a/fs/partitions/amiga.c b/fs/partitions/amiga.c index ba443d4229f8..70cbf44a1560 100644 --- a/fs/partitions/amiga.c +++ b/fs/partitions/amiga.c @@ -69,7 +69,13 @@ int amiga_partition(struct parsed_partitions *state) /* blksize is blocks per 512 byte standard block */ blksize = be32_to_cpu( rdb->rdb_BlockBytes ) / 512; - printk(" RDSK (%d)", blksize * 512); /* Be more informative */ + { + char tmp[7 + 10 + 1 + 1]; + + /* Be more informative */ + snprintf(tmp, sizeof(tmp), " RDSK (%d)", blksize * 512); + strlcat(state->pp_buf, tmp, PAGE_SIZE); + } blk = be32_to_cpu(rdb->rdb_PartitionList); put_dev_sector(sect); for (part = 1; blk>0 && part<=16; part++, put_dev_sector(sect)) { @@ -106,23 +112,27 @@ int amiga_partition(struct parsed_partitions *state) { /* Be even more informative to aid mounting */ char dostype[4]; + char tmp[42]; + __be32 *dt = (__be32 *)dostype; *dt = pb->pb_Environment[16]; if (dostype[3] < ' ') - printk(" (%c%c%c^%c)", + snprintf(tmp, sizeof(tmp), " (%c%c%c^%c)", dostype[0], dostype[1], dostype[2], dostype[3] + '@' ); else - printk(" (%c%c%c%c)", + snprintf(tmp, sizeof(tmp), " (%c%c%c%c)", dostype[0], dostype[1], dostype[2], dostype[3]); - printk("(res %d spb %d)", + strlcat(state->pp_buf, tmp, PAGE_SIZE); + snprintf(tmp, sizeof(tmp), "(res %d spb %d)", be32_to_cpu(pb->pb_Environment[6]), be32_to_cpu(pb->pb_Environment[4])); + strlcat(state->pp_buf, tmp, PAGE_SIZE); } res = 1; } - printk("\n"); + strlcat(state->pp_buf, "\n", PAGE_SIZE); rdb_done: return res; diff --git a/fs/partitions/atari.c b/fs/partitions/atari.c index 4439ff1b6cec..9875b05e80a2 100644 --- a/fs/partitions/atari.c +++ b/fs/partitions/atari.c @@ -62,7 +62,7 @@ int atari_partition(struct parsed_partitions *state) } pi = &rs->part[0]; - printk (" AHDI"); + strlcat(state->pp_buf, " AHDI", PAGE_SIZE); for (slot = 1; pi < &rs->part[4] && slot < state->limit; slot++, pi++) { struct rootsector *xrs; Sector sect2; @@ -81,7 +81,7 @@ int atari_partition(struct parsed_partitions *state) #ifdef ICD_PARTS part_fmt = 1; #endif - printk(" XGM<"); + strlcat(state->pp_buf, " XGM<", PAGE_SIZE); partsect = extensect = be32_to_cpu(pi->st); while (1) { xrs = read_part_sector(state, partsect, §2); @@ -120,14 +120,14 @@ int atari_partition(struct parsed_partitions *state) break; } } - printk(" >"); + strlcat(state->pp_buf, " >", PAGE_SIZE); } #ifdef ICD_PARTS if ( part_fmt!=1 ) { /* no extended partitions -> test ICD-format */ pi = &rs->icdpart[0]; /* sanity check: no ICD format if first partition invalid */ if (OK_id(pi->id)) { - printk(" ICD<"); + strlcat(state->pp_buf, " ICD<", PAGE_SIZE); for (; pi < &rs->icdpart[8] && slot < state->limit; slot++, pi++) { /* accept only GEM,BGM,RAW,LNX,SWP partitions */ if (!((pi->flg & 1) && OK_id(pi->id))) @@ -137,13 +137,13 @@ int atari_partition(struct parsed_partitions *state) be32_to_cpu(pi->st), be32_to_cpu(pi->siz)); } - printk(" >"); + strlcat(state->pp_buf, " >", PAGE_SIZE); } } #endif put_dev_sector(sect); - printk ("\n"); + strlcat(state->pp_buf, "\n", PAGE_SIZE); return 1; } diff --git a/fs/partitions/check.c b/fs/partitions/check.c index 72c52656dc2e..79fbf3f390f0 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c @@ -164,10 +164,16 @@ check_partition(struct gendisk *hd, struct block_device *bdev) state = kzalloc(sizeof(struct parsed_partitions), GFP_KERNEL); if (!state) return NULL; + state->pp_buf = (char *)__get_free_page(GFP_KERNEL); + if (!state->pp_buf) { + kfree(state); + return NULL; + } + state->pp_buf[0] = '\0'; state->bdev = bdev; disk_name(hd, 0, state->name); - printk(KERN_INFO " %s:", state->name); + snprintf(state->pp_buf, PAGE_SIZE, " %s:", state->name); if (isdigit(state->name[strlen(state->name)-1])) sprintf(state->name, "p"); @@ -185,17 +191,25 @@ check_partition(struct gendisk *hd, struct block_device *bdev) } } - if (res > 0) + if (res > 0) { + printk(KERN_INFO "%s", state->pp_buf); + + free_page((unsigned long)state->pp_buf); return state; + } if (state->access_beyond_eod) err = -ENOSPC; if (err) /* The partition is unrecognized. So report I/O errors if there were any */ res = err; if (!res) - printk(" unknown partition table\n"); + strlcat(state->pp_buf, " unknown partition table\n", PAGE_SIZE); else if (warn_no_part) - printk(" unable to read partition table\n"); + strlcat(state->pp_buf, " unable to read partition table\n", PAGE_SIZE); + + printk(KERN_INFO "%s", state->pp_buf); + + free_page((unsigned long)state->pp_buf); kfree(state); return ERR_PTR(res); } diff --git a/fs/partitions/check.h b/fs/partitions/check.h index 52f8bd399396..8e4e103ba216 100644 --- a/fs/partitions/check.h +++ b/fs/partitions/check.h @@ -16,6 +16,7 @@ struct parsed_partitions { int next; int limit; bool access_beyond_eod; + char *pp_buf; }; static inline void *read_part_sector(struct parsed_partitions *state, @@ -32,9 +33,12 @@ static inline void put_partition(struct parsed_partitions *p, int n, sector_t from, sector_t size) { if (n < p->limit) { + char tmp[1 + BDEVNAME_SIZE + 10 + 1]; + p->parts[n].from = from; p->parts[n].size = size; - printk(" %s%d", p->name, n); + snprintf(tmp, sizeof(tmp), " %s%d", p->name, n); + strlcat(p->pp_buf, tmp, PAGE_SIZE); } } diff --git a/fs/partitions/efi.c b/fs/partitions/efi.c index 9efb2cfe2410..dbb44d4bb8a7 100644 --- a/fs/partitions/efi.c +++ b/fs/partitions/efi.c @@ -630,6 +630,6 @@ int efi_partition(struct parsed_partitions *state) } kfree(ptes); kfree(gpt); - printk("\n"); + strlcat(state->pp_buf, "\n", PAGE_SIZE); return 1; } diff --git a/fs/partitions/ibm.c b/fs/partitions/ibm.c index fc8497643fd0..d1b8a5c4bc0a 100644 --- a/fs/partitions/ibm.c +++ b/fs/partitions/ibm.c @@ -75,6 +75,7 @@ int ibm_partition(struct parsed_partitions *state) unsigned char *data; Sector sect; sector_t labelsect; + char tmp[64]; res = 0; blocksize = bdev_logical_block_size(bdev); @@ -144,13 +145,15 @@ int ibm_partition(struct parsed_partitions *state) */ blocksize = label->cms.block_size; if (label->cms.disk_offset != 0) { - printk("CMS1/%8s(MDSK):", name); + snprintf(tmp, sizeof(tmp), "CMS1/%8s(MDSK):", name); + strlcat(state->pp_buf, tmp, PAGE_SIZE); /* disk is reserved minidisk */ offset = label->cms.disk_offset; size = (label->cms.block_count - 1) * (blocksize >> 9); } else { - printk("CMS1/%8s:", name); + snprintf(tmp, sizeof(tmp), "CMS1/%8s:", name); + strlcat(state->pp_buf, tmp, PAGE_SIZE); offset = (info->label_block + 1); size = label->cms.block_count * (blocksize >> 9); @@ -159,7 +162,8 @@ int ibm_partition(struct parsed_partitions *state) size-offset*(blocksize >> 9)); } else { if (strncmp(type, "LNX1", 4) == 0) { - printk("LNX1/%8s:", name); + snprintf(tmp, sizeof(tmp), "LNX1/%8s:", name); + strlcat(state->pp_buf, tmp, PAGE_SIZE); if (label->lnx.ldl_version == 0xf2) { fmt_size = label->lnx.formatted_blocks * (blocksize >> 9); @@ -178,7 +182,7 @@ int ibm_partition(struct parsed_partitions *state) offset = (info->label_block + 1); } else { /* unlabeled disk */ - printk("(nonl)"); + strlcat(tmp, sizeof(tmp), "(nonl)", PAGE_SIZE); size = i_size >> 9; offset = (info->label_block + 1); } @@ -197,7 +201,8 @@ int ibm_partition(struct parsed_partitions *state) * if not, something is wrong, skipping partition detection */ if (strncmp(type, "VOL1", 4) == 0) { - printk("VOL1/%8s:", name); + snprintf(tmp, sizeof(tmp), "VOL1/%8s:", name); + strlcat(state->pp_buf, tmp, PAGE_SIZE); /* * get block number and read then go through format1 * labels @@ -253,7 +258,7 @@ int ibm_partition(struct parsed_partitions *state) } - printk("\n"); + strlcat(state->pp_buf, "\n", PAGE_SIZE); goto out_freeall; diff --git a/fs/partitions/karma.c b/fs/partitions/karma.c index 1cc928bb762f..0ea19312706b 100644 --- a/fs/partitions/karma.c +++ b/fs/partitions/karma.c @@ -50,7 +50,7 @@ int karma_partition(struct parsed_partitions *state) } slot++; } - printk("\n"); + strlcat(state->pp_buf, "\n", PAGE_SIZE); put_dev_sector(sect); return 1; } diff --git a/fs/partitions/ldm.c b/fs/partitions/ldm.c index 648c9d8f3357..5bf8a04b5d9b 100644 --- a/fs/partitions/ldm.c +++ b/fs/partitions/ldm.c @@ -643,7 +643,7 @@ static bool ldm_create_data_partitions (struct parsed_partitions *pp, return false; } - printk (" [LDM]"); + strlcat(pp->pp_buf, " [LDM]", PAGE_SIZE); /* Create the data partitions */ list_for_each (item, &ldb->v_part) { @@ -658,7 +658,7 @@ static bool ldm_create_data_partitions (struct parsed_partitions *pp, part_num++; } - printk ("\n"); + strlcat(pp->pp_buf, "\n", PAGE_SIZE); return true; } diff --git a/fs/partitions/mac.c b/fs/partitions/mac.c index 74465ff7c263..68d6a216ee79 100644 --- a/fs/partitions/mac.c +++ b/fs/partitions/mac.c @@ -59,7 +59,7 @@ int mac_partition(struct parsed_partitions *state) put_dev_sector(sect); return 0; /* not a MacOS disk */ } - printk(" [mac]"); + strlcat(state->pp_buf, " [mac]", PAGE_SIZE); blocks_in_map = be32_to_cpu(part->map_count); for (blk = 1; blk <= blocks_in_map; ++blk) { int pos = blk * secsize; @@ -128,6 +128,6 @@ int mac_partition(struct parsed_partitions *state) #endif put_dev_sector(sect); - printk("\n"); + strlcat(state->pp_buf, "\n", PAGE_SIZE); return 1; } diff --git a/fs/partitions/msdos.c b/fs/partitions/msdos.c index 15bfb7b1e044..5f79a6677c69 100644 --- a/fs/partitions/msdos.c +++ b/fs/partitions/msdos.c @@ -213,10 +213,18 @@ static void parse_solaris_x86(struct parsed_partitions *state, put_dev_sector(sect); return; } - printk(" %s%d: name, origin); + { + char tmp[1 + BDEVNAME_SIZE + 10 + 11 + 1]; + + snprintf(tmp, sizeof(tmp), " %s%d: name, origin); + strlcat(state->pp_buf, tmp, PAGE_SIZE); + } if (le32_to_cpu(v->v_version) != 1) { - printk(" cannot handle version %d vtoc>\n", - le32_to_cpu(v->v_version)); + char tmp[64]; + + snprintf(tmp, sizeof(tmp), " cannot handle version %d vtoc>\n", + le32_to_cpu(v->v_version)); + strlcat(state->pp_buf, tmp, PAGE_SIZE); put_dev_sector(sect); return; } @@ -224,9 +232,12 @@ static void parse_solaris_x86(struct parsed_partitions *state, max_nparts = le16_to_cpu (v->v_nparts) > 8 ? SOLARIS_X86_NUMSLICE : 8; for (i=0; inextlimit; i++) { struct solaris_x86_slice *s = &v->v_slice[i]; + char tmp[3 + 10 + 1 + 1]; + if (s->s_size == 0) continue; - printk(" [s%d]", i); + snprintf(tmp, sizeof(tmp), " [s%d]", i); + strlcat(state->pp_buf, tmp, PAGE_SIZE); /* solaris partitions are relative to current MS-DOS * one; must add the offset of the current partition */ put_partition(state, state->next++, @@ -234,7 +245,7 @@ static void parse_solaris_x86(struct parsed_partitions *state, le32_to_cpu(s->s_size)); } put_dev_sector(sect); - printk(" >\n"); + strlcat(state->pp_buf, " >\n", PAGE_SIZE); #endif } @@ -250,6 +261,7 @@ static void parse_bsd(struct parsed_partitions *state, Sector sect; struct bsd_disklabel *l; struct bsd_partition *p; + char tmp[64]; l = read_part_sector(state, offset + 1, §); if (!l) @@ -258,7 +270,9 @@ static void parse_bsd(struct parsed_partitions *state, put_dev_sector(sect); return; } - printk(" %s%d: <%s:", state->name, origin, flavour); + + snprintf(tmp, sizeof(tmp), " %s%d: <%s:", state->name, origin, flavour); + strlcat(state->pp_buf, tmp, PAGE_SIZE); if (le16_to_cpu(l->d_npartitions) < max_partitions) max_partitions = le16_to_cpu(l->d_npartitions); @@ -275,16 +289,18 @@ static void parse_bsd(struct parsed_partitions *state, /* full parent partition, we have it already */ continue; if (offset > bsd_start || offset+size < bsd_start+bsd_size) { - printk("bad subpartition - ignored\n"); + strlcat(state->pp_buf, "bad subpartition - ignored\n", PAGE_SIZE); continue; } put_partition(state, state->next++, bsd_start, bsd_size); } put_dev_sector(sect); - if (le16_to_cpu(l->d_npartitions) > max_partitions) - printk(" (ignored %d more)", - le16_to_cpu(l->d_npartitions) - max_partitions); - printk(" >\n"); + if (le16_to_cpu(l->d_npartitions) > max_partitions) { + snprintf(tmp, sizeof(tmp), " (ignored %d more)", + le16_to_cpu(l->d_npartitions) - max_partitions); + strlcat(state->pp_buf, tmp, PAGE_SIZE); + } + strlcat(state->pp_buf, " >\n", PAGE_SIZE); } #endif @@ -333,7 +349,12 @@ static void parse_unixware(struct parsed_partitions *state, put_dev_sector(sect); return; } - printk(" %s%d: name, origin); + { + char tmp[1 + BDEVNAME_SIZE + 10 + 12 + 1]; + + snprintf(tmp, sizeof(tmp), " %s%d: name, origin); + strlcat(state->pp_buf, tmp, PAGE_SIZE); + } p = &l->vtoc.v_slice[1]; /* I omit the 0th slice as it is the same as whole disk. */ while (p - &l->vtoc.v_slice[0] < UNIXWARE_NUMSLICE) { @@ -347,7 +368,7 @@ static void parse_unixware(struct parsed_partitions *state, p++; } put_dev_sector(sect); - printk(" >\n"); + strlcat(state->pp_buf, " >\n", PAGE_SIZE); #endif } @@ -376,8 +397,10 @@ static void parse_minix(struct parsed_partitions *state, * the normal boot sector. */ if (msdos_magic_present (data + 510) && SYS_IND(p) == MINIX_PARTITION) { /* subpartition table present */ + char tmp[1 + BDEVNAME_SIZE + 10 + 9 + 1]; - printk(" %s%d: name, origin); + snprintf(tmp, sizeof(tmp), " %s%d: name, origin); + strlcat(state->pp_buf, tmp, PAGE_SIZE); for (i = 0; i < MINIX_NR_SUBPARTITIONS; i++, p++) { if (state->next == state->limit) break; @@ -386,7 +409,7 @@ static void parse_minix(struct parsed_partitions *state, put_partition(state, state->next++, start_sect(p), nr_sects(p)); } - printk(" >\n"); + strlcat(state->pp_buf, " >\n", PAGE_SIZE); } put_dev_sector(sect); #endif /* CONFIG_MINIX_SUBPARTITION */ @@ -425,7 +448,7 @@ int msdos_partition(struct parsed_partitions *state) if (aix_magic_present(state, data)) { put_dev_sector(sect); - printk( " [AIX]"); + strlcat(state->pp_buf, " [AIX]", PAGE_SIZE); return 0; } @@ -446,7 +469,7 @@ int msdos_partition(struct parsed_partitions *state) fb = (struct fat_boot_sector *) data; if (slot == 1 && fb->reserved && fb->fats && fat_valid_media(fb->media)) { - printk("\n"); + strlcat(state->pp_buf, "\n", PAGE_SIZE); put_dev_sector(sect); return 1; } else { @@ -491,21 +514,21 @@ int msdos_partition(struct parsed_partitions *state) n = min(size, max(sector_size, n)); put_partition(state, slot, start, n); - printk(" <"); + strlcat(state->pp_buf, " <", PAGE_SIZE); parse_extended(state, start, size); - printk(" >"); + strlcat(state->pp_buf, " >", PAGE_SIZE); continue; } put_partition(state, slot, start, size); if (SYS_IND(p) == LINUX_RAID_PARTITION) state->parts[slot].flags = ADDPART_FLAG_RAID; if (SYS_IND(p) == DM6_PARTITION) - printk("[DM]"); + strlcat(state->pp_buf, "[DM]", PAGE_SIZE); if (SYS_IND(p) == EZD_PARTITION) - printk("[EZD]"); + strlcat(state->pp_buf, "[EZD]", PAGE_SIZE); } - printk("\n"); + strlcat(state->pp_buf, "\n", PAGE_SIZE); /* second pass - output for each on a separate line */ p = (struct partition *) (0x1be + data); diff --git a/fs/partitions/osf.c b/fs/partitions/osf.c index fc22b85d436a..48cec7cbca17 100644 --- a/fs/partitions/osf.c +++ b/fs/partitions/osf.c @@ -72,7 +72,7 @@ int osf_partition(struct parsed_partitions *state) le32_to_cpu(partition->p_size)); slot++; } - printk("\n"); + strlcat(state->pp_buf, "\n", PAGE_SIZE); put_dev_sector(sect); return 1; } diff --git a/fs/partitions/sgi.c b/fs/partitions/sgi.c index 43b1df9aa16c..ea8a86dceaf4 100644 --- a/fs/partitions/sgi.c +++ b/fs/partitions/sgi.c @@ -76,7 +76,7 @@ int sgi_partition(struct parsed_partitions *state) } slot++; } - printk("\n"); + strlcat(state->pp_buf, "\n", PAGE_SIZE); put_dev_sector(sect); return 1; } diff --git a/fs/partitions/sun.c b/fs/partitions/sun.c index a32660e25f7f..b5b6fcfb3d36 100644 --- a/fs/partitions/sun.c +++ b/fs/partitions/sun.c @@ -116,7 +116,7 @@ int sun_partition(struct parsed_partitions *state) } slot++; } - printk("\n"); + strlcat(state->pp_buf, "\n", PAGE_SIZE); put_dev_sector(sect); return 1; } diff --git a/fs/partitions/sysv68.c b/fs/partitions/sysv68.c index 9030c864428e..9627ccffc1c4 100644 --- a/fs/partitions/sysv68.c +++ b/fs/partitions/sysv68.c @@ -54,6 +54,7 @@ int sysv68_partition(struct parsed_partitions *state) unsigned char *data; struct dkblk0 *b; struct slice *slice; + char tmp[64]; data = read_part_sector(state, 0, §); if (!data) @@ -73,7 +74,8 @@ int sysv68_partition(struct parsed_partitions *state) return -1; slices -= 1; /* last slice is the whole disk */ - printk("sysV68: %s(s%u)", state->name, slices); + snprintf(tmp, sizeof(tmp), "sysV68: %s(s%u)", state->name, slices); + strlcat(state->pp_buf, tmp, PAGE_SIZE); slice = (struct slice *)data; for (i = 0; i < slices; i++, slice++) { if (slot == state->limit) @@ -82,11 +84,12 @@ int sysv68_partition(struct parsed_partitions *state) put_partition(state, slot, be32_to_cpu(slice->blkoff), be32_to_cpu(slice->nblocks)); - printk("(s%u)", i); + snprintf(tmp, sizeof(tmp), "(s%u)", i); + strlcat(state->pp_buf, tmp, PAGE_SIZE); } slot++; } - printk("\n"); + strlcat(state->pp_buf, "\n", PAGE_SIZE); put_dev_sector(sect); return 1; } diff --git a/fs/partitions/ultrix.c b/fs/partitions/ultrix.c index db9eef260364..8dbaf9f77a99 100644 --- a/fs/partitions/ultrix.c +++ b/fs/partitions/ultrix.c @@ -39,7 +39,7 @@ int ultrix_partition(struct parsed_partitions *state) label->pt_part[i].pi_blkoff, label->pt_part[i].pi_nblocks); put_dev_sector(sect); - printk ("\n"); + strlcat(state->pp_buf, "\n", PAGE_SIZE); return 1; } else { put_dev_sector(sect); -- cgit v1.2.3 From bebf8cfaea1df1a104b993b995bb385e998a4dc8 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 10 Aug 2010 18:03:27 -0700 Subject: afs: destroy work queue on init failure We can clean up the work queue on this error path. This function is called from afs_init(). Signed-off-by: Dan Carpenter Acked-by: David Howells Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/afs/rxrpc.c | 1 + 1 file changed, 1 insertion(+) (limited to 'fs') diff --git a/fs/afs/rxrpc.c b/fs/afs/rxrpc.c index 67cf810e0fd6..654d8fdbf01f 100644 --- a/fs/afs/rxrpc.c +++ b/fs/afs/rxrpc.c @@ -100,6 +100,7 @@ int afs_open_socket(void) ret = kernel_bind(socket, (struct sockaddr *) &srx, sizeof(srx)); if (ret < 0) { sock_release(socket); + destroy_workqueue(afs_async_calls); _leave(" = %d [bind]", ret); return ret; } -- cgit v1.2.3 From a36517e9302b8396db24600bf9f67e4d1c441907 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Tue, 10 Aug 2010 18:03:32 -0700 Subject: fs/sysv: add v7 alias So that the module gets autoloaded when a v7 filesystem is mounted. Signed-off-by: Lubomir Rintel Cc: Christoph Hellwig Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/sysv/super.c | 1 + 1 file changed, 1 insertion(+) (limited to 'fs') diff --git a/fs/sysv/super.c b/fs/sysv/super.c index 0e44a6253352..2da3075aff78 100644 --- a/fs/sysv/super.c +++ b/fs/sysv/super.c @@ -559,4 +559,5 @@ static void __exit exit_sysv_fs(void) module_init(init_sysv_fs) module_exit(exit_sysv_fs) +MODULE_ALIAS("v7"); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 0bcaa65a56ab74003666cf741b0bfa1e9263a11c Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Tue, 10 Aug 2010 18:03:33 -0700 Subject: fs/sysv: v7: adjust sanity checks for some volumes Newly mkfs-ed filesystems from Seventh Edition have last modification time set to zero, but are otherwise perfectly valid. Also, tighten up other sanity checks to filter out most filesystems with [akpm@linux-foundation.org: coding-style fixes] Signed-off-by: Lubomir Rintel Cc: Christoph Hellwig Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/sysv/super.c | 6 ++++-- include/linux/sysv_fs.h | 11 +++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/sysv/super.c b/fs/sysv/super.c index 2da3075aff78..3785262b98e0 100644 --- a/fs/sysv/super.c +++ b/fs/sysv/super.c @@ -469,7 +469,7 @@ static int v7_fill_super(struct super_block *sb, void *data, int silent) v7sb = (struct v7_super_block *) bh->b_data; if (fs16_to_cpu(sbi, v7sb->s_nfree) > V7_NICFREE || fs16_to_cpu(sbi, v7sb->s_ninode) > V7_NICINOD || - fs32_to_cpu(sbi, v7sb->s_time) == 0) + fs32_to_cpu(sbi, v7sb->s_fsize) > V7_MAXSIZE) goto failed; /* plausibility check on root inode: it is a directory, @@ -479,7 +479,9 @@ static int v7_fill_super(struct super_block *sb, void *data, int silent) v7i = (struct sysv_inode *)(bh2->b_data + 64); if ((fs16_to_cpu(sbi, v7i->i_mode) & ~0777) != S_IFDIR || (fs32_to_cpu(sbi, v7i->i_size) == 0) || - (fs32_to_cpu(sbi, v7i->i_size) & 017) != 0) + (fs32_to_cpu(sbi, v7i->i_size) & 017) || + (fs32_to_cpu(sbi, v7i->i_size) > V7_NFILES * + sizeof(struct sysv_dir_entry))) goto failed; brelse(bh2); bh2 = NULL; diff --git a/include/linux/sysv_fs.h b/include/linux/sysv_fs.h index 96411306eec6..e47d6d90023d 100644 --- a/include/linux/sysv_fs.h +++ b/include/linux/sysv_fs.h @@ -148,6 +148,17 @@ struct v7_super_block { char s_fname[6]; /* file system name */ char s_fpack[6]; /* file system pack name */ }; +/* Constants to aid sanity checking */ +/* This is not a hard limit, nor enforced by v7 kernel. It's actually just + * the limit used by Seventh Edition's ls, though is high enough to assume + * that no reasonable file system would have that much entries in root + * directory. Thus, if we see anything higher, we just probably got the + * endiannes wrong. */ +#define V7_NFILES 1024 +/* The disk addresses are three-byte (despite direct block addresses being + * aligned word-wise in inode). If the most significant byte is non-zero, + * something is most likely wrong (not a filesystem, bad bytesex). */ +#define V7_MAXSIZE 0x00ffffff /* Coherent super-block data on disk */ #define COH_NICINOD 100 /* number of inode cache entries */ -- cgit v1.2.3 From ab654bab04be435a9671e0dcf49c18f9782dd838 Mon Sep 17 00:00:00 2001 From: Lubomir Rintel Date: Tue, 10 Aug 2010 18:03:34 -0700 Subject: fs/sysv/super.c: add support for non-PDP11 v7 filesystems This adds byte order autodetection (of PDP-11 and LE filesystems). No attempt is made to detect big-endian filesystems -- were there any? Tested with PDP-11 v7 filesystems and PC-IX maintenance floppy. [akpm@linux-foundation.org: coding-style fixes] Signed-off-by: Lubomir Rintel Cc: Christoph Hellwig Cc: Al Viro Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/sysv/super.c | 75 +++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 51 insertions(+), 24 deletions(-) (limited to 'fs') diff --git a/fs/sysv/super.c b/fs/sysv/super.c index 3785262b98e0..85359a8df605 100644 --- a/fs/sysv/super.c +++ b/fs/sysv/super.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "sysv.h" /* @@ -434,12 +435,46 @@ Ebadsize: goto failed; } -static int v7_fill_super(struct super_block *sb, void *data, int silent) +static int v7_sanity_check(struct super_block *sb, struct buffer_head *bh) { - struct sysv_sb_info *sbi; - struct buffer_head *bh, *bh2 = NULL; struct v7_super_block *v7sb; struct sysv_inode *v7i; + struct buffer_head *bh2; + struct sysv_sb_info *sbi; + + sbi = sb->s_fs_info; + + /* plausibility check on superblock */ + v7sb = (struct v7_super_block *) bh->b_data; + if (fs16_to_cpu(sbi, v7sb->s_nfree) > V7_NICFREE || + fs16_to_cpu(sbi, v7sb->s_ninode) > V7_NICINOD || + fs32_to_cpu(sbi, v7sb->s_fsize) > V7_MAXSIZE) + return 0; + + /* plausibility check on root inode: it is a directory, + with a nonzero size that is a multiple of 16 */ + bh2 = sb_bread(sb, 2); + if (bh2 == NULL) + return 0; + + v7i = (struct sysv_inode *)(bh2->b_data + 64); + if ((fs16_to_cpu(sbi, v7i->i_mode) & ~0777) != S_IFDIR || + (fs32_to_cpu(sbi, v7i->i_size) == 0) || + (fs32_to_cpu(sbi, v7i->i_size) & 017) || + (fs32_to_cpu(sbi, v7i->i_size) > V7_NFILES * + sizeof(struct sysv_dir_entry))) { + brelse(bh2); + return 0; + } + + brelse(bh2); + return 1; +} + +static int v7_fill_super(struct super_block *sb, void *data, int silent) +{ + struct sysv_sb_info *sbi; + struct buffer_head *bh; if (440 != sizeof (struct v7_super_block)) panic("V7 FS: bad super-block size"); @@ -453,7 +488,6 @@ static int v7_fill_super(struct super_block *sb, void *data, int silent) sbi->s_sb = sb; sbi->s_block_base = 0; sbi->s_type = FSTYPE_V7; - sbi->s_bytesex = BYTESEX_PDP; sb->s_fs_info = sbi; sb_set_blocksize(sb, 512); @@ -465,34 +499,27 @@ static int v7_fill_super(struct super_block *sb, void *data, int silent) goto failed; } - /* plausibility check on superblock */ - v7sb = (struct v7_super_block *) bh->b_data; - if (fs16_to_cpu(sbi, v7sb->s_nfree) > V7_NICFREE || - fs16_to_cpu(sbi, v7sb->s_ninode) > V7_NICINOD || - fs32_to_cpu(sbi, v7sb->s_fsize) > V7_MAXSIZE) - goto failed; + /* Try PDP-11 UNIX */ + sbi->s_bytesex = BYTESEX_PDP; + if (v7_sanity_check(sb, bh)) + goto detected; - /* plausibility check on root inode: it is a directory, - with a nonzero size that is a multiple of 16 */ - if ((bh2 = sb_bread(sb, 2)) == NULL) - goto failed; - v7i = (struct sysv_inode *)(bh2->b_data + 64); - if ((fs16_to_cpu(sbi, v7i->i_mode) & ~0777) != S_IFDIR || - (fs32_to_cpu(sbi, v7i->i_size) == 0) || - (fs32_to_cpu(sbi, v7i->i_size) & 017) || - (fs32_to_cpu(sbi, v7i->i_size) > V7_NFILES * - sizeof(struct sysv_dir_entry))) - goto failed; - brelse(bh2); - bh2 = NULL; + /* Try PC/IX, v7/x86 */ + sbi->s_bytesex = BYTESEX_LE; + if (v7_sanity_check(sb, bh)) + goto detected; + goto failed; + +detected: sbi->s_bh1 = bh; sbi->s_bh2 = bh; if (complete_read_super(sb, silent, 1)) return 0; failed: - brelse(bh2); + printk(KERN_ERR "VFS: could not find a valid V7 on %s.\n", + sb->s_id); brelse(bh); kfree(sbi); return -EINVAL; -- cgit v1.2.3 From 3694b91a59c3bc784735574878d36652dfd623c2 Mon Sep 17 00:00:00 2001 From: Suresh Jayaraman Date: Thu, 5 Aug 2010 18:52:21 +0530 Subject: cifs: update README to include details about 'fsc' option Signed-off-by: Suresh Jayaraman Signed-off-by: Steve French --- fs/cifs/README | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'fs') diff --git a/fs/cifs/README b/fs/cifs/README index a7081eeeb85d..7099a526f775 100644 --- a/fs/cifs/README +++ b/fs/cifs/README @@ -301,6 +301,16 @@ A partial list of the supported mount options follows: gid Set the default gid for inodes (similar to above). file_mode If CIFS Unix extensions are not supported by the server this overrides the default mode for file inodes. + fsc Enable local disk caching using FS-Cache (off by default). This + option could be useful to improve performance on a slow link, + heavily loaded server and/or network where reading from the + disk is faster than reading from the server (over the network). + This could also impact scalability positively as the + number of calls to the server are reduced. However, local + caching is not suitable for all workloads for e.g. read-once + type workloads. So, you need to consider carefully your + workload/scenario before using this option. Currently, local + disk caching is functional for CIFS files opened as read-only. dir_mode If CIFS Unix extensions are not supported by the server this overrides the default mode for directory inodes. port attempt to contact the server on this tcp port, before -- cgit v1.2.3 From c2e8139c9f797baa46515af6d350c51823736cbb Mon Sep 17 00:00:00 2001 From: Bryan Schumaker Date: Wed, 11 Aug 2010 09:37:53 +0100 Subject: NFS: Use kernel DNS resolver [ver #2] Use the kernel DNS resolver to translate hostnames to IP addresses. Create a new config option to choose between the legacy DNS resolver and the new resolver. Signed-off-by: Bryan Schumaker Acked-by: Trond Myklebust Signed-off-by: David Howells Signed-off-by: Steve French --- fs/nfs/Kconfig | 16 ++++++++++++++++ fs/nfs/dns_resolve.c | 24 ++++++++++++++++++++++++ fs/nfs/dns_resolve.h | 12 ++++++++++++ 3 files changed, 52 insertions(+) (limited to 'fs') diff --git a/fs/nfs/Kconfig b/fs/nfs/Kconfig index cc1bb33b59b8..c5bbdca13ac2 100644 --- a/fs/nfs/Kconfig +++ b/fs/nfs/Kconfig @@ -100,3 +100,19 @@ config NFS_FSCACHE help Say Y here if you want NFS data to be cached locally on disc through the general filesystem cache manager + +config NFS_USE_LEGACY_DNS + bool "Use the legacy NFS DNS resolver" + depends on NFS_V4 + help + The kernel now provides a method for translating a host name into an + IP address. Select Y here if you would rather use your own DNS + resolver script. + + If unsure, say N + +config NFS_USE_KERNEL_DNS + bool + depends on NFS_V4 && !NFS_USE_LEGACY_DNS + select DNS_RESOLVER + default y diff --git a/fs/nfs/dns_resolve.c b/fs/nfs/dns_resolve.c index 76fd235d0024..dba50a5625db 100644 --- a/fs/nfs/dns_resolve.c +++ b/fs/nfs/dns_resolve.c @@ -6,6 +6,29 @@ * Resolves DNS hostnames into valid ip addresses */ +#ifdef CONFIG_NFS_USE_KERNEL_DNS + +#include +#include + +ssize_t nfs_dns_resolve_name(char *name, size_t namelen, + struct sockaddr *sa, size_t salen) +{ + ssize_t ret; + char *ip_addr = NULL; + int ip_len; + + ip_len = dns_query(NULL, name, namelen, NULL, &ip_addr, NULL); + if (ip_len > 0) + ret = rpc_pton(ip_addr, ip_len, sa, salen); + else + ret = -ESRCH; + kfree(ip_addr); + return ret; +} + +#else + #include #include #include @@ -346,3 +369,4 @@ void nfs_dns_resolver_destroy(void) nfs_cache_unregister(&nfs_dns_resolve); } +#endif diff --git a/fs/nfs/dns_resolve.h b/fs/nfs/dns_resolve.h index a3f0938babf7..199bb5543a91 100644 --- a/fs/nfs/dns_resolve.h +++ b/fs/nfs/dns_resolve.h @@ -6,8 +6,20 @@ #define NFS_DNS_HOSTNAME_MAXLEN (128) + +#ifdef CONFIG_NFS_USE_KERNEL_DNS +static inline int nfs_dns_resolver_init(void) +{ + return 0; +} + +static inline void nfs_dns_resolver_destroy(void) +{} +#else extern int nfs_dns_resolver_init(void); extern void nfs_dns_resolver_destroy(void); +#endif + extern ssize_t nfs_dns_resolve_name(char *name, size_t namelen, struct sockaddr *sa, size_t salen); -- cgit v1.2.3 From 4a2d789267e00b5a1175ecd2ddefcc78b83fbf09 Mon Sep 17 00:00:00 2001 From: Wang Lei Date: Wed, 11 Aug 2010 09:37:58 +0100 Subject: DNS: If the DNS server returns an error, allow that to be cached [ver #2] If the DNS server returns an error, allow that to be cached in the DNS resolver key in lieu of a value. Userspace passes the desired error number as an option in the payload: "#dnserror=" Userspace must map h_errno from the name resolution routines to an appropriate Linux error before passing it up. Something like the following mapping is recommended: [HOST_NOT_FOUND] = ENODATA, [TRY_AGAIN] = EAGAIN, [NO_RECOVERY] = ECONNREFUSED, [NO_DATA] = ENODATA, in lieu of Linux errors specifically for representing name service errors. The filesystem must map these errors appropropriately before passing them to userspace. AFS is made to map ENODATA and EAGAIN to EDESTADDRREQ for the return to userspace; ECONNREFUSED is allowed to stand as is. The error can be seen in /proc/keys as a negative number after the description of the key. Compare, for example, the following key entries: 2f97238c I--Q-- 1 53s 3f010000 0 0 dns_resol afsdb:grand.centrall.org: -61 338bfbbe I--Q-- 1 59m 3f010000 0 0 dns_resol afsdb:grand.central.org: 37 If the error option is supplied in the payload, the main part of the payload is discarded. The key should have an expiry time set by userspace. Signed-off-by: Wang Lei Signed-off-by: David Howells Acked-by: Jeff Layton Signed-off-by: Steve French --- fs/afs/cell.c | 4 ++ net/dns_resolver/dns_key.c | 92 +++++++++++++++++++++++++++++++++++++++++--- net/dns_resolver/dns_query.c | 5 +++ 3 files changed, 96 insertions(+), 5 deletions(-) (limited to 'fs') diff --git a/fs/afs/cell.c b/fs/afs/cell.c index ffea35c63879..d0765883430e 100644 --- a/fs/afs/cell.c +++ b/fs/afs/cell.c @@ -73,6 +73,10 @@ static struct afs_cell *afs_cell_alloc(const char *name, char *vllist) if (!vllist || strlen(vllist) < 7) { ret = dns_query("afsdb", name, namelen, "ipv4", &dvllist, NULL); if (ret < 0) { + if (ret == -ENODATA || ret == -EAGAIN || ret == -ENOKEY) + /* translate these errors into something + * userspace might understand */ + ret = -EDESTADDRREQ; _leave(" = %d", ret); return ERR_PTR(ret); } diff --git a/net/dns_resolver/dns_key.c b/net/dns_resolver/dns_key.c index 400a04d5c9a1..739435a6af39 100644 --- a/net/dns_resolver/dns_key.c +++ b/net/dns_resolver/dns_key.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include "internal.h" @@ -43,6 +44,8 @@ MODULE_PARM_DESC(debug, "DNS Resolver debugging mask"); const struct cred *dns_resolver_cache; +#define DNS_ERRORNO_OPTION "dnserror" + /* * Instantiate a user defined key for dns_resolver. * @@ -59,9 +62,10 @@ static int dns_resolver_instantiate(struct key *key, const void *_data, size_t datalen) { struct user_key_payload *upayload; + unsigned long derrno; int ret; size_t result_len = 0; - const char *data = _data, *opt; + const char *data = _data, *end, *opt; kenter("%%%d,%s,'%s',%zu", key->serial, key->description, data, datalen); @@ -71,13 +75,77 @@ dns_resolver_instantiate(struct key *key, const void *_data, size_t datalen) datalen--; /* deal with any options embedded in the data */ + end = data + datalen; opt = memchr(data, '#', datalen); if (!opt) { - kdebug("no options currently supported"); - return -EINVAL; + /* no options: the entire data is the result */ + kdebug("no options"); + result_len = datalen; + } else { + const char *next_opt; + + result_len = opt - data; + opt++; + kdebug("options: '%s'", opt); + do { + const char *eq; + int opt_len, opt_nlen, opt_vlen, tmp; + + next_opt = memchr(opt, '#', end - opt) ?: end; + opt_len = next_opt - opt; + if (!opt_len) { + printk(KERN_WARNING + "Empty option to dns_resolver key %d\n", + key->serial); + return -EINVAL; + } + + eq = memchr(opt, '=', opt_len) ?: end; + opt_nlen = eq - opt; + eq++; + opt_vlen = next_opt - eq; /* will be -1 if no value */ + + tmp = opt_vlen >= 0 ? opt_vlen : 0; + kdebug("option '%*.*s' val '%*.*s'", + opt_nlen, opt_nlen, opt, tmp, tmp, eq); + + /* see if it's an error number representing a DNS error + * that's to be recorded as the result in this key */ + if (opt_nlen == sizeof(DNS_ERRORNO_OPTION) - 1 && + memcmp(opt, DNS_ERRORNO_OPTION, opt_nlen) == 0) { + kdebug("dns error number option"); + if (opt_vlen <= 0) + goto bad_option_value; + + ret = strict_strtoul(eq, 10, &derrno); + if (ret < 0) + goto bad_option_value; + + if (derrno < 1 || derrno > 511) + goto bad_option_value; + + kdebug("dns error no. = %lu", derrno); + key->type_data.x[0] = -derrno; + continue; + } + + bad_option_value: + printk(KERN_WARNING + "Option '%*.*s' to dns_resolver key %d:" + " bad/missing value\n", + opt_nlen, opt_nlen, opt, key->serial); + return -EINVAL; + } while (opt = next_opt + 1, opt < end); + } + + /* don't cache the result if we're caching an error saying there's no + * result */ + if (key->type_data.x[0]) { + kleave(" = 0 [h_error %ld]", key->type_data.x[0]); + return 0; } - result_len = datalen; + kdebug("store result"); ret = key_payload_reserve(key, result_len); if (ret < 0) return -EINVAL; @@ -135,13 +203,27 @@ no_match: return ret; } +/* + * Describe a DNS key + */ +static void dns_resolver_describe(const struct key *key, struct seq_file *m) +{ + int err = key->type_data.x[0]; + + seq_puts(m, key->description); + if (err) + seq_printf(m, ": %d", err); + else + seq_printf(m, ": %u", key->datalen); +} + struct key_type key_type_dns_resolver = { .name = "dns_resolver", .instantiate = dns_resolver_instantiate, .match = dns_resolver_match, .revoke = user_revoke, .destroy = user_destroy, - .describe = user_describe, + .describe = dns_resolver_describe, .read = user_read, }; diff --git a/net/dns_resolver/dns_query.c b/net/dns_resolver/dns_query.c index 03d5255f5cf2..c32be292c7e3 100644 --- a/net/dns_resolver/dns_query.c +++ b/net/dns_resolver/dns_query.c @@ -136,6 +136,11 @@ int dns_query(const char *type, const char *name, size_t namelen, if (ret < 0) goto put; + /* If the DNS server gave an error, return that to the caller */ + ret = rkey->type_data.x[0]; + if (ret) + goto put; + upayload = rcu_dereference_protected(rkey->payload.data, lockdep_is_held(&rkey->sem)); len = upayload->datalen; -- cgit v1.2.3 From bec5eb6141308a30a73682330cb045a40e442b8c Mon Sep 17 00:00:00 2001 From: wanglei Date: Wed, 11 Aug 2010 09:38:04 +0100 Subject: AFS: Implement an autocell mount capability [ver #2] Implement the ability for the root directory of a mounted AFS filesystem to accept lookups of arbitrary directory names, to interpet the names as the names of cells, to look the cell names up in the DNS for AFSDB records and to mount the root.cell volume of the nominated cell on the pseudo-directory created by lookup. This facility is requested by passing: -o autocell to the mountpoint for which this is desired, usually the /afs mount. To use this facility, a DNS upcall program is required for AFSDB records. This can be obtained from: http://people.redhat.com/~dhowells/afs/dns.afsdb.c It should be compiled with -lresolv and -lkeyutils and installed as, say: /usr/sbin/dns.afsdb Then the following line needs to be added to /sbin/request-key.conf: create dns_resolver afsdb:* * /usr/sbin/dns.afsdb %k This can be tested by mounting AFS, say: insmod dns_resolver.ko insmod af-rxrpc.ko insmod kafs.ko rootcell=grand.central.org mount -t afs "#grand.central.org:root.cell." /afs -o autocell and doing: ls /afs/grand.central.org/ which should show: archive/ cvs/ doc/ local/ project/ service/ software/ user/ www/ if it works. Signed-off-by: Wang Lei Signed-off-by: David Howells Signed-off-by: Steve French --- fs/afs/cell.c | 52 ++++++++++++++++++++++++--------- fs/afs/dir.c | 47 ++++++++++++++++++++++++++++-- fs/afs/inode.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ fs/afs/internal.h | 11 +++++-- fs/afs/mntpt.c | 78 ++++++++++++++++++++++++++++++++++--------------- fs/afs/proc.c | 2 +- fs/afs/super.c | 20 ++++++++++--- 7 files changed, 250 insertions(+), 46 deletions(-) (limited to 'fs') diff --git a/fs/afs/cell.c b/fs/afs/cell.c index d0765883430e..0d5eeadf6121 100644 --- a/fs/afs/cell.c +++ b/fs/afs/cell.c @@ -31,21 +31,20 @@ static struct afs_cell *afs_cell_root; * allocate a cell record and fill in its name, VL server address list and * allocate an anonymous key */ -static struct afs_cell *afs_cell_alloc(const char *name, char *vllist) +static struct afs_cell *afs_cell_alloc(const char *name, unsigned namelen, + char *vllist) { struct afs_cell *cell; struct key *key; - size_t namelen; char keyname[4 + AFS_MAXCELLNAME + 1], *cp, *dp, *next; char *dvllist = NULL, *_vllist = NULL; char delimiter = ':'; int ret; - _enter("%s,%s", name, vllist); + _enter("%*.*s,%s", namelen, namelen, name ?: "", vllist); BUG_ON(!name); /* TODO: want to look up "this cell" in the cache */ - namelen = strlen(name); if (namelen > AFS_MAXCELLNAME) { _leave(" = -ENAMETOOLONG"); return ERR_PTR(-ENAMETOOLONG); @@ -142,26 +141,29 @@ error: } /* - * create a cell record - * - "name" is the name of the cell - * - "vllist" is a colon separated list of IP addresses in "a.b.c.d" format + * afs_cell_crate() - create a cell record + * @name: is the name of the cell. + * @namsesz: is the strlen of the cell name. + * @vllist: is a colon separated list of IP addresses in "a.b.c.d" format. + * @retref: is T to return the cell reference when the cell exists. */ -struct afs_cell *afs_cell_create(const char *name, char *vllist) +struct afs_cell *afs_cell_create(const char *name, unsigned namesz, + char *vllist, bool retref) { struct afs_cell *cell; int ret; - _enter("%s,%s", name, vllist); + _enter("%*.*s,%s", namesz, namesz, name ?: "", vllist); down_write(&afs_cells_sem); read_lock(&afs_cells_lock); list_for_each_entry(cell, &afs_cells, link) { - if (strcasecmp(cell->name, name) == 0) + if (strncasecmp(cell->name, name, namesz) == 0) goto duplicate_name; } read_unlock(&afs_cells_lock); - cell = afs_cell_alloc(name, vllist); + cell = afs_cell_alloc(name, namesz, vllist); if (IS_ERR(cell)) { _leave(" = %ld", PTR_ERR(cell)); up_write(&afs_cells_sem); @@ -201,8 +203,18 @@ error: return ERR_PTR(ret); duplicate_name: + if (retref && !IS_ERR(cell)) + afs_get_cell(cell); + read_unlock(&afs_cells_lock); up_write(&afs_cells_sem); + + if (retref) { + _leave(" = %p", cell); + return cell; + } + + _leave(" = -EEXIST"); return ERR_PTR(-EEXIST); } @@ -233,7 +245,7 @@ int afs_cell_init(char *rootcell) *cp++ = 0; /* allocate a cell record for the root cell */ - new_root = afs_cell_create(rootcell, cp); + new_root = afs_cell_create(rootcell, strlen(rootcell), cp, false); if (IS_ERR(new_root)) { _leave(" = %ld", PTR_ERR(new_root)); return PTR_ERR(new_root); @@ -253,11 +265,12 @@ int afs_cell_init(char *rootcell) /* * lookup a cell record */ -struct afs_cell *afs_cell_lookup(const char *name, unsigned namesz) +struct afs_cell *afs_cell_lookup(const char *name, unsigned namesz, + bool dns_cell) { struct afs_cell *cell; - _enter("\"%*.*s\",", namesz, namesz, name ? name : ""); + _enter("\"%*.*s\",", namesz, namesz, name ?: ""); down_read(&afs_cells_sem); read_lock(&afs_cells_lock); @@ -271,6 +284,8 @@ struct afs_cell *afs_cell_lookup(const char *name, unsigned namesz) } } cell = ERR_PTR(-ENOENT); + if (dns_cell) + goto create_cell; found: ; } else { @@ -293,6 +308,15 @@ struct afs_cell *afs_cell_lookup(const char *name, unsigned namesz) up_read(&afs_cells_sem); _leave(" = %p", cell); return cell; + +create_cell: + read_unlock(&afs_cells_lock); + up_read(&afs_cells_sem); + + cell = afs_cell_create(name, namesz, NULL, true); + + _leave(" = %p", cell); + return cell; } #if 0 diff --git a/fs/afs/dir.c b/fs/afs/dir.c index b42d5cc1d6d2..0d38c09bd55e 100644 --- a/fs/afs/dir.c +++ b/fs/afs/dir.c @@ -476,6 +476,40 @@ static int afs_do_lookup(struct inode *dir, struct dentry *dentry, return 0; } +/* + * Try to auto mount the mountpoint with pseudo directory, if the autocell + * operation is setted. + */ +static struct inode *afs_try_auto_mntpt( + int ret, struct dentry *dentry, struct inode *dir, struct key *key, + struct afs_fid *fid) +{ + const char *devname = dentry->d_name.name; + struct afs_vnode *vnode = AFS_FS_I(dir); + struct inode *inode; + + _enter("%d, %p{%s}, {%x:%u}, %p", + ret, dentry, devname, vnode->fid.vid, vnode->fid.vnode, key); + + if (ret != -ENOENT || + !test_bit(AFS_VNODE_AUTOCELL, &vnode->flags)) + goto out; + + inode = afs_iget_autocell(dir, devname, strlen(devname), key); + if (IS_ERR(inode)) { + ret = PTR_ERR(inode); + goto out; + } + + *fid = AFS_FS_I(inode)->fid; + _leave("= %p", inode); + return inode; + +out: + _leave("= %d", ret); + return ERR_PTR(ret); +} + /* * look up an entry in a directory */ @@ -520,6 +554,13 @@ static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry, ret = afs_do_lookup(dir, dentry, &fid, key); if (ret < 0) { + inode = afs_try_auto_mntpt(ret, dentry, dir, key, &fid); + if (!IS_ERR(inode)) { + key_put(key); + goto success; + } + + ret = PTR_ERR(inode); key_put(key); if (ret == -ENOENT) { d_add(dentry, NULL); @@ -539,6 +580,7 @@ static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry, return ERR_CAST(inode); } +success: dentry->d_op = &afs_fs_dentry_operations; d_add(dentry, inode); @@ -696,8 +738,9 @@ static int afs_d_delete(struct dentry *dentry) goto zap; if (dentry->d_inode && - test_bit(AFS_VNODE_DELETED, &AFS_FS_I(dentry->d_inode)->flags)) - goto zap; + (test_bit(AFS_VNODE_DELETED, &AFS_FS_I(dentry->d_inode)->flags) || + test_bit(AFS_VNODE_PSEUDODIR, &AFS_FS_I(dentry->d_inode)->flags))) + goto zap; _leave(" = 0 [keep]"); return 0; diff --git a/fs/afs/inode.c b/fs/afs/inode.c index 320ffef11574..0747339011c3 100644 --- a/fs/afs/inode.c +++ b/fs/afs/inode.c @@ -19,6 +19,8 @@ #include #include #include +#include +#include #include "internal.h" struct afs_iget_data { @@ -101,6 +103,16 @@ static int afs_iget5_test(struct inode *inode, void *opaque) inode->i_version == data->fid.unique; } +/* + * iget5() comparator for inode created by autocell operations + * + * These pseudo inodes don't match anything. + */ +static int afs_iget5_autocell_test(struct inode *inode, void *opaque) +{ + return 0; +} + /* * iget5() inode initialiser */ @@ -117,6 +129,67 @@ static int afs_iget5_set(struct inode *inode, void *opaque) return 0; } +/* + * inode retrieval for autocell + */ +struct inode *afs_iget_autocell(struct inode *dir, const char *dev_name, + int namesz, struct key *key) +{ + struct afs_iget_data data; + struct afs_super_info *as; + struct afs_vnode *vnode; + struct super_block *sb; + struct inode *inode; + static atomic_t afs_autocell_ino; + + _enter("{%x:%u},%*.*s,", + AFS_FS_I(dir)->fid.vid, AFS_FS_I(dir)->fid.vnode, + namesz, namesz, dev_name ?: ""); + + sb = dir->i_sb; + as = sb->s_fs_info; + data.volume = as->volume; + data.fid.vid = as->volume->vid; + data.fid.unique = 0; + data.fid.vnode = 0; + + inode = iget5_locked(sb, atomic_inc_return(&afs_autocell_ino), + afs_iget5_autocell_test, afs_iget5_set, + &data); + if (!inode) { + _leave(" = -ENOMEM"); + return ERR_PTR(-ENOMEM); + } + + _debug("GOT INODE %p { ino=%lu, vl=%x, vn=%x, u=%x }", + inode, inode->i_ino, data.fid.vid, data.fid.vnode, + data.fid.unique); + + vnode = AFS_FS_I(inode); + + /* there shouldn't be an existing inode */ + BUG_ON(!(inode->i_state & I_NEW)); + + inode->i_size = 0; + inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO; + inode->i_op = &afs_autocell_inode_operations; + inode->i_nlink = 2; + inode->i_uid = 0; + inode->i_gid = 0; + inode->i_ctime.tv_sec = get_seconds(); + inode->i_ctime.tv_nsec = 0; + inode->i_atime = inode->i_mtime = inode->i_ctime; + inode->i_blocks = 0; + inode->i_version = 0; + inode->i_generation = 0; + + set_bit(AFS_VNODE_PSEUDODIR, &vnode->flags); + inode->i_flags |= S_NOATIME; + unlock_new_inode(inode); + _leave(" = %p", inode); + return inode; +} + /* * inode retrieval */ @@ -313,6 +386,19 @@ int afs_getattr(struct vfsmount *mnt, struct dentry *dentry, return 0; } +/* + * discard an AFS inode + */ +int afs_drop_inode(struct inode *inode) +{ + _enter(""); + + if (test_bit(AFS_VNODE_PSEUDODIR, &AFS_FS_I(inode)->flags)) + return generic_delete_inode(inode); + else + return generic_drop_inode(inode); +} + /* * clear an AFS inode */ diff --git a/fs/afs/internal.h b/fs/afs/internal.h index 8679089ce9a1..ce12a2b06f8f 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -42,6 +42,7 @@ typedef enum { struct afs_mount_params { bool rwpath; /* T if the parent should be considered R/W */ bool force; /* T to force cell type */ + bool autocell; /* T if set auto mount operation */ afs_voltype_t type; /* type of volume requested */ int volnamesz; /* size of volume name */ const char *volname; /* name of volume to mount */ @@ -358,6 +359,8 @@ struct afs_vnode { #define AFS_VNODE_READLOCKED 7 /* set if vnode is read-locked on the server */ #define AFS_VNODE_WRITELOCKED 8 /* set if vnode is write-locked on the server */ #define AFS_VNODE_UNLOCKING 9 /* set if vnode is being unlocked on the server */ +#define AFS_VNODE_AUTOCELL 10 /* set if Vnode is an auto mount point */ +#define AFS_VNODE_PSEUDODIR 11 /* set if Vnode is a pseudo directory */ long acl_order; /* ACL check count (callback break count) */ @@ -468,8 +471,8 @@ extern struct list_head afs_proc_cells; #define afs_get_cell(C) do { atomic_inc(&(C)->usage); } while(0) extern int afs_cell_init(char *); -extern struct afs_cell *afs_cell_create(const char *, char *); -extern struct afs_cell *afs_cell_lookup(const char *, unsigned); +extern struct afs_cell *afs_cell_create(const char *, unsigned, char *, bool); +extern struct afs_cell *afs_cell_lookup(const char *, unsigned, bool); extern struct afs_cell *afs_grab_cell(struct afs_cell *); extern void afs_put_cell(struct afs_cell *); extern void afs_cell_purge(void); @@ -558,6 +561,8 @@ extern int afs_fs_release_lock(struct afs_server *, struct key *, /* * inode.c */ +extern struct inode *afs_iget_autocell(struct inode *, const char *, int, + struct key *); extern struct inode *afs_iget(struct super_block *, struct key *, struct afs_fid *, struct afs_file_status *, struct afs_callback *); @@ -566,6 +571,7 @@ extern int afs_validate(struct afs_vnode *, struct key *); extern int afs_getattr(struct vfsmount *, struct dentry *, struct kstat *); extern int afs_setattr(struct dentry *, struct iattr *); extern void afs_evict_inode(struct inode *); +extern int afs_drop_inode(struct inode *); /* * main.c @@ -581,6 +587,7 @@ extern int afs_abort_to_error(u32); * mntpt.c */ extern const struct inode_operations afs_mntpt_inode_operations; +extern const struct inode_operations afs_autocell_inode_operations; extern const struct file_operations afs_mntpt_file_operations; extern int afs_mntpt_check_symlink(struct afs_vnode *, struct key *); diff --git a/fs/afs/mntpt.c b/fs/afs/mntpt.c index a9e23039ea34..6d552686c498 100644 --- a/fs/afs/mntpt.c +++ b/fs/afs/mntpt.c @@ -38,6 +38,11 @@ const struct inode_operations afs_mntpt_inode_operations = { .getattr = afs_getattr, }; +const struct inode_operations afs_autocell_inode_operations = { + .follow_link = afs_mntpt_follow_link, + .getattr = afs_getattr, +}; + static LIST_HEAD(afs_vfsmounts); static DECLARE_DELAYED_WORK(afs_mntpt_expiry_timer, afs_mntpt_expiry_timed_out); @@ -136,20 +141,16 @@ static struct vfsmount *afs_mntpt_do_automount(struct dentry *mntpt) { struct afs_super_info *super; struct vfsmount *mnt; + struct afs_vnode *vnode; struct page *page; - size_t size; - char *buf, *devname, *options; + char *devname, *options; + bool rwpath = false; int ret; _enter("{%s}", mntpt->d_name.name); BUG_ON(!mntpt->d_inode); - ret = -EINVAL; - size = mntpt->d_inode->i_size; - if (size > PAGE_SIZE - 1) - goto error_no_devname; - ret = -ENOMEM; devname = (char *) get_zeroed_page(GFP_KERNEL); if (!devname) @@ -159,28 +160,59 @@ static struct vfsmount *afs_mntpt_do_automount(struct dentry *mntpt) if (!options) goto error_no_options; - /* read the contents of the AFS special symlink */ - page = read_mapping_page(mntpt->d_inode->i_mapping, 0, NULL); - if (IS_ERR(page)) { - ret = PTR_ERR(page); - goto error_no_page; + vnode = AFS_FS_I(mntpt->d_inode); + if (test_bit(AFS_VNODE_PSEUDODIR, &vnode->flags)) { + /* if the directory is a pseudo directory, use the d_name */ + static const char afs_root_cell[] = ":root.cell."; + unsigned size = mntpt->d_name.len; + + ret = -ENOENT; + if (size < 2 || size > AFS_MAXCELLNAME) + goto error_no_page; + + if (mntpt->d_name.name[0] == '.') { + devname[0] = '#'; + memcpy(devname + 1, mntpt->d_name.name, size - 1); + memcpy(devname + size, afs_root_cell, + sizeof(afs_root_cell)); + rwpath = true; + } else { + devname[0] = '%'; + memcpy(devname + 1, mntpt->d_name.name, size); + memcpy(devname + size + 1, afs_root_cell, + sizeof(afs_root_cell)); + } + } else { + /* read the contents of the AFS special symlink */ + loff_t size = i_size_read(mntpt->d_inode); + char *buf; + + ret = -EINVAL; + if (size > PAGE_SIZE - 1) + goto error_no_page; + + page = read_mapping_page(mntpt->d_inode->i_mapping, 0, NULL); + if (IS_ERR(page)) { + ret = PTR_ERR(page); + goto error_no_page; + } + + ret = -EIO; + if (PageError(page)) + goto error; + + buf = kmap_atomic(page, KM_USER0); + memcpy(devname, buf, size); + kunmap_atomic(buf, KM_USER0); + page_cache_release(page); + page = NULL; } - ret = -EIO; - if (PageError(page)) - goto error; - - buf = kmap_atomic(page, KM_USER0); - memcpy(devname, buf, size); - kunmap_atomic(buf, KM_USER0); - page_cache_release(page); - page = NULL; - /* work out what options we want */ super = AFS_FS_S(mntpt->d_sb); memcpy(options, "cell=", 5); strcpy(options + 5, super->volume->cell->name); - if (super->volume->type == AFSVL_RWVOL) + if (super->volume->type == AFSVL_RWVOL || rwpath) strcat(options, ",rwpath"); /* try and do the mount */ diff --git a/fs/afs/proc.c b/fs/afs/proc.c index 852739d262a9..096b23f821a1 100644 --- a/fs/afs/proc.c +++ b/fs/afs/proc.c @@ -294,7 +294,7 @@ static ssize_t afs_proc_cells_write(struct file *file, const char __user *buf, if (strcmp(kbuf, "add") == 0) { struct afs_cell *cell; - cell = afs_cell_create(name, args); + cell = afs_cell_create(name, strlen(name), args, false); if (IS_ERR(cell)) { ret = PTR_ERR(cell); goto done; diff --git a/fs/afs/super.c b/fs/afs/super.c index 9cf80f02da16..77e1e5a61154 100644 --- a/fs/afs/super.c +++ b/fs/afs/super.c @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -48,6 +49,7 @@ struct file_system_type afs_fs_type = { static const struct super_operations afs_super_ops = { .statfs = afs_statfs, .alloc_inode = afs_alloc_inode, + .drop_inode = afs_drop_inode, .destroy_inode = afs_destroy_inode, .evict_inode = afs_evict_inode, .put_super = afs_put_super, @@ -62,12 +64,14 @@ enum { afs_opt_cell, afs_opt_rwpath, afs_opt_vol, + afs_opt_autocell, }; static const match_table_t afs_options_list = { { afs_opt_cell, "cell=%s" }, { afs_opt_rwpath, "rwpath" }, { afs_opt_vol, "vol=%s" }, + { afs_opt_autocell, "autocell" }, { afs_no_opt, NULL }, }; @@ -151,7 +155,8 @@ static int afs_parse_options(struct afs_mount_params *params, switch (token) { case afs_opt_cell: cell = afs_cell_lookup(args[0].from, - args[0].to - args[0].from); + args[0].to - args[0].from, + false); if (IS_ERR(cell)) return PTR_ERR(cell); afs_put_cell(params->cell); @@ -166,6 +171,10 @@ static int afs_parse_options(struct afs_mount_params *params, *devname = args[0].from; break; + case afs_opt_autocell: + params->autocell = 1; + break; + default: printk(KERN_ERR "kAFS:" " Unknown or invalid mount option: '%s'\n", p); @@ -252,10 +261,10 @@ static int afs_parse_device_name(struct afs_mount_params *params, /* lookup the cell record */ if (cellname || !params->cell) { - cell = afs_cell_lookup(cellname, cellnamesz); + cell = afs_cell_lookup(cellname, cellnamesz, true); if (IS_ERR(cell)) { - printk(KERN_ERR "kAFS: unable to lookup cell '%s'\n", - cellname ?: ""); + printk(KERN_ERR "kAFS: unable to lookup cell '%*.*s'\n", + cellnamesz, cellnamesz, cellname ?: ""); return PTR_ERR(cell); } afs_put_cell(params->cell); @@ -321,6 +330,9 @@ static int afs_fill_super(struct super_block *sb, void *data) if (IS_ERR(inode)) goto error_inode; + if (params->autocell) + set_bit(AFS_VNODE_AUTOCELL, &AFS_FS_I(inode)->flags); + ret = -ENOMEM; root = d_alloc_root(inode); if (!root) -- cgit v1.2.3 From 16c4042f08919f447d6b2a55679546c9b97c7264 Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Wed, 11 Aug 2010 14:17:39 -0700 Subject: writeback: avoid unnecessary calculation of bdi dirty thresholds Split get_dirty_limits() into global_dirty_limits()+bdi_dirty_limit(), so that the latter can be avoided when under global dirty background threshold (which is the normal state for most systems). Signed-off-by: Wu Fengguang Cc: Peter Zijlstra Cc: Christoph Hellwig Cc: Dave Chinner Cc: Jens Axboe Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/fs-writeback.c | 2 +- include/linux/writeback.h | 5 ++-- mm/backing-dev.c | 3 +- mm/page-writeback.c | 75 ++++++++++++++++++++++++----------------------- 4 files changed, 44 insertions(+), 41 deletions(-) (limited to 'fs') diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 2f76c4a081a2..fca43d4d7bf4 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -590,7 +590,7 @@ static inline bool over_bground_thresh(void) { unsigned long background_thresh, dirty_thresh; - get_dirty_limits(&background_thresh, &dirty_thresh, NULL, NULL); + global_dirty_limits(&background_thresh, &dirty_thresh); return (global_page_state(NR_FILE_DIRTY) + global_page_state(NR_UNSTABLE_NFS) >= background_thresh); diff --git a/include/linux/writeback.h b/include/linux/writeback.h index c24eca71e80c..72a5d647a5f2 100644 --- a/include/linux/writeback.h +++ b/include/linux/writeback.h @@ -124,8 +124,9 @@ struct ctl_table; int dirty_writeback_centisecs_handler(struct ctl_table *, int, void __user *, size_t *, loff_t *); -void get_dirty_limits(unsigned long *pbackground, unsigned long *pdirty, - unsigned long *pbdi_dirty, struct backing_dev_info *bdi); +void global_dirty_limits(unsigned long *pbackground, unsigned long *pdirty); +unsigned long bdi_dirty_limit(struct backing_dev_info *bdi, + unsigned long dirty); void page_writeback_init(void); void balance_dirty_pages_ratelimited_nr(struct address_space *mapping, diff --git a/mm/backing-dev.c b/mm/backing-dev.c index 08d357522e78..eaa4a5bbe063 100644 --- a/mm/backing-dev.c +++ b/mm/backing-dev.c @@ -81,7 +81,8 @@ static int bdi_debug_stats_show(struct seq_file *m, void *v) nr_more_io++; spin_unlock(&inode_lock); - get_dirty_limits(&background_thresh, &dirty_thresh, &bdi_thresh, bdi); + global_dirty_limits(&background_thresh, &dirty_thresh); + bdi_thresh = bdi_dirty_limit(bdi, dirty_thresh); #define K(x) ((x) << (PAGE_SHIFT - 10)) seq_printf(m, diff --git a/mm/page-writeback.c b/mm/page-writeback.c index 2cf69a5e46e6..1ea13ef350a8 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c @@ -267,10 +267,11 @@ static inline void task_dirties_fraction(struct task_struct *tsk, * * dirty -= (dirty/8) * p_{t} */ -static void task_dirty_limit(struct task_struct *tsk, unsigned long *pdirty) +static unsigned long task_dirty_limit(struct task_struct *tsk, + unsigned long bdi_dirty) { long numerator, denominator; - unsigned long dirty = *pdirty; + unsigned long dirty = bdi_dirty; u64 inv = dirty >> 3; task_dirties_fraction(tsk, &numerator, &denominator); @@ -278,10 +279,8 @@ static void task_dirty_limit(struct task_struct *tsk, unsigned long *pdirty) do_div(inv, denominator); dirty -= inv; - if (dirty < *pdirty/2) - dirty = *pdirty/2; - *pdirty = dirty; + return max(dirty, bdi_dirty/2); } /* @@ -391,9 +390,7 @@ unsigned long determine_dirtyable_memory(void) return x + 1; /* Ensure that we never return 0 */ } -void -get_dirty_limits(unsigned long *pbackground, unsigned long *pdirty, - unsigned long *pbdi_dirty, struct backing_dev_info *bdi) +void global_dirty_limits(unsigned long *pbackground, unsigned long *pdirty) { unsigned long background; unsigned long dirty; @@ -425,26 +422,28 @@ get_dirty_limits(unsigned long *pbackground, unsigned long *pdirty, } *pbackground = background; *pdirty = dirty; +} - if (bdi) { - u64 bdi_dirty; - long numerator, denominator; +unsigned long bdi_dirty_limit(struct backing_dev_info *bdi, + unsigned long dirty) +{ + u64 bdi_dirty; + long numerator, denominator; - /* - * Calculate this BDI's share of the dirty ratio. - */ - bdi_writeout_fraction(bdi, &numerator, &denominator); + /* + * Calculate this BDI's share of the dirty ratio. + */ + bdi_writeout_fraction(bdi, &numerator, &denominator); - bdi_dirty = (dirty * (100 - bdi_min_ratio)) / 100; - bdi_dirty *= numerator; - do_div(bdi_dirty, denominator); - bdi_dirty += (dirty * bdi->min_ratio) / 100; - if (bdi_dirty > (dirty * bdi->max_ratio) / 100) - bdi_dirty = dirty * bdi->max_ratio / 100; + bdi_dirty = (dirty * (100 - bdi_min_ratio)) / 100; + bdi_dirty *= numerator; + do_div(bdi_dirty, denominator); - *pbdi_dirty = bdi_dirty; - task_dirty_limit(current, pbdi_dirty); - } + bdi_dirty += (dirty * bdi->min_ratio) / 100; + if (bdi_dirty > (dirty * bdi->max_ratio) / 100) + bdi_dirty = dirty * bdi->max_ratio / 100; + + return bdi_dirty; } /* @@ -475,13 +474,24 @@ static void balance_dirty_pages(struct address_space *mapping, .range_cyclic = 1, }; - get_dirty_limits(&background_thresh, &dirty_thresh, - &bdi_thresh, bdi); - nr_reclaimable = global_page_state(NR_FILE_DIRTY) + global_page_state(NR_UNSTABLE_NFS); nr_writeback = global_page_state(NR_WRITEBACK); + global_dirty_limits(&background_thresh, &dirty_thresh); + + /* + * Throttle it only when the background writeback cannot + * catch-up. This avoids (excessively) small writeouts + * when the bdi limits are ramping up. + */ + if (nr_reclaimable + nr_writeback < + (background_thresh + dirty_thresh) / 2) + break; + + bdi_thresh = bdi_dirty_limit(bdi, dirty_thresh); + bdi_thresh = task_dirty_limit(current, bdi_thresh); + /* * In order to avoid the stacked BDI deadlock we need * to ensure we accurately count the 'dirty' pages when @@ -513,15 +523,6 @@ static void balance_dirty_pages(struct address_space *mapping, if (!dirty_exceeded) break; - /* - * Throttle it only when the background writeback cannot - * catch-up. This avoids (excessively) small writeouts - * when the bdi limits are ramping up. - */ - if (nr_reclaimable + nr_writeback < - (background_thresh + dirty_thresh) / 2) - break; - if (!bdi->dirty_exceeded) bdi->dirty_exceeded = 1; @@ -634,7 +635,7 @@ void throttle_vm_writeout(gfp_t gfp_mask) unsigned long dirty_thresh; for ( ; ; ) { - get_dirty_limits(&background_thresh, &dirty_thresh, NULL, NULL); + global_dirty_limits(&background_thresh, &dirty_thresh); /* * Boost the allowable dirty threshold a bit for page -- cgit v1.2.3 From 23539afc71937dbaca7de2229669f4475ff4ea7b Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Wed, 11 Aug 2010 14:17:41 -0700 Subject: writeback: don't redirty tail an inode with dirty pages Avoid delaying writeback for an expire inode with lots of dirty pages, but no active dirtier at the moment. Previously we only do that for the kupdate case. Any filesystem that does delayed allocation or unwritten extent conversion after IO completion will cause this - for example, XFS. Signed-off-by: Wu Fengguang Acked-by: Jan Kara Cc: Dave Chinner Cc: Christoph Hellwig Cc: Dave Chinner Cc: Jens Axboe Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/fs-writeback.c | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) (limited to 'fs') diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index fca43d4d7bf4..1ce364bbb003 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -363,18 +363,7 @@ writeback_single_inode(struct inode *inode, struct writeback_control *wbc) spin_lock(&inode_lock); inode->i_state &= ~I_SYNC; if (!(inode->i_state & I_FREEING)) { - if ((inode->i_state & I_DIRTY_PAGES) && wbc->for_kupdate) { - /* - * More pages get dirtied by a fast dirtier. - */ - goto select_queue; - } else if (inode->i_state & I_DIRTY) { - /* - * At least XFS will redirty the inode during the - * writeback (delalloc) and on io completion (isize). - */ - redirty_tail(inode); - } else if (mapping_tagged(mapping, PAGECACHE_TAG_DIRTY)) { + if (mapping_tagged(mapping, PAGECACHE_TAG_DIRTY)) { /* * We didn't write back all the pages. nfs_writepages() * sometimes bales out without doing anything. Redirty @@ -396,7 +385,6 @@ writeback_single_inode(struct inode *inode, struct writeback_control *wbc) * soon as the queue becomes uncongested. */ inode->i_state |= I_DIRTY_PAGES; -select_queue: if (wbc->nr_to_write <= 0) { /* * slice used up: queue for next turn @@ -419,6 +407,14 @@ select_queue: inode->i_state |= I_DIRTY_PAGES; redirty_tail(inode); } + } else if (inode->i_state & I_DIRTY) { + /* + * Filesystems can dirty the inode during writeback + * operations, such as delayed allocation during + * submission or metadata updates after data IO + * completion. + */ + redirty_tail(inode); } else if (atomic_read(&inode->i_count)) { /* * The inode is clean, inuse -- cgit v1.2.3 From 4ea879b96d437693485d21f4b7e1eb72f7615fc2 Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Wed, 11 Aug 2010 14:17:42 -0700 Subject: writeback: fix queue_io() ordering This was not a bug, since b_io is empty for kupdate writeback. The next patch will do requeue_io() for non-kupdate writeback, so let's fix it. Signed-off-by: Wu Fengguang Cc: Dave Chinner Cc: Martin Bligh Cc: Michael Rubin Cc: Peter Zijlstra Cc: Christoph Hellwig Cc: Jens Axboe Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/fs-writeback.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 1ce364bbb003..863bfb0eb492 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -249,10 +249,18 @@ static void move_expired_inodes(struct list_head *delaying_queue, /* * Queue all expired dirty inodes for io, eldest first. + * Before + * newly dirtied b_dirty b_io b_more_io + * =============> gf edc BA + * After + * newly dirtied b_dirty b_io b_more_io + * =============> g fBAedc + * | + * +--> dequeue for IO */ static void queue_io(struct bdi_writeback *wb, unsigned long *older_than_this) { - list_splice_init(&wb->b_more_io, wb->b_io.prev); + list_splice_init(&wb->b_more_io, &wb->b_io); move_expired_inodes(&wb->b_dirty, &wb->b_io, older_than_this); } -- cgit v1.2.3 From a50aeb40144982eb766053309b6fc33e14ca46f0 Mon Sep 17 00:00:00 2001 From: Wu Fengguang Date: Wed, 11 Aug 2010 14:17:43 -0700 Subject: writeback: merge for_kupdate and !for_kupdate cases Unify the logic for kupdate and non-kupdate cases. There won't be starvation because the inodes requeued into b_more_io will later be spliced _after_ the remaining inodes in b_io, hence won't stand in the way of other inodes in the next run. It avoids unnecessary redirty_tail() calls, hence the update of i_dirtied_when. The timestamp update is undesirable because it could later delay the inode's periodic writeback, or may exclude the inode from the data integrity sync operation (which checks timestamp to avoid extra work and livelock). === How the redirty_tail() comes about: It was a long story.. This redirty_tail() was introduced with wbc.more_io. The initial patch for more_io actually does not have the redirty_tail(), and when it's merged, several 100% iowait bug reports arised: reiserfs: http://lkml.org/lkml/2007/10/23/93 jfs: commit 29a424f28390752a4ca2349633aaacc6be494db5 JFS: clear PAGECACHE_TAG_DIRTY for no-write pages ext2: http://www.spinics.net/linux/lists/linux-ext4/msg04762.html They are all old bugs hidden in various filesystems that become "visible" with the more_io patch. At the time, the ext2 bug is thought to be "trivial", so not fixed. Instead the following updated more_io patch with redirty_tail() is merged: http://www.spinics.net/linux/lists/linux-ext4/msg04507.html This will in general prevent 100% on ext2 and possibly other unknown FS bugs. Signed-off-by: Wu Fengguang Cc: Dave Chinner Cc: Martin Bligh Cc: Michael Rubin Cc: Peter Zijlstra Cc: Christoph Hellwig Cc: Jens Axboe Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/fs-writeback.c | 43 ++++++++++--------------------------------- 1 file changed, 10 insertions(+), 33 deletions(-) (limited to 'fs') diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 863bfb0eb492..8a5807d2fb9d 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -374,45 +374,22 @@ writeback_single_inode(struct inode *inode, struct writeback_control *wbc) if (mapping_tagged(mapping, PAGECACHE_TAG_DIRTY)) { /* * We didn't write back all the pages. nfs_writepages() - * sometimes bales out without doing anything. Redirty - * the inode; Move it from b_io onto b_more_io/b_dirty. + * sometimes bales out without doing anything. */ - /* - * akpm: if the caller was the kupdate function we put - * this inode at the head of b_dirty so it gets first - * consideration. Otherwise, move it to the tail, for - * the reasons described there. I'm not really sure - * how much sense this makes. Presumably I had a good - * reasons for doing it this way, and I'd rather not - * muck with it at present. - */ - if (wbc->for_kupdate) { + inode->i_state |= I_DIRTY_PAGES; + if (wbc->nr_to_write <= 0) { /* - * For the kupdate function we move the inode - * to b_more_io so it will get more writeout as - * soon as the queue becomes uncongested. + * slice used up: queue for next turn */ - inode->i_state |= I_DIRTY_PAGES; - if (wbc->nr_to_write <= 0) { - /* - * slice used up: queue for next turn - */ - requeue_io(inode); - } else { - /* - * somehow blocked: retry later - */ - redirty_tail(inode); - } + requeue_io(inode); } else { /* - * Otherwise fully redirty the inode so that - * other inodes on this superblock will get some - * writeout. Otherwise heavy writing to one - * file would indefinitely suspend writeout of - * all the other files. + * Writeback blocked by something other than + * congestion. Delay the inode for some time to + * avoid spinning on the CPU (100% iowait) + * retrying writeback of the dirty page/inode + * that cannot be performed immediately. */ - inode->i_state |= I_DIRTY_PAGES; redirty_tail(inode); } } else if (inode->i_state & I_DIRTY) { -- cgit v1.2.3 From 81d73a32d775ae9674ea6edf0b5b721fc3bc57d9 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Wed, 11 Aug 2010 14:17:44 -0700 Subject: mm: fix writeback_in_progress() Commit 83ba7b071f3 ("writeback: simplify the write back thread queue") broke writeback_in_progress() as in that commit we started to remove work items from the list at the moment we start working on them and not at the moment they are finished. Thus if the flusher thread was doing some work but there was no other work queued, writeback_in_progress() returned false. This could in particular cause unnecessary queueing of background writeback from balance_dirty_pages() or writeout work from writeback_sb_if_idle(). This patch fixes the problem by introducing a bit in the bdi state which indicates that the flusher thread is processing some work and uses this bit for writeback_in_progress() test. NOTE: Both callsites of writeback_in_progress() (namely, writeback_inodes_sb_if_idle() and balance_dirty_pages()) would actually need a different information than what writeback_in_progress() provides. They would need to know whether *the kind of writeback they are going to submit* is already queued. But this information isn't that simple to provide so let's fix writeback_in_progress() for the time being. Signed-off-by: Jan Kara Cc: Christoph Hellwig Cc: Wu Fengguang Acked-by: Jens Axboe Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/fs-writeback.c | 4 +++- include/linux/backing-dev.h | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 8a5807d2fb9d..7d9d06ba184b 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -68,7 +68,7 @@ int nr_pdflush_threads; */ int writeback_in_progress(struct backing_dev_info *bdi) { - return !list_empty(&bdi->work_list); + return test_bit(BDI_writeback_running, &bdi->state); } static void bdi_queue_work(struct backing_dev_info *bdi, @@ -740,6 +740,7 @@ long wb_do_writeback(struct bdi_writeback *wb, int force_wait) struct wb_writeback_work *work; long wrote = 0; + set_bit(BDI_writeback_running, &wb->bdi->state); while ((work = get_next_work_item(bdi)) != NULL) { /* * Override sync mode, in case we must wait for completion @@ -766,6 +767,7 @@ long wb_do_writeback(struct bdi_writeback *wb, int force_wait) * Check for periodic writeback, kupdated() style */ wrote += wb_check_old_data_flush(wb); + clear_bit(BDI_writeback_running, &wb->bdi->state); return wrote; } diff --git a/include/linux/backing-dev.h b/include/linux/backing-dev.h index 7628219e5386..35b00746c712 100644 --- a/include/linux/backing-dev.h +++ b/include/linux/backing-dev.h @@ -31,6 +31,7 @@ enum bdi_state { BDI_async_congested, /* The async (write) queue is getting full */ BDI_sync_congested, /* The sync queue is getting full */ BDI_registered, /* bdi_register() was done */ + BDI_writeback_running, /* Writeback is in progress */ BDI_unused, /* Available bits start here */ }; -- cgit v1.2.3 From 12fdff3fc2483f906ae6404a6e8dcf2550310b6f Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 12 Aug 2010 16:54:57 +0100 Subject: Add a dummy printk function for the maintenance of unused printks Add a dummy printk function for the maintenance of unused printks through gcc format checking, and also so that side-effect checking is maintained too. Signed-off-by: David Howells Signed-off-by: Linus Torvalds --- arch/mn10300/kernel/mn10300-serial.c | 5 ----- fs/afs/internal.h | 12 +++--------- fs/cachefiles/internal.h | 13 +++---------- fs/fscache/internal.h | 14 ++++---------- include/linux/kernel.h | 7 +++++++ kernel/cred.c | 4 ---- net/rxrpc/ar-internal.h | 16 +++++----------- security/keys/internal.h | 5 ----- 8 files changed, 22 insertions(+), 54 deletions(-) (limited to 'fs') diff --git a/arch/mn10300/kernel/mn10300-serial.c b/arch/mn10300/kernel/mn10300-serial.c index ef34d5a0f8bd..9d49073e827a 100644 --- a/arch/mn10300/kernel/mn10300-serial.c +++ b/arch/mn10300/kernel/mn10300-serial.c @@ -44,11 +44,6 @@ static const char serial_revdate[] = "2007-11-06"; #include #include "mn10300-serial.h" -static inline __attribute__((format(printf, 1, 2))) -void no_printk(const char *fmt, ...) -{ -} - #define kenter(FMT, ...) \ printk(KERN_DEBUG "-->%s(" FMT ")\n", __func__, ##__VA_ARGS__) #define _enter(FMT, ...) \ diff --git a/fs/afs/internal.h b/fs/afs/internal.h index 8679089ce9a1..c6c93f180707 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -752,12 +752,6 @@ extern unsigned afs_debug; #define dbgprintk(FMT,...) \ printk("[%-6.6s] "FMT"\n", current->comm ,##__VA_ARGS__) -/* make sure we maintain the format strings, even when debugging is disabled */ -static inline __attribute__((format(printf,1,2))) -void _dbprintk(const char *fmt, ...) -{ -} - #define kenter(FMT,...) dbgprintk("==> %s("FMT")",__func__ ,##__VA_ARGS__) #define kleave(FMT,...) dbgprintk("<== %s()"FMT"",__func__ ,##__VA_ARGS__) #define kdebug(FMT,...) dbgprintk(" "FMT ,##__VA_ARGS__) @@ -792,9 +786,9 @@ do { \ } while (0) #else -#define _enter(FMT,...) _dbprintk("==> %s("FMT")",__func__ ,##__VA_ARGS__) -#define _leave(FMT,...) _dbprintk("<== %s()"FMT"",__func__ ,##__VA_ARGS__) -#define _debug(FMT,...) _dbprintk(" "FMT ,##__VA_ARGS__) +#define _enter(FMT,...) no_printk("==> %s("FMT")",__func__ ,##__VA_ARGS__) +#define _leave(FMT,...) no_printk("<== %s()"FMT"",__func__ ,##__VA_ARGS__) +#define _debug(FMT,...) no_printk(" "FMT ,##__VA_ARGS__) #endif /* diff --git a/fs/cachefiles/internal.h b/fs/cachefiles/internal.h index a8cd821226da..bd6bc1bde2d7 100644 --- a/fs/cachefiles/internal.h +++ b/fs/cachefiles/internal.h @@ -267,13 +267,6 @@ do { \ #define dbgprintk(FMT, ...) \ printk(KERN_DEBUG "[%-6.6s] "FMT"\n", current->comm, ##__VA_ARGS__) -/* make sure we maintain the format strings, even when debugging is disabled */ -static inline void _dbprintk(const char *fmt, ...) - __attribute__((format(printf, 1, 2))); -static inline void _dbprintk(const char *fmt, ...) -{ -} - #define kenter(FMT, ...) dbgprintk("==> %s("FMT")", __func__, ##__VA_ARGS__) #define kleave(FMT, ...) dbgprintk("<== %s()"FMT"", __func__, ##__VA_ARGS__) #define kdebug(FMT, ...) dbgprintk(FMT, ##__VA_ARGS__) @@ -304,9 +297,9 @@ do { \ } while (0) #else -#define _enter(FMT, ...) _dbprintk("==> %s("FMT")", __func__, ##__VA_ARGS__) -#define _leave(FMT, ...) _dbprintk("<== %s()"FMT"", __func__, ##__VA_ARGS__) -#define _debug(FMT, ...) _dbprintk(FMT, ##__VA_ARGS__) +#define _enter(FMT, ...) no_printk("==> %s("FMT")", __func__, ##__VA_ARGS__) +#define _leave(FMT, ...) no_printk("<== %s()"FMT"", __func__, ##__VA_ARGS__) +#define _debug(FMT, ...) no_printk(FMT, ##__VA_ARGS__) #endif #if 1 /* defined(__KDEBUGALL) */ diff --git a/fs/fscache/internal.h b/fs/fscache/internal.h index 6a026441c5a6..f6aad48d38a8 100644 --- a/fs/fscache/internal.h +++ b/fs/fscache/internal.h @@ -321,17 +321,11 @@ void fscache_put_context(struct fscache_cookie *cookie, void *context) #define dbgprintk(FMT, ...) \ printk(KERN_DEBUG "[%-6.6s] "FMT"\n", current->comm, ##__VA_ARGS__) -/* make sure we maintain the format strings, even when debugging is disabled */ -static inline __attribute__((format(printf, 1, 2))) -void _dbprintk(const char *fmt, ...) -{ -} - #define kenter(FMT, ...) dbgprintk("==> %s("FMT")", __func__, ##__VA_ARGS__) #define kleave(FMT, ...) dbgprintk("<== %s()"FMT"", __func__, ##__VA_ARGS__) #define kdebug(FMT, ...) dbgprintk(FMT, ##__VA_ARGS__) -#define kjournal(FMT, ...) _dbprintk(FMT, ##__VA_ARGS__) +#define kjournal(FMT, ...) no_printk(FMT, ##__VA_ARGS__) #ifdef __KDEBUG #define _enter(FMT, ...) kenter(FMT, ##__VA_ARGS__) @@ -358,9 +352,9 @@ do { \ } while (0) #else -#define _enter(FMT, ...) _dbprintk("==> %s("FMT")", __func__, ##__VA_ARGS__) -#define _leave(FMT, ...) _dbprintk("<== %s()"FMT"", __func__, ##__VA_ARGS__) -#define _debug(FMT, ...) _dbprintk(FMT, ##__VA_ARGS__) +#define _enter(FMT, ...) no_printk("==> %s("FMT")", __func__, ##__VA_ARGS__) +#define _leave(FMT, ...) no_printk("<== %s()"FMT"", __func__, ##__VA_ARGS__) +#define _debug(FMT, ...) no_printk(FMT, ##__VA_ARGS__) #endif /* diff --git a/include/linux/kernel.h b/include/linux/kernel.h index d848cb854655..2b0a35e6bc69 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -306,6 +306,13 @@ static inline void log_buf_kexec_setup(void) } #endif +/* + * Dummy printk for disabled debugging statements to use whilst maintaining + * gcc's format and side-effect checking. + */ +static inline __attribute__ ((format (printf, 1, 2))) +int no_printk(const char *s, ...) { return 0; } + extern int printk_needs_cpu(int cpu); extern void printk_tick(void); diff --git a/kernel/cred.c b/kernel/cred.c index 60bc8b1e32e6..9a3e22641fe7 100644 --- a/kernel/cred.c +++ b/kernel/cred.c @@ -22,10 +22,6 @@ #define kdebug(FMT, ...) \ printk("[%-5.5s%5u] "FMT"\n", current->comm, current->pid ,##__VA_ARGS__) #else -static inline __attribute__((format(printf, 1, 2))) -void no_printk(const char *fmt, ...) -{ -} #define kdebug(FMT, ...) \ no_printk("[%-5.5s%5u] "FMT"\n", current->comm, current->pid ,##__VA_ARGS__) #endif diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index 7043b294bb67..8e22bd345e71 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -597,12 +597,6 @@ extern unsigned rxrpc_debug; #define dbgprintk(FMT,...) \ printk("[%-6.6s] "FMT"\n", current->comm ,##__VA_ARGS__) -/* make sure we maintain the format strings, even when debugging is disabled */ -static inline __attribute__((format(printf,1,2))) -void _dbprintk(const char *fmt, ...) -{ -} - #define kenter(FMT,...) dbgprintk("==> %s("FMT")",__func__ ,##__VA_ARGS__) #define kleave(FMT,...) dbgprintk("<== %s()"FMT"",__func__ ,##__VA_ARGS__) #define kdebug(FMT,...) dbgprintk(" "FMT ,##__VA_ARGS__) @@ -655,11 +649,11 @@ do { \ } while (0) #else -#define _enter(FMT,...) _dbprintk("==> %s("FMT")",__func__ ,##__VA_ARGS__) -#define _leave(FMT,...) _dbprintk("<== %s()"FMT"",__func__ ,##__VA_ARGS__) -#define _debug(FMT,...) _dbprintk(" "FMT ,##__VA_ARGS__) -#define _proto(FMT,...) _dbprintk("### "FMT ,##__VA_ARGS__) -#define _net(FMT,...) _dbprintk("@@@ "FMT ,##__VA_ARGS__) +#define _enter(FMT,...) no_printk("==> %s("FMT")",__func__ ,##__VA_ARGS__) +#define _leave(FMT,...) no_printk("<== %s()"FMT"",__func__ ,##__VA_ARGS__) +#define _debug(FMT,...) no_printk(" "FMT ,##__VA_ARGS__) +#define _proto(FMT,...) no_printk("### "FMT ,##__VA_ARGS__) +#define _net(FMT,...) no_printk("@@@ "FMT ,##__VA_ARGS__) #endif /* diff --git a/security/keys/internal.h b/security/keys/internal.h index addb67b169f4..56a133d8f37d 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h @@ -15,11 +15,6 @@ #include #include -static inline __attribute__((format(printf, 1, 2))) -void no_printk(const char *fmt, ...) -{ -} - #ifdef __KDEBUG #define kenter(FMT, ...) \ printk(KERN_DEBUG "==> %s("FMT")\n", __func__, ##__VA_ARGS__) -- cgit v1.2.3 From 3f43231230664c23f4a7513232171dcb6ce9f068 Mon Sep 17 00:00:00 2001 From: Steve French Date: Thu, 12 Aug 2010 18:16:45 +0000 Subject: [NFS] Set CONFIG_KEYS when CONFIG_NFS_USE_KERNEL_DNS is set Previous patch relied on DNS_RESOLVER setting CONFIG_KEYS but needs to be selected in NFS config when using the new DNS resolver Signed-off-by: Bryan Schumaker CC: David Howells Signed-off-by: Steve French --- fs/nfs/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'fs') diff --git a/fs/nfs/Kconfig b/fs/nfs/Kconfig index c5bbdca13ac2..26a510a7be09 100644 --- a/fs/nfs/Kconfig +++ b/fs/nfs/Kconfig @@ -115,4 +115,5 @@ config NFS_USE_KERNEL_DNS bool depends on NFS_V4 && !NFS_USE_LEGACY_DNS select DNS_RESOLVER + select KEYS default y -- cgit v1.2.3 From 2069601b3f0ea38170d4b509b89f3ca0a373bdc1 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 12 Aug 2010 14:23:04 -0700 Subject: Revert "fsnotify: store struct file not struct path" This reverts commit 3bcf3860a4ff9bbc522820b4b765e65e4deceb3e (and the accompanying commit c1e5c954020e "vfs/fsnotify: fsnotify_close can delay the final work in fput" that was a horribly ugly hack to make it work at all). The 'struct file' approach not only causes that disgusting hack, it somehow breaks pulseaudio, probably due to some other subtlety with f_count handling. Fix up various conflicts due to later fsnotify work. Signed-off-by: Linus Torvalds --- fs/file_table.c | 9 --------- fs/notify/fanotify/fanotify.c | 8 ++++---- fs/notify/fanotify/fanotify_user.c | 6 +++--- fs/notify/fsnotify.c | 12 ++++++------ fs/notify/inotify/inotify_fsnotify.c | 12 ++++++------ fs/notify/notification.c | 33 +++++++++++--------------------- include/linux/fsnotify.h | 37 ++++++++++++++++++++---------------- include/linux/fsnotify_backend.h | 16 ++++++++-------- kernel/audit_watch.c | 4 ++-- 9 files changed, 61 insertions(+), 76 deletions(-) (limited to 'fs') diff --git a/fs/file_table.c b/fs/file_table.c index 2fc3b3c08911..edecd36fed9b 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -230,15 +230,6 @@ static void __fput(struct file *file) might_sleep(); fsnotify_close(file); - - /* - * fsnotify_create_event may have taken one or more references on this - * file. If it did so it left one reference for us to drop to make sure - * its calls to fput could not prematurely destroy the file. - */ - if (atomic_long_read(&file->f_count)) - return fput(file); - /* * The function eventpoll_release() should be the first called * in the file cleanup chain. diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index eb8f73c9c131..756566fe8449 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -17,9 +17,9 @@ static bool should_merge(struct fsnotify_event *old, struct fsnotify_event *new) old->data_type == new->data_type && old->tgid == new->tgid) { switch (old->data_type) { - case (FSNOTIFY_EVENT_FILE): - if ((old->file->f_path.mnt == new->file->f_path.mnt) && - (old->file->f_path.dentry == new->file->f_path.dentry)) + case (FSNOTIFY_EVENT_PATH): + if ((old->path.mnt == new->path.mnt) && + (old->path.dentry == new->path.dentry)) return true; case (FSNOTIFY_EVENT_NONE): return true; @@ -174,7 +174,7 @@ static bool fanotify_should_send_event(struct fsnotify_group *group, return false; /* if we don't have enough info to send an event to userspace say no */ - if (data_type != FSNOTIFY_EVENT_FILE) + if (data_type != FSNOTIFY_EVENT_PATH) return false; if (inode_mark && vfsmnt_mark) { diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 25a3b4dfcf61..032b837fcd11 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -65,7 +65,7 @@ static int create_fd(struct fsnotify_group *group, struct fsnotify_event *event) if (client_fd < 0) return client_fd; - if (event->data_type != FSNOTIFY_EVENT_FILE) { + if (event->data_type != FSNOTIFY_EVENT_PATH) { WARN_ON(1); put_unused_fd(client_fd); return -EINVAL; @@ -75,8 +75,8 @@ static int create_fd(struct fsnotify_group *group, struct fsnotify_event *event) * we need a new file handle for the userspace program so it can read even if it was * originally opened O_WRONLY. */ - dentry = dget(event->file->f_path.dentry); - mnt = mntget(event->file->f_path.mnt); + dentry = dget(event->path.dentry); + mnt = mntget(event->path.mnt); /* it's possible this event was an overflow event. in that case dentry and mnt * are NULL; That's fine, just don't call dentry open */ if (dentry && mnt) diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index 4d2a82c1ceb1..3970392b2722 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -84,7 +84,7 @@ void __fsnotify_update_child_dentry_flags(struct inode *inode) } /* Notify this dentry's parent about a child's events. */ -void __fsnotify_parent(struct file *file, struct dentry *dentry, __u32 mask) +void __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask) { struct dentry *parent; struct inode *p_inode; @@ -92,7 +92,7 @@ void __fsnotify_parent(struct file *file, struct dentry *dentry, __u32 mask) bool should_update_children = false; if (!dentry) - dentry = file->f_path.dentry; + dentry = path->dentry; if (!(dentry->d_flags & DCACHE_FSNOTIFY_PARENT_WATCHED)) return; @@ -124,8 +124,8 @@ void __fsnotify_parent(struct file *file, struct dentry *dentry, __u32 mask) * specifies these are events which came from a child. */ mask |= FS_EVENT_ON_CHILD; - if (file) - fsnotify(p_inode, mask, file, FSNOTIFY_EVENT_FILE, + if (path) + fsnotify(p_inode, mask, path, FSNOTIFY_EVENT_PATH, dentry->d_name.name, 0); else fsnotify(p_inode, mask, dentry->d_inode, FSNOTIFY_EVENT_INODE, @@ -217,8 +217,8 @@ int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, /* global tests shouldn't care about events on child only the specific event */ __u32 test_mask = (mask & ~FS_EVENT_ON_CHILD); - if (data_is == FSNOTIFY_EVENT_FILE) - mnt = ((struct file *)data)->f_path.mnt; + if (data_is == FSNOTIFY_EVENT_PATH) + mnt = ((struct path *)data)->mnt; else mnt = NULL; diff --git a/fs/notify/inotify/inotify_fsnotify.c b/fs/notify/inotify/inotify_fsnotify.c index 5e73eeb2c697..a91b69a6a291 100644 --- a/fs/notify/inotify/inotify_fsnotify.c +++ b/fs/notify/inotify/inotify_fsnotify.c @@ -52,9 +52,9 @@ static bool event_compare(struct fsnotify_event *old, struct fsnotify_event *new !strcmp(old->file_name, new->file_name)) return true; break; - case (FSNOTIFY_EVENT_FILE): - if ((old->file->f_path.mnt == new->file->f_path.mnt) && - (old->file->f_path.dentry == new->file->f_path.dentry)) + case (FSNOTIFY_EVENT_PATH): + if ((old->path.mnt == new->path.mnt) && + (old->path.dentry == new->path.dentry)) return true; break; case (FSNOTIFY_EVENT_NONE): @@ -147,10 +147,10 @@ static bool inotify_should_send_event(struct fsnotify_group *group, struct inode __u32 mask, void *data, int data_type) { if ((inode_mark->mask & FS_EXCL_UNLINK) && - (data_type == FSNOTIFY_EVENT_FILE)) { - struct file *file = data; + (data_type == FSNOTIFY_EVENT_PATH)) { + struct path *path = data; - if (d_unlinked(file->f_path.dentry)) + if (d_unlinked(path->dentry)) return false; } diff --git a/fs/notify/notification.c b/fs/notify/notification.c index d6c435adc7a2..f39260f8f865 100644 --- a/fs/notify/notification.c +++ b/fs/notify/notification.c @@ -31,7 +31,6 @@ * allocated and used. */ -#include #include #include #include @@ -90,8 +89,8 @@ void fsnotify_put_event(struct fsnotify_event *event) if (atomic_dec_and_test(&event->refcnt)) { pr_debug("%s: event=%p\n", __func__, event); - if (event->data_type == FSNOTIFY_EVENT_FILE) - fput(event->file); + if (event->data_type == FSNOTIFY_EVENT_PATH) + path_put(&event->path); BUG_ON(!list_empty(&event->private_data_list)); @@ -376,8 +375,8 @@ struct fsnotify_event *fsnotify_clone_event(struct fsnotify_event *old_event) } } event->tgid = get_pid(old_event->tgid); - if (event->data_type == FSNOTIFY_EVENT_FILE) - get_file(event->file); + if (event->data_type == FSNOTIFY_EVENT_PATH) + path_get(&event->path); return event; } @@ -424,22 +423,11 @@ struct fsnotify_event *fsnotify_create_event(struct inode *to_tell, __u32 mask, event->data_type = data_type; switch (data_type) { - case FSNOTIFY_EVENT_FILE: { - event->file = data; - /* - * if this file is about to disappear hold an extra reference - * until we return to __fput so we don't have to worry about - * future get/put destroying the file under us or generating - * additional events. Notice that we change f_mode without - * holding f_lock. This is safe since this is the only possible - * reference to this object in the kernel (it was about to be - * freed, remember?) - */ - if (!atomic_long_read(&event->file->f_count)) { - event->file->f_mode |= FMODE_NONOTIFY; - get_file(event->file); - } - get_file(event->file); + case FSNOTIFY_EVENT_PATH: { + struct path *path = data; + event->path.dentry = path->dentry; + event->path.mnt = path->mnt; + path_get(&event->path); break; } case FSNOTIFY_EVENT_INODE: @@ -447,7 +435,8 @@ struct fsnotify_event *fsnotify_create_event(struct inode *to_tell, __u32 mask, break; case FSNOTIFY_EVENT_NONE: event->inode = NULL; - event->file = NULL; + event->path.dentry = NULL; + event->path.mnt = NULL; break; default: BUG(); diff --git a/include/linux/fsnotify.h b/include/linux/fsnotify.h index e4e2204187ee..59d0df43ff9d 100644 --- a/include/linux/fsnotify.h +++ b/include/linux/fsnotify.h @@ -26,18 +26,19 @@ static inline void fsnotify_d_instantiate(struct dentry *dentry, } /* Notify this dentry's parent about a child's events. */ -static inline void fsnotify_parent(struct file *file, struct dentry *dentry, __u32 mask) +static inline void fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask) { if (!dentry) - dentry = file->f_path.dentry; + dentry = path->dentry; - __fsnotify_parent(file, dentry, mask); + __fsnotify_parent(path, dentry, mask); } /* simple call site for access decisions */ static inline int fsnotify_perm(struct file *file, int mask) { - struct inode *inode = file->f_path.dentry->d_inode; + struct path *path = &file->f_path; + struct inode *inode = path->dentry->d_inode; __u32 fsnotify_mask = 0; if (file->f_mode & FMODE_NONOTIFY) @@ -51,7 +52,7 @@ static inline int fsnotify_perm(struct file *file, int mask) else BUG(); - return fsnotify(inode, fsnotify_mask, file, FSNOTIFY_EVENT_FILE, NULL, 0); + return fsnotify(inode, fsnotify_mask, path, FSNOTIFY_EVENT_PATH, NULL, 0); } /* @@ -186,15 +187,16 @@ static inline void fsnotify_mkdir(struct inode *inode, struct dentry *dentry) */ static inline void fsnotify_access(struct file *file) { - struct inode *inode = file->f_path.dentry->d_inode; + struct path *path = &file->f_path; + struct inode *inode = path->dentry->d_inode; __u32 mask = FS_ACCESS; if (S_ISDIR(inode->i_mode)) mask |= FS_IN_ISDIR; if (!(file->f_mode & FMODE_NONOTIFY)) { - fsnotify_parent(file, NULL, mask); - fsnotify(inode, mask, file, FSNOTIFY_EVENT_FILE, NULL, 0); + fsnotify_parent(path, NULL, mask); + fsnotify(inode, mask, path, FSNOTIFY_EVENT_PATH, NULL, 0); } } @@ -203,15 +205,16 @@ static inline void fsnotify_access(struct file *file) */ static inline void fsnotify_modify(struct file *file) { - struct inode *inode = file->f_path.dentry->d_inode; + struct path *path = &file->f_path; + struct inode *inode = path->dentry->d_inode; __u32 mask = FS_MODIFY; if (S_ISDIR(inode->i_mode)) mask |= FS_IN_ISDIR; if (!(file->f_mode & FMODE_NONOTIFY)) { - fsnotify_parent(file, NULL, mask); - fsnotify(inode, mask, file, FSNOTIFY_EVENT_FILE, NULL, 0); + fsnotify_parent(path, NULL, mask); + fsnotify(inode, mask, path, FSNOTIFY_EVENT_PATH, NULL, 0); } } @@ -220,15 +223,16 @@ static inline void fsnotify_modify(struct file *file) */ static inline void fsnotify_open(struct file *file) { - struct inode *inode = file->f_path.dentry->d_inode; + struct path *path = &file->f_path; + struct inode *inode = path->dentry->d_inode; __u32 mask = FS_OPEN; if (S_ISDIR(inode->i_mode)) mask |= FS_IN_ISDIR; if (!(file->f_mode & FMODE_NONOTIFY)) { - fsnotify_parent(file, NULL, mask); - fsnotify(inode, mask, file, FSNOTIFY_EVENT_FILE, NULL, 0); + fsnotify_parent(path, NULL, mask); + fsnotify(inode, mask, path, FSNOTIFY_EVENT_PATH, NULL, 0); } } @@ -237,6 +241,7 @@ static inline void fsnotify_open(struct file *file) */ static inline void fsnotify_close(struct file *file) { + struct path *path = &file->f_path; struct inode *inode = file->f_path.dentry->d_inode; fmode_t mode = file->f_mode; __u32 mask = (mode & FMODE_WRITE) ? FS_CLOSE_WRITE : FS_CLOSE_NOWRITE; @@ -245,8 +250,8 @@ static inline void fsnotify_close(struct file *file) mask |= FS_IN_ISDIR; if (!(file->f_mode & FMODE_NONOTIFY)) { - fsnotify_parent(file, NULL, mask); - fsnotify(inode, mask, file, FSNOTIFY_EVENT_FILE, NULL, 0); + fsnotify_parent(path, NULL, mask); + fsnotify(inode, mask, path, FSNOTIFY_EVENT_PATH, NULL, 0); } } diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index 9bbfd7204b04..ed36fb57c426 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -203,20 +203,20 @@ struct fsnotify_event { /* to_tell may ONLY be dereferenced during handle_event(). */ struct inode *to_tell; /* either the inode the event happened to or its parent */ /* - * depending on the event type we should have either a file or inode - * We hold a reference on file, but NOT on inode. Since we have the ref on - * the file, it may be dereferenced at any point during this object's + * depending on the event type we should have either a path or inode + * We hold a reference on path, but NOT on inode. Since we have the ref on + * the path, it may be dereferenced at any point during this object's * lifetime. That reference is dropped when this object's refcnt hits - * 0. If this event contains an inode instead of a file, the inode may + * 0. If this event contains an inode instead of a path, the inode may * ONLY be used during handle_event(). */ union { - struct file *file; + struct path path; struct inode *inode; }; /* when calling fsnotify tell it if the data is a path or inode */ #define FSNOTIFY_EVENT_NONE 0 -#define FSNOTIFY_EVENT_FILE 1 +#define FSNOTIFY_EVENT_PATH 1 #define FSNOTIFY_EVENT_INODE 2 int data_type; /* which of the above union we have */ atomic_t refcnt; /* how many groups still are using/need to send this event */ @@ -293,7 +293,7 @@ struct fsnotify_mark { /* main fsnotify call to send events */ extern int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, const unsigned char *name, u32 cookie); -extern void __fsnotify_parent(struct file *file, struct dentry *dentry, __u32 mask); +extern void __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask); extern void __fsnotify_inode_delete(struct inode *inode); extern void __fsnotify_vfsmount_delete(struct vfsmount *mnt); extern u32 fsnotify_get_cookie(void); @@ -422,7 +422,7 @@ static inline int fsnotify(struct inode *to_tell, __u32 mask, void *data, int da return 0; } -static inline void __fsnotify_parent(struct file *file, struct dentry *dentry, __u32 mask) +static inline void __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask) {} static inline void __fsnotify_inode_delete(struct inode *inode) diff --git a/kernel/audit_watch.c b/kernel/audit_watch.c index 6bf2306be7d6..f0c9b2e7542d 100644 --- a/kernel/audit_watch.c +++ b/kernel/audit_watch.c @@ -526,8 +526,8 @@ static int audit_watch_handle_event(struct fsnotify_group *group, BUG_ON(group != audit_watch_group); switch (event->data_type) { - case (FSNOTIFY_EVENT_FILE): - inode = event->file->f_path.dentry->d_inode; + case (FSNOTIFY_EVENT_PATH): + inode = event->path.dentry->d_inode; break; case (FSNOTIFY_EVENT_INODE): inode = event->inode; -- cgit v1.2.3 From 2041f657aa4dc77afd63bbb34b34ed9476cf9ab9 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Fri, 13 Aug 2010 10:06:43 +0200 Subject: [S390] partitions: fix build error in ibm partition detection code 9c867fbe "partitions: fix sometimes unreadable partition strings" coverted one line within the ibm partition code incorrectly. Fix this to get rid of a build error. fs/partitions/ibm.c: In function 'ibm_partition': [...] fs/partitions/ibm.c:185: error: too many arguments to function 'strlcat' Cc: Alexey Dobriyan Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- fs/partitions/ibm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/partitions/ibm.c b/fs/partitions/ibm.c index d1b8a5c4bc0a..d513a07f44bb 100644 --- a/fs/partitions/ibm.c +++ b/fs/partitions/ibm.c @@ -182,7 +182,7 @@ int ibm_partition(struct parsed_partitions *state) offset = (info->label_block + 1); } else { /* unlabeled disk */ - strlcat(tmp, sizeof(tmp), "(nonl)", PAGE_SIZE); + strlcat(state->pp_buf, "(nonl)", PAGE_SIZE); size = i_size >> 9; offset = (info->label_block + 1); } -- cgit v1.2.3 From 02d6d685fc6f2d8b48b133b5a5a43755e005074e Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 27 Apr 2010 22:30:06 +0200 Subject: logfs: kill BKL logfs does not need the BKL, so use ->unlocked_ioctl instead of ->ioctl in file operations. Signed-off-by: Arnd Bergmann Signed-off-by: Joern Engel [ fixed trivial conflict ] Signed-off-by: Frederic Weisbecker --- fs/logfs/dir.c | 2 +- fs/logfs/file.c | 6 +++--- fs/logfs/logfs.h | 3 +-- 3 files changed, 5 insertions(+), 6 deletions(-) (limited to 'fs') diff --git a/fs/logfs/dir.c b/fs/logfs/dir.c index 675cc49197fe..9777eb5b5522 100644 --- a/fs/logfs/dir.c +++ b/fs/logfs/dir.c @@ -824,7 +824,7 @@ const struct inode_operations logfs_dir_iops = { }; const struct file_operations logfs_dir_fops = { .fsync = logfs_fsync, - .ioctl = logfs_ioctl, + .unlocked_ioctl = logfs_ioctl, .readdir = logfs_readdir, .read = generic_read_dir, }; diff --git a/fs/logfs/file.c b/fs/logfs/file.c index 4dd0f7c06e39..e86376b87af1 100644 --- a/fs/logfs/file.c +++ b/fs/logfs/file.c @@ -181,9 +181,9 @@ static int logfs_releasepage(struct page *page, gfp_t only_xfs_uses_this) } -int logfs_ioctl(struct inode *inode, struct file *file, unsigned int cmd, - unsigned long arg) +long logfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { + struct inode *inode = file->f_path.dentry->d_inode; struct logfs_inode *li = logfs_inode(inode); unsigned int oldflags, flags; int err; @@ -255,7 +255,7 @@ const struct file_operations logfs_reg_fops = { .aio_read = generic_file_aio_read, .aio_write = generic_file_aio_write, .fsync = logfs_fsync, - .ioctl = logfs_ioctl, + .unlocked_ioctl = logfs_ioctl, .llseek = generic_file_llseek, .mmap = generic_file_readonly_mmap, .open = generic_file_open, diff --git a/fs/logfs/logfs.h b/fs/logfs/logfs.h index 5e3b72077951..b8786264d243 100644 --- a/fs/logfs/logfs.h +++ b/fs/logfs/logfs.h @@ -504,8 +504,7 @@ extern const struct inode_operations logfs_reg_iops; extern const struct file_operations logfs_reg_fops; extern const struct address_space_operations logfs_reg_aops; int logfs_readpage(struct file *file, struct page *page); -int logfs_ioctl(struct inode *inode, struct file *file, unsigned int cmd, - unsigned long arg); +long logfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg); int logfs_fsync(struct file *file, int datasync); /* gc.c */ -- cgit v1.2.3 From b19dd42faf413b4705d4adb38521e82d73fa4249 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Sun, 4 Jul 2010 00:15:10 +0200 Subject: bkl: Remove locked .ioctl file operation The last user is gone, so we can safely remove this Signed-off-by: Arnd Bergmann Cc: John Kacur Cc: Al Viro Cc: Thomas Gleixner Signed-off-by: Frederic Weisbecker --- Documentation/filesystems/Locking | 8 +------- Documentation/filesystems/vfs.txt | 6 +----- fs/bad_inode.c | 7 ------- fs/compat_ioctl.c | 3 +-- fs/ioctl.c | 18 ++++-------------- fs/proc/inode.c | 17 ++++------------- include/linux/fs.h | 5 ++--- 7 files changed, 13 insertions(+), 51 deletions(-) (limited to 'fs') diff --git a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking index bbcc15651a21..2db4283efa8d 100644 --- a/Documentation/filesystems/Locking +++ b/Documentation/filesystems/Locking @@ -374,8 +374,6 @@ prototypes: ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t); int (*readdir) (struct file *, void *, filldir_t); unsigned int (*poll) (struct file *, struct poll_table_struct *); - int (*ioctl) (struct inode *, struct file *, unsigned int, - unsigned long); long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); long (*compat_ioctl) (struct file *, unsigned int, unsigned long); int (*mmap) (struct file *, struct vm_area_struct *); @@ -409,8 +407,7 @@ write: no aio_write: no readdir: no poll: no -ioctl: yes (see below) -unlocked_ioctl: no (see below) +unlocked_ioctl: no compat_ioctl: no mmap: no open: no @@ -453,9 +450,6 @@ move ->readdir() to inode_operations and use a separate method for directory anything that resembles union-mount we won't have a struct file for all components. And there are other reasons why the current interface is a mess... -->ioctl() on regular files is superceded by the ->unlocked_ioctl() that -doesn't take the BKL. - ->read on directories probably must go away - we should just enforce -EISDIR in sys_read() and friends. diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt index 94677e7dcb13..ed7e5efc06d8 100644 --- a/Documentation/filesystems/vfs.txt +++ b/Documentation/filesystems/vfs.txt @@ -727,7 +727,6 @@ struct file_operations { ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t); int (*readdir) (struct file *, void *, filldir_t); unsigned int (*poll) (struct file *, struct poll_table_struct *); - int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long); long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); long (*compat_ioctl) (struct file *, unsigned int, unsigned long); int (*mmap) (struct file *, struct vm_area_struct *); @@ -768,10 +767,7 @@ otherwise noted. activity on this file and (optionally) go to sleep until there is activity. Called by the select(2) and poll(2) system calls - ioctl: called by the ioctl(2) system call - - unlocked_ioctl: called by the ioctl(2) system call. Filesystems that do not - require the BKL should use this method instead of the ioctl() above. + unlocked_ioctl: called by the ioctl(2) system call. compat_ioctl: called by the ioctl(2) system call when 32 bit system calls are used on 64 bit kernels. diff --git a/fs/bad_inode.c b/fs/bad_inode.c index 52e59bf4aa5f..f024d8aaddef 100644 --- a/fs/bad_inode.c +++ b/fs/bad_inode.c @@ -55,12 +55,6 @@ static unsigned int bad_file_poll(struct file *filp, poll_table *wait) return POLLERR; } -static int bad_file_ioctl (struct inode *inode, struct file *filp, - unsigned int cmd, unsigned long arg) -{ - return -EIO; -} - static long bad_file_unlocked_ioctl(struct file *file, unsigned cmd, unsigned long arg) { @@ -159,7 +153,6 @@ static const struct file_operations bad_file_ops = .aio_write = bad_file_aio_write, .readdir = bad_file_readdir, .poll = bad_file_poll, - .ioctl = bad_file_ioctl, .unlocked_ioctl = bad_file_unlocked_ioctl, .compat_ioctl = bad_file_compat_ioctl, .mmap = bad_file_mmap, diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c index 70227e0dc01d..03e59aa318eb 100644 --- a/fs/compat_ioctl.c +++ b/fs/compat_ioctl.c @@ -1699,8 +1699,7 @@ asmlinkage long compat_sys_ioctl(unsigned int fd, unsigned int cmd, goto out_fput; } - if (!filp->f_op || - (!filp->f_op->ioctl && !filp->f_op->unlocked_ioctl)) + if (!filp->f_op || !filp->f_op->unlocked_ioctl) goto do_ioctl; break; } diff --git a/fs/ioctl.c b/fs/ioctl.c index 2d140a713861..f855ea4fc888 100644 --- a/fs/ioctl.c +++ b/fs/ioctl.c @@ -29,7 +29,6 @@ * @arg: command-specific argument for ioctl * * Invokes filesystem specific ->unlocked_ioctl, if one exists; otherwise - * invokes filesystem specific ->ioctl method. If neither method exists, * returns -ENOTTY. * * Returns 0 on success, -errno on error. @@ -39,21 +38,12 @@ static long vfs_ioctl(struct file *filp, unsigned int cmd, { int error = -ENOTTY; - if (!filp->f_op) + if (!filp->f_op || !filp->f_op->unlocked_ioctl) goto out; - if (filp->f_op->unlocked_ioctl) { - error = filp->f_op->unlocked_ioctl(filp, cmd, arg); - if (error == -ENOIOCTLCMD) - error = -EINVAL; - goto out; - } else if (filp->f_op->ioctl) { - lock_kernel(); - error = filp->f_op->ioctl(filp->f_path.dentry->d_inode, - filp, cmd, arg); - unlock_kernel(); - } - + error = filp->f_op->unlocked_ioctl(filp, cmd, arg); + if (error == -ENOIOCTLCMD) + error = -EINVAL; out: return error; } diff --git a/fs/proc/inode.c b/fs/proc/inode.c index 23561cda7245..9c2b5f484879 100644 --- a/fs/proc/inode.c +++ b/fs/proc/inode.c @@ -214,8 +214,7 @@ static long proc_reg_unlocked_ioctl(struct file *file, unsigned int cmd, unsigne { struct proc_dir_entry *pde = PDE(file->f_path.dentry->d_inode); long rv = -ENOTTY; - long (*unlocked_ioctl)(struct file *, unsigned int, unsigned long); - int (*ioctl)(struct inode *, struct file *, unsigned int, unsigned long); + long (*ioctl)(struct file *, unsigned int, unsigned long); spin_lock(&pde->pde_unload_lock); if (!pde->proc_fops) { @@ -223,19 +222,11 @@ static long proc_reg_unlocked_ioctl(struct file *file, unsigned int cmd, unsigne return rv; } pde->pde_users++; - unlocked_ioctl = pde->proc_fops->unlocked_ioctl; - ioctl = pde->proc_fops->ioctl; + ioctl = pde->proc_fops->unlocked_ioctl; spin_unlock(&pde->pde_unload_lock); - if (unlocked_ioctl) { - rv = unlocked_ioctl(file, cmd, arg); - if (rv == -ENOIOCTLCMD) - rv = -EINVAL; - } else if (ioctl) { - WARN_ONCE(1, "Procfs ioctl handlers must use unlocked_ioctl, " - "%pf will be called without the Bkl held\n", ioctl); - rv = ioctl(file->f_path.dentry->d_inode, file, cmd, arg); - } + if (ioctl) + rv = ioctl(file, cmd, arg); pde_users_dec(pde); return rv; diff --git a/include/linux/fs.h b/include/linux/fs.h index 7a0625e26a39..3c786fdaeab6 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1483,8 +1483,8 @@ struct block_device_operations; /* * NOTE: - * read, write, poll, fsync, readv, writev, unlocked_ioctl and compat_ioctl - * can be called without the big kernel lock held in all filesystems. + * all file operations except setlease can be called without + * the big kernel lock held in all filesystems. */ struct file_operations { struct module *owner; @@ -1495,7 +1495,6 @@ struct file_operations { ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t); int (*readdir) (struct file *, void *, filldir_t); unsigned int (*poll) (struct file *, struct poll_table_struct *); - int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long); long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); long (*compat_ioctl) (struct file *, unsigned int, unsigned long); int (*mmap) (struct file *, struct vm_area_struct *); -- cgit v1.2.3 From c7887325230aec47d47a32562a6e26014a0fafca Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 11 Aug 2010 11:26:22 +0100 Subject: Mark arguments to certain syscalls as being const Mark arguments to certain system calls as being const where they should be but aren't. The list includes: (*) The filename arguments of various stat syscalls, execve(), various utimes syscalls and some mount syscalls. (*) The filename arguments of some syscall helpers relating to the above. (*) The buffer argument of various write syscalls. Signed-off-by: David Howells Acked-by: David S. Miller Signed-off-by: Linus Torvalds --- arch/alpha/kernel/osf_sys.c | 6 +++--- arch/alpha/kernel/process.c | 2 +- arch/arm/kernel/sys_arm.c | 4 ++-- arch/arm/kernel/sys_oabi-compat.c | 6 +++--- arch/avr32/include/asm/syscalls.h | 2 +- arch/avr32/kernel/process.c | 3 ++- arch/blackfin/kernel/process.c | 2 +- arch/frv/kernel/process.c | 3 ++- arch/h8300/kernel/process.c | 2 +- arch/ia64/include/asm/unistd.h | 2 +- arch/ia64/kernel/process.c | 2 +- arch/m32r/kernel/process.c | 3 ++- arch/m68k/kernel/process.c | 2 +- arch/m68knommu/kernel/process.c | 2 +- arch/microblaze/kernel/sys_microblaze.c | 2 +- arch/mips/kernel/syscall.c | 2 +- arch/mn10300/kernel/process.c | 2 +- arch/parisc/hpux/fs.c | 7 ++++--- arch/powerpc/kernel/process.c | 2 +- arch/powerpc/kernel/sys_ppc32.c | 2 +- arch/s390/kernel/compat_linux.c | 10 +++++----- arch/s390/kernel/compat_linux.h | 10 +++++----- arch/s390/kernel/entry.h | 2 +- arch/s390/kernel/process.c | 2 +- arch/sh/include/asm/syscalls_32.h | 2 +- arch/sh/include/asm/syscalls_64.h | 2 +- arch/sh/kernel/process_64.c | 2 +- arch/sparc/kernel/sys_sparc32.c | 7 ++++--- arch/um/kernel/exec.c | 6 +++--- arch/um/kernel/internal.h | 2 +- arch/um/kernel/syscall.c | 2 +- arch/x86/ia32/sys_ia32.c | 14 +++++++------- arch/x86/include/asm/sys_ia32.h | 12 ++++++------ arch/x86/include/asm/syscalls.h | 2 +- arch/x86/kernel/entry_64.S | 4 ++-- arch/x86/kernel/process.c | 2 +- arch/xtensa/kernel/process.c | 2 +- fs/compat.c | 23 +++++++++++++---------- fs/stat.c | 29 ++++++++++++++++++----------- fs/utimes.c | 7 ++++--- include/linux/compat.h | 6 +++--- include/linux/fs.h | 6 +++--- include/linux/syscalls.h | 20 ++++++++++---------- include/linux/time.h | 2 +- 44 files changed, 125 insertions(+), 109 deletions(-) (limited to 'fs') diff --git a/arch/alpha/kernel/osf_sys.c b/arch/alpha/kernel/osf_sys.c index 88131c6e42e3..fb58150a7e8f 100644 --- a/arch/alpha/kernel/osf_sys.c +++ b/arch/alpha/kernel/osf_sys.c @@ -244,7 +244,7 @@ do_osf_statfs(struct path *path, struct osf_statfs __user *buffer, return error; } -SYSCALL_DEFINE3(osf_statfs, char __user *, pathname, +SYSCALL_DEFINE3(osf_statfs, const char __user *, pathname, struct osf_statfs __user *, buffer, unsigned long, bufsiz) { struct path path; @@ -358,7 +358,7 @@ osf_procfs_mount(char *dirname, struct procfs_args __user *args, int flags) return do_mount("", dirname, "proc", flags, NULL); } -SYSCALL_DEFINE4(osf_mount, unsigned long, typenr, char __user *, path, +SYSCALL_DEFINE4(osf_mount, unsigned long, typenr, const char __user *, path, int, flag, void __user *, data) { int retval; @@ -932,7 +932,7 @@ SYSCALL_DEFINE3(osf_setitimer, int, which, struct itimerval32 __user *, in, } -SYSCALL_DEFINE2(osf_utimes, char __user *, filename, +SYSCALL_DEFINE2(osf_utimes, const char __user *, filename, struct timeval32 __user *, tvs) { struct timespec tv[2]; diff --git a/arch/alpha/kernel/process.c b/arch/alpha/kernel/process.c index 395a464353b8..88e608aebc8c 100644 --- a/arch/alpha/kernel/process.c +++ b/arch/alpha/kernel/process.c @@ -387,7 +387,7 @@ EXPORT_SYMBOL(dump_elf_task_fp); * sys_execve() executes a new program. */ asmlinkage int -do_sys_execve(char __user *ufilename, char __user * __user *argv, +do_sys_execve(const char __user *ufilename, char __user * __user *argv, char __user * __user *envp, struct pt_regs *regs) { int error; diff --git a/arch/arm/kernel/sys_arm.c b/arch/arm/kernel/sys_arm.c index c23501842b98..5b7c541a4c63 100644 --- a/arch/arm/kernel/sys_arm.c +++ b/arch/arm/kernel/sys_arm.c @@ -62,7 +62,7 @@ asmlinkage int sys_vfork(struct pt_regs *regs) /* sys_execve() executes a new program. * This is called indirectly via a small wrapper */ -asmlinkage int sys_execve(char __user *filenamei, char __user * __user *argv, +asmlinkage int sys_execve(const char __user *filenamei, char __user * __user *argv, char __user * __user *envp, struct pt_regs *regs) { int error; @@ -84,7 +84,7 @@ int kernel_execve(const char *filename, char *const argv[], char *const envp[]) int ret; memset(®s, 0, sizeof(struct pt_regs)); - ret = do_execve((char *)filename, (char __user * __user *)argv, + ret = do_execve(filename, (char __user * __user *)argv, (char __user * __user *)envp, ®s); if (ret < 0) goto out; diff --git a/arch/arm/kernel/sys_oabi-compat.c b/arch/arm/kernel/sys_oabi-compat.c index 33ff678e32f2..4ad8da15ef2b 100644 --- a/arch/arm/kernel/sys_oabi-compat.c +++ b/arch/arm/kernel/sys_oabi-compat.c @@ -141,7 +141,7 @@ static long cp_oldabi_stat64(struct kstat *stat, return copy_to_user(statbuf,&tmp,sizeof(tmp)) ? -EFAULT : 0; } -asmlinkage long sys_oabi_stat64(char __user * filename, +asmlinkage long sys_oabi_stat64(const char __user * filename, struct oldabi_stat64 __user * statbuf) { struct kstat stat; @@ -151,7 +151,7 @@ asmlinkage long sys_oabi_stat64(char __user * filename, return error; } -asmlinkage long sys_oabi_lstat64(char __user * filename, +asmlinkage long sys_oabi_lstat64(const char __user * filename, struct oldabi_stat64 __user * statbuf) { struct kstat stat; @@ -172,7 +172,7 @@ asmlinkage long sys_oabi_fstat64(unsigned long fd, } asmlinkage long sys_oabi_fstatat64(int dfd, - char __user *filename, + const char __user *filename, struct oldabi_stat64 __user *statbuf, int flag) { diff --git a/arch/avr32/include/asm/syscalls.h b/arch/avr32/include/asm/syscalls.h index 66a197266637..ab608b70b24d 100644 --- a/arch/avr32/include/asm/syscalls.h +++ b/arch/avr32/include/asm/syscalls.h @@ -21,7 +21,7 @@ asmlinkage int sys_clone(unsigned long, unsigned long, unsigned long, unsigned long, struct pt_regs *); asmlinkage int sys_vfork(struct pt_regs *); -asmlinkage int sys_execve(char __user *, char __user *__user *, +asmlinkage int sys_execve(const char __user *, char __user *__user *, char __user *__user *, struct pt_regs *); /* kernel/signal.c */ diff --git a/arch/avr32/kernel/process.c b/arch/avr32/kernel/process.c index 2d76515745a4..e5daddff397d 100644 --- a/arch/avr32/kernel/process.c +++ b/arch/avr32/kernel/process.c @@ -383,7 +383,8 @@ asmlinkage int sys_vfork(struct pt_regs *regs) 0, NULL, NULL); } -asmlinkage int sys_execve(char __user *ufilename, char __user *__user *uargv, +asmlinkage int sys_execve(const char __user *ufilename, + char __user *__user *uargv, char __user *__user *uenvp, struct pt_regs *regs) { int error; diff --git a/arch/blackfin/kernel/process.c b/arch/blackfin/kernel/process.c index 93ec07da2e51..a566f61c002a 100644 --- a/arch/blackfin/kernel/process.c +++ b/arch/blackfin/kernel/process.c @@ -209,7 +209,7 @@ copy_thread(unsigned long clone_flags, /* * sys_execve() executes a new program. */ -asmlinkage int sys_execve(char __user *name, char __user * __user *argv, char __user * __user *envp) +asmlinkage int sys_execve(const char __user *name, char __user * __user *argv, char __user * __user *envp) { int error; char *filename; diff --git a/arch/frv/kernel/process.c b/arch/frv/kernel/process.c index 21d0fd19276d..428931cf2f0c 100644 --- a/arch/frv/kernel/process.c +++ b/arch/frv/kernel/process.c @@ -250,7 +250,8 @@ int copy_thread(unsigned long clone_flags, /* * sys_execve() executes a new program. */ -asmlinkage int sys_execve(char __user *name, char __user * __user *argv, char __user * __user *envp) +asmlinkage int sys_execve(const char __user *name, char __user * __user *argv, + char __user * __user *envp) { int error; char * filename; diff --git a/arch/h8300/kernel/process.c b/arch/h8300/kernel/process.c index 8c8b0ffa6ad7..8b7b78d77d5c 100644 --- a/arch/h8300/kernel/process.c +++ b/arch/h8300/kernel/process.c @@ -212,7 +212,7 @@ int copy_thread(unsigned long clone_flags, /* * sys_execve() executes a new program. */ -asmlinkage int sys_execve(char *name, char **argv, char **envp,int dummy,...) +asmlinkage int sys_execve(const char *name, char **argv, char **envp,int dummy,...) { int error; char * filename; diff --git a/arch/ia64/include/asm/unistd.h b/arch/ia64/include/asm/unistd.h index bb8b0fff32b3..46f36fc5125f 100644 --- a/arch/ia64/include/asm/unistd.h +++ b/arch/ia64/include/asm/unistd.h @@ -353,7 +353,7 @@ asmlinkage unsigned long sys_mmap2( int fd, long pgoff); struct pt_regs; struct sigaction; -long sys_execve(char __user *filename, char __user * __user *argv, +long sys_execve(const char __user *filename, char __user * __user *argv, char __user * __user *envp, struct pt_regs *regs); asmlinkage long sys_ia64_pipe(void); asmlinkage long sys_rt_sigaction(int sig, diff --git a/arch/ia64/kernel/process.c b/arch/ia64/kernel/process.c index 53f1648c8b81..a879c03b7f1c 100644 --- a/arch/ia64/kernel/process.c +++ b/arch/ia64/kernel/process.c @@ -633,7 +633,7 @@ dump_fpu (struct pt_regs *pt, elf_fpregset_t dst) } long -sys_execve (char __user *filename, char __user * __user *argv, char __user * __user *envp, +sys_execve (const char __user *filename, char __user * __user *argv, char __user * __user *envp, struct pt_regs *regs) { char *fname; diff --git a/arch/m32r/kernel/process.c b/arch/m32r/kernel/process.c index bc8c8c1511b2..8665a4d868ec 100644 --- a/arch/m32r/kernel/process.c +++ b/arch/m32r/kernel/process.c @@ -288,7 +288,8 @@ asmlinkage int sys_vfork(unsigned long r0, unsigned long r1, unsigned long r2, /* * sys_execve() executes a new program. */ -asmlinkage int sys_execve(char __user *ufilename, char __user * __user *uargv, +asmlinkage int sys_execve(const char __user *ufilename, + char __user * __user *uargv, char __user * __user *uenvp, unsigned long r3, unsigned long r4, unsigned long r5, unsigned long r6, struct pt_regs regs) diff --git a/arch/m68k/kernel/process.c b/arch/m68k/kernel/process.c index 1a6be27cf165..221d0b71ce39 100644 --- a/arch/m68k/kernel/process.c +++ b/arch/m68k/kernel/process.c @@ -315,7 +315,7 @@ EXPORT_SYMBOL(dump_fpu); /* * sys_execve() executes a new program. */ -asmlinkage int sys_execve(char __user *name, char __user * __user *argv, char __user * __user *envp) +asmlinkage int sys_execve(const char __user *name, char __user * __user *argv, char __user * __user *envp) { int error; char * filename; diff --git a/arch/m68knommu/kernel/process.c b/arch/m68knommu/kernel/process.c index 6aa66134b433..6350f68cd026 100644 --- a/arch/m68knommu/kernel/process.c +++ b/arch/m68knommu/kernel/process.c @@ -350,7 +350,7 @@ void dump(struct pt_regs *fp) /* * sys_execve() executes a new program. */ -asmlinkage int sys_execve(char *name, char **argv, char **envp) +asmlinkage int sys_execve(const char *name, char **argv, char **envp) { int error; char * filename; diff --git a/arch/microblaze/kernel/sys_microblaze.c b/arch/microblaze/kernel/sys_microblaze.c index f4e00b7f1259..6abab6ebedbe 100644 --- a/arch/microblaze/kernel/sys_microblaze.c +++ b/arch/microblaze/kernel/sys_microblaze.c @@ -47,7 +47,7 @@ asmlinkage long microblaze_clone(int flags, unsigned long stack, struct pt_regs return do_fork(flags, stack, regs, 0, NULL, NULL); } -asmlinkage long microblaze_execve(char __user *filenamei, char __user *__user *argv, +asmlinkage long microblaze_execve(const char __user *filenamei, char __user *__user *argv, char __user *__user *envp, struct pt_regs *regs) { int error; diff --git a/arch/mips/kernel/syscall.c b/arch/mips/kernel/syscall.c index 58bab2ef257f..bddce0bca195 100644 --- a/arch/mips/kernel/syscall.c +++ b/arch/mips/kernel/syscall.c @@ -254,7 +254,7 @@ asmlinkage int sys_execve(nabi_no_regargs struct pt_regs regs) int error; char * filename; - filename = getname((char __user *) (long)regs.regs[4]); + filename = getname((const char __user *) (long)regs.regs[4]); error = PTR_ERR(filename); if (IS_ERR(filename)) goto out; diff --git a/arch/mn10300/kernel/process.c b/arch/mn10300/kernel/process.c index 82b817c7f7b6..762eb325b949 100644 --- a/arch/mn10300/kernel/process.c +++ b/arch/mn10300/kernel/process.c @@ -268,7 +268,7 @@ asmlinkage long sys_vfork(void) 0, NULL, NULL); } -asmlinkage long sys_execve(char __user *name, +asmlinkage long sys_execve(const char __user *name, char __user * __user *argv, char __user * __user *envp) { diff --git a/arch/parisc/hpux/fs.c b/arch/parisc/hpux/fs.c index 6935123178eb..1444875a7611 100644 --- a/arch/parisc/hpux/fs.c +++ b/arch/parisc/hpux/fs.c @@ -36,7 +36,7 @@ int hpux_execve(struct pt_regs *regs) int error; char *filename; - filename = getname((char __user *) regs->gr[26]); + filename = getname((const char __user *) regs->gr[26]); error = PTR_ERR(filename); if (IS_ERR(filename)) goto out; @@ -169,7 +169,7 @@ static int cp_hpux_stat(struct kstat *stat, struct hpux_stat64 __user *statbuf) return copy_to_user(statbuf,&tmp,sizeof(tmp)) ? -EFAULT : 0; } -long hpux_stat64(char __user *filename, struct hpux_stat64 __user *statbuf) +long hpux_stat64(const char __user *filename, struct hpux_stat64 __user *statbuf) { struct kstat stat; int error = vfs_stat(filename, &stat); @@ -191,7 +191,8 @@ long hpux_fstat64(unsigned int fd, struct hpux_stat64 __user *statbuf) return error; } -long hpux_lstat64(char __user *filename, struct hpux_stat64 __user *statbuf) +long hpux_lstat64(const char __user *filename, + struct hpux_stat64 __user *statbuf) { struct kstat stat; int error = vfs_lstat(filename, &stat); diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c index e78a5add7f15..feacfb789686 100644 --- a/arch/powerpc/kernel/process.c +++ b/arch/powerpc/kernel/process.c @@ -1027,7 +1027,7 @@ int sys_execve(unsigned long a0, unsigned long a1, unsigned long a2, int error; char *filename; - filename = getname((char __user *) a0); + filename = getname((const char __user *) a0); error = PTR_ERR(filename); if (IS_ERR(filename)) goto out; diff --git a/arch/powerpc/kernel/sys_ppc32.c b/arch/powerpc/kernel/sys_ppc32.c index 19471a1cef1a..20fd701a686a 100644 --- a/arch/powerpc/kernel/sys_ppc32.c +++ b/arch/powerpc/kernel/sys_ppc32.c @@ -546,7 +546,7 @@ compat_ssize_t compat_sys_pread64(unsigned int fd, char __user *ubuf, compat_siz return sys_pread64(fd, ubuf, count, ((loff_t)poshi << 32) | poslo); } -compat_ssize_t compat_sys_pwrite64(unsigned int fd, char __user *ubuf, compat_size_t count, +compat_ssize_t compat_sys_pwrite64(unsigned int fd, const char __user *ubuf, compat_size_t count, u32 reg6, u32 poshi, u32 poslo) { return sys_pwrite64(fd, ubuf, count, ((loff_t)poshi << 32) | poslo); diff --git a/arch/s390/kernel/compat_linux.c b/arch/s390/kernel/compat_linux.c index 73b624ed9cd8..1e6449c79ab6 100644 --- a/arch/s390/kernel/compat_linux.c +++ b/arch/s390/kernel/compat_linux.c @@ -436,7 +436,7 @@ sys32_rt_sigqueueinfo(int pid, int sig, compat_siginfo_t __user *uinfo) * sys32_execve() executes a new program after the asm stub has set * things up for us. This should basically do what I want it to. */ -asmlinkage long sys32_execve(char __user *name, compat_uptr_t __user *argv, +asmlinkage long sys32_execve(const char __user *name, compat_uptr_t __user *argv, compat_uptr_t __user *envp) { struct pt_regs *regs = task_pt_regs(current); @@ -570,7 +570,7 @@ static int cp_stat64(struct stat64_emu31 __user *ubuf, struct kstat *stat) return copy_to_user(ubuf,&tmp,sizeof(tmp)) ? -EFAULT : 0; } -asmlinkage long sys32_stat64(char __user * filename, struct stat64_emu31 __user * statbuf) +asmlinkage long sys32_stat64(const char __user * filename, struct stat64_emu31 __user * statbuf) { struct kstat stat; int ret = vfs_stat(filename, &stat); @@ -579,7 +579,7 @@ asmlinkage long sys32_stat64(char __user * filename, struct stat64_emu31 __user return ret; } -asmlinkage long sys32_lstat64(char __user * filename, struct stat64_emu31 __user * statbuf) +asmlinkage long sys32_lstat64(const char __user * filename, struct stat64_emu31 __user * statbuf) { struct kstat stat; int ret = vfs_lstat(filename, &stat); @@ -597,7 +597,7 @@ asmlinkage long sys32_fstat64(unsigned long fd, struct stat64_emu31 __user * sta return ret; } -asmlinkage long sys32_fstatat64(unsigned int dfd, char __user *filename, +asmlinkage long sys32_fstatat64(unsigned int dfd, const char __user *filename, struct stat64_emu31 __user* statbuf, int flag) { struct kstat stat; @@ -655,7 +655,7 @@ asmlinkage long sys32_read(unsigned int fd, char __user * buf, size_t count) return sys_read(fd, buf, count); } -asmlinkage long sys32_write(unsigned int fd, char __user * buf, size_t count) +asmlinkage long sys32_write(unsigned int fd, const char __user * buf, size_t count) { if ((compat_ssize_t) count < 0) return -EINVAL; diff --git a/arch/s390/kernel/compat_linux.h b/arch/s390/kernel/compat_linux.h index cb97afc85c94..9635d759c2b9 100644 --- a/arch/s390/kernel/compat_linux.h +++ b/arch/s390/kernel/compat_linux.h @@ -193,7 +193,7 @@ long sys32_rt_sigprocmask(int how, compat_sigset_t __user *set, compat_sigset_t __user *oset, size_t sigsetsize); long sys32_rt_sigpending(compat_sigset_t __user *set, size_t sigsetsize); long sys32_rt_sigqueueinfo(int pid, int sig, compat_siginfo_t __user *uinfo); -long sys32_execve(char __user *name, compat_uptr_t __user *argv, +long sys32_execve(const char __user *name, compat_uptr_t __user *argv, compat_uptr_t __user *envp); long sys32_init_module(void __user *umod, unsigned long len, const char __user *uargs); @@ -207,16 +207,16 @@ long sys32_sendfile(int out_fd, int in_fd, compat_off_t __user *offset, size_t count); long sys32_sendfile64(int out_fd, int in_fd, compat_loff_t __user *offset, s32 count); -long sys32_stat64(char __user * filename, struct stat64_emu31 __user * statbuf); -long sys32_lstat64(char __user * filename, +long sys32_stat64(const char __user * filename, struct stat64_emu31 __user * statbuf); +long sys32_lstat64(const char __user * filename, struct stat64_emu31 __user * statbuf); long sys32_fstat64(unsigned long fd, struct stat64_emu31 __user * statbuf); -long sys32_fstatat64(unsigned int dfd, char __user *filename, +long sys32_fstatat64(unsigned int dfd, const char __user *filename, struct stat64_emu31 __user* statbuf, int flag); unsigned long old32_mmap(struct mmap_arg_struct_emu31 __user *arg); long sys32_mmap2(struct mmap_arg_struct_emu31 __user *arg); long sys32_read(unsigned int fd, char __user * buf, size_t count); -long sys32_write(unsigned int fd, char __user * buf, size_t count); +long sys32_write(unsigned int fd, const char __user * buf, size_t count); long sys32_fadvise64(int fd, loff_t offset, size_t len, int advise); long sys32_fadvise64_64(struct fadvise64_64_args __user *args); long sys32_sigaction(int sig, const struct old_sigaction32 __user *act, diff --git a/arch/s390/kernel/entry.h b/arch/s390/kernel/entry.h index 5bb1e6b5db26..403fb430a896 100644 --- a/arch/s390/kernel/entry.h +++ b/arch/s390/kernel/entry.h @@ -42,7 +42,7 @@ long sys_clone(unsigned long newsp, unsigned long clone_flags, int __user *parent_tidptr, int __user *child_tidptr); long sys_vfork(void); void execve_tail(void); -long sys_execve(char __user *name, char __user * __user *argv, +long sys_execve(const char __user *name, char __user * __user *argv, char __user * __user *envp); long sys_sigsuspend(int history0, int history1, old_sigset_t mask); long sys_sigaction(int sig, const struct old_sigaction __user *act, diff --git a/arch/s390/kernel/process.c b/arch/s390/kernel/process.c index 1039fdea15b5..7eafaf2662b9 100644 --- a/arch/s390/kernel/process.c +++ b/arch/s390/kernel/process.c @@ -267,7 +267,7 @@ asmlinkage void execve_tail(void) /* * sys_execve() executes a new program. */ -SYSCALL_DEFINE3(execve, char __user *, name, char __user * __user *, argv, +SYSCALL_DEFINE3(execve, const char __user *, name, char __user * __user *, argv, char __user * __user *, envp) { struct pt_regs *regs = task_pt_regs(current); diff --git a/arch/sh/include/asm/syscalls_32.h b/arch/sh/include/asm/syscalls_32.h index 8b30200305c3..be201fdc97aa 100644 --- a/arch/sh/include/asm/syscalls_32.h +++ b/arch/sh/include/asm/syscalls_32.h @@ -19,7 +19,7 @@ asmlinkage int sys_clone(unsigned long clone_flags, unsigned long newsp, asmlinkage int sys_vfork(unsigned long r4, unsigned long r5, unsigned long r6, unsigned long r7, struct pt_regs __regs); -asmlinkage int sys_execve(char __user *ufilename, char __user * __user *uargv, +asmlinkage int sys_execve(const char __user *ufilename, char __user * __user *uargv, char __user * __user *uenvp, unsigned long r7, struct pt_regs __regs); asmlinkage int sys_sigsuspend(old_sigset_t mask, unsigned long r5, diff --git a/arch/sh/include/asm/syscalls_64.h b/arch/sh/include/asm/syscalls_64.h index 751fd8811364..ee519f41d950 100644 --- a/arch/sh/include/asm/syscalls_64.h +++ b/arch/sh/include/asm/syscalls_64.h @@ -21,7 +21,7 @@ asmlinkage int sys_vfork(unsigned long r2, unsigned long r3, unsigned long r4, unsigned long r5, unsigned long r6, unsigned long r7, struct pt_regs *pregs); -asmlinkage int sys_execve(char *ufilename, char **uargv, +asmlinkage int sys_execve(const char *ufilename, char **uargv, char **uenvp, unsigned long r5, unsigned long r6, unsigned long r7, struct pt_regs *pregs); diff --git a/arch/sh/kernel/process_64.c b/arch/sh/kernel/process_64.c index d4ca6480e355..68d128d651b3 100644 --- a/arch/sh/kernel/process_64.c +++ b/arch/sh/kernel/process_64.c @@ -483,7 +483,7 @@ asmlinkage int sys_vfork(unsigned long r2, unsigned long r3, /* * sys_execve() executes a new program. */ -asmlinkage int sys_execve(char *ufilename, char **uargv, +asmlinkage int sys_execve(const char *ufilename, char **uargv, char **uenvp, unsigned long r5, unsigned long r6, unsigned long r7, struct pt_regs *pregs) diff --git a/arch/sparc/kernel/sys_sparc32.c b/arch/sparc/kernel/sys_sparc32.c index c0ca87553e1c..e6375a750d9a 100644 --- a/arch/sparc/kernel/sys_sparc32.c +++ b/arch/sparc/kernel/sys_sparc32.c @@ -162,7 +162,7 @@ static int cp_compat_stat64(struct kstat *stat, return err; } -asmlinkage long compat_sys_stat64(char __user * filename, +asmlinkage long compat_sys_stat64(const char __user * filename, struct compat_stat64 __user *statbuf) { struct kstat stat; @@ -173,7 +173,7 @@ asmlinkage long compat_sys_stat64(char __user * filename, return error; } -asmlinkage long compat_sys_lstat64(char __user * filename, +asmlinkage long compat_sys_lstat64(const char __user * filename, struct compat_stat64 __user *statbuf) { struct kstat stat; @@ -195,7 +195,8 @@ asmlinkage long compat_sys_fstat64(unsigned int fd, return error; } -asmlinkage long compat_sys_fstatat64(unsigned int dfd, char __user *filename, +asmlinkage long compat_sys_fstatat64(unsigned int dfd, + const char __user *filename, struct compat_stat64 __user * statbuf, int flag) { struct kstat stat; diff --git a/arch/um/kernel/exec.c b/arch/um/kernel/exec.c index 97974c1bdd12..59b20d93b6d4 100644 --- a/arch/um/kernel/exec.c +++ b/arch/um/kernel/exec.c @@ -44,7 +44,7 @@ void start_thread(struct pt_regs *regs, unsigned long eip, unsigned long esp) PT_REGS_SP(regs) = esp; } -static long execve1(char *file, char __user * __user *argv, +static long execve1(const char *file, char __user * __user *argv, char __user *__user *env) { long error; @@ -61,7 +61,7 @@ static long execve1(char *file, char __user * __user *argv, return error; } -long um_execve(char *file, char __user *__user *argv, char __user *__user *env) +long um_execve(const char *file, char __user *__user *argv, char __user *__user *env) { long err; @@ -71,7 +71,7 @@ long um_execve(char *file, char __user *__user *argv, char __user *__user *env) return err; } -long sys_execve(char __user *file, char __user *__user *argv, +long sys_execve(const char __user *file, char __user *__user *argv, char __user *__user *env) { long error; diff --git a/arch/um/kernel/internal.h b/arch/um/kernel/internal.h index 3bda43c7a786..1303a105fe91 100644 --- a/arch/um/kernel/internal.h +++ b/arch/um/kernel/internal.h @@ -1 +1 @@ -extern long um_execve(char *file, char __user *__user *argv, char __user *__user *env); +extern long um_execve(const char *file, char __user *__user *argv, char __user *__user *env); diff --git a/arch/um/kernel/syscall.c b/arch/um/kernel/syscall.c index 4393173923f5..7427c0b1930c 100644 --- a/arch/um/kernel/syscall.c +++ b/arch/um/kernel/syscall.c @@ -58,7 +58,7 @@ int kernel_execve(const char *filename, char *const argv[], char *const envp[]) fs = get_fs(); set_fs(KERNEL_DS); - ret = um_execve((char *)filename, (char __user *__user *)argv, + ret = um_execve(filename, (char __user *__user *)argv, (char __user *__user *) envp); set_fs(fs); diff --git a/arch/x86/ia32/sys_ia32.c b/arch/x86/ia32/sys_ia32.c index 3d093311d5e2..849813f398e7 100644 --- a/arch/x86/ia32/sys_ia32.c +++ b/arch/x86/ia32/sys_ia32.c @@ -51,7 +51,7 @@ #define AA(__x) ((unsigned long)(__x)) -asmlinkage long sys32_truncate64(char __user *filename, +asmlinkage long sys32_truncate64(const char __user *filename, unsigned long offset_low, unsigned long offset_high) { @@ -96,7 +96,7 @@ static int cp_stat64(struct stat64 __user *ubuf, struct kstat *stat) return 0; } -asmlinkage long sys32_stat64(char __user *filename, +asmlinkage long sys32_stat64(const char __user *filename, struct stat64 __user *statbuf) { struct kstat stat; @@ -107,7 +107,7 @@ asmlinkage long sys32_stat64(char __user *filename, return ret; } -asmlinkage long sys32_lstat64(char __user *filename, +asmlinkage long sys32_lstat64(const char __user *filename, struct stat64 __user *statbuf) { struct kstat stat; @@ -126,7 +126,7 @@ asmlinkage long sys32_fstat64(unsigned int fd, struct stat64 __user *statbuf) return ret; } -asmlinkage long sys32_fstatat(unsigned int dfd, char __user *filename, +asmlinkage long sys32_fstatat(unsigned int dfd, const char __user *filename, struct stat64 __user *statbuf, int flag) { struct kstat stat; @@ -408,8 +408,8 @@ asmlinkage long sys32_pread(unsigned int fd, char __user *ubuf, u32 count, ((loff_t)AA(poshi) << 32) | AA(poslo)); } -asmlinkage long sys32_pwrite(unsigned int fd, char __user *ubuf, u32 count, - u32 poslo, u32 poshi) +asmlinkage long sys32_pwrite(unsigned int fd, const char __user *ubuf, + u32 count, u32 poslo, u32 poshi) { return sys_pwrite64(fd, ubuf, count, ((loff_t)AA(poshi) << 32) | AA(poslo)); @@ -449,7 +449,7 @@ asmlinkage long sys32_sendfile(int out_fd, int in_fd, return ret; } -asmlinkage long sys32_execve(char __user *name, compat_uptr_t __user *argv, +asmlinkage long sys32_execve(const char __user *name, compat_uptr_t __user *argv, compat_uptr_t __user *envp, struct pt_regs *regs) { long error; diff --git a/arch/x86/include/asm/sys_ia32.h b/arch/x86/include/asm/sys_ia32.h index cf4e2e381cba..cb238526a9f1 100644 --- a/arch/x86/include/asm/sys_ia32.h +++ b/arch/x86/include/asm/sys_ia32.h @@ -18,13 +18,13 @@ #include /* ia32/sys_ia32.c */ -asmlinkage long sys32_truncate64(char __user *, unsigned long, unsigned long); +asmlinkage long sys32_truncate64(const char __user *, unsigned long, unsigned long); asmlinkage long sys32_ftruncate64(unsigned int, unsigned long, unsigned long); -asmlinkage long sys32_stat64(char __user *, struct stat64 __user *); -asmlinkage long sys32_lstat64(char __user *, struct stat64 __user *); +asmlinkage long sys32_stat64(const char __user *, struct stat64 __user *); +asmlinkage long sys32_lstat64(const char __user *, struct stat64 __user *); asmlinkage long sys32_fstat64(unsigned int, struct stat64 __user *); -asmlinkage long sys32_fstatat(unsigned int, char __user *, +asmlinkage long sys32_fstatat(unsigned int, const char __user *, struct stat64 __user *, int); struct mmap_arg_struct32; asmlinkage long sys32_mmap(struct mmap_arg_struct32 __user *); @@ -49,12 +49,12 @@ asmlinkage long sys32_rt_sigpending(compat_sigset_t __user *, compat_size_t); asmlinkage long sys32_rt_sigqueueinfo(int, int, compat_siginfo_t __user *); asmlinkage long sys32_pread(unsigned int, char __user *, u32, u32, u32); -asmlinkage long sys32_pwrite(unsigned int, char __user *, u32, u32, u32); +asmlinkage long sys32_pwrite(unsigned int, const char __user *, u32, u32, u32); asmlinkage long sys32_personality(unsigned long); asmlinkage long sys32_sendfile(int, int, compat_off_t __user *, s32); -asmlinkage long sys32_execve(char __user *, compat_uptr_t __user *, +asmlinkage long sys32_execve(const char __user *, compat_uptr_t __user *, compat_uptr_t __user *, struct pt_regs *); asmlinkage long sys32_clone(unsigned int, unsigned int, struct pt_regs *); diff --git a/arch/x86/include/asm/syscalls.h b/arch/x86/include/asm/syscalls.h index 5c044b43e9a7..feb2ff9bfc2d 100644 --- a/arch/x86/include/asm/syscalls.h +++ b/arch/x86/include/asm/syscalls.h @@ -23,7 +23,7 @@ long sys_iopl(unsigned int, struct pt_regs *); /* kernel/process.c */ int sys_fork(struct pt_regs *); int sys_vfork(struct pt_regs *); -long sys_execve(char __user *, char __user * __user *, +long sys_execve(const char __user *, char __user * __user *, char __user * __user *, struct pt_regs *); long sys_clone(unsigned long, unsigned long, void __user *, void __user *, struct pt_regs *); diff --git a/arch/x86/kernel/entry_64.S b/arch/x86/kernel/entry_64.S index c5ea5cdbe7b3..17be5ec7cbba 100644 --- a/arch/x86/kernel/entry_64.S +++ b/arch/x86/kernel/entry_64.S @@ -1185,13 +1185,13 @@ END(kernel_thread_helper) * execve(). This function needs to use IRET, not SYSRET, to set up all state properly. * * C extern interface: - * extern long execve(char *name, char **argv, char **envp) + * extern long execve(const char *name, char **argv, char **envp) * * asm input arguments: * rdi: name, rsi: argv, rdx: envp * * We want to fallback into: - * extern long sys_execve(char *name, char **argv,char **envp, struct pt_regs *regs) + * extern long sys_execve(const char *name, char **argv,char **envp, struct pt_regs *regs) * * do_sys_execve asm fallback arguments: * rdi: name, rsi: argv, rdx: envp, rcx: fake frame on the stack diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c index d401f1d2d06e..64ecaf0af9af 100644 --- a/arch/x86/kernel/process.c +++ b/arch/x86/kernel/process.c @@ -301,7 +301,7 @@ EXPORT_SYMBOL(kernel_thread); /* * sys_execve() executes a new program. */ -long sys_execve(char __user *name, char __user * __user *argv, +long sys_execve(const char __user *name, char __user * __user *argv, char __user * __user *envp, struct pt_regs *regs) { long error; diff --git a/arch/xtensa/kernel/process.c b/arch/xtensa/kernel/process.c index f167e0f5e05e..7c2f38f68ebb 100644 --- a/arch/xtensa/kernel/process.c +++ b/arch/xtensa/kernel/process.c @@ -318,7 +318,7 @@ long xtensa_clone(unsigned long clone_flags, unsigned long newsp, */ asmlinkage -long xtensa_execve(char __user *name, char __user * __user *argv, +long xtensa_execve(const char __user *name, char __user * __user *argv, char __user * __user *envp, long a3, long a4, long a5, struct pt_regs *regs) diff --git a/fs/compat.c b/fs/compat.c index e6d5d70cf3cf..718c7062aec1 100644 --- a/fs/compat.c +++ b/fs/compat.c @@ -77,7 +77,8 @@ int compat_printk(const char *fmt, ...) * Not all architectures have sys_utime, so implement this in terms * of sys_utimes. */ -asmlinkage long compat_sys_utime(char __user *filename, struct compat_utimbuf __user *t) +asmlinkage long compat_sys_utime(const char __user *filename, + struct compat_utimbuf __user *t) { struct timespec tv[2]; @@ -91,7 +92,7 @@ asmlinkage long compat_sys_utime(char __user *filename, struct compat_utimbuf __ return do_utimes(AT_FDCWD, filename, t ? tv : NULL, 0); } -asmlinkage long compat_sys_utimensat(unsigned int dfd, char __user *filename, struct compat_timespec __user *t, int flags) +asmlinkage long compat_sys_utimensat(unsigned int dfd, const char __user *filename, struct compat_timespec __user *t, int flags) { struct timespec tv[2]; @@ -106,7 +107,7 @@ asmlinkage long compat_sys_utimensat(unsigned int dfd, char __user *filename, st return do_utimes(dfd, filename, t ? tv : NULL, flags); } -asmlinkage long compat_sys_futimesat(unsigned int dfd, char __user *filename, struct compat_timeval __user *t) +asmlinkage long compat_sys_futimesat(unsigned int dfd, const char __user *filename, struct compat_timeval __user *t) { struct timespec tv[2]; @@ -125,7 +126,7 @@ asmlinkage long compat_sys_futimesat(unsigned int dfd, char __user *filename, st return do_utimes(dfd, filename, t ? tv : NULL, 0); } -asmlinkage long compat_sys_utimes(char __user *filename, struct compat_timeval __user *t) +asmlinkage long compat_sys_utimes(const char __user *filename, struct compat_timeval __user *t) { return compat_sys_futimesat(AT_FDCWD, filename, t); } @@ -169,7 +170,7 @@ static int cp_compat_stat(struct kstat *stat, struct compat_stat __user *ubuf) return err; } -asmlinkage long compat_sys_newstat(char __user * filename, +asmlinkage long compat_sys_newstat(const char __user * filename, struct compat_stat __user *statbuf) { struct kstat stat; @@ -181,7 +182,7 @@ asmlinkage long compat_sys_newstat(char __user * filename, return cp_compat_stat(&stat, statbuf); } -asmlinkage long compat_sys_newlstat(char __user * filename, +asmlinkage long compat_sys_newlstat(const char __user * filename, struct compat_stat __user *statbuf) { struct kstat stat; @@ -194,7 +195,8 @@ asmlinkage long compat_sys_newlstat(char __user * filename, } #ifndef __ARCH_WANT_STAT64 -asmlinkage long compat_sys_newfstatat(unsigned int dfd, char __user *filename, +asmlinkage long compat_sys_newfstatat(unsigned int dfd, + const char __user *filename, struct compat_stat __user *statbuf, int flag) { struct kstat stat; @@ -837,9 +839,10 @@ static int do_nfs4_super_data_conv(void *raw_data) #define NCPFS_NAME "ncpfs" #define NFS4_NAME "nfs4" -asmlinkage long compat_sys_mount(char __user * dev_name, char __user * dir_name, - char __user * type, unsigned long flags, - void __user * data) +asmlinkage long compat_sys_mount(const char __user * dev_name, + const char __user * dir_name, + const char __user * type, unsigned long flags, + const void __user * data) { char *kernel_type; unsigned long data_page; diff --git a/fs/stat.c b/fs/stat.c index c4ecd52c5737..12e90e213900 100644 --- a/fs/stat.c +++ b/fs/stat.c @@ -68,7 +68,8 @@ int vfs_fstat(unsigned int fd, struct kstat *stat) } EXPORT_SYMBOL(vfs_fstat); -int vfs_fstatat(int dfd, char __user *filename, struct kstat *stat, int flag) +int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat, + int flag) { struct path path; int error = -EINVAL; @@ -91,13 +92,13 @@ out: } EXPORT_SYMBOL(vfs_fstatat); -int vfs_stat(char __user *name, struct kstat *stat) +int vfs_stat(const char __user *name, struct kstat *stat) { return vfs_fstatat(AT_FDCWD, name, stat, 0); } EXPORT_SYMBOL(vfs_stat); -int vfs_lstat(char __user *name, struct kstat *stat) +int vfs_lstat(const char __user *name, struct kstat *stat) { return vfs_fstatat(AT_FDCWD, name, stat, AT_SYMLINK_NOFOLLOW); } @@ -147,7 +148,8 @@ static int cp_old_stat(struct kstat *stat, struct __old_kernel_stat __user * sta return copy_to_user(statbuf,&tmp,sizeof(tmp)) ? -EFAULT : 0; } -SYSCALL_DEFINE2(stat, char __user *, filename, struct __old_kernel_stat __user *, statbuf) +SYSCALL_DEFINE2(stat, const char __user *, filename, + struct __old_kernel_stat __user *, statbuf) { struct kstat stat; int error; @@ -159,7 +161,8 @@ SYSCALL_DEFINE2(stat, char __user *, filename, struct __old_kernel_stat __user * return cp_old_stat(&stat, statbuf); } -SYSCALL_DEFINE2(lstat, char __user *, filename, struct __old_kernel_stat __user *, statbuf) +SYSCALL_DEFINE2(lstat, const char __user *, filename, + struct __old_kernel_stat __user *, statbuf) { struct kstat stat; int error; @@ -234,7 +237,8 @@ static int cp_new_stat(struct kstat *stat, struct stat __user *statbuf) return copy_to_user(statbuf,&tmp,sizeof(tmp)) ? -EFAULT : 0; } -SYSCALL_DEFINE2(newstat, char __user *, filename, struct stat __user *, statbuf) +SYSCALL_DEFINE2(newstat, const char __user *, filename, + struct stat __user *, statbuf) { struct kstat stat; int error = vfs_stat(filename, &stat); @@ -244,7 +248,8 @@ SYSCALL_DEFINE2(newstat, char __user *, filename, struct stat __user *, statbuf) return cp_new_stat(&stat, statbuf); } -SYSCALL_DEFINE2(newlstat, char __user *, filename, struct stat __user *, statbuf) +SYSCALL_DEFINE2(newlstat, const char __user *, filename, + struct stat __user *, statbuf) { struct kstat stat; int error; @@ -257,7 +262,7 @@ SYSCALL_DEFINE2(newlstat, char __user *, filename, struct stat __user *, statbuf } #if !defined(__ARCH_WANT_STAT64) || defined(__ARCH_WANT_SYS_NEWFSTATAT) -SYSCALL_DEFINE4(newfstatat, int, dfd, char __user *, filename, +SYSCALL_DEFINE4(newfstatat, int, dfd, const char __user *, filename, struct stat __user *, statbuf, int, flag) { struct kstat stat; @@ -355,7 +360,8 @@ static long cp_new_stat64(struct kstat *stat, struct stat64 __user *statbuf) return copy_to_user(statbuf,&tmp,sizeof(tmp)) ? -EFAULT : 0; } -SYSCALL_DEFINE2(stat64, char __user *, filename, struct stat64 __user *, statbuf) +SYSCALL_DEFINE2(stat64, const char __user *, filename, + struct stat64 __user *, statbuf) { struct kstat stat; int error = vfs_stat(filename, &stat); @@ -366,7 +372,8 @@ SYSCALL_DEFINE2(stat64, char __user *, filename, struct stat64 __user *, statbuf return error; } -SYSCALL_DEFINE2(lstat64, char __user *, filename, struct stat64 __user *, statbuf) +SYSCALL_DEFINE2(lstat64, const char __user *, filename, + struct stat64 __user *, statbuf) { struct kstat stat; int error = vfs_lstat(filename, &stat); @@ -388,7 +395,7 @@ SYSCALL_DEFINE2(fstat64, unsigned long, fd, struct stat64 __user *, statbuf) return error; } -SYSCALL_DEFINE4(fstatat64, int, dfd, char __user *, filename, +SYSCALL_DEFINE4(fstatat64, int, dfd, const char __user *, filename, struct stat64 __user *, statbuf, int, flag) { struct kstat stat; diff --git a/fs/utimes.c b/fs/utimes.c index e4c75db5d373..179b58690657 100644 --- a/fs/utimes.c +++ b/fs/utimes.c @@ -126,7 +126,8 @@ out: * must be owner or have write permission. * Else, update from *times, must be owner or super user. */ -long do_utimes(int dfd, char __user *filename, struct timespec *times, int flags) +long do_utimes(int dfd, const char __user *filename, struct timespec *times, + int flags) { int error = -EINVAL; @@ -170,7 +171,7 @@ out: return error; } -SYSCALL_DEFINE4(utimensat, int, dfd, char __user *, filename, +SYSCALL_DEFINE4(utimensat, int, dfd, const char __user *, filename, struct timespec __user *, utimes, int, flags) { struct timespec tstimes[2]; @@ -188,7 +189,7 @@ SYSCALL_DEFINE4(utimensat, int, dfd, char __user *, filename, return do_utimes(dfd, filename, utimes ? tstimes : NULL, flags); } -SYSCALL_DEFINE3(futimesat, int, dfd, char __user *, filename, +SYSCALL_DEFINE3(futimesat, int, dfd, const char __user *, filename, struct timeval __user *, utimes) { struct timeval times[2]; diff --git a/include/linux/compat.h b/include/linux/compat.h index 168f7daa7bde..9ddc8780e8db 100644 --- a/include/linux/compat.h +++ b/include/linux/compat.h @@ -331,7 +331,7 @@ asmlinkage long compat_sys_epoll_pwait(int epfd, const compat_sigset_t __user *sigmask, compat_size_t sigsetsize); -asmlinkage long compat_sys_utimensat(unsigned int dfd, char __user *filename, +asmlinkage long compat_sys_utimensat(unsigned int dfd, const char __user *filename, struct compat_timespec __user *t, int flags); asmlinkage long compat_sys_signalfd(int ufd, @@ -348,9 +348,9 @@ asmlinkage long compat_sys_move_pages(pid_t pid, unsigned long nr_page, const int __user *nodes, int __user *status, int flags); -asmlinkage long compat_sys_futimesat(unsigned int dfd, char __user *filename, +asmlinkage long compat_sys_futimesat(unsigned int dfd, const char __user *filename, struct compat_timeval __user *t); -asmlinkage long compat_sys_newfstatat(unsigned int dfd, char __user * filename, +asmlinkage long compat_sys_newfstatat(unsigned int dfd, const char __user * filename, struct compat_stat __user *statbuf, int flag); asmlinkage long compat_sys_openat(unsigned int dfd, const char __user *filename, diff --git a/include/linux/fs.h b/include/linux/fs.h index 7a0625e26a39..5f0ca2fbb2a0 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2320,10 +2320,10 @@ void inode_set_bytes(struct inode *inode, loff_t bytes); extern int vfs_readdir(struct file *, filldir_t, void *); -extern int vfs_stat(char __user *, struct kstat *); -extern int vfs_lstat(char __user *, struct kstat *); +extern int vfs_stat(const char __user *, struct kstat *); +extern int vfs_lstat(const char __user *, struct kstat *); extern int vfs_fstat(unsigned int, struct kstat *); -extern int vfs_fstatat(int , char __user *, struct kstat *, int); +extern int vfs_fstatat(int , const char __user *, struct kstat *, int); extern int do_vfs_ioctl(struct file *filp, unsigned int fd, unsigned int cmd, unsigned long arg); diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 1b67bd333b5e..6e5d19788634 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -394,7 +394,7 @@ asmlinkage long sys_umount(char __user *name, int flags); asmlinkage long sys_oldumount(char __user *name); asmlinkage long sys_truncate(const char __user *path, long length); asmlinkage long sys_ftruncate(unsigned int fd, unsigned long length); -asmlinkage long sys_stat(char __user *filename, +asmlinkage long sys_stat(const char __user *filename, struct __old_kernel_stat __user *statbuf); asmlinkage long sys_statfs(const char __user * path, struct statfs __user *buf); @@ -403,21 +403,21 @@ asmlinkage long sys_statfs64(const char __user *path, size_t sz, asmlinkage long sys_fstatfs(unsigned int fd, struct statfs __user *buf); asmlinkage long sys_fstatfs64(unsigned int fd, size_t sz, struct statfs64 __user *buf); -asmlinkage long sys_lstat(char __user *filename, +asmlinkage long sys_lstat(const char __user *filename, struct __old_kernel_stat __user *statbuf); asmlinkage long sys_fstat(unsigned int fd, struct __old_kernel_stat __user *statbuf); -asmlinkage long sys_newstat(char __user *filename, +asmlinkage long sys_newstat(const char __user *filename, struct stat __user *statbuf); -asmlinkage long sys_newlstat(char __user *filename, +asmlinkage long sys_newlstat(const char __user *filename, struct stat __user *statbuf); asmlinkage long sys_newfstat(unsigned int fd, struct stat __user *statbuf); asmlinkage long sys_ustat(unsigned dev, struct ustat __user *ubuf); #if BITS_PER_LONG == 32 -asmlinkage long sys_stat64(char __user *filename, +asmlinkage long sys_stat64(const char __user *filename, struct stat64 __user *statbuf); asmlinkage long sys_fstat64(unsigned long fd, struct stat64 __user *statbuf); -asmlinkage long sys_lstat64(char __user *filename, +asmlinkage long sys_lstat64(const char __user *filename, struct stat64 __user *statbuf); asmlinkage long sys_truncate64(const char __user *path, loff_t length); asmlinkage long sys_ftruncate64(unsigned int fd, loff_t length); @@ -760,7 +760,7 @@ asmlinkage long sys_linkat(int olddfd, const char __user *oldname, int newdfd, const char __user *newname, int flags); asmlinkage long sys_renameat(int olddfd, const char __user * oldname, int newdfd, const char __user * newname); -asmlinkage long sys_futimesat(int dfd, char __user *filename, +asmlinkage long sys_futimesat(int dfd, const char __user *filename, struct timeval __user *utimes); asmlinkage long sys_faccessat(int dfd, const char __user *filename, int mode); asmlinkage long sys_fchmodat(int dfd, const char __user * filename, @@ -769,13 +769,13 @@ asmlinkage long sys_fchownat(int dfd, const char __user *filename, uid_t user, gid_t group, int flag); asmlinkage long sys_openat(int dfd, const char __user *filename, int flags, int mode); -asmlinkage long sys_newfstatat(int dfd, char __user *filename, +asmlinkage long sys_newfstatat(int dfd, const char __user *filename, struct stat __user *statbuf, int flag); -asmlinkage long sys_fstatat64(int dfd, char __user *filename, +asmlinkage long sys_fstatat64(int dfd, const char __user *filename, struct stat64 __user *statbuf, int flag); asmlinkage long sys_readlinkat(int dfd, const char __user *path, char __user *buf, int bufsiz); -asmlinkage long sys_utimensat(int dfd, char __user *filename, +asmlinkage long sys_utimensat(int dfd, const char __user *filename, struct timespec __user *utimes, int flags); asmlinkage long sys_unshare(unsigned long unshare_flags); diff --git a/include/linux/time.h b/include/linux/time.h index 12612701b1ae..9f15ac7ab92a 100644 --- a/include/linux/time.h +++ b/include/linux/time.h @@ -150,7 +150,7 @@ extern void do_gettimeofday(struct timeval *tv); extern int do_settimeofday(struct timespec *tv); extern int do_sys_settimeofday(struct timespec *tv, struct timezone *tz); #define do_posix_clock_monotonic_gettime(ts) ktime_get_ts(ts) -extern long do_utimes(int dfd, char __user *filename, struct timespec *times, int flags); +extern long do_utimes(int dfd, const char __user *filename, struct timespec *times, int flags); struct itimerval; extern int do_setitimer(int which, struct itimerval *value, struct itimerval *ovalue); -- cgit v1.2.3 From cd956a1c039a55a0ea58175b9a6e83c45799f3de Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 14 Aug 2010 13:05:31 -0700 Subject: fs/dcache: fix function param name in kernel-doc Fix parameter name in kernel-doc notation (causes a warning). Signed-off-by: Randy Dunlap Signed-off-by: Linus Torvalds --- fs/dcache.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/dcache.c b/fs/dcache.c index 166d35d56868..4d13bf50b7b1 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -1976,7 +1976,7 @@ global_root: * __d_path - return the path of a dentry * @path: the dentry/vfsmount to report * @root: root vfsmnt/dentry (may be modified by this function) - * @buffer: buffer to return value in + * @buf: buffer to return value in * @buflen: buffer length * * Convert a dentry into an ASCII path name. -- cgit v1.2.3 From d7824370e26325c881b665350ce64fb0a4fde24a Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 15 Aug 2010 11:35:52 -0700 Subject: mm: fix up some user-visible effects of the stack guard page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit makes the stack guard page somewhat less visible to user space. It does this by: - not showing the guard page in /proc//maps It looks like lvm-tools will actually read /proc/self/maps to figure out where all its mappings are, and effectively do a specialized "mlockall()" in user space. By not showing the guard page as part of the mapping (by just adding PAGE_SIZE to the start for grows-up pages), lvm-tools ends up not being aware of it. - by also teaching the _real_ mlock() functionality not to try to lock the guard page. That would just expand the mapping down to create a new guard page, so there really is no point in trying to lock it in place. It would perhaps be nice to show the guard page specially in /proc//maps (or at least mark grow-down segments some way), but let's not open ourselves up to more breakage by user space from programs that depends on the exact deails of the 'maps' file. Special thanks to Henrique de Moraes Holschuh for diving into lvm-tools source code to see what was going on with the whole new warning. Reported-and-tested-by: François Valenduc Cc: stable@kernel.org Signed-off-by: Linus Torvalds --- fs/proc/task_mmu.c | 8 +++++++- mm/mlock.c | 8 ++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index aea1d3f1ffb5..439fc1f1c1c4 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -210,6 +210,7 @@ static void show_map_vma(struct seq_file *m, struct vm_area_struct *vma) int flags = vma->vm_flags; unsigned long ino = 0; unsigned long long pgoff = 0; + unsigned long start; dev_t dev = 0; int len; @@ -220,8 +221,13 @@ static void show_map_vma(struct seq_file *m, struct vm_area_struct *vma) pgoff = ((loff_t)vma->vm_pgoff) << PAGE_SHIFT; } + /* We don't show the stack guard page in /proc/maps */ + start = vma->vm_start; + if (vma->vm_flags & VM_GROWSDOWN) + start += PAGE_SIZE; + seq_printf(m, "%08lx-%08lx %c%c%c%c %08llx %02x:%02x %lu %n", - vma->vm_start, + start, vma->vm_end, flags & VM_READ ? 'r' : '-', flags & VM_WRITE ? 'w' : '-', diff --git a/mm/mlock.c b/mm/mlock.c index 3f82720e0515..49e5e4cb8232 100644 --- a/mm/mlock.c +++ b/mm/mlock.c @@ -167,6 +167,14 @@ static long __mlock_vma_pages_range(struct vm_area_struct *vma, if (vma->vm_flags & VM_WRITE) gup_flags |= FOLL_WRITE; + /* We don't try to access the guard page of a stack vma */ + if (vma->vm_flags & VM_GROWSDOWN) { + if (start == vma->vm_start) { + start += PAGE_SIZE; + nr_pages--; + } + } + while (nr_pages > 0) { int i; -- cgit v1.2.3