diff options
Diffstat (limited to 'fs/xfs/xfs_file.c')
-rw-r--r-- | fs/xfs/xfs_file.c | 379 |
1 files changed, 344 insertions, 35 deletions
diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c index 56afcdb2377d..1eaeb8be3aae 100644 --- a/fs/xfs/xfs_file.c +++ b/fs/xfs/xfs_file.c | |||
@@ -36,6 +36,7 @@ | |||
36 | 36 | ||
37 | #include <linux/dcache.h> | 37 | #include <linux/dcache.h> |
38 | #include <linux/falloc.h> | 38 | #include <linux/falloc.h> |
39 | #include <linux/pagevec.h> | ||
39 | 40 | ||
40 | static const struct vm_operations_struct xfs_file_vm_ops; | 41 | static const struct vm_operations_struct xfs_file_vm_ops; |
41 | 42 | ||
@@ -959,17 +960,232 @@ xfs_vm_page_mkwrite( | |||
959 | return block_page_mkwrite(vma, vmf, xfs_get_blocks); | 960 | return block_page_mkwrite(vma, vmf, xfs_get_blocks); |
960 | } | 961 | } |
961 | 962 | ||
963 | /* | ||
964 | * This type is designed to indicate the type of offset we would like | ||
965 | * to search from page cache for either xfs_seek_data() or xfs_seek_hole(). | ||
966 | */ | ||
967 | enum { | ||
968 | HOLE_OFF = 0, | ||
969 | DATA_OFF, | ||
970 | }; | ||
971 | |||
972 | /* | ||
973 | * Lookup the desired type of offset from the given page. | ||
974 | * | ||
975 | * On success, return true and the offset argument will point to the | ||
976 | * start of the region that was found. Otherwise this function will | ||
977 | * return false and keep the offset argument unchanged. | ||
978 | */ | ||
979 | STATIC bool | ||
980 | xfs_lookup_buffer_offset( | ||
981 | struct page *page, | ||
982 | loff_t *offset, | ||
983 | unsigned int type) | ||
984 | { | ||
985 | loff_t lastoff = page_offset(page); | ||
986 | bool found = false; | ||
987 | struct buffer_head *bh, *head; | ||
988 | |||
989 | bh = head = page_buffers(page); | ||
990 | do { | ||
991 | /* | ||
992 | * Unwritten extents that have data in the page | ||
993 | * cache covering them can be identified by the | ||
994 | * BH_Unwritten state flag. Pages with multiple | ||
995 | * buffers might have a mix of holes, data and | ||
996 | * unwritten extents - any buffer with valid | ||
997 | * data in it should have BH_Uptodate flag set | ||
998 | * on it. | ||
999 | */ | ||
1000 | if (buffer_unwritten(bh) || | ||
1001 | buffer_uptodate(bh)) { | ||
1002 | if (type == DATA_OFF) | ||
1003 | found = true; | ||
1004 | } else { | ||
1005 | if (type == HOLE_OFF) | ||
1006 | found = true; | ||
1007 | } | ||
1008 | |||
1009 | if (found) { | ||
1010 | *offset = lastoff; | ||
1011 | break; | ||
1012 | } | ||
1013 | lastoff += bh->b_size; | ||
1014 | } while ((bh = bh->b_this_page) != head); | ||
1015 | |||
1016 | return found; | ||
1017 | } | ||
1018 | |||
1019 | /* | ||
1020 | * This routine is called to find out and return a data or hole offset | ||
1021 | * from the page cache for unwritten extents according to the desired | ||
1022 | * type for xfs_seek_data() or xfs_seek_hole(). | ||
1023 | * | ||
1024 | * The argument offset is used to tell where we start to search from the | ||
1025 | * page cache. Map is used to figure out the end points of the range to | ||
1026 | * lookup pages. | ||
1027 | * | ||
1028 | * Return true if the desired type of offset was found, and the argument | ||
1029 | * offset is filled with that address. Otherwise, return false and keep | ||
1030 | * offset unchanged. | ||
1031 | */ | ||
1032 | STATIC bool | ||
1033 | xfs_find_get_desired_pgoff( | ||
1034 | struct inode *inode, | ||
1035 | struct xfs_bmbt_irec *map, | ||
1036 | unsigned int type, | ||
1037 | loff_t *offset) | ||
1038 | { | ||
1039 | struct xfs_inode *ip = XFS_I(inode); | ||
1040 | struct xfs_mount *mp = ip->i_mount; | ||
1041 | struct pagevec pvec; | ||
1042 | pgoff_t index; | ||
1043 | pgoff_t end; | ||
1044 | loff_t endoff; | ||
1045 | loff_t startoff = *offset; | ||
1046 | loff_t lastoff = startoff; | ||
1047 | bool found = false; | ||
1048 | |||
1049 | pagevec_init(&pvec, 0); | ||
1050 | |||
1051 | index = startoff >> PAGE_CACHE_SHIFT; | ||
1052 | endoff = XFS_FSB_TO_B(mp, map->br_startoff + map->br_blockcount); | ||
1053 | end = endoff >> PAGE_CACHE_SHIFT; | ||
1054 | do { | ||
1055 | int want; | ||
1056 | unsigned nr_pages; | ||
1057 | unsigned int i; | ||
1058 | |||
1059 | want = min_t(pgoff_t, end - index, PAGEVEC_SIZE); | ||
1060 | nr_pages = pagevec_lookup(&pvec, inode->i_mapping, index, | ||
1061 | want); | ||
1062 | /* | ||
1063 | * No page mapped into given range. If we are searching holes | ||
1064 | * and if this is the first time we got into the loop, it means | ||
1065 | * that the given offset is landed in a hole, return it. | ||
1066 | * | ||
1067 | * If we have already stepped through some block buffers to find | ||
1068 | * holes but they all contains data. In this case, the last | ||
1069 | * offset is already updated and pointed to the end of the last | ||
1070 | * mapped page, if it does not reach the endpoint to search, | ||
1071 | * that means there should be a hole between them. | ||
1072 | */ | ||
1073 | if (nr_pages == 0) { | ||
1074 | /* Data search found nothing */ | ||
1075 | if (type == DATA_OFF) | ||
1076 | break; | ||
1077 | |||
1078 | ASSERT(type == HOLE_OFF); | ||
1079 | if (lastoff == startoff || lastoff < endoff) { | ||
1080 | found = true; | ||
1081 | *offset = lastoff; | ||
1082 | } | ||
1083 | break; | ||
1084 | } | ||
1085 | |||
1086 | /* | ||
1087 | * At lease we found one page. If this is the first time we | ||
1088 | * step into the loop, and if the first page index offset is | ||
1089 | * greater than the given search offset, a hole was found. | ||
1090 | */ | ||
1091 | if (type == HOLE_OFF && lastoff == startoff && | ||
1092 | lastoff < page_offset(pvec.pages[0])) { | ||
1093 | found = true; | ||
1094 | break; | ||
1095 | } | ||
1096 | |||
1097 | for (i = 0; i < nr_pages; i++) { | ||
1098 | struct page *page = pvec.pages[i]; | ||
1099 | loff_t b_offset; | ||
1100 | |||
1101 | /* | ||
1102 | * At this point, the page may be truncated or | ||
1103 | * invalidated (changing page->mapping to NULL), | ||
1104 | * or even swizzled back from swapper_space to tmpfs | ||
1105 | * file mapping. However, page->index will not change | ||
1106 | * because we have a reference on the page. | ||
1107 | * | ||
1108 | * Searching done if the page index is out of range. | ||
1109 | * If the current offset is not reaches the end of | ||
1110 | * the specified search range, there should be a hole | ||
1111 | * between them. | ||
1112 | */ | ||
1113 | if (page->index > end) { | ||
1114 | if (type == HOLE_OFF && lastoff < endoff) { | ||
1115 | *offset = lastoff; | ||
1116 | found = true; | ||
1117 | } | ||
1118 | goto out; | ||
1119 | } | ||
1120 | |||
1121 | lock_page(page); | ||
1122 | /* | ||
1123 | * Page truncated or invalidated(page->mapping == NULL). | ||
1124 | * We can freely skip it and proceed to check the next | ||
1125 | * page. | ||
1126 | */ | ||
1127 | if (unlikely(page->mapping != inode->i_mapping)) { | ||
1128 | unlock_page(page); | ||
1129 | continue; | ||
1130 | } | ||
1131 | |||
1132 | if (!page_has_buffers(page)) { | ||
1133 | unlock_page(page); | ||
1134 | continue; | ||
1135 | } | ||
1136 | |||
1137 | found = xfs_lookup_buffer_offset(page, &b_offset, type); | ||
1138 | if (found) { | ||
1139 | /* | ||
1140 | * The found offset may be less than the start | ||
1141 | * point to search if this is the first time to | ||
1142 | * come here. | ||
1143 | */ | ||
1144 | *offset = max_t(loff_t, startoff, b_offset); | ||
1145 | unlock_page(page); | ||
1146 | goto out; | ||
1147 | } | ||
1148 | |||
1149 | /* | ||
1150 | * We either searching data but nothing was found, or | ||
1151 | * searching hole but found a data buffer. In either | ||
1152 | * case, probably the next page contains the desired | ||
1153 | * things, update the last offset to it so. | ||
1154 | */ | ||
1155 | lastoff = page_offset(page) + PAGE_SIZE; | ||
1156 | unlock_page(page); | ||
1157 | } | ||
1158 | |||
1159 | /* | ||
1160 | * The number of returned pages less than our desired, search | ||
1161 | * done. In this case, nothing was found for searching data, | ||
1162 | * but we found a hole behind the last offset. | ||
1163 | */ | ||
1164 | if (nr_pages < want) { | ||
1165 | if (type == HOLE_OFF) { | ||
1166 | *offset = lastoff; | ||
1167 | found = true; | ||
1168 | } | ||
1169 | break; | ||
1170 | } | ||
1171 | |||
1172 | index = pvec.pages[i - 1]->index + 1; | ||
1173 | pagevec_release(&pvec); | ||
1174 | } while (index <= end); | ||
1175 | |||
1176 | out: | ||
1177 | pagevec_release(&pvec); | ||
1178 | return found; | ||
1179 | } | ||
1180 | |||
962 | STATIC loff_t | 1181 | STATIC loff_t |
963 | xfs_seek_data( | 1182 | xfs_seek_data( |
964 | struct file *file, | 1183 | struct file *file, |
965 | loff_t start, | 1184 | loff_t start) |
966 | u32 type) | ||
967 | { | 1185 | { |
968 | struct inode *inode = file->f_mapping->host; | 1186 | struct inode *inode = file->f_mapping->host; |
969 | struct xfs_inode *ip = XFS_I(inode); | 1187 | struct xfs_inode *ip = XFS_I(inode); |
970 | struct xfs_mount *mp = ip->i_mount; | 1188 | struct xfs_mount *mp = ip->i_mount; |
971 | struct xfs_bmbt_irec map[2]; | ||
972 | int nmap = 2; | ||
973 | loff_t uninitialized_var(offset); | 1189 | loff_t uninitialized_var(offset); |
974 | xfs_fsize_t isize; | 1190 | xfs_fsize_t isize; |
975 | xfs_fileoff_t fsbno; | 1191 | xfs_fileoff_t fsbno; |
@@ -985,36 +1201,74 @@ xfs_seek_data( | |||
985 | goto out_unlock; | 1201 | goto out_unlock; |
986 | } | 1202 | } |
987 | 1203 | ||
988 | fsbno = XFS_B_TO_FSBT(mp, start); | ||
989 | |||
990 | /* | 1204 | /* |
991 | * Try to read extents from the first block indicated | 1205 | * Try to read extents from the first block indicated |
992 | * by fsbno to the end block of the file. | 1206 | * by fsbno to the end block of the file. |
993 | */ | 1207 | */ |
1208 | fsbno = XFS_B_TO_FSBT(mp, start); | ||
994 | end = XFS_B_TO_FSB(mp, isize); | 1209 | end = XFS_B_TO_FSB(mp, isize); |
1210 | for (;;) { | ||
1211 | struct xfs_bmbt_irec map[2]; | ||
1212 | int nmap = 2; | ||
1213 | unsigned int i; | ||
995 | 1214 | ||
996 | error = xfs_bmapi_read(ip, fsbno, end - fsbno, map, &nmap, | 1215 | error = xfs_bmapi_read(ip, fsbno, end - fsbno, map, &nmap, |
997 | XFS_BMAPI_ENTIRE); | 1216 | XFS_BMAPI_ENTIRE); |
998 | if (error) | 1217 | if (error) |
999 | goto out_unlock; | 1218 | goto out_unlock; |
1000 | 1219 | ||
1001 | /* | 1220 | /* No extents at given offset, must be beyond EOF */ |
1002 | * Treat unwritten extent as data extent since it might | 1221 | if (nmap == 0) { |
1003 | * contains dirty data in page cache. | 1222 | error = ENXIO; |
1004 | */ | 1223 | goto out_unlock; |
1005 | if (map[0].br_startblock != HOLESTARTBLOCK) { | 1224 | } |
1006 | offset = max_t(loff_t, start, | 1225 | |
1007 | XFS_FSB_TO_B(mp, map[0].br_startoff)); | 1226 | for (i = 0; i < nmap; i++) { |
1008 | } else { | 1227 | offset = max_t(loff_t, start, |
1228 | XFS_FSB_TO_B(mp, map[i].br_startoff)); | ||
1229 | |||
1230 | /* Landed in a data extent */ | ||
1231 | if (map[i].br_startblock == DELAYSTARTBLOCK || | ||
1232 | (map[i].br_state == XFS_EXT_NORM && | ||
1233 | !isnullstartblock(map[i].br_startblock))) | ||
1234 | goto out; | ||
1235 | |||
1236 | /* | ||
1237 | * Landed in an unwritten extent, try to search data | ||
1238 | * from page cache. | ||
1239 | */ | ||
1240 | if (map[i].br_state == XFS_EXT_UNWRITTEN) { | ||
1241 | if (xfs_find_get_desired_pgoff(inode, &map[i], | ||
1242 | DATA_OFF, &offset)) | ||
1243 | goto out; | ||
1244 | } | ||
1245 | } | ||
1246 | |||
1247 | /* | ||
1248 | * map[0] is hole or its an unwritten extent but | ||
1249 | * without data in page cache. Probably means that | ||
1250 | * we are reading after EOF if nothing in map[1]. | ||
1251 | */ | ||
1009 | if (nmap == 1) { | 1252 | if (nmap == 1) { |
1010 | error = ENXIO; | 1253 | error = ENXIO; |
1011 | goto out_unlock; | 1254 | goto out_unlock; |
1012 | } | 1255 | } |
1013 | 1256 | ||
1014 | offset = max_t(loff_t, start, | 1257 | ASSERT(i > 1); |
1015 | XFS_FSB_TO_B(mp, map[1].br_startoff)); | 1258 | |
1259 | /* | ||
1260 | * Nothing was found, proceed to the next round of search | ||
1261 | * if reading offset not beyond or hit EOF. | ||
1262 | */ | ||
1263 | fsbno = map[i - 1].br_startoff + map[i - 1].br_blockcount; | ||
1264 | start = XFS_FSB_TO_B(mp, fsbno); | ||
1265 | if (start >= isize) { | ||
1266 | error = ENXIO; | ||
1267 | goto out_unlock; | ||
1268 | } | ||
1016 | } | 1269 | } |
1017 | 1270 | ||
1271 | out: | ||
1018 | if (offset != file->f_pos) | 1272 | if (offset != file->f_pos) |
1019 | file->f_pos = offset; | 1273 | file->f_pos = offset; |
1020 | 1274 | ||
@@ -1029,16 +1283,15 @@ out_unlock: | |||
1029 | STATIC loff_t | 1283 | STATIC loff_t |
1030 | xfs_seek_hole( | 1284 | xfs_seek_hole( |
1031 | struct file *file, | 1285 | struct file *file, |
1032 | loff_t start, | 1286 | loff_t start) |
1033 | u32 type) | ||
1034 | { | 1287 | { |
1035 | struct inode *inode = file->f_mapping->host; | 1288 | struct inode *inode = file->f_mapping->host; |
1036 | struct xfs_inode *ip = XFS_I(inode); | 1289 | struct xfs_inode *ip = XFS_I(inode); |
1037 | struct xfs_mount *mp = ip->i_mount; | 1290 | struct xfs_mount *mp = ip->i_mount; |
1038 | loff_t uninitialized_var(offset); | 1291 | loff_t uninitialized_var(offset); |
1039 | loff_t holeoff; | ||
1040 | xfs_fsize_t isize; | 1292 | xfs_fsize_t isize; |
1041 | xfs_fileoff_t fsbno; | 1293 | xfs_fileoff_t fsbno; |
1294 | xfs_filblks_t end; | ||
1042 | uint lock; | 1295 | uint lock; |
1043 | int error; | 1296 | int error; |
1044 | 1297 | ||
@@ -1054,21 +1307,77 @@ xfs_seek_hole( | |||
1054 | } | 1307 | } |
1055 | 1308 | ||
1056 | fsbno = XFS_B_TO_FSBT(mp, start); | 1309 | fsbno = XFS_B_TO_FSBT(mp, start); |
1057 | error = xfs_bmap_first_unused(NULL, ip, 1, &fsbno, XFS_DATA_FORK); | 1310 | end = XFS_B_TO_FSB(mp, isize); |
1058 | if (error) | 1311 | |
1059 | goto out_unlock; | 1312 | for (;;) { |
1313 | struct xfs_bmbt_irec map[2]; | ||
1314 | int nmap = 2; | ||
1315 | unsigned int i; | ||
1316 | |||
1317 | error = xfs_bmapi_read(ip, fsbno, end - fsbno, map, &nmap, | ||
1318 | XFS_BMAPI_ENTIRE); | ||
1319 | if (error) | ||
1320 | goto out_unlock; | ||
1321 | |||
1322 | /* No extents at given offset, must be beyond EOF */ | ||
1323 | if (nmap == 0) { | ||
1324 | error = ENXIO; | ||
1325 | goto out_unlock; | ||
1326 | } | ||
1327 | |||
1328 | for (i = 0; i < nmap; i++) { | ||
1329 | offset = max_t(loff_t, start, | ||
1330 | XFS_FSB_TO_B(mp, map[i].br_startoff)); | ||
1331 | |||
1332 | /* Landed in a hole */ | ||
1333 | if (map[i].br_startblock == HOLESTARTBLOCK) | ||
1334 | goto out; | ||
1335 | |||
1336 | /* | ||
1337 | * Landed in an unwritten extent, try to search hole | ||
1338 | * from page cache. | ||
1339 | */ | ||
1340 | if (map[i].br_state == XFS_EXT_UNWRITTEN) { | ||
1341 | if (xfs_find_get_desired_pgoff(inode, &map[i], | ||
1342 | HOLE_OFF, &offset)) | ||
1343 | goto out; | ||
1344 | } | ||
1345 | } | ||
1346 | |||
1347 | /* | ||
1348 | * map[0] contains data or its unwritten but contains | ||
1349 | * data in page cache, probably means that we are | ||
1350 | * reading after EOF. We should fix offset to point | ||
1351 | * to the end of the file(i.e., there is an implicit | ||
1352 | * hole at the end of any file). | ||
1353 | */ | ||
1354 | if (nmap == 1) { | ||
1355 | offset = isize; | ||
1356 | break; | ||
1357 | } | ||
1358 | |||
1359 | ASSERT(i > 1); | ||
1060 | 1360 | ||
1061 | holeoff = XFS_FSB_TO_B(mp, fsbno); | ||
1062 | if (holeoff <= start) | ||
1063 | offset = start; | ||
1064 | else { | ||
1065 | /* | 1361 | /* |
1066 | * xfs_bmap_first_unused() could return a value bigger than | 1362 | * Both mappings contains data, proceed to the next round of |
1067 | * isize if there are no more holes past the supplied offset. | 1363 | * search if the current reading offset not beyond or hit EOF. |
1068 | */ | 1364 | */ |
1069 | offset = min_t(loff_t, holeoff, isize); | 1365 | fsbno = map[i - 1].br_startoff + map[i - 1].br_blockcount; |
1366 | start = XFS_FSB_TO_B(mp, fsbno); | ||
1367 | if (start >= isize) { | ||
1368 | offset = isize; | ||
1369 | break; | ||
1370 | } | ||
1070 | } | 1371 | } |
1071 | 1372 | ||
1373 | out: | ||
1374 | /* | ||
1375 | * At this point, we must have found a hole. However, the returned | ||
1376 | * offset may be bigger than the file size as it may be aligned to | ||
1377 | * page boundary for unwritten extents, we need to deal with this | ||
1378 | * situation in particular. | ||
1379 | */ | ||
1380 | offset = min_t(loff_t, offset, isize); | ||
1072 | if (offset != file->f_pos) | 1381 | if (offset != file->f_pos) |
1073 | file->f_pos = offset; | 1382 | file->f_pos = offset; |
1074 | 1383 | ||
@@ -1092,9 +1401,9 @@ xfs_file_llseek( | |||
1092 | case SEEK_SET: | 1401 | case SEEK_SET: |
1093 | return generic_file_llseek(file, offset, origin); | 1402 | return generic_file_llseek(file, offset, origin); |
1094 | case SEEK_DATA: | 1403 | case SEEK_DATA: |
1095 | return xfs_seek_data(file, offset, origin); | 1404 | return xfs_seek_data(file, offset); |
1096 | case SEEK_HOLE: | 1405 | case SEEK_HOLE: |
1097 | return xfs_seek_hole(file, offset, origin); | 1406 | return xfs_seek_hole(file, offset); |
1098 | default: | 1407 | default: |
1099 | return -EINVAL; | 1408 | return -EINVAL; |
1100 | } | 1409 | } |