Loading fs/open.c +26 −3 Original line number Diff line number Diff line Loading @@ -231,7 +231,13 @@ int do_fallocate(struct file *file, int mode, loff_t offset, loff_t len) return -EINVAL; /* Return error if mode is not supported */ if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE)) if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE | FALLOC_FL_COLLAPSE_RANGE | FALLOC_FL_ZERO_RANGE)) return -EOPNOTSUPP; /* Punch hole and zero range are mutually exclusive */ if ((mode & (FALLOC_FL_PUNCH_HOLE | FALLOC_FL_ZERO_RANGE)) == (FALLOC_FL_PUNCH_HOLE | FALLOC_FL_ZERO_RANGE)) return -EOPNOTSUPP; /* Punch hole must have keep size set */ Loading @@ -239,11 +245,20 @@ int do_fallocate(struct file *file, int mode, loff_t offset, loff_t len) !(mode & FALLOC_FL_KEEP_SIZE)) return -EOPNOTSUPP; /* Collapse range should only be used exclusively. */ if ((mode & FALLOC_FL_COLLAPSE_RANGE) && (mode & ~FALLOC_FL_COLLAPSE_RANGE)) return -EINVAL; if (!(file->f_mode & FMODE_WRITE)) return -EBADF; /* It's not possible punch hole on append only file */ if (mode & FALLOC_FL_PUNCH_HOLE && IS_APPEND(inode)) /* * It's not possible to punch hole or perform collapse range * on append only file */ if (mode & (FALLOC_FL_PUNCH_HOLE | FALLOC_FL_COLLAPSE_RANGE) && IS_APPEND(inode)) return -EPERM; if (IS_IMMUTABLE(inode)) Loading Loading @@ -271,6 +286,14 @@ int do_fallocate(struct file *file, int mode, loff_t offset, loff_t len) if (((offset + len) > inode->i_sb->s_maxbytes) || ((offset + len) < 0)) return -EFBIG; /* * There is no need to overlap collapse range with EOF, in which case * it is effectively a truncate operation */ if ((mode & FALLOC_FL_COLLAPSE_RANGE) && (offset + len >= i_size_read(inode))) return -EINVAL; if (!file->f_op->fallocate) return -EOPNOTSUPP; Loading fs/xfs/xfs_bmap.c +193 −0 Original line number Diff line number Diff line Loading @@ -5378,3 +5378,196 @@ xfs_bunmapi( } return error; } /* * Shift extent records to the left to cover a hole. * * The maximum number of extents to be shifted in a single operation * is @num_exts, and @current_ext keeps track of the current extent * index we have shifted. @offset_shift_fsb is the length by which each * extent is shifted. If there is no hole to shift the extents * into, this will be considered invalid operation and we abort immediately. */ int xfs_bmap_shift_extents( struct xfs_trans *tp, struct xfs_inode *ip, int *done, xfs_fileoff_t start_fsb, xfs_fileoff_t offset_shift_fsb, xfs_extnum_t *current_ext, xfs_fsblock_t *firstblock, struct xfs_bmap_free *flist, int num_exts) { struct xfs_btree_cur *cur; struct xfs_bmbt_rec_host *gotp; struct xfs_bmbt_irec got; struct xfs_bmbt_irec left; struct xfs_mount *mp = ip->i_mount; struct xfs_ifork *ifp; xfs_extnum_t nexts = 0; xfs_fileoff_t startoff; int error = 0; int i; int whichfork = XFS_DATA_FORK; int logflags; xfs_filblks_t blockcount = 0; if (unlikely(XFS_TEST_ERROR( (XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_EXTENTS && XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_BTREE), mp, XFS_ERRTAG_BMAPIFORMAT, XFS_RANDOM_BMAPIFORMAT))) { XFS_ERROR_REPORT("xfs_bmap_shift_extents", XFS_ERRLEVEL_LOW, mp); return XFS_ERROR(EFSCORRUPTED); } if (XFS_FORCED_SHUTDOWN(mp)) return XFS_ERROR(EIO); ASSERT(current_ext != NULL); ifp = XFS_IFORK_PTR(ip, whichfork); if (!(ifp->if_flags & XFS_IFEXTENTS)) { /* Read in all the extents */ error = xfs_iread_extents(tp, ip, whichfork); if (error) return error; } /* * If *current_ext is 0, we would need to lookup the extent * from where we would start shifting and store it in gotp. */ if (!*current_ext) { gotp = xfs_iext_bno_to_ext(ifp, start_fsb, current_ext); /* * gotp can be null in 2 cases: 1) if there are no extents * or 2) start_fsb lies in a hole beyond which there are * no extents. Either way, we are done. */ if (!gotp) { *done = 1; return 0; } } /* We are going to change core inode */ logflags = XFS_ILOG_CORE; if (ifp->if_flags & XFS_IFBROOT) { cur = xfs_bmbt_init_cursor(mp, tp, ip, whichfork); cur->bc_private.b.firstblock = *firstblock; cur->bc_private.b.flist = flist; cur->bc_private.b.flags = 0; } else { cur = NULL; logflags |= XFS_ILOG_DEXT; } while (nexts++ < num_exts && *current_ext < XFS_IFORK_NEXTENTS(ip, whichfork)) { gotp = xfs_iext_get_ext(ifp, *current_ext); xfs_bmbt_get_all(gotp, &got); startoff = got.br_startoff - offset_shift_fsb; /* * Before shifting extent into hole, make sure that the hole * is large enough to accomodate the shift. */ if (*current_ext) { xfs_bmbt_get_all(xfs_iext_get_ext(ifp, *current_ext - 1), &left); if (startoff < left.br_startoff + left.br_blockcount) error = XFS_ERROR(EINVAL); } else if (offset_shift_fsb > got.br_startoff) { /* * When first extent is shifted, offset_shift_fsb * should be less than the stating offset of * the first extent. */ error = XFS_ERROR(EINVAL); } if (error) goto del_cursor; if (cur) { error = xfs_bmbt_lookup_eq(cur, got.br_startoff, got.br_startblock, got.br_blockcount, &i); if (error) goto del_cursor; XFS_WANT_CORRUPTED_GOTO(i == 1, del_cursor); } /* Check if we can merge 2 adjacent extents */ if (*current_ext && left.br_startoff + left.br_blockcount == startoff && left.br_startblock + left.br_blockcount == got.br_startblock && left.br_state == got.br_state && left.br_blockcount + got.br_blockcount <= MAXEXTLEN) { blockcount = left.br_blockcount + got.br_blockcount; xfs_iext_remove(ip, *current_ext, 1, 0); if (cur) { error = xfs_btree_delete(cur, &i); if (error) goto del_cursor; XFS_WANT_CORRUPTED_GOTO(i == 1, del_cursor); } XFS_IFORK_NEXT_SET(ip, whichfork, XFS_IFORK_NEXTENTS(ip, whichfork) - 1); gotp = xfs_iext_get_ext(ifp, --*current_ext); xfs_bmbt_get_all(gotp, &got); /* Make cursor point to the extent we will update */ if (cur) { error = xfs_bmbt_lookup_eq(cur, got.br_startoff, got.br_startblock, got.br_blockcount, &i); if (error) goto del_cursor; XFS_WANT_CORRUPTED_GOTO(i == 1, del_cursor); } xfs_bmbt_set_blockcount(gotp, blockcount); got.br_blockcount = blockcount; } else { /* We have to update the startoff */ xfs_bmbt_set_startoff(gotp, startoff); got.br_startoff = startoff; } if (cur) { error = xfs_bmbt_update(cur, got.br_startoff, got.br_startblock, got.br_blockcount, got.br_state); if (error) goto del_cursor; } (*current_ext)++; } /* Check if we are done */ if (*current_ext == XFS_IFORK_NEXTENTS(ip, whichfork)) *done = 1; del_cursor: if (cur) xfs_btree_del_cursor(cur, error ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR); xfs_trans_log_inode(tp, ip, logflags); return error; } fs/xfs/xfs_bmap.h +15 −0 Original line number Diff line number Diff line Loading @@ -127,6 +127,16 @@ static inline void xfs_bmap_init(xfs_bmap_free_t *flp, xfs_fsblock_t *fbp) { BMAP_RIGHT_FILLING, "RF" }, \ { BMAP_ATTRFORK, "ATTR" } /* * This macro is used to determine how many extents will be shifted * in one write transaction. We could require two splits, * an extent move on the first and an extent merge on the second, * So it is proper that one extent is shifted inside write transaction * at a time. */ #define XFS_BMAP_MAX_SHIFT_EXTENTS 1 #ifdef DEBUG void xfs_bmap_trace_exlist(struct xfs_inode *ip, xfs_extnum_t cnt, int whichfork, unsigned long caller_ip); Loading Loading @@ -169,5 +179,10 @@ int xfs_bunmapi(struct xfs_trans *tp, struct xfs_inode *ip, int xfs_check_nostate_extents(struct xfs_ifork *ifp, xfs_extnum_t idx, xfs_extnum_t num); uint xfs_default_attroffset(struct xfs_inode *ip); int xfs_bmap_shift_extents(struct xfs_trans *tp, struct xfs_inode *ip, int *done, xfs_fileoff_t start_fsb, xfs_fileoff_t offset_shift_fsb, xfs_extnum_t *current_ext, xfs_fsblock_t *firstblock, struct xfs_bmap_free *flist, int num_exts); #endif /* __XFS_BMAP_H__ */ fs/xfs/xfs_bmap_util.c +96 −1 Original line number Diff line number Diff line Loading @@ -1349,7 +1349,6 @@ xfs_free_file_space( * the freeing of the space succeeds at ENOSPC. */ tp = xfs_trans_alloc(mp, XFS_TRANS_DIOSTRAT); tp->t_flags |= XFS_TRANS_RESERVE; error = xfs_trans_reserve(tp, &M_RES(mp)->tr_write, resblks, 0); /* Loading Loading @@ -1467,6 +1466,102 @@ xfs_zero_file_space( } /* * xfs_collapse_file_space() * This routine frees disk space and shift extent for the given file. * The first thing we do is to free data blocks in the specified range * by calling xfs_free_file_space(). It would also sync dirty data * and invalidate page cache over the region on which collapse range * is working. And Shift extent records to the left to cover a hole. * RETURNS: * 0 on success * errno on error * */ int xfs_collapse_file_space( struct xfs_inode *ip, xfs_off_t offset, xfs_off_t len) { int done = 0; struct xfs_mount *mp = ip->i_mount; struct xfs_trans *tp; int error; xfs_extnum_t current_ext = 0; struct xfs_bmap_free free_list; xfs_fsblock_t first_block; int committed; xfs_fileoff_t start_fsb; xfs_fileoff_t shift_fsb; ASSERT(xfs_isilocked(ip, XFS_IOLOCK_EXCL)); trace_xfs_collapse_file_space(ip); start_fsb = XFS_B_TO_FSB(mp, offset + len); shift_fsb = XFS_B_TO_FSB(mp, len); error = xfs_free_file_space(ip, offset, len); if (error) return error; while (!error && !done) { tp = xfs_trans_alloc(mp, XFS_TRANS_DIOSTRAT); tp->t_flags |= XFS_TRANS_RESERVE; /* * We would need to reserve permanent block for transaction. * This will come into picture when after shifting extent into * hole we found that adjacent extents can be merged which * may lead to freeing of a block during record update. */ error = xfs_trans_reserve(tp, &M_RES(mp)->tr_write, XFS_DIOSTRAT_SPACE_RES(mp, 0), 0); if (error) { ASSERT(error == ENOSPC || XFS_FORCED_SHUTDOWN(mp)); xfs_trans_cancel(tp, 0); break; } xfs_ilock(ip, XFS_ILOCK_EXCL); error = xfs_trans_reserve_quota(tp, mp, ip->i_udquot, ip->i_gdquot, ip->i_pdquot, XFS_DIOSTRAT_SPACE_RES(mp, 0), 0, XFS_QMOPT_RES_REGBLKS); if (error) goto out; xfs_trans_ijoin(tp, ip, 0); xfs_bmap_init(&free_list, &first_block); /* * We are using the write transaction in which max 2 bmbt * updates are allowed */ error = xfs_bmap_shift_extents(tp, ip, &done, start_fsb, shift_fsb, ¤t_ext, &first_block, &free_list, XFS_BMAP_MAX_SHIFT_EXTENTS); if (error) goto out; error = xfs_bmap_finish(&tp, &free_list, &committed); if (error) goto out; error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES); xfs_iunlock(ip, XFS_ILOCK_EXCL); } return error; out: xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES | XFS_TRANS_ABORT); xfs_iunlock(ip, XFS_ILOCK_EXCL); return error; } /* * We need to check that the format of the data fork in the temporary inode is * valid for the target inode before doing the swap. This is not a problem with Loading fs/xfs/xfs_bmap_util.h +2 −0 Original line number Diff line number Diff line Loading @@ -99,6 +99,8 @@ int xfs_free_file_space(struct xfs_inode *ip, xfs_off_t offset, xfs_off_t len); int xfs_zero_file_space(struct xfs_inode *ip, xfs_off_t offset, xfs_off_t len); int xfs_collapse_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); Loading Loading
fs/open.c +26 −3 Original line number Diff line number Diff line Loading @@ -231,7 +231,13 @@ int do_fallocate(struct file *file, int mode, loff_t offset, loff_t len) return -EINVAL; /* Return error if mode is not supported */ if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE)) if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE | FALLOC_FL_COLLAPSE_RANGE | FALLOC_FL_ZERO_RANGE)) return -EOPNOTSUPP; /* Punch hole and zero range are mutually exclusive */ if ((mode & (FALLOC_FL_PUNCH_HOLE | FALLOC_FL_ZERO_RANGE)) == (FALLOC_FL_PUNCH_HOLE | FALLOC_FL_ZERO_RANGE)) return -EOPNOTSUPP; /* Punch hole must have keep size set */ Loading @@ -239,11 +245,20 @@ int do_fallocate(struct file *file, int mode, loff_t offset, loff_t len) !(mode & FALLOC_FL_KEEP_SIZE)) return -EOPNOTSUPP; /* Collapse range should only be used exclusively. */ if ((mode & FALLOC_FL_COLLAPSE_RANGE) && (mode & ~FALLOC_FL_COLLAPSE_RANGE)) return -EINVAL; if (!(file->f_mode & FMODE_WRITE)) return -EBADF; /* It's not possible punch hole on append only file */ if (mode & FALLOC_FL_PUNCH_HOLE && IS_APPEND(inode)) /* * It's not possible to punch hole or perform collapse range * on append only file */ if (mode & (FALLOC_FL_PUNCH_HOLE | FALLOC_FL_COLLAPSE_RANGE) && IS_APPEND(inode)) return -EPERM; if (IS_IMMUTABLE(inode)) Loading Loading @@ -271,6 +286,14 @@ int do_fallocate(struct file *file, int mode, loff_t offset, loff_t len) if (((offset + len) > inode->i_sb->s_maxbytes) || ((offset + len) < 0)) return -EFBIG; /* * There is no need to overlap collapse range with EOF, in which case * it is effectively a truncate operation */ if ((mode & FALLOC_FL_COLLAPSE_RANGE) && (offset + len >= i_size_read(inode))) return -EINVAL; if (!file->f_op->fallocate) return -EOPNOTSUPP; Loading
fs/xfs/xfs_bmap.c +193 −0 Original line number Diff line number Diff line Loading @@ -5378,3 +5378,196 @@ xfs_bunmapi( } return error; } /* * Shift extent records to the left to cover a hole. * * The maximum number of extents to be shifted in a single operation * is @num_exts, and @current_ext keeps track of the current extent * index we have shifted. @offset_shift_fsb is the length by which each * extent is shifted. If there is no hole to shift the extents * into, this will be considered invalid operation and we abort immediately. */ int xfs_bmap_shift_extents( struct xfs_trans *tp, struct xfs_inode *ip, int *done, xfs_fileoff_t start_fsb, xfs_fileoff_t offset_shift_fsb, xfs_extnum_t *current_ext, xfs_fsblock_t *firstblock, struct xfs_bmap_free *flist, int num_exts) { struct xfs_btree_cur *cur; struct xfs_bmbt_rec_host *gotp; struct xfs_bmbt_irec got; struct xfs_bmbt_irec left; struct xfs_mount *mp = ip->i_mount; struct xfs_ifork *ifp; xfs_extnum_t nexts = 0; xfs_fileoff_t startoff; int error = 0; int i; int whichfork = XFS_DATA_FORK; int logflags; xfs_filblks_t blockcount = 0; if (unlikely(XFS_TEST_ERROR( (XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_EXTENTS && XFS_IFORK_FORMAT(ip, whichfork) != XFS_DINODE_FMT_BTREE), mp, XFS_ERRTAG_BMAPIFORMAT, XFS_RANDOM_BMAPIFORMAT))) { XFS_ERROR_REPORT("xfs_bmap_shift_extents", XFS_ERRLEVEL_LOW, mp); return XFS_ERROR(EFSCORRUPTED); } if (XFS_FORCED_SHUTDOWN(mp)) return XFS_ERROR(EIO); ASSERT(current_ext != NULL); ifp = XFS_IFORK_PTR(ip, whichfork); if (!(ifp->if_flags & XFS_IFEXTENTS)) { /* Read in all the extents */ error = xfs_iread_extents(tp, ip, whichfork); if (error) return error; } /* * If *current_ext is 0, we would need to lookup the extent * from where we would start shifting and store it in gotp. */ if (!*current_ext) { gotp = xfs_iext_bno_to_ext(ifp, start_fsb, current_ext); /* * gotp can be null in 2 cases: 1) if there are no extents * or 2) start_fsb lies in a hole beyond which there are * no extents. Either way, we are done. */ if (!gotp) { *done = 1; return 0; } } /* We are going to change core inode */ logflags = XFS_ILOG_CORE; if (ifp->if_flags & XFS_IFBROOT) { cur = xfs_bmbt_init_cursor(mp, tp, ip, whichfork); cur->bc_private.b.firstblock = *firstblock; cur->bc_private.b.flist = flist; cur->bc_private.b.flags = 0; } else { cur = NULL; logflags |= XFS_ILOG_DEXT; } while (nexts++ < num_exts && *current_ext < XFS_IFORK_NEXTENTS(ip, whichfork)) { gotp = xfs_iext_get_ext(ifp, *current_ext); xfs_bmbt_get_all(gotp, &got); startoff = got.br_startoff - offset_shift_fsb; /* * Before shifting extent into hole, make sure that the hole * is large enough to accomodate the shift. */ if (*current_ext) { xfs_bmbt_get_all(xfs_iext_get_ext(ifp, *current_ext - 1), &left); if (startoff < left.br_startoff + left.br_blockcount) error = XFS_ERROR(EINVAL); } else if (offset_shift_fsb > got.br_startoff) { /* * When first extent is shifted, offset_shift_fsb * should be less than the stating offset of * the first extent. */ error = XFS_ERROR(EINVAL); } if (error) goto del_cursor; if (cur) { error = xfs_bmbt_lookup_eq(cur, got.br_startoff, got.br_startblock, got.br_blockcount, &i); if (error) goto del_cursor; XFS_WANT_CORRUPTED_GOTO(i == 1, del_cursor); } /* Check if we can merge 2 adjacent extents */ if (*current_ext && left.br_startoff + left.br_blockcount == startoff && left.br_startblock + left.br_blockcount == got.br_startblock && left.br_state == got.br_state && left.br_blockcount + got.br_blockcount <= MAXEXTLEN) { blockcount = left.br_blockcount + got.br_blockcount; xfs_iext_remove(ip, *current_ext, 1, 0); if (cur) { error = xfs_btree_delete(cur, &i); if (error) goto del_cursor; XFS_WANT_CORRUPTED_GOTO(i == 1, del_cursor); } XFS_IFORK_NEXT_SET(ip, whichfork, XFS_IFORK_NEXTENTS(ip, whichfork) - 1); gotp = xfs_iext_get_ext(ifp, --*current_ext); xfs_bmbt_get_all(gotp, &got); /* Make cursor point to the extent we will update */ if (cur) { error = xfs_bmbt_lookup_eq(cur, got.br_startoff, got.br_startblock, got.br_blockcount, &i); if (error) goto del_cursor; XFS_WANT_CORRUPTED_GOTO(i == 1, del_cursor); } xfs_bmbt_set_blockcount(gotp, blockcount); got.br_blockcount = blockcount; } else { /* We have to update the startoff */ xfs_bmbt_set_startoff(gotp, startoff); got.br_startoff = startoff; } if (cur) { error = xfs_bmbt_update(cur, got.br_startoff, got.br_startblock, got.br_blockcount, got.br_state); if (error) goto del_cursor; } (*current_ext)++; } /* Check if we are done */ if (*current_ext == XFS_IFORK_NEXTENTS(ip, whichfork)) *done = 1; del_cursor: if (cur) xfs_btree_del_cursor(cur, error ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR); xfs_trans_log_inode(tp, ip, logflags); return error; }
fs/xfs/xfs_bmap.h +15 −0 Original line number Diff line number Diff line Loading @@ -127,6 +127,16 @@ static inline void xfs_bmap_init(xfs_bmap_free_t *flp, xfs_fsblock_t *fbp) { BMAP_RIGHT_FILLING, "RF" }, \ { BMAP_ATTRFORK, "ATTR" } /* * This macro is used to determine how many extents will be shifted * in one write transaction. We could require two splits, * an extent move on the first and an extent merge on the second, * So it is proper that one extent is shifted inside write transaction * at a time. */ #define XFS_BMAP_MAX_SHIFT_EXTENTS 1 #ifdef DEBUG void xfs_bmap_trace_exlist(struct xfs_inode *ip, xfs_extnum_t cnt, int whichfork, unsigned long caller_ip); Loading Loading @@ -169,5 +179,10 @@ int xfs_bunmapi(struct xfs_trans *tp, struct xfs_inode *ip, int xfs_check_nostate_extents(struct xfs_ifork *ifp, xfs_extnum_t idx, xfs_extnum_t num); uint xfs_default_attroffset(struct xfs_inode *ip); int xfs_bmap_shift_extents(struct xfs_trans *tp, struct xfs_inode *ip, int *done, xfs_fileoff_t start_fsb, xfs_fileoff_t offset_shift_fsb, xfs_extnum_t *current_ext, xfs_fsblock_t *firstblock, struct xfs_bmap_free *flist, int num_exts); #endif /* __XFS_BMAP_H__ */
fs/xfs/xfs_bmap_util.c +96 −1 Original line number Diff line number Diff line Loading @@ -1349,7 +1349,6 @@ xfs_free_file_space( * the freeing of the space succeeds at ENOSPC. */ tp = xfs_trans_alloc(mp, XFS_TRANS_DIOSTRAT); tp->t_flags |= XFS_TRANS_RESERVE; error = xfs_trans_reserve(tp, &M_RES(mp)->tr_write, resblks, 0); /* Loading Loading @@ -1467,6 +1466,102 @@ xfs_zero_file_space( } /* * xfs_collapse_file_space() * This routine frees disk space and shift extent for the given file. * The first thing we do is to free data blocks in the specified range * by calling xfs_free_file_space(). It would also sync dirty data * and invalidate page cache over the region on which collapse range * is working. And Shift extent records to the left to cover a hole. * RETURNS: * 0 on success * errno on error * */ int xfs_collapse_file_space( struct xfs_inode *ip, xfs_off_t offset, xfs_off_t len) { int done = 0; struct xfs_mount *mp = ip->i_mount; struct xfs_trans *tp; int error; xfs_extnum_t current_ext = 0; struct xfs_bmap_free free_list; xfs_fsblock_t first_block; int committed; xfs_fileoff_t start_fsb; xfs_fileoff_t shift_fsb; ASSERT(xfs_isilocked(ip, XFS_IOLOCK_EXCL)); trace_xfs_collapse_file_space(ip); start_fsb = XFS_B_TO_FSB(mp, offset + len); shift_fsb = XFS_B_TO_FSB(mp, len); error = xfs_free_file_space(ip, offset, len); if (error) return error; while (!error && !done) { tp = xfs_trans_alloc(mp, XFS_TRANS_DIOSTRAT); tp->t_flags |= XFS_TRANS_RESERVE; /* * We would need to reserve permanent block for transaction. * This will come into picture when after shifting extent into * hole we found that adjacent extents can be merged which * may lead to freeing of a block during record update. */ error = xfs_trans_reserve(tp, &M_RES(mp)->tr_write, XFS_DIOSTRAT_SPACE_RES(mp, 0), 0); if (error) { ASSERT(error == ENOSPC || XFS_FORCED_SHUTDOWN(mp)); xfs_trans_cancel(tp, 0); break; } xfs_ilock(ip, XFS_ILOCK_EXCL); error = xfs_trans_reserve_quota(tp, mp, ip->i_udquot, ip->i_gdquot, ip->i_pdquot, XFS_DIOSTRAT_SPACE_RES(mp, 0), 0, XFS_QMOPT_RES_REGBLKS); if (error) goto out; xfs_trans_ijoin(tp, ip, 0); xfs_bmap_init(&free_list, &first_block); /* * We are using the write transaction in which max 2 bmbt * updates are allowed */ error = xfs_bmap_shift_extents(tp, ip, &done, start_fsb, shift_fsb, ¤t_ext, &first_block, &free_list, XFS_BMAP_MAX_SHIFT_EXTENTS); if (error) goto out; error = xfs_bmap_finish(&tp, &free_list, &committed); if (error) goto out; error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES); xfs_iunlock(ip, XFS_ILOCK_EXCL); } return error; out: xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES | XFS_TRANS_ABORT); xfs_iunlock(ip, XFS_ILOCK_EXCL); return error; } /* * We need to check that the format of the data fork in the temporary inode is * valid for the target inode before doing the swap. This is not a problem with Loading
fs/xfs/xfs_bmap_util.h +2 −0 Original line number Diff line number Diff line Loading @@ -99,6 +99,8 @@ int xfs_free_file_space(struct xfs_inode *ip, xfs_off_t offset, xfs_off_t len); int xfs_zero_file_space(struct xfs_inode *ip, xfs_off_t offset, xfs_off_t len); int xfs_collapse_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); Loading