diff options
Diffstat (limited to 'fs/xfs/xfs_file.c')
-rw-r--r-- | fs/xfs/xfs_file.c | 219 |
1 files changed, 219 insertions, 0 deletions
diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c index 92ba18f841f1..d78a746b6c7c 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,6 +960,224 @@ 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, |