Commit 27b31deb authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'xfs-6.10-fixes-5' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux

Pull xfs fixes from Chandan Babu:

 - Always free only post-EOF delayed allocations for files with the
   XFS_DIFLAG_PREALLOC or APPEND flags set.

 - Do not align cow fork delalloc to cowextsz hint when running low on
   space.

 - Allow zero-size symlinks and directories as long as the link count is
   zero.

 - Change XFS_IOC_EXCHANGE_RANGE to be a _IOW only ioctl. This was ioctl
   was introduced during v6.10 developement cycle.

 - xfs_init_new_inode() now creates an attribute fork on a newly created
   inode even if ATTR feature flag is not enabled.

* tag 'xfs-6.10-fixes-5' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux:
  xfs: honor init_xattrs in xfs_init_new_inode for !ATTR fs
  xfs: fix direction in XFS_IOC_EXCHANGE_RANGE
  xfs: allow unlinked symlinks and dirs with zero size
  xfs: restrict when we try to align cow fork delalloc to cowextsz hints
  xfs: fix freeing speculative preallocations for preallocated files
parents ebe70b3a 673cd885
...@@ -4058,20 +4058,32 @@ xfs_bmapi_reserve_delalloc( ...@@ -4058,20 +4058,32 @@ xfs_bmapi_reserve_delalloc(
xfs_extlen_t indlen; xfs_extlen_t indlen;
uint64_t fdblocks; uint64_t fdblocks;
int error; int error;
xfs_fileoff_t aoff = off; xfs_fileoff_t aoff;
bool use_cowextszhint =
whichfork == XFS_COW_FORK && !prealloc;
retry:
/* /*
* Cap the alloc length. Keep track of prealloc so we know whether to * Cap the alloc length. Keep track of prealloc so we know whether to
* tag the inode before we return. * tag the inode before we return.
*/ */
aoff = off;
alen = XFS_FILBLKS_MIN(len + prealloc, XFS_MAX_BMBT_EXTLEN); alen = XFS_FILBLKS_MIN(len + prealloc, XFS_MAX_BMBT_EXTLEN);
if (!eof) if (!eof)
alen = XFS_FILBLKS_MIN(alen, got->br_startoff - aoff); alen = XFS_FILBLKS_MIN(alen, got->br_startoff - aoff);
if (prealloc && alen >= len) if (prealloc && alen >= len)
prealloc = alen - len; prealloc = alen - len;
/* Figure out the extent size, adjust alen */ /*
if (whichfork == XFS_COW_FORK) { * If we're targetting the COW fork but aren't creating a speculative
* posteof preallocation, try to expand the reservation to align with
* the COW extent size hint if there's sufficient free space.
*
* Unlike the data fork, the CoW cancellation functions will free all
* the reservations at inactivation, so we don't require that every
* delalloc reservation have a dirty pagecache.
*/
if (use_cowextszhint) {
struct xfs_bmbt_irec prev; struct xfs_bmbt_irec prev;
xfs_extlen_t extsz = xfs_get_cowextsz_hint(ip); xfs_extlen_t extsz = xfs_get_cowextsz_hint(ip);
...@@ -4090,7 +4102,7 @@ xfs_bmapi_reserve_delalloc( ...@@ -4090,7 +4102,7 @@ xfs_bmapi_reserve_delalloc(
*/ */
error = xfs_quota_reserve_blkres(ip, alen); error = xfs_quota_reserve_blkres(ip, alen);
if (error) if (error)
return error; goto out;
/* /*
* Split changing sb for alen and indlen since they could be coming * Split changing sb for alen and indlen since they could be coming
...@@ -4140,6 +4152,17 @@ xfs_bmapi_reserve_delalloc( ...@@ -4140,6 +4152,17 @@ xfs_bmapi_reserve_delalloc(
out_unreserve_quota: out_unreserve_quota:
if (XFS_IS_QUOTA_ON(mp)) if (XFS_IS_QUOTA_ON(mp))
xfs_quota_unreserve_blkres(ip, alen); xfs_quota_unreserve_blkres(ip, alen);
out:
if (error == -ENOSPC || error == -EDQUOT) {
trace_xfs_delalloc_enospc(ip, off, len);
if (prealloc || use_cowextszhint) {
/* retry without any preallocation */
use_cowextszhint = false;
prealloc = 0;
goto retry;
}
}
return error; return error;
} }
......
...@@ -996,7 +996,7 @@ struct xfs_getparents_by_handle { ...@@ -996,7 +996,7 @@ struct xfs_getparents_by_handle {
#define XFS_IOC_FSGEOMETRY _IOR ('X', 126, struct xfs_fsop_geom) #define XFS_IOC_FSGEOMETRY _IOR ('X', 126, struct xfs_fsop_geom)
#define XFS_IOC_BULKSTAT _IOR ('X', 127, struct xfs_bulkstat_req) #define XFS_IOC_BULKSTAT _IOR ('X', 127, struct xfs_bulkstat_req)
#define XFS_IOC_INUMBERS _IOR ('X', 128, struct xfs_inumbers_req) #define XFS_IOC_INUMBERS _IOR ('X', 128, struct xfs_inumbers_req)
#define XFS_IOC_EXCHANGE_RANGE _IOWR('X', 129, struct xfs_exchange_range) #define XFS_IOC_EXCHANGE_RANGE _IOW ('X', 129, struct xfs_exchange_range)
/* XFS_IOC_GETFSUUID ---------- deprecated 140 */ /* XFS_IOC_GETFSUUID ---------- deprecated 140 */
......
...@@ -379,10 +379,13 @@ xfs_dinode_verify_fork( ...@@ -379,10 +379,13 @@ xfs_dinode_verify_fork(
/* /*
* A directory small enough to fit in the inode must be stored * A directory small enough to fit in the inode must be stored
* in local format. The directory sf <-> extents conversion * in local format. The directory sf <-> extents conversion
* code updates the directory size accordingly. * code updates the directory size accordingly. Directories
* being truncated have zero size and are not subject to this
* check.
*/ */
if (S_ISDIR(mode)) { if (S_ISDIR(mode)) {
if (be64_to_cpu(dip->di_size) <= fork_size && if (dip->di_size &&
be64_to_cpu(dip->di_size) <= fork_size &&
fork_format != XFS_DINODE_FMT_LOCAL) fork_format != XFS_DINODE_FMT_LOCAL)
return __this_address; return __this_address;
} }
...@@ -528,9 +531,19 @@ xfs_dinode_verify( ...@@ -528,9 +531,19 @@ xfs_dinode_verify(
if (mode && xfs_mode_to_ftype(mode) == XFS_DIR3_FT_UNKNOWN) if (mode && xfs_mode_to_ftype(mode) == XFS_DIR3_FT_UNKNOWN)
return __this_address; return __this_address;
/* No zero-length symlinks/dirs. */ /*
if ((S_ISLNK(mode) || S_ISDIR(mode)) && di_size == 0) * No zero-length symlinks/dirs unless they're unlinked and hence being
return __this_address; * inactivated.
*/
if ((S_ISLNK(mode) || S_ISDIR(mode)) && di_size == 0) {
if (dip->di_version > 1) {
if (dip->di_nlink)
return __this_address;
} else {
if (dip->di_onlink)
return __this_address;
}
}
fa = xfs_dinode_verify_nrext64(mp, dip); fa = xfs_dinode_verify_nrext64(mp, dip);
if (fa) if (fa)
......
...@@ -486,13 +486,11 @@ xfs_bmap_punch_delalloc_range( ...@@ -486,13 +486,11 @@ xfs_bmap_punch_delalloc_range(
/* /*
* Test whether it is appropriate to check an inode for and free post EOF * Test whether it is appropriate to check an inode for and free post EOF
* blocks. The 'force' parameter determines whether we should also consider * blocks.
* regular files that are marked preallocated or append-only.
*/ */
bool bool
xfs_can_free_eofblocks( xfs_can_free_eofblocks(
struct xfs_inode *ip, struct xfs_inode *ip)
bool force)
{ {
struct xfs_bmbt_irec imap; struct xfs_bmbt_irec imap;
struct xfs_mount *mp = ip->i_mount; struct xfs_mount *mp = ip->i_mount;
...@@ -526,11 +524,11 @@ xfs_can_free_eofblocks( ...@@ -526,11 +524,11 @@ xfs_can_free_eofblocks(
return false; return false;
/* /*
* Do not free real preallocated or append-only files unless the file * Only free real extents for inodes with persistent preallocations or
* has delalloc blocks and we are forced to remove them. * the append-only flag.
*/ */
if (ip->i_diflags & (XFS_DIFLAG_PREALLOC | XFS_DIFLAG_APPEND)) if (ip->i_diflags & (XFS_DIFLAG_PREALLOC | XFS_DIFLAG_APPEND))
if (!force || ip->i_delayed_blks == 0) if (ip->i_delayed_blks == 0)
return false; return false;
/* /*
...@@ -584,6 +582,22 @@ xfs_free_eofblocks( ...@@ -584,6 +582,22 @@ xfs_free_eofblocks(
/* Wait on dio to ensure i_size has settled. */ /* Wait on dio to ensure i_size has settled. */
inode_dio_wait(VFS_I(ip)); inode_dio_wait(VFS_I(ip));
/*
* For preallocated files only free delayed allocations.
*
* Note that this means we also leave speculative preallocations in
* place for preallocated files.
*/
if (ip->i_diflags & (XFS_DIFLAG_PREALLOC | XFS_DIFLAG_APPEND)) {
if (ip->i_delayed_blks) {
xfs_bmap_punch_delalloc_range(ip,
round_up(XFS_ISIZE(ip), mp->m_sb.sb_blocksize),
LLONG_MAX);
}
xfs_inode_clear_eofblocks_tag(ip);
return 0;
}
error = xfs_trans_alloc(mp, &M_RES(mp)->tr_itruncate, 0, 0, 0, &tp); error = xfs_trans_alloc(mp, &M_RES(mp)->tr_itruncate, 0, 0, 0, &tp);
if (error) { if (error) {
ASSERT(xfs_is_shutdown(mp)); ASSERT(xfs_is_shutdown(mp));
...@@ -891,7 +905,7 @@ xfs_prepare_shift( ...@@ -891,7 +905,7 @@ xfs_prepare_shift(
* Trim eofblocks to avoid shifting uninitialized post-eof preallocation * Trim eofblocks to avoid shifting uninitialized post-eof preallocation
* into the accessible region of the file. * into the accessible region of the file.
*/ */
if (xfs_can_free_eofblocks(ip, true)) { if (xfs_can_free_eofblocks(ip)) {
error = xfs_free_eofblocks(ip); error = xfs_free_eofblocks(ip);
if (error) if (error)
return error; return error;
......
...@@ -63,7 +63,7 @@ int xfs_insert_file_space(struct xfs_inode *, xfs_off_t offset, ...@@ -63,7 +63,7 @@ int xfs_insert_file_space(struct xfs_inode *, xfs_off_t offset,
xfs_off_t len); xfs_off_t len);
/* EOF block manipulation functions */ /* EOF block manipulation functions */
bool xfs_can_free_eofblocks(struct xfs_inode *ip, bool force); bool xfs_can_free_eofblocks(struct xfs_inode *ip);
int xfs_free_eofblocks(struct xfs_inode *ip); int xfs_free_eofblocks(struct xfs_inode *ip);
int xfs_swap_extents(struct xfs_inode *ip, struct xfs_inode *tip, int xfs_swap_extents(struct xfs_inode *ip, struct xfs_inode *tip,
......
...@@ -1155,7 +1155,7 @@ xfs_inode_free_eofblocks( ...@@ -1155,7 +1155,7 @@ xfs_inode_free_eofblocks(
} }
*lockflags |= XFS_IOLOCK_EXCL; *lockflags |= XFS_IOLOCK_EXCL;
if (xfs_can_free_eofblocks(ip, false)) if (xfs_can_free_eofblocks(ip))
return xfs_free_eofblocks(ip); return xfs_free_eofblocks(ip);
/* inode could be preallocated or append-only */ /* inode could be preallocated or append-only */
......
...@@ -42,6 +42,7 @@ ...@@ -42,6 +42,7 @@
#include "xfs_pnfs.h" #include "xfs_pnfs.h"
#include "xfs_parent.h" #include "xfs_parent.h"
#include "xfs_xattr.h" #include "xfs_xattr.h"
#include "xfs_sb.h"
struct kmem_cache *xfs_inode_cache; struct kmem_cache *xfs_inode_cache;
...@@ -870,9 +871,16 @@ xfs_init_new_inode( ...@@ -870,9 +871,16 @@ xfs_init_new_inode(
* this saves us from needing to run a separate transaction to set the * this saves us from needing to run a separate transaction to set the
* fork offset in the immediate future. * fork offset in the immediate future.
*/ */
if (init_xattrs && xfs_has_attr(mp)) { if (init_xattrs) {
ip->i_forkoff = xfs_default_attroffset(ip) >> 3; ip->i_forkoff = xfs_default_attroffset(ip) >> 3;
xfs_ifork_init_attr(ip, XFS_DINODE_FMT_EXTENTS, 0); xfs_ifork_init_attr(ip, XFS_DINODE_FMT_EXTENTS, 0);
if (!xfs_has_attr(mp)) {
spin_lock(&mp->m_sb_lock);
xfs_add_attr(mp);
spin_unlock(&mp->m_sb_lock);
xfs_log_sb(tp);
}
} }
/* /*
...@@ -1595,7 +1603,7 @@ xfs_release( ...@@ -1595,7 +1603,7 @@ xfs_release(
if (!xfs_ilock_nowait(ip, XFS_IOLOCK_EXCL)) if (!xfs_ilock_nowait(ip, XFS_IOLOCK_EXCL))
return 0; return 0;
if (xfs_can_free_eofblocks(ip, false)) { if (xfs_can_free_eofblocks(ip)) {
/* /*
* Check if the inode is being opened, written and closed * Check if the inode is being opened, written and closed
* frequently and we have delayed allocation blocks outstanding * frequently and we have delayed allocation blocks outstanding
...@@ -1856,15 +1864,13 @@ xfs_inode_needs_inactive( ...@@ -1856,15 +1864,13 @@ xfs_inode_needs_inactive(
/* /*
* This file isn't being freed, so check if there are post-eof blocks * This file isn't being freed, so check if there are post-eof blocks
* to free. @force is true because we are evicting an inode from the * to free.
* cache. Post-eof blocks must be freed, lest we end up with broken
* free space accounting.
* *
* Note: don't bother with iolock here since lockdep complains about * Note: don't bother with iolock here since lockdep complains about
* acquiring it in reclaim context. We have the only reference to the * acquiring it in reclaim context. We have the only reference to the
* inode at this point anyways. * inode at this point anyways.
*/ */
return xfs_can_free_eofblocks(ip, true); return xfs_can_free_eofblocks(ip);
} }
/* /*
...@@ -1947,15 +1953,11 @@ xfs_inactive( ...@@ -1947,15 +1953,11 @@ xfs_inactive(
if (VFS_I(ip)->i_nlink != 0) { if (VFS_I(ip)->i_nlink != 0) {
/* /*
* force is true because we are evicting an inode from the
* cache. Post-eof blocks must be freed, lest we end up with
* broken free space accounting.
*
* Note: don't bother with iolock here since lockdep complains * Note: don't bother with iolock here since lockdep complains
* about acquiring it in reclaim context. We have the only * about acquiring it in reclaim context. We have the only
* reference to the inode at this point anyways. * reference to the inode at this point anyways.
*/ */
if (xfs_can_free_eofblocks(ip, true)) if (xfs_can_free_eofblocks(ip))
error = xfs_free_eofblocks(ip); error = xfs_free_eofblocks(ip);
goto out; goto out;
......
...@@ -1148,33 +1148,23 @@ xfs_buffered_write_iomap_begin( ...@@ -1148,33 +1148,23 @@ xfs_buffered_write_iomap_begin(
} }
} }
retry:
error = xfs_bmapi_reserve_delalloc(ip, allocfork, offset_fsb,
end_fsb - offset_fsb, prealloc_blocks,
allocfork == XFS_DATA_FORK ? &imap : &cmap,
allocfork == XFS_DATA_FORK ? &icur : &ccur,
allocfork == XFS_DATA_FORK ? eof : cow_eof);
switch (error) {
case 0:
break;
case -ENOSPC:
case -EDQUOT:
/* retry without any preallocation */
trace_xfs_delalloc_enospc(ip, offset, count);
if (prealloc_blocks) {
prealloc_blocks = 0;
goto retry;
}
fallthrough;
default:
goto out_unlock;
}
if (allocfork == XFS_COW_FORK) { if (allocfork == XFS_COW_FORK) {
error = xfs_bmapi_reserve_delalloc(ip, allocfork, offset_fsb,
end_fsb - offset_fsb, prealloc_blocks, &cmap,
&ccur, cow_eof);
if (error)
goto out_unlock;
trace_xfs_iomap_alloc(ip, offset, count, allocfork, &cmap); trace_xfs_iomap_alloc(ip, offset, count, allocfork, &cmap);
goto found_cow; goto found_cow;
} }
error = xfs_bmapi_reserve_delalloc(ip, allocfork, offset_fsb,
end_fsb - offset_fsb, prealloc_blocks, &imap, &icur,
eof);
if (error)
goto out_unlock;
/* /*
* Flag newly allocated delalloc blocks with IOMAP_F_NEW so we punch * Flag newly allocated delalloc blocks with IOMAP_F_NEW so we punch
* them out if the write happens to fail. * them out if the write happens to fail.
......
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