@@ -624,8 +624,8 @@ unsigned find_lock_entries(struct address_space *mapping, pgoff_t *start,
unsigned find_get_entries(struct address_space *mapping, pgoff_t *start,
pgoff_t end, struct folio_batch *fbatch, pgoff_t *indices);
int truncate_inode_folio(struct address_space *mapping, struct folio *folio);
-bool truncate_inode_partial_folio(struct folio *folio, loff_t start,
- loff_t end);
+bool truncate_inode_partial_folio(struct folio *folio, loff_t lstart,
+ loff_t lend, pgoff_t *pstart, pgoff_t *pend);
long mapping_evict_folio(struct address_space *mapping, struct folio *folio);
unsigned long mapping_try_invalidate(struct address_space *mapping,
pgoff_t start, pgoff_t end, unsigned long *nr_failed);
@@ -1176,11 +1176,8 @@ static void shmem_undo_range(struct inode *inode, loff_t lstart, uoff_t lend,
if (folio) {
same_folio = lend < folio_next_pos(folio);
folio_mark_dirty(folio);
- if (!truncate_inode_partial_folio(folio, lstart, lend)) {
- start = folio_next_index(folio);
- if (same_folio)
- end = folio->index;
- }
+ truncate_inode_partial_folio(folio, lstart, lend, &start,
+ same_folio ? &end : NULL);
folio_unlock(folio);
folio_put(folio);
folio = NULL;
@@ -1190,8 +1187,7 @@ static void shmem_undo_range(struct inode *inode, loff_t lstart, uoff_t lend,
folio = shmem_get_partial_folio(inode, lend >> PAGE_SHIFT);
if (folio) {
folio_mark_dirty(folio);
- if (!truncate_inode_partial_folio(folio, lstart, lend))
- end = folio->index;
+ truncate_inode_partial_folio(folio, lstart, lend, NULL, &end);
folio_unlock(folio);
folio_put(folio);
}
@@ -1259,7 +1255,8 @@ static void shmem_undo_range(struct inode *inode, loff_t lstart, uoff_t lend,
if (!folio_test_large(folio)) {
truncate_inode_folio(mapping, folio);
- } else if (truncate_inode_partial_folio(folio, lstart, lend)) {
+ } else if (truncate_inode_partial_folio(folio,
+ lstart, lend, NULL, NULL)) {
/*
* If we split a page, reset the loop so
* that we pick up the new sub pages.
@@ -206,30 +206,43 @@ static int folio_split_or_unmap(struct folio *folio, struct page *split_at,
/*
* Handle partial folios. The folio may be entirely within the
* range if a split has raced with us. If not, we zero the part of the
- * folio that's within the [start, end] range, and then split the folio if
+ * folio that's within the [lstart, lend] range, and then split the folio if
* it's large. split_page_range() will discard pages which now lie beyond
* i_size, and we rely on the caller to discard pages which lie within a
* newly created hole.
*
+ * When @pstart and/or @pend are non-NULL they receive the indexes of the
+ * folio range fully covered by [lstart, lend] after any split (or none),
+ * aligned inwards to min_order, i.e. the range of folios wholly within
+ * [lstart, lend] and so safe to discard.
+ *
* Returns false if splitting failed so the caller can avoid
* discarding the entire folio which is stubbornly unsplit.
*/
-bool truncate_inode_partial_folio(struct folio *folio, loff_t start, loff_t end)
+bool truncate_inode_partial_folio(struct folio *folio, loff_t lstart,
+ loff_t lend, pgoff_t *pstart, pgoff_t *pend)
{
loff_t pos = folio_pos(folio);
size_t size = folio_size(folio);
unsigned int offset, length;
struct page *split_at, *split_at2;
+ unsigned long min_nrbytes;
unsigned int min_order;
- if (pos < start)
- offset = start - pos;
+ if (pos < lstart)
+ offset = lstart - pos;
else
offset = 0;
- if (pos + size <= (u64)end)
+ if (pos + size <= (u64)lend)
length = size - offset;
else
- length = end + 1 - pos - offset;
+ length = lend + 1 - pos - offset;
+
+ if (pstart)
+ *pstart = offset ? folio_next_index(folio) : folio->index;
+ if (pend)
+ *pend = (pos + size > (u64)lend) ? folio->index :
+ folio_next_index(folio);
folio_wait_writeback(folio);
if (length == size) {
@@ -247,10 +260,12 @@ bool truncate_inode_partial_folio(struct folio *folio, loff_t start, loff_t end)
if (folio_needs_release(folio))
folio_invalidate(folio, offset, length);
- if (!folio_test_large(folio))
- return true;
min_order = mapping_min_folio_order(folio->mapping);
+ min_nrbytes = mapping_min_folio_nrbytes(folio->mapping);
+ if (folio_order(folio) == min_order)
+ return true;
+
split_at = folio_page(folio, PAGE_ALIGN_DOWN(offset) / PAGE_SIZE);
if (!folio_split_or_unmap(folio, split_at, min_order)) {
/*
@@ -259,34 +274,57 @@ bool truncate_inode_partial_folio(struct folio *folio, loff_t start, loff_t end)
* for shmem truncate
*/
struct folio *folio2;
- pgoff_t end_idx;
+ pgoff_t end, aligned_end = round_down(pos + offset + length,
+ min_nrbytes) >> PAGE_SHIFT;
+ if (pstart)
+ *pstart = round_up(pos + offset,
+ min_nrbytes) >> PAGE_SHIFT;
+
+ end = aligned_end;
if (offset + length == size)
- goto no_split;
+ goto out;
/*
* After the first split at the start edge, the folio at the
* end edge may be freed and reused concurrently.
- * __filemap_get_folio() looks up the straddler at end_idx
+ * __filemap_get_folio() looks up the straddler at aligned_end
* and returns it locked and ref'd with the mapping
* validated.
*/
- end_idx = (pos + offset + length) >> PAGE_SHIFT;
- folio2 = __filemap_get_folio(folio->mapping, end_idx,
+ folio2 = __filemap_get_folio(folio->mapping, aligned_end,
FGP_LOCK | FGP_NOWAIT, 0);
- if (IS_ERR(folio2))
- goto no_split;
-
- /* make sure folio2 is large */
- if (!folio_test_large(folio2))
+ if (IS_ERR(folio2)) {
+ /*
+ * No sub-folio straddles the boundary when
+ * aligned_end is empty, so discarding up to it is
+ * safe. Otherwise the straddler is locked by
+ * someone else and we cannot obtain a reliable end
+ * position, so we fall back to folio->index, which
+ * is safe but leaves the sub-folios split off at
+ * the offset edge in the page cache.
+ */
+ if (PTR_ERR(folio2) != -ENOENT)
+ end = folio->index;
goto out;
+ }
- split_at2 = folio_page(folio2, (end_idx - folio2->index));
- folio_split_or_unmap(folio2, split_at2, min_order);
-out:
+ /* Already at the minimum order, nothing to split */
+ if (folio_order(folio2) == min_order)
+ goto out_put;
+
+ split_at2 = folio_page(folio2, (aligned_end - folio2->index));
+
+ /* Split failed, keep the straddler intact */
+ if (folio_split_or_unmap(folio2, split_at2, min_order))
+ end = folio2->index;
+
+out_put:
folio_unlock(folio2);
folio_put(folio2);
-no_split:
+out:
+ if (pend)
+ *pend = end;
return true;
}
if (folio_test_dirty(folio))
@@ -421,11 +459,8 @@ void truncate_inode_pages_range(struct address_space *mapping,
folio = __filemap_get_folio(mapping, lstart >> PAGE_SHIFT, FGP_LOCK, 0);
if (!IS_ERR(folio)) {
same_folio = lend < folio_next_pos(folio);
- if (!truncate_inode_partial_folio(folio, lstart, lend)) {
- start = folio_next_index(folio);
- if (same_folio)
- end = folio->index;
- }
+ truncate_inode_partial_folio(folio, lstart, lend, &start,
+ same_folio ? &end : NULL);
folio_unlock(folio);
folio_put(folio);
folio = NULL;
@@ -435,8 +470,8 @@ void truncate_inode_pages_range(struct address_space *mapping,
folio = __filemap_get_folio(mapping, lend >> PAGE_SHIFT,
FGP_LOCK, 0);
if (!IS_ERR(folio)) {
- if (!truncate_inode_partial_folio(folio, lstart, lend))
- end = folio->index;
+ truncate_inode_partial_folio(folio, lstart, lend,
+ NULL, &end);
folio_unlock(folio);
folio_put(folio);
}