diff options
Diffstat (limited to 'fs/xfs/xfs_bmap_util.c')
-rw-r--r-- | fs/xfs/xfs_bmap_util.c | 343 |
1 files changed, 130 insertions, 213 deletions
diff --git a/fs/xfs/xfs_bmap_util.c b/fs/xfs/xfs_bmap_util.c index 28c42fb0c12a..91bee2db3207 100644 --- a/fs/xfs/xfs_bmap_util.c +++ b/fs/xfs/xfs_bmap_util.c | |||
@@ -1087,99 +1087,120 @@ error1: /* Just cancel transaction */ | |||
1087 | return error; | 1087 | return error; |
1088 | } | 1088 | } |
1089 | 1089 | ||
1090 | /* | 1090 | static int |
1091 | * Zero file bytes between startoff and endoff inclusive. | 1091 | xfs_unmap_extent( |
1092 | * The iolock is held exclusive and no blocks are buffered. | 1092 | struct xfs_inode *ip, |
1093 | * | 1093 | xfs_fileoff_t startoffset_fsb, |
1094 | * This function is used by xfs_free_file_space() to zero | 1094 | xfs_filblks_t len_fsb, |
1095 | * partial blocks when the range to free is not block aligned. | 1095 | int *done) |
1096 | * When unreserving space with boundaries that are not block | ||
1097 | * aligned we round up the start and round down the end | ||
1098 | * boundaries and then use this function to zero the parts of | ||
1099 | * the blocks that got dropped during the rounding. | ||
1100 | */ | ||
1101 | STATIC int | ||
1102 | xfs_zero_remaining_bytes( | ||
1103 | xfs_inode_t *ip, | ||
1104 | xfs_off_t startoff, | ||
1105 | xfs_off_t endoff) | ||
1106 | { | 1096 | { |
1107 | xfs_bmbt_irec_t imap; | 1097 | struct xfs_mount *mp = ip->i_mount; |
1108 | xfs_fileoff_t offset_fsb; | 1098 | struct xfs_trans *tp; |
1109 | xfs_off_t lastoffset; | 1099 | struct xfs_bmap_free free_list; |
1110 | xfs_off_t offset; | 1100 | xfs_fsblock_t firstfsb; |
1111 | xfs_buf_t *bp; | 1101 | uint resblks = XFS_DIOSTRAT_SPACE_RES(mp, 0); |
1112 | xfs_mount_t *mp = ip->i_mount; | 1102 | int error; |
1113 | int nimap; | ||
1114 | int error = 0; | ||
1115 | 1103 | ||
1116 | /* | 1104 | error = xfs_trans_alloc(mp, &M_RES(mp)->tr_write, resblks, 0, 0, &tp); |
1117 | * Avoid doing I/O beyond eof - it's not necessary | 1105 | if (error) { |
1118 | * since nothing can read beyond eof. The space will | 1106 | ASSERT(error == -ENOSPC || XFS_FORCED_SHUTDOWN(mp)); |
1119 | * be zeroed when the file is extended anyway. | 1107 | return error; |
1120 | */ | 1108 | } |
1121 | if (startoff >= XFS_ISIZE(ip)) | ||
1122 | return 0; | ||
1123 | 1109 | ||
1124 | if (endoff > XFS_ISIZE(ip)) | 1110 | xfs_ilock(ip, XFS_ILOCK_EXCL); |
1125 | endoff = XFS_ISIZE(ip); | 1111 | error = xfs_trans_reserve_quota(tp, mp, ip->i_udquot, ip->i_gdquot, |
1112 | ip->i_pdquot, resblks, 0, XFS_QMOPT_RES_REGBLKS); | ||
1113 | if (error) | ||
1114 | goto out_trans_cancel; | ||
1126 | 1115 | ||
1127 | for (offset = startoff; offset <= endoff; offset = lastoffset + 1) { | 1116 | xfs_trans_ijoin(tp, ip, 0); |
1128 | uint lock_mode; | ||
1129 | 1117 | ||
1130 | offset_fsb = XFS_B_TO_FSBT(mp, offset); | 1118 | xfs_bmap_init(&free_list, &firstfsb); |
1131 | nimap = 1; | 1119 | error = xfs_bunmapi(tp, ip, startoffset_fsb, len_fsb, 0, 2, &firstfsb, |
1120 | &free_list, done); | ||
1121 | if (error) | ||
1122 | goto out_bmap_cancel; | ||
1132 | 1123 | ||
1133 | lock_mode = xfs_ilock_data_map_shared(ip); | 1124 | error = xfs_bmap_finish(&tp, &free_list, NULL); |
1134 | error = xfs_bmapi_read(ip, offset_fsb, 1, &imap, &nimap, 0); | 1125 | if (error) |
1135 | xfs_iunlock(ip, lock_mode); | 1126 | goto out_bmap_cancel; |
1136 | 1127 | ||
1137 | if (error || nimap < 1) | 1128 | error = xfs_trans_commit(tp); |
1138 | break; | 1129 | out_unlock: |
1139 | ASSERT(imap.br_blockcount >= 1); | 1130 | xfs_iunlock(ip, XFS_ILOCK_EXCL); |
1140 | ASSERT(imap.br_startoff == offset_fsb); | 1131 | return error; |
1141 | ASSERT(imap.br_startblock != DELAYSTARTBLOCK); | ||
1142 | 1132 | ||
1143 | if (imap.br_startblock == HOLESTARTBLOCK || | 1133 | out_bmap_cancel: |
1144 | imap.br_state == XFS_EXT_UNWRITTEN) { | 1134 | xfs_bmap_cancel(&free_list); |
1145 | /* skip the entire extent */ | 1135 | out_trans_cancel: |
1146 | lastoffset = XFS_FSB_TO_B(mp, imap.br_startoff + | 1136 | xfs_trans_cancel(tp); |
1147 | imap.br_blockcount) - 1; | 1137 | goto out_unlock; |
1148 | continue; | 1138 | } |
1149 | } | ||
1150 | 1139 | ||
1151 | lastoffset = XFS_FSB_TO_B(mp, imap.br_startoff + 1) - 1; | 1140 | static int |
1152 | if (lastoffset > endoff) | 1141 | xfs_adjust_extent_unmap_boundaries( |
1153 | lastoffset = endoff; | 1142 | struct xfs_inode *ip, |
1143 | xfs_fileoff_t *startoffset_fsb, | ||
1144 | xfs_fileoff_t *endoffset_fsb) | ||
1145 | { | ||
1146 | struct xfs_mount *mp = ip->i_mount; | ||
1147 | struct xfs_bmbt_irec imap; | ||
1148 | int nimap, error; | ||
1149 | xfs_extlen_t mod = 0; | ||
1154 | 1150 | ||
1155 | /* DAX can just zero the backing device directly */ | 1151 | nimap = 1; |
1156 | if (IS_DAX(VFS_I(ip))) { | 1152 | error = xfs_bmapi_read(ip, *startoffset_fsb, 1, &imap, &nimap, 0); |
1157 | error = dax_zero_page_range(VFS_I(ip), offset, | 1153 | if (error) |
1158 | lastoffset - offset + 1, | 1154 | return error; |
1159 | xfs_get_blocks_direct); | ||
1160 | if (error) | ||
1161 | return error; | ||
1162 | continue; | ||
1163 | } | ||
1164 | 1155 | ||
1165 | error = xfs_buf_read_uncached(XFS_IS_REALTIME_INODE(ip) ? | 1156 | if (nimap && imap.br_startblock != HOLESTARTBLOCK) { |
1166 | mp->m_rtdev_targp : mp->m_ddev_targp, | 1157 | xfs_daddr_t block; |
1167 | xfs_fsb_to_db(ip, imap.br_startblock), | ||
1168 | BTOBB(mp->m_sb.sb_blocksize), | ||
1169 | 0, &bp, NULL); | ||
1170 | if (error) | ||
1171 | return error; | ||
1172 | 1158 | ||
1173 | memset(bp->b_addr + | 1159 | ASSERT(imap.br_startblock != DELAYSTARTBLOCK); |
1174 | (offset - XFS_FSB_TO_B(mp, imap.br_startoff)), | 1160 | block = imap.br_startblock; |
1175 | 0, lastoffset - offset + 1); | 1161 | mod = do_div(block, mp->m_sb.sb_rextsize); |
1162 | if (mod) | ||
1163 | *startoffset_fsb += mp->m_sb.sb_rextsize - mod; | ||
1164 | } | ||
1176 | 1165 | ||
1177 | error = xfs_bwrite(bp); | 1166 | nimap = 1; |
1178 | xfs_buf_relse(bp); | 1167 | error = xfs_bmapi_read(ip, *endoffset_fsb - 1, 1, &imap, &nimap, 0); |
1179 | if (error) | 1168 | if (error) |
1180 | return error; | 1169 | return error; |
1170 | |||
1171 | if (nimap && imap.br_startblock != HOLESTARTBLOCK) { | ||
1172 | ASSERT(imap.br_startblock != DELAYSTARTBLOCK); | ||
1173 | mod++; | ||
1174 | if (mod && mod != mp->m_sb.sb_rextsize) | ||
1175 | *endoffset_fsb -= mod; | ||
1181 | } | 1176 | } |
1182 | return error; | 1177 | |
1178 | return 0; | ||
1179 | } | ||
1180 | |||
1181 | static int | ||
1182 | xfs_flush_unmap_range( | ||
1183 | struct xfs_inode *ip, | ||
1184 | xfs_off_t offset, | ||
1185 | xfs_off_t len) | ||
1186 | { | ||
1187 | struct xfs_mount *mp = ip->i_mount; | ||
1188 | struct inode *inode = VFS_I(ip); | ||
1189 | xfs_off_t rounding, start, end; | ||
1190 | int error; | ||
1191 | |||
1192 | /* wait for the completion of any pending DIOs */ | ||
1193 | inode_dio_wait(inode); | ||
1194 | |||
1195 | rounding = max_t(xfs_off_t, 1 << mp->m_sb.sb_blocklog, PAGE_SIZE); | ||
1196 | start = round_down(offset, rounding); | ||
1197 | end = round_up(offset + len, rounding) - 1; | ||
1198 | |||
1199 | error = filemap_write_and_wait_range(inode->i_mapping, start, end); | ||
1200 | if (error) | ||
1201 | return error; | ||
1202 | truncate_pagecache_range(inode, start, end); | ||
1203 | return 0; | ||
1183 | } | 1204 | } |
1184 | 1205 | ||
1185 | int | 1206 | int |
@@ -1188,24 +1209,10 @@ xfs_free_file_space( | |||
1188 | xfs_off_t offset, | 1209 | xfs_off_t offset, |
1189 | xfs_off_t len) | 1210 | xfs_off_t len) |
1190 | { | 1211 | { |
1191 | int done; | 1212 | struct xfs_mount *mp = ip->i_mount; |
1192 | xfs_fileoff_t endoffset_fsb; | ||
1193 | int error; | ||
1194 | xfs_fsblock_t firstfsb; | ||
1195 | xfs_bmap_free_t free_list; | ||
1196 | xfs_bmbt_irec_t imap; | ||
1197 | xfs_off_t ioffset; | ||
1198 | xfs_off_t iendoffset; | ||
1199 | xfs_extlen_t mod=0; | ||
1200 | xfs_mount_t *mp; | ||
1201 | int nimap; | ||
1202 | uint resblks; | ||
1203 | xfs_off_t rounding; | ||
1204 | int rt; | ||
1205 | xfs_fileoff_t startoffset_fsb; | 1213 | xfs_fileoff_t startoffset_fsb; |
1206 | xfs_trans_t *tp; | 1214 | xfs_fileoff_t endoffset_fsb; |
1207 | 1215 | int done = 0, error; | |
1208 | mp = ip->i_mount; | ||
1209 | 1216 | ||
1210 | trace_xfs_free_file_space(ip); | 1217 | trace_xfs_free_file_space(ip); |
1211 | 1218 | ||
@@ -1213,135 +1220,45 @@ xfs_free_file_space( | |||
1213 | if (error) | 1220 | if (error) |
1214 | return error; | 1221 | return error; |
1215 | 1222 | ||
1216 | error = 0; | ||
1217 | if (len <= 0) /* if nothing being freed */ | 1223 | if (len <= 0) /* if nothing being freed */ |
1218 | return error; | 1224 | return 0; |
1219 | rt = XFS_IS_REALTIME_INODE(ip); | ||
1220 | startoffset_fsb = XFS_B_TO_FSB(mp, offset); | ||
1221 | endoffset_fsb = XFS_B_TO_FSBT(mp, offset + len); | ||
1222 | |||
1223 | /* wait for the completion of any pending DIOs */ | ||
1224 | inode_dio_wait(VFS_I(ip)); | ||
1225 | 1225 | ||
1226 | rounding = max_t(xfs_off_t, 1 << mp->m_sb.sb_blocklog, PAGE_SIZE); | 1226 | error = xfs_flush_unmap_range(ip, offset, len); |
1227 | ioffset = round_down(offset, rounding); | ||
1228 | iendoffset = round_up(offset + len, rounding) - 1; | ||
1229 | error = filemap_write_and_wait_range(VFS_I(ip)->i_mapping, ioffset, | ||
1230 | iendoffset); | ||
1231 | if (error) | 1227 | if (error) |
1232 | goto out; | 1228 | return error; |
1233 | truncate_pagecache_range(VFS_I(ip), ioffset, iendoffset); | 1229 | |
1230 | startoffset_fsb = XFS_B_TO_FSB(mp, offset); | ||
1231 | endoffset_fsb = XFS_B_TO_FSBT(mp, offset + len); | ||
1234 | 1232 | ||
1235 | /* | 1233 | /* |
1236 | * Need to zero the stuff we're not freeing, on disk. | 1234 | * Need to zero the stuff we're not freeing, on disk. If it's a RT file |
1237 | * If it's a realtime file & can't use unwritten extents then we | 1235 | * and we can't use unwritten extents then we actually need to ensure |
1238 | * actually need to zero the extent edges. Otherwise xfs_bunmapi | 1236 | * to zero the whole extent, otherwise we just need to take of block |
1239 | * will take care of it for us. | 1237 | * boundaries, and xfs_bunmapi will handle the rest. |
1240 | */ | 1238 | */ |
1241 | if (rt && !xfs_sb_version_hasextflgbit(&mp->m_sb)) { | 1239 | if (XFS_IS_REALTIME_INODE(ip) && |
1242 | nimap = 1; | 1240 | !xfs_sb_version_hasextflgbit(&mp->m_sb)) { |
1243 | error = xfs_bmapi_read(ip, startoffset_fsb, 1, | 1241 | error = xfs_adjust_extent_unmap_boundaries(ip, &startoffset_fsb, |
1244 | &imap, &nimap, 0); | 1242 | &endoffset_fsb); |
1245 | if (error) | 1243 | if (error) |
1246 | goto out; | 1244 | return error; |
1247 | ASSERT(nimap == 0 || nimap == 1); | ||
1248 | if (nimap && imap.br_startblock != HOLESTARTBLOCK) { | ||
1249 | xfs_daddr_t block; | ||
1250 | |||
1251 | ASSERT(imap.br_startblock != DELAYSTARTBLOCK); | ||
1252 | block = imap.br_startblock; | ||
1253 | mod = do_div(block, mp->m_sb.sb_rextsize); | ||
1254 | if (mod) | ||
1255 | startoffset_fsb += mp->m_sb.sb_rextsize - mod; | ||
1256 | } | ||
1257 | nimap = 1; | ||
1258 | error = xfs_bmapi_read(ip, endoffset_fsb - 1, 1, | ||
1259 | &imap, &nimap, 0); | ||
1260 | if (error) | ||
1261 | goto out; | ||
1262 | ASSERT(nimap == 0 || nimap == 1); | ||
1263 | if (nimap && imap.br_startblock != HOLESTARTBLOCK) { | ||
1264 | ASSERT(imap.br_startblock != DELAYSTARTBLOCK); | ||
1265 | mod++; | ||
1266 | if (mod && (mod != mp->m_sb.sb_rextsize)) | ||
1267 | endoffset_fsb -= mod; | ||
1268 | } | ||
1269 | } | ||
1270 | if ((done = (endoffset_fsb <= startoffset_fsb))) | ||
1271 | /* | ||
1272 | * One contiguous piece to clear | ||
1273 | */ | ||
1274 | error = xfs_zero_remaining_bytes(ip, offset, offset + len - 1); | ||
1275 | else { | ||
1276 | /* | ||
1277 | * Some full blocks, possibly two pieces to clear | ||
1278 | */ | ||
1279 | if (offset < XFS_FSB_TO_B(mp, startoffset_fsb)) | ||
1280 | error = xfs_zero_remaining_bytes(ip, offset, | ||
1281 | XFS_FSB_TO_B(mp, startoffset_fsb) - 1); | ||
1282 | if (!error && | ||
1283 | XFS_FSB_TO_B(mp, endoffset_fsb) < offset + len) | ||
1284 | error = xfs_zero_remaining_bytes(ip, | ||
1285 | XFS_FSB_TO_B(mp, endoffset_fsb), | ||
1286 | offset + len - 1); | ||
1287 | } | 1245 | } |
1288 | 1246 | ||
1289 | /* | 1247 | if (endoffset_fsb > startoffset_fsb) { |
1290 | * free file space until done or until there is an error | 1248 | while (!done) { |
1291 | */ | 1249 | error = xfs_unmap_extent(ip, startoffset_fsb, |
1292 | resblks = XFS_DIOSTRAT_SPACE_RES(mp, 0); | 1250 | endoffset_fsb - startoffset_fsb, &done); |
1293 | while (!error && !done) { | 1251 | if (error) |
1294 | 1252 | return error; | |
1295 | /* | ||
1296 | * allocate and setup the transaction. Allow this | ||
1297 | * transaction to dip into the reserve blocks to ensure | ||
1298 | * the freeing of the space succeeds at ENOSPC. | ||
1299 | */ | ||
1300 | error = xfs_trans_alloc(mp, &M_RES(mp)->tr_write, resblks, 0, 0, | ||
1301 | &tp); | ||
1302 | if (error) { | ||
1303 | ASSERT(error == -ENOSPC || XFS_FORCED_SHUTDOWN(mp)); | ||
1304 | break; | ||
1305 | } | 1253 | } |
1306 | xfs_ilock(ip, XFS_ILOCK_EXCL); | ||
1307 | error = xfs_trans_reserve_quota(tp, mp, | ||
1308 | ip->i_udquot, ip->i_gdquot, ip->i_pdquot, | ||
1309 | resblks, 0, XFS_QMOPT_RES_REGBLKS); | ||
1310 | if (error) | ||
1311 | goto error1; | ||
1312 | |||
1313 | xfs_trans_ijoin(tp, ip, 0); | ||
1314 | |||
1315 | /* | ||
1316 | * issue the bunmapi() call to free the blocks | ||
1317 | */ | ||
1318 | xfs_bmap_init(&free_list, &firstfsb); | ||
1319 | error = xfs_bunmapi(tp, ip, startoffset_fsb, | ||
1320 | endoffset_fsb - startoffset_fsb, | ||
1321 | 0, 2, &firstfsb, &free_list, &done); | ||
1322 | if (error) | ||
1323 | goto error0; | ||
1324 | |||
1325 | /* | ||
1326 | * complete the transaction | ||
1327 | */ | ||
1328 | error = xfs_bmap_finish(&tp, &free_list, NULL); | ||
1329 | if (error) | ||
1330 | goto error0; | ||
1331 | |||
1332 | error = xfs_trans_commit(tp); | ||
1333 | xfs_iunlock(ip, XFS_ILOCK_EXCL); | ||
1334 | } | 1254 | } |
1335 | 1255 | ||
1336 | out: | 1256 | /* |
1337 | return error; | 1257 | * Now that we've unmap all full blocks we'll have to zero out any |
1338 | 1258 | * partial block at the beginning and/or end. xfs_zero_range is | |
1339 | error0: | 1259 | * smart enough to skip any holes, including those we just created. |
1340 | xfs_bmap_cancel(&free_list); | 1260 | */ |
1341 | error1: | 1261 | return xfs_zero_range(ip, offset, len, NULL); |
1342 | xfs_trans_cancel(tp); | ||
1343 | xfs_iunlock(ip, XFS_ILOCK_EXCL); | ||
1344 | goto out; | ||
1345 | } | 1262 | } |
1346 | 1263 | ||
1347 | /* | 1264 | /* |