]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
Merge tag 'xfs-4.12-merge-7' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux
authorLinus Torvalds <torvalds@linux-foundation.org>
Sat, 6 May 2017 18:46:16 +0000 (11:46 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sat, 6 May 2017 18:46:16 +0000 (11:46 -0700)
Pull xfs updates from Darrick Wong:
 "Here are the XFS changes for 4.12. The big new feature for this
  release is the new space mapping ioctl that we've been discussing
  since LSF2016, but other than that most of the patches are larger bug
  fixes, memory corruption prevention, and other cleanups.

  Summary:
   - various code cleanups
   - introduce GETFSMAP ioctl
   - various refactoring
   - avoid dio reads past eof
   - fix memory corruption and other errors with fragmented directory blocks
   - fix accidental userspace memory corruptions
   - publish fs uuid in superblock
   - make fstrim terminatable
   - fix race between quotaoff and in-core inode creation
   - avoid use-after-free when finishing up w/ buffer heads
   - reserve enough space to handle bmap tree resizing during cow remap"

* tag 'xfs-4.12-merge-7' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux: (53 commits)
  xfs: fix use-after-free in xfs_finish_page_writeback
  xfs: reserve enough blocks to handle btree splits when remapping
  xfs: wait on new inodes during quotaoff dquot release
  xfs: update ag iterator to support wait on new inodes
  xfs: support ability to wait on new inodes
  xfs: publish UUID in struct super_block
  xfs: Allow user to kill fstrim process
  xfs: better log intent item refcount checking
  xfs: fix up quotacheck buffer list error handling
  xfs: remove xfs_trans_ail_delete_bulk
  xfs: don't use bool values in trace buffers
  xfs: fix getfsmap userspace memory corruption while setting OF_LAST
  xfs: fix __user annotations for xfs_ioc_getfsmap
  xfs: corruption needs to respect endianess too!
  xfs: use NULL instead of 0 to initialize a pointer in xfs_ioc_getfsmap
  xfs: use NULL instead of 0 to initialize a pointer in xfs_getfsmap
  xfs: simplify validation of the unwritten extent bit
  xfs: remove unused values from xfs_exntst_t
  xfs: remove the unused XFS_MAXLINK_1 define
  xfs: more do_div cleanups
  ...

1  2 
fs/iomap.c
fs/xfs/libxfs/xfs_btree.c
fs/xfs/xfs_aops.c
fs/xfs/xfs_bmap_util.c
fs/xfs/xfs_buf.c
fs/xfs/xfs_iomap.c
fs/xfs/xfs_trans.c

diff --combined fs/iomap.c
index 4add7d4ad006ab8a78f71369c06253c678293ff9,0ca8b0fbbdadfeb216375cf4f3197a889f10d998..1faabe09b8fdb20f8dc355cc06ce452013ce64ec
@@@ -360,8 -360,7 +360,8 @@@ static int iomap_dax_zero(loff_t pos, u
        sector_t sector = iomap->blkno +
                (((pos & ~(PAGE_SIZE - 1)) - iomap->offset) >> 9);
  
 -      return __dax_zero_page_range(iomap->bdev, sector, offset, bytes);
 +      return __dax_zero_page_range(iomap->bdev, iomap->dax_dev, sector,
 +                      offset, bytes);
  }
  
  static loff_t
@@@ -888,14 -887,16 +888,14 @@@ iomap_dio_rw(struct kiocb *iocb, struc
                flags |= IOMAP_WRITE;
        }
  
 -      if (mapping->nrpages) {
 -              ret = filemap_write_and_wait_range(mapping, start, end);
 -              if (ret)
 -                      goto out_free_dio;
 +      ret = filemap_write_and_wait_range(mapping, start, end);
 +      if (ret)
 +              goto out_free_dio;
  
 -              ret = invalidate_inode_pages2_range(mapping,
 -                              start >> PAGE_SHIFT, end >> PAGE_SHIFT);
 -              WARN_ON_ONCE(ret);
 -              ret = 0;
 -      }
 +      ret = invalidate_inode_pages2_range(mapping,
 +                      start >> PAGE_SHIFT, end >> PAGE_SHIFT);
 +      WARN_ON_ONCE(ret);
 +      ret = 0;
  
        inode_dio_begin(inode);
  
                        break;
                }
                pos += ret;
+               if (iov_iter_rw(iter) == READ && pos >= dio->i_size)
+                       break;
        } while ((count = iov_iter_count(iter)) > 0);
        blk_finish_plug(&plug);
  
         * one is a pretty crazy thing to do, so we don't support it 100%.  If
         * this invalidation fails, tough, the write still worked...
         */
 -      if (iov_iter_rw(iter) == WRITE && mapping->nrpages) {
 +      if (iov_iter_rw(iter) == WRITE) {
                int err = invalidate_inode_pages2_range(mapping,
                                start >> PAGE_SHIFT, end >> PAGE_SHIFT);
                WARN_ON_ONCE(err);
index 3059a3ec7ecbf9b6260ef977366540bbd21c9e70,92aa20d565532b321e70b36fb9031af8f4614624..5392674bf8930550d949f6c01d0f47ec03228ba8
@@@ -2886,7 -2886,7 +2886,7 @@@ xfs_btree_split_worker
        struct xfs_btree_split_args     *args = container_of(work,
                                                struct xfs_btree_split_args, work);
        unsigned long           pflags;
 -      unsigned long           new_pflags = PF_FSTRANS;
 +      unsigned long           new_pflags = PF_MEMALLOC_NOFS;
  
        /*
         * we are in a transaction context here, but may also be doing work
@@@ -4842,6 -4842,21 +4842,21 @@@ xfs_btree_query_range
                        fn, priv);
  }
  
+ /* Query a btree for all records. */
+ int
+ xfs_btree_query_all(
+       struct xfs_btree_cur            *cur,
+       xfs_btree_query_range_fn        fn,
+       void                            *priv)
+ {
+       union xfs_btree_irec            low_rec;
+       union xfs_btree_irec            high_rec;
+       memset(&low_rec, 0, sizeof(low_rec));
+       memset(&high_rec, 0xFF, sizeof(high_rec));
+       return xfs_btree_query_range(cur, &low_rec, &high_rec, fn, priv);
+ }
  /*
   * Calculate the number of blocks needed to store a given number of records
   * in a short-format (per-AG metadata) btree.
diff --combined fs/xfs/xfs_aops.c
index 05eca126c688cfaa606ada5d161379f73b734fcf,0debaeb70f2a8e899b502724327eefea54a587ed..09af0f7cd55e278312881999755d3d8d0793d5c8
@@@ -111,11 -111,11 +111,11 @@@ xfs_finish_page_writeback
  
        bsize = bh->b_size;
        do {
+               if (off > end)
+                       break;
                next = bh->b_this_page;
                if (off < bvec->bv_offset)
                        goto next_bh;
-               if (off > end)
-                       break;
                bh->b_end_io(bh, !error);
  next_bh:
                off += bsize;
@@@ -189,7 -189,7 +189,7 @@@ xfs_setfilesize_trans_alloc
         * We hand off the transaction to the completion thread now, so
         * clear the flag here.
         */
 -      current_restore_flags_nested(&tp->t_pflags, PF_FSTRANS);
 +      current_restore_flags_nested(&tp->t_pflags, PF_MEMALLOC_NOFS);
        return 0;
  }
  
@@@ -252,7 -252,7 +252,7 @@@ xfs_setfilesize_ioend
         * thus we need to mark ourselves as being in a transaction manually.
         * Similarly for freeze protection.
         */
 -      current_set_flags_nested(&tp->t_pflags, PF_FSTRANS);
 +      current_set_flags_nested(&tp->t_pflags, PF_MEMALLOC_NOFS);
        __sb_writers_acquired(VFS_I(ip)->i_sb, SB_FREEZE_FS);
  
        /* we abort the update if there was an IO error */
@@@ -1016,7 -1016,7 +1016,7 @@@ xfs_do_writepage
         * Given that we do not allow direct reclaim to call us, we should
         * never be called while in a filesystem transaction.
         */
 -      if (WARN_ON_ONCE(current->flags & PF_FSTRANS))
 +      if (WARN_ON_ONCE(current->flags & PF_MEMALLOC_NOFS))
                goto redirty;
  
        /*
@@@ -1261,8 -1261,8 +1261,8 @@@ xfs_get_blocks
  
        if (nimaps) {
                trace_xfs_get_blocks_found(ip, offset, size,
-                               ISUNWRITTEN(&imap) ? XFS_IO_UNWRITTEN
-                                                  : XFS_IO_OVERWRITE, &imap);
+                       imap.br_state == XFS_EXT_UNWRITTEN ?
+                               XFS_IO_UNWRITTEN : XFS_IO_OVERWRITE, &imap);
                xfs_iunlock(ip, lockmode);
        } else {
                trace_xfs_get_blocks_notfound(ip, offset, size);
         * For unwritten extents do not report a disk address in the buffered
         * read case (treat as if we're reading into a hole).
         */
-       if (imap.br_startblock != HOLESTARTBLOCK &&
-           imap.br_startblock != DELAYSTARTBLOCK &&
-           !ISUNWRITTEN(&imap))
+       if (xfs_bmap_is_real_extent(&imap))
                xfs_map_buffer(inode, bh_result, &imap, offset);
  
        /*
diff --combined fs/xfs/xfs_bmap_util.c
index 8795e9cd867cdafba1e84c3313e33a41532b6132,7ac80a1facf2cc78549dae9947bec01db90aafb9..2b954308a1d671e9f09d06a5353713297f11be76
@@@ -81,7 -81,7 +81,7 @@@ xfs_zero_extent
        return blkdev_issue_zeroout(xfs_find_bdev_for_inode(VFS_I(ip)),
                block << (mp->m_super->s_blocksize_bits - 9),
                count_fsb << (mp->m_super->s_blocksize_bits - 9),
 -              GFP_NOFS, true);
 +              GFP_NOFS, 0);
  }
  
  int
@@@ -448,10 -448,9 +448,9 @@@ xfs_getbmap_adjust_shared
        next_map->br_blockcount = 0;
  
        /* Only written data blocks can be shared. */
-       if (!xfs_is_reflink_inode(ip) || whichfork != XFS_DATA_FORK ||
-           map->br_startblock == DELAYSTARTBLOCK ||
-           map->br_startblock == HOLESTARTBLOCK ||
-           ISUNWRITTEN(map))
+       if (!xfs_is_reflink_inode(ip) ||
+           whichfork != XFS_DATA_FORK ||
+           !xfs_bmap_is_real_extent(map))
                return 0;
  
        agno = XFS_FSB_TO_AGNO(mp, map->br_startblock);
@@@ -904,9 -903,9 +903,9 @@@ xfs_can_free_eofblocks(struct xfs_inod
  }
  
  /*
-  * This is called by xfs_inactive to free any blocks beyond eof
-  * when the link count isn't zero and by xfs_dm_punch_hole() when
-  * punching a hole to EOF.
+  * This is called to free any blocks beyond eof. The caller must hold
+  * IOLOCK_EXCL unless we are in the inode reclaim path and have the only
+  * reference to the inode.
   */
  int
  xfs_free_eofblocks(
        struct xfs_bmbt_irec    imap;
        struct xfs_mount        *mp = ip->i_mount;
  
-       ASSERT(xfs_isilocked(ip, XFS_IOLOCK_EXCL));
        /*
         * Figure out if there are any blocks beyond the end
         * of the file.  If not, then there is nothing to do.
@@@ -1209,11 -1206,8 +1206,8 @@@ xfs_adjust_extent_unmap_boundaries
                return error;
  
        if (nimap && imap.br_startblock != HOLESTARTBLOCK) {
-               xfs_daddr_t     block;
                ASSERT(imap.br_startblock != DELAYSTARTBLOCK);
-               block = imap.br_startblock;
-               mod = do_div(block, mp->m_sb.sb_rextsize);
+               mod = do_mod(imap.br_startblock, mp->m_sb.sb_rextsize);
                if (mod)
                        *startoffset_fsb += mp->m_sb.sb_rextsize - mod;
        }
diff --combined fs/xfs/xfs_buf.c
index ca09061369cb539510ecd6cb7dfdd05a986671e7,ba036c11be94d4a9346be655399ab2ea8fc75dfa..62fa39276a24bd91c26e3aa18c9f162f19842b95
@@@ -443,17 -443,17 +443,17 @@@ _xfs_buf_map_pages
                bp->b_addr = NULL;
        } else {
                int retried = 0;
 -              unsigned noio_flag;
 +              unsigned nofs_flag;
  
                /*
                 * vm_map_ram() will allocate auxillary structures (e.g.
                 * pagetables) with GFP_KERNEL, yet we are likely to be under
                 * GFP_NOFS context here. Hence we need to tell memory reclaim
 -               * that we are in such a context via PF_MEMALLOC_NOIO to prevent
 +               * that we are in such a context via PF_MEMALLOC_NOFS to prevent
                 * memory reclaim re-entering the filesystem here and
                 * potentially deadlocking.
                 */
 -              noio_flag = memalloc_noio_save();
 +              nofs_flag = memalloc_nofs_save();
                do {
                        bp->b_addr = vm_map_ram(bp->b_pages, bp->b_page_count,
                                                -1, PAGE_KERNEL);
                                break;
                        vm_unmap_aliases();
                } while (retried++ <= 1);
 -              memalloc_noio_restore(noio_flag);
 +              memalloc_nofs_restore(nofs_flag);
  
                if (!bp->b_addr)
                        return -ENOMEM;
@@@ -1079,6 -1079,8 +1079,8 @@@ voi
  xfs_buf_unlock(
        struct xfs_buf          *bp)
  {
+       ASSERT(xfs_buf_islocked(bp));
        XB_CLEAR_OWNER(bp);
        up(&bp->b_sema);
  
@@@ -1814,6 -1816,28 +1816,28 @@@ error
        return NULL;
  }
  
+ /*
+  * Cancel a delayed write list.
+  *
+  * Remove each buffer from the list, clear the delwri queue flag and drop the
+  * associated buffer reference.
+  */
+ void
+ xfs_buf_delwri_cancel(
+       struct list_head        *list)
+ {
+       struct xfs_buf          *bp;
+       while (!list_empty(list)) {
+               bp = list_first_entry(list, struct xfs_buf, b_list);
+               xfs_buf_lock(bp);
+               bp->b_flags &= ~_XBF_DELWRI_Q;
+               list_del_init(&bp->b_list);
+               xfs_buf_relse(bp);
+       }
+ }
  /*
   * Add a buffer to the delayed write list.
   *
diff --combined fs/xfs/xfs_iomap.c
index 4b47403f80892f393d67ed673b8d07d63cf33fdf,b2f0901bb517e52792b83941e444fe9826b6246a..a63f61c256bdc54f0a8ee6088702dbd5e91ee9aa
@@@ -240,7 -240,7 +240,7 @@@ xfs_iomap_write_direct
         */
        if (IS_DAX(VFS_I(ip))) {
                bmapi_flags = XFS_BMAPI_CONVERT | XFS_BMAPI_ZERO;
-               if (ISUNWRITTEN(imap)) {
+               if (imap->br_state == XFS_EXT_UNWRITTEN) {
                        tflags |= XFS_TRANS_RESERVE;
                        resblks = XFS_DIOSTRAT_SPACE_RES(mp, 0) << 1;
                }
@@@ -945,7 -945,7 +945,7 @@@ static inline bool imap_needs_alloc(str
        return !nimaps ||
                imap->br_startblock == HOLESTARTBLOCK ||
                imap->br_startblock == DELAYSTARTBLOCK ||
-               (IS_DAX(inode) && ISUNWRITTEN(imap));
+               (IS_DAX(inode) && imap->br_state == XFS_EXT_UNWRITTEN);
  }
  
  static inline bool need_excl_ilock(struct xfs_inode *ip, unsigned flags)
@@@ -976,7 -976,6 +976,7 @@@ xfs_file_iomap_begin
        int                     nimaps = 1, error = 0;
        bool                    shared = false, trimmed = false;
        unsigned                lockmode;
 +      struct block_device     *bdev;
  
        if (XFS_FORCED_SHUTDOWN(mp))
                return -EIO;
        }
  
        xfs_bmbt_to_iomap(ip, iomap, &imap);
 +
 +      /* optionally associate a dax device with the iomap bdev */
 +      bdev = iomap->bdev;
 +      if (blk_queue_dax(bdev->bd_queue))
 +              iomap->dax_dev = dax_get_by_host(bdev->bd_disk->disk_name);
 +      else
 +              iomap->dax_dev = NULL;
 +
        if (shared)
                iomap->flags |= IOMAP_F_SHARED;
        return 0;
@@@ -1149,7 -1140,6 +1149,7 @@@ xfs_file_iomap_end
        unsigned                flags,
        struct iomap            *iomap)
  {
 +      put_dax(iomap->dax_dev);
        if ((flags & IOMAP_WRITE) && iomap->type == IOMAP_DELALLOC)
                return xfs_file_iomap_end_delalloc(XFS_I(inode), offset,
                                length, written, iomap);
@@@ -1180,10 -1170,10 +1180,10 @@@ xfs_xattr_iomap_begin
        if (XFS_FORCED_SHUTDOWN(mp))
                return -EIO;
  
-       lockmode = xfs_ilock_data_map_shared(ip);
+       lockmode = xfs_ilock_attr_map_shared(ip);
  
        /* if there are no attribute fork or extents, return ENOENT */
-       if (XFS_IFORK_Q(ip) || !ip->i_d.di_anextents) {
+       if (!XFS_IFORK_Q(ip) || !ip->i_d.di_anextents) {
                error = -ENOENT;
                goto out_unlock;
        }
diff --combined fs/xfs/xfs_trans.c
index f5969c8274fc6a76d491779a249d4839ba830dd3,be86e4ed23d5ddd442fc7e576fe86715c5f91852..2011620008de8a4d422643bc20dbc2313873c16e
@@@ -134,7 -134,7 +134,7 @@@ xfs_trans_reserve
        bool            rsvd = (tp->t_flags & XFS_TRANS_RESERVE) != 0;
  
        /* Mark this thread as being in a transaction */
 -      current_set_flags_nested(&tp->t_pflags, PF_FSTRANS);
 +      current_set_flags_nested(&tp->t_pflags, PF_MEMALLOC_NOFS);
  
        /*
         * Attempt to reserve the needed disk blocks by decrementing
        if (blocks > 0) {
                error = xfs_mod_fdblocks(tp->t_mountp, -((int64_t)blocks), rsvd);
                if (error != 0) {
 -                      current_restore_flags_nested(&tp->t_pflags, PF_FSTRANS);
 +                      current_restore_flags_nested(&tp->t_pflags, PF_MEMALLOC_NOFS);
                        return -ENOSPC;
                }
                tp->t_blk_res += blocks;
@@@ -221,7 -221,7 +221,7 @@@ undo_blocks
                tp->t_blk_res = 0;
        }
  
 -      current_restore_flags_nested(&tp->t_pflags, PF_FSTRANS);
 +      current_restore_flags_nested(&tp->t_pflags, PF_MEMALLOC_NOFS);
  
        return error;
  }
@@@ -262,6 -262,28 +262,28 @@@ xfs_trans_alloc
        return 0;
  }
  
+ /*
+  * Create an empty transaction with no reservation.  This is a defensive
+  * mechanism for routines that query metadata without actually modifying
+  * them -- if the metadata being queried is somehow cross-linked (think a
+  * btree block pointer that points higher in the tree), we risk deadlock.
+  * However, blocks grabbed as part of a transaction can be re-grabbed.
+  * The verifiers will notice the corrupt block and the operation will fail
+  * back to userspace without deadlocking.
+  *
+  * Note the zero-length reservation; this transaction MUST be cancelled
+  * without any dirty data.
+  */
+ int
+ xfs_trans_alloc_empty(
+       struct xfs_mount                *mp,
+       struct xfs_trans                **tpp)
+ {
+       struct xfs_trans_res            resv = {0};
+       return xfs_trans_alloc(mp, &resv, 0, 0, XFS_TRANS_NO_WRITECOUNT, tpp);
+ }
  /*
   * Record the indicated change to the given field for application
   * to the file system's superblock when the transaction commits.
@@@ -914,7 -936,7 +936,7 @@@ __xfs_trans_commit
  
        xfs_log_commit_cil(mp, tp, &commit_lsn, regrant);
  
 -      current_restore_flags_nested(&tp->t_pflags, PF_FSTRANS);
 +      current_restore_flags_nested(&tp->t_pflags, PF_MEMALLOC_NOFS);
        xfs_trans_free(tp);
  
        /*
@@@ -944,7 -966,7 +966,7 @@@ out_unreserve
                if (commit_lsn == -1 && !error)
                        error = -EIO;
        }
 -      current_restore_flags_nested(&tp->t_pflags, PF_FSTRANS);
 +      current_restore_flags_nested(&tp->t_pflags, PF_MEMALLOC_NOFS);
        xfs_trans_free_items(tp, NULLCOMMITLSN, !!error);
        xfs_trans_free(tp);
  
@@@ -998,7 -1020,7 +1020,7 @@@ xfs_trans_cancel
                xfs_log_done(mp, tp->t_ticket, NULL, false);
  
        /* mark this thread as no longer being in a transaction */
 -      current_restore_flags_nested(&tp->t_pflags, PF_FSTRANS);
 +      current_restore_flags_nested(&tp->t_pflags, PF_MEMALLOC_NOFS);
  
        xfs_trans_free_items(tp, NULLCOMMITLSN, dirty);
        xfs_trans_free(tp);
   * chunk we've been working on and get a new transaction to continue.
   */
  int
__xfs_trans_roll(
+ xfs_trans_roll(
        struct xfs_trans        **tpp,
-       struct xfs_inode        *dp,
-       int                     *committed)
+       struct xfs_inode        *dp)
  {
        struct xfs_trans        *trans;
        struct xfs_trans_res    tres;
        int                     error;
  
-       *committed = 0;
        /*
         * Ensure that the inode is always logged.
         */
        if (error)
                return error;
  
-       *committed = 1;
        trans = *tpp;
  
        /*
                xfs_trans_ijoin(trans, dp, 0);
        return 0;
  }
- int
- xfs_trans_roll(
-       struct xfs_trans        **tpp,
-       struct xfs_inode        *dp)
- {
-       int                     committed;
-       return __xfs_trans_roll(tpp, dp, &committed);
- }