diff options
Diffstat (limited to 'fs/xfs/libxfs/xfs_bmap.c')
-rw-r--r-- | fs/xfs/libxfs/xfs_bmap.c | 418 |
1 files changed, 246 insertions, 172 deletions
diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c index c27344cf38e1..c6eb21940783 100644 --- a/fs/xfs/libxfs/xfs_bmap.c +++ b/fs/xfs/libxfs/xfs_bmap.c | |||
@@ -3974,9 +3974,6 @@ xfs_bmap_remap_alloc( | |||
3974 | * allocating, so skip that check by pretending to be freeing. | 3974 | * allocating, so skip that check by pretending to be freeing. |
3975 | */ | 3975 | */ |
3976 | error = xfs_alloc_fix_freelist(&args, XFS_ALLOC_FLAG_FREEING); | 3976 | error = xfs_alloc_fix_freelist(&args, XFS_ALLOC_FLAG_FREEING); |
3977 | if (error) | ||
3978 | goto error0; | ||
3979 | error0: | ||
3980 | xfs_perag_put(args.pag); | 3977 | xfs_perag_put(args.pag); |
3981 | if (error) | 3978 | if (error) |
3982 | trace_xfs_bmap_remap_alloc_error(ap->ip, error, _RET_IP_); | 3979 | trace_xfs_bmap_remap_alloc_error(ap->ip, error, _RET_IP_); |
@@ -3999,6 +3996,39 @@ xfs_bmap_alloc( | |||
3999 | return xfs_bmap_btalloc(ap); | 3996 | return xfs_bmap_btalloc(ap); |
4000 | } | 3997 | } |
4001 | 3998 | ||
3999 | /* Trim extent to fit a logical block range. */ | ||
4000 | void | ||
4001 | xfs_trim_extent( | ||
4002 | struct xfs_bmbt_irec *irec, | ||
4003 | xfs_fileoff_t bno, | ||
4004 | xfs_filblks_t len) | ||
4005 | { | ||
4006 | xfs_fileoff_t distance; | ||
4007 | xfs_fileoff_t end = bno + len; | ||
4008 | |||
4009 | if (irec->br_startoff + irec->br_blockcount <= bno || | ||
4010 | irec->br_startoff >= end) { | ||
4011 | irec->br_blockcount = 0; | ||
4012 | return; | ||
4013 | } | ||
4014 | |||
4015 | if (irec->br_startoff < bno) { | ||
4016 | distance = bno - irec->br_startoff; | ||
4017 | if (isnullstartblock(irec->br_startblock)) | ||
4018 | irec->br_startblock = DELAYSTARTBLOCK; | ||
4019 | if (irec->br_startblock != DELAYSTARTBLOCK && | ||
4020 | irec->br_startblock != HOLESTARTBLOCK) | ||
4021 | irec->br_startblock += distance; | ||
4022 | irec->br_startoff += distance; | ||
4023 | irec->br_blockcount -= distance; | ||
4024 | } | ||
4025 | |||
4026 | if (end < irec->br_startoff + irec->br_blockcount) { | ||
4027 | distance = irec->br_startoff + irec->br_blockcount - end; | ||
4028 | irec->br_blockcount -= distance; | ||
4029 | } | ||
4030 | } | ||
4031 | |||
4002 | /* | 4032 | /* |
4003 | * Trim the returned map to the required bounds | 4033 | * Trim the returned map to the required bounds |
4004 | */ | 4034 | */ |
@@ -4829,6 +4859,219 @@ xfs_bmap_split_indlen( | |||
4829 | return stolen; | 4859 | return stolen; |
4830 | } | 4860 | } |
4831 | 4861 | ||
4862 | int | ||
4863 | xfs_bmap_del_extent_delay( | ||
4864 | struct xfs_inode *ip, | ||
4865 | int whichfork, | ||
4866 | xfs_extnum_t *idx, | ||
4867 | struct xfs_bmbt_irec *got, | ||
4868 | struct xfs_bmbt_irec *del) | ||
4869 | { | ||
4870 | struct xfs_mount *mp = ip->i_mount; | ||
4871 | struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, whichfork); | ||
4872 | struct xfs_bmbt_irec new; | ||
4873 | int64_t da_old, da_new, da_diff = 0; | ||
4874 | xfs_fileoff_t del_endoff, got_endoff; | ||
4875 | xfs_filblks_t got_indlen, new_indlen, stolen; | ||
4876 | int error = 0, state = 0; | ||
4877 | bool isrt; | ||
4878 | |||
4879 | XFS_STATS_INC(mp, xs_del_exlist); | ||
4880 | |||
4881 | isrt = (whichfork == XFS_DATA_FORK) && XFS_IS_REALTIME_INODE(ip); | ||
4882 | del_endoff = del->br_startoff + del->br_blockcount; | ||
4883 | got_endoff = got->br_startoff + got->br_blockcount; | ||
4884 | da_old = startblockval(got->br_startblock); | ||
4885 | da_new = 0; | ||
4886 | |||
4887 | ASSERT(*idx >= 0); | ||
4888 | ASSERT(*idx < ifp->if_bytes / sizeof(struct xfs_bmbt_rec)); | ||
4889 | ASSERT(del->br_blockcount > 0); | ||
4890 | ASSERT(got->br_startoff <= del->br_startoff); | ||
4891 | ASSERT(got_endoff >= del_endoff); | ||
4892 | |||
4893 | if (isrt) { | ||
4894 | int64_t rtexts = XFS_FSB_TO_B(mp, del->br_blockcount); | ||
4895 | |||
4896 | do_div(rtexts, mp->m_sb.sb_rextsize); | ||
4897 | xfs_mod_frextents(mp, rtexts); | ||
4898 | } | ||
4899 | |||
4900 | /* | ||
4901 | * Update the inode delalloc counter now and wait to update the | ||
4902 | * sb counters as we might have to borrow some blocks for the | ||
4903 | * indirect block accounting. | ||
4904 | */ | ||
4905 | xfs_trans_reserve_quota_nblks(NULL, ip, -((long)del->br_blockcount), 0, | ||
4906 | isrt ? XFS_QMOPT_RES_RTBLKS : XFS_QMOPT_RES_REGBLKS); | ||
4907 | ip->i_delayed_blks -= del->br_blockcount; | ||
4908 | |||
4909 | if (whichfork == XFS_COW_FORK) | ||
4910 | state |= BMAP_COWFORK; | ||
4911 | |||
4912 | if (got->br_startoff == del->br_startoff) | ||
4913 | state |= BMAP_LEFT_CONTIG; | ||
4914 | if (got_endoff == del_endoff) | ||
4915 | state |= BMAP_RIGHT_CONTIG; | ||
4916 | |||
4917 | switch (state & (BMAP_LEFT_CONTIG | BMAP_RIGHT_CONTIG)) { | ||
4918 | case BMAP_LEFT_CONTIG | BMAP_RIGHT_CONTIG: | ||
4919 | /* | ||
4920 | * Matches the whole extent. Delete the entry. | ||
4921 | */ | ||
4922 | xfs_iext_remove(ip, *idx, 1, state); | ||
4923 | --*idx; | ||
4924 | break; | ||
4925 | case BMAP_LEFT_CONTIG: | ||
4926 | /* | ||
4927 | * Deleting the first part of the extent. | ||
4928 | */ | ||
4929 | trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_); | ||
4930 | got->br_startoff = del_endoff; | ||
4931 | got->br_blockcount -= del->br_blockcount; | ||
4932 | da_new = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, | ||
4933 | got->br_blockcount), da_old); | ||
4934 | got->br_startblock = nullstartblock((int)da_new); | ||
4935 | xfs_bmbt_set_all(xfs_iext_get_ext(ifp, *idx), got); | ||
4936 | trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_); | ||
4937 | break; | ||
4938 | case BMAP_RIGHT_CONTIG: | ||
4939 | /* | ||
4940 | * Deleting the last part of the extent. | ||
4941 | */ | ||
4942 | trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_); | ||
4943 | got->br_blockcount = got->br_blockcount - del->br_blockcount; | ||
4944 | da_new = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, | ||
4945 | got->br_blockcount), da_old); | ||
4946 | got->br_startblock = nullstartblock((int)da_new); | ||
4947 | xfs_bmbt_set_all(xfs_iext_get_ext(ifp, *idx), got); | ||
4948 | trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_); | ||
4949 | break; | ||
4950 | case 0: | ||
4951 | /* | ||
4952 | * Deleting the middle of the extent. | ||
4953 | * | ||
4954 | * Distribute the original indlen reservation across the two new | ||
4955 | * extents. Steal blocks from the deleted extent if necessary. | ||
4956 | * Stealing blocks simply fudges the fdblocks accounting below. | ||
4957 | * Warn if either of the new indlen reservations is zero as this | ||
4958 | * can lead to delalloc problems. | ||
4959 | */ | ||
4960 | trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_); | ||
4961 | |||
4962 | got->br_blockcount = del->br_startoff - got->br_startoff; | ||
4963 | got_indlen = xfs_bmap_worst_indlen(ip, got->br_blockcount); | ||
4964 | |||
4965 | new.br_blockcount = got_endoff - del_endoff; | ||
4966 | new_indlen = xfs_bmap_worst_indlen(ip, new.br_blockcount); | ||
4967 | |||
4968 | WARN_ON_ONCE(!got_indlen || !new_indlen); | ||
4969 | stolen = xfs_bmap_split_indlen(da_old, &got_indlen, &new_indlen, | ||
4970 | del->br_blockcount); | ||
4971 | |||
4972 | got->br_startblock = nullstartblock((int)got_indlen); | ||
4973 | xfs_bmbt_set_all(xfs_iext_get_ext(ifp, *idx), got); | ||
4974 | trace_xfs_bmap_post_update(ip, *idx, 0, _THIS_IP_); | ||
4975 | |||
4976 | new.br_startoff = del_endoff; | ||
4977 | new.br_state = got->br_state; | ||
4978 | new.br_startblock = nullstartblock((int)new_indlen); | ||
4979 | |||
4980 | ++*idx; | ||
4981 | xfs_iext_insert(ip, *idx, 1, &new, state); | ||
4982 | |||
4983 | da_new = got_indlen + new_indlen - stolen; | ||
4984 | del->br_blockcount -= stolen; | ||
4985 | break; | ||
4986 | } | ||
4987 | |||
4988 | ASSERT(da_old >= da_new); | ||
4989 | da_diff = da_old - da_new; | ||
4990 | if (!isrt) | ||
4991 | da_diff += del->br_blockcount; | ||
4992 | if (da_diff) | ||
4993 | xfs_mod_fdblocks(mp, da_diff, false); | ||
4994 | return error; | ||
4995 | } | ||
4996 | |||
4997 | void | ||
4998 | xfs_bmap_del_extent_cow( | ||
4999 | struct xfs_inode *ip, | ||
5000 | xfs_extnum_t *idx, | ||
5001 | struct xfs_bmbt_irec *got, | ||
5002 | struct xfs_bmbt_irec *del) | ||
5003 | { | ||
5004 | struct xfs_mount *mp = ip->i_mount; | ||
5005 | struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, XFS_COW_FORK); | ||
5006 | struct xfs_bmbt_irec new; | ||
5007 | xfs_fileoff_t del_endoff, got_endoff; | ||
5008 | int state = BMAP_COWFORK; | ||
5009 | |||
5010 | XFS_STATS_INC(mp, xs_del_exlist); | ||
5011 | |||
5012 | del_endoff = del->br_startoff + del->br_blockcount; | ||
5013 | got_endoff = got->br_startoff + got->br_blockcount; | ||
5014 | |||
5015 | ASSERT(*idx >= 0); | ||
5016 | ASSERT(*idx < ifp->if_bytes / sizeof(struct xfs_bmbt_rec)); | ||
5017 | ASSERT(del->br_blockcount > 0); | ||
5018 | ASSERT(got->br_startoff <= del->br_startoff); | ||
5019 | ASSERT(got_endoff >= del_endoff); | ||
5020 | ASSERT(!isnullstartblock(got->br_startblock)); | ||
5021 | |||
5022 | if (got->br_startoff == del->br_startoff) | ||
5023 | state |= BMAP_LEFT_CONTIG; | ||
5024 | if (got_endoff == del_endoff) | ||
5025 | state |= BMAP_RIGHT_CONTIG; | ||
5026 | |||
5027 | switch (state & (BMAP_LEFT_CONTIG | BMAP_RIGHT_CONTIG)) { | ||
5028 | case BMAP_LEFT_CONTIG | BMAP_RIGHT_CONTIG: | ||
5029 | /* | ||
5030 | * Matches the whole extent. Delete the entry. | ||
5031 | */ | ||
5032 | xfs_iext_remove(ip, *idx, 1, state); | ||
5033 | --*idx; | ||
5034 | break; | ||
5035 | case BMAP_LEFT_CONTIG: | ||
5036 | /* | ||
5037 | * Deleting the first part of the extent. | ||
5038 | */ | ||
5039 | trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_); | ||
5040 | got->br_startoff = del_endoff; | ||
5041 | got->br_blockcount -= del->br_blockcount; | ||
5042 | got->br_startblock = del->br_startblock + del->br_blockcount; | ||
5043 | xfs_bmbt_set_all(xfs_iext_get_ext(ifp, *idx), got); | ||
5044 | trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_); | ||
5045 | break; | ||
5046 | case BMAP_RIGHT_CONTIG: | ||
5047 | /* | ||
5048 | * Deleting the last part of the extent. | ||
5049 | */ | ||
5050 | trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_); | ||
5051 | got->br_blockcount -= del->br_blockcount; | ||
5052 | xfs_bmbt_set_all(xfs_iext_get_ext(ifp, *idx), got); | ||
5053 | trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_); | ||
5054 | break; | ||
5055 | case 0: | ||
5056 | /* | ||
5057 | * Deleting the middle of the extent. | ||
5058 | */ | ||
5059 | trace_xfs_bmap_pre_update(ip, *idx, state, _THIS_IP_); | ||
5060 | got->br_blockcount = del->br_startoff - got->br_startoff; | ||
5061 | xfs_bmbt_set_all(xfs_iext_get_ext(ifp, *idx), got); | ||
5062 | trace_xfs_bmap_post_update(ip, *idx, state, _THIS_IP_); | ||
5063 | |||
5064 | new.br_startoff = del_endoff; | ||
5065 | new.br_blockcount = got_endoff - del_endoff; | ||
5066 | new.br_state = got->br_state; | ||
5067 | new.br_startblock = del->br_startblock + del->br_blockcount; | ||
5068 | |||
5069 | ++*idx; | ||
5070 | xfs_iext_insert(ip, *idx, 1, &new, state); | ||
5071 | break; | ||
5072 | } | ||
5073 | } | ||
5074 | |||
4832 | /* | 5075 | /* |
4833 | * Called by xfs_bmapi to update file extent records and the btree | 5076 | * Called by xfs_bmapi to update file extent records and the btree |
4834 | * after removing space (or undoing a delayed allocation). | 5077 | * after removing space (or undoing a delayed allocation). |
@@ -5171,175 +5414,6 @@ done: | |||
5171 | return error; | 5414 | return error; |
5172 | } | 5415 | } |
5173 | 5416 | ||
5174 | /* Remove an extent from the CoW fork. Similar to xfs_bmap_del_extent. */ | ||
5175 | int | ||
5176 | xfs_bunmapi_cow( | ||
5177 | struct xfs_inode *ip, | ||
5178 | struct xfs_bmbt_irec *del) | ||
5179 | { | ||
5180 | xfs_filblks_t da_new; | ||
5181 | xfs_filblks_t da_old; | ||
5182 | xfs_fsblock_t del_endblock = 0; | ||
5183 | xfs_fileoff_t del_endoff; | ||
5184 | int delay; | ||
5185 | struct xfs_bmbt_rec_host *ep; | ||
5186 | int error; | ||
5187 | struct xfs_bmbt_irec got; | ||
5188 | xfs_fileoff_t got_endoff; | ||
5189 | struct xfs_ifork *ifp; | ||
5190 | struct xfs_mount *mp; | ||
5191 | xfs_filblks_t nblks; | ||
5192 | struct xfs_bmbt_irec new; | ||
5193 | /* REFERENCED */ | ||
5194 | uint qfield; | ||
5195 | xfs_filblks_t temp; | ||
5196 | xfs_filblks_t temp2; | ||
5197 | int state = BMAP_COWFORK; | ||
5198 | int eof; | ||
5199 | xfs_extnum_t eidx; | ||
5200 | |||
5201 | mp = ip->i_mount; | ||
5202 | XFS_STATS_INC(mp, xs_del_exlist); | ||
5203 | |||
5204 | ep = xfs_bmap_search_extents(ip, del->br_startoff, XFS_COW_FORK, &eof, | ||
5205 | &eidx, &got, &new); | ||
5206 | |||
5207 | ifp = XFS_IFORK_PTR(ip, XFS_COW_FORK); ifp = ifp; | ||
5208 | ASSERT((eidx >= 0) && (eidx < ifp->if_bytes / | ||
5209 | (uint)sizeof(xfs_bmbt_rec_t))); | ||
5210 | ASSERT(del->br_blockcount > 0); | ||
5211 | ASSERT(got.br_startoff <= del->br_startoff); | ||
5212 | del_endoff = del->br_startoff + del->br_blockcount; | ||
5213 | got_endoff = got.br_startoff + got.br_blockcount; | ||
5214 | ASSERT(got_endoff >= del_endoff); | ||
5215 | delay = isnullstartblock(got.br_startblock); | ||
5216 | ASSERT(isnullstartblock(del->br_startblock) == delay); | ||
5217 | qfield = 0; | ||
5218 | error = 0; | ||
5219 | /* | ||
5220 | * If deleting a real allocation, must free up the disk space. | ||
5221 | */ | ||
5222 | if (!delay) { | ||
5223 | nblks = del->br_blockcount; | ||
5224 | qfield = XFS_TRANS_DQ_BCOUNT; | ||
5225 | /* | ||
5226 | * Set up del_endblock and cur for later. | ||
5227 | */ | ||
5228 | del_endblock = del->br_startblock + del->br_blockcount; | ||
5229 | da_old = da_new = 0; | ||
5230 | } else { | ||
5231 | da_old = startblockval(got.br_startblock); | ||
5232 | da_new = 0; | ||
5233 | nblks = 0; | ||
5234 | } | ||
5235 | qfield = qfield; | ||
5236 | nblks = nblks; | ||
5237 | |||
5238 | /* | ||
5239 | * Set flag value to use in switch statement. | ||
5240 | * Left-contig is 2, right-contig is 1. | ||
5241 | */ | ||
5242 | switch (((got.br_startoff == del->br_startoff) << 1) | | ||
5243 | (got_endoff == del_endoff)) { | ||
5244 | case 3: | ||
5245 | /* | ||
5246 | * Matches the whole extent. Delete the entry. | ||
5247 | */ | ||
5248 | xfs_iext_remove(ip, eidx, 1, BMAP_COWFORK); | ||
5249 | --eidx; | ||
5250 | break; | ||
5251 | |||
5252 | case 2: | ||
5253 | /* | ||
5254 | * Deleting the first part of the extent. | ||
5255 | */ | ||
5256 | trace_xfs_bmap_pre_update(ip, eidx, state, _THIS_IP_); | ||
5257 | xfs_bmbt_set_startoff(ep, del_endoff); | ||
5258 | temp = got.br_blockcount - del->br_blockcount; | ||
5259 | xfs_bmbt_set_blockcount(ep, temp); | ||
5260 | if (delay) { | ||
5261 | temp = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp), | ||
5262 | da_old); | ||
5263 | xfs_bmbt_set_startblock(ep, nullstartblock((int)temp)); | ||
5264 | trace_xfs_bmap_post_update(ip, eidx, state, _THIS_IP_); | ||
5265 | da_new = temp; | ||
5266 | break; | ||
5267 | } | ||
5268 | xfs_bmbt_set_startblock(ep, del_endblock); | ||
5269 | trace_xfs_bmap_post_update(ip, eidx, state, _THIS_IP_); | ||
5270 | break; | ||
5271 | |||
5272 | case 1: | ||
5273 | /* | ||
5274 | * Deleting the last part of the extent. | ||
5275 | */ | ||
5276 | temp = got.br_blockcount - del->br_blockcount; | ||
5277 | trace_xfs_bmap_pre_update(ip, eidx, state, _THIS_IP_); | ||
5278 | xfs_bmbt_set_blockcount(ep, temp); | ||
5279 | if (delay) { | ||
5280 | temp = XFS_FILBLKS_MIN(xfs_bmap_worst_indlen(ip, temp), | ||
5281 | da_old); | ||
5282 | xfs_bmbt_set_startblock(ep, nullstartblock((int)temp)); | ||
5283 | trace_xfs_bmap_post_update(ip, eidx, state, _THIS_IP_); | ||
5284 | da_new = temp; | ||
5285 | break; | ||
5286 | } | ||
5287 | trace_xfs_bmap_post_update(ip, eidx, state, _THIS_IP_); | ||
5288 | break; | ||
5289 | |||
5290 | case 0: | ||
5291 | /* | ||
5292 | * Deleting the middle of the extent. | ||
5293 | */ | ||
5294 | temp = del->br_startoff - got.br_startoff; | ||
5295 | trace_xfs_bmap_pre_update(ip, eidx, state, _THIS_IP_); | ||
5296 | xfs_bmbt_set_blockcount(ep, temp); | ||
5297 | new.br_startoff = del_endoff; | ||
5298 | temp2 = got_endoff - del_endoff; | ||
5299 | new.br_blockcount = temp2; | ||
5300 | new.br_state = got.br_state; | ||
5301 | if (!delay) { | ||
5302 | new.br_startblock = del_endblock; | ||
5303 | } else { | ||
5304 | temp = xfs_bmap_worst_indlen(ip, temp); | ||
5305 | xfs_bmbt_set_startblock(ep, nullstartblock((int)temp)); | ||
5306 | temp2 = xfs_bmap_worst_indlen(ip, temp2); | ||
5307 | new.br_startblock = nullstartblock((int)temp2); | ||
5308 | da_new = temp + temp2; | ||
5309 | while (da_new > da_old) { | ||
5310 | if (temp) { | ||
5311 | temp--; | ||
5312 | da_new--; | ||
5313 | xfs_bmbt_set_startblock(ep, | ||
5314 | nullstartblock((int)temp)); | ||
5315 | } | ||
5316 | if (da_new == da_old) | ||
5317 | break; | ||
5318 | if (temp2) { | ||
5319 | temp2--; | ||
5320 | da_new--; | ||
5321 | new.br_startblock = | ||
5322 | nullstartblock((int)temp2); | ||
5323 | } | ||
5324 | } | ||
5325 | } | ||
5326 | trace_xfs_bmap_post_update(ip, eidx, state, _THIS_IP_); | ||
5327 | xfs_iext_insert(ip, eidx + 1, 1, &new, state); | ||
5328 | ++eidx; | ||
5329 | break; | ||
5330 | } | ||
5331 | |||
5332 | /* | ||
5333 | * Account for change in delayed indirect blocks. | ||
5334 | * Nothing to do for disk quota accounting here. | ||
5335 | */ | ||
5336 | ASSERT(da_old >= da_new); | ||
5337 | if (da_old > da_new) | ||
5338 | xfs_mod_fdblocks(mp, (int64_t)(da_old - da_new), false); | ||
5339 | |||
5340 | return error; | ||
5341 | } | ||
5342 | |||
5343 | /* | 5417 | /* |
5344 | * Unmap (remove) blocks from a file. | 5418 | * Unmap (remove) blocks from a file. |
5345 | * If nexts is nonzero then the number of extents to remove is limited to | 5419 | * If nexts is nonzero then the number of extents to remove is limited to |