2023-11-27 13:58:07 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
|
|
|
/* Miscellaneous routines.
|
|
|
|
*
|
|
|
|
* Copyright (C) 2023 Red Hat, Inc. All Rights Reserved.
|
|
|
|
* Written by David Howells (dhowells@redhat.com)
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/swap.h>
|
|
|
|
#include "internal.h"
|
|
|
|
|
2024-12-16 20:41:08 +00:00
|
|
|
/**
|
|
|
|
* netfs_alloc_folioq_buffer - Allocate buffer space into a folio queue
|
|
|
|
* @mapping: Address space to set on the folio (or NULL).
|
|
|
|
* @_buffer: Pointer to the folio queue to add to (may point to a NULL; updated).
|
|
|
|
* @_cur_size: Current size of the buffer (updated).
|
|
|
|
* @size: Target size of the buffer.
|
|
|
|
* @gfp: The allocation constraints.
|
|
|
|
*/
|
|
|
|
int netfs_alloc_folioq_buffer(struct address_space *mapping,
|
|
|
|
struct folio_queue **_buffer,
|
|
|
|
size_t *_cur_size, ssize_t size, gfp_t gfp)
|
|
|
|
{
|
|
|
|
struct folio_queue *tail = *_buffer, *p;
|
|
|
|
|
|
|
|
size = round_up(size, PAGE_SIZE);
|
|
|
|
if (*_cur_size >= size)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (tail)
|
|
|
|
while (tail->next)
|
|
|
|
tail = tail->next;
|
|
|
|
|
|
|
|
do {
|
|
|
|
struct folio *folio;
|
|
|
|
int order = 0, slot;
|
|
|
|
|
|
|
|
if (!tail || folioq_full(tail)) {
|
|
|
|
p = netfs_folioq_alloc(0, GFP_NOFS, netfs_trace_folioq_alloc_buffer);
|
|
|
|
if (!p)
|
|
|
|
return -ENOMEM;
|
|
|
|
if (tail) {
|
|
|
|
tail->next = p;
|
|
|
|
p->prev = tail;
|
|
|
|
} else {
|
|
|
|
*_buffer = p;
|
|
|
|
}
|
|
|
|
tail = p;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (size - *_cur_size > PAGE_SIZE)
|
|
|
|
order = umin(ilog2(size - *_cur_size) - PAGE_SHIFT,
|
|
|
|
MAX_PAGECACHE_ORDER);
|
|
|
|
|
|
|
|
folio = folio_alloc(gfp, order);
|
|
|
|
if (!folio && order > 0)
|
|
|
|
folio = folio_alloc(gfp, 0);
|
|
|
|
if (!folio)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
folio->mapping = mapping;
|
|
|
|
folio->index = *_cur_size / PAGE_SIZE;
|
|
|
|
trace_netfs_folio(folio, netfs_folio_trace_alloc_buffer);
|
|
|
|
slot = folioq_append_mark(tail, folio);
|
|
|
|
*_cur_size += folioq_folio_size(tail, slot);
|
|
|
|
} while (*_cur_size < size);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(netfs_alloc_folioq_buffer);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* netfs_free_folioq_buffer - Free a folio queue.
|
|
|
|
* @fq: The start of the folio queue to free
|
|
|
|
*
|
|
|
|
* Free up a chain of folio_queues and, if marked, the marked folios they point
|
|
|
|
* to.
|
|
|
|
*/
|
|
|
|
void netfs_free_folioq_buffer(struct folio_queue *fq)
|
|
|
|
{
|
|
|
|
struct folio_queue *next;
|
|
|
|
struct folio_batch fbatch;
|
|
|
|
|
|
|
|
folio_batch_init(&fbatch);
|
|
|
|
|
|
|
|
for (; fq; fq = next) {
|
|
|
|
for (int slot = 0; slot < folioq_count(fq); slot++) {
|
|
|
|
struct folio *folio = folioq_folio(fq, slot);
|
|
|
|
|
|
|
|
if (!folio ||
|
|
|
|
!folioq_is_marked(fq, slot))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
trace_netfs_folio(folio, netfs_folio_trace_put);
|
|
|
|
if (folio_batch_add(&fbatch, folio))
|
|
|
|
folio_batch_release(&fbatch);
|
|
|
|
}
|
|
|
|
|
|
|
|
netfs_stat_d(&netfs_n_folioq);
|
|
|
|
next = fq->next;
|
|
|
|
kfree(fq);
|
|
|
|
}
|
|
|
|
|
|
|
|
folio_batch_release(&fbatch);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(netfs_free_folioq_buffer);
|
|
|
|
|
2024-07-08 14:49:45 +01:00
|
|
|
/*
|
|
|
|
* Reset the subrequest iterator to refer just to the region remaining to be
|
|
|
|
* read. The iterator may or may not have been advanced by socket ops or
|
|
|
|
* extraction ops to an extent that may or may not match the amount actually
|
|
|
|
* read.
|
|
|
|
*/
|
|
|
|
void netfs_reset_iter(struct netfs_io_subrequest *subreq)
|
|
|
|
{
|
|
|
|
struct iov_iter *io_iter = &subreq->io_iter;
|
|
|
|
size_t remain = subreq->len - subreq->transferred;
|
|
|
|
|
|
|
|
if (io_iter->count > remain)
|
|
|
|
iov_iter_advance(io_iter, io_iter->count - remain);
|
|
|
|
else if (io_iter->count < remain)
|
|
|
|
iov_iter_revert(io_iter, remain - io_iter->count);
|
|
|
|
iov_iter_truncate(&subreq->io_iter, remain);
|
|
|
|
}
|
|
|
|
|
2023-11-27 13:58:07 +00:00
|
|
|
/**
|
|
|
|
* netfs_dirty_folio - Mark folio dirty and pin a cache object for writeback
|
|
|
|
* @mapping: The mapping the folio belongs to.
|
|
|
|
* @folio: The folio being dirtied.
|
|
|
|
*
|
|
|
|
* Set the dirty flag on a folio and pin an in-use cache object in memory so
|
|
|
|
* that writeback can later write to it. This is intended to be called from
|
|
|
|
* the filesystem's ->dirty_folio() method.
|
|
|
|
*
|
|
|
|
* Return: true if the dirty flag was set on the folio, false otherwise.
|
|
|
|
*/
|
|
|
|
bool netfs_dirty_folio(struct address_space *mapping, struct folio *folio)
|
|
|
|
{
|
|
|
|
struct inode *inode = mapping->host;
|
|
|
|
struct netfs_inode *ictx = netfs_inode(inode);
|
|
|
|
struct fscache_cookie *cookie = netfs_i_cookie(ictx);
|
|
|
|
bool need_use = false;
|
|
|
|
|
2024-07-18 21:07:32 +01:00
|
|
|
_enter("");
|
2023-11-27 13:58:07 +00:00
|
|
|
|
|
|
|
if (!filemap_dirty_folio(mapping, folio))
|
|
|
|
return false;
|
|
|
|
if (!fscache_cookie_valid(cookie))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (!(inode->i_state & I_PINNING_NETFS_WB)) {
|
|
|
|
spin_lock(&inode->i_lock);
|
|
|
|
if (!(inode->i_state & I_PINNING_NETFS_WB)) {
|
|
|
|
inode->i_state |= I_PINNING_NETFS_WB;
|
|
|
|
need_use = true;
|
|
|
|
}
|
|
|
|
spin_unlock(&inode->i_lock);
|
|
|
|
|
|
|
|
if (need_use)
|
|
|
|
fscache_use_cookie(cookie, true);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(netfs_dirty_folio);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* netfs_unpin_writeback - Unpin writeback resources
|
|
|
|
* @inode: The inode on which the cookie resides
|
|
|
|
* @wbc: The writeback control
|
|
|
|
*
|
|
|
|
* Unpin the writeback resources pinned by netfs_dirty_folio(). This is
|
|
|
|
* intended to be called as/by the netfs's ->write_inode() method.
|
|
|
|
*/
|
|
|
|
int netfs_unpin_writeback(struct inode *inode, struct writeback_control *wbc)
|
|
|
|
{
|
|
|
|
struct fscache_cookie *cookie = netfs_i_cookie(netfs_inode(inode));
|
|
|
|
|
|
|
|
if (wbc->unpinned_netfs_wb)
|
|
|
|
fscache_unuse_cookie(cookie, NULL, NULL);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(netfs_unpin_writeback);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* netfs_clear_inode_writeback - Clear writeback resources pinned by an inode
|
|
|
|
* @inode: The inode to clean up
|
|
|
|
* @aux: Auxiliary data to apply to the inode
|
|
|
|
*
|
|
|
|
* Clear any writeback resources held by an inode when the inode is evicted.
|
|
|
|
* This must be called before clear_inode() is called.
|
|
|
|
*/
|
|
|
|
void netfs_clear_inode_writeback(struct inode *inode, const void *aux)
|
|
|
|
{
|
|
|
|
struct fscache_cookie *cookie = netfs_i_cookie(netfs_inode(inode));
|
|
|
|
|
|
|
|
if (inode->i_state & I_PINNING_NETFS_WB) {
|
|
|
|
loff_t i_size = i_size_read(inode);
|
|
|
|
fscache_unuse_cookie(cookie, aux, &i_size);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(netfs_clear_inode_writeback);
|
2021-08-20 17:08:30 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* netfs_invalidate_folio - Invalidate or partially invalidate a folio
|
|
|
|
* @folio: Folio proposed for release
|
|
|
|
* @offset: Offset of the invalidated region
|
|
|
|
* @length: Length of the invalidated region
|
|
|
|
*
|
|
|
|
* Invalidate part or all of a folio for a network filesystem. The folio will
|
|
|
|
* be removed afterwards if the invalidated region covers the entire folio.
|
|
|
|
*/
|
|
|
|
void netfs_invalidate_folio(struct folio *folio, size_t offset, size_t length)
|
|
|
|
{
|
netfs: Replace PG_fscache by setting folio->private and marking dirty
When dirty data is being written to the cache, setting/waiting on/clearing
the fscache flag is always done in tandem with setting/waiting on/clearing
the writeback flag. The netfslib buffered write routines wait on and set
both flags and the write request cleanup clears both flags, so the fscache
flag is almost superfluous.
The reason it isn't superfluous is because the fscache flag is also used to
indicate that data just read from the server is being written to the cache.
The flag is used to prevent a race involving overlapping direct-I/O writes
to the cache.
Change this to indicate that a page is in need of being copied to the cache
by placing a magic value in folio->private and marking the folios dirty.
Then when the writeback code sees a folio marked in this way, it only
writes it to the cache and not to the server.
If a folio that has this magic value set is modified, the value is just
replaced and the folio will then be uplodaded too.
With this, PG_fscache is no longer required by the netfslib core, 9p and
afs.
Ceph and nfs, however, still need to use the old PG_fscache-based tracking.
To deal with this, a flag, NETFS_ICTX_USE_PGPRIV2, now has to be set on the
flags in the netfs_inode struct for those filesystems. This reenables the
use of PG_fscache in that inode. 9p and afs use the netfslib write helpers
so get switched over; cifs, for the moment, does page-by-page manual access
to the cache, so doesn't use PG_fscache and is unaffected.
Signed-off-by: David Howells <dhowells@redhat.com>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
cc: Matthew Wilcox (Oracle) <willy@infradead.org>
cc: Eric Van Hensbergen <ericvh@kernel.org>
cc: Latchesar Ionkov <lucho@ionkov.net>
cc: Dominique Martinet <asmadeus@codewreck.org>
cc: Christian Schoenebeck <linux_oss@crudebyte.com>
cc: Marc Dionne <marc.dionne@auristor.com>
cc: Ilya Dryomov <idryomov@gmail.com>
cc: Xiubo Li <xiubli@redhat.com>
cc: Steve French <sfrench@samba.org>
cc: Paulo Alcantara <pc@manguebit.com>
cc: Ronnie Sahlberg <ronniesahlberg@gmail.com>
cc: Shyam Prasad N <sprasad@microsoft.com>
cc: Tom Talpey <tom@talpey.com>
cc: Bharath SM <bharathsm@microsoft.com>
cc: Trond Myklebust <trond.myklebust@hammerspace.com>
cc: Anna Schumaker <anna@kernel.org>
cc: netfs@lists.linux.dev
cc: v9fs@lists.linux.dev
cc: linux-afs@lists.infradead.org
cc: ceph-devel@vger.kernel.org
cc: linux-cifs@vger.kernel.org
cc: linux-nfs@vger.kernel.org
cc: linux-fsdevel@vger.kernel.org
cc: linux-mm@kvack.org
2024-03-19 10:00:09 +00:00
|
|
|
struct netfs_folio *finfo;
|
netfs: Fix trimming of streaming-write folios in netfs_inval_folio()
When netfslib writes to a folio that it doesn't have data for, but that
data exists on the server, it will make a 'streaming write' whereby it
stores data in a folio that is marked dirty, but not uptodate. When it
does this, it attaches a record to folio->private to track the dirty
region.
When truncate() or fallocate() wants to invalidate part of such a folio, it
will call into ->invalidate_folio(), specifying the part of the folio that
is to be invalidated. netfs_invalidate_folio(), on behalf of the
filesystem, must then determine how to trim the streaming write record. In
a couple of cases, however, it does this incorrectly (the reduce-length and
move-start cases are switched over and don't, in any case, calculate the
value correctly).
Fix this by making the logic tree more obvious and fixing the cases.
Fixes: 9ebff83e6481 ("netfs: Prep to use folio->private for write grouping and streaming write")
Signed-off-by: David Howells <dhowells@redhat.com>
Link: https://lore.kernel.org/r/20240823200819.532106-5-dhowells@redhat.com
cc: Matthew Wilcox (Oracle) <willy@infradead.org>
cc: Pankaj Raghav <p.raghav@samsung.com>
cc: Jeff Layton <jlayton@kernel.org>
cc: Marc Dionne <marc.dionne@auristor.com>
cc: linux-afs@lists.infradead.org
cc: netfs@lists.linux.dev
cc: linux-mm@kvack.org
cc: linux-fsdevel@vger.kernel.org
Signed-off-by: Christian Brauner <brauner@kernel.org>
2024-08-23 21:08:12 +01:00
|
|
|
struct netfs_inode *ctx = netfs_inode(folio_inode(folio));
|
2023-09-29 17:28:25 +01:00
|
|
|
size_t flen = folio_size(folio);
|
|
|
|
|
2024-07-18 21:07:32 +01:00
|
|
|
_enter("{%lx},%zx,%zx", folio->index, offset, length);
|
2021-08-20 17:08:30 +01:00
|
|
|
|
netfs: Fix trimming of streaming-write folios in netfs_inval_folio()
When netfslib writes to a folio that it doesn't have data for, but that
data exists on the server, it will make a 'streaming write' whereby it
stores data in a folio that is marked dirty, but not uptodate. When it
does this, it attaches a record to folio->private to track the dirty
region.
When truncate() or fallocate() wants to invalidate part of such a folio, it
will call into ->invalidate_folio(), specifying the part of the folio that
is to be invalidated. netfs_invalidate_folio(), on behalf of the
filesystem, must then determine how to trim the streaming write record. In
a couple of cases, however, it does this incorrectly (the reduce-length and
move-start cases are switched over and don't, in any case, calculate the
value correctly).
Fix this by making the logic tree more obvious and fixing the cases.
Fixes: 9ebff83e6481 ("netfs: Prep to use folio->private for write grouping and streaming write")
Signed-off-by: David Howells <dhowells@redhat.com>
Link: https://lore.kernel.org/r/20240823200819.532106-5-dhowells@redhat.com
cc: Matthew Wilcox (Oracle) <willy@infradead.org>
cc: Pankaj Raghav <p.raghav@samsung.com>
cc: Jeff Layton <jlayton@kernel.org>
cc: Marc Dionne <marc.dionne@auristor.com>
cc: linux-afs@lists.infradead.org
cc: netfs@lists.linux.dev
cc: linux-mm@kvack.org
cc: linux-fsdevel@vger.kernel.org
Signed-off-by: Christian Brauner <brauner@kernel.org>
2024-08-23 21:08:12 +01:00
|
|
|
if (offset == 0 && length == flen) {
|
|
|
|
unsigned long long i_size = i_size_read(&ctx->inode);
|
|
|
|
unsigned long long fpos = folio_pos(folio), end;
|
|
|
|
|
|
|
|
end = umin(fpos + flen, i_size);
|
|
|
|
if (fpos < i_size && end > ctx->zero_point)
|
|
|
|
ctx->zero_point = end;
|
|
|
|
}
|
|
|
|
|
2024-08-14 21:38:21 +01:00
|
|
|
folio_wait_private_2(folio); /* [DEPRECATED] */
|
|
|
|
|
2023-09-29 17:28:25 +01:00
|
|
|
if (!folio_test_private(folio))
|
|
|
|
return;
|
|
|
|
|
|
|
|
finfo = netfs_folio_info(folio);
|
|
|
|
|
|
|
|
if (offset == 0 && length >= flen)
|
|
|
|
goto erase_completely;
|
|
|
|
|
|
|
|
if (finfo) {
|
|
|
|
/* We have a partially uptodate page from a streaming write. */
|
|
|
|
unsigned int fstart = finfo->dirty_offset;
|
|
|
|
unsigned int fend = fstart + finfo->dirty_len;
|
netfs: Fix trimming of streaming-write folios in netfs_inval_folio()
When netfslib writes to a folio that it doesn't have data for, but that
data exists on the server, it will make a 'streaming write' whereby it
stores data in a folio that is marked dirty, but not uptodate. When it
does this, it attaches a record to folio->private to track the dirty
region.
When truncate() or fallocate() wants to invalidate part of such a folio, it
will call into ->invalidate_folio(), specifying the part of the folio that
is to be invalidated. netfs_invalidate_folio(), on behalf of the
filesystem, must then determine how to trim the streaming write record. In
a couple of cases, however, it does this incorrectly (the reduce-length and
move-start cases are switched over and don't, in any case, calculate the
value correctly).
Fix this by making the logic tree more obvious and fixing the cases.
Fixes: 9ebff83e6481 ("netfs: Prep to use folio->private for write grouping and streaming write")
Signed-off-by: David Howells <dhowells@redhat.com>
Link: https://lore.kernel.org/r/20240823200819.532106-5-dhowells@redhat.com
cc: Matthew Wilcox (Oracle) <willy@infradead.org>
cc: Pankaj Raghav <p.raghav@samsung.com>
cc: Jeff Layton <jlayton@kernel.org>
cc: Marc Dionne <marc.dionne@auristor.com>
cc: linux-afs@lists.infradead.org
cc: netfs@lists.linux.dev
cc: linux-mm@kvack.org
cc: linux-fsdevel@vger.kernel.org
Signed-off-by: Christian Brauner <brauner@kernel.org>
2024-08-23 21:08:12 +01:00
|
|
|
unsigned int iend = offset + length;
|
2023-09-29 17:28:25 +01:00
|
|
|
|
|
|
|
if (offset >= fend)
|
|
|
|
return;
|
netfs: Fix trimming of streaming-write folios in netfs_inval_folio()
When netfslib writes to a folio that it doesn't have data for, but that
data exists on the server, it will make a 'streaming write' whereby it
stores data in a folio that is marked dirty, but not uptodate. When it
does this, it attaches a record to folio->private to track the dirty
region.
When truncate() or fallocate() wants to invalidate part of such a folio, it
will call into ->invalidate_folio(), specifying the part of the folio that
is to be invalidated. netfs_invalidate_folio(), on behalf of the
filesystem, must then determine how to trim the streaming write record. In
a couple of cases, however, it does this incorrectly (the reduce-length and
move-start cases are switched over and don't, in any case, calculate the
value correctly).
Fix this by making the logic tree more obvious and fixing the cases.
Fixes: 9ebff83e6481 ("netfs: Prep to use folio->private for write grouping and streaming write")
Signed-off-by: David Howells <dhowells@redhat.com>
Link: https://lore.kernel.org/r/20240823200819.532106-5-dhowells@redhat.com
cc: Matthew Wilcox (Oracle) <willy@infradead.org>
cc: Pankaj Raghav <p.raghav@samsung.com>
cc: Jeff Layton <jlayton@kernel.org>
cc: Marc Dionne <marc.dionne@auristor.com>
cc: linux-afs@lists.infradead.org
cc: netfs@lists.linux.dev
cc: linux-mm@kvack.org
cc: linux-fsdevel@vger.kernel.org
Signed-off-by: Christian Brauner <brauner@kernel.org>
2024-08-23 21:08:12 +01:00
|
|
|
if (iend <= fstart)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* The invalidation region overlaps the data. If the region
|
|
|
|
* covers the start of the data, we either move along the start
|
|
|
|
* or just erase the data entirely.
|
|
|
|
*/
|
|
|
|
if (offset <= fstart) {
|
|
|
|
if (iend >= fend)
|
|
|
|
goto erase_completely;
|
|
|
|
/* Move the start of the data. */
|
|
|
|
finfo->dirty_len = fend - iend;
|
|
|
|
finfo->dirty_offset = offset;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Reduce the length of the data if the invalidation region
|
|
|
|
* covers the tail part.
|
|
|
|
*/
|
|
|
|
if (iend >= fend) {
|
|
|
|
finfo->dirty_len = offset - fstart;
|
2023-09-29 17:28:25 +01:00
|
|
|
return;
|
netfs: Fix trimming of streaming-write folios in netfs_inval_folio()
When netfslib writes to a folio that it doesn't have data for, but that
data exists on the server, it will make a 'streaming write' whereby it
stores data in a folio that is marked dirty, but not uptodate. When it
does this, it attaches a record to folio->private to track the dirty
region.
When truncate() or fallocate() wants to invalidate part of such a folio, it
will call into ->invalidate_folio(), specifying the part of the folio that
is to be invalidated. netfs_invalidate_folio(), on behalf of the
filesystem, must then determine how to trim the streaming write record. In
a couple of cases, however, it does this incorrectly (the reduce-length and
move-start cases are switched over and don't, in any case, calculate the
value correctly).
Fix this by making the logic tree more obvious and fixing the cases.
Fixes: 9ebff83e6481 ("netfs: Prep to use folio->private for write grouping and streaming write")
Signed-off-by: David Howells <dhowells@redhat.com>
Link: https://lore.kernel.org/r/20240823200819.532106-5-dhowells@redhat.com
cc: Matthew Wilcox (Oracle) <willy@infradead.org>
cc: Pankaj Raghav <p.raghav@samsung.com>
cc: Jeff Layton <jlayton@kernel.org>
cc: Marc Dionne <marc.dionne@auristor.com>
cc: linux-afs@lists.infradead.org
cc: netfs@lists.linux.dev
cc: linux-mm@kvack.org
cc: linux-fsdevel@vger.kernel.org
Signed-off-by: Christian Brauner <brauner@kernel.org>
2024-08-23 21:08:12 +01:00
|
|
|
}
|
|
|
|
|
2023-09-29 17:28:25 +01:00
|
|
|
/* A partial write was split. The caller has already zeroed
|
|
|
|
* it, so just absorb the hole.
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
|
|
|
|
erase_completely:
|
|
|
|
netfs_put_group(netfs_folio_group(folio));
|
|
|
|
folio_detach_private(folio);
|
|
|
|
folio_clear_uptodate(folio);
|
|
|
|
kfree(finfo);
|
|
|
|
return;
|
2021-08-20 17:08:30 +01:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(netfs_invalidate_folio);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* netfs_release_folio - Try to release a folio
|
|
|
|
* @folio: Folio proposed for release
|
|
|
|
* @gfp: Flags qualifying the release
|
|
|
|
*
|
|
|
|
* Request release of a folio and clean up its private state if it's not busy.
|
|
|
|
* Returns true if the folio can now be released, false if not
|
|
|
|
*/
|
|
|
|
bool netfs_release_folio(struct folio *folio, gfp_t gfp)
|
|
|
|
{
|
|
|
|
struct netfs_inode *ctx = netfs_inode(folio_inode(folio));
|
netfs: Optimise away reads above the point at which there can be no data
Track the file position above which the server is not expected to have any
data (the "zero point") and preemptively assume that we can satisfy
requests by filling them with zeroes locally rather than attempting to
download them if they're over that line - even if we've written data back
to the server. Assume that any data that was written back above that
position is held in the local cache. Note that we have to split requests
that straddle the line.
Make use of this to optimise away some reads from the server. We need to
set the zero point in the following circumstances:
(1) When we see an extant remote inode and have no cache for it, we set
the zero_point to i_size.
(2) On local inode creation, we set zero_point to 0.
(3) On local truncation down, we reduce zero_point to the new i_size if
the new i_size is lower.
(4) On local truncation up, we don't change zero_point.
(5) On local modification, we don't change zero_point.
(6) On remote invalidation, we set zero_point to the new i_size.
(7) If stored data is discarded from the pagecache or culled from fscache,
we must set zero_point above that if the data also got written to the
server.
(8) If dirty data is written back to the server, but not fscache, we must
set zero_point above that.
(9) If a direct I/O write is made, set zero_point above that.
Assuming the above, any read from the server at or above the zero_point
position will return all zeroes.
The zero_point value can be stored in the cache, provided the above rules
are applied to it by any code that culls part of the local cache.
Signed-off-by: David Howells <dhowells@redhat.com>
cc: Jeff Layton <jlayton@kernel.org>
cc: linux-cachefs@redhat.com
cc: linux-fsdevel@vger.kernel.org
cc: linux-mm@kvack.org
2023-11-24 13:39:02 +00:00
|
|
|
unsigned long long end;
|
|
|
|
|
2024-08-23 21:08:11 +01:00
|
|
|
if (folio_test_dirty(folio))
|
|
|
|
return false;
|
|
|
|
|
netfs: Fix trimming of streaming-write folios in netfs_inval_folio()
When netfslib writes to a folio that it doesn't have data for, but that
data exists on the server, it will make a 'streaming write' whereby it
stores data in a folio that is marked dirty, but not uptodate. When it
does this, it attaches a record to folio->private to track the dirty
region.
When truncate() or fallocate() wants to invalidate part of such a folio, it
will call into ->invalidate_folio(), specifying the part of the folio that
is to be invalidated. netfs_invalidate_folio(), on behalf of the
filesystem, must then determine how to trim the streaming write record. In
a couple of cases, however, it does this incorrectly (the reduce-length and
move-start cases are switched over and don't, in any case, calculate the
value correctly).
Fix this by making the logic tree more obvious and fixing the cases.
Fixes: 9ebff83e6481 ("netfs: Prep to use folio->private for write grouping and streaming write")
Signed-off-by: David Howells <dhowells@redhat.com>
Link: https://lore.kernel.org/r/20240823200819.532106-5-dhowells@redhat.com
cc: Matthew Wilcox (Oracle) <willy@infradead.org>
cc: Pankaj Raghav <p.raghav@samsung.com>
cc: Jeff Layton <jlayton@kernel.org>
cc: Marc Dionne <marc.dionne@auristor.com>
cc: linux-afs@lists.infradead.org
cc: netfs@lists.linux.dev
cc: linux-mm@kvack.org
cc: linux-fsdevel@vger.kernel.org
Signed-off-by: Christian Brauner <brauner@kernel.org>
2024-08-23 21:08:12 +01:00
|
|
|
end = umin(folio_pos(folio) + folio_size(folio), i_size_read(&ctx->inode));
|
netfs: Optimise away reads above the point at which there can be no data
Track the file position above which the server is not expected to have any
data (the "zero point") and preemptively assume that we can satisfy
requests by filling them with zeroes locally rather than attempting to
download them if they're over that line - even if we've written data back
to the server. Assume that any data that was written back above that
position is held in the local cache. Note that we have to split requests
that straddle the line.
Make use of this to optimise away some reads from the server. We need to
set the zero point in the following circumstances:
(1) When we see an extant remote inode and have no cache for it, we set
the zero_point to i_size.
(2) On local inode creation, we set zero_point to 0.
(3) On local truncation down, we reduce zero_point to the new i_size if
the new i_size is lower.
(4) On local truncation up, we don't change zero_point.
(5) On local modification, we don't change zero_point.
(6) On remote invalidation, we set zero_point to the new i_size.
(7) If stored data is discarded from the pagecache or culled from fscache,
we must set zero_point above that if the data also got written to the
server.
(8) If dirty data is written back to the server, but not fscache, we must
set zero_point above that.
(9) If a direct I/O write is made, set zero_point above that.
Assuming the above, any read from the server at or above the zero_point
position will return all zeroes.
The zero_point value can be stored in the cache, provided the above rules
are applied to it by any code that culls part of the local cache.
Signed-off-by: David Howells <dhowells@redhat.com>
cc: Jeff Layton <jlayton@kernel.org>
cc: linux-cachefs@redhat.com
cc: linux-fsdevel@vger.kernel.org
cc: linux-mm@kvack.org
2023-11-24 13:39:02 +00:00
|
|
|
if (end > ctx->zero_point)
|
|
|
|
ctx->zero_point = end;
|
2021-08-20 17:08:30 +01:00
|
|
|
|
|
|
|
if (folio_test_private(folio))
|
|
|
|
return false;
|
2024-08-14 21:38:21 +01:00
|
|
|
if (unlikely(folio_test_private_2(folio))) { /* [DEPRECATED] */
|
|
|
|
if (current_is_kswapd() || !(gfp & __GFP_FS))
|
|
|
|
return false;
|
|
|
|
folio_wait_private_2(folio);
|
|
|
|
}
|
2021-08-20 17:08:30 +01:00
|
|
|
fscache_note_page_release(netfs_i_cookie(ctx));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(netfs_release_folio);
|