|
@@ -1087,99 +1087,120 @@ error1: /* Just cancel transaction */
|
|
|
return error;
|
|
|
}
|
|
|
|
|
|
-/*
|
|
|
- * Zero file bytes between startoff and endoff inclusive.
|
|
|
- * The iolock is held exclusive and no blocks are buffered.
|
|
|
- *
|
|
|
- * This function is used by xfs_free_file_space() to zero
|
|
|
- * partial blocks when the range to free is not block aligned.
|
|
|
- * When unreserving space with boundaries that are not block
|
|
|
- * aligned we round up the start and round down the end
|
|
|
- * boundaries and then use this function to zero the parts of
|
|
|
- * the blocks that got dropped during the rounding.
|
|
|
- */
|
|
|
-STATIC int
|
|
|
-xfs_zero_remaining_bytes(
|
|
|
- xfs_inode_t *ip,
|
|
|
- xfs_off_t startoff,
|
|
|
- xfs_off_t endoff)
|
|
|
+static int
|
|
|
+xfs_unmap_extent(
|
|
|
+ struct xfs_inode *ip,
|
|
|
+ xfs_fileoff_t startoffset_fsb,
|
|
|
+ xfs_filblks_t len_fsb,
|
|
|
+ int *done)
|
|
|
{
|
|
|
- xfs_bmbt_irec_t imap;
|
|
|
- xfs_fileoff_t offset_fsb;
|
|
|
- xfs_off_t lastoffset;
|
|
|
- xfs_off_t offset;
|
|
|
- xfs_buf_t *bp;
|
|
|
- xfs_mount_t *mp = ip->i_mount;
|
|
|
- int nimap;
|
|
|
- int error = 0;
|
|
|
+ struct xfs_mount *mp = ip->i_mount;
|
|
|
+ struct xfs_trans *tp;
|
|
|
+ struct xfs_bmap_free free_list;
|
|
|
+ xfs_fsblock_t firstfsb;
|
|
|
+ uint resblks = XFS_DIOSTRAT_SPACE_RES(mp, 0);
|
|
|
+ int error;
|
|
|
|
|
|
- /*
|
|
|
- * Avoid doing I/O beyond eof - it's not necessary
|
|
|
- * since nothing can read beyond eof. The space will
|
|
|
- * be zeroed when the file is extended anyway.
|
|
|
- */
|
|
|
- if (startoff >= XFS_ISIZE(ip))
|
|
|
- return 0;
|
|
|
+ error = xfs_trans_alloc(mp, &M_RES(mp)->tr_write, resblks, 0, 0, &tp);
|
|
|
+ if (error) {
|
|
|
+ ASSERT(error == -ENOSPC || XFS_FORCED_SHUTDOWN(mp));
|
|
|
+ return error;
|
|
|
+ }
|
|
|
|
|
|
- if (endoff > XFS_ISIZE(ip))
|
|
|
- endoff = XFS_ISIZE(ip);
|
|
|
+ xfs_ilock(ip, XFS_ILOCK_EXCL);
|
|
|
+ error = xfs_trans_reserve_quota(tp, mp, ip->i_udquot, ip->i_gdquot,
|
|
|
+ ip->i_pdquot, resblks, 0, XFS_QMOPT_RES_REGBLKS);
|
|
|
+ if (error)
|
|
|
+ goto out_trans_cancel;
|
|
|
|
|
|
- for (offset = startoff; offset <= endoff; offset = lastoffset + 1) {
|
|
|
- uint lock_mode;
|
|
|
+ xfs_trans_ijoin(tp, ip, 0);
|
|
|
|
|
|
- offset_fsb = XFS_B_TO_FSBT(mp, offset);
|
|
|
- nimap = 1;
|
|
|
+ xfs_bmap_init(&free_list, &firstfsb);
|
|
|
+ error = xfs_bunmapi(tp, ip, startoffset_fsb, len_fsb, 0, 2, &firstfsb,
|
|
|
+ &free_list, done);
|
|
|
+ if (error)
|
|
|
+ goto out_bmap_cancel;
|
|
|
|
|
|
- lock_mode = xfs_ilock_data_map_shared(ip);
|
|
|
- error = xfs_bmapi_read(ip, offset_fsb, 1, &imap, &nimap, 0);
|
|
|
- xfs_iunlock(ip, lock_mode);
|
|
|
+ error = xfs_bmap_finish(&tp, &free_list, NULL);
|
|
|
+ if (error)
|
|
|
+ goto out_bmap_cancel;
|
|
|
|
|
|
- if (error || nimap < 1)
|
|
|
- break;
|
|
|
- ASSERT(imap.br_blockcount >= 1);
|
|
|
- ASSERT(imap.br_startoff == offset_fsb);
|
|
|
- ASSERT(imap.br_startblock != DELAYSTARTBLOCK);
|
|
|
+ error = xfs_trans_commit(tp);
|
|
|
+out_unlock:
|
|
|
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
|
|
|
+ return error;
|
|
|
|
|
|
- if (imap.br_startblock == HOLESTARTBLOCK ||
|
|
|
- imap.br_state == XFS_EXT_UNWRITTEN) {
|
|
|
- /* skip the entire extent */
|
|
|
- lastoffset = XFS_FSB_TO_B(mp, imap.br_startoff +
|
|
|
- imap.br_blockcount) - 1;
|
|
|
- continue;
|
|
|
- }
|
|
|
+out_bmap_cancel:
|
|
|
+ xfs_bmap_cancel(&free_list);
|
|
|
+out_trans_cancel:
|
|
|
+ xfs_trans_cancel(tp);
|
|
|
+ goto out_unlock;
|
|
|
+}
|
|
|
|
|
|
- lastoffset = XFS_FSB_TO_B(mp, imap.br_startoff + 1) - 1;
|
|
|
- if (lastoffset > endoff)
|
|
|
- lastoffset = endoff;
|
|
|
+static int
|
|
|
+xfs_adjust_extent_unmap_boundaries(
|
|
|
+ struct xfs_inode *ip,
|
|
|
+ xfs_fileoff_t *startoffset_fsb,
|
|
|
+ xfs_fileoff_t *endoffset_fsb)
|
|
|
+{
|
|
|
+ struct xfs_mount *mp = ip->i_mount;
|
|
|
+ struct xfs_bmbt_irec imap;
|
|
|
+ int nimap, error;
|
|
|
+ xfs_extlen_t mod = 0;
|
|
|
|
|
|
- /* DAX can just zero the backing device directly */
|
|
|
- if (IS_DAX(VFS_I(ip))) {
|
|
|
- error = dax_zero_page_range(VFS_I(ip), offset,
|
|
|
- lastoffset - offset + 1,
|
|
|
- xfs_get_blocks_direct);
|
|
|
- if (error)
|
|
|
- return error;
|
|
|
- continue;
|
|
|
- }
|
|
|
+ nimap = 1;
|
|
|
+ error = xfs_bmapi_read(ip, *startoffset_fsb, 1, &imap, &nimap, 0);
|
|
|
+ if (error)
|
|
|
+ return error;
|
|
|
|
|
|
- error = xfs_buf_read_uncached(XFS_IS_REALTIME_INODE(ip) ?
|
|
|
- mp->m_rtdev_targp : mp->m_ddev_targp,
|
|
|
- xfs_fsb_to_db(ip, imap.br_startblock),
|
|
|
- BTOBB(mp->m_sb.sb_blocksize),
|
|
|
- 0, &bp, NULL);
|
|
|
- if (error)
|
|
|
- return error;
|
|
|
+ if (nimap && imap.br_startblock != HOLESTARTBLOCK) {
|
|
|
+ xfs_daddr_t block;
|
|
|
|
|
|
- memset(bp->b_addr +
|
|
|
- (offset - XFS_FSB_TO_B(mp, imap.br_startoff)),
|
|
|
- 0, lastoffset - offset + 1);
|
|
|
+ ASSERT(imap.br_startblock != DELAYSTARTBLOCK);
|
|
|
+ block = imap.br_startblock;
|
|
|
+ mod = do_div(block, mp->m_sb.sb_rextsize);
|
|
|
+ if (mod)
|
|
|
+ *startoffset_fsb += mp->m_sb.sb_rextsize - mod;
|
|
|
+ }
|
|
|
|
|
|
- error = xfs_bwrite(bp);
|
|
|
- xfs_buf_relse(bp);
|
|
|
- if (error)
|
|
|
- return error;
|
|
|
+ nimap = 1;
|
|
|
+ error = xfs_bmapi_read(ip, *endoffset_fsb - 1, 1, &imap, &nimap, 0);
|
|
|
+ if (error)
|
|
|
+ return error;
|
|
|
+
|
|
|
+ if (nimap && imap.br_startblock != HOLESTARTBLOCK) {
|
|
|
+ ASSERT(imap.br_startblock != DELAYSTARTBLOCK);
|
|
|
+ mod++;
|
|
|
+ if (mod && mod != mp->m_sb.sb_rextsize)
|
|
|
+ *endoffset_fsb -= mod;
|
|
|
}
|
|
|
- return error;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int
|
|
|
+xfs_flush_unmap_range(
|
|
|
+ struct xfs_inode *ip,
|
|
|
+ xfs_off_t offset,
|
|
|
+ xfs_off_t len)
|
|
|
+{
|
|
|
+ struct xfs_mount *mp = ip->i_mount;
|
|
|
+ struct inode *inode = VFS_I(ip);
|
|
|
+ xfs_off_t rounding, start, end;
|
|
|
+ int error;
|
|
|
+
|
|
|
+ /* wait for the completion of any pending DIOs */
|
|
|
+ inode_dio_wait(inode);
|
|
|
+
|
|
|
+ rounding = max_t(xfs_off_t, 1 << mp->m_sb.sb_blocklog, PAGE_SIZE);
|
|
|
+ start = round_down(offset, rounding);
|
|
|
+ end = round_up(offset + len, rounding) - 1;
|
|
|
+
|
|
|
+ error = filemap_write_and_wait_range(inode->i_mapping, start, end);
|
|
|
+ if (error)
|
|
|
+ return error;
|
|
|
+ truncate_pagecache_range(inode, start, end);
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
int
|
|
@@ -1188,24 +1209,10 @@ xfs_free_file_space(
|
|
|
xfs_off_t offset,
|
|
|
xfs_off_t len)
|
|
|
{
|
|
|
- int done;
|
|
|
- xfs_fileoff_t endoffset_fsb;
|
|
|
- int error;
|
|
|
- xfs_fsblock_t firstfsb;
|
|
|
- xfs_bmap_free_t free_list;
|
|
|
- xfs_bmbt_irec_t imap;
|
|
|
- xfs_off_t ioffset;
|
|
|
- xfs_off_t iendoffset;
|
|
|
- xfs_extlen_t mod=0;
|
|
|
- xfs_mount_t *mp;
|
|
|
- int nimap;
|
|
|
- uint resblks;
|
|
|
- xfs_off_t rounding;
|
|
|
- int rt;
|
|
|
+ struct xfs_mount *mp = ip->i_mount;
|
|
|
xfs_fileoff_t startoffset_fsb;
|
|
|
- xfs_trans_t *tp;
|
|
|
-
|
|
|
- mp = ip->i_mount;
|
|
|
+ xfs_fileoff_t endoffset_fsb;
|
|
|
+ int done = 0, error;
|
|
|
|
|
|
trace_xfs_free_file_space(ip);
|
|
|
|
|
@@ -1213,135 +1220,45 @@ xfs_free_file_space(
|
|
|
if (error)
|
|
|
return error;
|
|
|
|
|
|
- error = 0;
|
|
|
if (len <= 0) /* if nothing being freed */
|
|
|
- return error;
|
|
|
- rt = XFS_IS_REALTIME_INODE(ip);
|
|
|
- startoffset_fsb = XFS_B_TO_FSB(mp, offset);
|
|
|
- endoffset_fsb = XFS_B_TO_FSBT(mp, offset + len);
|
|
|
-
|
|
|
- /* wait for the completion of any pending DIOs */
|
|
|
- inode_dio_wait(VFS_I(ip));
|
|
|
+ return 0;
|
|
|
|
|
|
- rounding = max_t(xfs_off_t, 1 << mp->m_sb.sb_blocklog, PAGE_SIZE);
|
|
|
- ioffset = round_down(offset, rounding);
|
|
|
- iendoffset = round_up(offset + len, rounding) - 1;
|
|
|
- error = filemap_write_and_wait_range(VFS_I(ip)->i_mapping, ioffset,
|
|
|
- iendoffset);
|
|
|
+ error = xfs_flush_unmap_range(ip, offset, len);
|
|
|
if (error)
|
|
|
- goto out;
|
|
|
- truncate_pagecache_range(VFS_I(ip), ioffset, iendoffset);
|
|
|
+ return error;
|
|
|
+
|
|
|
+ startoffset_fsb = XFS_B_TO_FSB(mp, offset);
|
|
|
+ endoffset_fsb = XFS_B_TO_FSBT(mp, offset + len);
|
|
|
|
|
|
/*
|
|
|
- * Need to zero the stuff we're not freeing, on disk.
|
|
|
- * If it's a realtime file & can't use unwritten extents then we
|
|
|
- * actually need to zero the extent edges. Otherwise xfs_bunmapi
|
|
|
- * will take care of it for us.
|
|
|
+ * Need to zero the stuff we're not freeing, on disk. If it's a RT file
|
|
|
+ * and we can't use unwritten extents then we actually need to ensure
|
|
|
+ * to zero the whole extent, otherwise we just need to take of block
|
|
|
+ * boundaries, and xfs_bunmapi will handle the rest.
|
|
|
*/
|
|
|
- if (rt && !xfs_sb_version_hasextflgbit(&mp->m_sb)) {
|
|
|
- nimap = 1;
|
|
|
- error = xfs_bmapi_read(ip, startoffset_fsb, 1,
|
|
|
- &imap, &nimap, 0);
|
|
|
+ if (XFS_IS_REALTIME_INODE(ip) &&
|
|
|
+ !xfs_sb_version_hasextflgbit(&mp->m_sb)) {
|
|
|
+ error = xfs_adjust_extent_unmap_boundaries(ip, &startoffset_fsb,
|
|
|
+ &endoffset_fsb);
|
|
|
if (error)
|
|
|
- goto out;
|
|
|
- ASSERT(nimap == 0 || nimap == 1);
|
|
|
- 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);
|
|
|
- if (mod)
|
|
|
- startoffset_fsb += mp->m_sb.sb_rextsize - mod;
|
|
|
- }
|
|
|
- nimap = 1;
|
|
|
- error = xfs_bmapi_read(ip, endoffset_fsb - 1, 1,
|
|
|
- &imap, &nimap, 0);
|
|
|
- if (error)
|
|
|
- goto out;
|
|
|
- ASSERT(nimap == 0 || nimap == 1);
|
|
|
- if (nimap && imap.br_startblock != HOLESTARTBLOCK) {
|
|
|
- ASSERT(imap.br_startblock != DELAYSTARTBLOCK);
|
|
|
- mod++;
|
|
|
- if (mod && (mod != mp->m_sb.sb_rextsize))
|
|
|
- endoffset_fsb -= mod;
|
|
|
- }
|
|
|
- }
|
|
|
- if ((done = (endoffset_fsb <= startoffset_fsb)))
|
|
|
- /*
|
|
|
- * One contiguous piece to clear
|
|
|
- */
|
|
|
- error = xfs_zero_remaining_bytes(ip, offset, offset + len - 1);
|
|
|
- else {
|
|
|
- /*
|
|
|
- * Some full blocks, possibly two pieces to clear
|
|
|
- */
|
|
|
- if (offset < XFS_FSB_TO_B(mp, startoffset_fsb))
|
|
|
- error = xfs_zero_remaining_bytes(ip, offset,
|
|
|
- XFS_FSB_TO_B(mp, startoffset_fsb) - 1);
|
|
|
- if (!error &&
|
|
|
- XFS_FSB_TO_B(mp, endoffset_fsb) < offset + len)
|
|
|
- error = xfs_zero_remaining_bytes(ip,
|
|
|
- XFS_FSB_TO_B(mp, endoffset_fsb),
|
|
|
- offset + len - 1);
|
|
|
+ return error;
|
|
|
}
|
|
|
|
|
|
- /*
|
|
|
- * free file space until done or until there is an error
|
|
|
- */
|
|
|
- resblks = XFS_DIOSTRAT_SPACE_RES(mp, 0);
|
|
|
- while (!error && !done) {
|
|
|
-
|
|
|
- /*
|
|
|
- * allocate and setup the transaction. Allow this
|
|
|
- * transaction to dip into the reserve blocks to ensure
|
|
|
- * the freeing of the space succeeds at ENOSPC.
|
|
|
- */
|
|
|
- error = xfs_trans_alloc(mp, &M_RES(mp)->tr_write, resblks, 0, 0,
|
|
|
- &tp);
|
|
|
- if (error) {
|
|
|
- ASSERT(error == -ENOSPC || XFS_FORCED_SHUTDOWN(mp));
|
|
|
- break;
|
|
|
+ if (endoffset_fsb > startoffset_fsb) {
|
|
|
+ while (!done) {
|
|
|
+ error = xfs_unmap_extent(ip, startoffset_fsb,
|
|
|
+ endoffset_fsb - startoffset_fsb, &done);
|
|
|
+ if (error)
|
|
|
+ return error;
|
|
|
}
|
|
|
- xfs_ilock(ip, XFS_ILOCK_EXCL);
|
|
|
- error = xfs_trans_reserve_quota(tp, mp,
|
|
|
- ip->i_udquot, ip->i_gdquot, ip->i_pdquot,
|
|
|
- resblks, 0, XFS_QMOPT_RES_REGBLKS);
|
|
|
- if (error)
|
|
|
- goto error1;
|
|
|
-
|
|
|
- xfs_trans_ijoin(tp, ip, 0);
|
|
|
-
|
|
|
- /*
|
|
|
- * issue the bunmapi() call to free the blocks
|
|
|
- */
|
|
|
- xfs_bmap_init(&free_list, &firstfsb);
|
|
|
- error = xfs_bunmapi(tp, ip, startoffset_fsb,
|
|
|
- endoffset_fsb - startoffset_fsb,
|
|
|
- 0, 2, &firstfsb, &free_list, &done);
|
|
|
- if (error)
|
|
|
- goto error0;
|
|
|
-
|
|
|
- /*
|
|
|
- * complete the transaction
|
|
|
- */
|
|
|
- error = xfs_bmap_finish(&tp, &free_list, NULL);
|
|
|
- if (error)
|
|
|
- goto error0;
|
|
|
-
|
|
|
- error = xfs_trans_commit(tp);
|
|
|
- xfs_iunlock(ip, XFS_ILOCK_EXCL);
|
|
|
}
|
|
|
|
|
|
- out:
|
|
|
- return error;
|
|
|
-
|
|
|
- error0:
|
|
|
- xfs_bmap_cancel(&free_list);
|
|
|
- error1:
|
|
|
- xfs_trans_cancel(tp);
|
|
|
- xfs_iunlock(ip, XFS_ILOCK_EXCL);
|
|
|
- goto out;
|
|
|
+ /*
|
|
|
+ * Now that we've unmap all full blocks we'll have to zero out any
|
|
|
+ * partial block at the beginning and/or end. xfs_zero_range is
|
|
|
+ * smart enough to skip any holes, including those we just created.
|
|
|
+ */
|
|
|
+ return xfs_zero_range(ip, offset, len, NULL);
|
|
|
}
|
|
|
|
|
|
/*
|