Commit 92dfe8d2 authored by David Chinner's avatar David Chinner Committed by Tim Shimmin

[XFS] Make hole punching at EOF atomic.

If hole punching at EOF is done as two steps (i.e. truncate then extend)
the file is in a transient state between the two steps where an
application can see the incorrect file size. Punching a hole to EOF needs
to be treated in teh same way as all other hole punching cases so that the
file size is never seen to change.

SGI-PV: 962012
SGI-Modid: xfs-linux-melb:xfs-kern:28641a
Signed-off-by: default avatarDavid Chinner <dgc@sgi.com>
Signed-off-by: default avatarVlad Apostolov <vapo@sgi.com>
Signed-off-by: default avatarTim Shimmin <tes@sgi.com>
parent 511105b3
...@@ -71,6 +71,12 @@ xfs_fsb_to_db_io(struct xfs_iocore *io, xfs_fsblock_t fsb) ...@@ -71,6 +71,12 @@ xfs_fsb_to_db_io(struct xfs_iocore *io, xfs_fsblock_t fsb)
XFS_FSB_TO_DADDR((io)->io_mount, (fsb))); XFS_FSB_TO_DADDR((io)->io_mount, (fsb)));
} }
/*
* Flags for xfs_free_eofblocks
*/
#define XFS_FREE_EOF_LOCK (1<<0)
#define XFS_FREE_EOF_NOLOCK (1<<1)
/* /*
* Prototypes for functions in xfs_rw.c. * Prototypes for functions in xfs_rw.c.
*/ */
...@@ -91,10 +97,12 @@ extern void xfs_ioerror_alert(char *func, struct xfs_mount *mp, ...@@ -91,10 +97,12 @@ extern void xfs_ioerror_alert(char *func, struct xfs_mount *mp,
extern int xfs_rwlock(bhv_desc_t *bdp, bhv_vrwlock_t write_lock); extern int xfs_rwlock(bhv_desc_t *bdp, bhv_vrwlock_t write_lock);
extern void xfs_rwunlock(bhv_desc_t *bdp, bhv_vrwlock_t write_lock); extern void xfs_rwunlock(bhv_desc_t *bdp, bhv_vrwlock_t write_lock);
extern int xfs_setattr(bhv_desc_t *, bhv_vattr_t *vap, int flags, extern int xfs_setattr(bhv_desc_t *, bhv_vattr_t *vap, int flags,
cred_t *credp); cred_t *credp);
extern int xfs_change_file_space(bhv_desc_t *bdp, int cmd, xfs_flock64_t *bf, extern int xfs_change_file_space(bhv_desc_t *bdp, int cmd, xfs_flock64_t *bf,
xfs_off_t offset, cred_t *credp, int flags); xfs_off_t offset, cred_t *credp, int flags);
extern int xfs_set_dmattrs(bhv_desc_t *bdp, u_int evmask, u_int16_t state, extern int xfs_set_dmattrs(bhv_desc_t *bdp, u_int evmask, u_int16_t state,
cred_t *credp); cred_t *credp);
extern int xfs_free_eofblocks(struct xfs_mount *mp, struct xfs_inode *ip,
int flags);
#endif /* __XFS_RW_H__ */ #endif /* __XFS_RW_H__ */
...@@ -1201,13 +1201,15 @@ xfs_fsync( ...@@ -1201,13 +1201,15 @@ xfs_fsync(
} }
/* /*
* This is called by xfs_inactive to free any blocks beyond eof, * This is called by xfs_inactive to free any blocks beyond eof
* when the link count isn't zero. * when the link count isn't zero and by xfs_dm_punch_hole() when
* punching a hole to EOF.
*/ */
STATIC int int
xfs_inactive_free_eofblocks( xfs_free_eofblocks(
xfs_mount_t *mp, xfs_mount_t *mp,
xfs_inode_t *ip) xfs_inode_t *ip,
int flags)
{ {
xfs_trans_t *tp; xfs_trans_t *tp;
int error; int error;
...@@ -1216,6 +1218,7 @@ xfs_inactive_free_eofblocks( ...@@ -1216,6 +1218,7 @@ xfs_inactive_free_eofblocks(
xfs_filblks_t map_len; xfs_filblks_t map_len;
int nimaps; int nimaps;
xfs_bmbt_irec_t imap; xfs_bmbt_irec_t imap;
int use_iolock = (flags & XFS_FREE_EOF_LOCK);
/* /*
* Figure out if there are any blocks beyond the end * Figure out if there are any blocks beyond the end
...@@ -1256,11 +1259,13 @@ xfs_inactive_free_eofblocks( ...@@ -1256,11 +1259,13 @@ xfs_inactive_free_eofblocks(
* cache and we can't * cache and we can't
* do that within a transaction. * do that within a transaction.
*/ */
xfs_ilock(ip, XFS_IOLOCK_EXCL); if (use_iolock)
xfs_ilock(ip, XFS_IOLOCK_EXCL);
error = xfs_itruncate_start(ip, XFS_ITRUNC_DEFINITE, error = xfs_itruncate_start(ip, XFS_ITRUNC_DEFINITE,
ip->i_size); ip->i_size);
if (error) { if (error) {
xfs_iunlock(ip, XFS_IOLOCK_EXCL); if (use_iolock)
xfs_iunlock(ip, XFS_IOLOCK_EXCL);
return error; return error;
} }
...@@ -1297,7 +1302,8 @@ xfs_inactive_free_eofblocks( ...@@ -1297,7 +1302,8 @@ xfs_inactive_free_eofblocks(
error = xfs_trans_commit(tp, error = xfs_trans_commit(tp,
XFS_TRANS_RELEASE_LOG_RES); XFS_TRANS_RELEASE_LOG_RES);
} }
xfs_iunlock(ip, XFS_IOLOCK_EXCL | XFS_ILOCK_EXCL); xfs_iunlock(ip, (use_iolock ? (XFS_IOLOCK_EXCL|XFS_ILOCK_EXCL)
: XFS_ILOCK_EXCL));
} }
return error; return error;
} }
...@@ -1573,7 +1579,8 @@ xfs_release( ...@@ -1573,7 +1579,8 @@ xfs_release(
(ip->i_df.if_flags & XFS_IFEXTENTS)) && (ip->i_df.if_flags & XFS_IFEXTENTS)) &&
(!(ip->i_d.di_flags & (!(ip->i_d.di_flags &
(XFS_DIFLAG_PREALLOC | XFS_DIFLAG_APPEND)))) { (XFS_DIFLAG_PREALLOC | XFS_DIFLAG_APPEND)))) {
if ((error = xfs_inactive_free_eofblocks(mp, ip))) error = xfs_free_eofblocks(mp, ip, XFS_FREE_EOF_LOCK);
if (error)
return error; return error;
/* Update linux inode block count after free above */ /* Update linux inode block count after free above */
vn_to_inode(vp)->i_blocks = XFS_FSB_TO_BB(mp, vn_to_inode(vp)->i_blocks = XFS_FSB_TO_BB(mp,
...@@ -1654,7 +1661,8 @@ xfs_inactive( ...@@ -1654,7 +1661,8 @@ xfs_inactive(
(!(ip->i_d.di_flags & (!(ip->i_d.di_flags &
(XFS_DIFLAG_PREALLOC | XFS_DIFLAG_APPEND)) || (XFS_DIFLAG_PREALLOC | XFS_DIFLAG_APPEND)) ||
(ip->i_delayed_blks != 0)))) { (ip->i_delayed_blks != 0)))) {
if ((error = xfs_inactive_free_eofblocks(mp, ip))) error = xfs_free_eofblocks(mp, ip, XFS_FREE_EOF_LOCK);
if (error)
return VN_INACTIVE_CACHE; return VN_INACTIVE_CACHE;
/* Update linux inode block count after free above */ /* Update linux inode block count after free above */
vn_to_inode(vp)->i_blocks = XFS_FSB_TO_BB(mp, vn_to_inode(vp)->i_blocks = XFS_FSB_TO_BB(mp,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment