Bug fixes for 6.10-rc6:

* 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.
 
 Signed-off-by: Chandan Babu R <chandanbabu@kernel.org>
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYIAB0WIQQjMC4mbgVeU7MxEIYH7y4RirJu9AUCZnvYdwAKCRAH7y4RirJu
 9DRmAP9VwmSgBrVGZ459K6LluP12FoIpzUljEYSiQiyjhxuQJgD/fou/8G+/TTQH
 3TtdmC8Xo7SWRMq9+wPpH5OywbsvZQM=
 =fV8d
 -----END PGP SIGNATURE-----

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
This commit is contained in:
Linus Torvalds 2024-06-29 09:21:40 -07:00
commit 27b31deb90
8 changed files with 95 additions and 53 deletions

View file

@ -4058,20 +4058,32 @@ xfs_bmapi_reserve_delalloc(
xfs_extlen_t indlen;
uint64_t fdblocks;
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
* tag the inode before we return.
*/
aoff = off;
alen = XFS_FILBLKS_MIN(len + prealloc, XFS_MAX_BMBT_EXTLEN);
if (!eof)
alen = XFS_FILBLKS_MIN(alen, got->br_startoff - aoff);
if (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;
xfs_extlen_t extsz = xfs_get_cowextsz_hint(ip);
@ -4090,7 +4102,7 @@ xfs_bmapi_reserve_delalloc(
*/
error = xfs_quota_reserve_blkres(ip, alen);
if (error)
return error;
goto out;
/*
* Split changing sb for alen and indlen since they could be coming
@ -4140,6 +4152,17 @@ xfs_bmapi_reserve_delalloc(
out_unreserve_quota:
if (XFS_IS_QUOTA_ON(mp))
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;
}

View file

@ -996,7 +996,7 @@ struct xfs_getparents_by_handle {
#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_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 */

View file

@ -379,10 +379,13 @@ xfs_dinode_verify_fork(
/*
* A directory small enough to fit in the inode must be stored
* 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 (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)
return __this_address;
}
@ -528,9 +531,19 @@ xfs_dinode_verify(
if (mode && xfs_mode_to_ftype(mode) == XFS_DIR3_FT_UNKNOWN)
return __this_address;
/* No zero-length symlinks/dirs. */
if ((S_ISLNK(mode) || S_ISDIR(mode)) && di_size == 0)
return __this_address;
/*
* No zero-length symlinks/dirs unless they're unlinked and hence being
* 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);
if (fa)

View file

@ -486,13 +486,11 @@ xfs_bmap_punch_delalloc_range(
/*
* Test whether it is appropriate to check an inode for and free post EOF
* blocks. The 'force' parameter determines whether we should also consider
* regular files that are marked preallocated or append-only.
* blocks.
*/
bool
xfs_can_free_eofblocks(
struct xfs_inode *ip,
bool force)
struct xfs_inode *ip)
{
struct xfs_bmbt_irec imap;
struct xfs_mount *mp = ip->i_mount;
@ -526,11 +524,11 @@ xfs_can_free_eofblocks(
return false;
/*
* Do not free real preallocated or append-only files unless the file
* has delalloc blocks and we are forced to remove them.
* Only free real extents for inodes with persistent preallocations or
* the append-only flag.
*/
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;
/*
@ -584,6 +582,22 @@ xfs_free_eofblocks(
/* Wait on dio to ensure i_size has settled. */
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);
if (error) {
ASSERT(xfs_is_shutdown(mp));
@ -891,7 +905,7 @@ xfs_prepare_shift(
* Trim eofblocks to avoid shifting uninitialized post-eof preallocation
* 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);
if (error)
return error;

View file

@ -63,7 +63,7 @@ int xfs_insert_file_space(struct xfs_inode *, xfs_off_t offset,
xfs_off_t len);
/* 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_swap_extents(struct xfs_inode *ip, struct xfs_inode *tip,

View file

@ -1155,7 +1155,7 @@ xfs_inode_free_eofblocks(
}
*lockflags |= XFS_IOLOCK_EXCL;
if (xfs_can_free_eofblocks(ip, false))
if (xfs_can_free_eofblocks(ip))
return xfs_free_eofblocks(ip);
/* inode could be preallocated or append-only */

View file

@ -42,6 +42,7 @@
#include "xfs_pnfs.h"
#include "xfs_parent.h"
#include "xfs_xattr.h"
#include "xfs_sb.h"
struct kmem_cache *xfs_inode_cache;
@ -870,9 +871,16 @@ xfs_init_new_inode(
* this saves us from needing to run a separate transaction to set the
* fork offset in the immediate future.
*/
if (init_xattrs && xfs_has_attr(mp)) {
if (init_xattrs) {
ip->i_forkoff = xfs_default_attroffset(ip) >> 3;
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(
if (!xfs_ilock_nowait(ip, XFS_IOLOCK_EXCL))
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
* frequently and we have delayed allocation blocks outstanding
@ -1856,15 +1864,13 @@ xfs_inode_needs_inactive(
/*
* 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
* cache. Post-eof blocks must be freed, lest we end up with broken
* free space accounting.
* to free.
*
* Note: don't bother with iolock here since lockdep complains about
* acquiring it in reclaim context. We have the only reference to the
* inode at this point anyways.
*/
return xfs_can_free_eofblocks(ip, true);
return xfs_can_free_eofblocks(ip);
}
/*
@ -1947,15 +1953,11 @@ xfs_inactive(
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
* about acquiring it in reclaim context. We have the only
* 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);
goto out;

View file

@ -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) {
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);
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
* them out if the write happens to fail.