Skip to content

Commit

Permalink
Merge tag 'xfs-for-linus-4.10-rc6-5' of git://git.kernel.org/pub/scm/…
Browse files Browse the repository at this point in the history
…fs/xfs/xfs-linux

Pull xfs uodates from Darrick Wong:
 "I have some more fixes this week: better input validation, corruption
  avoidance, build fixes, memory leak fixes, and a couple from Christoph
  to avoid an ENOSPC failure.

  Summary:
   - Fix race conditions in the CoW code
   - Fix some incorrect input validation checks
   - Avoid crashing fs by running out of space when freeing inodes
   - Fix toctou race wrt whether or not an inode has an attr
   - Fix build error on arm
   - Fix page refcount corruption when readahead fails
   - Don't corrupt userspace in the bmap ioctl"

* tag 'xfs-for-linus-4.10-rc6-5' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux:
  xfs: prevent quotacheck from overloading inode lru
  xfs: fix bmv_count confusion w/ shared extents
  xfs: clear _XBF_PAGES from buffers when readahead page
  xfs: extsize hints are not unlikely in xfs_bmap_btalloc
  xfs: remove racy hasattr check from attr ops
  xfs: use per-AG reservations for the finobt
  xfs: only update mount/resv fields on success in __xfs_ag_resv_init
  xfs: verify dirblocklog correctly
  xfs: fix COW writeback race
  • Loading branch information
torvalds committed Jan 27, 2017
2 parents 5906374 + e0d76fa commit 3365135
Show file tree
Hide file tree
Showing 13 changed files with 220 additions and 63 deletions.
70 changes: 55 additions & 15 deletions fs/xfs/libxfs/xfs_ag_resv.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
#include "xfs_rmap_btree.h"
#include "xfs_btree.h"
#include "xfs_refcount_btree.h"
#include "xfs_ialloc_btree.h"

/*
* Per-AG Block Reservations
Expand Down Expand Up @@ -200,29 +201,39 @@ __xfs_ag_resv_init(
struct xfs_mount *mp = pag->pag_mount;
struct xfs_ag_resv *resv;
int error;
xfs_extlen_t reserved;

resv = xfs_perag_resv(pag, type);
if (used > ask)
ask = used;
resv->ar_asked = ask;
resv->ar_reserved = resv->ar_orig_reserved = ask - used;
mp->m_ag_max_usable -= ask;
reserved = ask - used;

trace_xfs_ag_resv_init(pag, type, ask);

error = xfs_mod_fdblocks(mp, -(int64_t)resv->ar_reserved, true);
if (error)
error = xfs_mod_fdblocks(mp, -(int64_t)reserved, true);
if (error) {
trace_xfs_ag_resv_init_error(pag->pag_mount, pag->pag_agno,
error, _RET_IP_);
xfs_warn(mp,
"Per-AG reservation for AG %u failed. Filesystem may run out of space.",
pag->pag_agno);
return error;
}

return error;
mp->m_ag_max_usable -= ask;

resv = xfs_perag_resv(pag, type);
resv->ar_asked = ask;
resv->ar_reserved = resv->ar_orig_reserved = reserved;

trace_xfs_ag_resv_init(pag, type, ask);
return 0;
}

/* Create a per-AG block reservation. */
int
xfs_ag_resv_init(
struct xfs_perag *pag)
{
struct xfs_mount *mp = pag->pag_mount;
xfs_agnumber_t agno = pag->pag_agno;
xfs_extlen_t ask;
xfs_extlen_t used;
int error = 0;
Expand All @@ -231,23 +242,45 @@ xfs_ag_resv_init(
if (pag->pag_meta_resv.ar_asked == 0) {
ask = used = 0;

error = xfs_refcountbt_calc_reserves(pag->pag_mount,
pag->pag_agno, &ask, &used);
error = xfs_refcountbt_calc_reserves(mp, agno, &ask, &used);
if (error)
goto out;

error = __xfs_ag_resv_init(pag, XFS_AG_RESV_METADATA,
ask, used);
error = xfs_finobt_calc_reserves(mp, agno, &ask, &used);
if (error)
goto out;

error = __xfs_ag_resv_init(pag, XFS_AG_RESV_METADATA,
ask, used);
if (error) {
/*
* Because we didn't have per-AG reservations when the
* finobt feature was added we might not be able to
* reserve all needed blocks. Warn and fall back to the
* old and potentially buggy code in that case, but
* ensure we do have the reservation for the refcountbt.
*/
ask = used = 0;

mp->m_inotbt_nores = true;

error = xfs_refcountbt_calc_reserves(mp, agno, &ask,
&used);
if (error)
goto out;

error = __xfs_ag_resv_init(pag, XFS_AG_RESV_METADATA,
ask, used);
if (error)
goto out;
}
}

/* Create the AGFL metadata reservation */
if (pag->pag_agfl_resv.ar_asked == 0) {
ask = used = 0;

error = xfs_rmapbt_calc_reserves(pag->pag_mount, pag->pag_agno,
&ask, &used);
error = xfs_rmapbt_calc_reserves(mp, agno, &ask, &used);
if (error)
goto out;

Expand All @@ -256,9 +289,16 @@ xfs_ag_resv_init(
goto out;
}

#ifdef DEBUG
/* need to read in the AGF for the ASSERT below to work */
error = xfs_alloc_pagf_init(pag->pag_mount, NULL, pag->pag_agno, 0);
if (error)
return error;

ASSERT(xfs_perag_resv(pag, XFS_AG_RESV_METADATA)->ar_reserved +
xfs_perag_resv(pag, XFS_AG_RESV_AGFL)->ar_reserved <=
pag->pagf_freeblks + pag->pagf_flcount);
#endif
out:
return error;
}
Expand Down
6 changes: 0 additions & 6 deletions fs/xfs/libxfs/xfs_attr.c
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,6 @@ xfs_attr_get(
if (XFS_FORCED_SHUTDOWN(ip->i_mount))
return -EIO;

if (!xfs_inode_hasattr(ip))
return -ENOATTR;

error = xfs_attr_args_init(&args, ip, name, flags);
if (error)
return error;
Expand Down Expand Up @@ -392,9 +389,6 @@ xfs_attr_remove(
if (XFS_FORCED_SHUTDOWN(dp->i_mount))
return -EIO;

if (!xfs_inode_hasattr(dp))
return -ENOATTR;

error = xfs_attr_args_init(&args, dp, name, flags);
if (error)
return error;
Expand Down
48 changes: 34 additions & 14 deletions fs/xfs/libxfs/xfs_bmap.c
Original file line number Diff line number Diff line change
Expand Up @@ -3629,7 +3629,7 @@ xfs_bmap_btalloc(
align = xfs_get_cowextsz_hint(ap->ip);
else if (xfs_alloc_is_userdata(ap->datatype))
align = xfs_get_extsz_hint(ap->ip);
if (unlikely(align)) {
if (align) {
error = xfs_bmap_extsize_align(mp, &ap->got, &ap->prev,
align, 0, ap->eof, 0, ap->conv,
&ap->offset, &ap->length);
Expand Down Expand Up @@ -3701,7 +3701,7 @@ xfs_bmap_btalloc(
args.minlen = ap->minlen;
}
/* apply extent size hints if obtained earlier */
if (unlikely(align)) {
if (align) {
args.prod = align;
if ((args.mod = (xfs_extlen_t)do_mod(ap->offset, args.prod)))
args.mod = (xfs_extlen_t)(args.prod - args.mod);
Expand Down Expand Up @@ -4514,8 +4514,6 @@ xfs_bmapi_write(
int n; /* current extent index */
xfs_fileoff_t obno; /* old block number (offset) */
int whichfork; /* data or attr fork */
char inhole; /* current location is hole in file */
char wasdelay; /* old extent was delayed */

#ifdef DEBUG
xfs_fileoff_t orig_bno; /* original block number value */
Expand Down Expand Up @@ -4603,22 +4601,44 @@ xfs_bmapi_write(
bma.firstblock = firstblock;

while (bno < end && n < *nmap) {
inhole = eof || bma.got.br_startoff > bno;
wasdelay = !inhole && isnullstartblock(bma.got.br_startblock);
bool need_alloc = false, wasdelay = false;

/*
* Make sure we only reflink into a hole.
*/
if (flags & XFS_BMAPI_REMAP)
ASSERT(inhole);
if (flags & XFS_BMAPI_COWFORK)
ASSERT(!inhole);
/* in hole or beyoned EOF? */
if (eof || bma.got.br_startoff > bno) {
if (flags & XFS_BMAPI_DELALLOC) {
/*
* For the COW fork we can reasonably get a
* request for converting an extent that races
* with other threads already having converted
* part of it, as there converting COW to
* regular blocks is not protected using the
* IOLOCK.
*/
ASSERT(flags & XFS_BMAPI_COWFORK);
if (!(flags & XFS_BMAPI_COWFORK)) {
error = -EIO;
goto error0;
}

if (eof || bno >= end)
break;
} else {
need_alloc = true;
}
} else {
/*
* Make sure we only reflink into a hole.
*/
ASSERT(!(flags & XFS_BMAPI_REMAP));
if (isnullstartblock(bma.got.br_startblock))
wasdelay = true;
}

/*
* First, deal with the hole before the allocated space
* that we found, if any.
*/
if (inhole || wasdelay) {
if (need_alloc || wasdelay) {
bma.eof = eof;
bma.conv = !!(flags & XFS_BMAPI_CONVERT);
bma.wasdel = wasdelay;
Expand Down
6 changes: 5 additions & 1 deletion fs/xfs/libxfs/xfs_bmap.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,9 @@ struct xfs_extent_free_item
/* Map something in the CoW fork. */
#define XFS_BMAPI_COWFORK 0x200

/* Only convert delalloc space, don't allocate entirely new extents */
#define XFS_BMAPI_DELALLOC 0x400

#define XFS_BMAPI_FLAGS \
{ XFS_BMAPI_ENTIRE, "ENTIRE" }, \
{ XFS_BMAPI_METADATA, "METADATA" }, \
Expand All @@ -120,7 +123,8 @@ struct xfs_extent_free_item
{ XFS_BMAPI_CONVERT, "CONVERT" }, \
{ XFS_BMAPI_ZERO, "ZERO" }, \
{ XFS_BMAPI_REMAP, "REMAP" }, \
{ XFS_BMAPI_COWFORK, "COWFORK" }
{ XFS_BMAPI_COWFORK, "COWFORK" }, \
{ XFS_BMAPI_DELALLOC, "DELALLOC" }


static inline int xfs_bmapi_aflag(int w)
Expand Down
90 changes: 87 additions & 3 deletions fs/xfs/libxfs/xfs_ialloc_btree.c
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,12 @@ xfs_finobt_set_root(
}

STATIC int
xfs_inobt_alloc_block(
__xfs_inobt_alloc_block(
struct xfs_btree_cur *cur,
union xfs_btree_ptr *start,
union xfs_btree_ptr *new,
int *stat)
int *stat,
enum xfs_ag_resv_type resv)
{
xfs_alloc_arg_t args; /* block allocation args */
int error; /* error return value */
Expand All @@ -103,6 +104,7 @@ xfs_inobt_alloc_block(
args.maxlen = 1;
args.prod = 1;
args.type = XFS_ALLOCTYPE_NEAR_BNO;
args.resv = resv;

error = xfs_alloc_vextent(&args);
if (error) {
Expand All @@ -122,6 +124,27 @@ xfs_inobt_alloc_block(
return 0;
}

STATIC int
xfs_inobt_alloc_block(
struct xfs_btree_cur *cur,
union xfs_btree_ptr *start,
union xfs_btree_ptr *new,
int *stat)
{
return __xfs_inobt_alloc_block(cur, start, new, stat, XFS_AG_RESV_NONE);
}

STATIC int
xfs_finobt_alloc_block(
struct xfs_btree_cur *cur,
union xfs_btree_ptr *start,
union xfs_btree_ptr *new,
int *stat)
{
return __xfs_inobt_alloc_block(cur, start, new, stat,
XFS_AG_RESV_METADATA);
}

STATIC int
xfs_inobt_free_block(
struct xfs_btree_cur *cur,
Expand Down Expand Up @@ -328,7 +351,7 @@ static const struct xfs_btree_ops xfs_finobt_ops = {

.dup_cursor = xfs_inobt_dup_cursor,
.set_root = xfs_finobt_set_root,
.alloc_block = xfs_inobt_alloc_block,
.alloc_block = xfs_finobt_alloc_block,
.free_block = xfs_inobt_free_block,
.get_minrecs = xfs_inobt_get_minrecs,
.get_maxrecs = xfs_inobt_get_maxrecs,
Expand Down Expand Up @@ -480,3 +503,64 @@ xfs_inobt_rec_check_count(
return 0;
}
#endif /* DEBUG */

static xfs_extlen_t
xfs_inobt_max_size(
struct xfs_mount *mp)
{
/* Bail out if we're uninitialized, which can happen in mkfs. */
if (mp->m_inobt_mxr[0] == 0)
return 0;

return xfs_btree_calc_size(mp, mp->m_inobt_mnr,
(uint64_t)mp->m_sb.sb_agblocks * mp->m_sb.sb_inopblock /
XFS_INODES_PER_CHUNK);
}

static int
xfs_inobt_count_blocks(
struct xfs_mount *mp,
xfs_agnumber_t agno,
xfs_btnum_t btnum,
xfs_extlen_t *tree_blocks)
{
struct xfs_buf *agbp;
struct xfs_btree_cur *cur;
int error;

error = xfs_ialloc_read_agi(mp, NULL, agno, &agbp);
if (error)
return error;

cur = xfs_inobt_init_cursor(mp, NULL, agbp, agno, btnum);
error = xfs_btree_count_blocks(cur, tree_blocks);
xfs_btree_del_cursor(cur, error ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR);
xfs_buf_relse(agbp);

return error;
}

/*
* Figure out how many blocks to reserve and how many are used by this btree.
*/
int
xfs_finobt_calc_reserves(
struct xfs_mount *mp,
xfs_agnumber_t agno,
xfs_extlen_t *ask,
xfs_extlen_t *used)
{
xfs_extlen_t tree_len = 0;
int error;

if (!xfs_sb_version_hasfinobt(&mp->m_sb))
return 0;

error = xfs_inobt_count_blocks(mp, agno, XFS_BTNUM_FINO, &tree_len);
if (error)
return error;

*ask += xfs_inobt_max_size(mp);
*used += tree_len;
return 0;
}
3 changes: 3 additions & 0 deletions fs/xfs/libxfs/xfs_ialloc_btree.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,7 @@ int xfs_inobt_rec_check_count(struct xfs_mount *,
#define xfs_inobt_rec_check_count(mp, rec) 0
#endif /* DEBUG */

int xfs_finobt_calc_reserves(struct xfs_mount *mp, xfs_agnumber_t agno,
xfs_extlen_t *ask, xfs_extlen_t *used);

#endif /* __XFS_IALLOC_BTREE_H__ */
2 changes: 1 addition & 1 deletion fs/xfs/libxfs/xfs_sb.c
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ xfs_mount_validate_sb(
sbp->sb_blocklog < XFS_MIN_BLOCKSIZE_LOG ||
sbp->sb_blocklog > XFS_MAX_BLOCKSIZE_LOG ||
sbp->sb_blocksize != (1 << sbp->sb_blocklog) ||
sbp->sb_dirblklog > XFS_MAX_BLOCKSIZE_LOG ||
sbp->sb_dirblklog + sbp->sb_blocklog > XFS_MAX_BLOCKSIZE_LOG ||
sbp->sb_inodesize < XFS_DINODE_MIN_SIZE ||
sbp->sb_inodesize > XFS_DINODE_MAX_SIZE ||
sbp->sb_inodelog < XFS_DINODE_MIN_LOG ||
Expand Down
Loading

0 comments on commit 3365135

Please sign in to comment.