Skip to content

Commit

Permalink
Merge tag 'xfs-4.15-fixes-4' of git://git.kernel.org/pub/scm/fs/xfs/x…
Browse files Browse the repository at this point in the history
…fs-linux

Pull xfs fixes from Darrick Wong:
 "Here are some bug fixes for 4.15-rc2.

   - fix memory leaks that appeared after removing ifork inline data
     buffer

   - recover deferred rmap update log items in correct order

   - fix memory leaks when buffer construction fails

   - fix memory leaks when bmbt is corrupt

   - fix some uninitialized variables and math problems in the quota
     scrubber

   - add some omitted attribution tags on the log replay commit

   - fix some UBSAN complaints about integer overflows with large sparse
     files

   - implement an effective inode mode check in online fsck

   - fix log's inability to retry quota item writeout due to transient
     errors"

* tag 'xfs-4.15-fixes-4' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux:
  xfs: Properly retry failed dquot items in case of error during buffer writeback
  xfs: scrub inode mode properly
  xfs: remove unused parameter from xfs_writepage_map
  xfs: ubsan fixes
  xfs: calculate correct offset in xfs_scrub_quota_item
  xfs: fix uninitialized variable in xfs_scrub_quota
  xfs: fix leaks on corruption errors in xfs_bmap.c
  xfs: fortify xfs_alloc_buftarg error handling
  xfs: log recovery should replay deferred ops in order
  xfs: always free inline data before resetting inode fork during ifree
  • Loading branch information
torvalds committed Dec 2, 2017
2 parents e1ba1c9 + 373b058 commit 788c1da
Show file tree
Hide file tree
Showing 13 changed files with 190 additions and 61 deletions.
6 changes: 4 additions & 2 deletions fs/xfs/libxfs/xfs_bmap.c
Original file line number Diff line number Diff line change
Expand Up @@ -5662,7 +5662,8 @@ xfs_bmap_collapse_extents(
*done = true;
goto del_cursor;
}
XFS_WANT_CORRUPTED_RETURN(mp, !isnullstartblock(got.br_startblock));
XFS_WANT_CORRUPTED_GOTO(mp, !isnullstartblock(got.br_startblock),
del_cursor);

new_startoff = got.br_startoff - offset_shift_fsb;
if (xfs_iext_peek_prev_extent(ifp, &icur, &prev)) {
Expand Down Expand Up @@ -5767,7 +5768,8 @@ xfs_bmap_insert_extents(
goto del_cursor;
}
}
XFS_WANT_CORRUPTED_RETURN(mp, !isnullstartblock(got.br_startblock));
XFS_WANT_CORRUPTED_GOTO(mp, !isnullstartblock(got.br_startblock),
del_cursor);

if (stop_fsb >= got.br_startoff + got.br_blockcount) {
error = -EIO;
Expand Down
14 changes: 13 additions & 1 deletion fs/xfs/scrub/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -318,8 +318,20 @@ xfs_scrub_dinode(

/* di_mode */
mode = be16_to_cpu(dip->di_mode);
if (mode & ~(S_IALLUGO | S_IFMT))
switch (mode & S_IFMT) {
case S_IFLNK:
case S_IFREG:
case S_IFDIR:
case S_IFCHR:
case S_IFBLK:
case S_IFIFO:
case S_IFSOCK:
/* mode is recognized */
break;
default:
xfs_scrub_ino_set_corrupt(sc, ino, bp);
break;
}

/* v1/v2 fields */
switch (dip->di_version) {
Expand Down
4 changes: 2 additions & 2 deletions fs/xfs/scrub/quota.c
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ xfs_scrub_quota_item(
unsigned long long rcount;
xfs_ino_t fs_icount;

offset = id * qi->qi_dqperchunk;
offset = id / qi->qi_dqperchunk;

/*
* We fed $id and DQNEXT into the xfs_qm_dqget call, which means
Expand Down Expand Up @@ -207,7 +207,7 @@ xfs_scrub_quota(
xfs_dqid_t id = 0;
uint dqtype;
int nimaps;
int error;
int error = 0;

if (!XFS_IS_QUOTA_RUNNING(mp) || !XFS_IS_QUOTA_ON(mp))
return -ENOENT;
Expand Down
12 changes: 6 additions & 6 deletions fs/xfs/xfs_aops.c
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,7 @@ xfs_map_blocks(
(ip->i_df.if_flags & XFS_IFEXTENTS));
ASSERT(offset <= mp->m_super->s_maxbytes);

if (offset + count > mp->m_super->s_maxbytes)
if ((xfs_ufsize_t)offset + count > mp->m_super->s_maxbytes)
count = mp->m_super->s_maxbytes - offset;
end_fsb = XFS_B_TO_FSB(mp, (xfs_ufsize_t)offset + count);
offset_fsb = XFS_B_TO_FSBT(mp, offset);
Expand Down Expand Up @@ -896,13 +896,13 @@ xfs_writepage_map(
struct writeback_control *wbc,
struct inode *inode,
struct page *page,
loff_t offset,
uint64_t end_offset)
uint64_t end_offset)
{
LIST_HEAD(submit_list);
struct xfs_ioend *ioend, *next;
struct buffer_head *bh, *head;
ssize_t len = i_blocksize(inode);
uint64_t offset;
int error = 0;
int count = 0;
int uptodate = 1;
Expand Down Expand Up @@ -1146,7 +1146,7 @@ xfs_do_writepage(
end_offset = offset;
}

return xfs_writepage_map(wpc, wbc, inode, page, offset, end_offset);
return xfs_writepage_map(wpc, wbc, inode, page, end_offset);

redirty:
redirty_page_for_writepage(wbc, page);
Expand Down Expand Up @@ -1265,7 +1265,7 @@ xfs_map_trim_size(
if (mapping_size > size)
mapping_size = size;
if (offset < i_size_read(inode) &&
offset + mapping_size >= i_size_read(inode)) {
(xfs_ufsize_t)offset + mapping_size >= i_size_read(inode)) {
/* limit mapping to block that spans EOF */
mapping_size = roundup_64(i_size_read(inode) - offset,
i_blocksize(inode));
Expand Down Expand Up @@ -1312,7 +1312,7 @@ xfs_get_blocks(
lockmode = xfs_ilock_data_map_shared(ip);

ASSERT(offset <= mp->m_super->s_maxbytes);
if (offset + size > mp->m_super->s_maxbytes)
if ((xfs_ufsize_t)offset + size > mp->m_super->s_maxbytes)
size = mp->m_super->s_maxbytes - offset;
end_fsb = XFS_B_TO_FSB(mp, (xfs_ufsize_t)offset + size);
offset_fsb = XFS_B_TO_FSBT(mp, offset);
Expand Down
23 changes: 7 additions & 16 deletions fs/xfs/xfs_bmap_item.c
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,8 @@ xfs_bud_init(
int
xfs_bui_recover(
struct xfs_mount *mp,
struct xfs_bui_log_item *buip)
struct xfs_bui_log_item *buip,
struct xfs_defer_ops *dfops)
{
int error = 0;
unsigned int bui_type;
Expand All @@ -404,9 +405,7 @@ xfs_bui_recover(
xfs_exntst_t state;
struct xfs_trans *tp;
struct xfs_inode *ip = NULL;
struct xfs_defer_ops dfops;
struct xfs_bmbt_irec irec;
xfs_fsblock_t firstfsb;

ASSERT(!test_bit(XFS_BUI_RECOVERED, &buip->bui_flags));

Expand Down Expand Up @@ -464,7 +463,6 @@ xfs_bui_recover(

if (VFS_I(ip)->i_nlink == 0)
xfs_iflags_set(ip, XFS_IRECOVERY);
xfs_defer_init(&dfops, &firstfsb);

/* Process deferred bmap item. */
state = (bmap->me_flags & XFS_BMAP_EXTENT_UNWRITTEN) ?
Expand All @@ -479,42 +477,35 @@ xfs_bui_recover(
break;
default:
error = -EFSCORRUPTED;
goto err_dfops;
goto err_inode;
}
xfs_trans_ijoin(tp, ip, 0);

count = bmap->me_len;
error = xfs_trans_log_finish_bmap_update(tp, budp, &dfops, type,
error = xfs_trans_log_finish_bmap_update(tp, budp, dfops, type,
ip, whichfork, bmap->me_startoff,
bmap->me_startblock, &count, state);
if (error)
goto err_dfops;
goto err_inode;

if (count > 0) {
ASSERT(type == XFS_BMAP_UNMAP);
irec.br_startblock = bmap->me_startblock;
irec.br_blockcount = count;
irec.br_startoff = bmap->me_startoff;
irec.br_state = state;
error = xfs_bmap_unmap_extent(tp->t_mountp, &dfops, ip, &irec);
error = xfs_bmap_unmap_extent(tp->t_mountp, dfops, ip, &irec);
if (error)
goto err_dfops;
goto err_inode;
}

/* Finish transaction, free inodes. */
error = xfs_defer_finish(&tp, &dfops);
if (error)
goto err_dfops;

set_bit(XFS_BUI_RECOVERED, &buip->bui_flags);
error = xfs_trans_commit(tp);
xfs_iunlock(ip, XFS_ILOCK_EXCL);
IRELE(ip);

return error;

err_dfops:
xfs_defer_cancel(&dfops);
err_inode:
xfs_trans_cancel(tp);
if (ip) {
Expand Down
3 changes: 2 additions & 1 deletion fs/xfs/xfs_bmap_item.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ struct xfs_bud_log_item *xfs_bud_init(struct xfs_mount *,
struct xfs_bui_log_item *);
void xfs_bui_item_free(struct xfs_bui_log_item *);
void xfs_bui_release(struct xfs_bui_log_item *);
int xfs_bui_recover(struct xfs_mount *mp, struct xfs_bui_log_item *buip);
int xfs_bui_recover(struct xfs_mount *mp, struct xfs_bui_log_item *buip,
struct xfs_defer_ops *dfops);

#endif /* __XFS_BMAP_ITEM_H__ */
15 changes: 10 additions & 5 deletions fs/xfs/xfs_buf.c
Original file line number Diff line number Diff line change
Expand Up @@ -1815,22 +1815,27 @@ xfs_alloc_buftarg(
btp->bt_daxdev = dax_dev;

if (xfs_setsize_buftarg_early(btp, bdev))
goto error;
goto error_free;

if (list_lru_init(&btp->bt_lru))
goto error;
goto error_free;

if (percpu_counter_init(&btp->bt_io_count, 0, GFP_KERNEL))
goto error;
goto error_lru;

btp->bt_shrinker.count_objects = xfs_buftarg_shrink_count;
btp->bt_shrinker.scan_objects = xfs_buftarg_shrink_scan;
btp->bt_shrinker.seeks = DEFAULT_SEEKS;
btp->bt_shrinker.flags = SHRINKER_NUMA_AWARE;
register_shrinker(&btp->bt_shrinker);
if (register_shrinker(&btp->bt_shrinker))
goto error_pcpu;
return btp;

error:
error_pcpu:
percpu_counter_destroy(&btp->bt_io_count);
error_lru:
list_lru_destroy(&btp->bt_lru);
error_free:
kmem_free(btp);
return NULL;
}
Expand Down
14 changes: 11 additions & 3 deletions fs/xfs/xfs_dquot.c
Original file line number Diff line number Diff line change
Expand Up @@ -970,14 +970,22 @@ xfs_qm_dqflush_done(
* 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) {
((lip->li_lsn == qip->qli_flush_lsn) ||
(lip->li_flags & XFS_LI_FAILED))) {

/* xfs_trans_ail_delete() drops the AIL lock. */
spin_lock(&ailp->xa_lock);
if (lip->li_lsn == qip->qli_flush_lsn)
if (lip->li_lsn == qip->qli_flush_lsn) {
xfs_trans_ail_delete(ailp, lip, SHUTDOWN_CORRUPT_INCORE);
else
} else {
/*
* Clear the failed state since we are about to drop the
* flush lock
*/
if (lip->li_flags & XFS_LI_FAILED)
xfs_clear_li_failed(lip);
spin_unlock(&ailp->xa_lock);
}
}

/*
Expand Down
40 changes: 38 additions & 2 deletions fs/xfs/xfs_dquot_item.c
Original file line number Diff line number Diff line change
Expand Up @@ -137,20 +137,55 @@ xfs_qm_dqunpin_wait(
wait_event(dqp->q_pinwait, (atomic_read(&dqp->q_pincount) == 0));
}

/*
* Callback used to mark a buffer with XFS_LI_FAILED when items in the buffer
* have been failed during writeback
*
* this informs the AIL that the dquot is already flush locked on the next push,
* and acquires a hold on the buffer to ensure that it isn't reclaimed before
* dirty data makes it to disk.
*/
STATIC void
xfs_dquot_item_error(
struct xfs_log_item *lip,
struct xfs_buf *bp)
{
struct xfs_dquot *dqp;

dqp = DQUOT_ITEM(lip)->qli_dquot;
ASSERT(!completion_done(&dqp->q_flush));
xfs_set_li_failed(lip, bp);
}

STATIC uint
xfs_qm_dquot_logitem_push(
struct xfs_log_item *lip,
struct list_head *buffer_list) __releases(&lip->li_ailp->xa_lock)
__acquires(&lip->li_ailp->xa_lock)
{
struct xfs_dquot *dqp = DQUOT_ITEM(lip)->qli_dquot;
struct xfs_buf *bp = NULL;
struct xfs_buf *bp = lip->li_buf;
uint rval = XFS_ITEM_SUCCESS;
int error;

if (atomic_read(&dqp->q_pincount) > 0)
return XFS_ITEM_PINNED;

/*
* The buffer containing this item failed to be written back
* previously. Resubmit the buffer for IO
*/
if (lip->li_flags & XFS_LI_FAILED) {
if (!xfs_buf_trylock(bp))
return XFS_ITEM_LOCKED;

if (!xfs_buf_resubmit_failed_buffers(bp, lip, buffer_list))
rval = XFS_ITEM_FLUSHING;

xfs_buf_unlock(bp);
return rval;
}

if (!xfs_dqlock_nowait(dqp))
return XFS_ITEM_LOCKED;

Expand Down Expand Up @@ -242,7 +277,8 @@ static const struct xfs_item_ops xfs_dquot_item_ops = {
.iop_unlock = xfs_qm_dquot_logitem_unlock,
.iop_committed = xfs_qm_dquot_logitem_committed,
.iop_push = xfs_qm_dquot_logitem_push,
.iop_committing = xfs_qm_dquot_logitem_committing
.iop_committing = xfs_qm_dquot_logitem_committing,
.iop_error = xfs_dquot_item_error
};

/*
Expand Down
21 changes: 21 additions & 0 deletions fs/xfs/xfs_inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -2400,6 +2400,24 @@ xfs_ifree_cluster(
return 0;
}

/*
* Free any local-format buffers sitting around before we reset to
* extents format.
*/
static inline void
xfs_ifree_local_data(
struct xfs_inode *ip,
int whichfork)
{
struct xfs_ifork *ifp;

if (XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_LOCAL)
return;

ifp = XFS_IFORK_PTR(ip, whichfork);
xfs_idata_realloc(ip, -ifp->if_bytes, whichfork);
}

/*
* This is called to return an inode to the inode free list.
* The inode should already be truncated to 0 length and have
Expand Down Expand Up @@ -2437,6 +2455,9 @@ xfs_ifree(
if (error)
return error;

xfs_ifree_local_data(ip, XFS_DATA_FORK);
xfs_ifree_local_data(ip, XFS_ATTR_FORK);

VFS_I(ip)->i_mode = 0; /* mark incore inode as free */
ip->i_d.di_flags = 0;
ip->i_d.di_dmevmask = 0;
Expand Down
Loading

0 comments on commit 788c1da

Please sign in to comment.