|
@@ -982,7 +982,32 @@ xfs_vm_writepage(
|
|
|
offset = i_size_read(inode);
|
|
|
end_index = offset >> PAGE_CACHE_SHIFT;
|
|
|
last_index = (offset - 1) >> PAGE_CACHE_SHIFT;
|
|
|
- if (page->index >= end_index) {
|
|
|
+
|
|
|
+ /*
|
|
|
+ * The page index is less than the end_index, adjust the end_offset
|
|
|
+ * to the highest offset that this page should represent.
|
|
|
+ * -----------------------------------------------------
|
|
|
+ * | file mapping | <EOF> |
|
|
|
+ * -----------------------------------------------------
|
|
|
+ * | Page ... | Page N-2 | Page N-1 | Page N | |
|
|
|
+ * ^--------------------------------^----------|--------
|
|
|
+ * | desired writeback range | see else |
|
|
|
+ * ---------------------------------^------------------|
|
|
|
+ */
|
|
|
+ if (page->index < end_index)
|
|
|
+ end_offset = (xfs_off_t)(page->index + 1) << PAGE_CACHE_SHIFT;
|
|
|
+ else {
|
|
|
+ /*
|
|
|
+ * Check whether the page to write out is beyond or straddles
|
|
|
+ * i_size or not.
|
|
|
+ * -------------------------------------------------------
|
|
|
+ * | file mapping | <EOF> |
|
|
|
+ * -------------------------------------------------------
|
|
|
+ * | Page ... | Page N-2 | Page N-1 | Page N | Beyond |
|
|
|
+ * ^--------------------------------^-----------|---------
|
|
|
+ * | | Straddles |
|
|
|
+ * ---------------------------------^-----------|--------|
|
|
|
+ */
|
|
|
unsigned offset_into_page = offset & (PAGE_CACHE_SIZE - 1);
|
|
|
|
|
|
/*
|
|
@@ -990,24 +1015,36 @@ xfs_vm_writepage(
|
|
|
* truncate operation that is in progress. We must redirty the
|
|
|
* page so that reclaim stops reclaiming it. Otherwise
|
|
|
* xfs_vm_releasepage() is called on it and gets confused.
|
|
|
+ *
|
|
|
+ * Note that the end_index is unsigned long, it would overflow
|
|
|
+ * if the given offset is greater than 16TB on 32-bit system
|
|
|
+ * and if we do check the page is fully outside i_size or not
|
|
|
+ * via "if (page->index >= end_index + 1)" as "end_index + 1"
|
|
|
+ * will be evaluated to 0. Hence this page will be redirtied
|
|
|
+ * and be written out repeatedly which would result in an
|
|
|
+ * infinite loop, the user program that perform this operation
|
|
|
+ * will hang. Instead, we can verify this situation by checking
|
|
|
+ * if the page to write is totally beyond the i_size or if it's
|
|
|
+ * offset is just equal to the EOF.
|
|
|
*/
|
|
|
- if (page->index >= end_index + 1 || offset_into_page == 0)
|
|
|
+ if (page->index > end_index ||
|
|
|
+ (page->index == end_index && offset_into_page == 0))
|
|
|
goto redirty;
|
|
|
|
|
|
/*
|
|
|
* The page straddles i_size. It must be zeroed out on each
|
|
|
* and every writepage invocation because it may be mmapped.
|
|
|
* "A file is mapped in multiples of the page size. For a file
|
|
|
- * that is not a multiple of the page size, the remaining
|
|
|
+ * that is not a multiple of the page size, the remaining
|
|
|
* memory is zeroed when mapped, and writes to that region are
|
|
|
* not written out to the file."
|
|
|
*/
|
|
|
zero_user_segment(page, offset_into_page, PAGE_CACHE_SIZE);
|
|
|
+
|
|
|
+ /* Adjust the end_offset to the end of file */
|
|
|
+ end_offset = offset;
|
|
|
}
|
|
|
|
|
|
- end_offset = min_t(unsigned long long,
|
|
|
- (xfs_off_t)(page->index + 1) << PAGE_CACHE_SHIFT,
|
|
|
- offset);
|
|
|
len = 1 << inode->i_blkbits;
|
|
|
|
|
|
bh = head = page_buffers(page);
|